Generating a Blog Site from Denote Entries

Updates

Generating a Blog Site from Denote Entries

I’ve really been enjoying Prot’s package Denote recently:

https://protesilaos.com/emacs/denote

My previous stream on Denote

It works well with how I like to write notes, basically as a linear series of entries to capture my thoughts over time.

Now that I’ve been using it for a couple of months, I’d like to start generating my personal website from a tagged subset of my Denote files so that I can share some of my personal writings with the public.

Today we’ll hack some Emacs Lisp to figure out a simple way to generate such a website!

The Plan

  • Find a list of all note files with a specific tag like public or publish
  • Generate a simple site with just an index page with post titles and dates and links to the full pages for the articles
  • RSS feed for the main posts
  • Optionally an RSS feed for specific tags

The Final Code

;; Initialize package sources
(require 'package)

;; Set the package installation directory so that packages aren't stored in the
;; ~/.emacs.d/elpa path.
(setq package-user-dir (expand-file-name "./.packages"))

(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/"))

;; Initialize the package system
(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

;; Install and load weblorg
(unless (package-installed-p 'weblorg)
  (package-install 'weblorg))
(require 'weblorg)

;; Install and load Denote
(unless (package-installed-p 'denote)
  (package-install 'denote))
(require 'denote)

(setq denote-directory "~/Denotes")

;; (setq files-to-publish
;;       (mapcar #'file-name-nondirectory
;;               (denote--directory-files-matching-regexp "_public.*\\.org$")))

;; (defun get-article-output-path (org-file pub-dir)
;;   (if (string-match "\\/index\\(.org|.html\\)$" org-file)
;;       pub-dir
;;     (let ((article-dir (concat pub-dir
;;                                (thread-first org-file
;;                                              (file-name-nondirectory)
;;                                              (file-name-sans-extension)
;;                                              (file-name-as-directory)
;;                                              (dw/strip-file-name-metadata)
;;                                              (concat "/")))))
;;       (message "ARTICLE DIR: %s" article-dir)
;;       (unless (file-directory-p article-dir)
;;         (make-directory article-dir t))
;;       article-dir)))

;; (defun org-html-publish-to-html (plist filename pub-dir)
;;   "Publish an org file to HTML, using the FILENAME as the output directory."
;;   (let ((article-path (get-article-output-path filename pub-dir)))
;;     (cl-letf (((symbol-function 'org-export-output-file-name)
;;                (lambda (extension &optional subtreep pub-dir)
;;                  (concat article-path "index" extension))))
;;       (org-publish-org-to 'html
;;                           filename
;;                           (concat "." (or (plist-get plist :html-extension)
;;                                           "html"))
;;                           plist
;;                           article-path))))

;; (setq org-publish-project-alist
;;       (list
;;        (list "blog-site:main"
;;              :auto-sitemap t
;;              :sitemap-filename "index.html"
;;              :base-extension "org"
;;              :base-directory denote-directory
;;              :exclude "\\.org$"
;;              :include files-to-publish
;;              :publishing-function '(org-html-publish-to-html)
;;              :publishing-directory "./public"
;;              :with-timestamps t
;;              :with-title nil)))

;; (org-publish-all t)

;; Configure the site
(setq denote-site (weblorg-site
                   :name "daviwil.com"
                   :base-url (if (string= (getenv "PROD") "true")
                                 "https://daviwil.com"
                               "http://localhost:8080")))

(defun dw/fix-slugs (posts)
  (weblorg-input-aggregate-all-desc
   (mapcar (lambda (post)
             (message "Post:" (cdaadr post))
             post)
           posts)))

(defun dw/strip-file-name-metadata (file-name)
  (replace-regexp-in-string "^.*--\\(.*?\\)__.*$" "\\1" file-name))

(setq posts
      (mapcar (lambda (file)
                ;; This also needs "route" and "url" which seems to be something
                ;; you need to do yourself when using :input-sources
                `("post" . (("title" . ,(denote--retrieve-title-value file 'org))
                            ("slug" . ,(dw/strip-file-name-metadata file))
                            ("file" . ,file))))
              (denote--directory-files-matching-regexp "_public.*\\.org$")))

;; Live streams index page
(weblorg-route
 :name "index"
 :site denote-site
 :input-source posts
 :template "post-index.html"
 :output "public/index.html"
 :url "index.html")

(weblorg-route
 :name "posts"
 :site denote-site
 :input-source posts
 :template "post.html"
 :output "public/{{ slug }}/index.html"
 :url "/{{ slug }}/")

;; Export the site
(weblorg-export)
Subscribe to the System Crafters Newsletter!
Stay up to date with the latest System Crafters news and updates! Read the Newsletter page for more information.
Name (optional)
Email Address