02 November 2015

如果你已经熟练使用 Emacs Org-Mode 并且也对 github 有所了解,那么在 Github Pages 上 搭建一个博客将是十分简单的事情。你 不需要 装诸如 Ruby、Jekyll 之类的软件,就可 以用 Org-Mode 写博客然后发表到 Gitub Pages 上。

在 GithubPages 上创建博客

你只需照着 https://pages.github.com/ 几步就可以创建一个自己的博客。请注意,这其 实就已经是一个“能工作”的博客了!“能工作”的意思是你往里添加的html页面都可见了。

当然,作为一个博客,仅仅能显示我们自己添加的页面还差了点。至少每次发表一个文章 之后,它应当能自动显示在博客首页而不需我们自己编辑 index.el 。这就需要用到 Jekyll 了。

配置 Jekyll

导入 Jekyll 框架

虽然我们需要用到 Jekyll,但我们 不用 在本机安装相关软件。相关处理都是在 Github Pages 服务器端运行的。我们要做的只是导入 Jekyll 框架并按照 Jekyll 的规则 命名以及编辑文件(准确的说,我们用的是 Jekyll-Bootstrap)。如下克隆一个 Jekyll-Bootstrap 的模板然后把它 push 到自己的博客上(以下以我的博客为例)。

git clone --depth 1 https://github.com/plusjade/jekyll-bootstrap.git blog
git remote set-url origin https://lgfang@github.com/lgfang/lgfang.github.io.git
git push -f

刷新博客页面应当可以看到博客内容已经变了。

更改配置

(详情参见博客源码)

  • 修改 _config.yml 填入自己的 google tracking id 和 disqus 账号等个人信息。

    前者是让google帮你统计页面访问情况的,后者是一个第三方评论系统。相信用法后面 org-mode 配置时会提到。

  • 更改 README.md 中的描述
  • 修改 index.md 成显示最近五篇博客的摘要。
  • 重定向 404 页面到寻找丢失儿童的页面。
  • 把主题改成 twitter 的。
    cd _layouts
    sed -i 's/bootstrap-3/twitter/g' *html
    

    NOTE: 可用的主题在 _includes/themes 下。

  • 添加更多的 css 和 js

    把 Org-Mode publish html 用到的 css 和 javascript 也加入到博客中。因为后面 将要提到的“笔记”的缘故,我把这些文件放到 mynotes/cssjs 目录下,并且编 辑_includes/themes/twitter/default.html 加入对 css 的引用:

    $ git diff
    diff --git a/_includes/themes/twitter/default.html b/_includes/themes/twitter/default.html
    index 3747764..01ba331 100644
    --- a/_includes/themes/twitter/default.html
    +++ b/_includes/themes/twitter/default.html
    @@ -17,6 +17,7 @@
         <!-- Le styles -->
         <link href="/bootstrap/css/bootstrap.2.2.2.min.css" rel="stylesheet">
         <link href="/css/style.css?body=1" rel="stylesheet" type="text/css" media="all">
    +    <link href="/mynotes/cssjs/tango.css" rel="stylesheet" type="text/css" media="all">
    
         <!-- Le fav and touch icons -->
       <!-- Update these with your own images
    

我的模板

至此 Jekyll 端的配置就基本完成了。为了方便起见我创建了一个名为“blog-template” 的 branch ;所有博客设置的改动都在这 branch 上。你图省事的话也可以直接克隆我现 成的模板。

愿意的话现在就可以用 markdown 的语法来写博客并按 yyyy-mm-dd-blog-title.md 的 格式放到 _posts 目录下就可以发表了。但是,作为资深 Org-Mode 用户:) 这显然不是 我的目的。用 Org-Mode 写博客不仅仅是免去了学习 markdown 的语法,更重要的是可 以利用 Org-Mode 的各种强大功能。

经验教训

  1. 一定要验证自己的每一步 Jekyll 改动都对,因为 debug 不方便。
  2. 检查自己注册时的邮箱,错误信息会发到此处。

我在此次配置过程犯了一个小错误,结果花了几乎一晚上来 debug :我试图在 categories.html 里包含一个我手工创建的 html 文件,但是文件名写错了。结果是 push 了新的博文,博客也不更新。应该是 Jekyll 处理失败就退出,导致整个博客始终 停留在错误被引入之前的状态。

配置 Org-Mode for blog

org-mode 需要做如下配置:

  • 创建两个工程(project)。
    • 一个用于发布博文,即把 Org-Mode 的原文转成 html
      1. 结果文件必须放到 _posts 下。
      2. 只导出 body, 因为我们需要 Jekyll 来帮我们生成 head 。
      3. 导出的 link 要用绝对路径,这主要为了链接图片方便。Jekyll 处理之后,博客 的 url 里会带上日期;而我本地的博文和图片等附件没有按日期创建目录(又烦 又不方便将来查看);所以最简单的办法就是把图片全放到固定的目录,然后在博 文里用绝对路径指向它们。
    • 一个用于发布附件(图片之类的)

      如前所述,图片不能直接放到 _posts 下。创建一个和 _posts 平级的目录 images, 把附件都放到这。

    (setq
          ;; nil as default to avoid leaking sensitive info unintentionally,
          ;; overwritten in per-project settings bellow
          org-html-postamble nil
          org-publish-project-alist
          '(
            ("blog-attachments"
             :base-directory "~/myblog"
             :base-extension "png\\|jpg"
             :recursive t
             :publishing-directory "~/websites/blog/images"
             :publishing-function org-publish-attachment)
    
            ("blog"
             :base-directory "~/myblog"
             :base-extension "org"
             :recursive t
             :publishing-directory "~/websites/blog/_posts"
             :publishing-function org-html-publish-to-html
             :auto-sitemap nil
             :body-only t ;; Only export section between <body> </body>
             :html-link-use-abs-url t
             ))
    
          org-export-allow-bind-keywords t  ; I like "#+BIND:var value"
    )
    
  • 创建 yasnippet 模板

    每篇博文的开头都要加上所谓的 front matter 才能够被 Jekyll 识别处理。老老实实 每次手工输入绝对不是 emacs 老鸟干的事。我定义了一个名为 new-blg 的 yasnippet 模板(NOTE: 为了和 yasnippet 自带的 "blog" 模板区分,我的模板名省略 了字母"o")。每次输入它就可以自动展开成所需的 front matter。具体代码如下:

    # -*- mode: snippet -*-
    # name: new-blg
    # --
    # -*-mode:org;coding:utf-8-*-
    # Created:  `(format "%s %s" (user-full-name) (format-time-string "%m/%d/%Y"))`
    # Modified`(format ": %s %s" (user-full-name) (format-time-string "%m/%d/%Y %H:%M>"))`
    
    #+OPTIONS: toc:nil num:nil
    #+BIND: org-html-link-home "`(replace-regexp-in-string "/Users/lgfang/myblog" "http://lgfang.github.io/images" default-directory t t)`"
    
    #+TITLE: ${1:title}
    
    #+BEGIN_HTML
    ---
    layout: post
    title: $1
    tagline:
    categories: ${2:computer}
    tags: [${3:emacs}]
    ---
    #+END_HTML
    
    $0
    

配置 Org-Mode for Notes

我主页上除了博文外,还有一些经年积累下来的笔记和演讲稿(什么该算做博文,什么该 算做笔记?我也很迷糊,谁有建议请留言,谢!)。对于这些资料,Org-Mode publish 出 来的效果更适合我的需求:可以做为幻灯片播放,可以快速跳转。因此,为它们定义了两 个和之前设置略有不同的 project。因为这些页面是原封不动发布到网站上,所以google analytic 和 disqus 的代码我们必须自己通过设置 postamble 来加上(请注意我把个人 信息存在变量中)。

(setq
       org-publish-project-alist
       '(
         ;; ...
        ("notes-attachments"
         :base-directory "~/mynotes"
         :base-extension "css\\|js\\|png\\|jpg\\|patch"
         :recursive t
         :publishing-directory "~/websites/blog/mynotes"
         :publishing-function org-publish-attachment)

        ("notes"
         :base-directory "~/mynotes"
         :base-extension "org"
         :recursive t
         :publishing-directory "~/websites/blog/mynotes"
         :publishing-function org-html-publish-to-html
         :exclude "\\(todo\\|confidential\\)"
         :html-postamble get-public-postamble
         :auto-sitemap t
         :sitemap-filename "notes_list.org"
         :sitemap-title "List of lgfang's notes"
         :sitemap-style tree
         :sitemap-sort-files anti-chronologically
         :sitemap-sort-folders last
         :sitemap-file-entry-format "%t")
        ;; ...                             
        ))

(defun get-public-postamble (plist)
  (let ((title (plist-get plist :title))
        (creator (plist-get plist :creator))
        (time (format-time-string org-html-metadata-timestamp-format)))
    (format
     "
     <br/>
     <p>
     <span class=\"date\">Created: %s</span> by <span class=\"creator\">%s</span>
     </p>

     <!-- DISQUS  -->
     <div id=\"disqus_thread\"></div>
     <script type=\"text/javascript\">
         /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
         var disqus_shortname = '%s'; // required: replace example with your forum shortname

         /* * * DON'T EDIT BELOW THIS LINE * * */
         (function() {
             var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
             dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
             (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
         })();
     </script>
     <noscript>Please enable JavaScript to view the <a href=\"http://disqus.com/?ref_noscript\">comments powered by Disqus.</a></noscript>
     <a href=\"http://disqus.com\" class=\"dsq-brlink\">comments powered by <span class=\"logo-disqus\">Disqus</span></a>

     <!-- google analytic -->
     <script>
       (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
       (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
       m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
       })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

       ga('create', '%s', 'auto');
       ga('send', 'pageview');

     </script>
     "
     time creator
     ;; The following variables are defined in private-info.el
     disqus_shortname google_analytic_track_id_blog)))
# -*- mode: snippet -*-
# name: new-notes
# --
# -*-mode:org;coding:utf-8-*-
# Created:  `(format "%s %s" (user-full-name) (format-time-string "%m/%d/%Y"))`
# Modified`(format ": %s %s" (user-full-name) (format-time-string "%m/%d/%Y %H:%M>"))`
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="../cssjs/lgfang.css" />
#+INFOJS_OPT: toc:t tdepth:2 ltoc:nil view:showall path:../cssjs/org-info.js up:../index.html home:../../index.html
#+OPTIONS: num:t

#+TITLE: ${1:title}

* ${2:header1}
$0


blog comments powered by Disqus