Change template variable resolution rules

I’ve submitted a small PR to change Site.Author from map[string]string to map[string]interface{} because it will help me with some template work that I’m doing, and I’d like to propose a possible change to how some things work with Hugo and variable resolution. I’ll admit up front that I currently have no idea how this would be implemented, that the change may end up being hugely incompatible with current behaviour, and that it may not be possible within the confines of the templating engine(s) Hugo supports.

I think it would be useful to implement some sort of dynamic resolution so that .Params.foo looks in <page>.Params for foo, and then it looks in <site>.Params for foo. This would only work on Node level objects (that is, nodes and pages), and could potentially be implemented through a new function that does this implicitly. This would work with more than just Params, but with other values that are either shared (Title) or pushed down some values from Site to the Node or Page (e.g., Author).

I’ll give an example of what I mean with a template that I’m adapting from @tom’s Red Lounge for a theme that I’m calling cabaret. The purpose here is to show social icons for an author if they either have an array map parameter Params.author.social or if they have a string array parameter Params.author.social-sites. I’ve also got support for Site.Author.social-sites (the purpose of PR#600, above), because my Params.social is a site-wide collection of social sites that I use for other purposes, and I can reuse here by matching each value of social-sites against the name value in Params.social.

<!-- layouts/partials/author/social/list.html -->
{{ if not .Params.noauthor }}
  {{ if isset .Params.author "social" }}
    {{ range .Params.author.social }}
      {{ partial "author/social/site" . }}
    {{ end }}
  {{ else if isset .Params.author "social-sites" }}
    {{ $site := .Site }}
    {{ range $name := index .Params.author "social-sites" }}
      {{ range $site.Params.social }}
        {{ if eq $name .site }}
          {{ partial "author/social/site" . }}
        {{ end }}
      {{ end }}
    {{ end }}
  {{ else if and (not .Site.Params.noauthor) (isset .Site.Author "social-sites") }}
    {{ $site := .Site }}
    {{ range $name := index $site.Author "social-sites" }}
      {{ range $site.Params.social }}
        {{ if eq $name .site }}
          {{ partial "author/social/site" . }}
        {{ end }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

With the sort of change that I’m suggesting here, the template shortens, because Hugo is doing the fallback-to-Site that I am doing above (and the shortened change also adds the case of having a .Site.Author.social map, which I don’t support in my code above).

<!-- layouts/partials/author/social/list.html -->
{{ if not .Params.noauthor }}
  {{ if isset .Author "social" }}
    {{ range .Author.social }}
      {{ partial "author/social/site" . }}
    {{ end }}
  {{ else if isset .Author "social-sites" }}
    {{ $site := .Site }}
    {{ range $name := index .Author "social-sites" }}
      {{ range $site.Params.social }}
        {{ if eq $name .site }}
          {{ partial "author/social/site" . }}
        {{ end }}
      {{ end }}
    {{ end }}
  {{ end }}
{{ end }}

This is obviously a pretty big change, and I’m definitely not suggesting it for 0.13, but if it sounds like a good change (I’d like to be able to have more overrides and fallbacks pretty much everywhere, including inside Hugo’s internals) I can start trying to figure out how it might work.

1 Like