Adding Google Analytics codes to Org Mode's HTML export
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.