使用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目录:最终用Org Mode生成的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可以免费建立私有仓库,或者向Github付费。

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可以远程编辑文件)

等等。