Пилим уютный бложик на Emacs: Шаблон

В прошлом посте я уже рассказывал, о чем идет речь и что хочу получить в результате, так что не буду повторяться и сразу приступлю к делу.

Сам по себе ox-html.el дает не такие уж и широкие возможности в плане определения шаблона страницы. Фактически, мы располагаем тремя блоками - preamble, content и postamble, и расположены они непосредственно в body страницы, причем preamble и postamble имеют одинаковый набор классов, устанавливаемый через переменную org-html--pre/postamble-class и заданные id. Тем не менее, в минимальном случае, и этого может оказаться достаточно.

Содержимое блоков preamble и postamble можно переопределить свойствами :html-preamble и :html-postamble для проекта, что более или менее подходит для создания хедера и футера страницы.

Так, можно создать файлы header.html и footer.html и загрузить их при объявлении проекта подобным способом.

(defun blog/file-get-contents (file-path)
  "Returns file contents as string"
  (with-temp-buffer
    (insert-file-contents file-path)
    (buffer-string)))

(setq org-publish-project-alist
      `(("blog"
         :base-directory       "~/blog/posts/"
         :publishing-directory "~/blog/public/"
         :publishing-function  org-html-publish-to-html
         :html-preamble        ,(blog/file-get-contents "~/blog/header.html")
         :html-postamble       ,(blog/file-get-contents "~/blog/footer.html"))))

Как уже было сказано выше, preamble и postamble будут выводиться в собственных блоках, так что структурно страница в результате будет выглядеть как-то так:

<html>
  <head><!-- head contents --></head>
  <body>
    <div id="preamble" class="status">
      <!-- header.html -->
    </div>
    <div id="content">
      <!-- post -->
    </div>
    <div id="postamble" class="status">
      <!-- footer.html -->
    </div>
  </body>
</html>

Чтобы переопределить сами блоки preamble и postamble, можно воспользоваться свойством проекта :html-divs или глобальной переменной org-html-divs, если проектов много. Содержимым должен быть список из трех вложенных списков, по одному для preamble, content и postamble. Во вложенных списках, первым элементом будет символ, обозначающий сам структурный элемент(preamble, content, postamble), вторым элементом - строка с именем тега, в котором будет заключен соответствующий структурный элемент, ну и третьим элементом вложенного списка будет аттрибут id тега тоже в виде строки. В общем случае, выглядеть это может так:

:html-divs ((preamble  "header"  "header")
            (content   "section" "content")
            (postamble "footer"  "footer"))

Стоит отметить, что использование nil в качестве значений ни к чему не приведет, кроме появления конструкции вида <nil></nil> в результате.

В целом, с этим уже можно работать, осталось подключить собственные стили и исключить генерируемые по умолчанию. Для подключения собственных стилей можно воспользоваться строковым свойством :html-head-extra, которое будет вставлено в head результирующего файла в дополнение к автоматически сгенерированному содержимому. Свойства :html-include-scripts и :html-include-default-style являются булевыми и определяют, включать ли стили и js, генерируемые по умолчанию. Любое значение, отличное от nil будет считаться истинным.

Ниже - содержимое проекта, которое должно получиться в результате.

(defun blog/file-get-contents (file-path)
  "Returns file contents as string"
  (with-temp-buffer
    (insert-file-contents file-path)
    (buffer-string)))

(defconst blog/head-extra
  "<link rel=\"stylesheet\" href=\"style.css\">")

(defconst blog/divs
  '((preamble  "header"  "header")
    (content   "section" "content")
    (postamble "footer"  "footer")))

(setq org-publish-project-alist
      `(("blog"
         :base-directory       "~/blog/posts/"
         :publishing-directory "~/blog/public/"
         :publishing-function  org-html-publish-to-html
         :html-preamble        ,(blog/file-get-contents "~/blog/header.html")
         :html-postamble       ,(blog/file-get-contents "~/blog/footer.html")
         :html-divs            ,blog/divs
         :html-head-extra      ,blog/head-extra
         :html-include-scripts nil
         :html-include-default-style nil)))