summaryrefslogtreecommitdiffstats
path: root/resources
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-01-02 12:33:26 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-03-23 18:51:22 +0100
commit597e418cb02883418f2cebb41400e8e61413f651 (patch)
tree177ad9c540b2583b6dab138c9f0490d28989c7f7 /resources
parent44f5c1c14cb1f42cc5f01739c289e9cfc83602af (diff)
Make Page an interface
The main motivation of this commit is to add a `page.Page` interface to replace the very file-oriented `hugolib.Page` struct. This is all a preparation step for issue #5074, "pages from other data sources". But this also fixes a set of annoying limitations, especially related to custom output formats, and shortcodes. Most notable changes: * The inner content of shortcodes using the `{{%` as the outer-most delimiter will now be sent to the content renderer, e.g. Blackfriday. This means that any markdown will partake in the global ToC and footnote context etc. * The Custom Output formats are now "fully virtualized". This removes many of the current limitations. * The taxonomy list type now has a reference to the `Page` object. This improves the taxonomy template `.Title` situation and make common template constructs much simpler. See #5074 Fixes #5763 Fixes #5758 Fixes #5090 Fixes #5204 Fixes #4695 Fixes #5607 Fixes #5707 Fixes #5719 Fixes #3113 Fixes #5706 Fixes #5767 Fixes #5723 Fixes #5769 Fixes #5770 Fixes #5771 Fixes #5759 Fixes #5776 Fixes #5777 Fixes #5778
Diffstat (limited to 'resources')
-rw-r--r--resources/image.go20
-rw-r--r--resources/image_cache.go20
-rw-r--r--resources/page/page.go365
-rw-r--r--resources/page/page_author.go45
-rw-r--r--resources/page/page_data.go42
-rw-r--r--resources/page/page_data_test.go57
-rw-r--r--resources/page/page_generate/.gitignore1
-rw-r--r--resources/page/page_generate/generate_page_wrappers.go212
-rw-r--r--resources/page/page_kinds.go25
-rw-r--r--resources/page/page_kinds_test.go31
-rw-r--r--resources/page/page_marshaljson.autogen.go198
-rw-r--r--resources/page/page_nop.go463
-rw-r--r--resources/page/page_outputformat.go85
-rw-r--r--resources/page/page_paths.go334
-rw-r--r--resources/page/page_paths_test.go258
-rw-r--r--resources/page/page_wrappers.autogen.go97
-rw-r--r--resources/page/pagegroup.go369
-rw-r--r--resources/page/pagegroup_test.go409
-rw-r--r--resources/page/pagemeta/page_frontmatter.go427
-rw-r--r--resources/page/pagemeta/page_frontmatter_test.go262
-rw-r--r--resources/page/pagemeta/pagemeta.go21
-rw-r--r--resources/page/pages.go115
-rw-r--r--resources/page/pages_cache.go136
-rw-r--r--resources/page/pages_cache_test.go86
-rw-r--r--resources/page/pages_language_merge.go64
-rw-r--r--resources/page/pages_prev_next.go42
-rw-r--r--resources/page/pages_prev_next_test.go83
-rw-r--r--resources/page/pages_related.go199
-rw-r--r--resources/page/pages_related_test.go86
-rw-r--r--resources/page/pages_sort.go348
-rw-r--r--resources/page/pages_sort_test.go279
-rw-r--r--resources/page/pagination.go404
-rw-r--r--resources/page/pagination_test.go307
-rw-r--r--resources/page/permalinks.go248
-rw-r--r--resources/page/permalinks_test.go180
-rw-r--r--resources/page/site.go53
-rw-r--r--resources/page/testhelpers_test.go554
-rw-r--r--resources/page/weighted.go140
-rw-r--r--resources/resource.go63
-rw-r--r--resources/resource/dates.go81
-rw-r--r--resources/resource/params.go89
-rw-r--r--resources/resource/resource_helpers.go70
-rw-r--r--resources/resource/resourcetypes.go98
-rw-r--r--resources/resource_metadata.go1
-rw-r--r--resources/resource_metadata_test.go4
-rw-r--r--resources/resource_test.go6
-rw-r--r--resources/testhelpers_test.go19
-rw-r--r--resources/transform.go4
48 files changed, 7414 insertions, 86 deletions
diff --git a/resources/image.go b/resources/image.go
index d46facac5..202b54fc2 100644
--- a/resources/image.go
+++ b/resources/image.go
@@ -21,7 +21,6 @@ import (
"image/draw"
"image/jpeg"
"io"
- "io/ioutil"
"os"
"strconv"
"strings"
@@ -126,8 +125,6 @@ type Image struct {
configInit sync.Once
configLoaded bool
- copyToDestinationInit sync.Once
-
imaging *Imaging
format imaging.Format
@@ -462,30 +459,23 @@ func (i *Image) decodeSource() (image.Image, error) {
return img, err
}
+// returns an opened file or nil if nothing to write.
func (i *Image) openDestinationsForWriting() (io.WriteCloser, error) {
targetFilenames := i.targetFilenames()
var changedFilenames []string
// Fast path:
- // This is a processed version of the original.
- // If it exists on destination with the same filename and file size, it is
- // the same file, so no need to transfer it again.
+ // This is a processed version of the original;
+ // check if it already existis at the destination.
for _, targetFilename := range targetFilenames {
- if fi, err := i.spec.BaseFs.PublishFs.Stat(targetFilename); err == nil && fi.Size() == i.osFileInfo.Size() {
+ if _, err := i.spec.BaseFs.PublishFs.Stat(targetFilename); err == nil {
continue
}
changedFilenames = append(changedFilenames, targetFilename)
}
if len(changedFilenames) == 0 {
- return struct {
- io.Writer
- io.Closer
- }{
- ioutil.Discard,
- ioutil.NopCloser(nil),
- }, nil
-
+ return nil, nil
}
return helpers.OpenFilesForWriting(i.spec.BaseFs.PublishFs, changedFilenames...)
diff --git a/resources/image_cache.go b/resources/image_cache.go
index 58be839b3..cf1e999ba 100644
--- a/resources/image_cache.go
+++ b/resources/image_cache.go
@@ -14,13 +14,11 @@
package resources
import (
- "fmt"
"image"
"io"
"path/filepath"
"strings"
"sync"
- "time"
"github.com/gohugoio/hugo/common/hugio"
@@ -99,6 +97,11 @@ func (c *imageCache) getOrCreate(
return err
}
+ if w == nil {
+ // Nothing to write.
+ return nil
+ }
+
defer w.Close()
_, err = io.Copy(w, r)
return err
@@ -121,10 +124,12 @@ func (c *imageCache) getOrCreate(
return err
}
- mw := hugio.NewMultiWriteCloser(w, destinations)
- defer mw.Close()
+ if destinations != nil {
+ w = hugio.NewMultiWriteCloser(w, destinations)
+ }
+ defer w.Close()
- return img.encodeTo(conf, conv, mw)
+ return img.encodeTo(conf, conv, w)
}
// Now look in the file cache.
@@ -157,8 +162,3 @@ func (c *imageCache) getOrCreate(
func newImageCache(fileCache *filecache.Cache, ps *helpers.PathSpec) *imageCache {
return &imageCache{fileCache: fileCache, pathSpec: ps, store: make(map[string]*Image)}
}
-
-func timeTrack(start time.Time, name string) {
- elapsed := time.Since(start)
- fmt.Printf("%s took %s\n", name, elapsed)
-}
diff --git a/resources/page/page.go b/resources/page/page.go
new file mode 100644
index 000000000..efbefb456
--- /dev/null
+++ b/resources/page/page.go
@@ -0,0 +1,365 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package page contains the core interfaces and types for the Page resource,
+// a core component in Hugo.
+package page
+
+import (
+ "html/template"
+
+ "github.com/bep/gitmap"
+ "github.com/gohugoio/hugo/config"
+
+ "github.com/gohugoio/hugo/common/hugo"
+ "github.com/gohugoio/hugo/common/maps"
+
+ "github.com/gohugoio/hugo/compare"
+
+ "github.com/gohugoio/hugo/navigation"
+ "github.com/gohugoio/hugo/related"
+ "github.com/gohugoio/hugo/resources/resource"
+ "github.com/gohugoio/hugo/source"
+)
+
+// Clear clears any global package state.
+func Clear() error {
+ spc.clear()
+ return nil
+}
+
+// AlternativeOutputFormatsProvider provides alternative output formats for a
+// Page.
+type AlternativeOutputFormatsProvider interface {
+ // AlternativeOutputFormats gives the alternative output formats for the
+ // current output.
+ // Note that we use the term "alternative" and not "alternate" here, as it
+ // does not necessarily replace the other format, it is an alternative representation.
+ AlternativeOutputFormats() OutputFormats
+}
+
+// AuthorProvider provides author information.
+type AuthorProvider interface {
+ Author() Author
+ Authors() AuthorList
+}
+
+// ChildCareProvider provides accessors to child resources.
+type ChildCareProvider interface {
+ Pages() Pages
+ Resources() resource.Resources
+}
+
+// ContentProvider provides the content related values for a Page.
+type ContentProvider interface {
+ Content() (interface{}, error)
+ Plain() string
+ PlainWords() []string
+ Summary() template.HTML
+ Truncated() bool
+ FuzzyWordCount() int
+ WordCount() int
+ ReadingTime() int
+ Len() int
+}
+
+// FileProvider provides the source file.
+type FileProvider interface {
+ File() source.File
+}
+
+// GetPageProvider provides the GetPage method.
+type GetPageProvider interface {
+ // GetPage looks up a page for the given ref.
+ // {{ with .GetPage "blog" }}{{ .Title }}{{ end }}
+ //
+ // This will return nil when no page could be found, and will return
+ // an error if the ref is ambiguous.
+ GetPage(ref string) (Page, error)
+}
+
+// GitInfoProvider provides Git info.
+type GitInfoProvider interface {
+ GitInfo() *gitmap.GitInfo
+}
+
+// InSectionPositioner provides section navigation.
+type InSectionPositioner interface {
+ NextInSection() Page
+ PrevInSection() Page
+}
+
+// InternalDependencies is considered an internal interface.
+type InternalDependencies interface {
+ GetRelatedDocsHandler() *RelatedDocsHandler
+}
+
+// OutputFormatsProvider provides the OutputFormats of a Page.
+type OutputFormatsProvider interface {
+ OutputFormats() OutputFormats
+}
+
+// Page is the core interface in Hugo.
+type Page interface {
+ ContentProvider
+ TableOfContentsProvider
+ PageWithoutContent
+}
+
+// PageMetaProvider provides page metadata, typically provided via front matter.
+type PageMetaProvider interface {
+ // The 4 page dates
+ resource.Dated
+
+ // Aliases forms the base for redirects generation.
+ Aliases() []string
+
+ // BundleType returns the bundle type: "leaf", "branch" or an empty string if it is none.
+ // See https://gohugo.io/content-management/page-bundles/
+ BundleType() string
+
+ // A configured description.
+ Description() string
+
+ // Whether this is a draft. Will only be true if run with the --buildDrafts (-D) flag.
+ Draft() bool
+
+ // IsHome returns whether this is the home page.
+ IsHome() bool
+
+ // Configured keywords.
+ Keywords() []string
+
+ // The Page Kind. One of page, home, section, taxonomy, taxonomyTerm.
+ Kind() string
+
+ // The configured layout to use to render this page. Typically set in front matter.
+ Layout() string
+
+ // The title used for links.
+ LinkTitle() string
+
+ // IsNode returns whether this is an item of one of the list types in Hugo,
+ // i.e. not a regular content
+ IsNode() bool
+
+ // IsPage returns whether this is a regular content
+ IsPage() bool
+
+ // Param looks for a param in Page and then in Site config.
+ Param(key interface{}) (interface{}, error)
+
+ // Path gets the relative path, including file name and extension if relevant,
+ // to the source of this Page. It will be relative to any content root.
+ Path() string
+
+ // The slug, typically defined in front matter.
+ Slug() string
+
+ // This page's language code. Will be the same as the site's.
+ Lang() string
+
+ // IsSection returns whether this is a section
+ IsSection() bool
+
+ // Section returns the first path element below the content root.
+ Section() string
+
+ // Returns a slice of sections (directories if it's a file) to this
+ // Page.
+ SectionsEntries() []string
+
+ // SectionsPath is SectionsEntries joined with a /.
+ SectionsPath() string
+
+ // Sitemap returns the sitemap configuration for this page.
+ Sitemap() config.Sitemap
+
+ // Type is a discriminator used to select layouts etc. It is typically set
+ // in front matter, but will fall back to the root section.
+ Type() string
+
+ // The configured weight, used as the first sort value in the default
+ // page sort if non-zero.
+ Weight() int
+}
+
+// PageRenderProvider provides a way for a Page to render itself.
+type PageRenderProvider interface {
+ Render(layout ...string) template.HTML
+}
+
+// PageWithoutContent is the Page without any of the content methods.
+type PageWithoutContent interface {
+ RawContentProvider
+ resource.Resource
+ PageMetaProvider
+ resource.LanguageProvider
+
+ // For pages backed by a file.
+ FileProvider
+
+ // Output formats
+ OutputFormatsProvider
+ AlternativeOutputFormatsProvider
+
+ // Tree navigation
+ ChildCareProvider
+ TreeProvider
+
+ // Horisontal navigation
+ InSectionPositioner
+ PageRenderProvider
+ PaginatorProvider
+ Positioner
+ navigation.PageMenusProvider
+
+ // TODO(bep)
+ AuthorProvider
+
+ // Page lookups/refs
+ GetPageProvider
+ RefProvider
+
+ resource.TranslationKeyProvider
+ TranslationsProvider
+
+ SitesProvider
+
+ // Helper methods
+ ShortcodeInfoProvider
+ compare.Eqer
+ maps.Scratcher
+ RelatedKeywordsProvider
+
+ DeprecatedWarningPageMethods
+}
+
+// Positioner provides next/prev navigation.
+type Positioner interface {
+ Next() Page
+ Prev() Page
+
+ // Deprecated: Use Prev. Will be removed in Hugo 0.57
+ PrevPage() Page
+
+ // Deprecated: Use Next. Will be removed in Hugo 0.57
+ NextPage() Page
+}
+
+// RawContentProvider provides the raw, unprocessed content of the page.
+type RawContentProvider interface {
+ RawContent() string
+}
+
+// RefProvider provides the methods needed to create reflinks to pages.
+type RefProvider interface {
+ Ref(argsm map[string]interface{}) (string, error)
+ RefFrom(argsm map[string]interface{}, source interface{}) (string, error)
+ RelRef(argsm map[string]interface{}) (string, error)
+ RelRefFrom(argsm map[string]interface{}, source interface{}) (string, error)
+}
+
+// RelatedKeywordsProvider allows a Page to be indexed.
+type RelatedKeywordsProvider interface {
+ // Make it indexable as a related.Document
+ RelatedKeywords(cfg related.IndexConfig) ([]related.Keyword, error)
+}
+
+// ShortcodeInfoProvider provides info about the shortcodes in a Page.
+type ShortcodeInfoProvider interface {
+ // 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.
+ HasShortcode(name string) bool
+}
+
+// SitesProvider provide accessors to get sites.
+type SitesProvider interface {
+ Site() Site
+ Sites() Sites
+}
+
+// TableOfContentsProvider provides the table of contents for a Page.
+type TableOfContentsProvider interface {
+ TableOfContents() template.HTML
+}
+
+// TranslationsProvider provides access to any translations.
+type TranslationsProvider interface {
+
+ // IsTranslated returns whether this content file is translated to
+ // other language(s).
+ IsTranslated() bool
+
+ // AllTranslations returns all translations, including the current Page.
+ AllTranslations() Pages
+
+ // Translations returns the translations excluding the current Page.
+ Translations() Pages
+}
+
+// TreeProvider provides section tree navigation.
+type TreeProvider interface {
+
+ // IsAncestor returns whether the current page is an ancestor of the given
+ // Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
+ IsAncestor(other interface{}) (bool, error)
+
+ // CurrentSection returns the page's current section or the page itself if home or a section.
+ // Note that this will return nil for pages that is not regular, home or section pages.
+ CurrentSection() Page
+
+ // IsDescendant returns whether the current page is a descendant of the given
+ // Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
+ IsDescendant(other interface{}) (bool, error)
+
+ // FirstSection returns the section on level 1 below home, e.g. "/docs".
+ // For the home page, this will return itself.
+ FirstSection() Page
+
+ // InSection returns whether the given page is in the current section.
+ // Note that this will always return false for pages that are
+ // not either regular, home or section pages.
+ InSection(other interface{}) (bool, error)
+
+ // Parent returns a section's parent section or a page's section.
+ // To get a section's subsections, see Page's Sections method.
+ Parent() Page
+
+ // Sections returns this section's subsections, if any.
+ // Note that for non-sections, this method will always return an empty list.
+ Sections() Pages
+}
+
+// DeprecatedWarningPageMethods lists deprecated Page methods that will trigger
+// a WARNING if invoked.
+// This was added in Hugo 0.55.
+type DeprecatedWarningPageMethods interface {
+ source.FileWithoutOverlap
+ DeprecatedWarningPageMethods1
+}
+
+type DeprecatedWarningPageMethods1 interface {
+ IsDraft() bool
+ Hugo() hugo.Info
+ LanguagePrefix() string
+ GetParam(key string) interface{}
+ RSSLink() template.URL
+ URL() string
+}
+
+// Move here to trigger ERROR instead of WARNING.
+// TODO(bep) create wrappers and put into the Page once it has some methods.
+type DeprecatedErrorPageMethods interface {
+}
diff --git a/resources/page/page_author.go b/resources/page/page_author.go
new file mode 100644
index 000000000..9e8a95182
--- /dev/null
+++ b/resources/page/page_author.go
@@ -0,0 +1,45 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package page
+
+// AuthorList is a list of all authors and their metadata.
+type AuthorList map[string]Author
+
+// Author contains details about the author of a page.
+type Author struct {
+ GivenName string
+ FamilyName string
+ DisplayName string
+ Thumbnail string
+ Image string
+ ShortBio string
+ LongBio string
+ Email string
+ Social AuthorSocial
+}
+
+// AuthorSocial is a place to put social details per author. These are the
+// standard keys that themes will expect to have available, but can be
+// expanded to any others on a per site basis
+// - website
+// - github
+// - facebook
+// - twitter
+// - googleplus
+// - pinterest
+// - instagram
+// - youtube
+// - linkedin
+// - skype
+type AuthorSocial map[string]string
diff --git a/resources/page/page_data.go b/resources/page/page_data.go
new file mode 100644
index 000000000..3345a44da
--- /dev/null
+++ b/resources/page/page_data.go
@@ -0,0 +1,42 @@
+// Copyright 2019 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.