URI: 
       page__meta.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       page__meta.go (23601B)
       ---
            1 // Copyright 2024 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         "fmt"
           19         "path/filepath"
           20         "regexp"
           21         "strings"
           22         "time"
           23 
           24         "github.com/bep/logg"
           25         "github.com/gobuffalo/flect"
           26         "github.com/gohugoio/hugo/langs"
           27         "github.com/gohugoio/hugo/markup/converter"
           28         xmaps "golang.org/x/exp/maps"
           29 
           30         "github.com/gohugoio/hugo/source"
           31 
           32         "github.com/gohugoio/hugo/common/hashing"
           33         "github.com/gohugoio/hugo/common/hugo"
           34         "github.com/gohugoio/hugo/common/loggers"
           35         "github.com/gohugoio/hugo/common/maps"
           36         "github.com/gohugoio/hugo/common/paths"
           37         "github.com/gohugoio/hugo/config"
           38         "github.com/gohugoio/hugo/helpers"
           39 
           40         "github.com/gohugoio/hugo/output"
           41         "github.com/gohugoio/hugo/resources/kinds"
           42         "github.com/gohugoio/hugo/resources/page"
           43         "github.com/gohugoio/hugo/resources/page/pagemeta"
           44         "github.com/gohugoio/hugo/resources/resource"
           45         "github.com/spf13/cast"
           46 )
           47 
           48 var cjkRe = regexp.MustCompile(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`)
           49 
           50 type pageMeta struct {
           51         term     string // Set for kind == KindTerm.
           52         singular string // Set for kind == KindTerm and kind == KindTaxonomy.
           53 
           54         resource.Staler
           55         *pageMetaParams
           56 
           57         // Set for standalone pages, e.g. robotsTXT.
           58         standaloneOutputFormat output.Format
           59 
           60         resourcePath string // Set for bundled pages; path relative to its bundle root.
           61         bundled      bool   // Set if this page is bundled inside another.
           62 
           63         pathInfo *paths.Path // Always set. This the canonical path to the Page.
           64         f        *source.File
           65 
           66         content *cachedContent // The source and the parsed page content.
           67 
           68         s *Site // The site this page belongs to.
           69 }
           70 
           71 // Prepare for a rebuild of the data passed in from front matter.
           72 func (m *pageMeta) setMetaPostPrepareRebuild() {
           73         params := xmaps.Clone(m.paramsOriginal)
           74         m.pageMetaParams.pageConfig = pagemeta.ClonePageConfigForRebuild(m.pageMetaParams.pageConfig, params)
           75 }
           76 
           77 type pageMetaParams struct {
           78         setMetaPostCount          int
           79         setMetaPostCascadeChanged bool
           80 
           81         pageConfig *pagemeta.PageConfig
           82 
           83         // These are only set in watch mode.
           84         datesOriginal   pagemeta.Dates
           85         paramsOriginal  map[string]any                                                // contains the original params as defined in the front matter.
           86         cascadeOriginal *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // contains the original cascade as defined in the front matter.
           87 }
           88 
           89 func (m *pageMetaParams) init(preserveOriginal bool) {
           90         if preserveOriginal {
           91                 if m.pageConfig.IsFromContentAdapter {
           92                         m.paramsOriginal = xmaps.Clone(m.pageConfig.ContentAdapterData)
           93                 } else {
           94                         m.paramsOriginal = xmaps.Clone(m.pageConfig.Params)
           95                 }
           96                 m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone()
           97         }
           98 }
           99 
          100 func (p *pageMeta) Aliases() []string {
          101         return p.pageConfig.Aliases
          102 }
          103 
          104 func (p *pageMeta) BundleType() string {
          105         switch p.pathInfo.Type() {
          106         case paths.TypeLeaf:
          107                 return "leaf"
          108         case paths.TypeBranch:
          109                 return "branch"
          110         default:
          111                 return ""
          112         }
          113 }
          114 
          115 func (p *pageMeta) Date() time.Time {
          116         return p.pageConfig.Dates.Date
          117 }
          118 
          119 func (p *pageMeta) PublishDate() time.Time {
          120         return p.pageConfig.Dates.PublishDate
          121 }
          122 
          123 func (p *pageMeta) Lastmod() time.Time {
          124         return p.pageConfig.Dates.Lastmod
          125 }
          126 
          127 func (p *pageMeta) ExpiryDate() time.Time {
          128         return p.pageConfig.Dates.ExpiryDate
          129 }
          130 
          131 func (p *pageMeta) Description() string {
          132         return p.pageConfig.Description
          133 }
          134 
          135 func (p *pageMeta) Lang() string {
          136         return p.s.Lang()
          137 }
          138 
          139 func (p *pageMeta) Draft() bool {
          140         return p.pageConfig.Draft
          141 }
          142 
          143 func (p *pageMeta) File() *source.File {
          144         return p.f
          145 }
          146 
          147 func (p *pageMeta) IsHome() bool {
          148         return p.Kind() == kinds.KindHome
          149 }
          150 
          151 func (p *pageMeta) Keywords() []string {
          152         return p.pageConfig.Keywords
          153 }
          154 
          155 func (p *pageMeta) Kind() string {
          156         return p.pageConfig.Kind
          157 }
          158 
          159 func (p *pageMeta) Layout() string {
          160         return p.pageConfig.Layout
          161 }
          162 
          163 func (p *pageMeta) LinkTitle() string {
          164         if p.pageConfig.LinkTitle != "" {
          165                 return p.pageConfig.LinkTitle
          166         }
          167 
          168         return p.Title()
          169 }
          170 
          171 func (p *pageMeta) Name() string {
          172         if p.resourcePath != "" {
          173                 return p.resourcePath
          174         }
          175         if p.pageConfig.Kind == kinds.KindTerm {
          176                 return p.pathInfo.Unnormalized().BaseNameNoIdentifier()
          177         }
          178         return p.Title()
          179 }
          180 
          181 func (p *pageMeta) IsNode() bool {
          182         return !p.IsPage()
          183 }
          184 
          185 func (p *pageMeta) IsPage() bool {
          186         return p.Kind() == kinds.KindPage
          187 }
          188 
          189 // Param is a convenience method to do lookups in Page's and Site's Params map,
          190 // in that order.
          191 //
          192 // This method is also implemented on SiteInfo.
          193 // TODO(bep) interface
          194 func (p *pageMeta) Param(key any) (any, error) {
          195         return resource.Param(p, p.s.Params(), key)
          196 }
          197 
          198 func (p *pageMeta) Params() maps.Params {
          199         return p.pageConfig.Params
          200 }
          201 
          202 func (p *pageMeta) Path() string {
          203         return p.pathInfo.Base()
          204 }
          205 
          206 func (p *pageMeta) PathInfo() *paths.Path {
          207         return p.pathInfo
          208 }
          209 
          210 func (p *pageMeta) IsSection() bool {
          211         return p.Kind() == kinds.KindSection
          212 }
          213 
          214 func (p *pageMeta) Section() string {
          215         return p.pathInfo.Section()
          216 }
          217 
          218 func (p *pageMeta) Sitemap() config.SitemapConfig {
          219         return p.pageConfig.Sitemap
          220 }
          221 
          222 func (p *pageMeta) Title() string {
          223         return p.pageConfig.Title
          224 }
          225 
          226 const defaultContentType = "page"
          227 
          228 func (p *pageMeta) Type() string {
          229         if p.pageConfig.Type != "" {
          230                 return p.pageConfig.Type
          231         }
          232 
          233         if sect := p.Section(); sect != "" {
          234                 return sect
          235         }
          236 
          237         return defaultContentType
          238 }
          239 
          240 func (p *pageMeta) Weight() int {
          241         return p.pageConfig.Weight
          242 }
          243 
          244 func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf config.AllProvider) error {
          245         frontmatter := pi.frontMatter
          246 
          247         if frontmatter != nil {
          248                 pcfg := p.pageConfig
          249                 // Needed for case insensitive fetching of params values
          250                 maps.PrepareParams(frontmatter)
          251                 pcfg.Params = frontmatter
          252                 // Check for any cascade define on itself.
          253                 if cv, found := frontmatter["cascade"]; found {
          254                         var err error
          255                         cascade, err := page.DecodeCascade(logger, true, cv)
          256                         if err != nil {
          257                                 return err
          258                         }
          259                         pcfg.CascadeCompiled = cascade
          260                 }
          261 
          262                 // Look for path, lang and kind, all of which values we need early on.
          263                 if v, found := frontmatter["path"]; found {
          264                         pcfg.Path = paths.ToSlashPreserveLeading(cast.ToString(v))
          265                         pcfg.Params["path"] = pcfg.Path
          266                 }
          267                 if v, found := frontmatter["lang"]; found {
          268                         lang := strings.ToLower(cast.ToString(v))
          269                         if _, ok := conf.PathParser().LanguageIndex[lang]; ok {
          270                                 pcfg.Lang = lang
          271                                 pcfg.Params["lang"] = pcfg.Lang
          272                         }
          273                 }
          274                 if v, found := frontmatter["kind"]; found {
          275                         s := cast.ToString(v)
          276                         if s != "" {
          277                                 pcfg.Kind = kinds.GetKindMain(s)
          278                                 if pcfg.Kind == "" {
          279                                         return fmt.Errorf("unknown kind %q in front matter", s)
          280                                 }
          281                                 pcfg.Params["kind"] = pcfg.Kind
          282                         }
          283                 }
          284         } else if p.pageMetaParams.pageConfig.Params == nil {
          285                 p.pageConfig.Params = make(maps.Params)
          286         }
          287 
          288         p.pageMetaParams.init(conf.Watching())
          289 
          290         return nil
          291 }
          292 
          293 func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) error {
          294         ps.m.setMetaPostCount++
          295         var cascadeHashPre uint64
          296         if ps.m.setMetaPostCount > 1 {
          297                 cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
          298                 ps.m.pageConfig.CascadeCompiled = ps.m.cascadeOriginal.Clone()
          299 
          300         }
          301 
          302         // Apply cascades first so they can be overridden later.
          303         if cascade != nil {
          304                 if ps.m.pageConfig.CascadeCompiled != nil {
          305                         cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
          306                                 vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
          307                                 if !found {
          308                                         ps.m.pageConfig.CascadeCompiled.Set(k, v)
          309                                 } else {
          310                                         // Merge
          311                                         for ck, cv := range v.Params {
          312                                                 if _, found := vv.Params[ck]; !found {
          313                                                         vv.Params[ck] = cv
          314                                                 }
          315                                         }
          316                                         for ck, cv := range v.Fields {
          317                                                 if _, found := vv.Fields[ck]; !found {
          318                                                         vv.Fields[ck] = cv
          319                                                 }
          320                                         }
          321                                 }
          322                                 return true
          323                         })
          324                         cascade = ps.m.pageConfig.CascadeCompiled
          325                 } else {
          326                         ps.m.pageConfig.CascadeCompiled = cascade
          327                 }
          328         }
          329 
          330         if cascade == nil {
          331                 cascade = ps.m.pageConfig.CascadeCompiled
          332         }
          333 
          334         if ps.m.setMetaPostCount > 1 {
          335                 ps.m.setMetaPostCascadeChanged = cascadeHashPre != hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
          336                 if !ps.m.setMetaPostCascadeChanged {
          337 
          338                         // No changes, restore any value that may be changed by aggregation.
          339                         ps.m.pageConfig.Dates = ps.m.datesOriginal
          340                         return nil
          341                 }
          342                 ps.m.setMetaPostPrepareRebuild()
          343 
          344         }
          345 
          346         // Cascade is also applied to itself.
          347         var err error
          348         cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
          349                 if !k.Matches(ps) {
          350                         return true
          351                 }
          352                 for kk, vv := range v.Params {
          353                         if _, found := ps.m.pageConfig.Params[kk]; !found {
          354                                 ps.m.pageConfig.Params[kk] = vv
          355                         }
          356                 }
          357 
          358                 for kk, vv := range v.Fields {
          359                         if ps.m.pageConfig.IsFromContentAdapter {
          360                                 if _, found := ps.m.pageConfig.ContentAdapterData[kk]; !found {
          361                                         ps.m.pageConfig.ContentAdapterData[kk] = vv
          362                                 }
          363                         } else {
          364                                 if _, found := ps.m.pageConfig.Params[kk]; !found {
          365                                         ps.m.pageConfig.Params[kk] = vv
          366                                 }
          367                         }
          368                 }
          369                 return true
          370         })
          371 
          372         if err != nil {
          373                 return err
          374         }
          375 
          376         if err := ps.setMetaPostParams(); err != nil {
          377                 return err
          378         }
          379 
          380         if err := ps.m.applyDefaultValues(); err != nil {
          381                 return err
          382         }
          383 
          384         // Store away any original values that may be changed from aggregation.
          385         ps.m.datesOriginal = ps.m.pageConfig.Dates
          386 
          387         return nil
          388 }
          389 
          390 func (p *pageState) setMetaPostParams() error {
          391         pm := p.m
          392         var mtime time.Time
          393         var contentBaseName string
          394         var ext string
          395         var isContentAdapter bool
          396         if p.File() != nil {
          397                 isContentAdapter = p.File().IsContentAdapter()
          398                 contentBaseName = p.File().ContentBaseName()
          399                 if p.File().FileInfo() != nil {
          400                         mtime = p.File().FileInfo().ModTime()
          401                 }
          402                 if !isContentAdapter {
          403                         ext = p.File().Ext()
          404                 }
          405         }
          406 
          407         var gitAuthorDate time.Time
          408         if p.gitInfo != nil {
          409                 gitAuthorDate = p.gitInfo.AuthorDate
          410         }
          411 
          412         descriptor := &pagemeta.FrontMatterDescriptor{
          413                 PageConfig:    pm.pageConfig,
          414                 BaseFilename:  contentBaseName,
          415                 ModTime:       mtime,
          416                 GitAuthorDate: gitAuthorDate,
          417                 Location:      langs.GetLocation(pm.s.Language()),
          418                 PathOrTitle:   p.pathOrTitle(),
          419         }
          420 
          421         if isContentAdapter {
          422                 if err := pm.pageConfig.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
          423                         return err
          424                 }
          425         }
          426 
          427         // Handle the date separately
          428         // TODO(bep) we need to "do more" in this area so this can be split up and
          429         // more easily tested without the Page, but the coupling is strong.
          430         err := pm.s.frontmatterHandler.HandleDates(descriptor)
          431         if err != nil {
          432                 p.s.Log.Errorf("Failed to handle dates for page %q: %s", p.pathOrTitle(), err)
          433         }
          434 
          435         if isContentAdapter {
          436                 // Done.
          437                 return nil
          438         }
          439 
          440         var buildConfig any
          441         var isNewBuildKeyword bool
          442         if v, ok := pm.pageConfig.Params["_build"]; ok {
          443                 hugo.Deprecate("The \"_build\" front matter key", "Use \"build\" instead. See https://gohugo.io/content-management/build-options.", "0.145.0")
          444                 buildConfig = v
          445         } else {
          446                 buildConfig = pm.pageConfig.Params["build"]
          447                 isNewBuildKeyword = true
          448         }
          449         pm.pageConfig.Build, err = pagemeta.DecodeBuildConfig(buildConfig)
          450         if err != nil {
          451                 var msgDetail string
          452                 if isNewBuildKeyword {
          453                         msgDetail = `. We renamed the _build keyword to build in Hugo 0.123.0. We recommend putting user defined params in the params section, e.g.:
          454 ---
          455 title: "My Title"
          456 params:
          457   build: "My Build"
          458 ---
          459 ยด
          460 
          461 `
          462                 }
          463                 return fmt.Errorf("failed to decode build config in front matter: %s%s", err, msgDetail)
          464         }
          465 
          466         var sitemapSet bool
          467 
          468         pcfg := pm.pageConfig
          469         params := pcfg.Params
          470         if params == nil {
          471                 panic("params not set for " + p.Title())
          472         }
          473 
          474         var draft, published, isCJKLanguage *bool
          475         var userParams map[string]any
          476         for k, v := range pcfg.Params {
          477                 loki := strings.ToLower(k)
          478 
          479                 if loki == "params" {
          480                         vv, err := maps.ToStringMapE(v)
          481                         if err != nil {
          482                                 return err
          483                         }
          484                         userParams = vv
          485                         delete(pcfg.Params, k)
          486                         continue
          487                 }
          488 
          489                 if loki == "published" { // Intentionally undocumented
          490                         vv, err := cast.ToBoolE(v)
          491                         if err == nil {
          492                                 published = &vv
          493                         }
          494                         // published may also be a date
          495                         continue
          496                 }
          497 
          498                 if pm.s.frontmatterHandler.IsDateKey(loki) {
          499                         continue
          500                 }
          501 
          502                 if loki == "path" || loki == "kind" || loki == "lang" {
          503                         // See issue 12484.
          504                         hugo.DeprecateLevelMin(loki+" in front matter", "", "v0.144.0", logg.LevelWarn)
          505                 }
          506 
          507                 switch loki {
          508                 case "title":
          509                         pcfg.Title = cast.ToString(v)
          510                         params[loki] = pcfg.Title
          511                 case "linktitle":
          512                         pcfg.LinkTitle = cast.ToString(v)
          513                         params[loki] = pcfg.LinkTitle
          514                 case "summary":
          515                         pcfg.Summary = cast.ToString(v)
          516                         params[loki] = pcfg.Summary
          517                 case "description":
          518                         pcfg.Description = cast.ToString(v)
          519                         params[loki] = pcfg.Description
          520                 case "slug":
          521                         // Don't start or end with a -
          522                         pcfg.Slug = strings.Trim(cast.ToString(v), "-")
          523                         params[loki] = pm.Slug()
          524                 case "url":
          525                         url := cast.ToString(v)
          526                         if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
          527                                 return fmt.Errorf("URLs with protocol (http*) not supported: %q. In page %q", url, p.pathOrTitle())
          528                         }
          529                         pcfg.URL = url
          530                         params[loki] = url
          531                 case "type":
          532                         pcfg.Type = cast.ToString(v)
          533                         params[loki] = pcfg.Type
          534                 case "keywords":
          535                         pcfg.Keywords = cast.ToStringSlice(v)
          536                         params[loki] = pcfg.Keywords
          537                 case "headless":
          538                         // Legacy setting for leaf bundles.
          539                         // This is since Hugo 0.63 handled in a more general way for all
          540                         // pages.
          541                         isHeadless := cast.ToBool(v)
          542                         params[loki] = isHeadless
          543                         if isHeadless {
          544                                 pm.pageConfig.Build.List = pagemeta.Never
          545                                 pm.pageConfig.Build.Render = pagemeta.Never
          546                         }
          547                 case "outputs":
          548                         o := cast.ToStringSlice(v)
          549                         // lower case names:
          550                         for i, s := range o {
          551                                 o[i] = strings.ToLower(s)
          552                         }
          553                         pm.pageConfig.Outputs = o
          554                 case "draft":
          555                         draft = new(bool)
          556                         *draft = cast.ToBool(v)
          557                 case "layout":
          558                         pcfg.Layout = cast.ToString(v)
          559                         params[loki] = pcfg.Layout
          560                 case "markup":
          561                         pcfg.Content.Markup = cast.ToString(v)
          562                         params[loki] = pcfg.Content.Markup
          563                 case "weight":
          564                         pcfg.Weight = cast.ToInt(v)
          565                         params[loki] = pcfg.Weight
          566                 case "aliases":
          567                         pcfg.Aliases = cast.ToStringSlice(v)
          568                         for i, alias := range pcfg.Aliases {
          569                                 if strings.HasPrefix(alias, "http://") || strings.HasPrefix(alias, "https://") {
          570                                         return fmt.Errorf("http* aliases not supported: %q", alias)
          571                                 }
          572                                 pcfg.Aliases[i] = filepath.ToSlash(alias)
          573                         }
          574                         params[loki] = pcfg.Aliases
          575                 case "sitemap":
          576                         pcfg.Sitemap, err = config.DecodeSitemap(p.s.conf.Sitemap, maps.ToStringMap(v))
          577                         if err != nil {
          578                                 return fmt.Errorf("failed to decode sitemap config in front matter: %s", err)
          579                         }
          580                         sitemapSet = true
          581                 case "iscjklanguage":
          582                         isCJKLanguage = new(bool)
          583                         *isCJKLanguage = cast.ToBool(v)
          584                 case "translationkey":
          585                         pcfg.TranslationKey = cast.ToString(v)
          586                         params[loki] = pcfg.TranslationKey
          587                 case "resources":
          588                         var resources []map[string]any
          589                         handled := true
          590 
          591                         switch vv := v.(type) {
          592                         case []map[any]any:
          593                                 for _, vvv := range vv {
          594                                         resources = append(resources, maps.ToStringMap(vvv))
          595                                 }
          596                         case []map[string]any:
          597                                 resources = append(resources, vv...)
          598                         case []any:
          599                                 for _, vvv := range vv {
          600                                         switch vvvv := vvv.(type) {
          601                                         case map[any]any:
          602                                                 resources = append(resources, maps.ToStringMap(vvvv))
          603                                         case map[string]any:
          604                                                 resources = append(resources, vvvv)
          605                                         }
          606                                 }
          607                         default:
          608                                 handled = false
          609                         }
          610 
          611                         if handled {
          612                                 pcfg.ResourcesMeta = resources
          613                                 break
          614                         }
          615                         fallthrough
          616                 default:
          617                         // If not one of the explicit values, store in Params
          618                         switch vv := v.(type) {
          619                         case []any:
          620                                 if len(vv) > 0 {
          621                                         allStrings := true
          622                                         for _, vvv := range vv {
          623                                                 if _, ok := vvv.(string); !ok {
          624                                                         allStrings = false
          625                                                         break
          626                                                 }
          627                                         }
          628                                         if allStrings {
          629                                                 // We need tags, keywords etc. to be []string, not []interface{}.
          630                                                 a := make([]string, len(vv))
          631                                                 for i, u := range vv {
          632                                                         a[i] = cast.ToString(u)
          633                                                 }
          634                                                 params[loki] = a
          635                                         } else {
          636                                                 params[loki] = vv
          637                                         }
          638                                 } else {
          639                                         params[loki] = []string{}
          640                                 }
          641 
          642                         default:
          643                                 params[loki] = vv
          644                         }
          645                 }
          646         }
          647 
          648         for k, v := range userParams {
          649                 params[strings.ToLower(k)] = v
          650         }
          651 
          652         if !sitemapSet {
          653                 pcfg.Sitemap = p.s.conf.Sitemap
          654         }
          655 
          656         if draft != nil && published != nil {
          657                 pcfg.Draft = *draft
          658                 p.m.s.Log.Warnf("page %q has both draft and published settings in its frontmatter. Using draft.", p.File().Filename())
          659         } else if draft != nil {
          660                 pcfg.Draft = *draft
          661         } else if published != nil {
          662                 pcfg.Draft = !*published
          663         }
          664         params["draft"] = pcfg.Draft
          665 
          666         if isCJKLanguage != nil {
          667                 pcfg.IsCJKLanguage = *isCJKLanguage
          668         } else if p.s.conf.HasCJKLanguage && p.m.content.pi.openSource != nil {
          669                 if cjkRe.Match(p.m.content.mustSource()) {
          670                         pcfg.IsCJKLanguage = true
          671                 } else {
          672                         pcfg.IsCJKLanguage = false
          673                 }
          674         }
          675 
          676         params["iscjklanguage"] = pcfg.IsCJKLanguage
          677 
          678         if err := pcfg.Init(false); err != nil {
          679                 return err
          680         }
          681 
          682         if err := pcfg.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
          683                 return err
          684         }
          685 
          686         return nil
          687 }
          688 
          689 // shouldList returns whether this page should be included in the list of pages.
          690 // global indicates site.Pages etc.
          691 func (p *pageMeta) shouldList(global bool) bool {
          692         if p.isStandalone() {
          693                 // Never list 404, sitemap and similar.
          694                 return false
          695         }
          696 
          697         switch p.pageConfig.Build.List {
          698         case pagemeta.Always:
          699                 return true
          700         case pagemeta.Never:
          701                 return false
          702         case pagemeta.ListLocally:
          703                 return !global
          704         }
          705         return false
          706 }
          707 
          708 func (p *pageMeta) shouldListAny() bool {
          709         return p.shouldList(true) || p.shouldList(false)
          710 }
          711 
          712 func (p *pageMeta) isStandalone() bool {
          713         return !p.standaloneOutputFormat.IsZero()
          714 }
          715 
          716 func (p *pageMeta) shouldBeCheckedForMenuDefinitions() bool {
          717         if !p.shouldList(false) {
          718                 return false
          719         }
          720 
          721         return p.pageConfig.Kind == kinds.KindHome || p.pageConfig.Kind == kinds.KindSection || p.pageConfig.Kind == kinds.KindPage
          722 }
          723 
          724 func (p *pageMeta) noRender() bool {
          725         return p.pageConfig.Build.Render != pagemeta.Always
          726 }
          727 
          728 func (p *pageMeta) noLink() bool {
          729         return p.pageConfig.Build.Render == pagemeta.Never
          730 }
          731 
          732 func (p *pageMeta) applyDefaultValues() error {
          733         if p.pageConfig.Build.IsZero() {
          734                 p.pageConfig.Build, _ = pagemeta.DecodeBuildConfig(nil)
          735         }
          736 
          737         if !p.s.conf.IsKindEnabled(p.Kind()) {
          738                 (&p.pageConfig.Build).Disable()
          739         }
          740 
          741         if p.pageConfig.Content.Markup == "" {
          742                 if p.File() != nil {
          743                         // Fall back to file extension
          744                         p.pageConfig.Content.Markup = p.s.ContentSpec.ResolveMarkup(p.File().Ext())
          745                 }
          746                 if p.pageConfig.Content.Markup == "" {
          747                         p.pageConfig.Content.Markup = "markdown"
          748                 }
          749         }
          750 
          751         if p.pageConfig.Title == "" && p.f == nil {
          752                 switch p.Kind() {
          753                 case kinds.KindHome:
          754                         p.pageConfig.Title = p.s.Title()
          755                 case kinds.KindSection:
          756                         sectionName := p.pathInfo.Unnormalized().BaseNameNoIdentifier()
          757                         if p.s.conf.PluralizeListTitles {
          758                                 sectionName = flect.Pluralize(sectionName)
          759                         }
          760                         if p.s.conf.CapitalizeListTitles {
          761                                 sectionName = p.s.conf.C.CreateTitle(sectionName)
          762                         }
          763                         p.pageConfig.Title = sectionName
          764                 case kinds.KindTerm:
          765                         if p.term != "" {
          766                                 if p.s.conf.CapitalizeListTitles {
          767                                         p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
          768                                 } else {
          769                                         p.pageConfig.Title = p.term
          770                                 }
          771                         } else {
          772                                 panic("term not set")
          773                         }
          774                 case kinds.KindTaxonomy:
          775                         if p.s.conf.CapitalizeListTitles {
          776                                 p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
          777                         } else {
          778                                 p.pageConfig.Title = strings.Replace(p.pathInfo.Unnormalized().BaseNameNoIdentifier(), "-", " ", -1)
          779                         }
          780                 case kinds.KindStatus404:
          781                         p.pageConfig.Title = "404 Page not found"
          782                 }
          783         }
          784 
          785         return nil
          786 }
          787 
          788 func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.Converter, error) {
          789         if ps == nil {
          790                 panic("no Page provided")
          791         }
          792         cp := p.s.ContentSpec.Converters.Get(markup)
          793         if cp == nil {
          794                 return converter.NopConverter, fmt.Errorf("no content renderer found for markup %q, page: %s", markup, ps.getPageInfoForError())
          795         }
          796 
          797         var id string
          798         var filename string
          799         var path string
          800         if p.f != nil {
          801                 id = p.f.UniqueID()
          802                 filename = p.f.Filename()
          803                 path = p.f.Path()
          804         } else {
          805                 path = p.Path()
          806         }
          807 
          808         doc := newPageForRenderHook(ps)
          809 
          810         documentLookup := func(id uint64) any {
          811                 if id == ps.pid {
          812                         // This prevents infinite recursion in some cases.
          813                         return doc
          814                 }
          815                 if v, ok := ps.pageOutput.pco.otherOutputs.Get(id); ok {
          816                         return v.po.p
          817                 }
          818                 return nil
          819         }
          820 
          821         cpp, err := cp.New(
          822                 converter.DocumentContext{
          823                         Document:       doc,
          824                         DocumentLookup: documentLookup,
          825                         DocumentID:     id,
          826                         DocumentName:   path,
          827                         Filename:       filename,
          828                 },
          829         )
          830         if err != nil {
          831                 return converter.NopConverter, err
          832         }
          833 
          834         return cpp, nil
          835 }
          836 
          837 // The output formats this page will be rendered to.
          838 func (m *pageMeta) outputFormats() output.Formats {
          839         if len(m.pageConfig.ConfiguredOutputFormats) > 0 {
          840                 return m.pageConfig.ConfiguredOutputFormats
          841         }
          842         return m.s.conf.C.KindOutputFormats[m.Kind()]
          843 }
          844 
          845 func (p *pageMeta) Slug() string {
          846         return p.pageConfig.Slug
          847 }
          848 
          849 func getParam(m resource.ResourceParamsProvider, key string, stringToLower bool) any {
          850         v := m.Params()[strings.ToLower(key)]
          851 
          852         if v == nil {
          853                 return nil
          854         }
          855 
          856         switch val := v.(type) {
          857         case bool:
          858                 return val
          859         case string:
          860                 if stringToLower {
          861                         return strings.ToLower(val)
          862                 }
          863                 return val
          864         case int64, int32, int16, int8, int:
          865                 return cast.ToInt(v)
          866         case float64, float32:
          867                 return cast.ToFloat64(v)
          868         case time.Time:
          869                 return val
          870         case []string:
          871                 if stringToLower {
          872                         return helpers.SliceToLower(val)
          873                 }
          874                 return v
          875         default:
          876                 return v
          877         }
          878 }
          879 
          880 func getParamToLower(m resource.ResourceParamsProvider, key string) any {
          881         return getParam(m, key, true)
          882 }
          883 
          884 func (ps *pageState) initLazyProviders() error {
          885         ps.init.Add(func(ctx context.Context) (any, error) {
          886                 pp, err := newPagePaths(ps)
          887                 if err != nil {
          888                         return nil, err
          889                 }
          890 
          891                 var outputFormatsForPage output.Formats
          892                 var renderFormats output.Formats
          893 
          894                 if ps.m.standaloneOutputFormat.IsZero() {
          895                         outputFormatsForPage = ps.m.outputFormats()
          896                         renderFormats = ps.s.h.renderFormats
          897                 } else {
          898                         // One of the fixed output format pages, e.g. 404.
          899                         outputFormatsForPage = output.Formats{ps.m.standaloneOutputFormat}
          900                         renderFormats = outputFormatsForPage
          901                 }
          902 
          903                 // Prepare output formats for all sites.
          904                 // We do this even if this page does not get rendered on
          905                 // its own. It may be referenced via one of the site collections etc.
          906                 // it will then need an output format.
          907                 ps.pageOutputs = make([]*pageOutput, len(renderFormats))
          908                 created := make(map[string]*pageOutput)
          909                 shouldRenderPage := !ps.m.noRender()
          910 
          911                 for i, f := range renderFormats {
          912 
          913                         if po, found := created[f.Name]; found {
          914                                 ps.pageOutputs[i] = po
          915                                 continue
          916                         }
          917 
          918                         render := shouldRenderPage
          919                         if render {
          920                                 _, render = outputFormatsForPage.GetByName(f.Name)
          921                         }
          922 
          923                         po := newPageOutput(ps, pp, f, render)
          924 
          925                         // Create a content provider for the first,
          926                         // we may be able to reuse it.
          927                         if i == 0 {
          928                                 contentProvider, err := newPageContentOutput(po)
          929                                 if err != nil {
          930                                         return nil, err
          931                                 }
          932                                 po.setContentProvider(contentProvider)
          933                         }
          934 
          935                         ps.pageOutputs[i] = po
          936                         created[f.Name] = po
          937 
          938                 }
          939 
          940                 if err := ps.initCommonProviders(pp); err != nil {
          941                         return nil, err
          942                 }
          943 
          944                 return nil, nil
          945         })
          946 
          947         return nil
          948 }