Catégorie « Jekyll »

Expérimentation sur la gestion des commentaires

Comme je le disais en annonçant la réouverture du blog, l’un des deux gros points noirs suite au choix de Jekyll et d’un site statique entièrement pré-généré c’était le problème des commentaires. En effet, habituellement c’est le moteur de blog qui propose un formulaire et traite côté serveur les soumissions de nouveaux commentaires. En passant au statique ce n’est plus possible directement puisque le serveur ne fait que… servir du statique justement. Aucune intelligence, donc pas non plus celle de gérer les commentaires.

Les solutions existantes

La solution la plus répandue dans ce cas est de passer par une plateforme externe telle que Discuss qui centralise les commentaires et les restitue via un widget JavaScript inclus dans la page. Cela ne me plaît pas tu tout d’une part parce que c’est une externalisation de la donnée alors qu’elle doit à mon sens rester groupée avec les articles et sûrement pas déléguée à un silo. D’autre part ça signifie charger un script externe systématiquement ce qui introduit d’une part de la lourdeur et d’autre part une fuite de données de navigation : Discuss ou son équivalent est mécaniquement au courant de qui regarde quelles pages chez moi et quand. Et non-seulement chez moi au aussi sur tous les autres sites utilisant leur plateforme. Et c’est généralement le cœur de leur modèle économique de monnayer d’une manière ou d’une autre ces données.

Bref la solution basée sur un silo centralisé était exclue.

Une autre solution aurait été de faire pareil mais en auto-hébergé. C’est-à-dire proposer quelques webservices exploités en JavaScript. Ça résout le problème de dépendance externe et de protection de la vie privée mais ça réintroduit le problème que je voulais résoudre en utilisant un générateur de site statique, à savoir ne plus avoir à m’inquiéter de la maintenance et de la sécurité. Certes ça aurait représenté un volume de code bien plus limité qu’une plateforme de blog complète, donc largement simplifié la maintenance mais quand même.

Donc cette solution là n’était pas valable non plus.

Et en faisant quelques recherches je n’ai rien trouvé comme proposition hors ces deux pistes. J’en ai conclus que j’allais devoir inventer une solution de toutes pièces et j’ai donc remis ça à plus tard pour ne pas bloquer la remise en ligne du blog. J’ai donc sorti le blog en laissant ça mûrir dans un coin de ma tête.

Mon idée

Mon cahier des charges était donc de trouver une solution n’impliquant pas de service tiers ni de service auto-hébergé à maintenir. C’était un peu mal parti : faire tout côté client mais avoir le résultat côté serveur sans introduire de faille potentielle semble assez insoluble.

J’ai fini par penser à une solution un peu bâtarde mais viable dans mon cas : passer par des mails. Puis traiter manuellement les soumissions de commentaires.

Forcément ça fait intervenir un service tiers : le fournisseur de messagerie (j’ai renoncé à vouloir auto-héberger du mail, c’est beaucoup trop compliqué et risqué) mais il ne fait que passe-plat et n’est pas chargé de conserver la donnée sur le long terme puisqu’une fois un commentaire réceptionné, je l’intègre à la page de l’article. J’ai donc regardé un peu ce qu’on pouvait faire pour envoyer du mail côté client. En JavaScript ça semble impossible (et pas très souhaitable pour des raisons assez évidentes en matière de spam) mais il reste toujours la bonne vieille méthode à base d’un formulaire en mode “mailto”. Ça donne un peut l’impression de revenir à l’Âge de Pierre mais l’essentiel c’est que ça marche.

Cette solution n’est pas exempte de défaut, à première vue au moins les suivants :

  • l’adresse e-mail de destination du formulaire est en clair, ce qui fait qu’elle risque d’être cible de spams à terme
  • les commentaires ne sont pas publiés instantanément, il faut que j’intervienne pour les intégrer
  • c’est chronophage : je dois faire l’intégration à la main (problème de passage à l’échelle)
  • on augmente dans l’absolu le risque juridique : comme c’est moi qui publie les commentaires, je passe automatiquement en mode de modération a priori, donc je ne peux pas prétendre ne pas avoir connaissance des contenus publiés, j’en suis donc responsable
  • ce n’est pas super “user friendly” de passer par son client mail pour envoyer le commentaire même si le contenu est pré-rempli

Reste que ces défauts sont acceptables au moins dans mon cas. Reprenons-les point par point :

  • publicité de l’adresse e-mail : c’est un défaut mineur, il suffit de faire une adresse dédiée et comme elle ne sert qu’à ça elle peut être remplacée du jour au lendemain si elle est la cible de spam
  • délai de publication : là non plus ce n’est pas super grave, on n’a pas vraiment besoin d’instanténéité sur des commentaires, même s’il est vrai que ça a toutes les chances de réduire les discussions entres lecteurs (les discussions entre moi et un visiteur ne sont pas affectées puisqu’elles nécessitent de toute façons une action de ma part pour avancer), c’est un peu dommage mais pas primordial, surtout dans le cas de mon audience confidentielle
  • chronophage : le problème évident de passage à l’échelle en cas de gros volume de contributions et cette solution n’est clairement pas généralisable à des sites ayant beaucoup de commentaires mais ce n’est pas un problème pour moi : je ne crois pas qu’un seul de mes articles ait dépassé les dix commentaires, donc ça reste totalement gérable
  • risque juridique : ça m’obligera potentiellement à refuser certains commentaires mais je pense que le risque reste minime
  • aspect “user friendly” : franchement ça demande juste de valider l’envoi du mail en acceptant qu’il contienne des donnés présentées de manière un peu technique… disons qu’on s’en fout, d’autant plus que mon blog traite entre autres de technique (et en passant je pense que je me débarrasse de la plupart des robots spammeurs qui ne doivent vraiment pas s’attendre à ça :D)

Voilà du coup ça me semble correct dans l’immédiat : pas parfait mais ça fait le boulot en rétablissant un système de commentaires. On va laisser ça tourner quelque temps puis on verra ^^


Archives par année dans Jekyll

Je continue dans mon exploration du monde merveilleux de Jekyll (le générateur de site statique qui fait maintenant tourner ce blog), avec une autre fonctionnalité de WordPress que je voulais reproduire : les archives par année avec menu dans la sidebar.

Ça semble trivial comme mais non. Avec Jekyll RIEN (ou presque) n’est trivial. Je suis assez vite tombé sur le plugin jekyll-archives qui permet de générer les pages d’arichive (non-paginées mais tant pis). Je suis assez vite arrivé à un truc qui marche de ce côté-là, pas de problème majeur.

Par contre c’était un peu plus compliqué pour générer le menu.

En cherchant un peu, j’ai fini par tomber sur cet article qui décrit comment générer une sorte de plan du site par date, basé sur une itération sur l’ensemble des articles. J’ai pu en tirer (en simplifiant) ce qu’il me fallait pour mon menu, à une exception près : le décompte des articles (forcément il n’en avait pas besoin dans son cas).

Et là je me suis heurté à un truc : comment incrémenter une variable dans Liquid (le moteur de templates de Jekyll) ? Naïvement, j’ai tenté un simple {% assign postCount = postCount + 1 %} mais ce fut un échec cuisant. Je n’ai pas trouvé ce que fait un “+” dans ce cas mais clairement pas une addition (ma variable valait toujours 0 et en inversant les arguments j’avais toujours 1). J’ai donc :

  1. cherché comment incrémenter une variable : rien trouvé dans la doc, forcément on n’a que des exemple d’assignations avec une variable en dur
  2. cherché sur le net : je suis tombé sur cette doc qui parle d’un tag increment gérant des variables incrémentales indépendantes des autres variables (pourquoi indépendantes ? qu’est-ce que ça apporte ? mystère…) et semble-t-il non-réinitialisable, donc ça ne répond pas à ma question
  3. puis j’ai fini par tomber là dessus et là j’ai enfin trouvé : pour faire une addition il faut passer par le filtre plus

Donc voilà, j’ai ma solution pour générer mon menu :

<h2>Archives</h2>
<ul>
  {% assign postCount = 0 %}
  {% for post in site.posts %}
    {% assign year = post.date | date: '%Y' %}
    {% if post.previous %}{% assign nextYear = post.previous.date | date: '%Y' %}{% else %}{% assign nextYear = 0 %}{% endif %}
    {% assign postCount = postCount | plus: 1 %}
    {% if year != nextYear %}
      <li><a href="/archives/{{ year }}/">{{ year }}</a> ({{ postCount }})</li>
      {% assign postCount = 0 %}
    {% endif %}
  {% endfor %}
</ul>

C’est laborieux mais ça marche.

Reste juste un défaut : rien qu’avec ça je suis passé de 20 à 30 secondes pour régénérer mon site. C’est un peu chiant quand on veut contrôler que ce qu’on vient de faire marche (mais ça n’a aucun impact pour le visiteur puisque lui n’accède qu’à des pages statiques).

J’ai donc cherché comment on pouvait faire un peu de caching dans Jekyll. Forcément j’ai rien trouvé de natif mais je suis tombé rapidement sur le plugin jekyll-include-cache. Il ajoute une nouvelle instruction alternative à include et qui s’utilise exactement pareil mais n’effectue l’évaluation du contenu qu’une fois : include_cached. Du coup en utilisant ça pour mes header, footer et sidebar, j’ai pu redescendre à environ 17 seconde de temps de génération. Ça reste beaucoup mais c’est quand même moitié moins.

Par contre le cache en question n’est pas régénéré à chaque changement (y compris changement du template inclus) mais seulement à chaque fois que le serveur est relancé, ce qui veut dire relancer systématiquement le serveur avant de publier pour prendre en compte les mises à jour (dans mon cas nouvel article ou changement de catégorie). Du coup ça reste largement bancal comme solution, pas sûr que je garde ça sur le long terme.


Plugin de tri des catégories pour Jekyll

Comme je le disais la semaine dernière, il restait des ajustements à faire sur le blog. L’un d’entre eux était d’afficher un menu présentant notamment les catégories (et aussi les archives par années mais ça viendra plus tard).

J’ai pas mal galéré pour trouver comment faire (ça me semble être un truc assez basique mais ce n’est pas trop mis en évidence dans la doc). J’ai assez rapidement fini par trouver dans quelle variable chercher les catégories (il s’agit de site.categories) qui se trouvent sous la forme d’un tableau associatif où la clé est le nom de la catégorie et la valeur un tableau des articles.

Ce qui m’a pris beaucoup plus de temps c’est trouver comment récupérer la clé lors de l’itération. J’ai fini par trouver dans la doc de Liquid (le moteur de templates de Jekyll) : lorsqu’on itère sur un tel tableau chaque item qu’on reçoit contient un tableau à deux entrées : la clé en 0 et la valeur en 1 (pour le coup je préfère les syntaxes de Twig ou AngularJS qui sont plus claires). Pour afficher les catégories, j’en étais donc arrivé à :

<ul>
  {% for category in site.categories %}
    <li><a href="/categories/{{ category[0]|slugify:'latin' }}/">{{ category[0] }}</a> ({{ category[1].size }})</li>
  {% endfor %}
</ul>

Restait à les trier parce que j’en ai quand même beaucoup… Et là pour le coup je n’ai rien trouvé de natif. Je suis tombé sur plusieurs plugins. D’abord un premier qui avait l’air riche mais que je n’ai pas réussi à faire fonctionner… Puis un deuxième beaucoup plus simple mais pas complètement satisfaisant (notamment le tri ne mettait à la fin ma catégorie “À propos du blog” à cause de l’accent). Ne connaissant rien à Ruby, j’ai un peu tâtonné pour épurer le truc et corriger ce qui me gênait.

Le résultat est accessible ici : jekyll-hash-table-sort-filters

L’exemple de code précédent, après installation du plugin, devient le suivant :

<ul>
  {% assign categories = site.categories | sort_by_keys %}
  {% for category in categories %}
    <li><a href="/categories/{{ category[0]|slugify:'latin' }}/">{{ category[0] }}</a> ({{ category[1].size }})</li>
  {% endfor %}
</ul>

EDIT du 10/06/2018 à 11h10 : forcément y avait une erreur dans la version du plugin que j’ai publiée… et qui faisait tout planter. C’est corrigé : jekyll-hash-table-sort-filters.

Et comme tout est généré une fois pour toute, je n’ai vu le problème qu’à la génération suivante, donc aujourd’hui.


Retour du blog

Ça faisait déjà près d’un an et demi que ce blog était mort suite à un crash serveur. Je ne pouvais pas le remettre en fonction directement parce qu’il utilisait une vieille version de WordPress qui n’était pas compatible avec PHP7 et je n’avais pas envie de pourrir mon nouveau serveur avec un vieux PHP. Quant à la mise à jour de WordPress était dissuasive parce que j’avais de nombreuses versions de retard et que ça se serait forcément fait dans la douleur.

En parallèle j’avais vu passer plusieurs articles parlant de remplacer WordPress et autres moteurs dynamiques par un générateur de site statique. Cet aspect me tentait bien mais pas pour les raisons avancées habituellement, en particulier la principale : les perfs je m’en fiche un peu (mon serveur se tourne les pouces la plupart du temps et j’ai une audience limitée).

Non, ce qui m’intéressait c’était plutôt de supprimer la maintenance et le besoin de mise à jour, soit précisément ce qui m’a fait repousser la remise en ligne du blog (et ce qui me causait sans doute de grosses failles de sécurité sur le précédent serveur). En effet, dans ce cas le générateur n’est pas en ligne, je peux le garder tranquillement en local sur mon ordi perso.

Par contre, ça a plusieurs gros inconvénients qui m’ont retenu de sauter le pas plus tôt, en particulier deux :

  • Pas de système de commentaires. C’est embêtant parce qu’il est exclus que je délègue ça à un silo genre Discuss. Et si c’est pour remettre du JS et un système de web services ben on reperd direct l’intérêt principal de s’éviter la maintenance… Je n’ai pas de solution correcte pour l’instant donc il faudra s’en passer (genre en passant par Mastodon ou à défaut Twitter), jusqu’à ce que je trouve quelque chose de viable.
  • Pas de moteur de recherche. Moins gênant mais quand même déléguer à Google et autres la recherche interne du site ça me déplaît pas mal. Là non plus je n’ai pas de solution pour l’instant (j’ai trouvé un plugin qui permettrait de le faire mais en passant par une API tierce, donc bon, on n’y gagne pas grand-chose au final).

J’ai finalement décidé de sauter le pas parce que ça m’est arrivé quand même assez souvent de me dire que j’aurais bien fait un article sur un sujet mais qu’en l’absence de blog j’ai soit rien fait, soit résumé en quelques pouets et/ou tweets.

J’ai donc cherché un peu dans les outils existants. Forcément y en a des dizaines… Du coup faut choisir. Après avoir écarté tout ce qui est NodeJS et rien trouvé en PHP, je me suis rabattu sur Jekyll (en Ruby) qui a le mérite d’avoir un peu d’ancienneté et donc pas mal de plugins dispo et probablement une durée de vie pas trop basse.

Après avoir rencontré pas mal de problèmes tels que :

  • besoin de choisir un thème
  • besoin d’installer des plugins et configurer plein de choses (Jekyll de base est assez vide)… pour la liste des plugins, cf. la page à propos
  • du temps perdu à comprendre que si ma pagination était morte d’un coup c’était parce que j’avais reformaté le fichier (oui les pages contiennent des en-têtes en YAML qui a l’idée VRAIMENT débile de se baser sur l’indentation, donc un formatage et boum ça marche plus… et vas-y pour comprendre que ça vient de là !)
  • repasser sur tous les articles pour faire en sorte que ça se rende bien (un convertisseur depuis une base WordPress est dispo mais il ne convertit pas les balises spécifiques… et j’en avais pas mal pour du code ou de la mise en forme autour des images)

J’ai enfin quelque chose de fonctionnel \o/

J’en profite également pour basculer sur un autre domaine (en .net plutôt que .com).

Il reste à mettre en place les redirections pour ressusciter les anciennes URL des articles mais ça peut attendre, après un an et demi, on est plus à quelque semaines près… Et il risque d’y avoir encore quelques ajustements mais ça a déjà le mérite d’être fonctionnel ^^