Custom Org HTML document preambles and postambles

There are multiple ways to set preambles and postambles when exporting from Org to HTML.1

:html-preamble

The easiest to use is the :html-preamble key when setting org-publish-project-alist2:

(setq org-publish-project-alist
          '(("project"
             :base-directory "/src"
             :publishing-directory "/dist"
             :publishing-function org-custom-html-publish-to-html
             :html-preamble "By %a on %C (originally published on %d)")))

The exporter supports placeholders like %a to insert the author name, %d for the publication date and %C for the last modification date3.

org-html-preamble

For more control (like showing some data conditionally), you can set org-html-preamble to point to a function. This disables the placeholders, but the function is called with the info attribute, which holds the same information4:

(defun custom-preamble (info)
  (let ((timestamp-format (plist-get info :html-metadata-timestamp-format)))
    (concat "By "
              (org-export-data (plist-get info :author) info)
              " on "
              (let ((file (plist-get info :input-file)))
                (format-time-string timestamp-format
                                    (and file (file-attribute-modification-time
                                               (file-attributes file)))))
              "(originally published on "
              (org-export-data (org-export-get-date info timestamp-format) info)
              ")")))

(setq org-html-preamble 'custom-preamble)

Now, we can only show the updated date when it differs from the publication date, for example:

(defun custom-preamble (info)
  (let ((timestamp-format (plist-get info :html-metadata-timestamp-format))
          (file (plist-get info :input-file)))
    (let ((published (org-export-data (org-export-get-date info timestamp-format) info))
            (updated (format-time-string timestamp-format
                                         (and file (file-attribute-modification-time
                                                    (file-attributes file))))))
        (concat "By " (org-export-data (plist-get info :author) info)
                " on " updated
                (unless (equal published updated)
                  (concat " (originally published on " published ")"))))))

(setq org-html-preamble 'custom-preamble)

  1. This document refers only to preambles, but everything discussed works for postambles as well.

    ↩︎
  2. This is equivalent to setting the org-html-preamble variable:

    (setq org-html-preamble "By %a on %C (originally published on %d)")
    
    ↩︎
  3. :

    From Emacs’ help for the org-html-preamble-format variable:

    Alist of languages and format strings for the HTML preamble.

    […]

    This format string can contain these elements:

    %t stands for the title. %s stands for the subtitle. %a stands for the author’s name. %e stands for the author’s email. %d stands for the date. %c will be replaced by ‘org-html-creator-string’. %v will be replaced by ‘org-html-validation-link’. %T will be replaced by the export time. %C will be replaced by the last modification time.

    If you need to use a “%” character, you need to escape it like that: “%%”.

    ↩︎
  4. (defun org-html-format-spec (info)
      "Return format specification for preamble and postamble.
    INFO is a plist used as a communication channel."
      (let ((timestamp-format (plist-get info :html-metadata-timestamp-format)))
        `((?t . ,(org-export-data (plist-get info :title) info))
            (?s . ,(org-export-data (plist-get info :subtitle) info))
            (?d . ,(org-export-data (org-export-get-date info timestamp-format)
                                    info))
            (?T . ,(format-time-string timestamp-format))
            (?a . ,(org-export-data (plist-get info :author) info))
            (?e . ,(mapconcat
                    (lambda (e) (format "<a href=\"mailto:%s\">%s</a>" e e))
                    (split-string (plist-get info :email)  ",+ *")
                    ", "))
            (?c . ,(plist-get info :creator))
            (?C . ,(let ((file (plist-get info :input-file)))
                     (format-time-string timestamp-format
                                         (and file (file-attribute-modification-time
                                                    (file-attributes file))))))
            (?v . ,(or (plist-get info :html-validation-link) "")))))
    
    ↩︎