UP | HOME

Published: 2016-04-24

Blogging with Orgmode

Table of Contents

After several attempts of trying to get Orgmode working for my blog, I think I’ve finally settled on a workable solution using org-publish and a free static hosting service that works with Dropbox.

Goals

I tried to keep the scope limited while moving to Orgmode, to keep things simple.

  1. Use Orgmode, duh!
  2. Publish the entire site as a single Orgmode project
  3. Use the auto-generated sitemap as the index (don’t want to manually add links to new blog posts)
  4. Free static hosting with custom domain

Setting up the project

Here’s my website’s Orgmode project defintion defined in my Emacs config1:

 1: (setq org-publish-project-alist
 2:       `(("website"
 3:          :base-directory "~/Projects/blog/"
 4:          :publishing-directory "~/Dropbox/Apps/updog/john2x/"
 5:          :recursive t
 6:          :exclude "level-.*\\|.*\.draft\.org"
 7:          :publishing-function org-html-publish-to-html
 8:          :auto-sitemap t
 9:          :sitemap-filename "index.org"
10:          :sitemap-title "John Louis Del Rosario"
11:          :sitemap-sort-files "chronologically"
12:          :sitemap-function my-website-sitemap-function
13:          :html-link-up "/"
14:          :html-link-home "/"
15:          :html-preamble "<p class=\"date\">Published: %d</p>"
16:          :html-postamble my-website-html-postamble)))

Let’s go over the interesting properties:

  1. :publishing-directory: Put our published files in the Dropbox folder so they are publicly accessible.
  2. :recursive: Recursively export all org files within the :base-directory.
  3. :exclude: Exclude some files from being exported and listed in the sitemap during publishing (e.g. drafts prefixed with .draft.org will not be published).
  4. :auto-sitemap: Turn on automatic sitemap creation/updating when re-publishing the project.
  5. :sitemap-filename: Name the generated sitemap as index.org so it gets published as index.html.
  6. :sitemap-sort-files: Sort sitemap links based on the files’ dates, useful for the blog posts.
  7. :sitemap-function: Use a custom function to generate the sitemap. We’ll take a look at this shortly.
  8. :html-preamble: Use custom preamble. Although the docs say we could specify the preamble as options (e.g. #+OPTIONS: html-preamble:"foo"), which would have been ideal, it doesn’t seem to work with Orgmode 8.3.4. So we hardcode it at the project level.
  9. :html-postamble: Use custom function for the postamble. See “Updates” below.

I wanted my index page to have all the links needed automatically added, which the sitemap function does nicely. But since the sitemap file is re-generated on every publish, I have to insert/remove any content from it via a custom sitemap generator function.

(defun my-website-sitemap-function (project &optional sitemap-filename)
  "Custom sitemap generator that inserts additional options."
  (let ((buffer (org-publish-org-sitemap project sitemap-filename)))
    (with-current-buffer buffer
      (insert "\n#+OPTIONS: html-preamble:nil")
      (insert "\n#+SUBTITLE: a.k.a. john2x")
      (insert "\n\n#+BEGIN_EXAMPLE")
      (insert "\nCopyright (c) 2016 John Louis Del Rosario")
      (insert "\n#+END_EXAMPLE")
      (save-buffer))))

It basically takes the output from the default sitemap function and adds/removes custom content.

Setup files

Orgmode allows documents to reference a SETUPFILE, which is basically an “included” template. This is useful for defining common header options across documents. For example:

#+SETUPFILE: path/to/setup_file.org
...

For this site, I have three setup files defined:

  1. A base template, org-templates/level-0.org
  2. A template specific for blog posts, org-templates/level-0-blog.org
  3. A template specific for one-off pages (e.g. the about page), org-templates/level-0-page.org

The level-0 prefix was taken from Sebastian Rose’s publishing tutorial2. It also makes excluding the templates easier.

The blog post template includes the base template, and it enables the rendering of the table of contents.

The page template includes the base template, and disables the preamble.

Hosting

Previous attempts

Github Pages

The previous version of this site was using Jekyll on Github Pages. I was hoping I could continue using Github Pages, but unfortunately there was no way to disable Jekyll processing. So my exported documents all came out incorrectly.

Bitbucket static file hosting

Next I looked into Bitbucket’s undocumented feature to serve static content (similar to Github Pages, but without Jekyll). It works great, but they don’t allow custom domains.

What’s up dog?

I finally stumbled on updog.co, a free service that serves files directly from your Dropbox folder. It also allows custom domains for each site. Since I already have a Dropbox account, I didn’t have to shell out any cash. And if/when the service goes down, the owner is cool enough to provide the source3, so if I can’t find an alternative, I’d just bite the bullet and probably run my own instance of the service.

Workflow

So now that I have everything set up, writing a new post is a great experience. I get to write using Org, which makes writing more enjoyable. For drafts that aren’t ready for publishing yet (but which I’d like to view a render of), I just need to name them *.draft.org and they won’t be listed in the sitemap.

Once everything is ready, a simple C-c C-e P p publishes everything and Dropbox + updog.co does the rest!

Improvements

There’s still some rough edges I’d like to iron out.

  1. The index/sitemap is very bare bones. I’ll probably write an improved sitemap function in the future.
  2. I’m still trying to figure out how to generate an RSS feed.
  3. I’d like to render the KEYWORDS option in a post (since Google ignores the keywords meta tag).
  4. My favicon doesn’t seem to be loaded. I wanted to use the Orgmode unicorn.
  5. The style is pretty much non-existent at the moment. Mostly due to laziness, but I kind of like the aesthetic of it (especially if using the Computer Modern font4).

Updates

Rendering keywords and improved postamble

I’ve figured out how to render KEYWORDS in the postamble. ox-html allows specifying a function for the html-postamble value. The function takes a single argument, a property list of the current document’s options. So I just needed to build up a string taking the values I needed from the property list. Here’s what it looks like:

(defun my-website-html-postamble (options)
  (concat "<hr>"
          (if (and (plist-get options ':keywords) (not (string= (plist-get options ':keywords) "")))
              (format "<p>Keywords: %s</p>" (plist-get options ':keywords))
              "")
          (format "<p class=\"date\">Modified: %s</p>" (format-time-string "%Y-%m-%d %H:%M:%S"))
          (format "<p>Copyright (c) %s %s</p>"
                  (car (split-string (car (plist-get options ':date)) "-")) ;; TODO: get from custom document option
                  (car (plist-get options ':author)))
          (format "<p>%s</p>" (plist-get options ':creator))))

Favicon

Jesse (maintainer of updog.co) has enabled custom favicon support for updog. Thanks Jesse!

Gitlab Pages

I’ve switched to hosting my website on Gitlab Pages using a plain HTML project.


Footnotes:


Keywords: emacs,orgmode

Modified: 2018-10-23 13:40:46 +08

Copyright (c) 2018 John Louis Del Rosario

Emacs 26.1 (Org mode 9.1.9)