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) "")))))
    
    ↩︎