Comment Creer Un Site Avec Hugo Partie 4 : Le Template Des Articles

Aldok

| 13 minutes

Revenir à l'index du tuto : Creer un site avec hugo

Créer un site avec Hugo partie 4 : le template des articles

Pour l'instant, nous n'avons aucun moyen de naviguer dans les différents posts depuis la page d'accueil - à part en recopiant les URL's dans la barre d'adresse évidemment.

On va donc voir comment lister automatiquement les différents articles (ou “pages enfants”) de la section blog.

1. Créer le template des pages qui vont lister d'autres pages

Dans le dossier layouts/_default/ , on peut voir qu'il y a un fichier list.html.

Il s'agit du template par défaut des pages qui affichez des listes, comme la page /blog dont la fonction est de lister les différents articles qui en dépendent.

Dans le fichier list.html (qui est vide et qui donc ne permet pas d'afficher quoi que ce soit dans la partie blog), ajoutez ces lignes :

{{ define "main" }}
<div class="container">
    <div class="section">
        <div class="content">
            <h1>{{ .Title }}</h1>
            {{ .Content }}
            {{ range .Pages }}
            <a href="{{ .Permalink }}">{{ .Title }}</a>
            {{ end }}
        </div>
    </div>
</div>
{{ end }}

Avec ce template tout simple, on va pouvoir afficher la liste des pages qui dépendent des différents répertoires que vous créez dans /content

Quelques explications sur le code généré :

{{ .Title }} 

Reprend le titre indiqué dans le frontmatter des articles créés en MarkDown

{{ .Content }}

Permet d'afficher le contenu de l'article créé en MarkDown

{{ range .Pages }}
   <a href="{{ .Permalink }}">{{ .Title }}</a>
{{ end }}

La boucle range .Pages va chercher toutes les pages présentes dans le dossier dans lequel on se trouve, et affichera le titre avec son lien pour chaque élément trouvé

// Petit rappel :

Pour rappel, Hugo va générer l'arborescence du site en fonction de l'architecture des dossiers créés. Ainsi, si on créer un dossier /blog qui contient “premier-post.md” et “deuxieme-post.md”, ça va générer les pages suivantes sur le site : https://domaine.tld/blog/premier-post et https://domaine.tld/blog/deuxieme-post .

De la même façon, si on créé un autre répertoire /categorie1, et que l'on y ajoute des articles, ils seront générés côté public avec la logique https://domaine.tld/categorie1/titre-du-post

// Fin petit rappel

Revenons à nos moutons : telle que nous l'avons construite, la page générée grâce au template list.html permet de ne lister que les articles présents dans le dossier. Si on veut la personnaliser un peu, et rajouter du contenu (comme la description de la catégorie), il va falloir créer une page _index.md à la racine du dossier /blog.

On va donc la créer avec la commande suivante :

hugo new blog/_index.md

Contenu de _index.md :

---
title: "Blog"
date: 2020-04-06T11:00:27+02:00
draft: false
---

# Titre de mon super blog
Bonjour et bienvenue dans la catégorie "blog"

Côté public on devrait avoir quelque chose dans ce genre (attention, ça dépend du contenu de votre dossier /blog ; ici je n'ai qu'un article intitulé “Mon Premier Post” mais vous pouvez en avoir plus :

Exemple premier post

2. Créer le template des pages de contenus

On va commencer par créer une nouvelle page enfant dans le dossier /blog , et même soyons fous dans un sous-dossier /2020

hugo new blog/2020/premier-post-de-2020.md

Contenu du fichier premier-post-de-2020.md :

---
title: "Mon premier post de l'année 2020"
date: 2020-03-29T11:13:57+02:00
draft: false
---

## Test nouveau post

Bonjour ceci est un article de test

### Titre de niveau 3

Contenu relatif au titre niveau 3

### Titre de niveau 3 bis

Contenu relatif au titre niveau 3 bis
etc.

Côté public, ça va afficher le contenu, mais sans aucun style ; on va remédier à ça en modifiant le template relatif aux pages enfants,

Pour personnaliser l'apparence des pages qui vont se trouver dans la catégorie /blog du site, il suffit de créer un sous-répertoire dans layout qui porte le même nom, à savoir /blog.

En d'autres mots, et là qu'on voit que Hugo est bien foutu et assez simple à prendre en main quand on a compris les mécanismes, il va appliquer tout template qui se trouve dans ce répertoire en priorité à tout contenu qui se trouvera dans le sous-répertoire content/blog

On va donc créer le template qui permet de gérer l'apparence des articles en créant un fichier single.html dans ce dossier /blog

On se retrouve donc avec une arborescence faite ainsi :

/layouts/blog/single.html

Ajoutons maintenant du contenu au fichier single.html :

{{ define "main" }}
<section class="section">
  <article>       
    <div class="columns is-centered">
      <div class="column max-800px">
        <h1 class="title is-1">{{ .Title }}</h1>
        <div class="content">
          {{ .Content }}
        </div>
      </div>
    </div>    
  </article>
</section>
{{ end }}

Si vous avez bien tout suivi, je structure mon contenu en utilisant des classes Bulma autour des boucles {{ .Title }} et {{ .Content }}

Pour avoir quelque chose de plus sympa visuellement, je vais également définir la largeur à 800px en ajoutant simplement cette classe à mon fichier style.css :

.max-800px {
max-width: 800px;
}

J'en profite également pour ajouter un peu de Lorem Ipsum afin de densifier le contenu, on se rend mieux compte du résultat final côté public, sur la page /blog/2020/premier-post-de-2020 fraîchement créé :

Exemple de post publie avec du lorem ipsum

On va rester un peu dans cette rubrique “blog” : à votre avis, comment modifier l'apparence de la page qui va lister tous les articles de cette catégorie, et uniquement cette page ?

En créant un template list.html dans le dossier /blog !

Faites le test pour voir : passez le titre en rouge avec un style inline. On devrait avoir un truc dans ce genre là dans le fichier layouts/blog/list.html :

{{ define "main" }}
<div class="container">
    <div class="section">
        <div class="content">
            <h1 style="color:red">{{ .Title }}</h1>
            {{ .Content }}
            {{ range .Pages }}
            <a href="{{ .Permalink }}">{{ .Title }}</a>
            {{ end }}
        </div>
    </div>
</div>
{{ end }}

Ensuite, rdv à la page /blog côté public : vous devriez voir le titre s'afficher en rouge. Le template blog/list.html a donc bel et bien prit la priorité sur le fichier _default/list.html

(Vous pouvez effacer ce fichier, c'était juste pour confirmer la bonne compréhension des priorités des templates !)

3. Améliorer l'ensemble : les metadatas, la photo de l'auteur, et l'image mise en avant

Les métadonnées et la photo de l'auteur :

Pour ajouter une photo d'auteur, il faut d'abord ajouter l'image à utiliser dans un dossier qui stocke les images ; avec Hugo, on fait ça dans le dossier /static/images

Ensuite, il faut déterminer l'image de l'auteur dans le fichier de configuration générale config.toml (Pour l'exemple, on va imaginer que j'utilise une photo intitulée avatar.jpg):

[params]
    author = "Alex"
    authorImage = "/images/avatar.jpg"

Et bien sûr pour charter tout ça, on va lui appliquer un peu de CSS dans style.css :

.author-image {
    object-fit: cover;
    border-radius: 50%;
    width: 48px;
    height: 48px;
}

Ça va permettre de créer une image de 48px arrondie.

Maintenant, ajoutons le code d'intégration à single.html pour inclure la photo, et les métadonnées :

<div class="title subtitle heading is-6">
    <div class="columns is-vcentered is-mobile">
      {{ with .Site.Params.authorImage }}
      <div class="column is-narrow">
        <img src="{{ . }}" class="author-image">
      </div>
      {{ end }}
      <div class="column">
        <p>{{ .Site.Params.Author }}</p>
        <p><time>{{ .PublishDate.Format "January 2, 2006" }}</time> | 
            {{ .ReadingTime }} {{ if eq .ReadingTime 1 }} minute {{ else }} minutes {{ end }}</p>
      </div>
    </div>
</div>

Le fichier doit ressembler à ça dans son intégralité :

Contenu de layout/blog/single.html

{{ define "main" }}
<section class="section">
  <article>
    <div class="columns is-centered">
        <div class="column max-800px">
          <h1 class="title is-1">{{ .Title }}</h1>
          <div class="title subtitle heading is-6">
            <div class="columns is-vcentered is-mobile">
              {{ with .Site.Params.authorImage }}
              <div class="column is-narrow">
                <img src="{{ . }}" class="author-image">
              </div>
              {{ end }}
              <div class="column">
                <p>{{ .Site.Params.Author }}</p>
                <p><time>{{ .PublishDate.Format "January 2, 2006" }}</time> | 
                    {{ .ReadingTime }} {{ if eq .ReadingTime 1 }} minute {{ else }} minutes {{ end }}</p>
              </div>
            </div>
          </div>
          <div class="content">
            {{ .Content }}
          </div>
        </div>
      </div>
  </article>
</section>
{{ end }}

Là encore, je ne vais pas m'étendre sur les spécificités des classes de Bulma, ce n'est pas l'objectif de l'article ; ça permet en gros de définir une structure en 2 colonnes (une petite optionnelle pour l'avatar, et le reste de la place pour les métadonnées), adaptée aux mobiles bien entendu.

Petit focus sur les “2 mins” affichées après la date : il s'agit d'une fonction propre à Hugo qui calcule approximativement le temps de lecture de l'article. Je n'ai pas plus de détails à ce sujet, et je n'en ai pas vraiment cherché pour le moment : ça pourrait faire l'objet d'un petit article complémentaire.

En ce qui concerne la date par contre, j'ai cherché et à priori pour le moment Hugo ne prends pas en compte la traduction automatiquement ; par défaut, le format sera basé sur le modèle anglais. On peut se rapprocher du modèle français en changeant l'ordre par JJ - MM - AAAA mais ça restera des mois en anglais…

Quelques pistes sont néanmoins à envisager : https://www.christopheducamp.com/2017/07/27/r1d4--mise-en-forme-des-dates-dans-gohugo/

Bref, au final côté public on devrait avoir un rendu dans ce style :

Exemple de titre

L'illustration de l'article (ou “l'image à la une” ;-) ) :

Pour l'exemple, on va utiliser une image d'illustration trouvée sur Pixabay ou Unsplash. Je vais prendre l'image d'un cliché de la Terre vue de l'espace, taille réduite.

Maintenant la question qui tue : où est-ce qu'on stocke l'image ? On serait tenté de répondre : dans le dossier “static” de mon thème ? Et bien non, l'image étant l'illustration d'un article, elle n'est pas relative au thème dans son ensemble, mais plutôt au contenu : on va donc la mettre dans le dossier /static présent à la racine du site.

Là encore, il va falloir s'organiser : libre à vous de définir l'arborescence pour ranger vos images (par date ? par thématiques ?). Pour faire simple, je vais la mettre dans un sous-dossier static/images/blog/

Une fois l'image dans son dossier, il va falloir l'appeler via le front matter de l'article.

Voilà la ligne à ajouter au frontmatter de l'article “Premier post de 2020” :

images: ["/images/blog/terre.jpg"]

Notez bien qu'il n'y a pas besoin de rappeler le dossier /static : c'est de là que partira Hugo pour chercher les images.

Ensuite, il faut l'afficher dans le template de single.html. On va la place juste avant le contenu :

{{ with .Params.images }}
<figure class="title-image"><img src="{{ index . 0 }}" alt=""></figure>
{{ end }}

On remarquera que l'image colle un peu trop au titre de l'article : pour ça il suffit de modifier un peu la classe title-image dans style.css en ajoutant ceci :

.title-image {
    padding-bottom: 1.5em;
}

4. Afficher les catégories et la pagination vers les articles précédents & suivants

4.1 Le lien vers les catégories

Via Hugo, on peut jouer avec les taxonomies. Pour rappel, une taxonomie est le nom un peu barbare utilisé pour définir des options de tri : par exemple, des catégories ou des tags.

Pour ajouter une taxonomie à un article, il faut ajouter une variable dans le front matter : soit la variable categories, soit la variable tags

Nous allons utiliser la variable categories pour l'exemple, en affectant 2 catégories à l'article en cours : Blogging et Développement Web.

categories : ["Blogging", "Développement web"]

Ensuite, il faut afficher ces catégories via le template. Pour ce faire, on va modifier le template comme ceci à partir de la <div class="title subtitle heading is-6"> :

<div class="title subtitle heading is-6">
	<div class="columns is-vcentered">
		<div class="column">
			<div class="columns is-vcentered is-mobile">
				{{ with .Site.Params.authorImage }}
					<div class="column is-narrow">
						<img src="{{ . }}" class="author-image">
					</div>
				{{ end }}
				<div class="column">
					<p>{{ .Site.Params.Author }}</p>
					<p><time>{{ .PublishDate.Format "January 2, 2006" }}</time> | {{ .ReadingTime }} {{ if eq .ReadingTime 1 }} minute {{ else }} minutes {{ end }}</p>
				</div>
			</div>
		</div>
		<div class="column">
			{{ range $idx, $category := .Params.categories }}
				{{- if ne $idx 0 }}, {{ end }}
					<a href="{{ "categories/" | relURL }}{{ $category | urlize }}">{{ $category }}</a>
				{{- end }}
		</div>
	</div>
</div>

Cela va permettre d'ajouter une colonne à côté de la date, qui passera automatiquement en dessous de celle-ci en mode responsive.

Côté public, on devrait voir les catégories s'afficher ainsi :

Exemple d'affichage des catégories sous le titre

Quelques explications sur le code qui appelle les catégories s'imposent :

{{ range $idx, $category := .Params.categories }}
    {{- if ne $idx 0 }}, {{ end }}<a href="{{ "categories/" | relURL }}{{ $category | urlize }}">{{ $category }}</a>
{{- end }}

Comme d'habitude, la première ligne ouvre une boucle avec la fonction range (qui peut s'apparenter à un tableau en Go - j'étudie tout juste le langage donc pardonnez mon éventuelle mauvaise interprétation). Cette boucle va faire 2 choses : elle vérifie l'existence d'une ou plusieurs catégories, via leur index (le numéro qui leur est attribué, par défaut dans l'ordre alphabétique) avec la variable $idx, et leur valeur avec la variable $category.

Si une ou plusieurs catégories existent, le code les affiche les unes à la suite des autres, séparés par une virgule et un espace grâce à la boucle {{- if ne $idx 0 }}, {{ end }} , qui créé la condition suivante : si l'élément à afficher n'est pas à l'index 0 ( if ne $idx 0 ), réaliser la condition (donc en l'occurence, une virgule suivie d'un espace - j'espère que je ne vous perd pas !)

Ensuite, le lien présent dans la balise <a> sera généré dynamiquement par la boucle {{ “categories/” | relURL }}{{ $category | urlize }}.

Arrêtons-nous un moment sur cette boucle : elle contient un nouveau caractère intéressant, le symbole | . Il s'agit d'un élément propre au langage de templating de Go, les pipes -> https://gohugo.io/templates/introduction/#pipes

Voici ce que fait ce bout de code : on va passer la chaîne de caractères categories/ dans la fonction relURL, qui sert comme son nom le suggère à générer une URL relative. Ici, le lien créé aura donc comme base d'écriture /categories/.

A savoir qu'il existe également la fonction absURL qui génère une URL absolue, donc tout le chemin depuis https:// … Pour en savoir plus, je vous renvoie vers la page de la documentation officielle, qui sera plus explicite que ma tentative d'explication : https://gohugo.io/functions/relurl/

Le pipe {{ $category | urlize }}. La fonction urlize servant tout bêtement à transformer une chaîne de caractères quelconque en chaîne de caractère “url friendly” : une catégorie qui aurait pour valeur “Développement web” génère donc la chaîne de caractère developpement-web pour être utilisable proprement dans une barre d'adresse.

Le lien généré prendra donc cette forme : Développement web

Et ce qui est vraiment top avec Hugo, c'est que le simple fait de créer une URL de cette façon justifie son existence : essayez de cliquer sur le lien généré, et votre page de catégorie s'affichera comme par magie ! Bon, normalement elle est bien vide et n'affiche qu'un lien vers l'article qui lui est associé, mais c'est normal, elle se base sur le template par défaut list.html qui est plutôt pauvre… Car c'est également le template list.html qui est chargé pour les pages de taxonomies.

Nous reviendrons un peu plus tard sur la personnalisation des templates de ces pages de catégories.

4.2 Les liens vers les posts précédents et suivants

Pour finir le template, on va maintenant ajouter ce qu'on trouve souvent à la fin des articles de blog : des liens “article précédent” et “article suivant”.

Hugo propose des variables prêtes à l'emploi pour cela : .PrevInSection et .NextInSection, qui génèrent des liens vers les pages précédentes et suivantes. Il suffit d'ajouter cette section après la balise </article> :

<section class="section">
  <div class="columns is-centered">
    <div class="column max-800px">
      <div class="columns is-mobile">
        <div class="column has-text-left">
          {{ with .PrevInSection }}
          <p>Previous post</p>
          <a href="{{ .Permalink }}">{{ .Title }}</a>
          {{ end }}
        </div>
        <div class="column has-text-right">
          {{ with .NextInSection }}
          <p>Next post</p>
          <a href="{{ .Permalink }}">{{ .Title }}</a>
        {{ end }}
        </div>
      </div>
    </div>
  </div>
</section>

Pour voir si le script fonctionne, il faut bien entendu ajouter 2 articles :

hugo new blog/2020/deuxieme-post-2020.md
hugo new blog/2020/troisieme-post-2020.md

Les colonnes créées avec les classes de Bulma permettent un affiche cohérent avec le reste du template. Voici ce à quoi doit ressembler le pied de page du 2eme article :

Exemple du pied de l'article

Conclusion : en route pour la création des pages !

On a vu comment fonctionnent le template des articles, on va maintenant pouvoir s'attaquer aux pages un peu plus figées, comme la page “à propos” ou “nous contacter”.

Nous reviendrons un peu plus tard sur le template des articles pour ajouter la possibilité aux visiteurs de laisser des commentaires.

Vous devriez également aimer ce qui suit...