Écrire un blog avec emacs
Dans ce tout premier billet, je vous propose de faire rapidement le tour du code qui me permet de générer ce site via Emacs et org-mode avec sa fonctionnalité publish. La configuration a été plus ou moins inspiré des blogs suivants :
L'implémentation
Le code complet peut être trouvé à cette adresse. Ici je détail quelques points
du code pour aider à comprendre son fonctionnement et plus généralement le
fonctionnement du dispacher ox-publish
d'org-mode.
Détails d'implémentation
D'abord je définis quelques symboles qui seront réutilisés plus bas. Ici, du code HTML qui sera ajouté dans la balise <head>. On ajoute le fichier css principal du site ainsi qu'un fichier css généré par Google pour les polices de caractères utilisés dans ce site.
(setq mp/head-extra (concat "<link rel=\"stylesheet\" type=\"text/css\" href=\"/res/style.css\"/>" "<link rel=\"stylesheet\" type=\"text/css\" href=\"/res/code.css\"/>" "<link rel=\"icon\" type=\"image/x-icon\" href=\"/res/favicon.ico\"/>")) (setq mp/project-path (file-name-directory (or (buffer-file-name) load-file-name))) (setq mp/header-file "res/header.html")
Ensuite, nous avons une fonction qui retourne le header de la page (qui contient mon nom et le menu de navigation du site) dans un fichier séparé qui sera inclu dans toutes les pages générées.
(defun mp/header (arg) (with-temp-buffer (insert-file-contents (concat mp/project-path mp/header-file)) (buffer-string)))
Après l'entête, le pied de page avec les informations de droit d'auteur et de licence.
(setq mp/footer (concat "<a rel=\"license\"" "href=\"http://creativecommons.org/licenses/by-sa/4.0/\"><img " "alt=\"Licence Creative Commons\" style=\"border-width:0\" " "src=\"https://i.creativecommons.org/l/by-sa/4.0/88x31.png\"" "/></a><br />" "© Matthias Paulmier -- " "Vous pouvez copier, modifier et/ou distribuer ce document " "sous les termes de la <a rel=\"license\" " "href=\"http://creativecommons.org/licenses/by-sa/4.0/\">Licence " "CC BY-SA</a>."))
Le fichier indexant les billets déjà postés sur le site s'appel la sitemap au sein d'org-mode. La fonction suivante permet de définir l'aspect général du site (titre capitalisé suivi de la liste des billets formattés).
(defun mp/sitemap (title list) "Create the sitemap. TITLE is the title of the sitemap. LIST holds the list of files to include in the sitemap" (concat "#+TITLE: " (capitalize title) "\n" (string-join (mapcar #'car (cdr list)) "\n\n")))
Les fonctions suivantes permettent de formatter les entrées dans le fichier. De
base, nous n'avons qu'une suite de titres menant vers chaque billets publié sur
le site. Ici j'ajoute plusieurs choses. Tout d'abord, la date de parution du
billet en français (sous forme de lien vers le billet), ainsi que les "tags" (ou
catégories) du billet. Pour ce faire je me sers des file-tags
qui peuvent être
spécifiés avec l'option d'entête #+FILETAGS
dans un fichiers sous org-mode. On
ajoute également la date de parution du billet. Ensuite, avec la fonction
mp/blog-post-get-preview
, nous récupérons un aperçu du billet. Pour cela, on
va chercher le texte se situant dans le block entouré par #+BEGIN_PREVIEW
et
#+END_PREVIEW
dans le fichier source du billet. Enfin, un second lien vers
l'article (en plus du titre) est ajouté à la fin de l'aperçu avec le texte Lire
la suite →.
(defun mp/get-tags (filename) "FILENAME if the file from which we want to extract the tags list. Returns the tags in the file." (with-temp-buffer (insert-file-contents (concat "blog/" filename)) (org-mode) org-file-tags)) (defun mp/blog-post-get-preview (filename) "Helper function that returns the content of the preview for the sitemap. FILENAME is the file of the article." (with-temp-buffer (insert-file-contents (concat "blog/" filename)) (goto-char (point-min)) (let ((beg (+ 1 (re-search-forward "^#\\+BEGIN_PREVIEW$"))) (end (progn (re-search-forward "^#\\+END_PREVIEW$") (match-beginning 0)))) (buffer-substring beg end)))) (defun mp/sitemap-entry (entry style project) "Formatter function for a sitemap entry. ENTRY is the file name. STYLE is the style of the sitemap (not used here but mandatory for the function to work with the ox-publish). PROJECT is the current project." (when (not (directory-name-p entry)) (format (concat "-----\n" "* [[file:%s][%s]] %s\n" "#+BEGIN_publidate\n" "Published on %s at %s\n" "#+END_publidate\n" "%s\n" "\n") entry (org-publish-find-title entry project) (concat (mapconcat #'(lambda (s) (format ":%s" s)) (mp/get-tags entry) "") ":") (capitalize (format-time-string "%FT" (org-publish-find-date entry project))) (capitalize (format-time-string "%T%z" (org-publish-find-date entry project))) ;; Build the actual preview of the article (let ((preview (mp/blog-post-get-preview entry))) (format (concat "%s\n\n" "#+BEGIN_readmorelink\n" "[[file:%s][Plus \\rarr]]\n" "#+END_readmorelink\n") preview entry)))))
Enfin, le dispacher ox-publish
fonctionne avec une liste associative,
org-publish-alist
, dans laquelle on spécifie les paramètres qui vont
déterminer la façon dont le site sera publié. Pour plus d'information sur cette
liste et les options disponibles, la documentation est très bien fournie (la
documentation pour org-mode et Emacs en général est très bien faite).
(setq org-publish-project-alist `(("blog" :components ("articles" "pages" "res")) ("articles" :base-directory ,(concat mp/project-path "blog") :base-extension "org" :publishing-directory ,(concat mp/project-path "public/blog") :publishing-function org-html-publish-to-html :with-title t :with-author t :with-creator nil :with-date t :headline-level 4 :with-toc nil :with-drawers t :with-sub-superscript nil :section-numbers nil :html-link-home "/" :html-head nil :html-head-extra ,mp/head-extra :head-include-scripts nil :html-viewport nil :html-doctype "html5" :html-link-up "" :html-link-home "" :html-preamble mp/header :html-postamble ,mp/footer :html-footnote-section "<div id='footnotes'><!--%s-->%s</div>" :html-format-drawer-function mp/org-export-format-drawer :auto-sitemap t :sitemap-function mp/sitemap :sitemap-format-entry mp/sitemap-entry :sitemap-filename "index.org" :sitemap-title "Le blog" :sitemap-sort-files anti-chronologically) ("pages" :base-directory ,mp/project-path :base-extension "org" :publishing-directory ,(concat mp/project-path "public") :publishing-function org-html-publish-to-html :exclude "README.org" :recursive t :with-title t :with-author nil :with-toc nil :with-date t :with-drawers t :with-sub-superscript nil :section-numbers nil :html-viewport nil :html-head nil :html-head-extra ,mp/head-extra :html-doctype "html5" :html-link-up "" :html-link-home "" :html-preamble mp/header :html-postamble ,mp/footer) ("res" :base-directory ,(concat mp/project-path "res") :base-extension ".*" :publishing-directory ,(concat mp/project-path "public/res") :publishing-function org-publish-attachment)))
Ce que je souhaite ajouter
J'ai déjà utilisé cette configuration pour un autre site auparavent (que j'utilisais uniquement pour organiser mes cours). Je sais donc qu'il y a quelques fonctionnalités que j'aimerai bien ajouter et qui feront peut-être l'objet d'un future billet.
Il y aura certainement d'autres choses que j'aurai besoins d'implémenter par la suite suivant mon utilisation. Si elles sont assez intéressante selon moi, elles feront sûrement l'objet d'autres billets.
Pagination de la sitemap
La sitemap peut devenir très grande suivant le nombre de billets. Un mécanisme de pagination automatique serait intéressant pour permettre de réduire sa taille. Une autre alternative serai d'archiver les billets afin qu'ils n'apparaissent plus sur cette page.
Un index des billets par catégorie
Avec le système que j'ai mis en place pour les catégories, j'aimerai avec une page d'index qui permettrai de lister tous les billets appartenant à chaque catégorie.
Mises à jour du post
Juin 2024
- Suppression du lien vers googleapi pour le téléchargement de la police
- Import en local de la police
- Mise à jour du fichier style.css