Trac のカスタムクエリやレポートを Excel HTML 形式でダウンロードできるようにするプラグイン

ついこの間 ExcelReportPlugin を使いたくなりインストールを試みようとしたのですが、「TracReportPluginPatch を当てる必要がある」「pyExcelerator が必要」ということに気づきました(いまさら)。
ここでパッチを当てるのはまあ仕方ないか…と思ったのですが、pyExcelerator は python 2.4+ を要求しています。インストール先の環境は python 2.3 なのです。これではプラグインを使うことが出来ません。
そこで別途ライブラリを必要とせず python 2.3 環境でも Excel 出力できるプラグインを書きました。と言っても本物の Excel ファイルを出力できるわけではなく Microsoft Office HTML という形式で出力します。この形式なら Trac + Genshi だけで処理できます。
どんな形式なのかは How to format an Excel workbook while streaming MIME content, Microsoft Office HTML and XML Reference あたりを見てください。
github に置いてみました。実験コードだし github が使ってみたかったので。
http://github.com/jun66j5/tracmsofficexmlplugin
いまのところ、以下のような感じになっています。

  • Trac 0.11.5 英語版でしか確認していません
  • カスタムクエリを Excel HTML でダウンロード
  • カスタムクエリを Excel XML でダウンロード (列の幅などまったく調整していません)
  • レポートを Excel HTML でダウンロード (ReportPluginPatch なしで)
  • python 2.3 で動きます (HTML/XML を出力しているだけだから)
  • もちろん Excel で開けます

プラグインを有効にするには trac.ini に以下の記述を足してください

[components]
tracmsofficexml.* = enabled

有効にすると以下のような感じでリンクが増えるようになっています。

カスタムクエリ
レポート

レポートを Excel HTML でダウンロードしたところ。

興味があれば遊んでみてください。
p.s. 実はこのプラグインの大部分を書いた後に ExcelReportPluginをpython2.3で利用するhack | Ryuzee.com の記事を見つけました。「python 2.3 だから ExcelReportPlugin 入れられない」って人はこちらを試してみてはどうでしょうか。

TraM 0.2 を fastcgi で使ってみる

4-5日前に Trac 0.11.x に対応した TraM という Trac 複数プロジェクトフロントエンドモジュールがリリースされました。

TraM0.2をリリースしました | Ryuzee.com

この記事や README を読むと解るのですが mod_python での利用を前提としています。が、うちの環境では fastcgi でやっているので残念ながら使えません…。

と、ここで断念するわけもなく tram/modpython_frontend.py を fastcgi に対応させた tram/fcgi_frontend.py を用意してあげればとりあえず動くだろうと考え(Trac はもともとそういう構造)、tram/fcgi_frontend.py を書くことにしました。

まず trac/web/modpython_frontend.pytram/modpython_frontend.py を見比べてみると、根本的なところは trac.web.main.dispatch_request の代わりに tram.main.dispatch_request を使うようにしていることのよう。

次に trac/web/fcgi_frontend.py を見てみると trac.web._fcgi.WSGIServer() に trac.web.main.dispatch_request を渡して run() を呼び出しているだけの構造。

ということで以下のようなものを tram/fcgi_frontend.py として配置しました。tram.main.dispatch_request を使うようにしただけです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pkg_resources

from trac import __version__ as VERSION
from trac.web import _fcgi
from tram.main import dispatch_request

def run():
    _fcgi.WSGIServer(dispatch_request).run()

if __name__ == '__main__':
    pkg_resources.require('Trac==%s' % VERSION)
    run()

これで tram をインストールしました。

$ python setup.py bdist_egg
$ sudo /usr/bin/easy_install -Z dist/*.egg

今度は trac.fcgi を変更して tram.fcgi_frontend を使うようにします。一応 tram.fcgi_frontend を import してみてダメだったら trac.web.fcgi_frontend を import するようにしておきました。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
os.environ['LANG'] = 'POSIX'
os.environ['TRAC_ENV_PARENT_DIR'] = '/var/lib/trac'

try:
    try:
        from tram import fcgi_frontend
    except ImportError, e:
        from trac.web import fcgi_frontend
    fcgi_frontend.run()
except SystemExit:
    raise
except Exception, e:
    print 'Content-Type: text/plain\r\n\r\n',
    print 'Oops...'
    print
    print 'Trac detected an internal error:'
    print
    print e
    print
    import traceback
    import StringIO
    tb = StringIO.StringIO()
    traceback.print_exc(file=tb)
    print tb.getvalue()

最後に trac.ini を変更して tracplugin.* = enabled を追加します。すべてプロジェクトに反映されるように inherit に指定している trac.ini に追加しておきました。

これで準備は整いました。trac.fcgi を再起動させてみて確認です…。

とりあえず手元ではすぐに見つかるような問題はなく動作しているようです。all というプロジェクトを作っていないのでいくつかのページで Environment not found と言われてしまいますが、プロジェクトを作成するのは何か気持ち悪いのでこのまま放っておきます。

後は TraM 0.2 は clearsilver ベースなので genshi 前提にしたカスタマイズが効いてないとかありますが、それはおいおい解消されるでしょう。

TraM + fastcgi はなんとなーく動いたと思います。Trac を mod_wsgi で運用している人もいるでしょうけど、そっちは気が向いたらということで。

なんか WorkflowEditorPlugin なるものが登録されてる

WorkflowEditorPlugin - Trac Hacks - Plugins Macros etc. - Trac

作ってるのは TicketExtPlugin作者id:szk-takanori さんみたい。

あとでみよう。

追記: これを書いたときには WorkflowEditorPlugin ページに screenshot がなかったけど、いまは追加されててどんな感じなのか解るようになってますね。

TracNav が Trac-0.11.1 に upgrade するとエラーになっていた

気づいたら TracNav マクロを使っているページで ↓ のエラーが表示されるようになっていた。

Traceback (most recent call last):
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 468, in _macro_formatter
    return macro.process(args, in_paragraph=True)
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 179, in process
    text = self.processor(text)
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 166, in _macro_processor
    text)
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 302, in expand_macro
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 230, in run
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 204, in parse_toc
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 169, in get_toc_entry_and_indent
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 158, in get_toc_entry
  File "build/bdist.linux-i686/egg/tracnav/tracnav.py", line 85, in format_toc
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 954, in format
    result = re.sub(self.wikiparser.rules, self.replace, result)
  File "/usr/lib/python2.3/sre.py", line 143, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 807, in replace
    replacement = self.handle_match(fullmatch)
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 803, in handle_match
    return internal_handler(match, fullmatch)
  File "/usr/lib/python2.3/site-packages/trac/wiki/formatter.py", line 374, in _lhref_formatter
    return self._make_link(ns, target, match, label, fullmatch)
TypeError: _make_link() takes exactly 5 arguments (6 given)

Trac-0.11 から 0.11.1 への変更で _make_link の引数が変わったみたい。というかこんな内部向けメソッドを TracNav マクロは使っているのね…。
Diff from tags/trac-0.11/trac/wiki/formatter.py@7556 to tags/trac-0.11.1/trac/wiki/formatter.py@7556 – The Trac Project

TracNav のサイトで確認するとチケットが上がっていてすでに修正されていので、TracNav の最新のソース(r3249 以降)で plugin を作り直したらエラーもなくなった。
#292 (Error: Macro JPNav(foobar) failed after update from trac 0.11dev to trac 0.12dev) - JavaParty - Java's Companion for Distributed Computing - Trac

Trac 0.11.x で Wiki ページにメニューを付ける

0.11に移行後、サイドメニューが表示されない。 - 日記trac にサイドメニューを付けてみる - 記憶は削除の方向でを読んで、「そういえば PukiWiki のメニューバーみたいなの*1をやってみよう」としてたのを思い出しました。
ということで Trac 0.11.x/Genshi でやってみました。

1. wiki_view.html を用意する

/usr/lib/python2.3/site-packages/trac/wiki/templates/wiki_view.html みたいなところにありますので、このファイルを ${TRAC_ENV}/templates/wiki_view.html にコピーします。
コピーしたら ↓ の差分を追加します。

--- wiki_view.html.orig	2008-08-07 02:29:02.000000000 +0900
+++ wiki_view.html	2008-09-05 20:33:21.000000000 +0900
@@ -34,6 +34,13 @@

       <div class="wikipage searchable" py:choose="" xml:space="preserve">
         <py:when test="page.exists" xml:space="preserve">
+          <py:if test="page.name != 'Menu'">
+            <py:with vars="page_menu = page.__class__(page.env, 'Menu')">
+              <py:if test="page_menu.exists">
+                <div class="wiki-toc">
+                  ${wiki_to_html(context, page_menu.text)}
+                </div>
+              </py:if>
+            </py:with>
+          </py:if>
           ${wiki_to_html(context, page.text)}
         </py:when>
         <py:otherwise>

wiki_view.html が用意出来たら、ファイルを配置したことを認知させるために tracd (または apache2) を再起動します。

2. wiki:Menu ページを記述する

表示させたい内容を wiki:Menu ページに記述します。

3. wiki:Menu 以外のページを見る

こんな感じになります。

wiki:Menu の内容を取り出すあたりが強引ですが、とりあえず出来ました。
表示する位置などは class="wiki-toc" を別のものにするなどして調整してください。

*1:特定の Wiki ページの内容がすべてのページのメニューとして表示される機能

Opera の WYSIWYG モードでの Enter キーが 挿入にならない

追記: Opera 9.5.1 を試したらちゃんと Enter キーで <p> 挿入になってた。Shift-Enter も <br> 挿入でまともでした。Opera 9.5 よりも前のバージョンの WYSIWYG モードは buggy らしい。
Rich HTML editing in the browser: part 1 - Opera Developer Community



9.5 よりも前のバージョンの Opera では WYSIWYG モードにおける Enter キーの挙動が期待とはかなりかけ離れていて、IE/Firefox/SafariWYSIWYG モードでは <p> 挿入になるのですが Opera では単に <br> 挿入になります。Shift-Enter でも <br> 挿入になります。
なんでじゃ。。。
DOM 操作を行って他のブラウザと同じような挙動になるようにしてもいいんですが、undo/redo がまともに使えない状態になってしまうのでやってません。
ということで、対処方法が解らず仕舞いで 9.5 よりも前のバージョンの Opera で tracwysiwyg を使うのはかなり厳しいはずです。。。

Firefox の WYSIWYG モードで execCommand('inserthtml', false, '') が効かないときがあるのに対処する

tracwysiwyg では改行アイコンを実現するために ↓ のようなコードを実行するようにしていました。

function() {
    contentDocument.execCommand('inserthtml', false, '<br>');
}

なのですが、Firefox の場合カーソル位置によっては <br> が入らないことがあります。

WYSIWYG モードが以下のような html を持っているとします。

<p>
first<br>
second<br>
</p>

second と <br> の間にカーソルがあるときに inserthtml で <br> を入れようとしても全く効きません。

結局取った対処は、このカーソル位置でも Shift-Enter による改行挿入は効くようなので、以下のようなコードで Shift-Enter と同等の keyevent を発動させるようにしました。

TracWysiwyg.prototype.insertLineBreak = function() {
    var d = this.contentDocument;
    var event = d.createEvent("KeyboardEvent");
    event.initKeyEvent("keypress", true, true, null, false, false, true, false, 0x000d, 0);
    d.body.dispatchEvent(event);
};