Dans les précédentes parties, nous avons vu comment créer nos pages à l'aide de layout et partials, puis avons ajouter un peu de ruby pour automatiser la création de celles-ci. Nous allons maintenant ajouter une fonction blog, grâce à l'extension blog de Middleman.

Installation et configuration du blog

L'extension blog est une gem, donc comme pour les précédentes, on va ajouter la gem dans le Gemfile :

# Gemfile
gem "middleman-blog", "~> 3.6.0.beta.2"

On installe la gem avec bundle install puis on va initialiser le blog à la main dans config.rb. Il existe une commande pour le faire automatiquement mais qui est surtout utile en début de projet.

# depuis la racine du projet
$ bundle install
# config.rb

###
# Blog settings
###

Time.zone = "Paris"
I18n.config.enforce_available_locales = false

activate :blog do |blog|
  # This will add a prefix to all links, template references and source paths
  # blog.prefix = "blog"
    blog.name = "blog"
    blog.permalink = "/{title}.html"
  # Matcher for blog source files
    blog.sources = "{year}-{month}-{day}-{title}.html"
  # blog.taglink = "tags/{tag}.html"
    blog.layout = "layouts/blog"
  # blog.summary_separator = /()/
  # blog.summary_length = 250
  # blog.year_link = "{year}.html"
  # blog.month_link = "{year}/{month}.html"
  # blog.day_link = "{year}/{month}/{day}.html"
    blog.default_extension = ".markdown"

    blog.new_article_template = "source/new-article.markdown" 

  # blog.tag_template = "tag.html"
  # blog.calendar_template = "calendar.html"

  # Enable pagination
    blog.paginate = true
    blog.per_page = 10
    blog.page_link = "page/{num}"

  # Custom categories
    blog.custom_collections = {
      category: {
        link: '/categories/{category}.html',
        template: '/category.html'
      }
    }
end

page "/feed.xml", layout: false

J'ai volontairement laissé le bloc avec toutes les options activables à titre informatif. Lorsqu'on va créer un nouvel article, son url sera de type année-mois-jour-titre. C'est ce qu'on voit pour blog.sources, mais ce n'est pas pour autant qu'on souhaite utiliser le même nom pour notre permalien. On définit donc la structure qu'on veut dans blog.permalink, ici j'ai choisi de ne garder que le titre.

blog.layout permet de préciser quel layout on utilise pour le blog, on va donc créer un nouveau layout pour le blog dans le quel on affichera l'article mais également les informations sur l'auteur et la date de publication ainsi que les commentaires.

blog.default_extension permet d'indiquer le type de fichier qu'on va utiliser, ici ce sera du Markdown.

blog.new_article_template définit l'emplacement d'un modèle d'article qui servira comme base à chaque création d'article. On s'en servira pour avoir le bloc Frontmatter déjà prêt à la création.

Le bloc pagination va nous permettre d'activer la pagination, ici on affichera 10 articles par page.

Parmi les éléments commentés, on voit les year_link, month_link et day_link que l'on n'utilisera pas ici, de même que les tags et le calendrier. blog.summary permet de créer automatiquement des résumés en utilisant les premiers mots de l'article. On ne l'utilisera pas non plus au profit d'un extrait qui sera géré avec Frontmatter.

Le bloc custom collections va nous permettre de créer des collections d'articles en fonction de la catégorie indiquée dans l'en-tête. Middleman se charge automatiquement de créer les pages listant les articles par catégories.

Puisqu'on n'utilise pas les tags, on aurait très bien pu se servir des tags en guise catégories, l'usage est le même. L'intérêt ici c'est de montrer comment créer des collections. Par contre il y a une limitation, un article ne peut appartenir qu'à une seule catégorie, ce qui est le cas ici.

Inception de layouts

Le layout pour le blog est ni plus ni moins qu'une copie du layout de base auquel on va venir ajouter les éléments spécifiques au blog. On pourrait donc tout à fait copier le fichier layout.slim et le modifier. Mais ça ne serait pas très DRY tout ça. Heureusement Middleman a une fonction pour nous aider : wrap_layout qui va nous permettre d'imbriquer le layout blog dans le layout principal.

Dans ce nouveau layout on va inclure les informations sur l'auteur. Le nom de l'auteur sera stocké dans les données de l'article, mais il faut pouvoir retrouver sa photo et cette information se trouve dans le fichier data/members.yml. On va donc créer un helper pour effectuer cette tâche.

Ouvrez le fichier config.rb, cherchez le bloc commenté helper et ajoutez ce qui suit :

# config.rb
# Methods defined in the helpers block are available in templates
helpers do
  def find_author(author_slug)
    author_slug = author_slug.downcase
    result = data.members.select {|member| member.keys.first == author_slug }
    raise ArgumentError unless result.any?
    result.first
  end
end

Dans le fichier members.yml, chaque membre est identifié par son nom en minuscule. ( comparez les fichiers members.yml et activities.yml pour voir la différence).

On passe au helper le nom de l'auteur (current_article.data.author) et on fait un select dans le fichier data/members.yml pour retrouver notre auteur.

Pour plus d'informations sur la gestion des auteurs dans Middleman, référez-vous à ce billet sur Pixelcabin

Dans le layout de blog on utilisera donc ce helper pour créer une variable author :

- author = find_author(current_article.data.author)

Création du layout blog

On crée un nouveau fichier blog.slim dans le dossier layouts/

/ layouts/blog.slim
= wrap_layout :layout do

  div role="main"

  = partial "partials/header"

  aside
    - author = find_author(current_article.data.author)
    = image_tag "/images/equipe/#{author.picture}"
    h3 = author.name
    p 
      ' Publié le : 
      = I18n.l current_page.date, format: '%e %b %Y'
    p
      ' Dans :
      = current_article.data.category

  article
    = current_article.data.excerpt

    == yield 

Comment fonctionne ce layout imbriqué ? Quand Middleman va tomber sur un article de blog, il va savoir automatiquement qu'il faut utiliser le layout blog. A la première ligne wrap_layout lui indique d'aller chercher le layout principal et d'inclure le code qui suit dans le = yield.

Puis le yield du layout blog va lui inclure le code contenu dans l'article.

Création des pages

On continue donc avec la création du modèle d'article que l'on va créer dans le dossier source. Le fichier est au format .erb ce qui va nous permettre de récupérer les variables @title et @date automatiquement.

Modèle pour le blog

# new-article.erb
---
title: <%= @title %>
title_seo: ""
description: ""
date: <%= @date %>
hero_image:
thumbnail:
category:
excerpt:
author:
---

Pas grand chose à dire sur ce ficher, on y met simplement notre code FrontMatter auquel on a ajouté thumbnail, category, excerpt et author. Le thumbnail sera utilisé dans les listes d'articles, l'excerpt sera utilisé en en-tête d'article et dans les listes.

Lister les articles

On va avoir besoin de listes d'articles pour plusieurs pages. La première c'est la page blog qui liste l'ensemble des articles. Puis les pages catégories pour filtrer les articles en fonction des catégories.

Les catégories sont gérées par des custom collections. Middleman se charge de créer les listes pour les catégories qu'ils trouvent dans les articles. Si vous décidez de renter "chaton" pour la clé category:, une page categories/chaton.html sera créée, listant tous les articles "chaton".

Pour le site du projet, les catégories sont décidées en amont, il y en a 6 : La vie au camp, Activités, Reportage photo, Portrait de résident, Conseils et Lifehack.

La liste des catégories est affichée sur la page blog et dans toutes les pages de catégories. Je pense que vous avez deviné, on va créer un nouveau partial !

La boucle pour afficher les articles est également commune aux deux types de pages, on peut donc tout mettre dans le partial articles_list.html.slim

/ _articles_list.htlm.slim
p Catégories :
ul
  li = link_to "La vie  au camp",      category_path("la-vie-au-camp")
  li = link_to "Activités",            category_path("activites")
  li = link_to "Reportage photos",     category_path("reportage-photos")
  li = link_to "Portrait de résident", category_path("portrait-de-resident")
  li = link_to "Conseils",             category_path("conseils")
  li = link_to "Lifehack",             category_path("lifehack")

ul
- page_articles.each_with_index do |article, i|
  li
    h2 = link_to article.title, article
    p
    ' Publié le
    = I18n.l article.date, format: '%e %b %Y'
    '  par
    = article.data.author
    p = article.data.excerpt
    = image_tag article.data.thumbnail

On peut ensuite créer les pages blog.html.slim et category.html.slim dans le dossier source/. Les deux pages sont assez similaires, on pourrait d'ailleurs n'en créer qu'une et ajouter quelques conditions. L'avantage d'avoir deux pages distinctes c'est de pouvoir mieux les personnalisées par la suite.

Les pages catégories étant créées à la volée, on ne peut pas personnaliser facilement le bloc Frontmatter et donc le titre à afficher dans le header ce qui va nécessiter une petite astuce.

# blog.html.slim
---
title: "L'actualité du camp pour rester informé"
title_seo: "Blog du camp de vacances No Wifi Summercamp"
description: "Retrouvez l'actualité du camp, avec des reportages sur la vie au camp, sur les diverses activités de plein air, découvrez nos résidents, profitez de conseils..."
hero_image: blog.jpg
---
div role="main"

  = partial "partials/header"

  = partial "partials/articles-list"

  = partial "partials/call-to-action"

La page blog contient donc uniquement le bloc Frontmatter. Pour la page category, l'astuce va consister à passer le nom de la catégorie au _partial _header.html.slim qu'il faudra donc aller modifier.

# category.html.slim
---
hero_image: blog.jpg
---
div role="main"

  = partial "partials/header", locals: {title: category}

  = partial "partials/articles-list"

  = partial "partials/call-to-action"

En passant la variable title à _header, on crée une variable locale qu'on va pouvoir tester. Dans le cas d'une catégorie, la variable title est égale au nom de la catégorie et donc elle n'est pas nulle. Dans les autres cas, la variable est nulle et on peut donc lui assigner current_page.data.title. Soit le fichier suivant :

/ partials/_header.html.slim
header
  / Set the title
  - title = current_page.data.title if title.blank?

  / Check if it's the homepage
  - if current_page.url == '/'
    = image_tag "/images/logo-home.png", alt: "No Wifi Summercamp, camp de vacances en plein air"
    p = current_page.data.slogan_sup
    p = current_page.data.slogan
    h1 = title
  - else
    h1 = title

Créer des articles

Nos pages sont prêtes, on va pouvoir maintenant créer des articles pour en vérifier le bon fonctionnement. On a configuré le blog de manière à ce que les articles se situent dans un dossier blog et soient dans un format année-mois-jour-titre. On pourrait aller les créer manuellement mais il y a bien sur une commande pour ça :

# depuis la racine du projet
$ middleman article "Titre de mon article"

Un nouveau fichier sera automatiquement créé dans le dossier blog à la date du jour et grâce au modèle de blog crée plus tôt le bloc Frontmatter est déjà en place avec le titre et la date.

Ecrire en markdown

On peut choisir le format dans lequel rédiger nos articles, on pourrait tout à fait les rédiger en HTML pur. Si vous connaissez ou utilisez déjà le Markdown, inutile de prêcher des convaincus, si non vous avez tout à gagner à rédiger les articles dans ce format.

C'est très simple à écrire, permet de se concentrer sur le contenu plutôt que la forme, et n'importe qui peut l'apprendre rapidement. Si vous ne connaissez la syntaxe cette cheatsheet est faite pour vous.

Pour les articles du projet, vous pouvez vous servir dans ce gist ou sur le dépot github du projet.

Ajouter une pagination

On a maintenant une liste d'articles qui s'affiche sur la page blog et pour chacune des catégories. Par contre il n'y a pas de pagination. Si on avait 200 articles, ils s'afficheraient tous sur la même page. De même il nous manque des liens en bas de pages pour naviguer plus facilement entre les articles.

Pour la pagination, il suffit simplement d'ajouter deux variables dans le Frontmatter des pages que l'on souhaite paginer. Rouvrez donc les fichiers blog.html.slim et category.html.slim et ajouter ce qui suit dans l'en-tête :

pageable : true
per_page: 20 

Middleman va se charger de créer une nouvelle page tous les 20 articles. Maintenant il nous faut des liens pour naviguer vers les 20 articles suivants ou précédents.

per_page permet d'écraser la valeur définie dans config.rb ( mise ici pour l'exemple), on pourrait ainsi avoir, pour la liste principale 20 articles et pour les catégories 30 articles.

On va ensuite ajouter ces liens dans le partial _articles-list.html.slim, soit le fichier complet :

/_articles-list.html.slim

/ Variables for pagination
- locals      = current_resource.metadata[:locals]
- prev_page   = locals['prev_page']
- page_number = locals['page_number']
- num_pages   = locals['num_pages']
- next_page   = locals['next_page']

p Catégories :
ul
  li = link_to "La vie  au camp",      category_path("la-vie-au-camp")
  li = link_to "Activités",            category_path("activites")
  li = link_to "Reportage photo",      category_path("reportage-photo")
  li = link_to "Portrait de résident", category_path("portrait-de-resident")
  li = link_to "Conseils",             category_path("conseils")
  li = link_to "Lifehack",             category_path("lifehack")

ul
- page_articles.each_with_index do |article, i|
  li
    h2 = link_to article.title, article
    p
    ' Publié le
    = I18n.l article.date, format: '%e %b %Y'
    '  par
    = article.data.author
    p = article.data.excerpt
    = image_tag article.data.thumbnail


- if num_pages > 1
  nav role= "navigation"
    - if prev_page
      = link_to '< Billets récents', prev_page
    span
      '  Page #{page_number} 
      '  sur #{num_pages}
    - if next_page
      = link_to 'Billets anciens >', next_page

Reste à ajouter les liens de navigation inter-articles, cette fois dans le layout blog.slim à la suite de = yield :

/ blog.slim
nav role= "navigation"
  ul
    li
      - prev_article = current_article.previous_article
      - if prev_article
          = link_to prev_article.title, prev_article.url,
            rel: 'prev', title: "Article précédent: #{prev_article.title}"
    li
      - next_article = current_article.next_article
      - if next_article
          = link_to next_article.title, next_article.url,
            rel: 'next', title: "Article suivant: #{next_article.title}"

Ajouter des commentaires avec Disqus

Le blog est prêt ! On peut créer facilement de nouveaux billets, les lister et les catégoriser, mais il nous manque encore les commentaires.

Puisqu'on est sur un système statique, l'ajout de commentaires ne peut passer que par un système tiers. Il en existe plusieurs, chacun a ses avantages et inconvénients et répond ou non aux attentes. Le plus simple à mettre en œuvre pour Middleman c'est Disqus puisqu'une gem est là pour faire le travail pour nous.

Si vous n'avez pas de compte Disqus, il est nécessaire d'en créer un et de déclarer le site (dans l'onglet moderation). Ajouter ensuite la gem dans le Gemfile:

# Gemfile
gem "middleman-disqus"

Puis en ligne de commande :

$ bundle install

La configuration de Disqus se fait dans le fichier config.rb où on va ajouter ce qui suit :

# config.rb
activate :disqus do |d|
  d.shortname = 'nowifisummercamp' # Remplacer par votre nom Disqus 
end

Disqus est activé, il ne reste plus qu'à ajouter les commentaires au blog en ajoutant ce qui suit dans le layout blog.slim après les liens de navigations :

/layouts/blog.slim
= disqus

On arrive à la fin de la partie "contenu et fonctionnalités". Les pages du sites sont rédigées, on a un blog avec des commentaires, il nous manque un peu d'habillage CSS et on pourra mettre le tout en ligne.

Retrouvrez tous les fichiers dans ce gist (sauf les billets de blog, voir plus haut)

Article précédent : Tutoriel Middleman, création des pages
Article suivant : Tutoriel Middleman, ajouter un peu de CSS

Je suis disponible pour des missions longues / CDD / CDI Cliquez pour en savoir plus