Un des derniers points qui manquaient depuis la résurrection de ce blog était de faire refonctionner les anciennes URL des articles.
En effet, lors du passage sous Jekyll, d’une part le nommage des articles a été modifié (par défaut Jekyll les met en .html là où ma réécriture d’URL sous WordPress me les faisait juste finir par un /) et d’autre part j’ai changé de domaine, passant de wp.darathor.com à blog.darathor.net.
Il manquait donc deux opérations :
rediriger l’ancien domaine vers le nouveau
rediriger les anciennes URL vers les nouvelles
La première étape n’a rien de compliqué et se fait assez rapidement dans la configuration des VHOST d’Apache.
La seconde est un peu moins immédiate puisqu’il m’a fallu construire la table de correspondance des anciennes URL vers les nouvelles.
J’ai choisi comme stratégie de stocker une liste d’alias dans l’en-tête de chaque articles et de générer un fichier .htaccess à partir de ça.
Déclaration des alias
La déclaration des alias est assez simple et se limite à une liste d’URL relatives :
aliases:-/?p=3-/2007/05/01/ouverture-du-site/
L’avantage de fonctionner avec une liste d’alias en URL relatives c’est que cette même mécanique peut être ré-exploitée si je veux changer l’URL d’un article par la suite (notamment pour corriger une coquille).
Reste à l’alimenter dans mon cas de migration depuis WordPress.
Le script de migration de WordPress vers Jekyll m’avait extrait une entrée de la forme wordpress_url: http://wp.darathor.com/?p=10 contenant une des formes d’URL proposée par WordPress. Par contre il ne s’agissait pas de la forme générée par la réécriture automatique de WordPress qu’il m’a fallu reconstruire pour aboutir aux deux alias ci-dessous. J’ai finalement choisi de garder les deux URL à chaque fois étant donné que les deux pointaient sur le contenu et que je n’aime pas les liens morts.
Génération du fichier .htaccess
Dans mon cas j’utilise Apache comme serveur web, donc je peux passer par un fichier .htaccess ce qui a un gros avantage : tout reste stocké dans le code versionné et déployable automatiquement sans aller toucher au VHOST.
Pour générer ce fichier j’ai procédé comme pour le robots.txt via une simple page nommée htaccess (sans extension sinon à la génération Jekyll la colle arbitrairement derrière le permalien) :
---
layout: null
permalink: .htaccess
---
ErrorDocument 404 /404.html
{% for page in site.pages -%}
{%- if page.aliases -%}
{%- for alias in page.aliases -%}
Redirect 301 {{ alias }} {{ page.url }}
{% endfor -%}
{%- endif -%}
{%- endfor -%}
{% for post in site.posts -%}
{%- if post.aliases -%}
{%- for alias in post.aliases -%}
Redirect 301 {{ alias }} {{ post.url }}
{% endfor -%}
{%- endif -%}
{%- endfor -%}
On retrouve donc l’en-tête avec un layout: null pour que le fichier généré ne soit pas habillé d’une structure HTML et un permalink: .htaccess indiquant le nom du fichier à générer.
Ensuite le contenu consiste en une simple boucle sur les articles avec à chaque fois une boucle sur les alias pour générer les redirections 301.
J’en ai également profité pour ajouter la ligne ErrorDocument 404 /404.html qui dit à Apache d’utiliser une page 404 personnalisée plutôt que la page par défaut.
Ajout du 07/10/2018 à 12h30
Dans mon .htaccess je ne gérais les alias que pour les articles et pas pour les pages. J’ai mis à jour le code ci-dessus pour en tenir compte également.
Forcément quand le site est statique, c’est plus compliqué de mettre en place un moteur de recherche. Il y a plusieurs approches possibles :
faire une partie dynamique côté serveur pour la recherche : bof, le but c’est d’avoir maintenance côté serveur, donc non
faire appel à une API tierce qui ferait l’indexation puis la recherche : bof aussi, ça permet certes de rester statique côté serveur mais délocaliser la chose n’est pas mieux (en plus ça veut dire maintenance sur l’utilisation de l’API) donc non
déléguer complètement à un moteur externe type Google Search : ça existe sur certains sites (quoique ça fait un moment que je l’ai plus vu) sous la forme d’un formulaire de recherche redirigeant sur Google avec une recherche de type site:monsite.com ...… c’est mieux côté maintenance mais pire côté délocalisation puisqu’on quitte carrément le site, donc non
Reste la dernière : faire la recherche directement dans le navigateur en JS à partir d’un index fourni, solution finalement retenue.
Bon, c’est loin d’être parfait comme concept : ça ne passe évidemment pas à l’échelle notamment mais ce site reste un blog perso donc la volumétrie de devrait pas exploser…
L’index fait environ 500ko avec mes 156 articles actuels (représentant un peu plus de 10 ans) donc ça reste jouable, même si j’ai augmenté mon rythme ces derniers temps (notamment à cause des republications). Dans tous les cas on reste loin des pages d’accueil avec des gros carrousels d’images à 2Mo chacune qu’on trouve sur certains sites (notamment e-commerce) et ce n’est de toutes façons chargé que sur la page de recherche.
Donc c’est réaliste dans mon cas et vous pouvez le tester via le champ de recherche dans la colonne de droite.
Solution existante et adaptations
Je suis parti de cet article proposant une solution basée sur la bibliothèque Lunr mais comme souvent c’était loin d’être satisfaisant de base :
forcément c’est en anglais, donc fallait au moins traduire
ça ne permet pas de placer le champ de recherche sur toutes les pages (à moins d’inclure le code de recherche sur toutes les pages aussi, ce qui est exclus)
visuellement le rendu ne me convenait pas du tout
toutes les pages sont indexées, y compris les pages de catégories par exemple qui ne sont que des regroupements d’articles
Du coup j’ai réorganisé les choses différemment en :
séparant l’index dans un fichier JS à part
modifiant le formulaire pour qu’il fasse un simple GET vers une page dédiée au résultat de recherche
modifiant le JS pour que le terme à rechercher soit cherché dans l’URL
ajoutant dans l’en-tête des pages devant être indexées un paramètre searchable: true
La section suivante détaille la marche à suivre pour intégrer tout ça sur votre site.
Mise en œuvre
La bibliothèque Lunr
À récupérer ici et à ajouter dans le dossier assets/js.
Le code JS utilisant Lunr
Dans un fichier assets/js/search.js :
Je n’ai pas cherché à extraire le rendu HTML du résultat de recherche, donc il faudra directement modifier ce fichier pour l’arranger.
Le fichier d’index : search-index.js
Dans un fichier search-index.html à la racine du projet :
Il est possible de modifier ce fichier si vous voulez ajouter des informations à l’index (par exemple des données complémentaires pour l’affichage des résultats).
La page de résultat de recherche
Dans un fichier search.md à la racine du projet :
L’entrée layout est à adapter en fonction de ce que vous avez défini sur votre site.
Le formulaire de recherche
Il reste à intégrer le formulaire de recherche là où vous souhaitez de voir apparaitre :
Pages indexables
En l’état, seuls les articles seront indexés. Il reste donc à ajouter searchable: true dans l’en-tête de chaque page que vous souhaitez indexer.
Par défaut, le serveur de développement de Jekyll remonte des erreurs assez sommaires en ne précisant que le template concerné. C’est inutilisable quand on débugue un template (même si un numéro de ligne ne serait pas du luxe) mais quand l’erreur vient d’un plugin c’est plutôt limité.
Pour un affichage un peu plus détaillé avec une trace d’exécution permettant de déterminer quelle ligne de quel plugin est en cause, il faut utiliser l’option --trace ou -t.
bundle exec jekyll serve --drafts -t
Fichiers sitemap.xml et robots.txt
Il existe un plugin pour générer le fichier sitemap.xml, par contre je n’ai rien trouvé pour gérer son intégration au fichier robots.txt. On peut certes faire un fichier robots.txt à la main mais le lien est censé être absolu, donc ce n’est pas super propre de le mettre en dur.
Finalement je m’en suis sorti en ajoutant un fichier robots.html avec le contenu suivant :
La directive permalink: robots.txt permet de forcer le bon nom de fichier, layout: null dit que le contenu ne doit pas du tout être habillé et comme on est dans une page normale on a accès aux variables globales, notamment site.url.
Éviter la profusion d’espaces dans les pages
Le moteur de template de Jekyll fonctionne en ajoutant des pseudo-balises dans le code HTML notamment pour indiquer les structures de contrôles, boucles, affectations de variables, etc.
Pour que le code reste lisible on fait des retours à la ligne entre ces différentes balises et on indente. Ceci aboutit à de très nombreuses lignes uniquement peuplées d’espaces (ceux de l’indentation).
Exemple :
Il est possible d’y remédier en utilisant un caractère - dans les balises. Chaque - suivant une ouverture de balise ({% devient {%-) indique que les espaces précédents doivent être ignorés et sur les fermetures ( (%} devient -%})) que les espaces suivants doivent être ignorés.
L’exemple précédent devient alors :
C’est la même syntaxe que l’on retrouve dans d’autres moteurs de templates comme Twig pour PHP (la plupart des syntaxes sont assez proches en fait entre ces deux moteurs).
Remarque : cette syntaxe semble ne pas bien fonctionner avec le tag endraw, en effet j’ai des erreurs dès que je place un tiret un en début de ce tag (alors qu’à la fin et sur le tag raw ça marche)…
Voici mon second plugin pour Jekyll, toujours lié à mon menu de catégories.
Contexte
En plus de 10 ans d’existence de ce blog, j’ai beaucoup varié les thématiques de mes articles. Au début j’étais purement en mode bloc-notes sur des astuces techniques pour pouvoir les retrouver facilement. Puis j’ai pas mal parlé de l’iPhone, de mes développements perso, de mes créations “artistiques” (BD, cartes Magic, etc)… à force ça commence à faire beaucoup de catégories et ce n’est plus très lisible dans une liste en vrac.
Du coup j’ai voulu refaire ce que WordPress proposait via les sous-catégories : grouper les catégories par thématiques. Je n’ai pas trouvé de plugin qui le fasse bien. J’en avais testé un qui avait l’air de faire ce que je voulais mais je n’ai pas réussi à le faire marcher… peut-être que j’aurais dû insister mais je ne l’ai plus retrouvé donc j’ai fini par décider d’en développer un moi-même. Après tout le fonctionnel est simple.
Pour le coup effectivement, rien de bien compliqué, un connaisseur de Ruby y aurait sans doute passé moins d’un quart d’heure. Bon perso je n’y connais rien à Ruby, je l’ai découvert en débugant des plugins ces trois derniers mois, donc forcément ça m’a pris un peu plus de temps mais j’ai finalement ce que je voulais ^^
Le plugin
Ce plugin se résume donc à un nouveau filtre Liquid qui prend en entrée les catégories et renvoie en sortie une un tableau à double-entrée avec pour chaque thématique un sous-tableau contenant l’ensemble de ses catégories.
Les thématiques ou « groupes de catégories » sont définis dans le fichier de configuration _config.yml, avec la liste des groupes et pour chacune la liste des catégories qui la composent. Les catégories non-affectées étant regroupées dans un groupe « Default » (une seconde entrée de configuration permettant de définir son libellé).
Histoire de pas spammer avec trop d’articles je vais reprendre mon habitude de faire des tirs groupés pour les petites astuces, remarques et autres ^^
Publication avec les bonnes URL dans les métas SEO
Une petite subtilité que je n’avais pas remarquée tout de suite : les URL générées dans les métas SEO sont des URL absolues et dans les pages générées par le mode serveur dédié au dev (via bundle exec jekyll serve) commencent par l’IP locale et non le domaine spécifié dans le _config.yml (c’est pas illogique mais comme ça ne touche que des métas SEO, ça ne saute pas aux yeux).
J’ai rapidement trouvé dans la doc qu’il faut utiliser jekyll build pour générer en mode production. Plus précisément jekyll build --destination _deploy qui génère les pages dans un autre dossier histoire que les deux ne se marchent pas sur les pieds.
En y repensant c’est effectivement plus propre de travailler comme ça avec une commande dédiée à la génération pour la prod et un dossier séparé, ça évite de déployer par erreur des brouillons par exemple.
Brouillons et gestion des dates
Jekyll gère les brouillons en les plaçant dans un dossier à part et en ajoutant une option à la génération si on veut les inclure (cf la documentation).
Tout ça fonctionne très bien à une exception près : la doc dit que la date de l’article sera la date de dernière modification du fichier. Jusque-là pourquoi pas, ça se tient assez bien. Mais elle ne précise pas plusieurs subtilités :
d’une part ça ne fonctionne que pour le fichier Markdown et pas pour les HTML (là, l’article n’est apparemment pas visible sans date)
d’autre part il ne s’agit que d’une valeur par défaut dans le cas où la date n’est pas présente dans l’en-tête de l’article
enfin, si la date spécifiée est dans le futur, l’article ne sera pas visible, même pour un brouillon (donc on ne peut pas planifier une date à l’avance)
Dans l’ensemble c’est assez bancal comme fonctionnement je trouve, mais une fois qu’on sait comme ça marche, on s’en sort.