Org mode is considered to be one of the most remarkable modes in Emacs. Basically, it is a lightweight markup language, which is similar as Markdown and Textile, but is far more powerful. The .org files can be exported to many other formats, such as .tex and .html. We will take advantage of the publishing system provided by org-mode to manage the pages of the website.
Here I assume you have already had some basic knowledge of org-mode, Git and Jekyll, and already installed Git and Jekyll on your system. This post is based on these two articles: Using org to Blog with Jekyll and Blogging with Emacs org-mode and Github Pages.
The configuration of the publishing system in org-mode is done by setting the
value of org-publish-project-alist
variable.
(setq org-publish-project-alist '(("jekyll-cute-jumper-github-io" ;; settings for cute-jumper.github.io :base-directory "~/Documents/org-publish/cute-jumper.github.io/org" :base-extension "org" :publishing-directory "~/Documents/org-publish/cute-jumper.github.io/deploy" :recursive t :publishing-function org-html-publish-to-html :with-toc nil :headline-levels 4 :auto-preamble nil :auto-sitemap nil :html-extension "html" :body-only t)))
Note that the :body-only t
is essential because here we only need to
generate the content between <body>
and </body>
, and let Jekyll take care
of the rest.
For convenience, I start my configuration from Jekyll-Bootstrap.
cd ~/Documents/org-publish/cute-jumper.github.io/ # Clone into publishing directory git clone https://github.com/plusjade/jekyll-bootstrap.git deploy cd deploy # Setup upstream to your remote repo in GitHub git remote set-url origin git@github.com: cute-jumper/cute-jumper.github.io.git
Then, edit the _config.yml
file under the root directory. Check the
documentation for details.
Here is my directory structure:
/ ├── deploy │ ├── ...other files already in repo │ ├── about.html │ ├── _drafts │ │ └── .html files │ ├── index.html │ ├── _posts │ │ └── .html files │ └── timeline.html └── org ├── about.org ├── _drafts │ └── .org files ├── index.org ├── _posts │ └── .org files └── timeline.org
Note that there is a mapping from the .org
files in org/
to .html
files
in deploy/
. We only need to focus on the org/
directory and write our
posts there, and use org-publish
to convert these .org
files to .html
files and run jekyll serve
under deploy/
, then we can see the results in
http://localhost:4000. All the other configurations, such as changing themes
and setting up comments should be done in the deploy/
directory, and that's
what we use Jekyll for.
In order to make the exported .html
files correctly be processed by Jekyll,
we need to make some changes to our .org
files.
Front-matter. Usually, I put the following snippet at the beginning of every
post. This is called front-matter, which will be handled by Jekyll and is
required if we want the exported .html
understood by Jekyll.
#+BEGIN_EXPORT html --- layout: post title: Org-mode to GitHub pages with Jekyll excerpt: Introduce how to use Emacs's Org-mode with Jekyll to generate GitHub Pages categories: - Emacs tags: - [Emacs, org-mode, GitHub, Jekyll] --- #+END_EXPORT
Name Convention. By the Jekyll convention, we need to puts our posts(org
files) under _posts/
, and the file name should be the format
yyyy-mm-dd-name.org
. The following code, from Blogging with Emacs
Org-mode and Jekyll, can simplify our work. I made some slight
modifications to the original code to fit my own needs.
(defvar jekyll-directory (expand-file-name "~/Documents/org-publish/cute-jumper.github.io/org/") "Path to Jekyll blog.") (defvar jekyll-drafts-dir "_drafts/" "Relative path to drafts directory.") (defvar jekyll-posts-dir "_posts/" "Relative path to posts directory.") (defvar jekyll-post-ext ".org" "File extension of Jekyll posts.") (defvar jekyll-post-template "BEGIN_EXPORT\n---\nlayout: post\ntitle: %s\nexcerpt: \ncategories:\n - \ntags:\n - \n---\n#+END_EXPORT\n\n* " "Default template for Jekyll posts. %s will be replace by the post title.") (defun jekyll-make-slug (s) "Turn a string into a slug." (replace-regexp-in-string " " "-" (downcase (replace-regexp-in-string "[^A-Za-z0-9 ]" "" s)))) (defun jekyll-yaml-escape (s) "Escape a string for YAML." (if (or (string-match ":" s) (string-match "\"" s)) (concat "\"" (replace-regexp-in-string "\"" "\\\\\"" s) "\"") s)) (defun jekyll-draft-post (title) "Create a new Jekyll blog post." (interactive "sPost Title: ") (let ((draft-file (concat jekyll-directory jekyll-drafts-dir (jekyll-make-slug title) jekyll-post-ext))) (if (file-exists-p draft-file) (find-file draft-file) (find-file draft-file) (insert (format jekyll-post-template (jekyll-yaml-escape title)))))) (defun jekyll-publish-post () "Move a draft post to the posts directory, and rename it so that it contains the date." (interactive) (cond ((not (equal (file-name-directory (buffer-file-name (current-buffer))) (concat jekyll-directory jekyll-drafts-dir))) (message "This is not a draft post.")) ((buffer-modified-p) (message "Can't publish post; buffer has modifications.")) (t (let ((filename (concat jekyll-directory jekyll-posts-dir (format-time-string "%Y-%m-%d-") (file-name-nondirectory (buffer-file-name (current-buffer))))) (old-point (point))) (rename-file (buffer-file-name (current-buffer)) filename) (kill-buffer nil) (find-file filename) (set-window-point (selected-window) old-point)))))
Link Syntax. Jekyll supports many kinds of links. For example, by default
this post, named 2013-10-06-orgmode-to-github-pages-with-jekyll.org
, will
finally have a link
/emacs/2013/10/06/orgmode-to-github-pages-with-jekyll/
, which means the
original org-mode's link syntax [[file:/path/to/file][description]]
will not work because the
final URL will change unless you modify the settings to prohibit Jekyll
from doing such a path transformation. Thanks to the high customizability
of org-mode, there is an elegant way to solve this. I stole from this
thread in stackoverflow.
(defun org-jekyll-post-link-follow (path) (org-open-file-with-emacs path)) (defun org-jekyll-post-link-export (path desc format) (cond ((eq format 'html) (format "<a href=\"{%% post_url %s %%}\">%s</a>" path desc)))) (org-add-link-type "jekyll-post" 'org-jekyll-post-link-follow 'org-jekyll-post-link-export)
When we want to link to a post, we now use the syntax: [[jekyll-post:filename][description]]
instead.
In order to make use of Jekyll's template system, we sometimes need to embed
the template code into our org files. Thus, we may need
#+BEGIN_EXPORT html...#+END_EXPORT
structure very often.
Following is the current version of my index.org
, most of which is copied
from Using org to Blog with Jekyll.
#+BEGIN_EXPORT html --- layout: page title: cd /home/cute-jumper/ tagline: <br/>$ ls -t blogs.happy.programming/ --- #+END_EXPORT #+OPTIONS: num:nil Hello, welcome to the homepage of cute-jumper. * Posts #+BEGIN_EXPORT html <ul class="posts"> {% for post in site.posts %} <li><span>{{ post.date | date_to_string }}</span> » <a href="{{ BASE_PATH }}{{ post.url }}">{{ post.title }}</a></li> <em>{{ post.excerpt}}</em> {% endfor %} </ul> #+END_EXPORT * Timeline #+BEGIN_EXPORT html <ul class="timeline"> <li><a href="timeline.html">Timeline</a></li> </ul> #+END_EXPORT * About #+BEGIN_EXPORT html <ul class="about"> <li><a href="about.html">About</a></li> </ul> #+END_EXPORT
To be honest, it is kind of wired to embed so many HTML in org file, but it seems inevitable if we want to use the template system.
Now let us check the whole workflow to publish our web pages after we have done all the settings above.
jekyll-draft-post
to write a draft. (You may want to add
deploy/_drafts
directory into your .gitignore file…That's what I did.)jekyll-publish-post
, which will prepend a
timestamp in front of the original file name and move the file from
_drafts/
to _posts/
.org-publish
, and choose jekyll-cute-jumper-github-io
.deploy/
directory, and run jekyll serve
to see the results locally.It's so easy!
I'm thinking about making my own themes because the official themes in http://themes.jekyllbootstrap.com/ don't fit my needs very well. Maybe someday I will also remove Jekyll-Bootstrap and build my own things from scratch.
Currently this site is just a demo, and much more work needs to be done. I would try to improve this site when I have time.^_^