使用 Org-Mode 生成博客

Table of Contents

如今市面上博客程序繁多,各有优缺;在线笔记也是五花八门,都难以满足我的日常需求(尤其是对文章内代码编辑的方便)。Org Mode 自带了生成整站的本事,本站正是用此生成的一堆 HTML 文件,我是考虑到:

希望能帮助喜欢 Org Mode 的朋友。系统完整的 demo 代码已放到 GitHub:https://github.com/1u4nx/orgmode-blog-demo

1. 目录布局

要像写代码一样有一个清晰整洁的目录结构:

├── html/
├── Makefile
├── Makefile.el
├── src/
├── static/
└── templates/
  • html 目录:最终生成的 HTML 文件全部存于此处,如果需要在 Web 服务器上部署博客,只需把此目录上传即可;
  • Makefile 和 Makefile.el:Makefile.el 包含了生成 HTML 的代码及配置,为了方便用 make 命令,所以又再多出一个 Makefile;
  • src 目录:所有用 Org Mode 写的笔记全部都放在这里;
  • static 目录:博客会用到自定义的 CSS 样式,也许你还会用到 logo、JS,都统一放这里;
  • templates 目录:注意看我博客,每页都有相同的顶部和底部,其实就是在生成每个 HTML 时自动将顶部和底部插入相关代码,我把顶部和底部代码独立成 HTML 文件放于此处。

2. Makefile.el

整套系统并不复杂,核心点就是 Makefile.el 中几段代码。

Org Mode 自带了一个叫 ox-publish 的发布框架,可以用它生成一个静态网站,只需要配置 org-publish-project-alist 变量即可,更加详细的流程可以参考官方文档:http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html

完成源码+注释如下:

(require 'package)

;;; 后面需要加载三方库,所以先初始化包管理器
;; (package-initialize)
;; (setq package-enable-at-startup nil)

;; 如果需要高亮代码
;; (require 'htmlize)

(require 'org)
(require 'ox-html)
(require 'ox-publish)

;; HTML模板目录
(defvar *site-template-directory* "templates")

(defun read-html-template (template-file)
  (with-temp-buffer
    (insert-file-contents (concat *site-template-directory* "/" template-file))
    (buffer-string)))

(let ((org-publish-project-alist
       '(;; 配置 images 目录
         ("images"
          :base-directory "src"
          ;; 生成时要拷贝这些扩展名的文件
          :base-extension "jpg\\|png\\|c\\|gif"
          ;; 生成的目录
          :publishing-directory "html"
          ;; 是否递归
          :recursive t
          :publishing-function org-publish-attachment)
         ;; static 是网站静态文件的目录
         ("static"
          :base-directory "static"
          :base-extension "jpg\\|png\\|c\\|gif\\|css\\|js"
          :publishing-directory "html/static"
          :recursive t
          :publishing-function org-publish-attachment)
         ;; src 是网站 org 文件目录
         ("wiki-src"
          :base-directory "src"
          :base-extension "org"
          :publishing-directory "html"
          :recursive t
          ;; org-html-publish-to-html会把 org 转成 HTML 文件
          :publishing-function org-html-publish-to-html
          :headline-levels 4)
         ;; 这个 rss.xml 目前是手动维护内容
         ("rss.xml"
          :base-directory "src"
          :base-extension "xml"
          :publishing-directory "html"
          :publishing-function org-publish-attachment)
         ("wiki-project" :components ("wiki-src" "static" "rss.xml" "images"))))
      ;;; 设置 CSS 样式
      (org-html-head-extra "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/default.css\" />")
      ;;; 取消默认的 CSS
      (org-html-head-include-default-style nil)
      ;;; 取消默认的 Javascript 代码
      (org-html-head-include-scripts nil)
      ;;; XXX 用 org-html-head 可以设置 <head> 部分
      (org-html-preamble (read-html-template "preamble.html"))
      (org-html-postamble (read-html-template "postamble.html")))

  ;;; 设置 Mathjax 库的路径
  (add-to-list 'org-html-mathjax-options '(path "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"))

  ;;; 开始导出
  (org-publish-project "wiki-project"))

为了方便,这些再写一个Makefile:

all:
        # 为了避免编码问题,先设置好编码
        LANG=zh_EN.UTF-8 emacs --script Makefile.el
clean:
        rm -rf html/*
        rm -rf ~/.org-timestamps/

3. 扩展功能

3.1. 站内搜索

可以考虑使用 Google 的站内搜索服务( https://cse.google.com/cse/all ),把代码贴到相关文件中,我是放在 index.org 中的,详细见 demo 代码中的 src/index.org。

3.2. 留言板

可用三方的留言服务,比如 Disqus、网易云跟贴等服务,然后把相关 JavaScript 代码贴到页面底部模板中(preamble.html)。

3.3. RSS

我是把博文和笔记混合在一起的,有时并不想把笔记出现在 RSS 中,所以 RSS 我是手动维护的 src/rss.xml。

4. 部署

4.1. 使用版本控制

通常可以把整套博客放到 GitHub 和 Bitbucket 中,它们都有免费的私有仓库。

4.2. 服务器部署

我只在服务器上安装了 Emacs 和 Nginx,安装 Emacs 是为了直接在服务器上生成 HTML。并从版本仓库中 clone 博客到某目录,再将 Web 服务器的目录指向 html 目录下。然后写了一个包含以下步骤的 update.sh 脚本:

进入项目目录
git pull 最新源文件
执行 make 命令

5. 我的发布流程

5.1. 本地预览

在博客项目目录中执行 make 命令,然后进入 html 目录,执行 Python 自带的 Web 服务:

$ python3 -m http.server

浏览器访问 127.0.0.1:8000。

5.2. 发布

我 commit 的粒度一般是一篇笔记提交一次,分章节的文章一口气无法写完的话,就是每完成一节就 commit 一次,然后在 commit 信息中写清楚。

每当准备更新线上博客时,本地执行:

$ ssh 服务器地址 update.sh

除此之外你也可以:

  • 本地生成好 HTML 上传到服务器;
  • 直接在服务器上写笔记(Emacs 可以远程编辑文件)

等等。