How a programmer publish static HTML blog in Emacs
I can publish my blog in five seconds, running only one Emacs command!
I give you a minimum solution at first, then I explain why and provide technical details.
Basic Emacs lisp knowledges are required.
Please note I'm NOT targeted at specific static site generator like Nikola or Jekyll.
Solution
Tested on Linux and OSX.
Requirements:
- Org-mode bundled with Emacs 24.x
- Nikola as the blog generator
- FTP client ncftp
- Latest org2nikola (v0.0.9+)
- Insert below code into ~/.emacs
(defun org2nikola-after-hook-setup (title slug)
(let ((url (concat "http://blog.yourdomain.net/posts/" slug ".html"))
(nikola-dir (file-truename "~/projs/nikola-root"))
(password "yourpassowrd")
(username "yourusername")
dir
file
lines
rlt
res
cmd)
(kill-new title)
(kill-new url)
(message "%s => kill-ring" url)
(setq rlt (shell-command-to-string (format "cd %s; nikola build" nikola-dir)))
(setq lines (split-string rlt "\n"))
(dolist (l lines)
(when (string-match "output\\(.*/\\)*\\([^/]*\\)$" l)
(setq dir (match-string 1 l))
(setq file (match-string 2 l))
(setq cmd (format "ncftpput -b -u %s -p %s ftp.yourdomain.net /blog%s %s/output%s%s"
username password dir nikola-dir dir file))
(message "cmd=%s" cmd)
(shell-command cmd)
))
))
(add-hook 'org2nikola-after-hook 'org2nikola-after-hook-setup)
You can write blog into org file, export and publicize it with command `M-x org2nikola-export-subtree`.
Why
I used Wordpress and Org2blog for a very long time. Then I turned to static blog generator because:
- Wordpress is slow to load web page
- I waste too much time to "manage" Wordpress (applying security patch, fighting with spam comment …)
- As a programmer, I need publish code snippets. Wordpress is BAD at rendering code.
Yes, only wordpress sucks. For Org2blog, I can only say good things. Actually, this article is inspired purely by Puneeth Chaganti's excellent work on org2blog.
Technical Details
Generating HTML
As I said, Nikola is my blog generator which converts my org file into HTML.
But I prefer using Org-mode to do the HTML rendering. Nikola only need handle remaining minor book keeping stuff (creating RSS feed, for example).
It's because I want to minimize the dependency. I may switch to other blog generator in the future. But my web site always has the same look and feel because the HTML is generated by Org-mode.
I learned this trick from Org2blog.
If we only use org-mode to create HTML, then extracting code snippet is really easy. All we need is to use regular expression to extract the content between HTML tag "<pre>".
Actually I don't even bother doing the extraction. I just replace all the "<pre>" tag with my own tag because I assume the HTML produced by org-mode is stable.
Here is the Emacs lisp code I borrowed from Org2blog:
(defun org2nikola-replace-pre (html)
"Replace pre blocks with sourcecode shortcode blocks.
shamelessly copied from org2blog/wp-replace-pre()"
(save-excursion
(let (pos code lang info params header code-start code-end html-attrs pre-class)
(with-temp-buffer
(insert html)
(goto-char (point-min))
(save-match-data
(while (re-search-forward "<pre\\(.*?\\)>" nil t 1)
;; When the codeblock is a src_block
(unless
(save-match-data
(setq pre-class (match-string-no-properties 1))
(string-match "example" pre-class))
;; Replace the <pre...> text
(setq lang (replace-regexp-in-string ".*src-\\([a-zA-Z0-9]+\\).*" "\\1" pre-class) )
(replace-match "")
(setq code-start (point))
;; Go to end of code and remove </pre>
(re-search-forward "</pre.*?>" nil t 1)
(replace-match "")
(setq code-end (point))
(setq code (buffer-substring-no-properties code-start code-end))
;; Delete the code
(delete-region code-start code-end)
;; Stripping out all the code highlighting done by htmlize
(setq code (replace-regexp-in-string "<.*?>" "" code))
;; class linenums will add stripes which will destory the 3rd party skins
(insert (concat "\n<pre class=\"prettyprint lang-"
(org2nikola-fix-unsupported-language lang)
"\">\n"
code
"</pre>\n"))
)))
;; Get the new html!
(setq html (buffer-substring-no-properties (point-min) (point-max))))
))
html)
The code snippet inside "<pre>" tag could be rendered by 3rd party javascript library google-code-prettify or SyntaxHighlighter.
BTW, the last straw that push me away the Wordpress is its wrapper of SyntaxHighlighter. SyntaxHighlighter is a beautiful and user-friendly library. But the wrapper forces me to tweak the php code in terrible editor from Wordpress.
Create blog posts
Emacs creates HTML files and meta files. Things created by Emacs will be copied into Nikola's folder.
Nikola's duty is simple. Basically it only copy my stuff from its input folder to its output folder when I trigger "nikola build" command.
The "nikola build" command will also dump the list of files to be uploaded into stdout.
The build message dumped:
Scanning posts....done! . render_archive:output/2014/index.html . render_sources:output/posts/jump-to-the-positions-before-and-after-m-x-imenu.wp . render_posts:cache/posts/jump-to-the-positions-before-and-after-m-x-imenu.html . render_indexes:output/index.html . render_indexes:output/index-17.html . render_tags:output/categories/en.html . render_tags:output/categories/emacs.html . render_tags:output/assets/js/tag_cloud_data.json . render_tags:output/categories/emacs.xml . generate_rss:output/rss.xml . render_pages:output/posts/jump-to-the-positions-before-and-after-m-x-imenu.html . render_pages:output/posts/why-emacs-is-better-editor-part-two.html . render_tags:output/categories/en.xml
Only the files in sub-folder "output" need be uploaded.
Preprocess nikola output
It's all done by Emacs Lisp.
The final output contain lines like:
ncftpput -b -u yourname -p yourpassword ftp.yourdomain.net /blog/2014/ /home/yourname/nikola-root/output/2014/index.html
As you can see, each line is a ftp upload command we need execute in shell.
FTP upload
Ncftp is my choice of FTP client. It's solid and efficient.
Its command line tool "ncftpput" has a flag "-b". With the flag ncftpput will start a daemon at background and handles the ftp upload as a batch job submit. It means ftp connection will be reused and the user is not blocked by the upload operation. So the upload operation is extremely fast.
I use Emacs Lisp API `file-truename` to convert all relative path to absolute path to avoid any platform (OSX) issue.
BTW, you can `cat ~/.ncftp/spool/log` to check the FTP upload status.
Summary
The workflow is simple.
Emacs and third party JS libraries are responsible for the look and feel of my website.
Blog generator like Nikola is just a thin layer to relay the HTML files created by Emacs.
Ncftp will upload HTML files. It's the best FTP client which could be easily integrated into Emacs.