Writing a new org-mode exporter back-end

I’ve been maintaining a simple static website for my jujutsu club since about 1995. For most of that time it was simply hand coded HTML and CSS. I’ve wanted to update the site for a while to give it a more modern look, and to handle mobile devices better. I also wanted to move away from hand-coding the HTML and so was interested by org-mode’s HTML export capacity.

The HTML exporter backend that ships with org-mode didn’t produce the structure I was looking for, and I found myself spending an age fighting the CSS to try to produce the appearance I was after in a range of browsers.

I eventually decided that it was time to stop handcrafting the CSS and look to using a modern framework where the compatibility problem was already solved and I would have simple building blocks to work with. I came across bootstrap and played around with that for a while, handcoding the HTML to experiment with various layouts.

Once I knew what HTML structure I wanted, it was time to try to work out how to persuade org-mode to produce it. It seemed obvious that I wanted some variant of ox-html and on reading that code it seemed that all I needed to modify was ox-html-template, the function that defines the structure of the HTML page. Here I started to get frustrated: where were all the hooks I’d grown to expect in emacs code? There was no obvious way to override ox-html-template. Cursing the lack of a hook variable, I started looking in to how to override a lisp function and dicovered flet. That solution seemed unnecessarily complicated, particularly as I still wanted to be able to use the vanilla ox-html exporter for other projects. I decided to bite the bullet, copy the entirety of ox-html into a new backend and just modify the template function.

It was then that enlightenment dawned. The level at which modifications should be done was not the ox-html backend, but the creation of backends themselves. org-export-define-backend sets up an association between the various elements in the document and the function used to render them. One of those elements is template so associating that with my new template function would do what I wanted, Even better, github-alphapapa on reddit pointed me at the existence of org-define-derived-function which eliminates all the boiler plate so creating my new backend is as simple as:

(org-export-define-derived-backend 'jujutsu-site 'html
  :translate-alist '((template . org-jujutsu-template)))

org-jujutsu-template is modelled unashamedly on ox-html-template, modified for the HTML code i want. It can be found in the elisp directory of the jujutsu website on github.

With a backend now creating the desired HTML for a web page, I could use the publishing functionality of org to process all the files and publish them on a staging server for review:

(setq jujutsu-base-dir "~/personal/jujutsu/org-site/src")
(setq jujutsu-publish-dir "~/Sites/jujutsu")
(setq org-publish-project-alist
      '(("jj-org-files"
         :auto-sitemap t
         :base-directory "~/personal/jujutsu/org-site/src"
         :html-doctype "html5"
         :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/bootstrap.css\" />"
         :html-postamble "https://code.jquery.com/jquery-3.2.1.slim.min.js
https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js
https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js    
"
         :publishing-directory "~/Sites/jujutsu"
         :publishing-function org-jujutsu-site-publish-to-html
         :recursive t
         :with-toc nil
         :section-numbers nil)
        ("jj-css"
         :base-directory "~/personal/jujutsu/org-site/src/css"
         :base-extension "css"
         :html-style nil
         :publishing-directory  "~/Sites/jujutsu/css"
         :publishing-function org-publish-attachment)
        ("jj-images"
         :base-directory "~/personal/jujutsu/org-site/src/images"
         :base-extension any
         :publishing-directory  "~/Sites/jujutsu/images"
         :publishing-function org-publish-attachment)
        ("jj-htaccess"
         :base-directory "~/personal/jujutsu/org-site/src"
         :base-extension "htaccess"
         :publishing-directory  "~/Sites/jujutsu"
         :publishing-function org-publish-attachment)

        ("jujutsu"
         :components ("jj-org-files" "jj-css" "jj-images" "jj-htaccess"))))

Bootstrap requires some javascript libraries for part of its functionality; I use the html-postamble to ensure they are included. The htaccess, css, and image files are simply copied across from the source tree. Org files are published using the new jujutsu-site backend.

(defun org-jujutsu-site-publish-to-html (plist filename pub-dir)
  "Publish an org file to HTML.

FILENAME is the filename of the Org file to be published.  PLIST
is the property list for the given project.  PUB-DIR is the
publishing directory.

Return output file name."
  (org-publish-org-to 'jujutsu-site filename
                      (concat "." (or (plist-get plist :html-extension)
                                      org-html-extension
                                      "html"))
                      pub-dir))

The moral of this story? If I’d spent a bit more time reading the manual which points out the existence of https://orgmode.org/worg/dev/org-export-reference.html, or exploring the org exporter code and looking into how backends were created I could have saved myself a lot of angst.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s