page__new.go (5708B)
1 // Copyright 2019 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package hugolib
15
16 import (
17 "html/template"
18 "strings"
19
20 "go.uber.org/atomic"
21
22 "github.com/gohugoio/hugo/common/hugo"
23
24 "github.com/gohugoio/hugo/common/maps"
25
26 "github.com/gohugoio/hugo/output"
27
28 "github.com/gohugoio/hugo/lazy"
29
30 "github.com/gohugoio/hugo/resources/page"
31 )
32
33 func newPageBase(metaProvider *pageMeta) (*pageState, error) {
34 if metaProvider.s == nil {
35 panic("must provide a Site")
36 }
37
38 s := metaProvider.s
39
40 ps := &pageState{
41 pageOutput: nopPageOutput,
42 pageOutputTemplateVariationsState: atomic.NewUint32(0),
43 pageCommon: &pageCommon{
44 FileProvider: metaProvider,
45 AuthorProvider: metaProvider,
46 Scratcher: maps.NewScratcher(),
47 store: maps.NewScratch(),
48 Positioner: page.NopPage,
49 InSectionPositioner: page.NopPage,
50 ResourceMetaProvider: metaProvider,
51 ResourceParamsProvider: metaProvider,
52 PageMetaProvider: metaProvider,
53 RelatedKeywordsProvider: metaProvider,
54 OutputFormatsProvider: page.NopPage,
55 ResourceTypeProvider: pageTypesProvider,
56 MediaTypeProvider: pageTypesProvider,
57 RefProvider: page.NopPage,
58 ShortcodeInfoProvider: page.NopPage,
59 LanguageProvider: s,
60 pagePages: &pagePages{},
61
62 InternalDependencies: s,
63 init: lazy.New(),
64 m: metaProvider,
65 s: s,
66 },
67 }
68
69 ps.shortcodeState = newShortcodeHandler(ps, ps.s)
70
71 siteAdapter := pageSiteAdapter{s: s, p: ps}
72
73 ps.pageMenus = &pageMenus{p: ps}
74 ps.PageMenusProvider = ps.pageMenus
75 ps.GetPageProvider = siteAdapter
76 ps.GitInfoProvider = ps
77 ps.TranslationsProvider = ps
78 ps.ResourceDataProvider = &pageData{pageState: ps}
79 ps.RawContentProvider = ps
80 ps.ChildCareProvider = ps
81 ps.TreeProvider = pageTree{p: ps}
82 ps.Eqer = ps
83 ps.TranslationKeyProvider = ps
84 ps.ShortcodeInfoProvider = ps
85 ps.AlternativeOutputFormatsProvider = ps
86
87 return ps, nil
88 }
89
90 func newPageBucket(p *pageState) *pagesMapBucket {
91 return &pagesMapBucket{owner: p, pagesMapBucketPages: &pagesMapBucketPages{}}
92 }
93
94 func newPageFromMeta(
95 n *contentNode,
96 parentBucket *pagesMapBucket,
97 meta map[string]any,
98 metaProvider *pageMeta) (*pageState, error) {
99 if metaProvider.f == nil {
100 metaProvider.f = page.NewZeroFile(metaProvider.s.LogDistinct)
101 }
102
103 ps, err := newPageBase(metaProvider)
104 if err != nil {
105 return nil, err
106 }
107
108 bucket := parentBucket
109
110 if ps.IsNode() {
111 ps.bucket = newPageBucket(ps)
112 }
113
114 if meta != nil || parentBucket != nil {
115 if err := metaProvider.setMetadata(bucket, ps, meta); err != nil {
116 return nil, ps.wrapError(err)
117 }
118 }
119
120 if err := metaProvider.applyDefaultValues(n); err != nil {
121 return nil, err
122 }
123
124 ps.init.Add(func() (any, error) {
125 pp, err := newPagePaths(metaProvider.s, ps, metaProvider)
126 if err != nil {
127 return nil, err
128 }
129
130 makeOut := func(f output.Format, render bool) *pageOutput {
131 return newPageOutput(ps, pp, f, render)
132 }
133
134 shouldRenderPage := !ps.m.noRender()
135
136 if ps.m.standalone {
137 ps.pageOutput = makeOut(ps.m.outputFormats()[0], shouldRenderPage)
138 } else {
139 outputFormatsForPage := ps.m.outputFormats()
140
141 // Prepare output formats for all sites.
142 // We do this even if this page does not get rendered on
143 // its own. It may be referenced via .Site.GetPage and
144 // it will then need an output format.
145 ps.pageOutputs = make([]*pageOutput, len(ps.s.h.renderFormats))
146 created := make(map[string]*pageOutput)
147 for i, f := range ps.s.h.renderFormats {
148 po, found := created[f.Name]
149 if !found {
150 render := shouldRenderPage
151 if render {
152 _, render = outputFormatsForPage.GetByName(f.Name)
153 }
154 po = makeOut(f, render)
155 created[f.Name] = po
156 }
157 ps.pageOutputs[i] = po
158 }
159 }
160
161 if err := ps.initCommonProviders(pp); err != nil {
162 return nil, err
163 }
164
165 return nil, nil
166 })
167
168 return ps, err
169 }
170
171 // Used by the legacy 404, sitemap and robots.txt rendering
172 func newPageStandalone(m *pageMeta, f output.Format) (*pageState, error) {
173 m.configuredOutputFormats = output.Formats{f}
174 m.standalone = true
175 p, err := newPageFromMeta(nil, nil, nil, m)
176 if err != nil {
177 return nil, err
178 }
179
180 if err := p.initPage(); err != nil {
181 return nil, err
182 }
183
184 return p, nil
185 }
186
187 type pageDeprecatedWarning struct {
188 p *pageState
189 }
190
191 func (p *pageDeprecatedWarning) IsDraft() bool { return p.p.m.draft }
192 func (p *pageDeprecatedWarning) Hugo() hugo.Info { return p.p.s.Info.Hugo() }
193 func (p *pageDeprecatedWarning) LanguagePrefix() string { return p.p.s.Info.LanguagePrefix }
194 func (p *pageDeprecatedWarning) GetParam(key string) any {
195 return p.p.m.params[strings.ToLower(key)]
196 }
197
198 func (p *pageDeprecatedWarning) RSSLink() template.URL {
199 f := p.p.OutputFormats().Get("RSS")
200 if f == nil {
201 return ""
202 }
203 return template.URL(f.Permalink())
204 }
205
206 func (p *pageDeprecatedWarning) URL() string {
207 if p.p.IsPage() && p.p.m.urlPaths.URL != "" {
208 // This is the url set in front matter
209 return p.p.m.urlPaths.URL
210 }
211 // Fall back to the relative permalink.
212 return p.p.RelPermalink()
213 }