From 3cb027c93ac91b24d6f5c819950d069ad6b4affc Mon Sep 17 00:00:00 2001 From: JSDurand Date: Sat, 28 Aug 2021 19:22:12 +0800 Subject: Refinements to publish the blog * org-conf.el (durand-org-publish-sidebar) (durand-org-publish-insert-sidebar): Use a sidebar for the navigation: Instead of the built-in UP/HOME buttons, I want to go to sitemap files for different types of blog posts quickly. This is more convenient for the readers I think. This is inspired by the design of Protesilaos' website. (durand-org-publish-css-file): Add my own CSS file in the header. (durand-org-publish-favicon): Add the favicon I produced by a simple C program. (org-publish-project-alist): Add a Math type of blog posts. (durand-sitemap-custom-string-alist): Add a custom string to each type's sitemap file. (durand-org-publish-sitemap, durand-org-publish-sitemap-format): Use a table instead of a list in the sitemap. This is also inspired by the design of Protesilaos' website. (durand-org-publish-plist-get): The built-in `plist-get' mal-functions, and this is a replacement. After I update my Emacs, this problem might be fixed, and this replcement might not be needed anymore. (durand-org-publish-convert-time): Convert the result of `parse-time-string' to a time value. The result may lack some information, as the parsed string does not provide them. So this function supplies 0 for the missing values. Note that it assumes the year, month, and day cannot be missing, though. (durand-org-index-entries-max-num): Show at most a fixed number of entries on the index page. This maximum is set to 10 now. (durand-org-post-process): Shorten the date strings in the sitemap files: I need the exact date for sorting, but I don't want to show those information in the sitemap files. So I convert the long form of dates to comments, and use the shortened form for display. Also this generates a proper index page from the various sitemap files. Right now it will insert the entries to the index file each time this is called, which is kind of inefficient. I might adapt a more efficient method later on. I also want to generate Atom feeds in this function, but right now I don't have such a need, and this is not fulfilled yet. This kind of imitates the behaviour of Protesilaos' website. --- org-conf.el | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 322 insertions(+), 27 deletions(-) diff --git a/org-conf.el b/org-conf.el index 019c033..68d4de7 100644 --- a/org-conf.el +++ b/org-conf.el @@ -546,13 +546,46 @@ If DESC is non-`nil', then it is the description of the new link." ;;; publishing settings +;;;;; Custom preamble for the sidebar + +(defvar durand-org-publish-sidebar nil + "The sidebar that provides the navigation of the website.") + +(setq durand-org-publish-sidebar + "
\n\ + Math \n\ + Code \n\ + Life \n\ + Home
") + +;; REVIEW: This might be unnecessary. +(defun durand-org-publish-insert-sidebar (_arg) + "Return the sidebar." + durand-org-publish-sidebar) + +;;;;;; Add a custom head + +(defvar durand-org-publish-css-file nil + "A custom css-file for publishing.") + +(setq durand-org-publish-css-file + "") + +(defvar durand-org-publish-favicon nil + "A custom favicon for publishing.") + +(setq durand-org-publish-favicon + "") + ;;;;; Projects settings (setq org-publish-project-alist (list (list "website" - :components (list "code" "life" "main")) + :components (list "math" "code" "life" "main")) (list "code" :base-directory (directory-file-name @@ -575,11 +608,44 @@ If DESC is non-`nil', then it is the description of the new link." :sitemap-date-format "Published: %F %a %R" :sitemap-filename "code-sitemap.org" :sitemap-title "About coding" - :html-link-home "index.html" :sitemap-sort-files 'anti-chronologically - :html-head "" - :html-preamble t) + :html-head (concat durand-org-publish-css-file + "\n" + durand-org-publish-favicon) + :html-link-home "" + :html-link-up "" + :html-home/up-format "" + :html-preamble #'durand-org-publish-insert-sidebar) + (list + "math" + :base-directory (directory-file-name + (expand-file-name + "math" (expand-file-name + "blog" org-directory))) + :base-extension "org" + :publishing-directory (directory-file-name + (expand-file-name + "public" org-directory)) + :publishing-function #'org-html-publish-to-html + :section-numbers nil + :with-toc nil + :with-email t + :with-creator t + :auto-sitemap t + :recursive t + :sitemap-function #'durand-org-publish-sitemap + :sitemap-format-entry #'durand-org-publish-sitemap-format + :sitemap-date-format "Published: %F %a %R" + :sitemap-filename "math-sitemap.org" + :sitemap-title "Mathematics" + :sitemap-sort-files 'anti-chronologically + :html-head (concat durand-org-publish-css-file + "\n" + durand-org-publish-favicon) + :html-link-home "" + :html-link-up "" + :html-home/up-format "" + :html-preamble #'durand-org-publish-insert-sidebar) (list "life" :base-directory (directory-file-name @@ -602,11 +668,14 @@ If DESC is non-`nil', then it is the description of the new link." :sitemap-date-format "Published: %F %a %R" :sitemap-filename "life-sitemap.org" :sitemap-title "My life" - :html-link-home "index.html" :sitemap-sort-files 'anti-chronologically - :html-head "" - :html-preamble t) + :html-head (concat durand-org-publish-css-file + "\n" + durand-org-publish-favicon) + :html-link-home "" + :html-link-up "" + :html-home/up-format "" + :html-preamble #'durand-org-publish-insert-sidebar) (list "main" :base-directory (directory-file-name @@ -623,24 +692,32 @@ If DESC is non-`nil', then it is the description of the new link." :with-creator t :auto-sitemap nil :recursive nil - :html-head "" - :html-preamble t))) + :html-head (concat durand-org-publish-css-file + "\n" + durand-org-publish-favicon) + :html-link-home "" + :html-link-up "" + :html-home/up-format "" + :html-preamble #'durand-org-publish-insert-sidebar))) ;;;;; Sitemap function ;;;;;; A hack to insert some string in the sitemap file. -(defvar durand-sitemap-custom-string-alist - (list - (cons "About coding" - "This is the coding blog. It contains my coding experiments, or \ -one might think of them as development diaries.") - (cons "My life" - "This is my casual blog. It contains articles about my plain \ -life.")) +(defvar durand-sitemap-custom-string-alist nil "An association list that relates the title of the sitemap and the string to insert.") +(setq durand-sitemap-custom-string-alist + (list + (cons "About coding" + "This is my coding blog. It contains my coding experiments, or \ +one might think of them as development diaries.") + (cons "My life" + "This is my casual blog. It contains articles about my plain \ +life.") + (cons "Mathematics" + "My Mathematics-related articles are put here."))) + ;;;;;; Custom sitemap function (defun durand-org-publish-sitemap (title rep) @@ -650,19 +727,237 @@ TITLE is the title of the sitemap. REP is a representation of the files and directories in the project. Use such functions as `org-list-to-org' or `org-list-to-subtree' to transform it." - (format "#+TITLE: %s\n#+AUTHOR: JSDurand\n%s#+DATE: <%s>\n\n%s\n\n%s" + (format "#+TITLE: %s\n#+AUTHOR: JSDurand\n%s#+DATE: <%s>\n\n%s\n\n\ +#+ATTR_HTML: :border nil :rules nil :frame nil\n\ +%s" title "#+HTML_LINK_UP: index.html" (format-time-string "%F %a %R") (cdr (assoc title durand-sitemap-custom-string-alist #'string=)) - (org-list-to-org rep))) + ;; generate a table + (org-list-to-generic + rep + '(:ustart "|---|" + :uend "|---|" + :isep "|---|")))) ;;;;;; Custom sitemap format +;; NOTE: I use a table to style the entries. + +;; NOTE: This stores the date in an ugly long format. But worry not: +;; it will be replaced by a clean form in the post-processing phase. +;; The long form is inserted here so that we can sort the entries +;; precisely. + (defun durand-org-publish-sitemap-format (entry _style project) - "Format the entry for the sitemap. -A date is added." - (format "[[file:%s][(%s) %s]]" - entry (format-time-string - "%F %R" (org-publish-find-date entry project)) + "Format the entry for the sitemap as a table with a date. +ENTRY is the entry file name to format. + +STYLE is either 'list or 'tree, which is ignored by us. + +PROJECT is the current project." + (format "| %s | [[file:%s][%s]] |" + (format-time-string + "%FT%T%z" + (org-publish-find-date entry project) + (current-time-zone)) + entry (org-publish-find-title entry project))) + +;;;;; Post-processing + +;; I post-process the sitemaps in order to generate a section +;; of "latest updates" on the index page. + +;;;;;; fix: plist-get not working + +;; For some reason the built-in `plist-get' does not work... +(defun durand-org-publish-plist-get (prop plist) + (let (res) + (while (consp plist) + (cond + ((eq (car plist) prop) + (setq res (cadr plist)) + (setq plist nil)) + ((setq plist (cddr plist))))) + res)) + +;;;;;; Helper for converting the format from `parse-time-string' + +(defun durand-org-publish-convert-time (spec) + "Convert SPEC to a valid time value. +SPEC should be the result of `parse-time-string'. + +It is assumed that the year, the month, and the day components +are present." + (let ((sec (car spec)) + (minute (cadr spec)) + (hour (caddr spec))) + (encode-time + (append + (list + (or sec 0) + (or minute 0) + (or hour 0)) + (cdddr spec))))) + +;;;;;; Post process to generate the index page + +(defvar durand-org-index-entries-max-num 10 + "The maximal number of entries to show on the index page.") + +(autoload #'durand-take (locate-user-emacs-file "common.el")) + +(defun durand-org-post-process (project) + "Generate a proper index page and Atom feeds. +Also shorten the date strings in the sitemap files, and store the +completion information in an attribute." + (let* ((project-plist (cdr (assoc project org-publish-project-alist #'string=))) + (components (durand-org-publish-plist-get + :components project-plist)) + (sitemap-file (durand-org-publish-plist-get + :sitemap-filename project-plist)) + ;; I cheat here + (publishing-dir (expand-file-name "~/org/public/")) + (publish-sitemap-file (cond + (sitemap-file + (replace-regexp-in-string + "org$" "html" + (expand-file-name sitemap-file publishing-dir))))) + (index-file (expand-file-name "index.html" publishing-dir)) + contents) + (cond + ;; depth-first recursion + (components + (setq contents (apply #'append + (delete nil (mapcar #'durand-org-post-process components)))) + ;; We only want some items + (setq contents + (durand-take + durand-org-index-entries-max-num + (sort contents + (lambda (x y) + (time-less-p + (car y) (car x)))))) + ;; If contents is non-nil, we need to process the info in the + ;; index page. + (cond + (contents + (with-temp-buffer + (insert-file-contents index-file) + (goto-char (point-min)) + (search-forward "Latest updates") + (goto-char (line-end-position)) + (while (search-forward "") + (match-end 0)))) + (insert "\n") + (insert " + + +++ ++\n\n\n") + (mapc + (lambda (entry) + (insert "") + (insert "\n") + (insert "\n\n")) + contents) + (insert "\n
") + (insert (format-time-string "%F" (car entry))) + (insert "") + (insert (cdr entry)) + (insert "
") + (setq contents (buffer-substring-no-properties + (point-min) (point-max)))) + (write-region contents nil index-file)))) + ((and publish-sitemap-file + (stringp publish-sitemap-file) + (file-exists-p publish-sitemap-file)) + (with-temp-buffer + (insert-file-contents publish-sitemap-file) + (goto-char (point-min)) + (search-forward "" nil t) + (let ((regexp (rx-to-string + '(seq + ""))) + ">") + t)) + pos pos-2 temp temp-time) + (while (re-search-forward regexp nil t) + (setq pos (point)) + ;; replace the first org-left by org-right + (save-excursion + (goto-char (match-beginning 0)) + (setq pos-2 (match-end 0)) + (save-match-data + (cond + ((re-search-forward "left" pos-2 t) + (replace-match "right") + ;; fix pos + (setq pos (1+ pos)))))) + (search-forward "\n") + (write-region nil nil publish-sitemap-file))) + (setq + temp + (cons + (cons temp-time + (progn + (re-search-forward regexp) + (setq pos (point)) + (search-forward "