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"))))
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
(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.