Richard Kallos


Static site generation with Org Mode

:: emacs, writing

UPDATE (2017–01–28):
I no longer use Emacs and Org mode for blogging. I’ve since switched to Frog. I’ve kept this post up for shame and to those who may be interested in blogging with Org.

Let’s get this out of the way; I love Emacs and Org mode. I’ve been using Emacs as my daily editor for the past several years. I was using Vim before that, but tried Emacs and switched over when I wanted a system for organizing lecture notes. Don’t get me wrong, I think Vim is fantastic, and I still use it occasionally, but Emacs has won my heart.

I wrote this post in Emacs

org-publish as a blogging system

This site is generated using Org mode’s publishing features in org-publish. Each post is a .org file that gets transpiled to an HTML file when I run the org-publish command. My setup takes care of RSS, images, custom Javascript (if any) and CSS. I’m also thinking of incorporating either TRAMP or rsync to push changes to my website.

The rest of this blog post is going to go over the various settings I use. To start off, let’s pull in some org-mode libraries that we’ll use to generate our site map.

(require 'org)
(require 'ox-html)
(require 'ox-publish)
(require 'ox-rss)

Next, let’s set a couple of variables. I maintain 2 separate custom files in my \~/.emacs.d folder, one for Windows and another for Linux, so I create a variable that is editable through Emacs’s customize command. The rest of the variables are boilerplate HTML that will go into every page on the site.

(defcustom blog-base-dir "~/blog/" "Base directory for blog files")

(setq website-html-head "<link rel=\"stylesheet\" href=\"css/site.css\"
  type=\"text/css\"/>")

(setq website-html-preamble
  "<h1>Richard Kallos</h1>
<hr></hr>
<div class=\"nav\">
<ul>
<li><a href=\"/\">Home</a></li>
<li><a href=\"/static/about.html\">About</a></li>
<li><a href=\"http://github.com/rkallos\">GitHub</a></li>
<li><a href=\"/static/resume.html\">Résumé</a></li>
<li><a href=\"/index.xml\">RSS</a></li>
</ul>
</div>")

(setq website-html-postamble "<div class=\"footer\"> Copyright 2016 %a (%v
  HTML).<br> Last updated %C.<br> Built with %c.  </div>")

Finally, let’s dig into the meat and potatoes of the system,

(setq org-publish-project-alist
      `(("blog"
         :base-directory ,(concat blog-base-dir "/raw")
         :base-extension "org"
         :publishing-directory ,(concat blog-base-dir "/out")
         :publishing-function org-html-publish-to-html

         :export-with-tags nil
         :exclude-tags ("todo" "noexport")
         :exclude "level-.*\\|.*\.draft\.org"
         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil
         :with-date t

         :auto-sitemap t
         :sitemap-filename "index.org"
         :sitemap-title "Home"
         :sitemap-sort-files anti-chronologically
         :sitemap-file-entry-format "%d - %t"
         :sitemap-function org-publish-org-sitemap

         :html-doctype "html5"
         :html-html5-fancy t
         :html-head ,website-html-head
         :html-head-extra
         "<link rel=\"alternate\" type=\"application/rss+xml\"
                href=\"http://rkallos.com/posts.xml\"
                title=\"RSS feed\">"
         :html-preamble ,website-html-preamble
         :html-postamble ,website-html-postamble)

        ("images"
         :base-directory ,(concat blog-base-dir "/img")
         :base-extension "jpg\\|gif\\|png\\|svg"
         :publishing-directory ,(concat blog-base-dir "/out/img")
         :recursive t
         :publishing-function org-publish-attachment)

        ("js"
         :base-directory ,(concat blog-base-dir "/js")
         :base-extension "js"
         :publishing-directory ,(concat blog-base-dir "/out/js")
         :recursive t
         :publishing-function org-publish-attachment)

        ("css"
         :base-directory ,(concat blog-base-dir "/css")
         :base-extension "css"
         :publishing-directory ,(concat blog-base-dir "/out/css")
         :recursive t
         :publishing-function org-publish-attachment)

        ("rss"
         :base-directory ,(concat blog-base-dir "/raw")
         :base-extension "org"
         :publishing-directory ,(concat blog-base-dir "/out")
         :publishing-function (org-rss-publish-to-rss)
         :html-link-use-abs-url t

         :export-with-tags nil
         :exclude-tags ("todo" "noexport")
         :exclude "level-.*\\|.*\.draft\.org"
         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil
         :with-date t)

        ("static"
         :base-directory ,(concat blog-base-dir "/raw/static")
         :base-extension "org"
         :publishing-directory ,(concat blog-base-dir "/out/static")
         :recursive t
         :publishing-function org-html-publish-to-html

         :with-title t
         :section-numbers nil
         :headline-levels 4
         :with-toc nil

         :html-doctype "html5"
         :html-html5-fancy t
         :html-head ,website-html-head
         :html-preamble ,website-html-preamble
         :html-postamble ,website-html-postamble)

        ("website" :components ("blog" "static" "images" "js" "css" "rss"))))

This snippet defines 7 different ‘projects’ for org-publish; blog, images, js, css, rss, static, and the aggregate project website. When drafting a blog post, I can hit C-c C-e to open Org mode’s handy export buffer, then hit P p to publish the blog.

Reasons to love blogging with Org Mode

My editor, my way

I’ve spent years honing my Emacs configuration. I find reading and exiting text in Emacs to be a pleasure, and I find myself increasingly wanting to do everything I can inside of Emacs. However, that doesn’t justify why I don’t use a different format like Markdown.

Portability

Since Org mode and org-publish are written in Emacs Lisp, I can generate my blog anywhere where Emacs runs with virtually no hassle.

Single input, multiple documents

Org mode has many different output formats. With a single .org file, you can generate HTML, LaTeX, OpenDocument, and many other formats. If you want to generate multiple output files from a single input file, using Org mode makes a lot of sense. Should I so choose, I could turn my blog into a LaTeX Ebook without much effort. From what I understand, I think only Pandoc rivals Org mode in terms of compatible file formats. Well, guess what! Org mode supports exporting to Pandoc with the ox-pandoc library, so I can use all of Pandoc’s formats with Org mode files.

Living Inside Emacs

Much like a craftsman’s workshop, a text editor can be very personal and highly customized. I’ve spent a great deal of time molding both myself and my text editor to be as unobtrusive as possible. As a result, I prefer working in Emacs to anything else. At this point, blogging with Org Mode presents very little friction to me, as I am already accustomed (and addicted) to using Emacs and Org Mode.

Static HTML

As someone who debugs software for a living, I’ve grown to appreciate software with as few ‘moving parts’ as possible. Static HTML with a splash of CSS can give me a simple website that’s (hopefully) beautiful.

Conclusion

Emacs’s fabulous Org mode is extraordinarily flexible. Given my love of Emacs and Org mode, writing my blog using Org mode and org-publish seemed like a great choice. I can’t say I’ve used this system extensively yet, since this is my first post on this new domain, but I’m excited and hopeful for future posts!