Updated: 2019 Sep 11

Building a Blog with Emacs Org-Mode

As my WordPress subscription neared its expiration I decided I would finally take the time to build my own website using a pure Emacs and Org mode setup. WordPress, while a fine CMS, is overkill as I have no need for a built-in editor, themes, or plug-ins. I just need a powerful markup language and static HTML page generation.

While using WordPress I already composed my posts in Org mode and I imported them using the org2blog package. This was an okay solution, but I was never happy with how clunky of an experience it was and felt redundant when Org mode has built in HTML exporting. Add to that a cheap WordPress.com plans offer few customization options.

Goals

  1. Have a lightweight, consistent layout and mobile scaling website
  2. Be able to easily rebuild my website from source and sync it to any web server
  3. Do so without adding any new software dependencies (just Emacs, git, make, ssh, rsync)

Tools

Editor Emacs
Markup Language Org mode
Site Generator Emacs
Syncing ssh & rsync

Setup

Org mode's built-in publishing functionality handles converting the Org files into HTML pages and linking them all together. We need to specify the org-publish-project-alist where our files are, how to export them, and where to put them.

(setq org-publish-project-alist
      `(("home"
         :base-directory "~/Documents/blog/org/"
         :base-extension "org"
         :publishing-directory "~/Documents/blog/html/"
         :publishing-function org-html-publish-to-html
         :recursive nil
         :html-head     ,taingram-head
         :html-preamble "<div id=\"updated\">Updated: %C</div>"
         :html-postamble ,taingram-footer)
        ("blog"
         :base-directory "~/Documents/blog/org/blog/"
         :base-extension "org"
         :publishing-directory "~/Documents/blog/html/blog/"
         :publishing-function org-html-publish-to-html
         :auto-sitemap t
         :sitemap-filename "blog.org"
         :sitemap-sort-files anti-chronologically
         :sitemap-format-entry org-sitemap-custom-entry-format
         :html-head ,taingram-head
         :html-preamble "<div id=\"updated\">Updated: %C</div><nav><a href=\"/\">&lt; Home</a></nav>"
         :html-postamble ,taingram-footer)
        ("static"
         :base-directory "~/Documents/blog/org/"
         :base-extension "css\\|jpg\\|gif\\|png\\|pdf"
         :recursive t
         :publishing-directory "~/Documents/blog/html/"
         :publishing-function org-publish-attachment)
        ("taingram.org" :components ("home" "blog" "static"))))

One interesting thing to note is that I've separated "home" from "blog". I did this because while I am interested in having a sitemap generated listing my blog posts, of which I will keep adding many pages to, I see less value having one for the root directory of my website. Where I will rarely add pages and when I do it is easier to just add links manually than have a large and confusing sitemap.

You can see the variables html-(head|preamble|postamble) this is where I insert my stylesheet, and extra HTML for navigation and footer. The variables I set them to are shown below.

(defvar taingram-head "<link rel=\"stylesheet\" href=\"/style.css\" type=\"text/css\"/>")

(defvar taingram-footer "<hr/>
<footer>
  <div class=\"copyright-container\">
    <div class=\"copyright\">
      Copyright &copy; 2019 Thomas Ingram some rights reserved<br/>
      Content is available under
      <a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\">
      CC-BY-SA 4.0
      </a> unless otherwise noted
    </div>
    <div class=\"cc-badge\">
      <a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\">
        <img alt=\"Creative Commons License\"
             src=\"https://i.creativecommons.org/l/by-sa/4.0/88x31.png\" />
       </a>
    </div>
  </div>

  <div class=\"generated\">
    Created with %c; powered by  <a href=\"https://httpd.apache.org/\">Apache</a> and <a href=\"https://getfedora.org/\">Fedora</a> <a href=\"https://www.gnu.org\">GNU</a>/<a href=\"https://www.kernel.org/\">Linux</a>
  </div>
</footer>")

This the basics needed for site generation.

HTML5

I preferred to write my own CSS stylesheet from scratch so I disabled

; Disable some Org publish defaults
:html-head-include-scripts nil
:html-head-include-default-style nil
:with-toc nil
:section-numbers nil

; Use HTML5 tags
:html-html5-fancy t
:html-doctype "html5"

Tramp

In this config I do not make use of Emacs' tramp features which would allow for directly exporting these files to the remote server by changing :publishing-directory to something like "//ssh:username@website.com:~/public_html//". In my (limited) testing I have found this to be far to slow in comparison with syncing the files after export.

TODO RSS Feed

(require 'ox-rss)

Building

There are two ways to build this blog, one within Emacs and one outside of Emacs.

For those unfamiliar with Emacs I have provided a makefile that will generate the HTML files (only GNU Emacs and make are required dependencies).

all:
	emacs -l publish.el -f org-publish-all

publish: all
	rsync -e ssh -vr html/ taingram@taingram.org:/var/www/taingram.org/html/

clean:
	rm -r html/

Writing New Blog Posts

Originally, to write a new post I was simply navigating to the blog's git repo and adding a new file as you usually would. I am curious if this can be streamlined using Org mode's capture features.

Conclusion

Thanks to Thibault Marin for his help with make a custom sitemap function using an Org mode macro.