gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
75.75k stars 7.53k forks source link

Add arbitrary front matter parameter as a new filter to _cascade.target #10936

Open jmooring opened 1 year ago

jmooring commented 1 year ago

Reference: https://discourse.gohugo.io/t/render-some-pages-only-in-non-production/44306/7

content/tutorials/how-to-change-your-password.md

+++
title = 'How to change your password'
date = 2023-05-10T11:46:46-07:00
draft = false
foo = 'something'   # This could be string, bool, int, etc.
+++
[[cascade]]

[cascade._build]
list = 'never'
render = 'never'

[cascade._target]
environment = '{production}'
foo = 'something'      # This needs some thought (`params.foo` ?) 

N.B. Edited example above to reflect new issue title per this comment

bep commented 1 year ago

We already have section, right? Looking at this now I think it was a mistake not to just use type back when we added that. For all other purposes in Hugo, type==section if not set in front matter.

Could we not just rename (and map old values) it to type and use the .Type method on Page to filter? Or are there cases where we need both?

jmooring commented 1 year ago

We can currently target environment, language, kind, and path (which obviously gets you section as well).

type==section if not set in front matter.

And that's what this issue is about: targeting pages based on something in front matter. In some cases targeting type would be useful, and in other cases targeting a page parameter would be better.

This may not be easy or efficient to do: we would need to read front matter before cascading front matter.

bep commented 1 year ago

We can currently target environment, language, kind, and path (which obviously gets you section as well).

OK, then my memory did not serve me well, I thought we has a section field. Never mind, we can add a type.

jmooring commented 1 year ago

Thinking more about this, targeting pages based on type in front matter is not a good idea, at least for the use case described in the related topic---it would preclude you from using type to control which layout is used.

The right way is to target based on a custom front matter parameter, which may not be possible—it seems like front matter parameters are not available until after we cascade front matter parameters down from site configuration. Changing issue title...

sanderjson commented 1 year ago

What is different about draft: true?

...
draft: true
internal: false

& can update defaults per archetype (hugo new docnote)

bep commented 1 year ago

The right way is to target based on a custom front matter parameter, which may not be possible—it seems like front matter parameters are not available until after we cascade front matter parameters down from site configuration.

I don't understand/see the limitation. I'm pretty sure the cascade is applied after we have read front matter.

What is different about draft: true?

Not much.

jmooring commented 1 year ago

@sanderjson Good idea, and workable in some scenarios, but using draft hijacks the draft functionality. The goal is to generate two or more unique builds, where the inclusion/exclusion is arbitrary, at the page level, but not based on path. Cascading build options targeted to a front matter parameter seems like an elegant solution.

The next best approach that I could think of was multiple content directories, mounted based on environment, and we can do that today.

@bep Not sure about the limitation, but it wasn't obvious to me how to get what I wanted.

bep commented 1 year ago

@bep Not sure about the limitation, but it wasn't obvious to me how to get what I wanted.

Yea, to do this I think you would have to expand the Matches method with a map[string]any (which may be nil) and pass the frontmatter found here:

https://github.com/gohugoio/hugo/blob/master/hugolib/page__meta.go#L387

This behaviour needs to be carefully described in the documentation, though, as people may think they can filter on the final, cascaded front matter, which is not possible due to the ... chicken and egg situation.

But I do agree that it would be very useful to have in some situations. I assume it would look like this?

title = 'Blog'
[[cascade]]
background = 'yosemite.jpg'
[cascade._target]
kind = 'page'
[cascade._target.meta]
type = '{blog,post}'
foo = 'bar`

I have deliberately avoided the Markdown specific term front matter in the above, but that may be a too good/familar a term to give up.

sanderjson commented 1 year ago

@sanderjson Good idea, and workable in some scenarios, but using draft hijacks the draft functionality. The goal is to generate two or more unique builds, where the inclusion/exclusion is arbitrary, at the page level, but not based on path. Cascading build options targeted to a front matter parameter seems like an elegant solution.

I was suggesting that the mechanisms currently in place for drafts might make for a good experience. I agree that we do not want to hijack the draft functionality. I was proposing that there could exist four options internal, draft, internal, not draft, not internal, draft, not internal, not draft, and the user could set the defaults in archetype.

bep commented 1 year ago

I was suggesting that the mechanisms currently in place for drafts might make for a good experience.

This issue is about cascade in which the draft command would also benefit (you can draft all pages matching some critierias).

Also, the draft keyword has internally in Hugo been revamped with the more powerful _build keyword, so draft translates into:

[_build]
  list = 'never'
  publishResources = false
  render = 'never'
tomowang commented 7 months ago

It seems that cascade use PageMatcher for _target as the key of map and use such data structure for seraching and merging descendants

if ps.m.pageConfig.Cascade != nil {
    for k, v := range cascade {
        vv, found := ps.m.pageConfig.Cascade[k]
        if !found {
            ps.m.pageConfig.Cascade[k] = v
        } else {
            // Merge
            for ck, cv := range v {
                if _, found := vv[ck]; !found {
                    vv[ck] = cv
                }
            }
        }
    }
    cascade = ps.m.pageConfig.Cascade
}

Add meta like map[string]any to PageMatcher will break all these. Any suggestions?