RedmineのコンテンツをMarkdownに変換しました
Textile vs Markdown
軽量マークアップ言語にはたくさんの種類があります。私も教育で使うテキストの作成やRedmineのWikiでは長年 Textile を使って来ました。 しかし、最近はGithubの標準のマークアップ言語がMarkdownだったり、Atom(エディター)をはじめたくさんのMarkdownをサポートするツールが現れ、私も MacDownというツールが気に入り開発ドキュメント等のMarkdown化がどんんどん進んでいます。このブログもはてな記法ではなくMarkdownを使っています。
TextileとMarkdownを比べると 標準のMarkdown は記述能力が低くテキストを作るには能力不足ですが、 GitHubの拡張 や php Markdown Extra などの拡張機能を使えば、ほぼ同等です。
Redmine
Redmineのマークアップ言語のデフォルトはTextileですが、Version 2.5からMarkdownをサポートするようになりました。ただし、Redmineサーバー全体の設定なので新規にRedmineを始める人以外には使いにくいものでした。 redmine_persist_wfmtのようなプラグインを使えばコンテンツ(Wiki, Ticket...)単位でTextitle/Markdownが選択できるのでこれを使えば良いかもしれません。
しかし、私はRedmineのWikiを教育でよく使っています。お客様毎に教育で使うコード、ヒント、参考情報・・・などをWikiを使い提供しています。しかも以前に作ったWikiを元に作成する事が多いので、そのTextileとMarkdwonが混在するのは望ましくありません。
そこで、Textileコンテンツを全てMarkdownに変換してしま事にしました。
Textile to Markdown
"Convert Textile to Markdown" で検索すると Pandoc というドキュメント変換ツールが良く出来てきます。このツールは色々なドキュメント形式を相互変換できる素晴らしいツールですが、やはり完璧ではありませんでした。 Markdown→TextileではGitHub拡張をサポートしていますが、Textile→MarkdownではテーブルをGitHub拡張に変換してくれたりしません・・・
そこで、自作を検討しました。MarkdownとTexitleは似ています、正規表現を使えばほとんど変換できます。
def textile_to_markdown(textile) d = [] pre = false table_header = false text_line = false textile.each_line do |s| s.chomp! if pre if s =~ /<\/pre>/ d << "~~~" pre = false else d << s end next end s.gsub!(/(^|\s)\*([^\s\*].*?)\*(\s|$)/, " **\\2** ") s.gsub!(/(^|\s)@([^\s].*?)@(\s|$)/, " `\\2` ") s.gsub!(/(^|\s)-([^\s].*?)-(\s|$)/, " ~~\\2~~ ") s.gsub!(/"(.*?)":(.*?)\.html/, " [\\1](\\2.html) ") d << "" if text_line text_line = false case s when /^<pre>/ d << "~~~" pre = true when /^\*\*\* (.*)$/ d << " * " + $1 when /^\*\* (.*)$/ d << " * " + $1 when /^\* (.*)$/ d << "* " + $1 when /^\#\#\# (.*)$/ d << " 1. " + $1 when /^\#\# (.*)$/ d << " 1. " + $1 when /^\# (.*)$/ d << "1. " + $1 when /^h(\d)\. (.*)$/ d << "#" * $1.to_i + " " + $2 when /^!(.*?)!/ d << "![](#{$1})" when /^\|_\./ d << s.gsub("|_.", "| ") table_header = true when /^\|/ d << s.gsub(/\=\..+?\|/, ":---:|").gsub(/\s+.+?\s+\|/, "---|") if table_header table_header = false d << s.gsub("|=.", "| ") when /^\s*$/ d << s else d << s text_line = true end end d.join("\n") + "\n" end
このコードは全てのTextileフォーマットをサポートしているわけではありませんが、私が使ってる機能はほぼ網羅しています。 変換されたMarkdownはGitHub拡張やphp Markdown Extraの機能を使っています。
さてRedmineでは色々なところにTextileが書けますが、今回は Wiki, Ticket, Ticketの履歴のコンテンツに対応しました。必要があれば追加して下さい。
def update_content(model, attrbute) total = model.count step = total / 10 puts " #{model}.#{attrbute} : #{total}" model.all.each_with_index do |rec, ix| n = ix + 1 puts sprintf("%8d", n) if n % step == 0 rec[attrbute] = textile_to_markdown(rec[attrbute]) if rec[attrbute] rec.save! end end update_content(WikiContent, :text) update_content(Issue, :description) update_content(Journal, :notes)
コードは Gistにも置きました。
このコードを以下のように runnder で実行するとTextileがMarkdownに変換されます。
$ rails runner -e production tools/textile2md.rb
注意: このプログラムは全てのTextile形式を変換できるものではありません、またバグ等で正しく変換できない場合もありますので、変換前に必ずRDBのバックアップを取って下さい。