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 }