Blogging from your text editor with orgmode and middleman

Introduction

Before setting up this blog, I used emacs but had never really written any lisp. This post details some serious yak shaving, but it did give me a good excuse to write some lisp and some shitty web design.

I'm not suggesting that this is the most efficient way to publish words and pictures on the internet. This is simply a description of how I'm doing it at the moment.

Components

resources/workflow_overview.png

Posts are composed in Emacs.

A keystroke publishes the post to a directory that is part of a git repo. Publishing to the web is simply 'git commit' and 'git push heroku'. Heroku builds the static Middleman static site, which includes our published org files.

Trying this at home

You can view the code for both the site and my emacs configuration if you are really keen, but I'll sketch the outline here so you can build something similar if you feel inspired.

The workflow above is something of a simplification. The real approach is more like this:

resources/workflow_better.png

Creating a new post

I'm going to assume that you have emacs and the org package alrady installed.

I use org capture templates to kick off a new post. My capture templates variable looks like this:

(setq org-capture-templates
      '(("t" "Todo (unsorted)" entry (file+headline org-default-notes-file "Unsorted Tasks")
         "* TODO %?\n  %i\n  %a")
        ("b" "Blog draft" entry (file (choose-new-post "~/site/org/blog/"))
         "\n* TODO %?\n")
        ("p" "Project Entry" entry (file (choose-project-file))
         "* TODO %?\n  %i\n  %a\n\n")))

Where I have a function 'choose-new-post which looks like this:

(defun choose-blog-file (base-path)
  "Finds the project file, creating one if it doesn't already exist"
  (let* ((date (org-read-date "" 'totime nil nil (current-time) "")) ;;(date)
         (title (read-string "Post title: "))                       ;;(title)
         (url-title (replace-regexp-in-string "\s+" "-" title))       ;;(url)
         (path (format "%s/%s/%s/index.org" base-path (format-time-string "%Y/%m/%d") url-title))) ;;(path)
    (make-directory (file-name-directory path) t)
    (append-to-file (format "#+begin_html\n---\ntitle: %s\ntags: \ndescription: \n---\n#+end_html\n\n" title) nil path)
    path))

The function:

The 'Todo' and 'Project Entry' templates are for other uses, but the 'Blog draft' entry ensures that pressing "Ctrl-C, C" ("C-c c" in emacs-speak) and then "b" takes me directly to a new file (the filename returned by the function 'choose-new-post')

Org-publish

Somewhere within your emacs configuration, you need to set the variable 'org-publish-project-alist' to something like:

(setq org-publish-project-alist
      '(( "blog"
          :base-directory "~/site/org/blog/"                     ;;(base-dir)
          :base-extension "org"                                  ;;(base-ext)
          :publishing-directory "~/site/source/blog/"             ;;(pub-dir)
          :recursive t                                                ;;(rec)
          :body-only t                                               ;;(body)
          :html-extension "html.erb"                             ;;(html-ext)
          :publish-function org-publish-org-to-html)
        ( "blog-static"                                            ;;(static)
          :base-directory "~/site/org/blog/"
          :base-extension "png\\|jpg\\|gif"
          :publishing-directory "~/site/source/blog/"
          :recursive t
          :publish-function org-publish-attachment)
        ("whole" :components ("blog" "blog-static"))))              ;;(whole)

What this does is take all org files from the base directory, publish only the body (I use Middleman to wrap all of the content up) and dump the resulting html in the publishing directory.

The publish directory is the source directory of a Middleman application, so the resulting html (or html.erb) is used as the content for the static site.

I also configure a shortcut to publish both the static resources and the org files with only one command.

The org-publish will render each of the org files in the base directory to html. It will generate a table of contents and wrap all of the source code in appropriate tags (see the source of the code block above).

Middleman

Middleman has an excellent blogging plugin, but it manages the directory structure itself. It is useful if your posts contain no attachments. If you have posts that contain relative links to external files (mine do), the links will break when middleman constructs the new site structure.

I'm using the same setup for my lab notebook which includes lots of attached files, so I had to roll my own stipped-back version.

All of the lifting is done by the classes in orgblog.rb. A writeup of the Middleman configuration will have to wait for another time.

comments powered by Disqus