I recently moved to a literate programming style of emacs configuration. I've configured org-mode to automatically publish the latest HTML version of this configuration whenever I publish my blog posts. Since I love metadata and statistics, I wondered whether or not I could get the org-mode export to add Google Analytics tracking code for me. Turns out I can!

I recently answered a question on the emacs sub-reddit about how org-mode properties could be used inside the HTML export. When setting up org-mode to blog with Jekyll, which I do, the internet advises using a BEGIN_EXPORT html block to embed the Jekyll YAML front-matter into the file that Jekyll then picks up and applies its templates to. Since the org HTML exporter will copy these verbatim, this works quite nicely. The reddit post asked about how properties could be used instead such that exports can be made more compatible with other backends.

An example section of an org-mode file being used to blog with Jekyll might look like this:

#+BEGIN_EXPORT html
---
layout: post
title: "Adding Google Analytics codes to Org Mode's HTML export"
---
#+END_EXPORT

After reading some documentation I found that you could derive a custom org-mode export backend from the HTML backend. This allows you to attach filter and translation functions to each section of a document and fall back to the default org-mode HTML export functions when you don't define them. In the answer to the question above I defined a translation function for the special template section that read the title property and inserted it at the top.

This got me thinking. Could I do a similar thing for the Google Analytics code for my exported Emacs config file?

I retrospect I could simply have included a BEGIN_EXPORT html block at the bottom of my org-mode file with the <script></script> tag inside it and the org mode HTML export would have rendered it verbatim. That solution is pragmatic and probably better in general but it doesn't involve learning more about derived backends.

The documentation for custom org-mode export backends days that you can attach a translation function to the the special template keyword but doesn't define what template actually is. It turns out that it's the post-HTML-export value of the content of the document before it has the main page template wrapped around it. E.g. it doesn't include the <head /> or <body /> tag or any footer information. I couldn't find any higher level place in the export backend that would provide the tags I needed, but it turns out that in the default org-mode HTML exporter the template translation function is the thing that provides the <head /> and <body /> tags. It wraps the content in the page template, so by defining my own translation function I override this behaviour. To get the rest of the HTML structure back I need to call (org-html-template template info) to produce the tag I need. My function does this, then searches the HTML string for the </body> tag and inserts my analytics string right before it:

(defun mfoot-append-google-analytics-tag (template info)
  "Appends my Google Analytics script segment to the body"
  (let ((html-template (org-html-template template info)))
    (let ((pos (string-match (regexp-quote "</body>") html-template)))
      (concat (substring html-template 0 pos)
        "<script type=\"text/javascript\">
          (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
          (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
          m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
          })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

          ga('create', 'UA-24568117-1', 'auto');
          ga('send', 'pageview');

        </script>"
        (substring html-template pos)))))

I can then define the backend tell it to use my export function:

(require 'ox-html)
(org-export-define-derived-backend 'mfoot-html-with-google-analytics 'html
  :translate-alist
  '(
    (template . mfoot-append-google-analytics-tag)
  )
)

In my org-publish-project-alist I have a meta-project called mfoot.com with various components. Each of these defines a chunk of this site that's published in a different way:

  • The .org files which are published into my Jekyll root in a body-only form so Jekyll can wrap its own templating code around them
  • A static copy of any images in my org directory structure into the Jekyll images directory
  • A project that publishes my emacs configuration in an HTML form to the static/emacs-config directory in the Jekyll root
  • A project that publishes my emacs configuration in a raw form to that same root.

In this way I can use org's export dialog (C-c C-e) to generate the mfoot.com meta-project which executes each of these. See the Blog section of my generated emacs config for the source for this.

If you have your project defined in this way you just need to define a publishing function to use:

(defun mfoot-export-emacs-config-to-file
  (plist filename pub-dir)
  "Export current buffer to an blog HTML file"
  (let* ((extension (concat "." org-html-extension))
     (org-export-coding-system org-html-coding-system))
   (org-publish-org-to 'mfoot-html-with-google-analytics filename extension plist pub-dir)))

Then you need to associate the publishing project with the publishing function in your org-publish-project-alist:

("emacs-dotfiles-mfoot.com" ; Publish an HTML version of this file to the static folder.
  :base-directory "~/repositories/dotfiles/.emacs.d/"
  :base-extension "org"
  :publishing-directory "~/repositories/mfoot.com/jekyll/static/emacs-config"
  :exclude ".*"
  :include ("config.org")
  :publishing-function mfoot-export-emacs-config-to-file
  :html-extension "html"
)

That's it. Next time you export this project you'll get a tracking code right before the </body> tag in your HTML.

Note: Please use your own tracking code, don't copy this verbatim.