Musings tagged as hugo

In order to get the new site up and running and switching to Hugo a few things were missing.

Pagination

I did not want a Next and Previous navigation as this gets really annoying with lots of pages. I wanted a list of pages, not too long through, and the ability to quickly traverse pages.

First we define the parameters for number of shown pages and at which position the active page is supposed to be located. This neatly describes the number of previous and following pages to generate. This goes into the config.toml site configuration:

[params]
	pgtrspan = 6
	pgtractive = 3

Then this goes into a partial template which you can include to show the paginator, lets call this pagenav.html:

<div id="page-bar">
  {{ $curpage := .Paginator.PageNumber }}
  {{ $lastpage := .Paginator.TotalPages }}

  <!-- if we have previous pages show quick rewind buttons -->
  {{ if .Paginator.HasPrev }}
  <div class="pagenumber">
   <a href="{{ .Paginator.First.URL }}"><span class="oi" data-glyph="caret-left" title="previous page" aria-hidden="true"></span><span class="oi" data-glyph="caret-left" title="previous page" aria-hidden="true"></span></a>
  </div>
  <div class="pagenumber">
   <a href="{{ .Paginator.Prev.URL }}"><span class="oi" data-glyph="caret-left" title="previous page" aria-hidden="true"></span></a>
  </div>
  {{ end }}

  <!-- get the width of the navpage in number of pages -->
  {{ $pgtrspan := $.Site.Params.pgtrspan }}

  <!-- get the index of the active page -->
  {{ $pgtractive := $.Site.Params.pgtractive }}

  
  {{ $lowerbound := sub $curpage (sub $pgtrspan (add $pgtractive 1)) }}
  {{ $upperbound := add $curpage (sub $pgtrspan $pgtractive) }}

  {{ if le $upperbound $pgtrspan }}
    {{ $.Scratch.Set "lowerbound" 1 }}
    {{ $.Scratch.Set "upperbound" $pgtrspan }}
  {{ else if gt $upperbound $lastpage }}
    {{ $.Scratch.Set "lowerbound" (add (sub $lastpage $pgtrspan) 1) }}
    {{ $.Scratch.Set "upperbound" $lastpage }}
  {{ else }}
    {{ $.Scratch.Set "lowerbound" $lowerbound }}
    {{ $.Scratch.Set "upperbound" $upperbound }}
  {{ end }}

  <!-- loop through paginator pages and only display stuff within the boundaries -->
  {{ range $id, $pager := .Paginator.Pagers }}
  {{ if and (ge $pager.PageNumber ($.Scratch.Get "lowerbound")) (le $pager.PageNumber ($.Scratch.Get "upperbound")) }}
  <div class="pagenumber">
    {{ if eq $curpage $pager.PageNumber }}
      {{ $pager.PageNumber }}
    {{ else }}
      <a href="{{ $pager.URL }}">{{ $pager.PageNumber }}</a>
    {{ end }}
  </div>
  {{ end }}
  {{ end }}

  <!-- if we have following pages show quick forward buttons -->
  {{ if .Paginator.HasNext }}
  <div class="pagenumber">
   <a href="{{ .Paginator.Next.URL }}"><span class="oi" data-glyph="caret-right" title="next page" aria-hidden="true"></span></a>
  </div>
  <div class="pagenumber">
   <a href="{{ .Paginator.Last.URL }}"><span class="oi" data-glyph="caret-right" title="previous page" aria-hidden="true"></span><span class="oi" data-glyph="caret-right" title="previous page" aria-hidden="true"></span></a>
  </div>
  {{ end }}
</div>

So, finally just put this below whatever paginator range you like:

{{ $paginator := .Paginate .Data.Pages }}
{{ range $paginator.Pages }}
<article>
    <h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2>
    {{ .Content }}
</article>
{{ end }}
{{ partial "pagenav.html" . }}

And presto! Proper pagination bar.

Post Archive

Now the archive business is rather simple but took me a while to figure out as the documentation is really lacking in this regard. There are a couple of examples you can find online but none of them really did it the exact way I wanted it. Yearly archives are fairly easy to do but scrolling through a year worth of posts can be just as annoying if the post volume is high enough. So, ideally I want monthly archives but grouped by year. Leveraging this with Hugo’s taxonomy system is a tiny bit annoying but once it works it is perfectly serviceable.

First of all, add an archive taxonomy to your site’s config.toml

[taxonomies]
	archive = "archive"

Next, on all your posts, add the year and month as seperate taxonomy terms in the front matter

+++
title = "Wabbit"
date = 2017-12-09T16:59:48+01:00
archive = ["2017","2017-12"]
+++

So, the problem is how do we separate the YYYY terms from the YYYY-MM terms? Simple answer is, we don’t. When do we want the archive to start? That’s right, from the year and month we made the first post. When do we want it to end? How about at the latest post? So we create the termlisting taxonomy file as archive.terms.html which goes into one of the proper folders, in my case I use the layouts/taxonomy folder in the theme.

We then loop over all the years between the oldest and the newest post and after printing the list item for said year we loop through the terms. Only if a term matches the YYYY-MM format with YYYY matching the year we are currently indexing will it be printed. And since printing the term as is would be redundant as it contains the year again, we beautify it by use of dateFormat.

By the way, if someone knows a more elegant way in Hugo/Go-Template syntax to return true or false depending on whether a RegEx matches a string please drop me a line. The combination of len, findRE and a comparison operation was the shortest way I could think of with the limited toolset you get.

{{ partial "header.html" . }}

<section id="archive">
  <h1>Archive</h1>
  <ul>
    {{ $data := .Data }}
    {{ $firstyear := (index .Data.Pages.ByDate 1).Date.Format "2006" }}
    {{ $currentyear := now.Format "2006" }}

    {{ range $i,$year := (seq $currentyear $firstyear) }}
      <li><a href="/{{ $data.Plural }}/{{ $year }}">{{ $year }}</a><ul>
      {{ range $key, $value := $data.Terms.Alphabetical.Reverse }}
        {{ if gt (len (findRE (print "^" $year "-\\d\\d$") $value.Name)) 0 }}
          <li><a href="/{{ $data.Plural }}/{{ $value.Name }}">{{ dateFormat "January" (print $value.Name "-01") }}</a> ({{ $value.Count }})</li>
	{{ end }}
      {{ end }}
      </ul></li>
    {{ end }}
  </ul>
</section>

{{ partial "footer.html" . }}

Now, all that is left is to create archive.html which will be used to actually display the paginated list of articles for a year or for a month, depending on which gets selected by the viewer. We only check whether the term is in the YYYY or YYYY-MM format and output a header which is formatted accordingly. The rest is just simple iterating through the posts.

{{ partial "header.html" . }}

{{ $paginator := .Paginate (where .Data.Pages "Type" "musings") 10 }}
{{ $data := .Data }}

{{ if gt (len (findRE "\\d\\d\\d\\d-\\d\\d$" .Data.Term)) 0 }}
{{ $.Scratch.Set "heading" (print (dateFormat "January" (print $data.Term "-01")) " " (replaceRE "-\\d\\d$" "" $data.Term)) }}
{{ else }}
{{ $.Scratch.Set "heading" $data.Term }}
{{ end }}

<h1>Archive for {{ $.Scratch.Get "heading" }}</h1>
{{ range $paginator.Pages }}
<article>
  <h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2>
    {{ .Content }}
</article>
{{ end }}
{{ partial "pagenav.html" . }}

{{ partial "footer.html" . }}

And we’re done.

1