22 Jan 2024

Org Static Blog: A Simple Static Site Generator

I picked up most of my computer skills from reading posts online, and it got me thinking about having my own blog where I can share what I've learned. But I've always been a bit hesitant about learning all the complicated stuff in web development. Recently, I came across a third-party emacs package called org-static-blog while searching for the built-in functionality org-publish. After browsing a few blog sites created with this package, I was impressed by its simplicity and the ability to blog using org files. It became clear that this package is exactly what I need at this stage.

After a few minites of survey, I observed the following facts.

  1. The source code is simple enough. There is only a single elisp script with less than 1000 lines. The code is well-organized, with customization variables appearing first, followed by commands. It doesn't involve any complex or cryptic syntax.
  2. The developer maintains this project over 8 years and he continues to use it in his blog site.
  3. There are a few appealing blog sites are created by org static blog, e.g., Bastian, Jose Antonio Ortega Ruiz, Justin Abrahms, Musa Al-hassy

Finally, I decide to give it a try. I hope it can give me a way to share my thoughts on the internet.

Go through the manual

The README contains a brief introduction to the usage and a minimal working example.

First, let us review the introduction.

Static blog generators are a dime a dozen. This is one more, which focuses on being simple. All files are simple org-mode files in a directory. The only requirement is that every org file must have a #+TITLE and a #+DATE, and optionally, #+FILETAGS, #+DESCRIPTION and #+IMAGE.

After collecting the appropriate org files, publishing them by org static blog needs only two steps: 1) customizing org static blog parameters; 2) calling org-static-blog-publish to publish all posts, or calling org-static-blog-publish-filename to publish only a particular post.

The blog site generated by org static blog consists of six parts.

  1. Posts. Every org file in org-static-blog-posts-directory is one blog post. Each blog post is rendered as its own HTML page.
  2. Index. The index page contains the last few blog posts on a single page. The number of entries on the index page can be customized using org-static-blog-index-length.
  3. Tags. Each blog post can be tagged, and each tag links to a page that lists all other posts of the same tag. This feature is only enabled when org-static-blog-enable-tags is t.
  4. Archives. This page lists the publishing dates and headlines of every blog post.
  5. RSS Feeds. This is a machine-readable XML file that contains every blog post. It is not meant to be consumed by humans. Instead RSS readers can use the RSS feed to aggregate entries from multiple blogs.
  6. Drafts. They are rendered like regular blog posts, but are not included in the index, the archive, or the RSS feed.

Every HTML page in org static blog can be customized in the following aspects.

  1. org-static-blog-page-header. This is inserted into the <head> of every page. Use this to include custom CSS and JavaScript for your blog.
  2. org-static-blog-page-preamble. This is inserted just before the content of every page. This is a good place to put the header or menus for your blog.
  3. org-static-blog-page-postamble. This is inserted after the content of every generated page: after any blog post page, after the index page, the tag pages and the archive. This is where you can include copyright notices.
  4. org-static-blog-post-preamble and org-static-blog-post-postamble. The returned values are prepended and appended to every blog post. If you want to change the formatting of dates, titles, or the tag list, overwrite these functions. In particular the content of org-static-blog-post-comments is inserted at the end of each blog post. Use this to add a comment box.

Other features:

  1. Optionally show a preview of the post (instead of the full post) on the index page setting org-static-blog-use-preview to t. The region of the post used as a preview is, by default, its first paragraph, but can be fine-tuned using org-static-blog-preview-start and org-static-blog-preview-end.
  2. Activate a few convenience key bindings by

    (add-to-list 'auto-mode-alist
                 (cons (concat
                        org-static-blog-posts-directory ".*\\.org\\'")
                       'org-static-blog-mode))
    

    These key bindings are:

    1. C-c C-f / C-c C-b to open next/previous post.
    2. C-c C-p to open the matching published HTML file of a post.
    3. C-c C-n to create a new blog post.

Minimal Configuration

Following the example in its manual, I try to build a blog site at ./org-blog/ with a folder ./org-blog/org/ containing several org files.

The first step is, of course, installing the package.

(package-install 'org-static-blog)

The next step is setting up variables. I want to do the following things.

  1. Set the site title to Hello, Org Static Blog
  2. Set the url of the site to file:///home/dou/Documents/2024-01-22-TryOrgStaticBlog/org-blog/
  3. Set the directory that holds all html files to ./org-blog/
  4. Set the directory that holds all org files to be published to ./org-blog/org/
  5. Set the directory that holds all drafts to ./org-blog/drafts/
  6. Set the page header as the content of the file ./org-blog/static/header.html
  7. Set the page preamble as the content of the file ./org-blog/static/preamble.html
  8. Set the page postamble as the content of the file ./org-blog/static/postamble.html
  9. Enable tags
  10. Enable preview
(require 'org-static-blog)

(setq dms/org-static-blog-root-dir "/home/dou/Documents/2024-01-22-TryOrgStaticBlog/org-blog/")

(setq org-static-blog-publish-title "Hello, Org Static Blog")
(setq org-static-blog-publish-url (format "file://%s" dms/org-static-blog-root-dir))
(setq org-static-blog-publish-directory (format "%s" dms/org-static-blog-root-dir))
(setq org-static-blog-posts-directory (format "%sorg" dms/org-static-blog-root-dir))
(setq org-static-blog-drafts-directory (format "%sdrafts" dms/org-static-blog-root-dir))
(setq org-static-blog-page-header (with-temp-buffer
  (insert-file-contents (format "%sstatic/header.html" dms/org-static-blog-root-dir))
  (buffer-string)))
(setq org-static-blog-page-preamble (with-temp-buffer
  (insert-file-contents (format "%sstatic/preamble.html" dms/org-static-blog-root-dir))
  (buffer-string)))
(setq org-static-blog-page-postamble (with-temp-buffer
  (insert-file-contents (format "%sstatic/postamble.html" dms/org-static-blog-root-dir))
  (buffer-string)))
(setq org-static-blog-enable-tags t)
(setq org-static-blog-use-preview t)

Contents of header.html, preamble.html and postamble.html are given as follows. Assets static/style.css and static/favicon.ico are downloaded from Bastian.

<meta name="author" content="Dou Meishi">
<meta name="referrer" content="no-referrer">
<link href= "static/style.css" rel="stylesheet" type="text/css" />
<link href="static/favicon.ico" rel="icon" />
<div class="header">
  Hello, Org Static Blog
</div>
Created by <a href="https://github.com/bastibe/org-static-blog/">Org Static Blog</a>

Currently, the project layout looks like

org-blog/
├── drafts
├── org
│   └── notes.org
└── static
    ├── favicon.ico
    ├── header.html
    ├── postamble.html
    ├── preamble.html
    └── style.css

3 directories, 6 files

Finally, call org-static-blog-publish to generate the site. At this time, the project layout becomes

org-blog/
├── archive.html
├── drafts
├── index.html
├── notes.html
├── org
│   └── notes.org
├── rss.xml
├── static
│   ├── favicon.ico
│   ├── header.html
│   ├── postamble.html
│   ├── preamble.html
│   └── style.css
└── tags.html

3 directories, 11 files

Question

  1. Does it scans all org files in org-static-blog-posts-directory recursively or not?

    Yes. For example, a folder named 2024/ in it will be published to the folder 2024/ in org-static-blog-publish-directory.

  2. Does it support following symbolic links when checking org files?

    Yes but no. I test the following based on the project layout in the Minimal Configuration section. I renamed the ordinary file notes.org inside org-blog/org/ to org-blog/../ but leave a symbolic link. So the project layout becomes.

    org-blog/
    ├── drafts
    ├── org
    │   └── notes.org -> /home/dou/Documents/2024-01-22-TryOrgStaticBlog/notes.org
    └── static
        ├── favicon.ico
        ├── header.html
        ├── postamble.html
        ├── preamble.html
        └── style.css
    
    3 directories, 6 files
    

    However, calling org-static-blog-pulish exports org-blog/org/notes.org to ~/Documents/notes.html.

    This issue comes function org-static-blog-get-post-public-path. Running either

    (org-static-blog-get-post-public-path "~/Documents/2024-01-22-TryOrgStaticBlog/notes.org")
    

    or

    (org-static-blog-get-post-public-path "~/Documents/2024-01-22-TryOrgStaticBlog/org-blog/org/notes.org")
    

    gives the result ../../notes.html.

  3. What will happen if org-static-blog-publish-directory is the same as org-static-blog-posts-directory? In this case, what will happen if org-static-blog-drafts-directory is a subfolder?

    It behaves like exporting all org files to the same directory, and posts inside the drafts directory will not be included in the index.

Build My Blog Site

It is also not hard to extend the minimal configuration to build a real blog site. But before tweaking these scripts and assets, I need, of course, get a public URL for hosting the site. Fortunately, GitHub Pages allows hosting directly from a GitHub repository. What I need to do is create a publich repo, say org-blog, and go to Settings -> Pages and set the deploy target to https://dou-meishi.github.io/blog/. Now I can replace the previous local URL to this one in the script and assets.

Besides changing the publish URL, there are a few other things to make the site a slightly more visual appealing.

  1. Add a top bar at each page, showing links to the homepage and the archive page.

    This can be easily done by modifying the preamble.html

    <div class="header">
      <div class="sitelinks">
        <a href="https://dou-meishi.github.io/org-blog/index.html">Home</a>
        |
        <a href="https://dou-meishi.github.io/org-blog/archive.html">All Posts</a>
      </div>
    </div>
    
  2. Render math formulae. This can also be achieved by adding appropriate javascript and stylesheets. Currently, I use KaTeX, which seems to be faster than MathJax.

    <!-- Math Support by KaTeX -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV" crossorigin="anonymous">
    <!-- The loading of KaTeX is deferred to speed up page rendering -->
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" integrity="sha384-XjKyOOlGwcjNTAIQHIpgOno0Hl1YQqzUOEleOLALmuqehneUG+vnGctmUb0ZY0l8" crossorigin="anonymous"></script>
    <!-- To automatically render math in text elements, include the auto-render extension: -->
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
    
  3. Customize the top of the index page. This is done by setting the org-static-blog-index-front-matter variable in the lisp script.

    (setq org-static-blog-index-front-matter
          "<h1 class=title> Recent Posts </h1>")
    
  4. Change the default ellipsis (...) used in preview to ...

    (setq org-static-blog-preview-ellipsis "...")
    
  5. Use a different CSS stylesheet. I have been using this stylesheet for three years and always appreciating its clean design. It is designed for HTML exported from org files by org-publish. To adapt it to files exported by org-static-blog, I add several additional rules in a patched CSS.

    <link href= "https://gongzhitaao.org/orgcss/org.css" rel="stylesheet" type="text/css" />
    <link href= "https://dou-meishi.github.io/org-blog/static/dou-org-blog.css" rel="stylesheet" type="text/css" />
    
  6. Sync posts from my document folder. As I mentioned in the previous post, my notes resides in different event directories in the document folder, and, of course, I do not want to share the whole ~/Documents/ folder. So I list all files I want to share in ~/.unison/syncpost.prf, which looks like

    source default.prf
    
    root = /home/dou/Documents
    root = /home/dou/Documents/2024-01-24-MyOrgBlog/
    
    nodeletion = /home/dou/Documents
    
    path = 2023-09-19-Compactness/notes.org
    path = 2023-10-23-BanachSpaceExample/notes.org
    path = 2024-01-07-ReviewUnison/basics.org
    path = 2024-01-07-ReviewUnison/advanced.org
    path = 2024-01-11-CodeBlockinLaTeX/notes.org
    path = 2024-01-11-CodeBlockinLaTeX/simple-code.png
    path = 2024-01-11-CodeBlockinLaTeX/tcolorbox-listings.png
    path = 2024-01-14-TryOrgPublish/notes.org
    path = 2024-01-22-TryOrgStaticBlog/notes.org
    

    Whenever I want to post something, I just check this file, run unison-gui syncpost, and execute all lisp script in the build-blog.el.

The lisp script and static assets are all included in my git repo for this blog.

External Links   refs

Below are other users' configuration on org static blog.

  1. a simple setup: simplicity - programming (and other) musings
  2. another simple setup Justin's emacs configuration - Writting - Blogging
  3. an extensive setup: AlBasmala: Blogging with Emacs & Org-mode (•̀ᴗ•́)و
Tags: emacs
Created by Org Static Blog