/ Octopress

搬家到 Octopress

身為一個Geek工程師,用 VIM 來寫部落格應該也是很合理的。所以,我就這麼樣拋棄我長久以來維護的 TextCube 跳槽到 Octopress 了。不過,眾多(?)TextCube 的用戶也不用擔心,基本上我還是會維護的,只是官方那種趨於牛步的更新速度,實在讓人覺得很無力啊!官方最後一次的更新,在 svn 上面竟然已經是四月的事情了(眼神死


所以我就理所當然入主 Octopress 了!

網路上已經有很多教學,我就不再野人獻曝,請大家參考底下文章:

我用 Octopress

簡而言之是一種Geek用純文字編輯的部落格系統。你需要有 git, ruby, markdown 等環境,對一般人來說可能不是那麼討喜。不過我拋棄 Windows 已經三年多了(除了 Canon RAW 要轉檔(眼神死),所以在 Ubuntu 底下有這些環境也是很合理的。

他是一個使用 Markdown 來撰寫文章的系統,透過 rake generate 來產生整個部落格所需要的檔案,然後在藉由 git 來將部落格給發佈到遠端,你可以使用 gitHub 或是 Heroku 這兩個服務商。

何不 TextCube

我也用了很久,打從他 0.96 版的時候我就開使用,只是人次沒破百萬而已(笑)。他是一套韓國開發的開放原始碼部落格系統,當 Wordpress 在台灣還不是那麼紅的時候,這個系統也曾經有許多使用者在用,但是,為什麼使用者流失的這麼快?原因出在於大幅度改版,與舊版不相容等問題,導致一堆人離家出走。

我之所以一直使用的原因,其實我並不覺得他難用,對我來說 Wordpress 反而很難用,我兩種都用過好一陣子,就使用者親和力來說,當初 TextCube 我覺得是比 Wordpress 好得多的。

嗯,其實我的 Textcube 還活著,在這裡 http://tc.hinablue.me/,其實我一直在等官方把 TC2.0 釋出,可是已經快兩年過去了,完全沒有消息,雖然偶爾還是會跟官方的開發者聯繫(大多都是 debug 就是),只是,就這樣了吧,等到他真的發佈再說了。

TC to OC

說轉換無痛其實是騙人的(喂)。不過如果是 TC 1.6 以上的版本,要轉換其實並沒有太大的困難。Octopress 本身是使用 permalink 來當作網址,所以,在 TC 裡面就必須要有 slogan 的項目設定。一般來說,會使用數字當作是文章網址,除非你特別設定使用 slogan 當網址,否則預設都是數字。

數字也可以轉換到 Octopress 來,只是檔名就沒那麼好理解了。因為 Octopress 是以檔案來當作草稿,所以當你出現一堆 300.markdown, 301.markdown 的時候,鬼才知道裡面到底寫了什麼東西。所以不是不行,而是後續維護上可能會造成一些不必要得麻煩。

我有寫了簡單的兩隻程式幫我轉,資料庫是用 Idiorm 這個套件,你可以自行去 gitHub 下載。底下的範例是使用我的網址,請不要照抄,另外,如果你有使用我的 HC_Emoticons 外掛,也會一併轉換。

裡面會把原本部落格使用的圖片,轉去一個靜態網址 http://static.hinablue.me/blog,然後 HC_Emoticons 會轉去這個靜態網址 http://static.hinablue.me/blog/emoticons,這兩個網址請自行設定。

#!/usr/bin/php -q 
<?php

require_once "idiorm/idiorm.php";

ORM::configure('mysql:host=localhost;dbname=tcback');
ORM::configure('username', 'root');
ORM::configure('password', 'CalineILoveYou');
ORM::configure('driver_options', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

$regx = "/<a href=\"http:\/\/www\.flickr\.com\/photos\/(?P<userid>[^\/]+)\/(?P<photoid>.*)\" title=\"(?P<title>.*)\" target=\"_blank\"><img src=\"(?P<src>(.*)\.jpg)\" border=\"0\"><\/a>/i";

// 依照類別將文章匯出
$posts = ORM::for_table('tc_Entries')
    ->where_in('category', 1)
    ->find_many();

// 請把 hinablue 改成你的 flickr username
function flickr_replace($m) {
    return '[!['.str_replace('_','\_',$m[3]).']('.$m[4].')](http://www.flickr.com/photos/hinablue/'.$m[2].')';
}

exec('mkdir -p entry');
exec('rm -rf ./entry/*.markdown');

foreach($posts as $post) {

    // 底下的 categoires 是自訂的分類
    // 你可以寫一個,像是 [Life]
    // 或是多個,像是 [Life, Chats]
    $data = '---
    layout: post
    title: "'.str_replace('"','\"',$post->title).'"
    date: '.date("Y-m-d H:i", $post->created).'
    comments: true
    categories: []
    ---

    ';

    $entry = $post->content;

    $entry = preg_replace("/<br>/i","\n",$entry);
    $entry = preg_replace_callback($regx, "flickr_replace", $entry);
    $entry = str_replace("[##_ATTACH_PATH_##]", "http://static.hinablue.me/blog", $entry);
    $entry = str_replace("http://blog.hinablue.me/attach/1/", "http://static.hinablue.me/blog/", $entry);
    $entry = preg_replace("/\[##_1(?|L|R)\|([0-9]+)\.jpg\|([^#]+)\|_##\]/i", '<img src="http://static.hinablue.me/blog\1.jpg" \2 />', $entry);
    $entry = str_replace("/plugins/HC_Emoticons/", "http://static.hinablue.me/blog/", $entry);
    $entry = preg_replace("/\[##_HCEMO_([^_#]+)_##\]/i", '\1', $entry);
    $entry = preg_replace('/longdesc="([^"]+)"/i', '', $entry);
    $entry = str_replace('http://blog.hinablue.mehttp', 'http', $entry);
    $data .= $entry;

    // 這裡是使用 $post->slogan 來當作檔名
    // 如果你要用數字,則改為 $post->id 即可
    $fp = fopen("./entry/".date("Y-m-d", $post->created).'-'.str_replace("_", "-", $post->slogan).'.markdown', "w+");
    if (!fwrite($fp, $data)) {
        echo $post->id." failed.\n";
    }

    fclose($fp);

    // 這邊會檢查檔案,如果有 ## 這種 TextCube 專用 replace 存在
    // 則會告訴你這些檔案,你可能有需要做修改
    if(preg_match("/##/i", $data)) {
        echo "./entry/".date("Y-m-d", $post->created).'-'.str_replace("_", "-", $post->slogan).'.markdown'." ";
    }

}
?>

這裡還有第二個檔案,他是將 .markdown 檔案逐行取出,然後做一些字串的過濾等等。

#!/usr/bin/php -q 
<?php

exec('ls ./entry/*.markdown', $rst);

foreach($rst as $file) {

    $fp = fopen($file, "r+");
    $lines = 0;
    $data = '';
    $is_code = false;
    while(!feof($fp)) {
        // 逐行取出
        $line = fgets($fp);

        // 換掉 [code] 套件的原始碼區塊
        if(preg_match("/\[code ([a-z0-9]+)\]/i", $line)) {
            $is_code = true;
            $line = "\n";
        }
        if(preg_match("/\[\/code\]/i", $line)) {
            $is_code = false;
            $line = "\n";
        }
        if($is_code) {
            if(trim($line) != "") {
                $line = "\t".$line;
                $data .= $line;
            }
        } else {
            $data .= $line;
        }

        // 超過 20 行則插入 read more
        $lines++;
        if($lines == 20) {
            $data .= "<!-- more -->\n";
        }

        // HTML Code 替換
        $data = str_replace("&nbsp;", " ", $data);
        $data = htmlspecialchars_decode($data);
        $data = str_replace('<meta http-equiv="content-type" content="text/html; charset=utf-8">', '', $data);
    }

    fclose($fp);

    // HC_Emoticons 錯誤替換
    $data = str_replace('blog/emo','emo',$data);

    file_put_contents($file, $data);
}
?>

我用上面這兩個小程式轉完了五百多篇文章,當然不會完全沒有問題。不過,因為我用的外掛不多,加上圖片大多數都已經在 Flickr 上面了,所以轉換起來也沒有什麼太大的問題。而因為是使用 Markdown 的關係,有許多跳脫符號是必須要去留意的,這個可能就真的得土法煉鋼,用正規作掉慢慢看了。

Generate too long

這是這個部落格系統唯一的問題癥結點吧。雖然龍哥有提過使用 rake isolaterake integrate 可以用於測試文章時使用,可以大幅簡短 rake generate 的運算時間。不過,當我要發佈的時候,還是得面臨一次全部重新運算的問題。所以,我就想到幾個比較垃圾取巧的方法來避開。

  • 既然他要運算,那就給他算。
  • 給他算的是新的文章,舊的文章我先搬走。
  • 等他算完在把新文章蓋入舊的文章中。
  • 搬回 public 之後就可以上線了。

由於我是放在 Heroku 上面,我寫得 shell-script 應該是只有我自己合用,如果想改的人請自便。

#!/bin/bash 

[ ! -f _config.yml ] && echo "You are not under octopress directory." && exit 0;

# 找出在 10 分鐘以內**沒有**修改過的 markdown 檔案,-mmin 單位是分鐘。
TIMESTAMP=10
# 利用 sed 把檔案洗出來並重新組合成 public 底下的結構
# 依照 _config.yml 所設定的 permalink: /blog/:year/:month/:day/:title/
# 所以這裡的輸出結果是 \/blog\/\1\/\2\/\3\/\4\/
# MAINPATH="blog"
# SUBPATH="\1\/\2\/\3\/\4"
# 如果你的 permalink 有另外做設定,請自行修改
# 例如我的 permalink: /entry/:title/
# 就設定為 entry\/\4
# \1 代表年
# \2 代表月
# \3 代表日
# \4 代表標題(title)
MAINPATH="entry"
SUBPATH="\4"

FILES=`find ./source/_posts/ -type f -name "*.markdown" -mmin +$TIMESTAMP | sed -e p -e "s/\.\/source\/_posts\/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)-\([^.]\+\).markdown/\.\/public\/$MAINPATH\/$SUBPATH\//g" | xargs -n 2`

# 建立暫存資料夾
mkdir -p tmp
mkdir -p tmp/source/
mkdir -p tmp/public/

for (( index=0; index<${#FILES[@]}; index++ )); do
    while IFS=' ' read -ra FILE; do
        # 把 source/_posts 跟 public 的文章目錄移動到暫存資料夾
        cp "${FILE[0]}" "./tmp/source/"
        cp -r "${FILE[1]}" "./tmp/public/"

        TT=`echo "${FILE[0]}" | sed -e "s/\.\/source\/_posts\///g"`
        # 只取文章開頭八行的設定,回寫 source/_posts
        # 這個動作是很危險的!
        # 這麼做是為了確保 Archive 跟 Categories 能夠整正常產生。
        TS=`head -8 ./tmp/source/"$TT"`
        echo "$TS" > "${FILE[0]}"
    done <<< "${FILES[$index]}"
done

# 重新產生網站的檔案
rake generate

# 把檔案拷貝回去
find ./tmp/public/ -type d | xargs -n1 [email protected] cp -r @ "./public/$MAINPATH/"
cp ./tmp/source/*.markdown ./source/_posts/

這個 shell-script 有個非常危險的回寫動作,要用之前,請確定你對 shell-script 有一定的認識,不然如果發生什麼意外,我可不能擔保喔!