hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
// Copyright 2018 The Hugo Authors. All rights reserved.
2013-07-04 11:32:55 -04:00
//
2015-11-23 22:16:36 -05:00
// Licensed under the Apache License, Version 2.0 (the "License");
2013-07-04 11:32:55 -04:00
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2015-11-23 22:16:36 -05:00
// http://www.apache.org/licenses/LICENSE-2.0
2013-07-04 11:32:55 -04:00
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
2014-01-29 17:50:31 -05:00
"bytes"
2018-04-19 12:06:40 -04:00
"context"
2014-01-29 17:50:31 -05:00
"errors"
"fmt"
2015-01-25 06:08:02 -05:00
"reflect"
2017-08-14 03:24:38 -04:00
"unicode"
2015-01-25 06:08:02 -05:00
2017-08-19 07:16:00 -04:00
"github.com/gohugoio/hugo/related"
2016-11-01 18:04:12 -04:00
"github.com/bep/gitmap"
2017-06-13 12:42:45 -04:00
"github.com/gohugoio/hugo/helpers"
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
"github.com/gohugoio/hugo/hugolib/pagemeta"
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
"github.com/gohugoio/hugo/resource"
2017-06-13 12:42:45 -04:00
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/parser"
2017-06-13 13:07:35 -04:00
"github.com/mitchellh/mapstructure"
2014-11-28 15:16:57 -05:00
2014-12-07 13:48:00 -05:00
"html/template"
"io"
"path"
"path/filepath"
2015-09-03 06:22:20 -04:00
"regexp"
2014-12-07 13:48:00 -05:00
"strings"
2015-01-21 09:28:05 -05:00
"sync"
2014-12-07 13:48:00 -05:00
"time"
2015-09-03 06:22:20 -04:00
"unicode/utf8"
2015-01-25 06:08:02 -05:00
2017-06-13 12:42:45 -04:00
bp "github.com/gohugoio/hugo/bufferpool"
2017-08-17 04:24:17 -04:00
"github.com/gohugoio/hugo/compare"
2017-06-13 12:42:45 -04:00
"github.com/gohugoio/hugo/source"
2017-06-13 13:07:35 -04:00
"github.com/spf13/cast"
2013-07-04 11:32:55 -04:00
)
2015-09-03 06:22:20 -04:00
var (
2017-05-26 03:51:17 -04:00
cjk = regexp . MustCompile ( ` \p { Han}|\p { Hangul}|\p { Hiragana}|\p { Katakana} ` )
// This is all the kinds we can expect to find in .Site.Pages.
allKindsInPages = [ ] string { KindPage , KindHome , KindSection , KindTaxonomy , KindTaxonomyTerm }
allKinds = append ( allKindsInPages , [ ] string { kindRSS , kindSitemap , kindRobotsTXT , kind404 } ... )
2017-08-17 04:24:17 -04:00
// Assert that it implements the Eqer interface.
_ compare . Eqer = ( * Page ) ( nil )
_ compare . Eqer = ( * PageOutput ) ( nil )
2017-08-19 07:16:00 -04:00
// Assert that it implements the interface needed for related searches.
_ related . Document = ( * Page ) ( nil )
2015-09-03 06:22:20 -04:00
)
2016-11-11 03:19:16 -05:00
const (
2016-11-13 06:33:11 -05:00
KindPage = "page"
2016-11-11 03:19:16 -05:00
// The rest are node types; home page, sections etc.
2017-09-25 22:25:33 -04:00
2016-11-13 06:33:11 -05:00
KindHome = "home"
KindSection = "section"
KindTaxonomy = "taxonomy"
KindTaxonomyTerm = "taxonomyTerm"
2016-11-11 03:19:16 -05:00
// Temporary state.
2016-11-13 06:33:11 -05:00
kindUnknown = "unknown"
2016-11-11 03:19:16 -05:00
// The following are (currently) temporary nodes,
// i.e. nodes we create just to render in isolation.
2016-11-15 04:43:49 -05:00
kindRSS = "RSS"
2016-11-13 06:33:11 -05:00
kindSitemap = "sitemap"
kindRobotsTXT = "robotsTXT"
kind404 = "404"
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
pageResourceType = "page"
2016-11-11 03:19:16 -05:00
)
2013-07-04 11:32:55 -04:00
type Page struct {
2016-11-16 14:32:38 -05:00
* pageInit
2018-04-19 12:06:40 -04:00
* pageContentInit
2016-11-13 05:43:23 -05:00
2016-11-13 06:33:11 -05:00
// Kind is the discriminator that identifies the different page types
// in the different page collections. This can, as an example, be used
// to to filter regular pages, find sections etc.
2016-11-13 05:43:23 -05:00
// Kind will, for the pages available to the templates, be one of:
// page, home, section, taxonomy and taxonomyTerm.
2016-11-13 06:33:11 -05:00
// It is of string type to make it easy to reason about in
// the templates.
Kind string
2016-11-11 03:19:16 -05:00
// Since Hugo 0.18 we got rid of the Node type. So now all pages are ...
// pages (regular pages, home page, sections etc.).
// Sections etc. will have child pages. These were earlier placed in .Data.Pages,
// but can now be more intuitively also be fetched directly from .Pages.
// This collection will be nil for regular pages.
Pages Pages
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
// Since Hugo 0.32, a Page can have resources such as images and CSS associated
// with itself. The resource will typically be placed relative to the Page,
// but templates should use the links (Permalink and RelPermalink)
// provided by the Resource object.
Resources resource . Resources
2018-01-15 14:40:39 -05:00
// This is the raw front matter metadata that is going to be assigned to
// the Resources above.
resourcesMetadata [ ] map [ string ] interface { }
2016-11-16 11:52:03 -05:00
// translations will contain references to this page in other language
// if available.
translations Pages
2017-11-17 10:28:35 -05:00
// A key that maps to translation(s) of this page. This value is fetched
// from the page front matter.
translationKey string
2016-11-16 11:52:03 -05:00
// Params contains configuration defined in the params section of page frontmatter.
2018-01-15 14:40:39 -05:00
params map [ string ] interface { }
2016-11-13 08:27:10 -05:00
2018-04-19 12:06:40 -04:00
// Called when needed to init the content (render shortcodes etc.).
contentInitFn func ( p * Page ) func ( )
2016-11-16 11:52:03 -05:00
// Content sections
2018-04-19 12:06:40 -04:00
contentv template . HTML
summary template . HTML
2016-11-13 08:27:10 -05:00
TableOfContents template . HTML
2018-04-19 12:06:40 -04:00
// Passed to the shortcodes
pageWithoutContent * PageWithoutContent
2016-11-13 08:27:10 -05:00
2016-11-16 11:52:03 -05:00
Aliases [ ] string
Images [ ] Image
Videos [ ] Video
2018-04-19 12:06:40 -04:00
truncated bool
2016-11-13 08:27:10 -05:00
Draft bool
2016-11-16 11:52:03 -05:00
Status string
2016-11-13 08:27:10 -05:00
2016-11-16 11:52:03 -05:00
// PageMeta contains page stats such as word count etc.
PageMeta
2016-11-13 08:27:10 -05:00
2016-11-16 11:52:03 -05:00
// Markup contains the markup type for the content.
Markup string
2016-11-13 08:27:10 -05:00
extension string
contentType string
renderable bool
2017-03-25 13:28:38 -04:00
Layout string
// For npn-renderable pages (see IsRenderable), the content itself
// is used as template and the template name is stored here.
selfLayout string
2016-11-13 08:27:10 -05:00
2016-11-16 11:52:03 -05:00
linkTitle string
2016-11-13 08:27:10 -05:00
frontmatter [ ] byte
2016-08-12 18:33:17 -04:00
2016-12-01 04:21:49 -05:00
// rawContent is the raw content read from the content file.
2016-08-12 18:33:17 -04:00
rawContent [ ] byte
2016-12-01 04:21:49 -05:00
// workContent is a copy of rawContent that may be mutated during site build.
workContent [ ] byte
2016-11-16 11:52:03 -05:00
// whether the content is in a CJK language.
isCJKLanguage bool
2017-03-10 14:54:50 -05:00
shortcodeState * shortcodeHandler
2016-11-16 11:52:03 -05:00
// the content stripped for HTML
2016-11-16 14:32:38 -05:00
plain string // TODO should be []byte
plainWords [ ] string
2016-11-16 11:52:03 -05:00
// rendering configuration
Reuse the BlackFriday instance when possible
This is in heavy use in rendering, so this makes a difference:
```bash
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 124551144 107743429 -13.49%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 528684 435118 -17.70%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 53306848 45147832 -15.31%
```
2017-12-16 12:56:58 -05:00
renderingConfig * helpers . BlackFriday
2016-11-16 11:52:03 -05:00
// menus
2016-11-16 14:32:38 -05:00
pageMenus PageMenus
2016-11-16 11:52:03 -05:00
2014-10-16 20:20:09 -04:00
Source
2016-11-16 11:52:03 -05:00
2015-05-23 06:28:01 -04:00
Position ` json:"-" `
2016-11-02 16:34:19 -04:00
2016-11-01 18:04:12 -04:00
GitInfo * gitmap . GitInfo
2016-10-31 14:53:33 -04:00
// This was added as part of getting the Nodes (taxonomies etc.) to work as
// Pages in Hugo 0.18.
// It is deliberately named similar to Section, but not exported (for now).
// We currently have only one level of section in Hugo, but the page can live
// any number of levels down the file path.
// To support taxonomies like /categories/hugo etc. we will need to keep track
// of that information in a general way.
// So, sections represents the path to the content, i.e. a content file or a
// virtual content file in the situations where a taxonomy or a section etc.
// isn't accomanied by one.
sections [ ] string
2017-04-09 04:33:04 -04:00
// Will only be set for sections and regular pages.
parent * Page
// When we create paginator pages, we create a copy of the original,
// but keep track of it here.
origOnCopy * Page
// Will only be set for section pages and the home page.
subSections Pages
2017-01-03 11:28:51 -05:00
s * Site
2016-11-13 08:27:10 -05:00
2016-11-16 15:06:10 -05:00
// Pulled over from old Node. TODO(bep) reorg and group (embed)
2016-11-13 08:27:10 -05:00
Site * SiteInfo ` json:"-" `
2018-01-15 14:40:39 -05:00
title string
2016-11-13 08:27:10 -05:00
Description string
Keywords [ ] string
Data map [ string ] interface { }
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
pagemeta . PageDates
2016-11-13 08:27:10 -05:00
Sitemap Sitemap
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
pagemeta . URLPath
frontMatterURL string
2018-01-05 05:07:50 -05:00
permalink string
relPermalink string
2018-01-10 04:20:08 -05:00
// relative target path without extension and any base path element from the baseURL.
2018-01-05 05:07:50 -05:00
// This is used to construct paths in the page resources.
2018-01-10 04:20:08 -05:00
relTargetPathBase string
2018-01-20 11:11:03 -05:00
// Is set to a forward slashed path if this is a Page resources living in a folder below its owner.
resourcePath string
2016-11-13 08:27:10 -05:00
2018-01-23 08:02:54 -05:00
// This is enabled if it is a leaf bundle (the "index.md" type) and it is marked as headless in front matter.
// Being headless means that
// 1. The page itself is not rendered to disk
// 2. It is not available in .Site.Pages etc.
// 3. But you can get it via .Site.GetPage
headless bool
2017-03-18 11:46:10 -04:00
layoutDescriptor output . LayoutDescriptor
2016-11-13 08:27:10 -05:00
scratch * Scratch
2017-02-04 22:20:06 -05:00
// It would be tempting to use the language set on the Site, but in they way we do
// multi-site processing, these values may differ during the initial page processing.
2016-11-16 14:32:38 -05:00
language * helpers . Language
2017-02-04 22:20:06 -05:00
lang string
2017-03-02 09:35:25 -05:00
2017-03-16 03:32:14 -04:00
// The output formats this page will be rendered to.
outputFormats output . Formats
2017-03-06 07:18:33 -05:00
2017-03-16 03:32:14 -04:00
// This is the PageOutput that represents the first item in outputFormats.
2017-03-09 13:19:29 -05:00
// Use with care, as there are potential for inifinite loops.
mainPageOutput * PageOutput
2017-03-17 11:35:09 -04:00
targetPathDescriptorPrototype * targetPathDescriptor
2017-03-06 07:18:33 -05:00
}
2018-04-19 12:06:40 -04:00
func ( p * Page ) initContent ( ) {
p . contentInit . Do ( func ( ) {
// This careful dance is here to protect against circular loops in shortcode/content
// constructs.
// TODO(bep) context vs the remote shortcodes
ctx , cancel := context . WithTimeout ( context . Background ( ) , p . s . Timeout )
defer cancel ( )
c := make ( chan error , 1 )
go func ( ) {
var err error
p . contentInitMu . Lock ( )
defer p . contentInitMu . Unlock ( )
if p . contentInitFn != nil {
p . contentInitFn ( p ) ( )
}
if len ( p . summary ) == 0 {
if err = p . setAutoSummary ( ) ; err != nil {
err = fmt . Errorf ( "Failed to set user auto summary for page %q: %s" , p . pathOrTitle ( ) , err )
}
}
c <- err
} ( )
select {
case <- ctx . Done ( ) :
p . s . Log . WARN . Printf ( ` WARNING: Timed out creating content for page %q (.Content will be empty). This is most likely a circular shortcode content loop that should be fixed. If this is just a shortcode calling a slow remote service, try to set "timeout=20000" (or higher, value is in milliseconds) in config.toml. ` , p . pathOrTitle ( ) )
case err := <- c :
if err != nil {
p . s . Log . ERROR . Println ( err )
}
}
} )
}
// This is sent to the shortcodes for this page. Not doing that will create an infinite regress. So,
// shortcodes can access .Page.TableOfContents, but not .Page.Content etc.
func ( p * Page ) withoutContent ( ) * PageWithoutContent {
p . pageInit . withoutContentInit . Do ( func ( ) {
p . pageWithoutContent = & PageWithoutContent { Page : p }
} )
return p . pageWithoutContent
}
2018-04-15 10:42:54 -04:00
func ( p * Page ) Content ( ) ( interface { } , error ) {
2018-04-19 12:06:40 -04:00
return p . content ( ) , nil
}
func ( p * Page ) Truncated ( ) bool {
p . initContent ( )
return p . truncated
}
func ( p * Page ) content ( ) template . HTML {
p . initContent ( )
return p . contentv
}
func ( p * Page ) Summary ( ) template . HTML {
p . initContent ( )
return p . summary
2018-04-15 10:42:54 -04:00
}
2018-03-15 04:37:30 -04:00
// Sites is a convenience method to get all the Hugo sites/languages configured.
func ( p * Page ) Sites ( ) SiteInfos {
infos := make ( SiteInfos , len ( p . s . owner . Sites ) )
for i , site := range p . s . owner . Sites {
infos [ i ] = & site . Info
}
return infos
}
2017-08-19 07:16:00 -04:00
// SearchKeywords implements the related.Document interface needed for fast page searches.
func ( p * Page ) SearchKeywords ( cfg related . IndexConfig ) ( [ ] related . Keyword , error ) {
v , err := p . Param ( cfg . Name )
if err != nil {
return nil , err
}
return cfg . ToKeywords ( v )
}
// PubDate is when this page was or will be published.
// NOTE: This is currently used for search only and is not meant to be used
// directly in templates. We need to consolidate the dates in this struct.
// TODO(bep) see https://github.com/gohugoio/hugo/issues/3854
func ( p * Page ) PubDate ( ) time . Time {
if ! p . PublishDate . IsZero ( ) {
return p . PublishDate
}
return p . Date
}
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
func ( * Page ) ResourceType ( ) string {
return pageResourceType
}
2017-03-24 11:54:37 -04:00
func ( p * Page ) RSSLink ( ) template . URL {
2017-03-25 14:36:50 -04:00
f , found := p . outputFormats . GetByName ( output . RSSFormat . Name )
2017-03-24 11:54:37 -04:00
if ! found {
return ""
}
return template . URL ( newOutputFormat ( p , f ) . Permalink ( ) )
}
2017-03-16 03:58:50 -04:00
func ( p * Page ) createLayoutDescriptor ( ) output . LayoutDescriptor {
var section string
2017-03-06 07:18:33 -05:00
switch p . Kind {
case KindSection :
2017-04-09 04:33:04 -04:00
// In Hugo 0.22 we introduce nested sections, but we still only
// use the first level to pick the correct template. This may change in
// the future.
2017-03-16 03:58:50 -04:00
section = p . sections [ 0 ]
2017-03-06 07:18:33 -05:00
case KindTaxonomy , KindTaxonomyTerm :
2017-03-16 03:58:50 -04:00
section = p . s . taxonomiesPluralSingular [ p . sections [ 0 ] ]
2017-03-06 07:18:33 -05:00
default :
2017-03-16 03:58:50 -04:00
}
return output . LayoutDescriptor {
Kind : p . Kind ,
Type : p . Type ( ) ,
2017-07-02 04:46:28 -04:00
Lang : p . Lang ( ) ,
2017-03-16 03:58:50 -04:00
Layout : p . Layout ,
Section : section ,
2017-03-06 07:18:33 -05:00
}
2016-11-16 14:32:38 -05:00
}
// pageInit lazy initializes different parts of the page. It is extracted
// into its own type so we can easily create a copy of a given page.
type pageInit struct {
languageInit sync . Once
pageMenusInit sync . Once
pageMetaInit sync . Once
2017-03-26 05:45:12 -04:00
pageOutputInit sync . Once
2016-11-16 14:32:38 -05:00
renderingConfigInit sync . Once
2018-04-19 12:06:40 -04:00
withoutContentInit sync . Once
}
type pageContentInit struct {
contentInitMu sync . Mutex
contentInit sync . Once
plainInit sync . Once
plainWordsInit sync . Once
}
func ( p * Page ) resetContent ( init func ( page * Page ) func ( ) ) {
p . pageContentInit = & pageContentInit { }
if init == nil {
init = func ( page * Page ) func ( ) {
return func ( ) { }
}
}
p . contentInitFn = init
2013-07-04 11:32:55 -04:00
}
2016-11-13 06:33:11 -05:00
// IsNode returns whether this is an item of one of the list types in Hugo,
// i.e. not a regular content page.
func ( p * Page ) IsNode ( ) bool {
return p . Kind != KindPage
}
// IsHome returns whether this is the home page.
func ( p * Page ) IsHome ( ) bool {
return p . Kind == KindHome
}
2017-04-09 04:33:04 -04:00
// IsSection returns whether this is a section page.
func ( p * Page ) IsSection ( ) bool {
return p . Kind == KindSection
}
2016-11-13 06:33:11 -05:00
// IsPage returns whether this is a regular content page.
func ( p * Page ) IsPage ( ) bool {
return p . Kind == KindPage
}
2018-04-23 02:41:19 -04:00
// BundleType returns the bundle type: "leaf", "branch" or an empty string if it is none.
// See https://gohugo.io/content-management/page-bundles/
func ( p * Page ) BundleType ( ) string {
if p . IsNode ( ) {
return "branch"
}
var source interface { } = p . Source . File
if fi , ok := source . ( * fileInfo ) ; ok {
switch fi . bundleTp {
case bundleBranch :
return "branch"
case bundleLeaf :
return "leaf"
}
}
return ""
}
2014-10-16 20:20:09 -04:00
type Source struct {
Frontmatter [ ] byte
Content [ ] byte
source . File
2013-07-04 11:32:55 -04:00
}
type PageMeta struct {
2016-08-17 07:41:48 -04:00
wordCount int
fuzzyWordCount int
readingTime int
2014-01-29 17:50:31 -05:00
Weight int
2013-07-04 11:32:55 -04:00
}
type Position struct {
2015-01-21 08:35:33 -05:00
Prev * Page
Next * Page
2014-04-18 03:23:13 -04:00
PrevInSection * Page
NextInSection * Page
2013-07-04 11:32:55 -04:00
}
type Pages [ ] * Page
2016-01-07 21:48:13 -05:00
2017-03-09 08:18:12 -05:00
func ( ps Pages ) String ( ) string {
return fmt . Sprintf ( "Pages(%d)" , len ( ps ) )
2016-11-13 08:27:10 -05:00
}
2018-03-21 12:21:46 -04:00
func ( ps Pages ) findPagePosByFilename ( filename string ) int {
2016-01-11 12:06:52 -05:00
for i , x := range ps {
2018-03-21 12:21:46 -04:00
if x . Source . Filename ( ) == filename {
2016-01-11 12:06:52 -05:00
return i
}
}
return - 1
}
2016-01-07 21:48:13 -05:00
2017-08-19 07:16:00 -04:00
func ( ps Pages ) removeFirstIfFound ( p * Page ) Pages {
ii := - 1
for i , pp := range ps {
if pp == p {
ii = i
break
}
}
if ii != - 1 {
ps = append ( ps [ : ii ] , ps [ ii + 1 : ] ... )
}
return ps
}
2018-03-21 12:21:46 -04:00
func ( ps Pages ) findPagePosByFilnamePrefix ( prefix string ) int {
2017-06-08 14:00:05 -04:00
if prefix == "" {
return - 1
}
2018-03-21 12:21:46 -04:00
lenDiff := - 1
currPos := - 1
prefixLen := len ( prefix )
// Find the closest match
2017-06-08 14:00:05 -04:00
for i , x := range ps {
2018-03-21 12:21:46 -04:00
if strings . HasPrefix ( x . Source . Filename ( ) , prefix ) {
diff := len ( x . Source . Filename ( ) ) - prefixLen
if lenDiff == - 1 || diff < lenDiff {
lenDiff = diff
currPos = i
}
2017-06-08 14:00:05 -04:00
}
}
2018-03-21 12:21:46 -04:00
return currPos
2017-06-08 14:00:05 -04:00
}
2017-06-08 16:32:01 -04:00
// findPagePos Given a page, it will find the position in Pages
2016-01-07 21:48:13 -05:00
// will return -1 if not found
2017-06-08 16:32:01 -04:00
func ( ps Pages ) findPagePos ( page * Page ) int {
2016-01-07 21:48:13 -05:00
for i , x := range ps {
2018-04-05 04:57:13 -04:00
if x . Source . Filename ( ) == page . Source . Filename ( ) {
2016-01-07 21:48:13 -05:00
return i
}
}
return - 1
}
2016-12-01 04:21:49 -05:00
func ( p * Page ) createWorkContentCopy ( ) {
p . workContent = make ( [ ] byte , len ( p . rawContent ) )
copy ( p . workContent , p . rawContent )
}
2014-01-27 17:16:28 -05:00
func ( p * Page ) Plain ( ) string {
2018-04-19 12:06:40 -04:00
p . initContent ( )
p . initPlain ( true )
2014-01-29 17:50:31 -05:00
return p . plain
2013-10-15 09:15:52 -04:00
}
2018-04-19 12:06:40 -04:00
func ( p * Page ) initPlain ( lock bool ) {
2015-02-04 18:38:50 -05:00
p . plainInit . Do ( func ( ) {
2018-04-19 12:06:40 -04:00
if lock {
p . contentInitMu . Lock ( )
defer p . contentInitMu . Unlock ( )
}
p . plain = helpers . StripHTML ( string ( p . contentv ) )
2016-08-16 16:50:15 -04:00
} )
}
2018-04-19 12:06:40 -04:00
func ( p * Page ) PlainWords ( ) [ ] string {
p . initContent ( )
p . initPlainWords ( true )
return p . plainWords
}
func ( p * Page ) initPlainWords ( lock bool ) {
2016-08-16 16:50:15 -04:00
p . plainWordsInit . Do ( func ( ) {
2018-04-19 12:06:40 -04:00
if lock {
p . contentInitMu . Lock ( )
defer p . contentInitMu . Unlock ( )
}
p . plainWords = strings . Fields ( p . plain )
2015-07-12 05:28:19 -04:00
} )
}
2015-10-11 23:51:04 -04:00
// Param is a convenience method to do lookups in Page's and Site's Params map,
// in that order.
//
2016-10-17 13:30:21 -04:00
// This method is also implemented on Node and SiteInfo.
2015-10-11 23:51:04 -04:00
func ( p * Page ) Param ( key interface { } ) ( interface { } , error ) {
keyStr , err := cast . ToStringE ( key )
if err != nil {
return nil , err
}
2017-02-19 02:50:08 -05:00
2016-10-17 13:30:21 -04:00
keyStr = strings . ToLower ( keyStr )
2017-02-19 02:50:08 -05:00
result , _ := p . traverseDirect ( keyStr )
if result != nil {
return result , nil
}
keySegments := strings . Split ( keyStr , "." )
if len ( keySegments ) == 1 {
return nil , nil
}
return p . traverseNested ( keySegments )
}
func ( p * Page ) traverseDirect ( key string ) ( interface { } , error ) {
keyStr := strings . ToLower ( key )
2018-01-15 14:40:39 -05:00
if val , ok := p . params [ keyStr ] ; ok {
2015-10-11 23:51:04 -04:00
return val , nil
}
2017-02-19 02:50:08 -05:00
2015-10-11 23:51:04 -04:00
return p . Site . Params [ keyStr ] , nil
}
2017-02-19 02:50:08 -05:00
func ( p * Page ) traverseNested ( keySegments [ ] string ) ( interface { } , error ) {
2018-01-15 14:40:39 -05:00
result := traverse ( keySegments , p . params )
2017-02-19 02:50:08 -05:00
if result != nil {
return result , nil
}
result = traverse ( keySegments , p . Site . Params )
if result != nil {
return result , nil
}
// Didn't find anything, but also no problems.
return nil , nil
}
func traverse ( keys [ ] string , m map [ string ] interface { } ) interface { } {
// Shift first element off.
firstKey , rest := keys [ 0 ] , keys [ 1 : ]
result := m [ firstKey ]
// No point in continuing here.
if result == nil {
return result
}
if len ( rest ) == 0 {
// That was the last key.
return result
}
2017-09-25 22:25:33 -04:00
// That was not the last key.
return traverse ( rest , cast . ToStringMap ( result ) )
2017-02-19 02:50:08 -05:00
}
2014-12-09 13:33:55 -05:00
func ( p * Page ) Author ( ) Author {
authors := p . Authors ( )
2016-09-18 13:10:11 -04:00
for _ , author := range authors {
return author
2016-09-11 04:46:56 -04:00
}
2016-09-18 13:10:11 -04:00
return Author { }
}
2016-09-15 22:28:13 -04:00
2016-09-18 13:10:11 -04:00
func ( p * Page ) Authors ( ) AuthorList {
2018-01-15 14:40:39 -05:00
authorKeys , ok := p . params [ "authors" ]
2016-09-18 13:10:11 -04:00
if ! ok {
return AuthorList { }
}
authors := authorKeys . ( [ ] string )
if len ( authors ) < 1 || len ( p . Site . Authors ) < 1 {
return AuthorList { }
2014-12-09 13:33:55 -05:00
}
2016-09-18 13:10:11 -04:00
al := make ( AuthorList )
for _ , author := range authors {
a , ok := p . Site . Authors [ author ]
if ok {
al [ author ] = a
2014-12-09 13:33:55 -05:00
}
}
2016-09-18 13:10:11 -04:00
return al
2014-12-09 13:33:55 -05:00
}
2015-03-11 13:34:57 -04:00
func ( p * Page ) UniqueID ( ) string {
return p . Source . UniqueID ( )
2014-10-01 14:26:43 -04:00
}
Shortcode rewrite, take 2
This commit contains a restructuring and partial rewrite of the shortcode handling.
Prior to this commit rendering of the page content was mingled with handling of the shortcodes. This led to several oddities.
The new flow is:
1. Shortcodes are extracted from page and replaced with placeholders.
2. Shortcodes are processed and rendered
3. Page is processed
4. The placeholders are replaced with the rendered shortcodes
The handling of summaries is also made simpler by this.
This commit also introduces some other chenges:
1. distinction between shortcodes that need further processing and those who do not:
* `{{< >}}`: Typically raw HTML. Will not be processed.
* `{{% %}}`: Will be processed by the page's markup engine (Markdown or (infuture) Asciidoctor)
The above also involves a new shortcode-parser, with lexical scanning inspired by Rob Pike's talk called "Lexical Scanning in Go",
which should be easier to understand, give better error messages and perform better.
2. If you want to exclude a shortcode from being processed (for documentation etc.), the inner part of the shorcode must be commented out, i.e. `{{%/* movie 47238zzb */%}}`. See the updated shortcode section in the documentation for further examples.
The new parser supports nested shortcodes. This isn't new, but has two related design choices worth mentioning:
* The shortcodes will be rendered individually, so If both `{{< >}}` and `{{% %}}` are used in the nested hierarchy, one will be passed through the page's markdown processor, the other not.
* To avoid potential costly overhead of always looking far ahead for a possible closing tag, this implementation looks at the template itself, and is branded as a container with inner content if it contains a reference to `.Inner`
Fixes #565
Fixes #480
Fixes #461
And probably some others.
2014-10-27 16:48:30 -04:00
// for logging
func ( p * Page ) lineNumRawContentStart ( ) int {
return bytes . Count ( p . frontmatter , [ ] byte ( "\n" ) ) + 1
}
2016-08-01 17:04:44 -04:00
var (
2016-09-19 16:14:15 -04:00
internalSummaryDivider = [ ] byte ( "HUGOMORE42" )
2016-08-01 17:04:44 -04:00
)
Shortcode rewrite, take 2
This commit contains a restructuring and partial rewrite of the shortcode handling.
Prior to this commit rendering of the page content was mingled with handling of the shortcodes. This led to several oddities.
The new flow is:
1. Shortcodes are extracted from page and replaced with placeholders.
2. Shortcodes are processed and rendered
3. Page is processed
4. The placeholders are replaced with the rendered shortcodes
The handling of summaries is also made simpler by this.
This commit also introduces some other chenges:
1. distinction between shortcodes that need further processing and those who do not:
* `{{< >}}`: Typically raw HTML. Will not be processed.
* `{{% %}}`: Will be processed by the page's markup engine (Markdown or (infuture) Asciidoctor)
The above also involves a new shortcode-parser, with lexical scanning inspired by Rob Pike's talk called "Lexical Scanning in Go",
which should be easier to understand, give better error messages and perform better.
2. If you want to exclude a shortcode from being processed (for documentation etc.), the inner part of the shorcode must be commented out, i.e. `{{%/* movie 47238zzb */%}}`. See the updated shortcode section in the documentation for further examples.
The new parser supports nested shortcodes. This isn't new, but has two related design choices worth mentioning:
* The shortcodes will be rendered individually, so If both `{{< >}}` and `{{% %}}` are used in the nested hierarchy, one will be passed through the page's markdown processor, the other not.
* To avoid potential costly overhead of always looking far ahead for a possible closing tag, this implementation looks at the template itself, and is branded as a container with inner content if it contains a reference to `.Inner`
Fixes #565
Fixes #480
Fixes #461
And probably some others.
2014-10-27 16:48:30 -04:00
2017-08-14 02:42:30 -04:00
// replaceDivider replaces the <!--more--> with an internal value and returns
// whether the contentis truncated or not.
// Note: The content slice will be modified if needed.
func replaceDivider ( content , from , to [ ] byte ) ( [ ] byte , bool ) {
2017-08-14 03:24:38 -04:00
dividerIdx := bytes . Index ( content , from )
if dividerIdx == - 1 {
return content , false
}
afterSummary := content [ dividerIdx + len ( from ) : ]
2017-08-14 02:42:30 -04:00
// If the raw content has nothing but whitespace after the summary
// marker then the page shouldn't be marked as truncated. This check
// is simplest against the raw content because different markup engines
// (rst and asciidoc in particular) add div and p elements after the
// summary marker.
2017-08-14 03:24:38 -04:00
truncated := bytes . IndexFunc ( afterSummary , func ( r rune ) bool { return ! unicode . IsSpace ( r ) } ) != - 1
content = append ( content [ : dividerIdx ] , append ( to , afterSummary ... ) ... )
2017-08-14 02:42:30 -04:00
2017-08-14 03:24:38 -04:00
return content , truncated
2017-08-14 02:42:30 -04:00
}
2017-01-26 14:58:25 -05:00
// We have to replace the <!--more--> with something that survives all the
// rendering engines.
func ( p * Page ) replaceDivider ( content [ ] byte ) [ ] byte {
2017-02-21 02:46:03 -05:00
summaryDivider := helpers . SummaryDivider
2017-02-21 02:49:04 -05:00
// TODO(bep) handle better.
if p . Ext ( ) == "org" || p . Markup == "org" {
2017-02-21 02:46:03 -05:00
summaryDivider = [ ] byte ( "# more" )
}
2017-01-26 14:58:25 -05:00
2017-08-14 02:42:30 -04:00
replaced , truncated := replaceDivider ( content , summaryDivider , internalSummaryDivider )
2018-04-19 12:06:40 -04:00
p . truncated = truncated
2017-01-26 14:58:25 -05:00
2017-08-14 02:42:30 -04:00
return replaced
2017-01-26 14:58:25 -05:00
}
2016-08-01 17:04:44 -04:00
// Returns the page as summary and main if a user defined split is provided.
2016-10-25 14:40:32 -04:00
func ( p * Page ) setUserDefinedSummaryIfProvided ( rawContentCopy [ ] byte ) ( * summaryContent , error ) {
Shortcode rewrite, take 2
This commit contains a restructuring and partial rewrite of the shortcode handling.
Prior to this commit rendering of the page content was mingled with handling of the shortcodes. This led to several oddities.
The new flow is:
1. Shortcodes are extracted from page and replaced with placeholders.
2. Shortcodes are processed and rendered
3. Page is processed
4. The placeholders are replaced with the rendered shortcodes
The handling of summaries is also made simpler by this.
This commit also introduces some other chenges:
1. distinction between shortcodes that need further processing and those who do not:
* `{{< >}}`: Typically raw HTML. Will not be processed.
* `{{% %}}`: Will be processed by the page's markup engine (Markdown or (infuture) Asciidoctor)
The above also involves a new shortcode-parser, with lexical scanning inspired by Rob Pike's talk called "Lexical Scanning in Go",
which should be easier to understand, give better error messages and perform better.
2. If you want to exclude a shortcode from being processed (for documentation etc.), the inner part of the shorcode must be commented out, i.e. `{{%/* movie 47238zzb */%}}`. See the updated shortcode section in the documentation for further examples.
The new parser supports nested shortcodes. This isn't new, but has two related design choices worth mentioning:
* The shortcodes will be rendered individually, so If both `{{< >}}` and `{{% %}}` are used in the nested hierarchy, one will be passed through the page's markdown processor, the other not.
* To avoid potential costly overhead of always looking far ahead for a possible closing tag, this implementation looks at the template itself, and is branded as a container with inner content if it contains a reference to `.Inner`
Fixes #565
Fixes #480
Fixes #461
And probably some others.
2014-10-27 16:48:30 -04:00
2016-10-25 14:40:32 -04:00
sc , err := splitUserDefinedSummaryAndContent ( p . Markup , rawContentCopy )
2016-10-18 02:43:44 -04:00
if err != nil {
return nil , err
}
2015-05-10 07:33:50 -04:00
2016-08-01 17:04:44 -04:00
if sc == nil {
// No divider found
return nil , nil
}
2015-02-04 18:38:50 -05:00
2018-04-19 12:06:40 -04:00
p . summary = helpers . BytesToHTML ( sc . summary )
2016-08-01 17:04:44 -04:00
return sc , nil
}
// Make this explicit so there is no doubt about what is what.
type summaryContent struct {
2017-01-26 14:58:25 -05:00
summary [ ] byte
content [ ] byte
2016-08-01 17:04:44 -04:00
}
2016-10-18 02:43:44 -04:00
func splitUserDefinedSummaryAndContent ( markup string , c [ ] byte ) ( sc * summaryContent , err error ) {
defer func ( ) {
if r := recover ( ) ; r != nil {
err = fmt . Errorf ( "summary split failed: %s" , r )
}
} ( )
2016-08-07 17:34:04 -04:00
c = bytes . TrimSpace ( c )
2016-08-01 17:04:44 -04:00
startDivider := bytes . Index ( c , internalSummaryDivider )
if startDivider == - 1 {
2016-10-18 02:43:44 -04:00
return
2016-08-01 17:04:44 -04:00
}
endDivider := startDivider + len ( internalSummaryDivider )
endSummary := startDivider
var (
startMarkup [ ] byte
endMarkup [ ] byte
addDiv bool
)
switch markup {
default :
startMarkup = [ ] byte ( "<p>" )
endMarkup = [ ] byte ( "</p>" )
case "asciidoc" :
startMarkup = [ ] byte ( "<div class=\"paragraph\">" )
endMarkup = [ ] byte ( "</div>" )
case "rst" :
startMarkup = [ ] byte ( "<p>" )
endMarkup = [ ] byte ( "</p>" )
addDiv = true
}
// Find the closest end/start markup string to the divider
2016-10-18 02:43:44 -04:00
fromStart := - 1
2016-08-01 17:04:44 -04:00
fromIdx := bytes . LastIndex ( c [ : startDivider ] , startMarkup )
2016-10-18 02:43:44 -04:00
if fromIdx != - 1 {
fromStart = startDivider - fromIdx - len ( startMarkup )
}
2016-08-01 17:04:44 -04:00
fromEnd := bytes . Index ( c [ endDivider : ] , endMarkup )
if fromEnd != - 1 && fromEnd <= fromStart {
endSummary = startDivider + fromEnd + len ( endMarkup )
2016-08-07 17:34:04 -04:00
} else if fromStart != - 1 && fromEnd != - 1 {
2016-08-01 17:04:44 -04:00
endSummary = startDivider - fromStart - len ( startMarkup )
}
withoutDivider := bytes . TrimSpace ( append ( c [ : startDivider ] , c [ endDivider : ] ... ) )
2016-08-07 17:34:04 -04:00
var (
2017-01-26 14:58:25 -05:00
summary [ ] byte
2016-08-07 17:34:04 -04:00
)
if len ( withoutDivider ) > 0 {
summary = bytes . TrimSpace ( withoutDivider [ : endSummary ] )
}
2016-08-01 17:04:44 -04:00
if addDiv {
// For the rst
summary = append ( append ( [ ] byte ( nil ) , summary ... ) , [ ] byte ( "</div>" ) ... )
}
2016-10-18 02:43:44 -04:00
if err != nil {
return
}
sc = & summaryContent {
2017-01-26 14:58:25 -05:00
summary : summary ,
content : withoutDivider ,
2016-08-01 17:04:44 -04:00
}
2016-10-18 02:43:44 -04:00
return
2016-08-01 17:04:44 -04:00
}
func ( p * Page ) setAutoSummary ( ) error {
var summary string
var truncated bool
2018-04-19 12:06:40 -04:00
// This careful init dance could probably be refined, but it is purely for performance
// reasons. These "plain" methods are expensive if the plain content is never actually
// used.
p . initPlain ( false )
2016-08-01 17:04:44 -04:00
if p . isCJKLanguage {
2018-04-19 12:06:40 -04:00
p . initPlainWords ( false )
summary , truncated = p . s . ContentSpec . TruncateWordsByRune ( p . plainWords )
2016-08-01 17:04:44 -04:00
} else {
2018-04-19 12:06:40 -04:00
summary , truncated = p . s . ContentSpec . TruncateWordsToWholeSentence ( p . plain )
2016-08-01 17:04:44 -04:00
}
2018-04-19 12:06:40 -04:00
p . summary = template . HTML ( summary )
p . truncated = truncated
2016-08-01 17:04:44 -04:00
return nil
2018-04-19 12:06:40 -04:00
2014-01-27 17:16:28 -05:00
}
2014-01-28 23:11:05 -05:00
func ( p * Page ) renderContent ( content [ ] byte ) [ ] byte {
2017-02-04 22:20:06 -05:00
return p . s . ContentSpec . RenderBytes ( & helpers . RenderingContext {
2016-08-07 08:03:03 -04:00
Content : content , RenderTOC : true , PageFmt : p . determineMarkupType ( ) ,
2017-02-04 22:20:06 -05:00
Cfg : p . Language ( ) ,
DocumentID : p . UniqueID ( ) , DocumentName : p . Path ( ) ,
2017-08-02 15:37:03 -04:00
Config : p . getRenderingConfig ( ) } )
2014-11-28 15:16:57 -05:00
}
Reuse the BlackFriday instance when possible
This is in heavy use in rendering, so this makes a difference:
```bash
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 124551144 107743429 -13.49%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 528684 435118 -17.70%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 53306848 45147832 -15.31%
```
2017-12-16 12:56:58 -05:00
func ( p * Page ) getRenderingConfig ( ) * helpers . BlackFriday {
2015-01-25 06:08:02 -05:00
p . renderingConfigInit . Do ( func ( ) {
2017-12-29 02:58:38 -05:00
bfParam := p . getParamToLower ( "blackfriday" )
2017-04-06 16:29:37 -04:00
if bfParam == nil {
Reuse the BlackFriday instance when possible
This is in heavy use in rendering, so this makes a difference:
```bash
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 124551144 107743429 -13.49%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 528684 435118 -17.70%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 53306848 45147832 -15.31%
```
2017-12-16 12:56:58 -05:00
p . renderingConfig = p . s . ContentSpec . BlackFriday
2017-04-06 16:29:37 -04:00
return
}
Reuse the BlackFriday instance when possible
This is in heavy use in rendering, so this makes a difference:
```bash
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 124551144 107743429 -13.49%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 528684 435118 -17.70%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4 53306848 45147832 -15.31%
```
2017-12-16 12:56:58 -05:00
// Create a copy so we can modify it.
bf := * p . s . ContentSpec . BlackFriday
p . renderingConfig = & bf
if p . Language ( ) == nil {
panic ( fmt . Sprintf ( "nil language for %s with source lang %s" , p . BaseFileName ( ) , p . lang ) )
}
2017-04-06 16:29:37 -04:00
pageParam := cast . ToStringMap ( bfParam )
if err := mapstructure . Decode ( pageParam , & p . renderingConfig ) ; err != nil {
2017-01-10 04:55:03 -05:00
p . s . Log . FATAL . Printf ( "Failed to get rendering config for %s:\n%s" , p . BaseFileName ( ) , err . Error ( ) )
2015-01-25 06:08:02 -05:00
}
2016-10-16 13:28:21 -04:00
2015-01-21 09:28:05 -05:00
} )
2014-11-28 15:16:57 -05:00
2015-01-25 06:08:02 -05:00
return p . renderingConfig
2015-01-21 08:35:33 -05:00
}
2017-01-03 11:28:51 -05:00
func ( s * Site ) newPage ( filename string ) * Page {
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
fi := newFileInfo (
s . SourceSpec ,
s . absContentDir ( ) ,
filename ,
nil ,
bundleNot ,
)
return s . newPageFromFile ( fi )
}
func ( s * Site ) newPageFromFile ( fi * fileInfo ) * Page {
return & Page {
2018-04-19 12:06:40 -04:00
pageInit : & pageInit { } ,
pageContentInit : & pageContentInit { } ,
Kind : kindFromFileInfo ( fi ) ,
contentType : "" ,
Source : Source { File : fi } ,
Keywords : [ ] string { } , Sitemap : Sitemap { Priority : - 1 } ,
2018-01-15 14:40:39 -05:00
params : make ( map [ string ] interface { } ) ,
2016-07-25 16:22:09 -04:00
translations : make ( Pages , 0 ) ,
2018-01-27 12:03:06 -05:00
sections : sectionsFromFile ( fi ) ,
2017-02-04 22:20:06 -05:00
Site : & s . Info ,
s : s ,
2016-05-14 00:35:16 -04:00
}
2013-08-13 19:39:24 -04:00
}
2013-07-04 11:32:55 -04:00
2013-09-18 13:17:43 -04:00
func ( p * Page ) IsRenderable ( ) bool {
2014-01-29 17:50:31 -05:00
return p . renderable
2013-09-18 13:17:43 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) Type ( ) string {
if p . contentType != "" {
return p . contentType
2014-01-29 17:50:31 -05:00
}
2014-10-16 20:20:09 -04:00
2015-03-07 06:25:38 -05:00
if x := p . Section ( ) ; x != "" {
2014-01-29 17:50:31 -05:00
return x
}
2013-07-04 11:32:55 -04:00
2014-01-29 17:50:31 -05:00
return "page"
2013-07-04 11:32:55 -04:00
}
2017-04-09 04:33:04 -04:00
// Section returns the first path element below the content root. Note that
// since Hugo 0.22 we support nested sections, but this will always be the first
// element of any nested path.
2015-03-07 06:25:38 -05:00
func ( p * Page ) Section ( ) string {
2017-11-06 22:58:41 -05:00
if p . Kind == KindSection || p . Kind == KindTaxonomy || p . Kind == KindTaxonomyTerm {
2017-02-20 03:33:35 -05:00
return p . sections [ 0 ]
}
2015-03-07 06:25:38 -05:00
return p . Source . Section ( )
2014-10-16 20:20:09 -04:00
}
2017-01-03 11:28:51 -05:00
func ( s * Site ) NewPageFrom ( buf io . Reader , name string ) ( * Page , error ) {
p , err := s . NewPage ( name )
2014-05-01 14:11:56 -04:00
if err != nil {
return p , err
}
2015-04-03 15:41:12 -04:00
_ , err = p . ReadFrom ( buf )
2014-05-01 14:11:56 -04:00
return p , err
}
2017-01-03 11:28:51 -05:00
func ( s * Site ) NewPage ( name string ) ( * Page , error ) {
2014-01-29 17:50:31 -05:00
if len ( name ) == 0 {
return nil , errors . New ( "Zero length page name" )
}
2013-08-05 10:53:58 -04:00
2014-01-29 17:50:31 -05:00
// Create new page
2017-01-03 11:28:51 -05:00
p := s . newPage ( name )
p . s = s
p . Site = & s . Info
2013-08-05 10:53:58 -04:00
2014-05-01 13:20:58 -04:00
return p , nil
}
2015-04-03 15:41:12 -04:00
func ( p * Page ) ReadFrom ( buf io . Reader ) ( int64 , error ) {
2014-01-29 17:50:31 -05:00
// Parse for metadata & body
2015-04-03 15:41:12 -04:00
if err := p . parse ( buf ) ; err != nil {
2018-04-17 09:37:05 -04:00
p . s . Log . ERROR . Printf ( "%s for %s" , err , p . File . Path ( ) )
2015-04-03 15:41:12 -04:00
return 0 , err
2014-01-29 17:50:31 -05:00
}
2013-08-05 10:53:58 -04:00
2015-04-03 15:41:12 -04:00
return int64 ( len ( p . rawContent ) ) , nil
2013-07-04 11:32:55 -04:00
}
2016-08-17 07:41:48 -04:00
func ( p * Page ) WordCount ( ) int {
p . analyzePage ( )
return p . wordCount
}
func ( p * Page ) ReadingTime ( ) int {
p . analyzePage ( )
return p . readingTime
}
func ( p * Page ) FuzzyWordCount ( ) int {
p . analyzePage ( )
return p . fuzzyWordCount
}
2013-07-04 11:32:55 -04:00
func ( p * Page ) analyzePage ( ) {
2018-04-19 12:06:40 -04:00
p . initContent ( )
2016-08-17 07:41:48 -04:00
p . pageMetaInit . Do ( func ( ) {
if p . isCJKLanguage {
p . wordCount = 0
2018-04-19 12:06:40 -04:00
for _ , word := range p . plainWords {
2016-08-17 07:41:48 -04:00
runeCount := utf8 . RuneCountInString ( word )
if len ( word ) == runeCount {
p . wordCount ++
} else {
p . wordCount += runeCount
}
2015-09-03 06:22:20 -04:00
}
2016-08-17 07:41:48 -04:00
} else {
2018-04-19 12:06:40 -04:00
p . wordCount = helpers . TotalWords ( p . plain )
2015-09-03 06:22:20 -04:00
}
2015-09-03 06:22:20 -04:00
2016-08-17 07:41:48 -04:00
// TODO(bep) is set in a test. Fix that.
if p . fuzzyWordCount == 0 {
p . fuzzyWordCount = ( p . wordCount + 100 ) / 100 * 100
}
2015-09-03 06:22:20 -04:00
2016-08-17 07:41:48 -04:00
if p . isCJKLanguage {
p . readingTime = ( p . wordCount + 500 ) / 501
} else {
p . readingTime = ( p . wordCount + 212 ) / 213
}
} )
2013-07-04 11:32:55 -04:00
}
2017-07-17 17:20:13 -04:00
// HasShortcode return whether the page has a shortcode with the given name.
// This method is mainly motivated with the Hugo Docs site's need for a list
// of pages with the `todo` shortcode in it.
func ( p * Page ) HasShortcode ( name string ) bool {
if p . shortcodeState == nil {
return false
}
return p . shortcodeState . nameSet [ name ]
}
2016-07-25 16:22:09 -04:00
// AllTranslations returns all translations, including the current Page.
func ( p * Page ) AllTranslations ( ) Pages {
return p . translations
}
2016-08-09 08:26:55 -04:00
// IsTranslated returns whether this content file is translated to
// other language(s).
func ( p * Page ) IsTranslated ( ) bool {
return len ( p . translations ) > 1
}
2016-07-25 16:22:09 -04:00
// Translations returns the translations excluding the current Page.
func ( p * Page ) Translations ( ) Pages {
translations := make ( Pages , 0 )
for _ , t := range p . translations {
2017-02-19 09:00:18 -05:00
if t . Lang ( ) != p . Lang ( ) {
2016-07-25 16:22:09 -04:00
translations = append ( translations , t )
}
}
return translations
}
2017-11-17 10:28:35 -05:00
// TranslationKey returns the key used to map language translations of this page.
// It will use the translationKey set in front matter if set, or the content path and
// filename (excluding any language code and extension), e.g. "about/index".
// The Page Kind is always prepended.
func ( p * Page ) TranslationKey ( ) string {
if p . translationKey != "" {
return p . Kind + "/" + p . translationKey
}
if p . IsNode ( ) {
return path . Join ( p . Kind , path . Join ( p . sections ... ) , p . TranslationBaseName ( ) )
}
return path . Join ( p . Kind , filepath . ToSlash ( p . Dir ( ) ) , p . TranslationBaseName ( ) )
}
2013-10-25 18:37:53 -04:00
func ( p * Page ) LinkTitle ( ) string {
2014-01-29 17:50:31 -05:00
if len ( p . linkTitle ) > 0 {
return p . linkTitle
}
2018-01-15 14:40:39 -05:00
return p . title
2013-10-25 18:37:53 -04:00
}
2016-06-14 09:53:49 -04:00
func ( p * Page ) shouldBuild ( ) bool {
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
return shouldBuild ( p . s . BuildFuture , p . s . BuildExpired ,
p . s . BuildDrafts , p . Draft , p . PublishDate , p . ExpiryDate )
2016-05-11 11:45:09 -04:00
}
2016-06-14 09:53:49 -04:00
func shouldBuild ( buildFuture bool , buildExpired bool , buildDrafts bool , Draft bool ,
2016-05-11 11:45:09 -04:00
publishDate time . Time , expiryDate time . Time ) bool {
if ! ( buildDrafts || ! Draft ) {
return false
2014-05-29 00:48:40 -04:00
}
2016-05-11 11:45:09 -04:00
if ! buildFuture && ! publishDate . IsZero ( ) && publishDate . After ( time . Now ( ) ) {
return false
}
if ! buildExpired && ! expiryDate . IsZero ( ) && expiryDate . Before ( time . Now ( ) ) {
return false
}
return true
2014-05-29 00:48:40 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) IsDraft ( ) bool {
return p . Draft
2014-08-20 11:09:35 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) IsFuture ( ) bool {
2016-06-13 11:37:10 -04:00
if p . PublishDate . IsZero ( ) {
return false
}
2016-05-11 10:04:53 -04:00
return p . PublishDate . After ( time . Now ( ) )
}
func ( p * Page ) IsExpired ( ) bool {
2016-06-13 11:37:10 -04:00
if p . ExpiryDate . IsZero ( ) {
return false
}
2016-05-11 10:04:53 -04:00
return p . ExpiryDate . Before ( time . Now ( ) )
2014-08-20 11:09:35 -04:00
}
2016-09-14 12:51:34 -04:00
func ( p * Page ) URL ( ) string {
2016-11-15 04:43:49 -05:00
if p . IsPage ( ) && p . URLPath . URL != "" {
2016-09-14 12:51:34 -04:00
// This is the url set in front matter
return p . URLPath . URL
}
// Fall back to the relative permalink.
2016-11-15 04:43:49 -05:00
u := p . RelPermalink ( )
2016-09-14 12:51:34 -04:00
return u
}
2017-03-09 13:19:29 -05:00
// Permalink returns the absolute URL to this Page.
func ( p * Page ) Permalink ( ) string {
2018-01-23 08:02:54 -05:00
if p . headless {
return ""
}
2017-03-09 13:19:29 -05:00
return p . permalink
}
2016-11-15 04:43:49 -05:00
2017-03-09 13:19:29 -05:00
// RelPermalink gets a URL to the resource relative to the host.
func ( p * Page ) RelPermalink ( ) string {
2018-01-23 08:02:54 -05:00
if p . headless {
return ""
}
2017-03-09 13:19:29 -05:00
return p . relPermalink
}
2016-11-15 04:43:49 -05:00
2018-01-15 14:40:39 -05:00
// See resource.Resource
2018-01-20 11:11:03 -05:00
// This value is used, by default, in Resources.ByPrefix etc.
2018-01-15 14:40:39 -05:00
func ( p * Page ) Name ( ) string {
2018-01-20 11:11:03 -05:00
if p . resourcePath != "" {
return p . resourcePath
2018-01-15 14:40:39 -05:00
}
return p . title
}
func ( p * Page ) Title ( ) string {
return p . title
}
func ( p * Page ) Params ( ) map [ string ] interface { } {
return p . params
}
2018-01-10 04:20:08 -05:00
func ( p * Page ) subResourceTargetPathFactory ( base string ) string {
return path . Join ( p . relTargetPathBase , base )
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
}
2018-04-19 12:06:40 -04:00
func ( p * Page ) setContentInit ( cfg * BuildCfg ) error {
if ! p . shouldRenderTo ( p . s . rc . Format ) {
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
// No need to prepare
return nil
}
var shortcodeUpdate bool
if p . shortcodeState != nil {
shortcodeUpdate = p . shortcodeState . updateDelta ( )
}
2018-04-19 12:06:40 -04:00
resetFunc := func ( page * Page ) func ( ) {
return func ( ) {
err := page . prepareForRender ( cfg )
if err != nil {
p . s . Log . ERROR . Printf ( "Failed to prepare page %q for render: %s" , page . Path ( ) , err )
}
}
}
if shortcodeUpdate || cfg . whatChanged . other {
p . resetContent ( resetFunc )
}
// Handle bundled pages.
for _ , r := range p . Resources . ByType ( pageResourceType ) {
shortcodeUpdate = false
bp := r . ( * Page )
if bp . shortcodeState != nil {
shortcodeUpdate = bp . shortcodeState . updateDelta ( )
}
if shortcodeUpdate || cfg . whatChanged . other {
p . s . PathSpec . ProcessingStats . Incr ( & p . s . PathSpec . ProcessingStats . Pages )
bp . resetContent ( resetFunc )
}
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
}
2018-04-19 12:06:40 -04:00
return nil
}
func ( p * Page ) prepareForRender ( cfg * BuildCfg ) error {
s := p . s
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
// If we got this far it means that this is either a new Page pointer
// or a template or similar has changed so wee need to do a rerendering
// of the shortcodes etc.
// If in watch mode or if we have multiple output formats,
// we need to keep the original so we can
// potentially repeat this process on rebuild.
needsACopy := p . s . running ( ) || len ( p . outputFormats ) > 1
var workContentCopy [ ] byte
if needsACopy {
workContentCopy = make ( [ ] byte , len ( p . workContent ) )
copy ( workContentCopy , p . workContent )
} else {
// Just reuse the same slice.
workContentCopy = p . workContent
}
2017-03-23 15:05:10 -04:00
var err error
2018-04-19 12:06:40 -04:00
// Note: The shortcodes in a page cannot access the page content it lives in,
// hence the withoutContent().
if workContentCopy , err = handleShortcodes ( p . withoutContent ( ) , workContentCopy ) ; err != nil {
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
s . Log . ERROR . Printf ( "Failed to handle shortcodes for page %s: %s" , p . BaseFileName ( ) , err )
2017-03-23 15:05:10 -04:00
}
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
if p . Markup != "html" {
// Now we know enough to create a summary of the page and count some words
summaryContent , err := p . setUserDefinedSummaryIfProvided ( workContentCopy )
if err != nil {
s . Log . ERROR . Printf ( "Failed to set user defined summary for page %q: %s" , p . Path ( ) , err )
} else if summaryContent != nil {
workContentCopy = summaryContent . content
}
2018-04-19 12:06:40 -04:00
p . contentv = helpers . BytesToHTML ( workContentCopy )
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
} else {
2018-04-19 12:06:40 -04:00
p . contentv = helpers . BytesToHTML ( workContentCopy )
2018-04-19 11:40:54 -04:00
}
2017-03-17 11:35:09 -04:00
return nil
2013-10-02 20:00:21 -04:00
}
2015-08-02 02:02:20 -04:00
var ErrHasDraftAndPublished = errors . New ( "both draft and published parameters were found in page's frontmatter" )
2018-01-25 22:54:15 -05:00
func ( p * Page ) update ( frontmatter map [ string ] interface { } ) error {
if frontmatter == nil {
return errors . New ( "missing frontmatter data" )
2014-05-01 14:11:56 -04:00
}
2016-10-24 16:29:48 -04:00
// Needed for case insensitive fetching of params values
2018-01-25 22:54:15 -05:00
helpers . ToLowerMap ( frontmatter )
2016-10-24 16:29:48 -04:00
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
var mtime time . Time
if p . Source . FileInfo ( ) != nil {
mtime = p . Source . FileInfo ( ) . ModTime ( )
}
2018-03-11 13:59:11 -04:00
var gitAuthorDate time . Time
if p . GitInfo != nil {
gitAuthorDate = p . GitInfo . AuthorDate
}
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
descriptor := & pagemeta . FrontMatterDescriptor {
2018-03-11 13:59:11 -04:00
Frontmatter : frontmatter ,
Params : p . params ,
Dates : & p . PageDates ,
PageURLs : & p . URLPath ,
BaseFilename : p . BaseFileName ( ) ,
ModTime : mtime ,
GitAuthorDate : gitAuthorDate ,
}
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
// Handle the date separately
// TODO(bep) we need to "do more" in this area so this can be split up and
// more easily tested without the Page, but the coupling is strong.
err := p . s . frontmatterHandler . HandleDates ( descriptor )
if err != nil {
p . s . Log . ERROR . Printf ( "Failed to handle dates for page %q: %s" , p . Path ( ) , err )
}
2017-09-08 06:28:15 -04:00
2015-09-03 06:22:20 -04:00
var draft , published , isCJKLanguage * bool
2018-01-25 22:54:15 -05:00
for k , v := range frontmatter {
2014-01-29 17:50:31 -05:00
loki := strings . ToLower ( k )
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
if loki == "published" { // Intentionally undocumented
vv , err := cast . ToBoolE ( v )
if err == nil {
published = & vv
}
// published may also be a date
continue
}
if p . s . frontmatterHandler . IsDateKey ( loki ) {
continue
}
2014-01-29 17:50:31 -05:00
switch loki {
case "title" :
2018-01-15 14:40:39 -05:00
p . title = cast . ToString ( v )
p . params [ loki ] = p . title
2014-01-29 17:50:31 -05:00
case "linktitle" :
2015-03-07 06:25:38 -05:00
p . linkTitle = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . linkTitle
2014-01-29 17:50:31 -05:00
case "description" :
2015-03-07 06:25:38 -05:00
p . Description = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Description
2014-01-29 17:50:31 -05:00
case "slug" :
2015-04-09 12:14:26 -04:00
p . Slug = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Slug
2014-01-29 17:50:31 -05:00
case "url" :
2014-04-05 01:26:43 -04:00
if url := cast . ToString ( v ) ; strings . HasPrefix ( url , "http://" ) || strings . HasPrefix ( url , "https://" ) {
2015-03-18 01:16:54 -04:00
return fmt . Errorf ( "Only relative URLs are supported, %v provided" , url )
2014-01-29 17:50:31 -05:00
}
2016-07-26 13:04:10 -04:00
p . URLPath . URL = cast . ToString ( v )
hugolib: Extract date and slug from filename
This commit makes it possible to extract the date from the content filename. Also, the filenames in these cases will make for very poor permalinks, so we will also use the remaining part as the page `slug` if that value is not set in front matter.
This should make it easier to move content from Jekyll to Hugo.
To enable, put this in your `config.toml`:
```toml
[frontmatter]
date = [":filename", ":default"]
```
This commit is also a spring cleaning of how the different dates are configured in Hugo. Hugo will check for dates following the configuration from left to right, starting with `:filename` etc.
So, if you want to use the `file modification time`, this can be a good configuration:
```toml
[frontmatter]
date = [ "date",":fileModTime", ":default"]
lastmod = ["lastmod" ,":fileModTime", ":default"]
```
The current `:default` values for the different dates are
```toml
[frontmatter]
date = ["date","publishDate", "lastmod"]
lastmod = ["lastmod", "date","publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
```
The above will now be the same as:
```toml
[frontmatter]
date = [":default"]
lastmod = [":default"]
publishDate = [":default"]
expiryDate = [":default"]
```
Note:
* We have some built-in aliases to the above: lastmod => modified, publishDate => pubdate, published and expiryDate => unpublishdate.
* If you want a new configuration for, say, `date`, you can provide only that line, and the rest will be preserved.
* All the keywords to the right that does not start with a ":" maps to front matter parameters, and can be any date param (e.g. `myCustomDateParam`).
* The keywords to the left are the **4 predefined dates in Hugo**, i.e. they are constant values.
* The current "special date handlers" are `:fileModTime` and `:filename`. We will soon add `:git` to that list.
Fixes #285
Closes #3310
Closes #3762
Closes #4340
2018-03-11 06:32:55 -04:00
p . frontMatterURL = p . URLPath . URL
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . URLPath . URL
2014-01-29 17:50:31 -05:00
case "type" :
2015-03-07 06:25:38 -05:00
p . contentType = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . contentType
2014-10-16 20:20:09 -04:00
case "extension" , "ext" :
2015-03-07 06:25:38 -05:00
p . extension = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . extension
2014-01-29 17:50:31 -05:00
case "keywords" :
2015-03-07 06:25:38 -05:00
p . Keywords = cast . ToStringSlice ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Keywords
2018-01-23 08:02:54 -05:00
case "headless" :
// For now, only the leaf bundles ("index.md") can be headless (i.e. produce no output).
// We may expand on this in the future, but that gets more complex pretty fast.
if p . TranslationBaseName ( ) == "index" {
p . headless = cast . ToBool ( v )
}
p . params [ loki ] = p . headless
2017-03-08 07:45:33 -05:00
case "outputs" :
2017-03-19 16:09:31 -04:00
o := cast . ToStringSlice ( v )
if len ( o ) > 0 {
2017-03-16 03:32:14 -04:00
// Output formats are exlicitly set in front matter, use those.
2017-04-04 03:12:33 -04:00
outFormats , err := p . s . outputFormatsConfig . GetByNames ( o ... )
2017-03-19 16:09:31 -04:00
2017-03-08 07:45:33 -05:00
if err != nil {
2017-03-16 03:32:14 -04:00
p . s . Log . ERROR . Printf ( "Failed to resolve output formats: %s" , err )
2017-03-08 07:45:33 -05:00
} else {
2017-03-16 03:32:14 -04:00
p . outputFormats = outFormats
2018-01-15 14:40:39 -05:00
p . params [ loki ] = outFormats
2017-03-08 07:45:33 -05:00
}
}
2014-01-29 17:50:31 -05:00
case "draft" :
2015-08-02 02:02:20 -04:00
draft = new ( bool )
* draft = cast . ToBool ( v )
2014-01-29 17:50:31 -05:00
case "layout" :
2015-11-02 11:24:50 -05:00
p . Layout = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Layout
2014-01-29 17:50:31 -05:00
case "markup" :
2015-03-07 06:25:38 -05:00
p . Markup = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Markup
2014-01-29 17:50:31 -05:00
case "weight" :
2015-03-07 06:25:38 -05:00
p . Weight = cast . ToInt ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Weight
2014-01-29 17:50:31 -05:00
case "aliases" :
2015-03-07 06:25:38 -05:00
p . Aliases = cast . ToStringSlice ( v )
for _ , alias := range p . Aliases {
2014-01-29 17:50:31 -05:00
if strings . HasPrefix ( alias , "http://" ) || strings . HasPrefix ( alias , "https://" ) {
return fmt . Errorf ( "Only relative aliases are supported, %v provided" , alias )
}
}
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Aliases
2014-01-29 17:50:31 -05:00
case "status" :
2015-03-07 06:25:38 -05:00
p . Status = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Status
2014-05-06 11:02:56 -04:00
case "sitemap" :
2015-03-07 06:25:38 -05:00
p . Sitemap = parseSitemap ( cast . ToStringMap ( v ) )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . Sitemap
2015-09-03 06:22:20 -04:00
case "iscjklanguage" :
isCJKLanguage = new ( bool )
* isCJKLanguage = cast . ToBool ( v )
2017-11-17 10:28:35 -05:00
case "translationkey" :
p . translationKey = cast . ToString ( v )
2018-01-15 14:40:39 -05:00
p . params [ loki ] = p . translationKey
case "resources" :
var resources [ ] map [ string ] interface { }
handled := true
switch vv := v . ( type ) {
case [ ] map [ interface { } ] interface { } :
for _ , vvv := range vv {
resources = append ( resources , cast . ToStringMap ( vvv ) )
}
case [ ] map [ string ] interface { } :
for _ , vvv := range vv {
resources = append ( resources , vvv )
}
case [ ] interface { } :
for _ , vvv := range vv {
switch vvvv := vvv . ( type ) {
case map [ interface { } ] interface { } :
resources = append ( resources , cast . ToStringMap ( vvvv ) )
case map [ string ] interface { } :
resources = append ( resources , vvvv )
}
}
default :
handled = false
}
if handled {
p . params [ loki ] = resources
p . resourcesMetadata = resources
break
}
fallthrough
2014-01-29 17:50:31 -05:00
default :
// If not one of the explicit values, store in Params
switch vv := v . ( type ) {
case bool :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
case string :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
case int64 , int32 , int16 , int8 , int :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
case float64 , float32 :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
case time . Time :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
default : // handle array of strings as well
switch vvv := vv . ( type ) {
case [ ] interface { } :
2015-06-25 05:46:09 -04:00
if len ( vvv ) > 0 {
switch vvv [ 0 ] . ( type ) {
2015-07-26 09:28:56 -04:00
case map [ interface { } ] interface { } : // Proper parsing structured array from YAML based FrontMatter
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vvv
2015-07-26 09:28:56 -04:00
case map [ string ] interface { } : // Proper parsing structured array from JSON based FrontMatter
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vvv
2016-12-27 21:08:24 -05:00
case [ ] interface { } :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vvv
2015-06-25 05:46:09 -04:00
default :
a := make ( [ ] string , len ( vvv ) )
for i , u := range vvv {
a [ i ] = cast . ToString ( u )
}
2018-01-15 14:40:39 -05:00
p . params [ loki ] = a
2015-06-25 05:46:09 -04:00
}
} else {
2018-01-15 14:40:39 -05:00
p . params [ loki ] = [ ] string { }
2014-01-29 17:50:31 -05:00
}
2014-04-23 02:55:43 -04:00
default :
2018-01-15 14:40:39 -05:00
p . params [ loki ] = vv
2014-01-29 17:50:31 -05:00
}
}
}
}
2015-05-14 16:06:36 -04:00
2015-08-02 02:02:20 -04:00
if draft != nil && published != nil {
p . Draft = * draft
2017-01-10 04:55:03 -05:00
p . s . Log . ERROR . Printf ( "page %s has both draft and published settings in its frontmatter. Using draft." , p . File . Path ( ) )
2015-08-02 02:02:20 -04:00
return ErrHasDraftAndPublished
} else if draft != nil {
p . Draft = * draft
} else if published != nil {
p . Draft = ! * published
}
2018-01-15 14:40:39 -05:00
p . params [ "draft" ] = p . Draft
2015-08-02 02:02:20 -04:00
2015-09-03 06:22:20 -04:00
if isCJKLanguage != nil {
p . isCJKLanguage = * isCJKLanguage
2017-02-04 22:20:06 -05:00
} else if p . s . Cfg . GetBool ( "hasCJKLanguage" ) {
2015-09-03 06:22:20 -04:00
if cjk . Match ( p . rawContent ) {
p . isCJKLanguage = true
} else {
p . isCJKLanguage = false
}
}
2018-01-15 14:40:39 -05:00
p . params [ "iscjklanguage" ] = p . isCJKLanguage
2015-09-03 06:22:20 -04:00
2014-01-29 17:50:31 -05:00
return nil
2013-07-04 11:32:55 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) GetParam ( key string ) interface { } {
2017-12-29 02:58:38 -05:00
return p . getParam ( key , false )
}
func ( p * Page ) getParamToLower ( key string ) interface { } {
2015-05-31 14:30:53 -04:00
return p . getParam ( key , true )
}
func ( p * Page ) getParam ( key string , stringToLower bool ) interface { } {
2018-01-15 14:40:39 -05:00
v := p . params [ strings . ToLower ( key ) ]
2014-01-29 17:50:31 -05:00
if v == nil {
return nil
}
2016-05-14 00:35:16 -04:00
switch val := v . ( type ) {
2014-01-29 17:50:31 -05:00
case bool :
2016-05-14 00:35:16 -04:00
return val
case string :
if stringToLower {
return strings . ToLower ( val )
}
return val
2014-01-29 17:50:31 -05:00
case int64 , int32 , int16 , int8 , int :
2014-04-05 01:26:43 -04:00
return cast . ToInt ( v )
2014-01-29 17:50:31 -05:00
case float64 , float32 :
2014-04-05 01:26:43 -04:00
return cast . ToFloat64 ( v )
2016-05-14 00:35:16 -04:00
case time . Time :
return val
2014-01-29 17:50:31 -05:00
case [ ] string :
2015-05-31 14:30:53 -04:00
if stringToLower {
2016-05-14 00:35:16 -04:00
return helpers . SliceToLower ( val )
2015-05-31 14:30:53 -04:00
}
2014-11-28 15:16:57 -05:00
return v
2016-05-14 00:35:16 -04:00
case map [ string ] interface { } : // JSON and TOML
return v
case map [ interface { } ] interface { } : // YAML
return v
2014-01-29 17:50:31 -05:00
}
2015-01-24 06:44:35 -05:00
2017-01-10 04:55:03 -05:00
p . s . Log . ERROR . Printf ( "GetParam(\"%s\"): Unknown type %s\n" , key , reflect . TypeOf ( v ) )
2014-01-29 17:50:31 -05:00
return nil
2013-07-04 11:32:55 -04:00
}
2016-11-13 08:27:10 -05:00
func ( p * Page ) HasMenuCurrent ( menuID string , me * MenuEntry ) bool {
2016-11-23 16:06:15 -05:00
sectionPagesMenu := p . Site . sectionPagesMenu
2015-01-06 12:11:06 -05:00
// page is labeled as "shadow-member" of the menu with the same identifier as the section
2017-02-20 03:33:35 -05:00
if sectionPagesMenu != "" {
section := p . Section ( )
if section != "" && sectionPagesMenu == menuID && section == me . Identifier {
return true
}
2015-01-06 12:11:06 -05:00
}
2014-04-23 02:59:19 -04:00
2016-11-13 08:27:10 -05:00
if ! me . HasChildren ( ) {
return false
}
menus := p . Menus ( )
if m , ok := menus [ menuID ] ; ok {
for _ , child := range me . Children {
if child . IsEqual ( m ) {
return true
2014-04-23 02:59:19 -04:00
}
2016-11-13 08:27:10 -05:00
if p . HasMenuCurrent ( menuID , child ) {
return true
}
}
}
if p . IsPage ( ) {
return false
}
// The following logic is kept from back when Hugo had both Page and Node types.
// TODO(bep) consolidate / clean
2018-01-15 14:40:39 -05:00
nme := MenuEntry { Page : p , Name : p . title , URL : p . URL ( ) }
2016-11-13 08:27:10 -05:00
for _ , child := range me . Children {
if nme . IsSameResource ( child ) {
return true
}
if p . HasMenuCurrent ( menuID , child ) {
return true
2014-04-23 02:59:19 -04:00
}
}
return false
}
2016-11-13 08:27:10 -05:00
func ( p * Page ) IsMenuCurrent ( menuID string , inme * MenuEntry ) bool {
2015-03-07 06:25:38 -05:00
menus := p . Menus ( )
2014-04-23 02:59:19 -04:00
2016-11-13 08:27:10 -05:00
if me , ok := menus [ menuID ] ; ok {
if me . IsEqual ( inme ) {
return true
}
}
if p . IsPage ( ) {
return false
}
// The following logic is kept from back when Hugo had both Page and Node types.
// TODO(bep) consolidate / clean
2018-01-15 14:40:39 -05:00
me := MenuEntry { Page : p , Name : p . title , URL : p . URL ( ) }
2016-11-13 08:27:10 -05:00
if ! me . IsSameResource ( inme ) {
return false
}
// this resource may be included in several menus
// search for it to make sure that it is in the menu with the given menuId
if menu , ok := ( * p . Site . Menus ) [ menuID ] ; ok {
for _ , menuEntry := range * menu {
if menuEntry . IsSameResource ( inme ) {
return true
}
descendantFound := p . isSameAsDescendantMenu ( inme , menuEntry )
if descendantFound {
return descendantFound
}
}
2014-04-23 02:59:19 -04:00
}
return false
}
2016-11-13 08:27:10 -05:00
func ( p * Page ) isSameAsDescendantMenu ( inme * MenuEntry , parent * MenuEntry ) bool {
if parent . HasChildren ( ) {
for _ , child := range parent . Children {
if child . IsSameResource ( inme ) {
return true
}
descendantFound := p . isSameAsDescendantMenu ( inme , child )
if descendantFound {
return descendantFound
}
}
}
return false
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) Menus ( ) PageMenus {
p . pageMenusInit . Do ( func ( ) {
p . pageMenus = PageMenus { }
2014-04-23 02:59:19 -04:00
2018-01-15 14:40:39 -05:00
if ms , ok := p . params [ "menu" ] ; ok {
2016-11-15 04:43:49 -05:00
link := p . RelPermalink ( )
2014-04-23 02:59:19 -04:00
2017-12-15 12:49:21 -05:00
me := MenuEntry { Page : p , Name : p . LinkTitle ( ) , Weight : p . Weight , URL : link }
2014-04-23 02:59:19 -04:00
2015-02-04 15:27:27 -05:00
// Could be the name of the menu to attach it to
mname , err := cast . ToStringE ( ms )
2014-04-23 02:59:19 -04:00
2015-02-04 15:27:27 -05:00
if err == nil {
2014-04-23 02:59:19 -04:00
me . Menu = mname
2015-03-07 06:25:38 -05:00
p . pageMenus [ mname ] = & me
2015-02-04 15:27:27 -05:00
return
2014-04-23 02:59:19 -04:00
}
2015-02-04 15:27:27 -05:00
// Could be a slice of strings
mnames , err := cast . ToStringSliceE ( ms )
2014-04-23 02:59:19 -04:00
2015-02-04 15:27:27 -05:00
if err == nil {
for _ , mname := range mnames {
me . Menu = mname
2015-03-07 06:25:38 -05:00
p . pageMenus [ mname ] = & me
2015-02-04 15:27:27 -05:00
}
2016-03-10 04:31:12 -05:00
return
2015-02-04 15:27:27 -05:00
}
2014-04-23 02:59:19 -04:00
2015-02-04 15:27:27 -05:00
// Could be a structured menu entry
menus , err := cast . ToStringMapE ( ms )
2014-04-23 02:59:19 -04:00
if err != nil {
2018-01-15 14:40:39 -05:00
p . s . Log . ERROR . Printf ( "unable to process menus for %q\n" , p . title )
2014-04-23 02:59:19 -04:00
}
2015-02-04 15:27:27 -05:00
for name , menu := range menus {
2017-12-15 12:49:21 -05:00
menuEntry := MenuEntry { Page : p , Name : p . LinkTitle ( ) , URL : link , Weight : p . Weight , Menu : name }
2016-03-10 04:31:12 -05:00
if menu != nil {
2018-01-15 14:40:39 -05:00
p . s . Log . DEBUG . Printf ( "found menu: %q, in %q\n" , name , p . title )
2016-03-10 04:31:12 -05:00
ime , err := cast . ToStringMapE ( menu )
if err != nil {
2018-01-15 14:40:39 -05:00
p . s . Log . ERROR . Printf ( "unable to process menus for %q: %s" , p . title , err )
2016-03-10 04:31:12 -05:00
}
2015-02-04 15:27:27 -05:00
2016-03-22 19:29:39 -04:00
menuEntry . marshallMap ( ime )
2015-02-04 15:27:27 -05:00
}
2015-03-07 06:25:38 -05:00
p . pageMenus [ name ] = & menuEntry
2016-07-28 03:30:58 -04:00
2015-02-04 15:27:27 -05:00
}
2014-04-23 02:59:19 -04:00
}
2015-02-04 15:27:27 -05:00
} )
2014-04-23 02:59:19 -04:00
2015-03-07 06:25:38 -05:00
return p . pageMenus
2014-04-23 02:59:19 -04:00
}
2017-05-06 14:15:28 -04:00
func ( p * Page ) shouldRenderTo ( f output . Format ) bool {
_ , found := p . outputFormats . GetByName ( f . Name )
return found
}
2016-03-20 16:40:03 -04:00
func ( p * Page ) determineMarkupType ( ) string {
// Try markup explicitly set in the frontmatter
p . Markup = helpers . GuessType ( p . Markup )
if p . Markup == "unknown" {
// Fall back to file extension (might also return "unknown")
p . Markup = helpers . GuessType ( p . Source . Ext ( ) )
2014-01-29 17:50:31 -05:00
}
2013-09-18 17:21:27 -04:00
2016-03-20 16:40:03 -04:00
return p . Markup
2013-12-06 23:56:51 -05:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) parse ( reader io . Reader ) error {
2014-05-01 13:19:51 -04:00
psr , err := parser . ReadFrom ( reader )
2014-01-29 17:50:31 -05:00
if err != nil {
return err
}
2013-07-04 11:32:55 -04:00
2015-03-07 06:25:38 -05:00
p . renderable = psr . IsRenderable ( )
p . frontmatter = psr . FrontMatter ( )
2015-09-03 06:22:20 -04:00
p . rawContent = psr . Content ( )
2016-05-14 00:35:16 -04:00
p . lang = p . Source . File . Lang ( )
2015-09-03 06:22:20 -04:00
2014-05-01 13:19:51 -04:00
meta , err := psr . Metadata ( )
2017-06-19 08:41:03 -04:00
if err != nil {
return fmt . Errorf ( "failed to parse page metadata for %q: %s" , p . File . Path ( ) , err )
}
2018-01-25 22:54:15 -05:00
if meta == nil {
// missing frontmatter equivalent to empty frontmatter
meta = map [ string ] interface { } { }
2014-05-01 13:19:51 -04:00
}
2013-09-18 13:17:43 -04:00
2018-03-11 13:59:11 -04:00
if p . s != nil && p . s . owner != nil {
gi , enabled := p . s . owner . gitInfo . forPage ( p )
if gi != nil {
p . GitInfo = gi
} else if enabled {
p . s . Log . WARN . Printf ( "Failed to find GitInfo for page %q" , p . Path ( ) )
}
}
2018-01-25 22:54:15 -05:00
return p . update ( meta )
2014-05-01 13:19:51 -04:00
}
2013-09-18 13:17:43 -04:00
2015-07-02 09:32:57 -04:00
func ( p * Page ) RawContent ( ) string {
return string ( p . rawContent )
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) SetSourceContent ( content [ ] byte ) {
p . Source . Content = content
2014-05-01 13:19:51 -04:00
}
2013-12-06 23:32:00 -05:00
2015-03-07 06:25:38 -05:00
func ( p * Page ) SetSourceMetaData ( in interface { } , mark rune ) ( err error ) {
2017-06-13 12:47:17 -04:00
// See https://github.com/gohugoio/hugo/issues/2458
2016-09-19 04:52:07 -04:00
defer func ( ) {
if r := recover ( ) ; r != nil {
var ok bool
err , ok = r . ( error )
if ! ok {
err = fmt . Errorf ( "error from marshal: %v" , r )
}
}
} ( )
2016-12-26 16:23:20 -05:00
buf := bp . GetBuffer ( )
defer bp . PutBuffer ( buf )
2016-09-19 04:52:07 -04:00
2016-12-26 16:23:20 -05:00
err = parser . InterfaceToFrontMatter ( in , mark , buf )
2014-05-01 13:19:51 -04:00
if err != nil {
2016-09-19 04:52:07 -04:00
return
2014-01-29 17:50:31 -05:00
}
2014-05-01 13:19:51 -04:00
2016-12-26 16:23:20 -05:00
_ , err = buf . WriteRune ( '\n' )
if err != nil {
return
}
p . Source . Frontmatter = buf . Bytes ( )
2013-12-06 23:32:00 -05:00
2016-09-19 04:52:07 -04:00
return
2013-12-06 23:32:00 -05:00
}
2013-08-25 00:27:41 -04:00
2015-03-07 06:25:38 -05:00
func ( p * Page ) SafeSaveSourceAs ( path string ) error {
return p . saveSourceAs ( path , true )
2014-05-02 01:04:48 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) SaveSourceAs ( path string ) error {
return p . saveSourceAs ( path , false )
2014-05-02 01:04:48 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) saveSourceAs ( path string , safe bool ) error {
2015-01-30 14:42:02 -05:00
b := bp . GetBuffer ( )
defer bp . PutBuffer ( b )
2015-03-07 06:25:38 -05:00
b . Write ( p . Source . Frontmatter )
b . Write ( p . Source . Content )
2014-05-01 13:21:37 -04:00
2015-01-30 14:42:02 -05:00
bc := make ( [ ] byte , b . Len ( ) , b . Len ( ) )
copy ( bc , b . Bytes ( ) )
2016-11-23 12:28:14 -05:00
return p . saveSource ( bc , path , safe )
2014-05-01 13:21:37 -04:00
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) saveSource ( by [ ] byte , inpath string , safe bool ) ( err error ) {
2014-11-06 11:52:01 -05:00
if ! filepath . IsAbs ( inpath ) {
2017-02-04 22:20:06 -05:00
inpath = p . s . PathSpec . AbsPathify ( inpath )
2014-05-01 13:21:37 -04:00
}
2017-01-10 04:55:03 -05:00
p . s . Log . INFO . Println ( "creating" , inpath )
2014-05-02 01:04:48 -04:00
if safe {
2017-01-10 04:55:03 -05:00
err = helpers . SafeWriteToDisk ( inpath , bytes . NewReader ( by ) , p . s . Fs . Source )
2014-05-02 01:04:48 -04:00
} else {
2017-01-10 04:55:03 -05:00
err = helpers . WriteToDisk ( inpath , bytes . NewReader ( by ) , p . s . Fs . Source )
2014-05-02 01:04:48 -04:00
}
if err != nil {
return
}
2014-05-01 13:21:37 -04:00
return nil
}
2015-03-07 06:25:38 -05:00
func ( p * Page ) SaveSource ( ) error {
return p . SaveSourceAs ( p . FullFilePath ( ) )
2014-05-01 13:21:37 -04:00
}
2018-04-19 12:06:40 -04:00
// TODO(bep) lazy consolidate
2017-05-06 04:48:27 -04:00
func ( p * Page ) processShortcodes ( ) error {
2017-05-06 14:15:28 -04:00
p . shortcodeState = newShortcodeHandler ( p )
2018-04-19 12:06:40 -04:00
tmpContent , err := p . shortcodeState . extractShortcodes ( string ( p . workContent ) , p . withoutContent ( ) )
2017-05-06 04:48:27 -04:00
if err != nil {
return err
}
2016-12-01 04:21:49 -05:00
p . workContent = [ ] byte ( tmpContent )
2017-03-10 14:54:50 -05:00
2017-05-06 04:48:27 -04:00
return nil
2014-01-27 17:16:28 -05:00
}
2014-05-01 13:21:37 -04:00
func ( p * Page ) FullFilePath ( ) string {
2015-05-31 12:54:50 -04:00
return filepath . Join ( p . Dir ( ) , p . LogicalName ( ) )
2014-05-01 13:21:37 -04:00
}
2016-10-31 05:23:01 -04:00
// Pre render prepare steps
func ( p * Page ) prepareLayouts ( ) error {
// TODO(bep): Check the IsRenderable logic.
2016-11-13 05:43:23 -05:00
if p . Kind == KindPage {
2016-10-31 05:23:01 -04:00
if ! p . IsRenderable ( ) {
2017-03-08 07:45:33 -05:00
self := "__" + p . UniqueID ( )
2018-04-19 12:06:40 -04:00
err := p . s . TemplateHandler ( ) . AddLateTemplate ( self , string ( p . content ( ) ) )
2016-10-31 05:23:01 -04:00
if err != nil {
return err
}
2017-03-25 13:28:38 -04:00
p . selfLayout = self
2016-10-31 05:23:01 -04:00
}
}
2017-03-27 14:43:49 -04:00
2016-10-31 05:23:01 -04:00
return nil
}
2016-10-31 13:03:02 -04:00
func ( p * Page ) prepareData ( s * Site ) error {
2017-04-09 04:33:04 -04:00
if p . Kind != KindSection {
var pages Pages
p . Data = make ( map [ string ] interface { } )
switch p . Kind {
case KindPage :
case KindHome :
pages = s . RegularPages
case KindTaxonomy :
plural := p . sections [ 0 ]
term := p . sections [ 1 ]
if s . Info . preserveTaxonomyNames {
if v , ok := s . taxonomiesOrigKey [ fmt . Sprintf ( "%s-%s" , plural , term ) ] ; ok {
term = v
}
2016-12-26 13:30:57 -05:00
}
2017-04-09 04:33:04 -04:00
singular := s . taxonomiesPluralSingular [ plural ]
taxonomy := s . Taxonomies [ plural ] . Get ( term )
p . Data [ singular ] = taxonomy
p . Data [ "Singular" ] = singular
p . Data [ "Plural" ] = plural
p . Data [ "Term" ] = term
pages = taxonomy . Pages ( )
case KindTaxonomyTerm :
plural := p . sections [ 0 ]
singular := s . taxonomiesPluralSingular [ plural ]
p . Data [ "Singular" ] = singular
p . Data [ "Plural" ] = plural
p . Data [ "Terms" ] = s . Taxonomies [ plural ]
// keep the following just for legacy reasons
p . Data [ "OrderedIndex" ] = p . Data [ "Terms" ]
p . Data [ "Index" ] = p . Data [ "Terms" ]
// A list of all KindTaxonomy pages with matching plural
for _ , p := range s . findPagesByKind ( KindTaxonomy ) {
if p . sections [ 0 ] == plural {
pages = append ( pages , p )
}
2017-03-05 15:24:14 -05:00
}
}
2016-10-31 14:53:33 -04:00
2017-04-09 04:33:04 -04:00
p . Data [ "Pages" ] = pages
p . Pages = pages
}
2016-11-11 03:19:16 -05:00
2016-11-11 05:35:55 -05:00
// Now we know enough to set missing dates on home page etc.
p . updatePageDates ( )
2016-10-31 05:23:01 -04:00
return nil
}
2016-11-11 05:35:55 -05:00
func ( p * Page ) updatePageDates ( ) {
2016-11-16 15:06:10 -05:00
// TODO(bep) there is a potential issue with page sorting for home pages
2016-11-11 05:35:55 -05:00
// etc. without front matter dates set, but let us wrap the head around
// that in another time.
2016-11-13 06:33:11 -05:00
if ! p . IsNode ( ) {
2016-11-11 05:35:55 -05:00
return
}
if ! p . Date . IsZero ( ) {
if p . Lastmod . IsZero ( ) {
p . Lastmod = p . Date
}
return
} else if ! p . Lastmod . IsZero ( ) {
if p . Date . IsZero ( ) {
p . Date = p . Lastmod
}
return
}
// Set it to the first non Zero date in children
var foundDate , foundLastMod bool
for _ , child := range p . Pages {
if ! child . Date . IsZero ( ) {
p . Date = child . Date
foundDate = true
}
if ! child . Lastmod . IsZero ( ) {
p . Lastmod = child . Lastmod
foundLastMod = true
}
if foundDate && foundLastMod {
break
}
}
}
2016-11-16 14:32:38 -05:00
// copy creates a copy of this page with the lazy sync.Once vars reset
// so they will be evaluated again, for word count calculations etc.
2016-10-31 05:23:01 -04:00
func ( p * Page ) copy ( ) * Page {
2018-04-19 12:06:40 -04:00
p . contentInitMu . Lock ( )
2016-11-16 14:32:38 -05:00
c := * p
2018-04-19 12:06:40 -04:00
p . contentInitMu . Unlock ( )
2017-04-08 12:04:12 -04:00
c . pageInit = & pageInit { }
2018-04-19 12:06:40 -04:00
c . pageContentInit = & pageContentInit { }
2016-11-16 14:32:38 -05:00
return & c
2016-10-31 05:23:01 -04:00
}
2016-11-13 08:27:10 -05:00
func ( p * Page ) Hugo ( ) * HugoInfo {
return hugoInfo
}
2017-04-04 12:14:41 -04:00
func ( p * Page ) Ref ( refs ... string ) ( string , error ) {
if len ( refs ) == 0 {
return "" , nil
}
if len ( refs ) > 1 {
return p . Site . Ref ( refs [ 0 ] , nil , refs [ 1 ] )
}
return p . Site . Ref ( refs [ 0 ] , nil )
2016-11-13 08:27:10 -05:00
}
2017-04-04 12:14:41 -04:00
func ( p * Page ) RelRef ( refs ... string ) ( string , error ) {
if len ( refs ) == 0 {
return "" , nil
}
if len ( refs ) > 1 {
return p . Site . RelRef ( refs [ 0 ] , nil , refs [ 1 ] )
}
return p . Site . RelRef ( refs [ 0 ] , nil )
2016-11-13 08:27:10 -05:00
}
func ( p * Page ) String ( ) string {
2018-01-15 14:40:39 -05:00
return fmt . Sprintf ( "Page(%q)" , p . title )
2016-11-13 08:27:10 -05:00
}
// Scratch returns the writable context associated with this Page.
func ( p * Page ) Scratch ( ) * Scratch {
if p . scratch == nil {
p . scratch = newScratch ( )
}
return p . scratch
}
func ( p * Page ) Language ( ) * helpers . Language {
p . initLanguage ( )
return p . language
}
func ( p * Page ) Lang ( ) string {
// When set, Language can be different from lang in the case where there is a
// content file (doc.sv.md) with language indicator, but there is no language
// config for that language. Then the language will fall back on the site default.
if p . Language ( ) != nil {
return p . Language ( ) . Lang
}
return p . lang
}
2016-11-21 04:11:34 -05:00
func ( p * Page ) isNewTranslation ( candidate * Page ) bool {
2016-12-23 03:52:05 -05:00
if p . Kind != candidate . Kind {
2016-11-13 08:27:10 -05:00
return false
}
if p . Kind == KindPage || p . Kind == kindUnknown {
panic ( "Node type not currently supported for this op" )
}
// At this point, we know that this is a traditional Node (home page, section, taxonomy)
// It represents the same node, but different language, if the sections is the same.
if len ( p . sections ) != len ( candidate . sections ) {
return false
}
for i := 0 ; i < len ( p . sections ) ; i ++ {
if p . sections [ i ] != candidate . sections [ i ] {
return false
}
}
2016-11-21 04:11:34 -05:00
// Finally check that it is not already added.
2016-12-23 03:52:05 -05:00
for _ , translation := range p . translations {
if candidate == translation {
2016-11-21 04:11:34 -05:00
return false
}
}
2016-11-13 08:27:10 -05:00
return true
}
func ( p * Page ) shouldAddLanguagePrefix ( ) bool {
if ! p . Site . IsMultiLingual ( ) {
return false
}
2017-11-02 03:25:20 -04:00
if p . s . owner . IsMultihost ( ) {
return true
}
2016-11-13 08:27:10 -05:00
if p . Lang ( ) == "" {
return false
}
if ! p . Site . defaultContentLanguageInSubdir && p . Lang ( ) == p . Site . multilingual . DefaultLang . Lang {
return false
}
return true
}
func ( p * Page ) initLanguage ( ) {
p . languageInit . Do ( func ( ) {
if p . language != nil {
return
}
2017-02-04 22:20:06 -05:00
2016-11-13 08:27:10 -05:00
ml := p . Site . multilingual
if ml == nil {
panic ( "Multilanguage not set" )
}
2017-02-04 22:20:06 -05:00
if p . lang == "" {
p . lang = ml . DefaultLang . Lang
2016-11-13 08:27:10 -05:00
p . language = ml . DefaultLang
return
}
2017-02-04 22:20:06 -05:00
language := ml . Language ( p . lang )
2016-11-13 08:27:10 -05:00
if language == nil {
// It can be a file named stefano.chiodino.md.
2017-02-04 22:20:06 -05:00
p . s . Log . WARN . Printf ( "Page language (if it is that) not found in multilang setup: %s." , p . lang )
2016-11-13 08:27:10 -05:00
language = ml . DefaultLang
}
p . language = language
2017-02-04 22:20:06 -05:00
2016-11-13 08:27:10 -05:00
} )
}
func ( p * Page ) LanguagePrefix ( ) string {
return p . Site . LanguagePrefix
}
func ( p * Page ) addLangPathPrefixIfFlagSet ( outfile string , should bool ) string {
if helpers . IsAbsURL ( outfile ) {
return outfile
}
if ! should {
return outfile
}
hadSlashSuffix := strings . HasSuffix ( outfile , "/" )
outfile = "/" + path . Join ( p . Lang ( ) , outfile )
if hadSlashSuffix {
outfile += "/"
}
return outfile
}
2018-01-27 12:03:06 -05:00
func sectionsFromFile ( fi * fileInfo ) [ ] string {
dirname := fi . Dir ( )
2017-12-28 05:32:02 -05:00
dirname = strings . Trim ( dirname , helpers . FilePathSeparator )
if dirname == "" {
return nil
:sparkles: Implement Page bundling and image handling
This commit is not the smallest in Hugo's history.
Some hightlights include:
* Page bundles (for complete articles, keeping images and content together etc.).
* Bundled images can be processed in as many versions/sizes as you need with the three methods `Resize`, `Fill` and `Fit`.
* Processed images are cached inside `resources/_gen/images` (default) in your project.
* Symbolic links (both files and dirs) are now allowed anywhere inside /content
* A new table based build summary
* The "Total in nn ms" now reports the total including the handling of the files inside /static. So if it now reports more than you're used to, it is just **more real** and probably faster than before (see below).
A site building benchmark run compared to `v0.31.1` shows that this should be slightly faster and use less memory:
```bash
▶ ./benchSite.sh "TOML,num_langs=.*,num_root_sections=5,num_pages=(500|1000),tags_per_page=5,shortcodes,render"
benchmark old ns/op new ns/op delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 101785785 78067944 -23.30%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 185481057 149159919 -19.58%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 103149918 85679409 -16.94%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 203515478 169208775 -16.86%
benchmark old allocs new allocs delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 532464 391539 -26.47%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1056549 772702 -26.87%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 555974 406630 -26.86%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 1086545 789922 -27.30%
benchmark old bytes new bytes delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 53243246 43598155 -18.12%
BenchmarkSiteBuilding/TOML,num_langs=1,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 105811617 86087116 -18.64%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=500,tags_per_page=5,shortcodes,render-4 54558852 44545097 -18.35%
BenchmarkSiteBuilding/TOML,num_langs=3,num_root_sections=5,num_pages=1000,tags_per_page=5,shortcodes,render-4 106903858 86978413 -18.64%
```
Fixes #3651
Closes #3158
Fixes #1014
Closes #2021
Fixes #1240
Updates #3757
2017-07-24 03:00:23 -04:00
}
2018-01-27 12:03:06 -05:00
parts := strings . Split ( dirname , helpers . FilePathSeparator )
if fi . bundleTp == bundleLeaf && len ( parts ) > 0 {
// my-section/mybundle/index.md => my-section
return parts [ : len ( parts ) - 1 ]
}
return parts
2016-11-13 08:27:10 -05:00
}
2018-01-21 14:40:58 -05:00
func kindFromFileInfo ( fi * fileInfo ) string {
if fi . TranslationBaseName ( ) == "_index" {
if fi . Dir ( ) == "" {
return KindHome
}
// Could be index for section, taxonomy, taxonomy term
// We don't know enough yet to determine which
return kindUnknown
2016-11-13 08:27:10 -05:00
}
2018-01-21 14:40:58 -05:00
return KindPage
2016-11-13 08:27:10 -05:00
}
2016-11-16 11:52:03 -05:00
func ( p * Page ) setValuesForKind ( s * Site ) {
2016-11-13 08:27:10 -05:00
if p . Kind == kindUnknown {
// This is either a taxonomy list, taxonomy term or a section
2016-11-16 11:52:03 -05:00
nodeType := s . kindFromSections ( p . sections )
2016-11-13 08:27:10 -05:00
if nodeType == kindUnknown {
2016-11-16 11:52:03 -05:00
panic ( fmt . Sprintf ( "Unable to determine page kind from %q" , p . sections ) )
2016-11-13 08:27:10 -05:00
}
p . Kind = nodeType
}
2016-11-16 11:52:03 -05:00
2016-11-13 08:27:10 -05:00
switch p . Kind {
case KindHome :
2016-11-15 04:43:49 -05:00
p . URLPath . URL = "/"
2017-04-09 04:33:04 -04:00
case KindPage :
default :
2018-01-12 11:17:10 -05:00
if p . URLPath . URL == "" {
p . URLPath . URL = "/" + path . Join ( p . sections ... ) + "/"
}
2016-11-13 08:27:10 -05:00
}
}
2017-03-26 13:34:30 -04:00
// Used in error logs.
func ( p * Page ) pathOrTitle ( ) string {
if p . Path ( ) != "" {
return p . Path ( )
}
2018-01-15 14:40:39 -05:00
return p . title
2017-03-26 13:34:30 -04:00
}