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