This blog is now being written with org-mode and Emacs. I have been threatening to do so for a while now. I have previously used ikiwiki, which is also a static site generator. I have switched to org-mode, mainly because I like org markup better than markdown. I write most everything else in org-mode; the context switch, I think, kept me from writing.1

1 Blog files setup

As I have decided that all I need is the publishing features of org-mode, I had to structure the sources for the blog smartly. I think I have come up with a structure that can be maintained and grokked by (org-publish) without requiring too much additional Emacs Lisp.

Another thing I am changing is not publishing directly from a git hook. Not publishing directly from a git hook allows easier writing across several computers. I have (org-publish) write to ~/public_html/jamestechnotes.com for local testing.

This is my setup, from which I borrowed heavily from Dennis Ogbe's writeup:

  • All of my websites are beneath ~/src/wikis. This one is beneath /src/wikis/jamestechnotes.com. If you happen to be really interested in the layout, all of the source is published.2
  • The elisp that glues all of this together is in my emacs config; relevant sections will be detailed below.
  • The blog files split into the following structure:
    • /pages/ — Static pages, like the home page, live here as org files.
    • /css/ — css files live here.
    • /blog/entry/ — individual blog posts live here.
    • /img/ – (Not yet implemented) images would live here.

1.1 How does this help?

Hopefully, /css/ and /img/ are rather clear. They hold, well, css files and images. The images bother me. As images are binary artifacts, they are rather awkward3 to check into git. I suspect if I start accumulating large images, I will look into using some like git annex to manage them.

Static pages, which don't change often, live out of the way beneath /pages/. There is a separate publishing project just for these.

Individual blog entries live in their own org file in /blog/entry/. If I actually start writing many post and the directory get unwieldy, I will have to hash the entries, probably by year. This section also has it's own publishing project, that also generates a sitemap index. Elisp will be developed to coerce this sitemap into a proper blog index.

I have an elisp function, jr/prepare-pages, which looks for a file named header.html in the base-directory of the project. The function is called when (org-publish) generates the preamble of each page. I use this machinery to build the top navigation menu on the pages.

A benefit of this layout, to which I did not pay attention, is the top directory of the web site contains no content that is publishable. Adding a todo.org file here will allow me to add org todo items with ideas for future blog post and have them show up in my Agenda. I could also add actual todo headline in actual blog posts and mark them noexport so they won't publish.

2 Show me the code

While (org-publish) already does a lot out of the box, a bit of help is need to achieve the desired layout. First, links to stylesheets need to be added to the <head> of the document. The variable jr/html-head contains text which is inserted in the document.

(setq jr/html-head
      (concat
       "<link rel=\"stylesheet\" href=\"/../css/org.css\" />\n" ;; start with org.css
       "<link rel=\"stylesheet\" href=\"/../css/main.css\" />"))

org.css was copied from an exported html buffer. main.css contains custom layouts which will override the defaults from org.css and required.

The jr/prepare-pages function mentioned earlier the inserts header.html into the preamble is listed here:

(defun jr/prepare-pages (plist)
  (message "In the prepare pages.")
  (message (plist-get plist :base-directory))
  (let ((header (concat
                 (file-name-as-directory
                  (plist-get plist :base-directory))
                 "header.html")))
    (message header)
    (when (file-exists-p header)
      (with-temp-buffer
        (insert-file-contents header)
        (buffer-string)))))

Now comes the publishing project:

(setq org-publish-project-alist
      `(("jamestechnotes-com" :components ("jamestechnotes-com-css"
                                           "jamestechnotes-com-entries"
                                           "jamestechnotes-com-pages"))
        ("jamestechnotes-com-css"
         :base-directory "~/src/wikis/jamestechnotes.com/css"
         :publishing-directory "~/public_html/jamestechnotes.com/css"
         :base-extension "css"
         :publishing-function org-publish-attachment)
        ("jamestechnotes-com-entries"
         :base-directory "~/src/wikis/jamestechnotes.com/blog"
         :publishing-directory "~/public_html/jamestechnotes.com/blog"
         :recursive t
         :publishing-function org-html-publish-to-html
         :preparation-function jr/prepare-pages
         :htmlized-source t
         :with-toc nil
         :section-numbers nil
         :html-doctype "xhtml5"
         :html-html5-fancy t
         :html-head-include-default-style nil
         :html-head-include-scripts nil
         :html-head ,jr/html-head
         :html-preamble jr/prepare-pages

         :auto-sitemap t
         :sitemap-filename "blog.org"
         :sitemap-title "Blog articles"
         :sitemap-sort-files anti-chronologically

         :makeindex t)
        ("jamestechnotes-com-pages"
         :base-directory "~/src/wikis/jamestechnotes.com/pages"
         :publishing-directory "~/public_html/jamestechnotes.com"
         :publishing-function org-html-publish-to-html
         :preparation-function jr/prepare-pages
         :htmlized-source t
         :with-toc nil
         :section-numbers nil
         :html-doctype "xhtml5"
         :html-html5-fancy t
         :html-head-include-default-style nil
         :html-head-include-scripts nil
         :html-head ,jr/html-head
         :html-preamble jr/prepare-pages

         :auto-sitemap t
         :sitemap-filename "blog.org"
         :sitemap-title "Blog articles"
         :sitemap-sort-files anti-chronologically

         :makeindex t)))

The interesting thing to note here is the use of quasiquote `, which allows use of unquote , for :html-head. :html-head requires a string, not a variable containing a string. The unquote and quasiquote allows this to function.

Footnotes:

1

Well, at least that my excuse. ;)

2

All of the code I write (that isn't owned by others) lives in my git repositories, including source for my websites.

3

Git handles binary artifacts reasonably well. I just think such shouldn't be done in general.

Date: 2020-03-15 Sun 00:00

Author: James Richardson

Created: 2020-03-15 Sun 21:57

Validate