2017年11月 5日 (日)

Dataloader+MariaDB Connector/JのDockerイメージを作りました

これから少しずつSalesforce関連の情報をBlogに残していきたいと思います。今回はこれだけ見ると何の意味もなさそうですが、これから先の記事のベースになるDataloaderとMariaDB(MySQL互換DB)とやり取りをするために、必要な Connectorをまとめたコンテナを作成してDockerHubにおいておきました。
DockerHubのURL : https://hub.docker.com/r/okazakiyuji/dataloader/
Dockerfileは
FROM centos
MAINTAINER okazakiyuji <zaki@mbf.nifty.com>
RUN localedef -f UTF-8 -i ja_JP /usr/lib/locale/ja_JP.UTF-8
RUN yum -y update && \
    yum -y install git svn java-1.8.0-openjdk maven unzip mysql && \
    yum clean all
# Build Dataloader
WORKDIR /tmp
ADD https://github.com/forcedotcom/dataloader/archive/master.zip ./
ENV DL_VER 40.0.0
ENV DL_BIN_DIR /root/dl/bin
RUN mkdir -p $DL_BIN_DIR
RUN unzip master.zip && \
    cd ./dataloader-master && \
    mvn clean package -DskipTests && \
    mv target/dataloader-$DL_VER-uber.jar $DL_BIN_DIR && \
    cp license.txt $DL_BIN_DIR/dataloader-license.txt && \
    cd .. && rm -r ./dataloader-master ./master.zip
# Install mysql connector
ENV MYSQL_CON_VER 2.1.0
ADD https://downloads.mariadb.com/Connectors/java/connector-java-$MYSQL_CON_VER/mariadb-java-client-$MYSQL_CON_VER.jar $DL_BIN_DIR/
# Copy scripts
WORKDIR $DL_BIN_DIR
ADD encript.sh ./
ADD process.sh ./
RUN chmod a+x encript.sh process.sh
ENV DATALOADER_CLASSPATH $DL_BIN_DIR/mariadb-java-client-$MYSQL_CON_VER.jar:$DL_BIN_DIR/dataloader-$DL_VER-uber.jar
ENV PATH $PATH:$DL_BIN_DIR
Dockerfileの先頭で
FROM:okazakiyuji/dataloader
のように指定していただければ使えます。といっても難しいと思うので、次は、このコンテナを使用して、SalesforceからMariaDBへのバックアップについての記事を書きます。

| | コメント (0) | トラックバック (0)

2017年8月27日 (日)

Dockerでkanon(Trac)を動かしてみた2 - イメージの作成

前の記事で、多くの人は必要ない部分としてイメージの作成については触れていなかったので、ここで解説します。素人なので、間違っているところもあると思うので、お気づきの点は教えてください。
まずはファイルを取得します。
$ hg clone https://bitbucket.org/okazakiyuji/kanon
$ cd kanon
$ hg update org
リポジトリのファイル(ゴミもあります)の一覧です。
(1) Dockerfile
イメージを作成するためのファイル
(2) docker-entrypoint.sh
runで実行するときに起動するスクリプト
(3) kanon-setup
修正してあるインストーラ
(4) pkglist.rhel6
yumでインストールするパッケージ、ビルドの初期に実行したいのでKanonから抜き出した
(5) pluginlist
インストールするプラグインのURL
上書きするファイルの(3),(5)の修正内容
・元のKanonConductorでうまくインストールできない場所を修正する
 プラグインのインストール元をhttp->httpsに変更(3),(5)
・コンテナ化のために必要なこと
 インストールの初期の質問はなしにする(3)
 Maven, Jenkinsは無効に(3)
 SELinuxの無効化もしない(3)
 SampleProjectはインストールしない(3)
 サービスは動かさない(3)
残りの二つのファイルはデータの永続化のための処理が入っています。内容の説明はファイル内のコメントでご勘弁を
(1) Dockerfile
$ cat Dockerfile
FROM centos:6
MAINTAINER okazakiyuji <zaki@mbf.nifty.com>
# ロケールの設定
RUN localedef -f UTF-8 -i ja_JP /usr/lib/locale/ja_JP.UTF-8
# パッケージのインストール kanon-setupのものも先にインストールする
ADD pkglist.rhel6 /tmp/
RUN yum -y update && \
    yum -y install mercurial `cat /tmp/pkglist.rhel6` subversion-python && \
    yum clean all
# セットアップに必要な環境変数
ENV KANON_IN_CONTAINER 1
ENV MAVEN "n"
ENV JENKINS "n"
# マウントするフォルダの退避先を作成
RUN mkdir -p /tmp/save
# 上書きするファイルを/tmpにコピーする
COPY kanon-setup /tmp/kanon-setup
COPY pluginlist /tmp/pluginlist
# インストール、マウントするフォルダをコピー、いらないファイルの削除
RUN hg clone https://bitbucket.org/okamototk/kanonconductor \
        /tmp/kanonconductor && \
    cd /tmp && \
    cp kanon-setup kanonconductor/ && \
    cp pluginlist kanonconductor/ && \
    /tmp/kanonconductor/kanon-setup && \
    cd /tmp/save && \
    cp -r /var/opt/kanon ./var && \
    cp -r /etc/opt/kanon ./etc && \
    cp -r /opt/kanon/lib ./lib && \
    cd /tmp && rm -rf kanonconductor kanon-setup
# マウントする場所を明示しておく
VOLUME /opt/kanon/lib
VOLUME /etc/opt/kanon
VOLUME /var/opt/kanon
# ホストに接続するポート
EXPOSE 80
# 実行時に動くスクリプトのコピー
ADD docker-entrypoint.sh /
# 実行時に起動するファイルの指定
ENTRYPOINT ["/docker-entrypoint.sh"]
# 実行時のスクリプトに渡す引数(コマンド)
CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]
(2) docker-entrypoint.sh
$ cat docker-entrypoint.sh
#!/bin/bash
# マウントしたフォルダに何もない場合は初回の起動とする
if [ ! -d /var/opt/kanon/trac ] ; then
    # 初回起動時はコピーしていたファイルを戻す
    cp -r /tmp/save/lib /opt/kanon/
    cp -r /tmp/save/var/* /var/opt/kanon/
    cp -r /tmp/save/etc/* /etc/opt/kanon/
fi
if [ "$1" = '/usr/sbin/apachectl' ]; then
    # https://github.com/CentOS/CentOS-Dockerfiles/blob/master/httpd/centos6/run-httpd.sh を参照した
    rm -rf /run/httpd/*
    exec /usr/sbin/apachectl -D FOREGROUND
    exit 0
fi
# CMDで/usr/sbin/apachectl以外が指定された場合はそのままコマンドとして実行する
exec "$@"

| | コメント (0) | トラックバック (0)

Dockerでkanon(Trac)を動かしてみた

長い間他の仕事をしていて、久しぶりにサーバ管理に戻ってきました。戻ってくると、VM(仮想マシン)が山ほどあり、Kanon使っているだけなのに、無駄にリソースを消費するし、多すぎてもう管理できなくなってます(T_T)。ということで、だいぶほったらかしていた「DockerでKanonを動かす」をやってみることにしました。Dockerfileの細かい内容は知らなくても使えるので、この記事ではGithubのイメージを持ってきて試す記事になってます。
○要件
・ コンテナのなかに複数プロジェクト(kanonなら問題なし)
・ VMのなかに複数の仕切られた空間
・ Dockerのビルドでインストールまで(ほぼ)完了する
・ 実行時にプロジェクト作成/trac-admin実行できる
・ データは永続化する
・ 再起動時に自動起動
○調べて試したこと

・ コミットしないで終了して再起動すると元に戻る
データの永続化はボリュームをマウントする。 今回はデータボリュームコンテナにする
・ ビルド時にデータがある場所に、実行時に空のマウントするとデータは消える
Dockerfile内のビルドの最後で、マウントするフォルダ(/var/opt/kanon, /etc/opt/kanon, /opt/kanon/lib)を退避しておき、コンテナの起動時に動くスクリプトで、初回の一回だけデータをコピーする。
・ 複数のコンテナの起動はDocker-composeで簡単にできる
docker-compose.ymlファイル内のサービスの定義をコピーして、名前とポートとデータボリュームコンテナ名を変更するだけ。必要ならホストのFirewallはあけてください。
・ コンテナ内では一つのサービス、apacheはFOREGROUNDで
・ コンテナの自動再始動はrestart: alwaysでできる

docker-compose.ymlファイル内で指定
Kanonのコンテナを作成することは置いておいて、DockerHubから持ってきたイメージでの確認の手順です。
0. インストール
・ OSのインストール
CentOS の GNOME Desktopでインストールする,ユーザはsudo使えるように管理者にしておいた方がいいでしょう。
・ dockerのインストール
$ sudo yum update && yum -y install docker
$ sudo systemctl start docker
$ sudo systemctl enable docker
・ docker-composeのインストール
https://github.com/docker/compose/releases にインストール方法が書いてます(一応rcはやめて一つ前のものにする)
$ curl -L https://github.com/docker/compose/releases/download/1.15.0/docker-compose-`uname -s`-`uname -m` > docker-compose
$ chmod +x ./docker-compose
$ sudo mv ./docker-compose /usr/bin/docker-compose
1. フォルダ作成して移動
$ mkdir kanon_containers
$ cd kanon_containers
2. docker-compose.ymlファイル作成
$ vi docker-compose.yml
version: '2'
services:
  kanon_1:
    # とりあえず動くように作ったコンテナを持ってくる
    image: okazakiyuji/kanon:org
    volumes: # 運用時に変更されるフォルダにデータボリュームコンテナをマウントする
      - kanon_1-var:/var/opt/kanon
      - kanon_1-etc:/etc/opt/kanon
      - kanon_1-lib:/opt/kanon/lib
    ports:
      - "8081:80"
    restart: always # 再起動時に再始動
  kanon_2: # portとデータボリュームコンテナ名を変更して二つ目のKanon
    image: okazakiyuji/kanon:org
    volumes:
      - kanon_2-var:/var/opt/kanon
      - kanon_2-etc:/etc/opt/kanon
      - kanon_2-lib:/opt/kanon/lib
    ports:
      - "8082:80" # 別のポート
    restart: always
volumes:
  kanon_1-var:
    driver: local
  kanon_1-etc:
    driver: local
  kanon_1-lib:
    driver: local
  kanon_2-var:
    driver: local
  kanon_2-etc:
    driver: local
  kanon_2-lib:
    driver: local
3. Dockerイメージを持ってくる
$ sudo docker pull okazakiyuji/kanon:org
コンテナの中身についてはあとで…
4. コンテナを起動する
$ sudo docker-compose up -d
...
コンテナ起動中
...
$ curl http://localhost:8081/trac
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Available Projects</title>
  </head>
  <body>
    <h1>Available Projects</h1>
    <ul>
    </ul>
  </body>
※ localhost:8081/tracでプロジェクトがないが、エラーは出ないことを確認
サーバの外から見えない場合はFirewallの設定を変更してください
$ curl http://localhost:8082/trac
...
※ localhost:8082/tracでプロジェクトがないが、エラーは出ないことを確認
5. プロジェクトを作成
$ sudo docker-compose exec kanon_1 /bin/bash
[root@6b47e7284acc /]# /opt/kanon/bin/kanon-create-project test1 svn default n
...
プロジェクトの作成
...
[root@6b47e7284acc /]# exit
$ sudo docker-compose exec kanon_2 /bin/bash
[root@fad824e04e58 /]# /opt/kanon/bin/kanon-create-project test2 svn default n
...
プロジェクトの作成
...
$ curl http://localhost:8081/trac
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Available Projects</title>
  </head>
  <body>
    <h1>Available Projects</h1>
    <ul>
      <li>
        <a href="/trac/test1" title="My example project">test1</a>
      </li>
    </ul>
  </body>
</html>
※ localhost:8081/tracでプロジェクトtestが追加されていることを確認
$ curl http://localhost:8082/trac
...
※ localhost:8082/tracでプロジェクトtest2が追加されていることを確認
6. 再起動してもコンテナが起動するかを確認
$ sudo reboot now
...
再起動
...
※ localhost:8081/tracでプロジェクトtestが追加されていることを確認
7. データコンテナを削除
確認後に削除したい場合は次のように
$ sudo docker-compose down
$ sudo docker volume ls
...
local               kanoncontainers_kanon_1-etc
local               kanoncontainers_kanon_1-lib
local               kanoncontainers_kanon_1-var
local               kanoncontainers_kanon_2-etc
local               kanoncontainers_kanon_2-lib
local               kanoncontainers_kanon_2-var
$ sudo docker volume rm kanoncontainers_kanon_1-etc kanoncontainers_kanon_1-lib kanoncontainers_kanon_1-var
$ sudo docker volume rm kanoncontainers_kanon_2-etc kanoncontainers_kanon_2-lib kanoncontainers_kanon_2-var
これで運用までうまくいくと思うんですが…

| | コメント (0) | トラックバック (0)

2014年11月 2日 (日)

チケットの表示ページで(ユーザとか)DBからSELECTできるようにするプラグイン

★はじめに
Trac使っていてよく言われていたのが次のに挙げた二つです。いまさらになってしまうんですが作ってみました。ただし、TracのDBの中身とかSQLがわからない人にはちょっと使うのは難しいとおもいます。私自身それほどのレベルではないですが(^_^A;
  • ユーザ名からアカウント名の頭文字がわからん人がいる(ZガンダムがMSZ-006だってすぐにはわからんようなもの?)
  • カスタムフィールドに設定できないようなフィールドをDBのクエリで選択可能にしたい(数が多いがほかのフィールドやユーザでフィルタがかけられるとか)
結局やることはDBから持ってくるってことをプラグイン化すればいいだけですが、SQLを頑張って書けばいろいろなことができます。

★どういうことができる
確認のために下の方につけた設定では次の4つのことができます。
  1. ヘッダのところをDBから値を持ってきて次の行に表示Ticket_db_header_2
  2. チケット変更のところをreadonlyに変更するTicket_db_readonly
  3. チケット変更のところをDBからのQuery結果でSELECTにするTicket_db_field_select
  4. チケットのアクションのところの担当の変更をDBからのQuery結果でSELECTにするTicket_db_owner_select
ユーザ選択の時にマネージャのみを選択できるようにしたり、直前のownerををデフォルトで選択しておくとかの例を載せています。SQLを頑張って書けばいろいろ使えると思います。

★インストール方法
  1. 最後に添付してあるファイルをダウンロードし展開する
  2. そのなかのフォルダでpython setup.py install
  3. 管理画面からプラグインを有効にする。
  4. 次を参考にtrac.iniに設定を追加する。

★設定方法
trac.iniを直接書き換えます。
適切な位置に
[dbqueryfield]
を追加する。
その下の中身のデータはよくある次の形式で書いていきます。
グループ名.データ名 = 値
※ グループ名は適当でかまいませんがフィールド名を入れておくとわかりやすいかも
データ名は次の4つがあります
  1. operation
  2. field
  3. type
  4. sql
※ operationがreadonlyの場合はsqlは設定しません
※ typeは省略するとすべてのtypeに対して処理が行われます
※ typeは一つの値しか設定できません
1 operationに設定できるのは次の4つです
  • readonly フィールドを変更できないように(隣のフィールドのSQLで参照したいときとかに参照される側にreadonlyにする)
  • header ヘッダの表示の次の行にクエリ結果を表示
  • field フィールドをクエリ結果で選択可能に
  • action アクションのオーナ変更を選択できるように
2 fieldは通常はカスタムフィールド名
operationがaction:"アクションのオーナ変更"の場合は"アクション名_次のステータス"になります。わからない場合はソース表示で確認してみてください"id=action_アクション名_次のステータス_owner"となっているところがあるのでその中を抜いていただければOkです。
3 typeを指定した場合はこの処理をするtypeを指定します
"バグ"とか"タスク"とかですね
4 sqlデータを取得するためのSQLを指定する
SQLにはチケットのフィールド名を埋め込むことができます。pythonのフォーマットの%(~~)型のような書き方ですがconfigの値に"%("が含まれるとエラーになるので%を$に置き換えてください。
・operationがheaderの場合はSELECTの結果は表示名一つです。ユーザフィールドの値を元にユーザ名を持ってくる場合は次のようになります
SELECT s.value FROM session_attribute s WHERE s.sid='$(user)s' AND s.name='name'
・operationがfieldまたはactionの場合はSELECTの結果はフィールドの値、表示名、デフォルトかどうか(1:Default)です。ユーザフィールドを選択にする場合のSQLは次のようになります。

SELECT s.value FROM session_attribute s WHERE s.sid='$(user)s' AND s.name='name'

・長いSQLも大丈夫みたいです(これは直前のownerが選択された状態で表示されます)
SELECT s.sid, s.sid || ':' || s.value, '1' FROM session_attribute s WHERE  s.name='name' AND s.sid=(SELECT tc.oldvalue FROM ticket_change tc WHERE tc.ticket='$(id)d' AND tc.field='owner' AND tc.time=(SELECT tc1.time FROM ticket_change tc1 WHERE tc1.ticket='$(id)d' AND field='owner' ORDER BY tc1.time DESC LIMIT 1) ORDER BY tc.time DESC LIMIT 1) UNION SELECT s.sid, s.sid || ':' || s.value, '0' FROM session_attribute s WHERE  s.name='name' AND s.sid<>(SELECT tc.oldvalue FROM ticket_change tc WHERE tc.ticket='$(id)d' AND tc.field='owner' AND tc.time=(SELECT tc1.time FROM ticket_change tc1 WHERE tc1.ticket='$(id)d' AND field='owner' ORDER BY tc1.time DESC LIMIT 1) ORDER BY tc.time DESC LIMIT 1)

★サンプルの設定
trac.iniのこのプラグインの設定
[dbqueryfield]
manager_assign_reassign.field = manager_assign_reassign
manager_assign_reassign.operation = action
manager_assign_reassign.sql = SELECT s.sid, s.sid || ':' || s1.value, 0 FROM session_attribute s LEFT JOIN session_attribute s1 on s.sid=s1.sid AND s1.name='name' WHERE s.authenticated='1' AND s.name='emp_type' AND s.value='1'
reassign_reassign.field = reassign_reassign
reassign_reassign.operation = action
reassign_reassign.sql = SELECT s.sid, s.sid || ':' || s.value, 0 FROM session_attribute s WHERE s.authenticated='1' AND s.name='name'
reject_reassign.field = reject_reassign
reject_reassign.operation = action
reject_reassign.sql = SELECT s.sid, s.sid || ':' || s.value, '1' FROM session_attribute s WHERE  s.name='name' AND s.sid=(SELECT tc.oldvalue FROM ticket_change tc WHERE tc.ticket='$(id)d' AND tc.field='owner' AND tc.time=(SELECT tc1.time FROM ticket_change tc1 WHERE tc1.ticket='$(id)d' AND field='owner' ORDER BY tc1.time DESC LIMIT 1) ORDER BY tc.time DESC LIMIT 1) UNION SELECT s.sid, s.sid || ':' || s.value, '0' FROM session_attribute s WHERE  s.name='name' AND s.sid<>(SELECT tc.oldvalue FROM ticket_change tc WHERE tc.ticket='$(id)d' AND tc.field='owner' AND tc.time=(SELECT tc1.time FROM ticket_change tc1 WHERE tc1.ticket='$(id)d' AND field='owner' ORDER BY tc1.time DESC LIMIT 1) ORDER BY tc.time DESC LIMIT 1)
test.field = test
test.operation = readonly
type.field = type
type.operation = readonly
user_f.field = user
user_f.operation = field
user_f.sql = SELECT s.sid, s.sid || ':' || s.value, CASE WHEN s.sid='$(user)s' THEN '1' ELSE '0' END FROM session_attribute s WHERE s.name='name'
user_f.type = タスク
user_h.field = user
user_h.operation = header
user_h.sql = SELECT s.value FROM session_attribute s WHERE s.sid='$(user)s' AND s.name='name'
trac.iniのカスタムフィールドはユーザの選択を確認できるように追加
[ticket-custom]
complete = text
complete.label = 進捗率(%)
complete.order = 2
due_assign = text
due_assign.date = true
due_assign.date_empty = on
due_assign.label = 開始予定日
due_assign.order = 0
due_close = text
due_close.date = true
due_close.date_empty = on
due_close.label = 終了予定日
due_close.order = 1
parents = text
parents.label = 親チケット
user = text
user.format = plain
user.label = ユーザ
user.options =
user.order = 4
user.value =
trac.iniのワークフローのところは"承認者へ回送","差し戻し","担当者変更"の三つを作うぃ少し簡単にするように修正
[ticket-workflow]
leave = * -> *
leave.default = 1
leave.name = 変更しない
leave.operations = leave_status
manager_assign = assigned -> manager_assigned
manager_assign.name = 承認者へ回送
manager_assign.operations = set_owner
manager_assign.permissions = TICKET_MODIFY
reassign = new,assigned,reopened -> assigned
reassign.name = 担当者変更
reassign.operations = set_owner
reassign.permissions = TICKET_MODIFY
reject = manager_assigned -> assigned
reject.name = 差し戻し
reject.operations = set_owner
reject.permissions = TICKET_MODIFY
reopen = closed -> reopened
reopen.name = 差し戻す
reopen.operations = del_resolution
reopen.permissions = TICKET_CREATE
resolve = manager_assigned -> closed
resolve.name = 承認する
resolve.operations = set_resolution
resolve.permissions = TICKET_MODIFY
resolve.set_resolution = 対応済
resolve2 = assigned -> closed
resolve2.name = 対応せずに解決にする
resolve2.operations = set_resolution
resolve2.permissions = TICKET_MODIFY
resolve2.set_resolution = 不正,対応しない,重複,再現しない
ユーザプロファイルにemp_typeを作って偉い人は1を設定する
Sqlite_database_browser_2
プラグインの本体ticket_filter.py(ライセンスはBSD)を挙げておきます
# -*- coding: utf-8 -*-
from genshi.builder import tag
from genshi.filters.transform import Transformer
from trac.core import Component
from trac.core import implements
from trac.web.api import IRequestFilter
from trac.web.api import ITemplateStreamFilter
class TicketDBQueryFieldFilter(Component):
    """
    チケット表示画面でDBにqueryして表示を変更するfilter
    """
    implements(IRequestFilter, ITemplateStreamFilter)
    FIELDS_INFO = {}
    def __init__(self):
        options = self.config.options('dbqueryfield')
        for _option, _value in options:
            self.log.debug("option = ")
            o = _option.split('.')
            self.log.debug(o)
            field = o[0]
            option = o[1]
            try:
                opt = self.FIELDS_INFO[field]
            except:
                self.FIELDS_INFO[field]={}
                opt = self.FIELDS_INFO[field]
                opt['type'] = ''
            opt[option] = _value.replace("$(","%(")
        for key in self.FIELDS_INFO.keys():
            v = self.FIELDS_INFO[key]
            operation = v['operation']
            k = v['field']
            if operation=='readonly': #ReadOnlyにする
                v['id'] = 'field-%s' % k
                v['name'] = 'field_%s' % k
                if v['field'] in ('type', 'priority', 'milestone', 'component', 'version', 'severity'):
                    v['xpath'] = '//select[@id="field-%s"]' % k
                else:
                    v['xpath'] = '//input[@id="field-%s"]' % k
            elif operation=='header':
                v['xpath'] = '//td[@headers="h_%s"]/text()' % k
            elif operation=='field':
                v['id'] = 'field-%s'
                v['name'] = 'field_%s' % k
                v['xpath'] = '//input[@id="field-%s"]' % k
            elif operation=='action':
                v['id'] = 'action_%s_owner' % k
                v['name'] = 'action_%s_owner' % k
                v['xpath'] = '//input[@id="action_%s_owner"]' % k
                v['operation'] = 'field'
            else:
                self.log.error('Unknown operation(%s).' % operation)
    # IRequestFilter methods
    def pre_process_request(self, req, handler):
        return handler
    def post_process_request(self, req, template, data, content_type):
        if req.path_info.startswith('/ticket/'):
            field_values = []
            tkt = data['ticket']
            tkt['id'] = tkt.id
            tkt['authuser'] = req.authname
            cursor = self.env.get_db_cnx().cursor()
            for key in self.FIELDS_INFO.keys():
                v = self.FIELDS_INFO[key]
                if v['type'] and v['type'] != tkt['type']:
                    continue
                operation = v['operation']
                k = v['field']
                try:
                    field_value = tkt[k]
                except:
                    field_value = ''
                value = None
                xpath = v['xpath']
                if operation=='readonly':
                    value = tag.input(id=v['id'], name=v['name'], value=tkt[k], readonly=1)
                elif operation=='header':
                    if tkt[k]: #カスタムフィールドに値が入っている場合
                        sel_data = []
                        cursor.execute(v['sql'] % tkt)
                        sel_data.append(field_value)
                        for row in cursor:
                            sel_data.append(tag.br())
                            sel_data.append(row[0])
                            break
                        value = tag.span(*sel_data)
                elif operation=='field':
                    sel_data = []
                    cursor.execute(v['sql'] % tkt)
                    options=[]
                    for row in cursor:
                        if str(row[2]) == '1':
                            options.append(tag.option(row[1], value = row[0], selected = True))
                        else:
                            options.append(tag.option(row[1], value = row[0]))
                    value = tag.select(options, id=v['id'], name=v['name'])
                if value:
                    field_values.append({'key':k, 'value':value, 'xpath':xpath})
            data['filter'] = field_values
        return template, data, content_type
    # ITemplateStreamFilter methods
    def filter_stream(self, req, method, filename, stream, data):
        if 'filter' in data:
            for r in data['filter']:
                path = r['xpath']
                stream |= Transformer(path).replace(r['value'])
        return stream

| | コメント (4) | トラックバック (0)

2014年4月13日 (日)

TracLightningにコバンザメしてKanonと同様にPluginをインストールする

Kanonのインストーラをいじくってきて、だいぶ手を広げすぎて進まなくなってきたので、まず末端のWindowsのところを早めに終わらせたくて、今できているインストールの部分まで書いておきます。

KanonのインストーラTrac1.0.1に対応してくると、プラグインのインストールは、easy_install, resourceフォルダ, SVNリポジトリ, HGリポジトリ, BZRリポジトリといろいろなところからインストールしないといけないようになりました。そうなるとインストーラも複雑になるので、インストール方法と場所をTSVファイルに書いて読みながらインストールしたほうがいいかと思い修正してみました。やってるうちにこれWindowsに持って行けるんじゃない?ってことでさらにやってみました。
動いてもなんのやくにも立たないのですが手順は
  1. TracLightningをインストール
  2. TracLightningフォルダ下のpython,python-libをどこかに移動
  3. Python2.7のx86版をhttp://www.python.jp/download/ からインストール
  4. TortoiseHGをインストール
  5. c:\TracLight\kanonにkanon関連ファイルの展開
  6. C:\TracLight\kanonにhttps://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.pyをダウンロード
  7. コマンドプロンプトでC:\TracLight\kanon>c:\TracLight\Python\python.exe ez_setup.py と実行してeasy_installをインストール
  8. C:\TracLight\Python\Scripts\easy_install.exe があること確認
  9. barはhttps://launchpad.net/bzr/2.6/2.6b1/+download/bzr-2.6b1-1-setup.exeでC:\TracLight\kanon\Bazaarにインストール
  10. C:\TracLight\kanonに「pluginlist-1.0」をダウンロード
  11. C:\TracLight\kanonに「kanon-setup-win.vbs」をダウンロード
  12. パスを設定
    SET PATH=%path%;C:\TracLight\Python\;C:\TracLight\Python\Scripts\;C:\TracLight\kanon\Bazaar;C:\TracLight\CollabNetSVN
  13. カレントディレクトリ変更
    cd /D c:\TracLight\kanon
  14. スクリプト実行
    cscript kanon-setup-win.vbs //NoLogo
この前のKanonの記事ではリソースのフォルダとか足りなくてエラーになると思います。その場合はTSVファイルから削除してみてください。
もしかしてbashのスクリプトのままでOkだったのかなぁ。

| | コメント (0) | トラックバック (0)

2013年11月24日 (日)

kanonをTrac1.0.1+MySQL対応に変更してみた

Tracをいろいろ使っていると、Excelと連携しなきゃいけないことってよくあります。そうなるとXMLRPCを使うってことになるんですが、遅いとか独自に追加したテーブルが見えないとか文句言われます。XMLRPCPluginを拡張してサーバサイドでSQL実行すればとか言っても、そんなこと無理みたいです。今のところ社内のネットにサーバをおいているので、MySQLのポートには普通に使えるから、DBをMySQLに変更すればいいんだからそうすれば?って言っても無理なんですよね。だいたいそんなことできるのは自分でTracインストールできる人に限られるわけで、一般的には難しいんだろうなぁと思っていたら、なぜか自分がやらなきゃいけなくなってしまいました。
ということでやってみたんですが、エラーチェックとかも入ってないし、あまり確認もしていません。
やったことは、
1. MySQL関連のパッケージのインストール
2. kanon-create-projectでSQLiteのコマンドのところをMySQLに切り替えられるように
3. テンプレートにあるレポートのSQLの関数がMySQLように変更
の三つですかね。
CentOSのインストールからプロジェクトの作成までの手順は
1. CentOS6.4をminimalでインストール
2. rootでログインして一般ユーザを追加する
# adduser ユーザ名
# password ユーザ名
3. 起動時にネットを有効になるようにする
# vi /etc/sysconfig/network-scripts/ifcfg-eth0
  onbootをyesに変更する。DHCPでないならいろいろ設定してください必要ならresolv.confとかも
4. ネット使えるように
# service network restart
必要ならProxyを設定してください
5. hgをインストールする
# yum -y install hg
6. そのほかをアップデートしておく
# yum -y update
7. 再起動する
# reboot
8. 一般ユーザでログインする(できればsshで)
9. bitbucketのリポジトリをcloneする
$ hg clone https://bitbucket.org/okamototk/kanonconductor
10. パッチを何等かの方法でコピーし適用する(viを起動して貼り付けるとか)
$ cd kanonconductor
$ hg import ../patch3.txt --user ユーザ名
$ su
# ./kanon-setup
y
n
n
11. 説明面倒なのでfirewallを止めてしまいます
# service iptables stop
# chkconfig iptables off
# service ip6tables stop
# chkconfig ip6tables off
12. ブラウザでSampleProjectに接続確認
13. MySQLの設定等
# service mysqld start
# chkconfig mysqld on
14. rootパスワードの設定
# mysql -u root -p
インストール直後はパスワードなしでEnter
mysql> set password for root@localhost=PASSWORD('パスワード');
mysql> quit
15. MySQL設定ファイルを編集
# vi /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
default-character-set=utf8
character-set-server = utf8
skip-character-set-client-handshake
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
16. MySQLサービスを再起動
# service mysqld start
17. MySQLプロジェクトの作成
/opt/kanon/bin/kanon-create-project test1 svn default n y パスワード
たぶんこれで大丈夫かなぁ。だめならコメントしてください。
diff -r 6f8292651be4 etc/opt/kanon/trac-template/agile/trac-init-mysql.sql
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/opt/kanon/trac-template/agile/trac-init-mysql.sql     Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,277 @@
+UPDATE enum SET name = '対応済' WHERE type ='resolution' AND value = '1';
+UPDATE enum SET name = '不正' WHERE type ='resolution' AND value = '2';
+UPDATE enum SET name = '対応しない' WHERE type ='resolution' AND value = '3';
+UPDATE enum SET name = '重複' WHERE type ='resolution' AND value = '4';
+UPDATE enum SET name = '再現しない' WHERE type ='resolution' AND value = '5';
+UPDATE enum SET name = '最重要' WHERE type ='priority' AND value = '1';
+UPDATE enum SET name = '重要' WHERE type ='priority' AND value = '2';
+UPDATE enum SET name = '普通' WHERE type ='priority' AND value = '3';
+UPDATE enum SET name = '低い' WHERE type ='priority' AND value = '4';
+UPDATE enum SET name = '最低' WHERE type ='priority' AND value = '5';
+UPDATE component SET name='チームA' WHERE name = 'component1';
+UPDATE component SET name='チームB' WHERE name = 'component2';
+UPDATE milestone SET name ='スプリント0(準備)', description='[/burndown?selected_milestone=スプリント0(準備) バーンダウンチャートへのリンク][[BR]]開発の準備の スプリント。開発環境の準備、全体のアーキテクチャデザイン、プロトタイプ検証などを実施' WHERE name = 'milestone1';
+UPDATE milestone SET name ='スプリント1', description='[/burndown?selected_milestone=スプリント1 バーンダウンチャートへのリンク][[BR]]スプリント1' WHERE name = 'milestone2';
+UPDATE milestone SET name ='スプリント3', description='[/burndown?selected_milestone=スプリント2 バーンダウンチャートへのリンク][[BR]]スプリント2' WHERE name = 'milestone3';
+UPDATE milestone SET name ='スプリント4(リリース準備)', description='[/burndown?selected_milestone=スプリント4(リリース準備) バーンダウンチャートへのリンク][[BR]]リリース準備のためのスプリント' WHERE name = 'milestone4';
+DELETE FROM version WHERE name = '1.0';
+DELETE FROM version WHERE name = '2.0';
+UPDATE enum SET name = 'ストーリー' WHERE type ='ticket_type' AND value = '1';
+UPDATE enum SET name = 'タスク' WHERE type ='ticket_type' AND value = '2';
+UPDATE enum SET name = 'バグ' WHERE type ='ticket_type' AND value = '3';
+INSERT INTO enum VALUES('ticket_type', '課題', '4');
+
+INSERT INTO session_attribute values ('admin', '1','name','管理太郎');
+INSERT INTO session_attribute values ('admin', '1','enabled','1');
+INSERT INTO session_attribute values ('guest', '1','name','客人開発者');
+INSERT INTO session_attribute values ('guest', '1','enabled','1');
+INSERT INTO session_attribute values ('leader', '1','name','頭春蔵');
+INSERT INTO session_attribute values ('leader', '1','enabled','1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'admin-penguin.png',0,0,'admin', 'Avatar','127.0.0.1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'guest-hamster.png',0,0,'guest', 'Avatar','127.0.0.1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'leader-cat.png',0,0,'leader', 'Avatar','127.0.0.1');
+
+
+INSERT INTO report VALUES(18,'trac',"バックログの確認",
+"SELECT
+  CASE tt.status WHEN 'closed' THEN 5 WHEN 'new' THEN 3 ELSE 1 END AS __color__,
+   (CASE tt.status
+      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;'
+      ELSE
+        (CASE tt.owner WHEN $USER THEN 'font-weight: bold' END)
+    END) AS __style__,
+  tt.milestone AS __group__,
+  t.id AS ticket,
+  '' AS 'ストーリー',
+  '[/ticket/'||tt.id||' #'||tt.id||']' AS description,
+  tt.summary AS 'タスク',
+  tt.owner AS '担当者',
+  tt.status AS '状態',
+  CASE WHEN st.child IS NULL THEN peh.value ELSE eh.value END AS '見積',
+  CASE WHEN st.child IS NULL THEN pth.value ELSE th.value END AS '作業時間',
+  '' AS 説明,
+  t.id AS _id
+FROM ticket t
+  LEFT JOIN subtickets st  ON st.parent =t.id AND t.type='ストーリー'
+  LEFT JOIN ticket tt ON tt.id=st.child
+  LEFT JOIN milestone m ON t.milestone = m.name
+  LEFT JOIN ticket_custom eh ON eh.ticket = tt.id AND eh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom th ON th.ticket = tt.id AND th.name = 'totalhours'
+  LEFT JOIN ticket_custom peh ON peh.ticket = t.id AND peh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom pth ON pth.ticket = t.id AND pth.name = 'totalhours'
+WHERE t.type='ストーリー'  AND st.child IS NOT NULL AND t.status <> 'closed'
+
+UNION
+
+SELECT
+  4 AS __color__,
+  'color: black; font-weight: bold;'  AS __style__,
+  t.milestone AS __group__,
+  t.id AS ticket,
+  t.summary AS 'ストーリー',
+  '',
+  '',
+  '',
+  '',
+  '',
+  '',
+  '[/newticket?type=タスク&parents='||t.id||'&milestone='||t.milestone||' タス ク作成]' AS  説明,
+  t.id AS _id
+FROM ticket t
+WHERE t.type='ストーリー' AND t.status<>'closed'
+
+UNION
+
+SELECT
+  CASE t.status WHEN 'closed' THEN 5 WHEN 'new' THEN 3 ELSE 1 END AS __color__,
+   (CASE t.status
+      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;'
+      ELSE
+        (CASE t.owner WHEN $USER THEN 'font-weight: bold' END)
+    END) AS __style__,
+  t.milestone AS __group__,
+  '-' AS ticket,
+  'その他:' ||t.type AS 'ストーリー',
+  '[/ticket/'||t.id||' #'||t.id||']' AS description,
+  t.summary as 'タスク',
+  t.owner AS '担当者',
+  t.status AS '状態',
+  eh.value AS '見積',
+  th.value AS '作業時間',
+  '',
+  NULL AS _id
+FROM ticket as t
+  LEFT JOIN ticket_custom eh ON eh.ticket = t.id AND eh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom th ON th.ticket = t.id AND th.name = 'totalhours'
+WHERE NOT t.type IN ('ストーリー')  AND
+  NOT EXISTS (SELECT * from subtickets WHERE subtickets.child = t.id)
+
+ORDER BY __group__ DESC, ticket, description","
+ * スプリント毎のストーリー(プロダクトバックログ)とタスク(スプリントバックログ)を確認することができます。
+ * 各スプリントのスプリントバックログ(タスク)の一覧を表示します。スプリントバックログはプロダクトバックログ(ストーリー)に紐づいている必要があります。
+ * 一番右のタスク作成をクリックすると、ストーリーに対応したタスクを作成することができます。
+ * クローズされたチケットはグレーで表示されます。
+");
+
+
+INSERT INTO report VALUES(19,'trac',"バックログの確認(チーム別)",
+"SELECT
+  CASE tt.status WHEN 'closed' THEN 5 WHEN 'new' THEN 3 ELSE 1 END AS __color__,
+   (CASE tt.status
+      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;'
+      ELSE
+        (CASE tt.owner WHEN $USER THEN 'font-weight: bold' END)
+    END) AS __style__,
+  tt.milestone AS __group__,
+  t.id AS id,
+  '' AS 'ストーリー',
+  tt.id AS ticket,
+  tt.summary AS 'タスク',
+  tt.owner AS '担当者',
+  tt.status AS '状態',
+  CASE WHEN st.child IS NULL THEN peh.value ELSE eh.value END AS '見積',
+  CASE WHEN st.child IS NULL THEN pth.value ELSE th.value END AS '作業時間',
+  '' AS description
+FROM ticket t
+  LEFT JOIN subtickets st  ON st.parent =t.id AND t.type='ストーリー'
+  LEFT JOIN ticket tt ON tt.id=st.child
+  LEFT JOIN milestone m ON t.milestone = m.name
+  LEFT JOIN ticket_custom eh ON eh.ticket = tt.id AND eh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom th ON th.ticket = tt.id AND th.name = 'totalhours'
+  LEFT JOIN ticket_custom peh ON peh.ticket = t.id AND peh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom pth ON pth.ticket = t.id AND pth.name = 'totalhours'
+WHERE t.component=$TEAM AND t.type='ストーリー'  AND st.child IS NOT NULL AND t.status <> 'closed'
+
+UNION
+
+SELECT
+  4 AS __color__,
+  'color: black; font-weight: bold;'  AS __style__,
+  t.milestone AS __group__,
+  t.id AS id,
+  t.summary AS 'ストーリー',
+  '',
+  '',
+  '',
+  '',
+  '',
+  '',
+  '[/newticket?type=タスク&parents='||t.id||'&milestone='||t.milestone||' タス ク作成]' AS  description
+FROM ticket t
+WHERE t.component=$TEAM AND t.type='ストーリー' AND t.status<>'closed'
+
+UNION
+
+SELECT
+  CASE t.status WHEN 'closed' THEN 5 WHEN 'new' THEN 3 ELSE 1 END AS __color__,
+   (CASE t.status
+      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;'
+      ELSE
+        (CASE t.owner WHEN $USER THEN 'font-weight: bold' END)
+    END) AS __style__,
+  t.milestone AS __group__,
+  '-' AS id,
+  'その他:' ||t.type AS 'ストーリー',
+  t.id AS ticket,
+  t.summary as 'タスク',
+  t.owner AS '担当者',
+  t.status AS '状態',
+  eh.value AS '見積',
+  th.value AS '作業時間',
+  ''
+FROM ticket as t
+  LEFT JOIN ticket_custom eh ON eh.ticket = t.id AND eh.name = 'estimatedhours'
+  LEFT JOIN ticket_custom th ON th.ticket = t.id AND th.name = 'totalhours'
+WHERE t.component=$TEAM AND NOT t.type IN ('ストーリー')  AND
+  NOT EXISTS (SELECT * from subtickets WHERE subtickets.child = t.id)
+
+ORDER BY __group__ DESC, id,ticket DESC","
+ * チーム別のスプリントのストーリー(プロダクトバックログ)とタスク(スプリントバ ックログ)を確認することができます。
+ * {{{[report:19?TEAM=チームA チームAのバックログ]}}}のように、レポートのリンク、もしくはURLの最後にTEAM変数でチーム名を指定して利用します。
+ * 各スプリントのスプリントバックログ(タスク)の一覧を表示します。スプリントバックログはプロダクトバックログ(ストーリー)に紐づいている必要があります。
+ * 一番右のタスク作成をクリックすると、ストーリーに対応したタスクを作成することができます。
+ * クローズされたチケットはグレーで表示されます。
+");
+
+
+
+INSERT INTO report VALUES(20,'trac',"スプリント計画・担当者割り当て","
+query:?status=accepted
+&
+status=assigned
+&
+status=new
+&
+status=reopened
+&
+type=タスク
+&
+group=milestone
+&
+col=id
+&
+col=parents
+&
+col=summary
+&
+col=type
+&
+col=priority
+&
+col=owner
+&
+col=estimatedhours
+&
+col=billable
+&
+col=status
+&
+order=priority,parents
+","
+このレポートはスプリント計画、担当者割り当てなどに利用します。
+
+バッチ編集機能により、'''チェックしたチケットを一括して'''スプリントに割り当て たり、担当者を割り当てることができます。
+
+ * プロダクトバックログは「集計に含める」をFalseに設定してください。
+ * スプリントバックログは「集計に含める」をTrueにしてバーンダウンチャートの工数に含めるようにします。
+ * スプリントバックログに対して見積時間を入力してください。
+");
+
+INSERT INTO report VALUES(21,'trac',"スプリント計画・プロダクトバックログの整理","
+query:?status=accepted
+&
+status=assigned
+&
+status=new
+&
+status=reopened
+&
+type=ストーリー゙
+&
+group=milestone
+&
+col=id
+&
+col=summary
+&
+col=status
+&
+col=type
+&
+col=owner
+&
+col=priority
+&
+col=estimatedhours
+&
+col=billable
+&
+order=priority
+","
+このレポートはスプリント計画時にスプリントで実行するプロダクトバックログを選択 するときに利用します。
+ * 次のスプリントで実施するチケットにチェックを入れて、バッチ編集でマイルストーンを設定してください。
+ * プロダクトバックログは「集計に含める」をFalseに設定してください。
+");
+
+
+
diff -r 6f8292651be4 etc/opt/kanon/trac-template/common/trac-1.0.ini
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/opt/kanon/trac-template/common/trac-1.0.ini   Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+
+[attachment]
+max_size = 262144
+render_unsafe_content = false
+
+[browser]
+downloadable_paths = *
+hide_properties = svk:merge
+
+[changeset]
+max_diff_bytes = 10000000
+max_diff_files = 0
+wiki_format_messages = true
+
+[header_logo]
+alt =
+height = -1
+link = /trac
+src=site/logo.png
+width = -1
+
+[logging]
+log_file = trac.log
+log_level = INFO
+log_type = file
+
+[mimeviewer]
+enscript_modes = text/x-dylan:dylan:4
+enscript_path = enscript
+max_preview_size = 262144
+mime_map = text/x-trac-wiki:wiki
+php_path = php
+silvercity_modes =
+tab_width = 8
+
+[notification]
+always_notify_owner = false
+always_notify_reporter = false
+always_notify_updater = true
+ambiguous_char_width = double
+mime_encoding = base64
+smtp_always_bcc =
+smtp_always_cc =
+smtp_default_domain =
+smtp_enabled = false
+smtp_from = trac@localhost
+smtp_password =
+smtp_port = 25
+smtp_replyto = trac@localhost
+smtp_server = localhost
+smtp_subject_prefix = __default__
+smtp_user =
+use_public_cc = false
+use_short_addr = false
+use_tls = false
+
+[search]
+min_query_length = 2
+
+[ticket]
+default_component =
+default_milestone =
+default_priority = 通常
+default_type = タスク
+default_version =
+restrict_owner = false
+
+[timeline]
+changeset_long_messages = false
+changeset_show_files = 0
+default_daysback = 30
+ticket_show_details = true
+
+[trac]
+authz_file = /etc/opt/kanon/svnauthz
+nbase_url =
+check_auth_ip = true
+database = sqlite:db/trac.db
+default_charset = cp932
+#default_handler = TagsWikiModule
+htdocs_location =
+ignore_auth_case = false
+mainnav = wiki,timeline,roadmap,browser,tickets,newticket,search
+metanav = login,logout,settings,help,about
+permission_store = DefaultPermissionStore
+permission_policies = PrivateWikiSystem, DefaultPermissionPolicy, LegacyAttachmentPolicy
+repository_type = svn
+request_filters = PageQueryModule
+templates_dir = /etc/opt/kanon/trac-template/default
+timeout = 20
+
+
+[wiki]
+ignore_missing_pages = true
+split_page_names = false
+
+[account-manager]
+htdigest_realm = kanon
+password_format = htdigest
+password_store = HtDigestStore
+password_file = /etc/opt/kanon/kanon_users.htdigest
+
+[components]
+tracext.git.* = enabled
+tracbzr.* = enabled
+tracext.hg.backend.csetpropertyrenderer = enabled
+tracext.hg.backend.hgdefaultpropertyrenderer = enabled
+tracext.hg.backend.hgextpropertyrenderer = enabled
+tracext.hg.backend.mercurialconnector = enabled
+datefield.filter.customfieldadmintweak = enabled
+datefield.filter.datefieldmodule = enabled
+iniadmin.iniadmin.iniadminplugin = enabled
+acct_mgr.* = enabled
+acct_mgr.admin.accountmanageradminpage = disabled
+addcomment.macro.addcommentmacro = enabled
+ganttcalendar.ticketgantt.* = enabled
+ganttcalendar.ticketcalendar.* = enabled
+ganttcalendar.complete_by_close.completeticketobserver = enabled
+ganttcalendar.ticketvalidator.ticketvalidator = enabled
+ganttcalendar.admin.* = enabled
+tracjsgantt.tracjsgantt.taacjsganttsupport = enabled
+tracjsgantt.tracjsgantt.tracjsganttchart = enabled
+tracjsgantt.tracjsgantt.tracjsganttsupport = enabled
+tracusermanager.* = enabled
+completeuser.web_ui.completeuserweb = enabled
+tracwysiwyg.* = enabled
+#hudsontracplus.* = enabled
+svnauthz.admin_ui.* = enabled
+advancedworkflow.controller.*  = enabled
+# batchmod.web_ui.* = enabled
+tracmsofficexml.* = enabled
+querychart.admin.adminpanel = enabled
+querychart.macro.macro = enabled
+querychart.model.ticketstatuslogmodelprovider = enabled
+reportinclude.macro.reportincludemacro = enabled
+reportinclude.web_ui.reportincludemodule = enabled
+xdocview.xdocview.xdocrenderer = enabled
+talm_importer.importer.importmodule = enabled
+tracautowikify.autowikify.autowikify = enabled
+graphviz.graphviz.graphviz = enabled
+tracsectionedit.web_ui.wikisectioneditmodule = enabled
+privatewiki.api.privatewikisystem = enabled
+newwikipagebutton.newwikipagebutton.newwikipagebuttonplugin = enabled
+workfloweditor.workfloweditor_admin.workflowchangehandler = enabled
+workfloweditor.workfloweditor_admin.workfloweditoradmin = enabled
+# ticket_clone.simpleticketclonebutton = enabled
+tracdragdrop.* = enabled
+tracopt.ticket.commit_updater.committicketreferencemacro = enabled
+tracopt.ticket.commit_updater.committicketupdater = enabled
+tracopt.ticket.deleter.ticketdeleter = enabled
+tracdiscussion.api.discussionapi = enabled
+tracdiscussion.init.discussioninit = enabled
+themeengine.* = enabled
+kanontheme.theme.kanontheme = enabled
+kanontheme.themewater.kanonwatertheme = enabled
+tracrpc.api.xmlrpcsystem = enabled
+tracrpc.json_rpc.jsonrpcprotocol = enabled
+tracrpc.search.searchrpc = enabled
+tracrpc.ticket.* = enabled
+tracrpc.web_ui.rpcweb = enabled
+tracrpc.wiki.wikirpc = enabled
+tracrpc.xml_rpc.xmlrpcprotocol = enabled
+customfieldadmin.api.customfields = enabled
+customfieldadmin.customfieldadmin.customfieldadminpage = enabled
+tracopt.mimeview.enscript.enscriptrenderer = enabled
+tracopt.mimeview.php.phprenderer = enabled
+tracopt.perm.authz_policy.authzpolicy = enabled
+tracopt.perm.config_perm_provider.extrapermissionsprovider = enabled
+tracopt.ticket.clone.ticketclonebutton = enabled
+tracopt.versioncontrol.git.git_fs.csetpropertyrenderer = enabled
+tracopt.versioncontrol.git.git_fs.gitconnector = enabled
+tracopt.versioncontrol.git.git_fs.gitwebprojectsrepositoryprovider = enabled
+tracopt.versioncontrol.svn.svn_fs.subversionconnector = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionmergepropertydiffrenderer = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionmergepropertyrenderer = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionpropertyrenderer = enabled
+
+[datefield]
+format = ymd
+
+[hudson]
+display_subprojects = true
+feed_url = http://localhost/hudson/rssAll
+main_page = http://localhost/hudson/
+
+[discussion]
+title=フォーラム
+
+[project]
+footer=Powerd by<br />Kanon<br /> Ver 0.1
+
+[theme]
+enable_css = enabled
+
+[notification]
+#TracLightningのパッチを当てないとchange.authorは有効にならない
+#ticket_subject_template = $prefix #$ticket.id: [$change.author] $summary
+ticket_subject_template = $prefix #$ticket.id: $summary
+
+[importer]
+datetime_format = %Y/%m/%d
+
+[trac-jsgantt]
+date_format = %Y/%m/%d
+fields.finish = due_close
+fields.parent = parents
+fields.percent = complete
+fields.start = due_assign
+
+[hudsonplus]
+display_in_new_tab = true
+hudson_url = http://localhost:8080/jenkins/
+navigation_label = ビルド
diff -r 6f8292651be4 etc/opt/kanon/trac-template/default/trac-init-mysql.sql
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/opt/kanon/trac-template/default/trac-init-mysql.sql   Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,65 @@
+UPDATE enum SET name = '対応済' WHERE type ='resolution' AND value = '1';
+UPDATE enum SET name = '不正' WHERE type ='resolution' AND value = '2';
+UPDATE enum SET name = '対応しない' WHERE type ='resolution' AND value = '3';
+UPDATE enum SET name = '重複' WHERE type ='resolution' AND value = '4';
+UPDATE enum SET name = '再現しない' WHERE type ='resolution' AND value = '5';
+UPDATE enum SET name = '今すぐ' WHERE type ='priority' AND value = '1';
+UPDATE enum SET name = '急いで' WHERE type ='priority' AND value = '2';
+UPDATE enum SET name = '高め' WHERE type ='priority' AND value = '3';
+UPDATE enum SET name = '通常' WHERE type ='priority' AND value = '4';
+UPDATE enum SET name = '低め' WHERE type ='priority' AND value = '5';
+UPDATE enum SET name = 'バグ' WHERE type ='ticket_type' AND value = '1';
+UPDATE enum SET name = '機能追加' WHERE type ='ticket_type' AND value = '2';
+UPDATE enum SET name = '仕様変更' WHERE type ='ticket_type' AND value = '3';
+UPDATE component SET name ='その他' WHERE name = 'component1';
+UPDATE component SET name ='ユーザ管理機能' WHERE name = 'component2';
+INSERT INTO component VALUES('検索機能', 'somebody', '');
+UPDATE milestone SET name ='要件定義完了', description='要件定義の完了' WHERE name = 'milestone1';
+UPDATE milestone SET name ='1.0αリリース', description='お客様の要件確認用のプロトタイプリリース。[[BR]][[QueryChart(query:?type=タスク,col:due_close,width:800)]]' WHERE name = 'milestone2';
+UPDATE milestone SET name ='1.0βリリース', description='機能試験終了後のお客様受け入れ試験用のリリース' WHERE name = 'milestone3';
+UPDATE milestone SET name ='1.0リリース', description='正式版リリース' WHERE name = 'milestone4';
+UPDATE version SET name ='1.0α' WHERE name = '1.0';
+UPDATE version SET name ='1.0β' WHERE name = '2.0';
+
+INSERT INTO enum VALUES('ticket_type', 'タスク', '4');
+INSERT INTO enum VALUES('ticket_type', '課題', '5');
+INSERT INTO enum VALUES('ticket_type', '連絡', '6');
+
+INSERT INTO session_attribute values ('admin', '1','name','管理太郎');
+INSERT INTO session_attribute values ('admin', '1','enabled','1');
+INSERT INTO session_attribute values ('guest', '1','name','客人開発者');
+INSERT INTO session_attribute values ('guest', '1','enabled','1');
+INSERT INTO session_attribute values ('leader', '1','name','頭春蔵');
+INSERT INTO session_attribute values ('leader', '1','enabled','1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'admin-penguin.png',0,0,'admin', 'Avatar','127.0.0.1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'guest-hamster.png',0,0,'guest', 'Avatar','127.0.0.1');
+INSERT INTO attachment values ('wiki', 'UserManagerPluginPictures', 'leader-cat.png',0,0,'leader', 'Avatar','127.0.0.1');
+
+INSERT INTO report VALUES('9','','未解決チケット(進捗確認用)',
+
+"SELECT owner AS __group__,
+   id AS ticket,
+   summary as '概要    ',
+   a.value as '開始日',
+   c.value as '終了日',
+   (CASE status WHEN 'assigned' THEN d.value||' *' ELSE d.value END) AS '達成率',
+   t.type AS 'タイプ ',
+   t.priority as '優先度',
+   changetime AS _changetime, description AS _description,
+   reporter AS _reporter,
+   (CASE  WHEN c.value ='' THEN 5
+          WHEN c.value < DATE_FORMAT(NOW(),'%Y/%m/%d') THEN 1
+          WHEN c.value < DATE_FORMAT(DATE_ADD(NOW(),interval 1 week),'%Y/%m/%d') THEN 2
+          ELSE 3 END) AS __color__
+  FROM ticket t
+  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
+  LEFT JOIN ticket_custom a ON a.ticket = t.id AND a.name = 'due_assign'
+  LEFT JOIN ticket_custom c ON c.ticket = t.id AND c.name = 'due_close'
+  LEFT JOIN ticket_custom d ON d.ticket = t.id AND d.name = 'complete'
+  WHERE status IN ('new', 'assigned', 'reopened')
+  ORDER BY owner, a.value, p.value, milestone, t.type, time","
+ * 担当者別に未解決チケットを表示します。
+ * 終了日順に表示し、終了日を過ぎたものは、赤で、終了日が1週間以内のものは黄色 で表示します。
+ * チケットに着手済みであれば、達成率に '*' が付与されます。
+ * 終了日が設定されていないものについては、青で表示します。
+");
diff -r 6f8292651be4 kanon-setup
--- a/kanon-setup       Tue Sep 03 02:25:18 2013 +0900
+++ b/kanon-setup       Sun Nov 24 00:00:48 2013 +0900
@@ -4,6 +4,7 @@
export KANON_OPT=/opt/kanon
export KANON_VAR=/var/opt/kanon
+export TRAC_VER=1.0
probe_debian() {
     if [ -f /etc/debian_version ]; then
@@ -103,6 +104,8 @@
if [ "$YN" = "y" ]
then
     JENKINS=y
+else
+    JENKINS=n
fi
@@ -226,7 +229,7 @@
else
     cp -frn etc/* /etc
fi
-
+cp etc/opt/kanon/trac-template/common/trac-1.0.ini /etc/opt/kanon/trac-template/common/trac.ini
### setup configuration
"$python" resource/virtualenv.py --distribute "$KANON_OPT"
@@ -272,19 +275,42 @@
     try_easy_install pygments
fi
-try_easy_install 'Genshi>=0.6,<0.7dev'
-try_easy_install 'Babel>=0.9.5,<1.0'
-try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-0.12.4.ja1.zip
-try_easy_install 'xlrd'
-try_easy_install 'xlwt'
-try_easy_install 'TracGit' https://github.com/hvr/trac-git-plugin/zipball/v0.12.0.5
-try_easy_install 'TracBzr'
-try_easy_install 'TracMercurial' http://svn.edgewall.org/repos/trac/plugins/0.12/mercurial-plugin
+if [ "$TRAC_VER" = "1.0" ]; then
+  try_easy_install 'Genshi'
+  try_easy_install 'Babel'
+#  try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-1.0.ja1.zip
+  try_easy_install 'Trac' http://download.edgewall.org/trac/Trac-1.0.1.zip
+  try_easy_install 'xlrd'
+  try_easy_install 'xlwt'
+#  try_easy_install 'TracGit' GitはTrac標準になった
+  try_easy_install 'TracBzr'
+#  try_easy_install 'TracMercurial' HGのpackageからインストールする
+else
+  try_easy_install 'Genshi>=0.6,<0.7dev'
+  try_easy_install 'Babel>=0.9.5,<1.0'
+  try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-0.12.4.ja1.zip
+  try_easy_install 'xlrd'
+  try_easy_install 'xlwt'
+  try_easy_install 'TracGit' https://github.com/hvr/trac-git-plugin/zipball/v0.12.0.5
+  try_easy_install 'TracBzr'
+  try_easy_install 'TracMercurial' http://svn.edgewall.org/repos/trac/plugins/0.12/mercurial-plugin
+fi
#resource/pluginsディレクトリのプラグインをインストール
pushd .
+if [ "$TRAC_VER" = "1.0" ]; then
+  RESOURCE_LIST=resource/trac-plugin-1.0
+  PLUGIN_LIST=pluginlist-1.0
+  PLUGIN_LIST_HG=pluginlist_hg-1.0
+  PLUGIN_LIST_GIT=pluginlist_git-1.0
+else
+  RESOURCE_LIST=resource/trac-plugin-0.12
+  PLUGIN_LIST=pluginlist
+  PLUGIN_LIST_HG=
+  PLUGIN_LIST_GIT=
+fi
-for i in `ls -1 resource/trac-plugins`; do
+for i in `cat $RESOURCE_LIST`; do
     rm -rf "resource/trac-plugins/$i/build" "resource/trac-plugins/$i/dist"
     if ! "$KANON_OPT/bin/easy_install" -Z --no-deps "resource/trac-plugins/$i"; then
         echo "*** STOP *** インストールに失敗しました: resource/trac-plugins/$i"
@@ -293,7 +319,7 @@
done
# install plugins from web site
-for i in `cat pluginlist`; do
+for i in `cat $PLUGIN_LIST`; do
     workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
     svn co -q $i $workdir
     if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
@@ -302,6 +328,30 @@
     fi
     rm -fr "$workdir"
done
+if [ "$PLUGIN_LIST_HG" != "" ]
+then
+  for i in `cat $PLUGIN_LIST_HG`; do
+      workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
+      hg clone $i $workdir
+      if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
+          echo "*** STOP *** インストールに失敗しました: $i"
+          exit 1
+      fi
+      rm -fr "$workdir"
+  done
+fi
+if [ "$PLUGIN_LIST_GIT" != "" ]
+then
+  for i in `cat $PLUGIN_LIST_GIT`; do
+      workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
+      git clone $i $workdir
+      if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
+          echo "*** STOP *** インストールに失敗しました: $i"
+          exit 1
+      fi
+      rm -fr "$workdir"
+  done
+fi
popd
@@ -363,7 +413,7 @@
# setup SampleProject
if [ ! -d "$KANON_VAR/trac/SampleProject" ]
then
-    "$KANON_OPT/bin/kanon-create-project" SampleProject svn default y
+    "$KANON_OPT/bin/kanon-create-project" SampleProject svn default $JENKINS
     svn import SampleProject file://$KANON_VAR/svn/SampleProject/ -m "initial import."
     chown $APACHE_USER.$APACHE_USER -R $KANON_VAR/svn/SampleProject
fi
diff -r 6f8292651be4 opt/kanon/bin/kanon-create-project
--- a/opt/kanon/bin/kanon-create-project        Tue Sep 03 02:25:18 2013 +0900
+++ b/opt/kanon/bin/kanon-create-project        Sun Nov 24 00:00:48 2013 +0900
@@ -10,6 +10,12 @@
REPO_TYPE=$2
TEMPLATE=$3
JENKINS=$4
+MYSQL=$5
+DBROOTUSER=root
+DBROOTPW=$6
+DBNAME=trac_$1
+DBUSERNAME=user_$1
+DBUSERPW=`mkpasswd -s 0`
export PYTHONPATH=$KANON_HOME/lib/python2.6:$KANON_HOME/lib/python2.6/site-packages
export PATH=$KANON_HOME/bin:$PATH
@@ -201,14 +207,26 @@
    exit
fi
+# DATABASE作成
+if [ "$MYSQL" = 'y' ]
+then
+   mysql -u $DBROOTUSER --password=$DBROOTPW -e "DROP DATABASE IF EXISTS $DBNAME;"
+   mysql -u $DBROOTUSER --password=$DBROOTPW -e "create database $DBNAME DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;"
+   mysql -u $DBROOTUSER --password=$DBROOTPW -e "create user $DBUSERNAME IDENTIFIED BY '$DBUSERPW';"
+   mysql -u $DBROOTUSER --password=$DBROOTPW -e "GRANT ALL ON $DBNAME.* TO $DBUSERNAME@localhost IDENTIFIED BY '$DBUSERPW';"
+   mysql -u $DBROOTUSER --password=$DBROOTPW -e "GRANT ALL ON $DBNAME.* TO $DBUSERNAME@'%' IDENTIFIED BY '$DBUSERPW';"
+   DB_CONNECT_STR="mysql://$DBUSERNAME:$DBUSERPW@localhost/$DBNAME"
+else
+   DB_CONNECT_STR=sqlite:db/trac.db
+fi
# trac作成
TRAC_PROJECT_PATH=$KANON_VAR/trac/$PROJECT_NAME
if [ "$REPO_TYPE" != '' ]
then
-   trac-admin $TRAC_PROJECT_PATH initenv $PROJECT_NAME sqlite:db/trac.db $REPO_TYPE /var/opt/kanon/$REPO_TYPE/$PROJECT_NAME
+   trac-admin $TRAC_PROJECT_PATH initenv $PROJECT_NAME $DB_CONNECT_STR $REPO_TYPE /var/opt/kanon/$REPO_TYPE/$PROJECT_NAME
else
-   trac-admin $TRAC_PROJECT_PATH initenv $PROJECT_NAME sqlite:db/trac.db
+   trac-admin $TRAC_PROJECT_PATH initenv $PROJECT_NAME $DB_CONNECT_STR
fi
cat "$KANON_CONFIG"/trac-template/"$TEMPLATE"/trac-ticketcustom.ini >> \
@@ -223,7 +241,12 @@
if [ -f "$KANON_CONFIG/trac-template/$TEMPLATE/trac-init.sql" ]
then
     # setup database
-    sqlite3   -init $KANON_CONFIG/trac-template/$TEMPLATE/trac-init.sql $KANON_VAR/trac/$PROJECT_NAME/db/trac.db .quit
+    if [ "$MYSQL" = 'y' ]
+    then
+        mysql -u $DBUSERNAME --password=$DBUSERPW $DBNAME < $KANON_CONFIG/trac-template/$TEMPLATE/trac-init-mysql.sql
+    else
+        sqlite3   -init $KANON_CONFIG/trac-template/$TEMPLATE/trac-init.sql $KANON_VAR/trac/$PROJECT_NAME/db/trac.db .quit
+    fi
fi
if [ -d "$KANON_CONFIG/trac-template/$TEMPLATE/trac-site" ]
@@ -309,10 +332,17 @@
trac-admin $TRAC_PROJECT_PATH permission add leader MILESTONE_CREATE MILESTONE_MODIFY TICKET_ADMIN
trac-admin $TRAC_PROJECT_PATH permission add guest developer
-sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('admin', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/admin-penguin.png');"
-sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('guest', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/guest-hamster.png');"
-sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('leader', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/leader-cat.png');"
+if [ "$MYSQL" = 'y' ]
+then
+   mysql -u $DBUSERNAME --password=$DBUSERPW $DBNAME -e"INSERT INTO session_attribute values ('admin', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/admin-penguin.png');"
+   mysql -u $DBUSERNAME --password=$DBUSERPW $DBNAME -e"INSERT INTO session_attribute values ('guest', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/guest-hamster.png');"
+   mysql -u $DBUSERNAME --password=$DBUSERPW $DBNAME -e"INSERT INTO session_attribute values ('leader', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/leader-cat.png');"
+else
+   sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('admin', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/admin-penguin.png');"
+   sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('guest', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/guest-hamster.png');"
+   sqlite3 $TRAC_PROJECT_PATH/db/trac.db "INSERT INTO session_attribute values ('leader', '1','picture_href','/trac/"$PROJECT_NAME"/raw-attachment/wiki/UserManagerPluginPictures/leader-cat.png');"
+fi
chown $APACHE_USER.$APACHE_USER -R $TRAC_PROJECT_PATH
echo "
diff -r 6f8292651be4 pkglist.rhel6
--- a/pkglist.rhel6     Tue Sep 03 02:25:18 2013 +0900
+++ b/pkglist.rhel6     Sun Nov 24 00:00:48 2013 +0900
@@ -9,3 +9,6 @@
bzr
gcc
git
+mysql-server
+MySQL-python
+expect
diff -r 6f8292651be4 pluginlist-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/pluginlist-1.0    Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,17 @@
+http://svn.sourceforge.jp/svnroot/shibuya-trac/sandbox/okamototk/tracsubticketsplugin
+http://trac-hacks.org/svn/iniadminplugin/0.11
+http://trac-hacks.org/svn/xmlrpcplugin/trunk
+http://trac-hacks.org/svn/customfieldadminplugin/0.11
+http://trac-hacks.org/svn/tracdragdropplugin/0.12
+http://trac-hacks.org/svn/tracwysiwygplugin/0.12
+http://trac-hacks.org/svn/exceldownloadplugin/0.12
+http://trac-hacks.org/svn/tocmacro/0.11
+http://trac-hacks.org/svn/macropostplugin/0.11
+http://trac-hacks.org/svn/addcommentmacro/0.11
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/ganttcalendarplugin/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/completeuserplugin/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/reportincludeplugin/trunk/0.12
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/trackanontheme/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/hudsontracplus/0.11
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/tracavatarplugin/branches/0.12-kanon
+http://trac-hacks.org/svn/themeengineplugin/trunk/
diff -r 6f8292651be4 pluginlist_hg-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/pluginlist_hg-1.0 Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,1 @@
+http://hg.edgewall.org/trac/mercurial-plugin#1.0
diff -r 6f8292651be4 resource/trac-plugin-0.12
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/resource/trac-plugin-0.12 Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,27 @@
+TracMacOSTheme
+acct_mgr
+advancedticketworkflow
+autowikifyplugin
+batchmodify
+datefield
+discussion
+hudsontracplus
+lightningtheme
+macropost
+mailarchiveplugin
+masterticketsplugin
+privatewikiplugin
+querychart
+searchhyperestraier
+sectioneditplugin
+svnauthzadminplugin
+themeengineplugin
+ticketclone
+ticketimportplugin
+timingandestimationplugin
+tracjsganttplugin
+tracnav
+tractags
+usermanager
+workfloweditorplugin
+xdocview
diff -r 6f8292651be4 resource/trac-plugin-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/resource/trac-plugin-1.0  Sun Nov 24 00:00:48 2013 +0900
@@ -0,0 +1,23 @@
+TracMacOSTheme
+acct_mgr
+advancedticketworkflow
+autowikifyplugin
+datefield
+discussion
+hudsontracplus
+lightningtheme
+mailarchiveplugin
+masterticketsplugin
+privatewikiplugin
+querychart
+searchhyperestraier
+sectioneditplugin
+svnauthzadminplugin
+ticketimportplugin
+timingandestimationplugin
+tracjsganttplugin
+tracnav
+tractags
+usermanager
+workfloweditorplugin
+xdocview

| | コメント (0) | トラックバック (0)

2013年11月11日 (月)

kanonをTrac1.0.1対応に変更してみた

Tracを利用して社内システムを構築しようということになり、どうせならTracのバージョンを最新にあげたいなぁということでやってみました。確認したのはCentOS6.4のminimalインストールの状態から新規インストールしてSampleProjectを使ってみて問題がないかの確認だけです。超危険なので参考ということで…

やった内容は
  1. kanon-setupの修正
    • ネット上のリポジトリにSVNだけでなくGit,Hgを使えるように修正
    • Genshiのバージョン指定を外す
    • Babelのバージョン指定を外す
    • Tracは1.0.1を指定する
    • TracGitは標準になったのでインストールしない
    • TracMercurialはedgewallのhgから持ってくる
  2. resourceフォルダのplugin
    • Batchmodifyは標準になったのでインストールしない
    • Macropostはtrac-hacksのリポジトリから持ってくる
    • Themeengineリポジトリからインストール
    • ticketcloneは標準なのでインストールしない
  3. pluginlistにあるもの
    • tracsteinschartは動かなかったのでインストールしないことにした
  4. common/trac.iniのComponentから削除
    • ticket_clone.simpleticketclonebutton
    • batchmod.web_ui.*
  5. common/trac.iniのComponentに追加
    • tracopt.mimeview.enscript.enscriptrenderer = enabled
    • tracopt.mimeview.php.phprenderer = enabled
    • tracopt.perm.authz_policy.authzpolicy = enabled
    • tracopt.perm.config_perm_provider.extrapermissionsprovider = enabled
    • tracopt.ticket.clone.ticketclonebutton = enabled
    • tracopt.versioncontrol.git.git_fs.csetpropertyrenderer = enabled
    • tracopt.versioncontrol.git.git_fs.gitconnector = enabled
    • tracopt.versioncontrol.git.git_fs.gitwebprojectsrepositoryprovider = enabled
    • tracopt.versioncontrol.svn.svn_fs.subversionconnector = enabled
    • tracopt.versioncontrol.svn.svn_prop.subversionmergepropertydiffrenderer = enabled
    • tracopt.versioncontrol.svn.svn_prop.subversionmergepropertyrenderer = enabled
    • tracopt.versioncontrol.svn.svn_prop.subversionpropertyrenderer = enabled
パッチ
diff -r 6f8292651be4 etc/opt/kanon/trac-template/common/trac-1.0.ini
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/opt/kanon/trac-template/common/trac-1.0.ini   Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+
+[attachment]
+max_size = 262144
+render_unsafe_content = false
+
+[browser]
+downloadable_paths = *
+hide_properties = svk:merge
+
+[changeset]
+max_diff_bytes = 10000000
+max_diff_files = 0
+wiki_format_messages = true
+
+[header_logo]
+alt =
+height = -1
+link = /trac
+src=site/logo.png
+width = -1
+
+[logging]
+log_file = trac.log
+log_level = INFO
+log_type = file
+
+[mimeviewer]
+enscript_modes = text/x-dylan:dylan:4
+enscript_path = enscript
+max_preview_size = 262144
+mime_map = text/x-trac-wiki:wiki
+php_path = php
+silvercity_modes =
+tab_width = 8
+
+[notification]
+always_notify_owner = false
+always_notify_reporter = false
+always_notify_updater = true
+ambiguous_char_width = double
+mime_encoding = base64
+smtp_always_bcc =
+smtp_always_cc =
+smtp_default_domain =
+smtp_enabled = false
+smtp_from = trac@localhost
+smtp_password =
+smtp_port = 25
+smtp_replyto = trac@localhost
+smtp_server = localhost
+smtp_subject_prefix = __default__
+smtp_user =
+use_public_cc = false
+use_short_addr = false
+use_tls = false
+
+[search]
+min_query_length = 2
+
+[ticket]
+default_component =
+default_milestone =
+default_priority = 通常
+default_type = タスク
+default_version =
+restrict_owner = false
+
+[timeline]
+changeset_long_messages = false
+changeset_show_files = 0
+default_daysback = 30
+ticket_show_details = true
+
+[trac]
+authz_file = /etc/opt/kanon/svnauthz
+nbase_url =
+check_auth_ip = true
+database = sqlite:db/trac.db
+default_charset = cp932
+#default_handler = TagsWikiModule
+htdocs_location =
+ignore_auth_case = false
+mainnav = wiki,timeline,roadmap,browser,tickets,newticket,search
+metanav = login,logout,settings,help,about
+permission_store = DefaultPermissionStore
+permission_policies = PrivateWikiSystem, DefaultPermissionPolicy, LegacyAttachmentPolicy
+repository_type = svn
+request_filters = PageQueryModule
+templates_dir = /etc/opt/kanon/trac-template/default
+timeout = 20
+
+
+[wiki]
+ignore_missing_pages = true
+split_page_names = false
+
+[account-manager]
+htdigest_realm = kanon
+password_format = htdigest
+password_store = HtDigestStore
+password_file = /etc/opt/kanon/kanon_users.htdigest
+
+[components]
+tracext.git.* = enabled
+tracbzr.* = enabled
+tracext.hg.backend.csetpropertyrenderer = enabled
+tracext.hg.backend.hgdefaultpropertyrenderer = enabled
+tracext.hg.backend.hgextpropertyrenderer = enabled
+tracext.hg.backend.mercurialconnector = enabled
+datefield.filter.customfieldadmintweak = enabled
+datefield.filter.datefieldmodule = enabled
+iniadmin.iniadmin.iniadminplugin = enabled
+acct_mgr.* = enabled
+acct_mgr.admin.accountmanageradminpage = disabled
+addcomment.macro.addcommentmacro = enabled
+ganttcalendar.ticketgantt.* = enabled
+ganttcalendar.ticketcalendar.* = enabled
+ganttcalendar.complete_by_close.completeticketobserver = enabled
+ganttcalendar.ticketvalidator.ticketvalidator = enabled
+ganttcalendar.admin.* = enabled
+tracjsgantt.tracjsgantt.taacjsganttsupport = enabled
+tracjsgantt.tracjsgantt.tracjsganttchart = enabled
+tracjsgantt.tracjsgantt.tracjsganttsupport = enabled
+tracusermanager.* = enabled
+completeuser.web_ui.completeuserweb = enabled
+tracwysiwyg.* = enabled
+#hudsontracplus.* = enabled
+svnauthz.admin_ui.* = enabled
+advancedworkflow.controller.*  = enabled
+# batchmod.web_ui.* = enabled
+tracmsofficexml.* = enabled
+querychart.admin.adminpanel = enabled
+querychart.macro.macro = enabled
+querychart.model.ticketstatuslogmodelprovider = enabled
+reportinclude.macro.reportincludemacro = enabled
+reportinclude.web_ui.reportincludemodule = enabled
+xdocview.xdocview.xdocrenderer = enabled
+talm_importer.importer.importmodule = enabled
+tracautowikify.autowikify.autowikify = enabled
+graphviz.graphviz.graphviz = enabled
+tracsectionedit.web_ui.wikisectioneditmodule = enabled
+privatewiki.api.privatewikisystem = enabled
+newwikipagebutton.newwikipagebutton.newwikipagebuttonplugin = enabled
+workfloweditor.workfloweditor_admin.workflowchangehandler = enabled
+workfloweditor.workfloweditor_admin.workfloweditoradmin = enabled
+# ticket_clone.simpleticketclonebutton = enabled
+tracdragdrop.* = enabled
+tracopt.ticket.commit_updater.committicketreferencemacro = enabled
+tracopt.ticket.commit_updater.committicketupdater = enabled
+tracopt.ticket.deleter.ticketdeleter = enabled
+tracdiscussion.api.discussionapi = enabled
+tracdiscussion.init.discussioninit = enabled
+themeengine.* = enabled
+kanontheme.theme.kanontheme = enabled
+kanontheme.themewater.kanonwatertheme = enabled
+tracrpc.api.xmlrpcsystem = enabled
+tracrpc.json_rpc.jsonrpcprotocol = enabled
+tracrpc.search.searchrpc = enabled
+tracrpc.ticket.* = enabled
+tracrpc.web_ui.rpcweb = enabled
+tracrpc.wiki.wikirpc = enabled
+tracrpc.xml_rpc.xmlrpcprotocol = enabled
+customfieldadmin.api.customfields = enabled
+customfieldadmin.customfieldadmin.customfieldadminpage = enabled
+tracopt.mimeview.enscript.enscriptrenderer = enabled
+tracopt.mimeview.php.phprenderer = enabled
+tracopt.perm.authz_policy.authzpolicy = enabled
+tracopt.perm.config_perm_provider.extrapermissionsprovider = enabled
+tracopt.ticket.clone.ticketclonebutton = enabled
+tracopt.versioncontrol.git.git_fs.csetpropertyrenderer = enabled
+tracopt.versioncontrol.git.git_fs.gitconnector = enabled
+tracopt.versioncontrol.git.git_fs.gitwebprojectsrepositoryprovider = enabled
+tracopt.versioncontrol.svn.svn_fs.subversionconnector = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionmergepropertydiffrenderer = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionmergepropertyrenderer = enabled
+tracopt.versioncontrol.svn.svn_prop.subversionpropertyrenderer = enabled
+
+[datefield]
+format = ymd
+
+[hudson]
+display_subprojects = true
+feed_url = http://localhost/hudson/rssAll
+main_page = http://localhost/hudson/
+
+[discussion]
+title=フォーラム
+
+[project]
+footer=Powerd by<br />Kanon<br /> Ver 0.1
+
+[theme]
+enable_css = enabled
+
+[notification]
+#TracLightningのパッチを当てないとchange.authorは有効にならない
+#ticket_subject_template = $prefix #$ticket.id: [$change.author] $summary
+ticket_subject_template = $prefix #$ticket.id: $summary
+
+[importer]
+datetime_format = %Y/%m/%d
+
+[trac-jsgantt]
+date_format = %Y/%m/%d
+fields.finish = due_close
+fields.parent = parents
+fields.percent = complete
+fields.start = due_assign
+
+[hudsonplus]
+display_in_new_tab = true
+hudson_url = http://localhost:8080/jenkins/
+navigation_label = ビルド
diff -r 6f8292651be4 kanon-setup
--- a/kanon-setup       Tue Sep 03 02:25:18 2013 +0900
+++ b/kanon-setup       Mon Nov 11 23:08:37 2013 +0900
@@ -4,6 +4,7 @@
export KANON_OPT=/opt/kanon
export KANON_VAR=/var/opt/kanon
+export TRAC_VER=1.0
probe_debian() {
     if [ -f /etc/debian_version ]; then
@@ -226,7 +227,7 @@
else
     cp -frn etc/* /etc
fi
-
+cp etc/opt/kanon/trac-template/common/trac-1.0.ini /etc/opt/kanon/trac-template/common/trac.ini
### setup configuration
"$python" resource/virtualenv.py --distribute "$KANON_OPT"
@@ -272,19 +273,42 @@
     try_easy_install pygments
fi
-try_easy_install 'Genshi>=0.6,<0.7dev'
-try_easy_install 'Babel>=0.9.5,<1.0'
-try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-0.12.4.ja1.zip
-try_easy_install 'xlrd'
-try_easy_install 'xlwt'
-try_easy_install 'TracGit' https://github.com/hvr/trac-git-plugin/zipball/v0.12.0.5
-try_easy_install 'TracBzr'
-try_easy_install 'TracMercurial' http://svn.edgewall.org/repos/trac/plugins/0.12/mercurial-plugin
+if [ "$TRAC_VER" = "1.0" ]; then
+  try_easy_install 'Genshi'
+  try_easy_install 'Babel'
+#  try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-1.0.ja1.zip
+  try_easy_install 'Trac' http://download.edgewall.org/trac/Trac-1.0.1.zip
+  try_easy_install 'xlrd'
+  try_easy_install 'xlwt'
+#  try_easy_install 'TracGit' GitはTrac標準になった
+  try_easy_install 'TracBzr'
+#  try_easy_install 'TracMercurial' HGのpackageからインストールする
+else
+  try_easy_install 'Genshi>=0.6,<0.7dev'
+  try_easy_install 'Babel>=0.9.5,<1.0'
+  try_easy_install 'Trac' http://www.i-act.co.jp/project/products/downloads/Trac-0.12.4.ja1.zip
+  try_easy_install 'xlrd'
+  try_easy_install 'xlwt'
+  try_easy_install 'TracGit' https://github.com/hvr/trac-git-plugin/zipball/v0.12.0.5
+  try_easy_install 'TracBzr'
+  try_easy_install 'TracMercurial' http://svn.edgewall.org/repos/trac/plugins/0.12/mercurial-plugin
+fi
#resource/pluginsディレクトリのプラグインをインストール
pushd .
+if [ "$TRAC_VER" = "1.0" ]; then
+  RESOURCE_LIST=resource/trac-plugin-1.0
+  PLUGIN_LIST=pluginlist-1.0
+  PLUGIN_LIST_HG=pluginlist_hg-1.0
+  PLUGIN_LIST_GIT=pluginlist_git-1.0
+else
+  RESOURCE_LIST=resource/trac-plugin-0.12
+  PLUGIN_LIST=pluginlist
+  PLUGIN_LIST_HG=
+  PLUGIN_LIST_GIT=
+fi
-for i in `ls -1 resource/trac-plugins`; do
+for i in `cat $RESOURCE_LIST`; do
     rm -rf "resource/trac-plugins/$i/build" "resource/trac-plugins/$i/dist"
     if ! "$KANON_OPT/bin/easy_install" -Z --no-deps "resource/trac-plugins/$i"; then
         echo "*** STOP *** インストールに失敗しました: resource/trac-plugins/$i"
@@ -293,7 +317,7 @@
done
# install plugins from web site
-for i in `cat pluginlist`; do
+for i in `cat $PLUGIN_LIST`; do
     workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
     svn co -q $i $workdir
     if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
@@ -302,6 +326,30 @@
     fi
     rm -fr "$workdir"
done
+if [ "$PLUGIN_LIST_HG" != "" ]
+then
+  for i in `cat $PLUGIN_LIST_HG`; do
+      workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
+      hg clone $i $workdir
+      if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
+          echo "*** STOP *** インストールに失敗しました: $i"
+          exit 1
+      fi
+      rm -fr "$workdir"
+  done
+fi
+if [ "$PLUGIN_LIST_GIT" != "" ]
+then
+  for i in `cat $PLUGIN_LIST_GIT`; do
+      workdir=`mktemp -d /tmp/kanon_build.XXXXXXXX` || exit 1
+      git clone $i $workdir
+      if ! "$KANON_OPT/bin/easy_install" -Z $workdir; then
+          echo "*** STOP *** インストールに失敗しました: $i"
+          exit 1
+      fi
+      rm -fr "$workdir"
+  done
+fi
popd
diff -r 6f8292651be4 opt/kanon/bin/create-sample-project
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/opt/kanon/bin/create-sample-project       Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,14 @@
+#!/bin/bash
+svnadmin create /var/opt/kanon/svn/Sample
+cp /etc/opt/kanon/vcs-template/svn/* /var/opt/kanon/svn/Sample/hooks
+chown apache.apache -R /var/opt/kanon/svn/Sample
+# /opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample initenv Sample sqlite:db/trac.db svn /var/opt/kanon/svn/Sample
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample initenv Sample sqlite:db/trac.db
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add anonymous DISCUSSION_VIEW
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add authenticated DISCUSSION_APPEND TICKET_EDIT_CC XML_RPC
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add admin TRAC_ADMIN
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add developer authenticated
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add leader MILESTONE_CREATE MILESTONE_MODIFY TICKET_ADMIN
+/opt/kanon/bin/trac-admin /var/opt/kanon/trac/Sample permission add guest developer
+chown apache.apache -R /var/opt/kanon/trac/Sample
+
diff -r 6f8292651be4 pluginlist-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/pluginlist-1.0    Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,17 @@
+http://svn.sourceforge.jp/svnroot/shibuya-trac/sandbox/okamototk/tracsubticketsplugin
+http://trac-hacks.org/svn/iniadminplugin/0.11
+http://trac-hacks.org/svn/xmlrpcplugin/trunk
+http://trac-hacks.org/svn/customfieldadminplugin/0.11
+http://trac-hacks.org/svn/tracdragdropplugin/0.12
+http://trac-hacks.org/svn/tracwysiwygplugin/0.12
+http://trac-hacks.org/svn/exceldownloadplugin/0.12
+http://trac-hacks.org/svn/tocmacro/0.11
+http://trac-hacks.org/svn/macropostplugin/0.11
+http://trac-hacks.org/svn/addcommentmacro/0.11
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/ganttcalendarplugin/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/completeuserplugin/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/reportincludeplugin/trunk/0.12
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/trackanontheme/trunk
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/hudsontracplus/0.11
+http://svn.sourceforge.jp/svnroot/shibuya-trac/plugins/tracavatarplugin/branches/0.12-kanon
+http://trac-hacks.org/svn/themeengineplugin/trunk/
diff -r 6f8292651be4 pluginlist_hg-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/pluginlist_hg-1.0 Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,1 @@
+http://hg.edgewall.org/trac/mercurial-plugin#1.0
diff -r 6f8292651be4 resource/trac-plugin-0.12
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/resource/trac-plugin-0.12 Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,27 @@
+TracMacOSTheme
+acct_mgr
+advancedticketworkflow
+autowikifyplugin
+batchmodify
+datefield
+discussion
+hudsontracplus
+lightningtheme
+macropost
+mailarchiveplugin
+masterticketsplugin
+privatewikiplugin
+querychart
+searchhyperestraier
+sectioneditplugin
+svnauthzadminplugin
+themeengineplugin
+ticketclone
+ticketimportplugin
+timingandestimationplugin
+tracjsganttplugin
+tracnav
+tractags
+usermanager
+workfloweditorplugin
+xdocview
diff -r 6f8292651be4 resource/trac-plugin-1.0
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/resource/trac-plugin-1.0  Mon Nov 11 23:08:37 2013 +0900
@@ -0,0 +1,23 @@
+TracMacOSTheme
+acct_mgr
+advancedticketworkflow
+autowikifyplugin
+datefield
+discussion
+hudsontracplus
+lightningtheme
+mailarchiveplugin
+masterticketsplugin
+privatewikiplugin
+querychart
+searchhyperestraier
+sectioneditplugin
+svnauthzadminplugin
+ticketimportplugin
+timingandestimationplugin
+tracjsganttplugin
+tracnav
+tractags
+usermanager
+workfloweditorplugin
+xdocview
今日はここまでということで

| | コメント (0) | トラックバック (0)

2012年1月22日 (日)

メールで受け取ったファイルをSVNに登録する

まぁあんまり困る人はいないでしょうが、社外から社内へのアクセス方法がほぼメールだけとかに制限されている会社ってありますよね。メールで受信したファイルをSVNに自動で登録する仕組みを作ってみました。いつものように超プロトタイプレベルですので、自分で何とかできる人だけ参考にしてみて下さい。

大体受け取るメールというのはこういった形のものを想定しています。

TO:特別に用意したアドレスがおすすめ

SUB:svn-commit)120120進捗報告資料を送信します。

BODY: ProjectName:SampleProject

Path:/120120

Comment:refs #1 進捗報告資料を送信します。

メールのタイトルの先頭に"svn-commit"が入っていて、メールの本文にリポジトリの情報を引き出すためのプロジェクト名「ProjectName:」が指定していて,リポジトリ内の保存先のフォルダを「Path:」で指定し、,「Comment:」でコミット時のメッセージを書かいてあり、一つ以上の添付ファイルがついているメールに対しての未処理を行います。プロジェクト名とかメールアドレスはInitTracUsersで、プロジェクト名はInitTracReposで設定されているものである必要があり、リポジトリはチェックアウト済でなければなりません。 また、保存先に指定するパスは最低限1階層は指定してください。(svn addで面倒なことになるので、そうしてます)

ファイルとしては次の三つです
1. SVNUpdate.bas

「SVNUpdate.bas」をダウンロード

2. SVNClient.cls

「SVNClient.cls」をダウンロード

3. CommandLauncher.cls

「CommandLauncher.cls」をダウンロード

さすがにOutlookなので、ソースをそのまま貼り付けてもうれしい人もいないと思うのでファイルにしてますので、それをダウンロードしてインポートしてください。

その他の修正箇所ですが、まずは、
ThisOutlookSessionのApplication_NewMailExを次のようにしてください。

'メールを受信したとき
Private Sub Application_NewMailEx(ByVal EntryIDCollection As String)
    Dim col As Variant
    InitTracUsers
    InitTracRepos

    '複数受信したときはカンマ区切りでメールのIDが入ってくる
    If InStr(1, EntryIDCollection, ",") = 0 Then
        Set col = New Collection
        col.Add EntryIDCollection
    Else
        col = Split(EntryIDCollection, ",")
    End If
    For Each msgId In col
        受信したメールの処理 msgId
    Next
End Sub

次はSVNUpdateのInitTracUsersとInitTracReposを適切に書き換えてください。

Public Sub InitTracUsers()
    Dim c As Collection
    Set m_TracUsers = New Collection
    Set c = New Collection
    c.Add "admin", "name"
    c.Add "admin", "password"
    m_TracUsers.Add c, "admin@example.com"
    Set c = New Collection
    c.Add "admin2", "name"
    c.Add "admin2", "password"
    m_TracUsers.Add c, "admin2@example.com"
End Sub

Public Sub InitTracRepos()
    Dim c As Collection
    Set m_TracRepos = New Collection
    Set c = New Collection
    c.Add "D:\SVN\SampleProject", "WorkingCopy"
    c.Add "http://localhost/svn/SampleProject", "URL"
    m_TracRepos.Add c, "SampleProject"
End Sub

あとは、SVNUpdateのsvn_commitの中にSVNのファイル名が書いてありますのでそこを書き換えておいてください

    Dim colUser As Collection
    Set colUser = m_TracUsers.Item(oMsg.SenderEmailAddress)
    m_svn.init "c:\TracLight\CollabNetSVN\svn.exe", colUser.Item("name"), colUser.Item("password")
    m_svn.Update svnRoot

次は上で書いたようにリポジトリをチェックアウトしておいてください。そうすると指定したアドレスから受信したメールの添付ファイルを適切な位置に保存してSVNにコミットしてれます。ZIPファイルの場合はファイルを展開して一階層のフォルダを捨てたうえで展開してコミットします。

いろいろエラー処理とか追加したほうがよいとも思うんですが、私自身が必要としている範囲を大きく超える必要もないかということで、こんなかんじかなぁ。本業がメールにおぼれてえらいことになったので作っただけなので、本業のほうがかたづけばそれでいいということで、必要な人がいなければ、ここまででいいのかなってことでお許しを…

| | コメント (0) | トラックバック (0)

2011年10月31日 (月)

TracのXMLRPCを使ったExcelのマクロでmilestoneなどの設定を取得/更新する

少し前まで仕事で、Tracのテンプレートを作成することをやっていたんですが、テンプレートはプロジェクトの設定時にしか適用できないので、プロジェクトを改善していった結果をよそのプロジェクトに反映しづらいんです。これをなんとするために、XMLRPCを使って、Tracの設定をExcelに取り込み、その内容を逆に設定するマクロを作りました。とうぜん独自の変更があるプロジェクトへの適用は気を付けながらやるしかないですが…問題点としては、TracのXMLRPCで設定/取得できることしかあつかえないので、milestone等のdefault値は取得できませんし、カスタムフィールドは取得できても設定できません。(プラグイン作らないといけないのかなぁ)


1.設定
Settingシートを開き、アドレス、プロジェクト名、ユーザ名などを設定してください。

Tracconfig1

2. インポート
TracからMilestoneなどの設定を取り込みます。
Configシートのそのテーブルの所で右ボタンを押すと、一つのテーブルの取り込みと更新ができますが、Settingシートのインポートテーブルを押すと、すべてのテーブルを一括取り込みを行います。
まずはConfigシートを削除しておいてからやっていただくとわかりやすいと思います。

Tracconfig3

3. アップデート
アップデートボタンを押すと、現在の設定とかの比較とかなしに、いきなりTracの設定を削除した後追加します。明確には更新ではありませんが、これで問題ないようです。

Tracconfig4

変更結果の確認
Tracconfig5

更新先のアドレスかプロジェクト名を変更すれば設定のコピーになります

4. ソース
インデントなくなってしまいますが、一応載せておきます。見づらいのでExcelのVBEditorで見てください
ライセンスはBSDです。
a) TracConfig.bas

Attribute VB_Name = "TracConfig" Sub import_all_tables() Dim user As String, pw As String, URL As String, projectName As String Dim settingSheet As Worksheet Dim trac As TracXMLRPC Set trac = New TracXMLRPC initialize settingSheet, user, pw, URL, projectName trac.init URL, projectName, user, pw ImportTracTableM ThisWorkbook.Names("milestone"), trac.milestone ImportTracTableM ThisWorkbook.Names("component"), trac.component ImportTracTableM ThisWorkbook.Names("version"), trac.version ImportTracTableC ThisWorkbook.Names("type"), trac.ticketType ImportTracTableC ThisWorkbook.Names("priority"), trac.priority ImportTracTableC ThisWorkbook.Names("resolution"), trac.resolution ImportTracTableC ThisWorkbook.Names("severity"), trac.severity ImportTracTableM ThisWorkbook.Names("ticketfield"), trac.field End Sub

Sub table_import()
Dim user As String, pw As String, URL As String, projectName As String
Dim settingSheet As Worksheet
Dim trac As TracXMLRPC
Set trac = New TracXMLRPC

initialize settingSheet, user, pw, URL, projectName
trac.init URL, projectName, user, pw

Select Case Sheet5.selectedTable
Case "milestone"
ImportTracTableM ThisWorkbook.Names("milestone"), trac.milestone
Case "component"
ImportTracTableM ThisWorkbook.Names("component"), trac.component
Case "version"
ImportTracTableM ThisWorkbook.Names("version"), trac.version
Case "type"
ImportTracTableC ThisWorkbook.Names("type"), trac.ticketType
Case "priority"
ImportTracTableC ThisWorkbook.Names("priority"), trac.priority
Case "resolution"
ImportTracTableC ThisWorkbook.Names("resolution"), trac.resolution
Case "severity"
ImportTracTableC ThisWorkbook.Names("severity"), trac.severity
Case "ticketfield"
' If MsgBox("date,date_emptyの情報は取得できませんが、情報の再取得を行ってよろしいですか", vbOKCancel, "") = vbOK Then
ImportTracTableM ThisWorkbook.Names("ticketfield"), trac.field
' End If
End Select
End Sub

Sub update_all_tables()
Dim user As String, pw As String, URL As String, projectName As String
Dim settingSheet As Worksheet
Dim trac As TracXMLRPC
Set trac = New TracXMLRPC

initialize settingSheet, user, pw, URL, projectName
trac.init URL, projectName, user, pw

Dim m As Map
Dim c As Collection
Set m = createMap(ThisWorkbook.Names("milestone"))
Set trac.milestone = m
ImportTracTableM ThisWorkbook.Names("milestone"), trac.milestone
Set m = createMap(ThisWorkbook.Names("component"))
Set trac.component = m
ImportTracTableM ThisWorkbook.Names("component"), trac.component
Set m = createMap(ThisWorkbook.Names("version"))
Set trac.version = m
ImportTracTableM ThisWorkbook.Names("version"), trac.version
Set c = createCollection(ThisWorkbook.Names("type"))
Set trac.ticketType = c
ImportTracTableC ThisWorkbook.Names("type"), trac.ticketType
Set c = createCollection(ThisWorkbook.Names("priority"))
Set trac.priority = c
ImportTracTableC ThisWorkbook.Names("priority"), trac.priority
Set c = createCollection(ThisWorkbook.Names("resolution"))
Set trac.resolution = c
ImportTracTableC ThisWorkbook.Names("resolution"), trac.resolution
Set c = createCollection(ThisWorkbook.Names("severity"))
Set trac.severity = c
ImportTracTableC ThisWorkbook.Names("severity"), trac.severity
End Sub

Sub table_update()
Dim user As String, pw As String, URL As String, projectName As String
Dim settingSheet As Worksheet
Dim trac As TracXMLRPC
Set trac = New TracXMLRPC

initialize settingSheet, user, pw, URL, projectName
trac.init URL, projectName, user, pw

Dim m As Map
Dim c As Collection
Select Case Sheet5.selectedTable
Case "milestone"
Set m = createMap(ThisWorkbook.Names("milestone"))
Set trac.milestone = m
ImportTracTableM ThisWorkbook.Names("milestone"), trac.milestone
Case "component"
Set m = createMap(ThisWorkbook.Names("component"))
Set trac.component = m
ImportTracTableM ThisWorkbook.Names("component"), trac.component
Case "version"
Set m = createMap(ThisWorkbook.Names("version"))
Set trac.version = m
ImportTracTableM ThisWorkbook.Names("version"), trac.version
Case "type"
Set c = createCollection(ThisWorkbook.Names("type"))
Set trac.ticketType = c
ImportTracTableC ThisWorkbook.Names("type"), trac.ticketType
Case "priority"
Set c = createCollection(ThisWorkbook.Names("priority"))
Set trac.priority = c
ImportTracTableC ThisWorkbook.Names("priority"), trac.priority
Case "resolution"
Set c = createCollection(ThisWorkbook.Names("resolution"))
Set trac.resolution = c
ImportTracTableC ThisWorkbook.Names("resolution"), trac.resolution
Case "severity"
Set c = createCollection(ThisWorkbook.Names("severity"))
Set trac.severity = c
ImportTracTableC ThisWorkbook.Names("severity"), trac.severity
End Select
End Sub


Private Sub initialize(ByRef settingSheet As Worksheet, ByRef user As String, ByRef pw As String, ByRef URL As String, ByRef projectName As String)
Set settingSheet = Sheet1

URL = settingSheet.Cells(2, 3).Value
If settingSheet.Cells(5, 3).Value = True Then
Dim frm As PwDlg
Set frm = New PwDlg
frm.Show
user = frm.TextBox1.Value
pw = frm.TextBox2.Value
Unload frm
Else
user = settingSheet.Cells(6, 3).Value
pw = settingSheet.Cells(7, 3).Value
End If
projectName = settingSheet.Cells(3, 3).Value
' query = settingSheet.Cells(4, 3).Value
End Sub
Sub Test()
'データを取得する
Dim user As String, pw As String, URL As String, projectName As String
Dim settingSheet As Worksheet
Dim trac As TracXMLRPC
Set trac = New TracXMLRPC

initialize settingSheet, user, pw, URL, projectName
trac.init URL, projectName, user, pw

ImportTracTableM ThisWorkbook.Names("milestone"), trac.milestone
ImportTracTableM ThisWorkbook.Names("component"), trac.component
ImportTracTableM ThisWorkbook.Names("version"), trac.version

ImportTracTableC ThisWorkbook.Names("type"), trac.ticketType
ImportTracTableC ThisWorkbook.Names("priority"), trac.priority
ImportTracTableC ThisWorkbook.Names("resolution"), trac.resolution
ImportTracTableC ThisWorkbook.Names("severity"), trac.severity

ImportTracTableM ThisWorkbook.Names("ticketfield"), trac.field

Dim m As Map
Dim c As Collection
Set c = createCollection(ThisWorkbook.Names("resolution"))
Set trac.resolution = c
Set m = createMap(ThisWorkbook.Names("milestone"))
Set trac.milestone = m
End Sub

'名前で設定されている列をその範囲に関係なく下まで削除する
'名前の範囲は先頭の一行のみにする
Private Sub ClearTable(nm As name)
Dim row As Integer
row = 1
Do '行方向下に動くループ
If nm.RefersToRange.Cells(row, 1) = "" Then Exit Do
'値の入っていないセルが来たらループを抜ける
nm.RefersToRange.Rows(row) = "" '行ごとクリア
row = row + 1
Loop
nm.RefersTo = nm.RefersToRange.Rows(1) '名前を再設定
End Sub

Sub ImportTracTableC(nameType As name, c As Collection)
Dim t
Dim row As Integer
Dim col As Integer, colT As Integer
Dim bottomLeft As Range
row = 1
ClearTable nameType
colT = nameType.RefersToRange.Columns.Count
If colT <> 1 Then 'カラムが一つの場合は取得値は文字列
Debug.Print "error"
Exit Sub
End If
For Each t In c
Set bottomLeft = nameType.RefersToRange.Cells(row, 1)
bottomLeft = t
row = row + 1
Next
If row > 1 Then
nameType.RefersTo = Range(nameType.RefersToRange.Cells(1, 1), bottomLeft) '名前を再設定
End If
End Sub

Sub ImportTracTableM(nameType As name, m As Map)
Dim t
Dim row As Integer
Dim col As Integer, colT As Integer
Dim bottomLeft As Range
row = 1
ClearTable nameType
colT = nameType.RefersToRange.Columns.Count
If colT <= 1 Then 'カラムが一つの場合は取得値は文字列
Debug.Print "error"
Exit Sub
End If
For Each t In m.Keys
Dim m2 As Map
Set m2 = m.Values.Item(t)
For col = 1 To colT '配列なのでループを回る
Dim itemName As String, itemValue As String
itemName = nameType.RefersToRange.Cells(0, col)
Set bottomLeft = nameType.RefersToRange.Cells(row, col)
On Error Resume Next '設定されてない値は取得できないので無視する
itemValue = ""
Select Case TypeName(m2.Values.Item(itemName))
Case "Collection"
Dim i
For Each i In m2.Values.Item(itemName)
If itemValue = "" Then
itemValue = i
Else
itemValue = itemValue + "|" + i
End If
Next
Case Else
itemValue = m2.Values.Item(itemName)
End Select

bottomLeft = itemValue
On Error GoTo 0
Next
row = row + 1
Next
If row > 1 Then
nameType.RefersTo = Range(nameType.RefersToRange.Cells(1, 1), bottomLeft) '名前を再設定
End If
End Sub

Private Function createCollection(nm As name) As Collection
Dim row As Integer
Dim col As Integer, colT As Integer
Set createCollection = New Collection

colT = nm.RefersToRange.Columns.Count ' milestoneとかのためにあとで使う
row = 1
Do
If nm.RefersToRange.Cells(row, 1) = "" Then Exit Do
createCollection.add nm.RefersToRange.Rows(row).text
row = row + 1
Loop
End Function

Private Function createMap(nm As name) As Map
Dim row As Integer
Dim col As Integer, colT As Integer
Set createMap = New Map

colT = nm.RefersToRange.Columns.Count
row = 1
Do
If nm.RefersToRange.Cells(row, 1) = "" Then Exit Do
Dim m2 As Map
Set m2 = New Map
For col = 1 To colT '配列なのでループを回る
Dim bottomLeft As Range
Set bottomLeft = nm.RefersToRange.Cells(row, col)
On Error Resume Next '設定されてない値は取得できないので無視する
Debug.Print nm.RefersToRange.Cells(0, col) & " = " & bottomLeft
If bottomLeft.Value <> "" Then
m2.add bottomLeft.Value, nm.RefersToRange.Cells(0, col).text
Else
m2.add "", nm.RefersToRange.Cells(0, col).text
End If
On Error GoTo 0
Next
createMap.add m2, m2.Values.Item("name")
' m2.remove "name"
row = row + 1
Loop
End Function

b) TracXMLRPC.cls

Option Explicit

Dim m_xmlrpc As XMLRPC
Dim m_URL As String
Dim m_projectName As String

Dim m_priority As Collection
Dim m_resolution As Collection
Dim m_severity As Collection
Dim m_type As Collection
'statusは更新できない
Dim m_status As Collection

Dim m_component As Map
Dim m_version As Map
Dim m_milestone As Map

Dim m_field As Map

'クラスを初期化します.各引数は次のように指定してください
'URL:http://localhost/trac
'projectName:SampleProject
'user:admin
'pw:admin
Public Sub init(URL As String, projectName As String, user As String, pw As String, Optional fUserXmlServer As Boolean = False)
m_projectName = projectName
m_URL = URL
If Mid(m_URL, Len(m_URL) - 1) <> "/" Then
m_URL = m_URL & "/"
End If
Set m_xmlrpc = New XMLRPC
If m_projectName <> "" Then
m_xmlrpc.init m_URL & m_projectName & "/login/xmlrpc", user, pw, fUserXmlServer
Else
m_xmlrpc.init m_URL & "login/xmlrpc", user, pw, fUserXmlServer
End If
Set m_milestone = Nothing
Set m_status = Nothing
Set m_version = Nothing
Set m_component = Nothing
Set m_priority = Nothing
Set m_resolution = Nothing
Set m_severity = Nothing
Set m_type = Nothing
Set m_field = Nothing
End Sub

'IDを指定してチケットの情報を取得します
'戻り値がマップに変更になりました
Public Function getTicket(id As Long) As Map
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.get", id)
'取得した結果はCollectionなので一つ目の要素を取得して、
'次のArrayで返ってくる値の中から、id,createtime,timeを捨てます。
Set getTicket = result.Item(1).Item(4)
'idは捨てたので追加します
getTicket.add id, "id"
End Function

Public Function createTicket(summary As String, description As String, attributes As Map, notify As Boolean) As Long
Dim result As Collection
createTicket = 0
On Error Resume Next
'attributeの中にあっても意味がないものを削除
attributes.remove "id"
attributes.remove "summary"
attributes.remove "description"
attributes.remove "time"
attributes.remove "createtime"
On Error GoTo 0
Set result = m_xmlrpc.Send("ticket.create", summary, description, attributes, notify)
createTicket = result.Item(1)
End Function

Public Function updateTicket(id As Long, comment As String, attributes As Map, Optional notify As Boolean = False) As Map
On Error Resume Next
'attributeの中にあっても意味がないものを削除
attributes.remove "id"
attributes.remove "comment"
attributes.remove "time"
attributes.remove "changetime"
On Error GoTo 0
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.update", id, comment, attributes, notify)
Set updateTicket = result.Item(1).Item(4)
updateTicket.add id, "id"
End Function

Public Function getActions(id As Long) As Collection
Set getActions = m_xmlrpc.Send("ticket.getActions", id).Item(1) '変更されたチケットのIDが返ってくる
End Function

'チケットに対する変更内容が配列で取得できます
'配列の要素も配列になっていて、
'変更時刻、更新者、変更項目、旧値、新値になっている。
Public Function changeLog(id As Long) As Collection
Set changeLog = m_xmlrpc.Send("ticket.changeLog", id).Item(1) '変更されたチケットのIDが返ってくる
End Function

'チケット(Map)のCollectionを返します.
'チケットの情報がstructなので、Mapになりました
Public Function queryTicket(query As String) As Collection
Dim params As String
Set queryTicket = New Collection
If query = "" Then
params = ""
Else
params = "" & query & ""
End If
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.query", query)
Dim n
For Each n In result.Item(1)
Dim ticket As Map, r As Collection
Set r = m_xmlrpc.Send("ticket.get", n)
Set ticket = r.Item(1).Item(4)
ticket.add n, "id"
queryTicket.add ticket, "" & n
Next
End Function

'==============================================================================

'milestone等の情報を取得します.指定できるのは次のものです.
'"resolution","milestone","version","component","priority","severity"
Private Function getAllArray(method As String, methodSub As String) As Map
Dim nms As Collection
Set getAllArray = New Map
Set nms = m_xmlrpc.Send(method)
Dim nm
For Each nm In nms.Item(1)
Dim c As Collection
Set c = m_xmlrpc.Send(methodSub, nm)
If c.Count <> 1 Then 'データが取得できない
err.Raise 0, "", ""
Else
getAllArray.add c.Item(1), "" & nm
End If
Next
End Function

Private Function ticketEnumGetAll(enumName As String) As Collection
Set ticketEnumGetAll = Send("ticket." & enumName & ".getAll").Item(1)
End Function

Public Property Get priority() As Collection
If m_priority Is Nothing Then
Set m_priority = ticketEnumGetAll("priority")
End If
Set priority = m_priority
End Property

Public Property Get resolution() As Collection
If m_resolution Is Nothing Then
Set m_resolution = ticketEnumGetAll("resolution")
End If
Set resolution = m_resolution
End Property

Public Property Get severity() As Collection
If m_severity Is Nothing Then
Set m_severity = ticketEnumGetAll("severity")
End If
Set severity = m_severity
End Property

Public Property Get ticketType() As Collection
If m_type Is Nothing Then
Set m_type = ticketEnumGetAll("type")
End If
Set ticketType = m_type
End Property


'表示順はCollectionの中の順になります。
'デフォルト値は設定できないので、どうなるのかわかりません
'名前は変更ではなく削除してからの追加なので、注意してください
Private Function ticketEnumReset(enumNm As String, colOld As Collection, colNew As Collection) As Collection
Dim itemName
Dim nm As String
'今あるものを削除する
For Each itemName In colOld
nm = itemName
Call Send("ticket." & enumNm & ".delete", nm)
Next
'追加する
Dim p As Integer
p = 1
For Each itemName In colNew
nm = itemName
Dim res As Collection
Set res = Send("ticket." & enumNm & ".create", nm, p)
p = p + 1
Next
Set ticketEnumReset = ticketEnumGetAll(enumNm)
End Function

Public Property Set priority(colNew As Collection)
Set m_priority = ticketEnumReset("priority", Me.priority, colNew)
End Property

Public Property Set resolution(colNew As Collection)
Set m_resolution = ticketEnumReset("resolution", Me.resolution, colNew)
End Property

Public Property Set severity(colNew As Collection)
Set m_severity = ticketEnumReset("severity", Me.severity, colNew)
End Property

Public Property Set ticketType(colNew As Collection)
Set m_type = ticketEnumReset("type", Me.ticketType, colNew)
End Property

'=======================================================================
'
Private Sub removeDefaultDate(m As Map, itemName As String)
Dim i
For Each i In m.Values
If i.Values.Item(itemName) = 0 Then
i.update "", itemName
End If
Next
End Sub

Private Function ticketModelGetAll(modelName As String) As Map
Set ticketModelGetAll = getAllArray("ticket." & modelName & ".getAll", "ticket." & modelName & ".get")
'MS-Officeで扱うにはこのデフォルト値はないほうがいい
On Error Resume Next
'TODO:確認
removeDefaultDate ticketModelGetAll, "due"
removeDefaultDate ticketModelGetAll, "completed"
removeDefaultDate ticketModelGetAll, "time"
On Error GoTo 0
End Function

'二重のマップになる
'キーはマイルストン名
'due, completed, description, name
'due,completedのデフォルト値は0なので注意
Public Property Get milestone() As Map
If m_milestone Is Nothing Then
Set m_milestone = ticketModelGetAll("milestone")
End If
Set milestone = m_milestone
End Property

'二重のマップになる
'キーはバージョン名
'time, description, name
'timeのデフォルト値は0なので注意
Public Property Get version() As Map
If m_version Is Nothing Then
Set m_version = ticketModelGetAll("version")
End If
Set version = m_version
End Property

'二重のマップになる
'キーはコンポーネント名
'owner, description, name
'dueのデフォルト値は0なので注意
Public Property Get component() As Map
If m_component Is Nothing Then
Set m_component = ticketModelGetAll("component")
End If
Set component = m_component
End Property

'statusは更新できない
Public Property Get status() As Collection
If m_status Is Nothing Then
Set m_status = ticketEnumGetAll("status")
End If
Set status = m_status
End Property


'表示順はCollectionの中の順になります。
'デフォルト値は設定できないので、どうなるのかわかりません
'名前は変更ではなく削除してからの追加なので、注意してください
Private Function ticketModelReset(enumNm As String, colOld As Map, mapNew As Map) As Map
Dim itemName
Dim nm As String
'今あるものを削除する
On Error Resume Next
For Each itemName In colOld.Keys
nm = itemName
Call Send("ticket." & enumNm & ".delete", nm)
Next
On Error GoTo 0
'追加する
For Each itemName In mapNew.Keys
nm = itemName
Call Send("ticket." & enumNm & ".create", nm, mapNew.Values.Item(itemName))
Next
Set ticketModelReset = ticketModelGetAll(enumNm)
End Function

Public Property Set milestone(mapNew As Map)
Set m_milestone = ticketModelReset("milestone", Me.milestone, mapNew)
End Property

Public Property Set version(mapNew As Map)
Set m_version = ticketModelReset("version", Me.version, mapNew)
End Property

Public Property Set component(mapNew As Map)
Set m_component = ticketModelReset("component", Me.component, mapNew)
End Property


'==========================================================
Public Property Get field() As Map
If m_field Is Nothing Then
Dim res As Collection, f
Set m_field = New Map
Set res = m_xmlrpc.Send("ticket.getTicketFields")
For Each f In res.Item(1)
m_field.add f, f.Values.Item("name")
Next
End If
Set field = m_field
End Property

'============================================================================
Public Function TicketPutAttachment(id As Long, path As String, fileName As String, description As String) As String
Dim n As Long, fileSize As Long
n = FreeFile
fileSize = FileLen(path)
Dim buf() As Byte
ReDim buf(fileSize - 1)
Open path For Binary As #n
Get #n, , buf
Close #n

Dim c As Collection
Set c = m_xmlrpc.Send("ticket.putAttachment", id, fileName, description, buf)
TicketPutAttachment = c.Item(1)
End Function

Public Function TicketListAttachments(id As Long) As Collection
Dim c As Collection
Set c = m_xmlrpc.Send("ticket.listAttachments", id)
Set TicketListAttachments = c.Item(1)
End Function

Public Function TicketGetAttachment(id As Long, path As String, fileName As String) As Collection
Dim c As Collection
Set c = m_xmlrpc.Send("ticket.getAttachment", id, fileName)

On Error Resume Next
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
FSO.DeleteFile path
Set FSO = Nothing
On Error GoTo 0

Dim n As Long
n = FreeFile
Dim buff() As Byte
Open path For Binary Access Write As #n
buff = c.Item(1)
Put #n, , buff
Close #n
Set TicketGetAttachment = c
End Function

'=======================================================================
'指定時刻以後に変更のあったチケットのidを配列で返します
Public Function getRecentChanges(since As Date) As Collection
'変更されたチケットのIDが返ってくる
Set getRecentChanges = m_xmlrpc.Send("ticket.getRecentChanges", since).Item(1)
End Function

'接続してレスポンスを得ます
Function Send(ParamArray val()) As Collection
Dim method As String, params As String
method = val(0)
Dim i As Integer
For i = 1 To UBound(val) '最初の一つは捨てる
params = params & "" & m_xmlrpc.CreateParamStr(val(i)) & ""
Next
Set Send = m_xmlrpc.Send0(method, params)
End Function

c) XMLParam.cls

Option Explicit

'XMLRPCのデータとVBAのデータの変換を行うクラスです
'i4,int <-> Long,Integer,Byte
'Base64 <- Byte()
'struct <-> Map
'array(s) <-> Collection,?()
'string <-> String
'double <-> Double,Float
'boolean <-> Boolean
'dateTime.iso8601 <-> Date
'エラー;
' method Error number Desciption
' CreateParamStr vbObjectError + 516 変換できない型
' CreateRetVal vbObjectError + 517 仕様にない型

Dim m_date As ISO8601Date
Dim m_base64 As Base64EncDec
Dim m_encode As Long
Dim m_decode As Long

Private Sub Class_Initialize()
Set m_date = New ISO8601Date
Set m_base64 = New Base64EncDec
m_encode = 15
m_decode = 0
End Sub

Function CreateParamStr(val As Variant) As String
Dim strParam
Dim strType As String
strType = TypeName(val)
Select Case strType
Case "Byte()"
Dim buf() As Byte
buf = val
strParam = "" & m_base64.encodeBase64(buf) & ""
Case "String"
If m_encode And 1 = 1 Then
val = Replace(val, "&", "&")
End If
If m_encode And 2 = 2 Then
val = Replace(val, "<", "<")
End If
If m_encode And 4 = 4 Then
val = Replace(val, ">", ">")
End If
If m_encode And 8 = 8 Then
val = Replace(val, """", """)
End If
If m_encode And 16 = 16 Then
val = Replace(val, " ", " ")
End If
strParam = "" & val & ""
Case "Double", "Single"
strParam = "" & val & ""
Case "Byte", "Integer", "Long"
strParam = "" & val & ""
Case "Boolean"
If val = False Then
strParam = "0"
Else
strParam = "1"
End If
Case "Date"
Dim d As Date
d = val
strParam = "" & m_date.DateToISODate(d) & ""
Case "Map"
Dim m As Map
Set m = val
Dim k
For Each k In m.Keys
strParam = strParam & "" & k & "" & CreateParamStr(m.Values(k)) & "" & vbCrLf
Next
'valueが一つも無くてもstructになっていないと引数として使用できない。
strParam = "" & strParam & ""
Case "Collection"
Dim a
For Each a In val
strParam = strParam & CreateParamStr(a) & vbCrLf
Next
If InStr(strParam, "") > 0 Or InStr(strParam, "") > 0 Then
strParam = "" & strParam & ""
Else
strParam = "" & strParam & ""
End If
Case Else
If Right(strType, 2) = "()" Then 'VBAの配列の場合はArrayにする
Dim a1
For Each a1 In val
strParam = strParam & CreateParamStr(a1) & vbCrLf
Next
If InStr(strParam, "") > 0 Or InStr(strParam, "") > 0 Then
strParam = "" & strParam & ""
Else
strParam = "" & strParam & ""
End If
Else
err.Raise vbObjectError + 516, "Paramertr error is occurred in CreateParamStr."
End If
End Select
CreateParamStr = strParam
End Function

Function CreateRetVal(val As Variant) As Variant
Dim n As String, v As String, nn
' If val.BaseName <> "value" Then
' Debug.Print "引数のBaseNameはvalueになるようにしてください"
' End If
Set val = val.ChildNodes(0)
Select Case val.BaseName
Case "array", "arrays"
Dim i As Integer
Set CreateRetVal = New Collection
'dataは読み捨てる
Set val = val.ChildNodes(0)
For i = 0 To val.ChildNodes.Length - 1
CreateRetVal.add CreateRetVal(val.ChildNodes(i))
Next
Case "struct"
Dim colV As Map
Set colV = New Map
For Each nn In val.ChildNodes
Dim childNode
Set childNode = nn.ChildNodes(0)
colV.add CreateRetVal(nn.ChildNodes(1)), childNode.ChildNodes(0).text
Next
Set CreateRetVal = colV
Case "int", "i4"
CreateRetVal = CLng(val.text)
Case "string"
Dim strWork As String
strWork = val.text
If m_decode And 16 = 16 Then
strWork = Replace(strWork, " ", " ")
End If
If m_decode And 8 = 8 Then
strWork = Replace(strWork, """, """")
End If
If m_decode And 4 = 4 Then
strWork = Replace(strWork, ">", ">")
End If
If m_decode And 2 = 2 Then
strWork = Replace(strWork, "<", "<")
End If
If m_decode And 1 = 1 Then
strWork = Replace(strWork, "&", "&")
End If
CreateRetVal = strWork
Case "double"
'CDbl
CreateRetVal = CDbl(val.text)
Case "boolean"
CreateRetVal = CBool(val.text)
Case "dateTime.iso8601"
Dim d As ISO8601Date
Set d = New ISO8601Date
CreateRetVal = d.ISODateToDate(val.text)
Case "base64"
CreateRetVal = m_base64.decodeBase64(val.text)
Case Else
err.Raise vbObjectError + 517, , "Unknown type"
End Select
End Function

「tracconfig.xlsm」をダウンロード

| | コメント (0) | トラックバック (0)

2011年10月24日 (月)

VBAでXMLRPCを汎用的に修正してTrac連携をそれに合わせて修正した

課題山積みなのに、最近は通勤電車の中でしかTracいじる時間が取れなくて、何も進んでないんですが、やっと公開できる部品ができたので公開します。(部品なのでこれだけでは何の役にも立ちませんm(_ _)m)
やり始めたきっかけは、以前の記事のTracの設定をXMLRPCでできるようにとかそういったところだったんですが、VBAもよくわからない昔に作ったものはあまりにもひどいので、ほぼ全書き換えしました。XMLRPCを使ってTrac-VBA連携をする基本のモジュールを、Trac用の所とそうでない所を分割してみました。公開する7ファイルの概要は次のものです。ソースはさらにその下に貼り付けます。

1. XMLRPC
MSXML2.XMLHTTPを使ってXMLRPCでサーバと接続する。
2. Map
マップです。XMLRPCで使用する構造体を扱うために使用する。
3. XMLParam
XMLRPCの引数/戻り値とVBAの型の変換をする。
4. ISO8601Date
時差を考慮しXMLRPCとVBAの間の日付を変換する。
5. Base64EncDec
バイナリデータをBase64でエンコードとデコードする
6. TracXMLRPC
TracのXMLRPCのインタフェースを使いやすく。

一つ一つのソースを上げていきます。ライセンスは後に添付するExcelファイルの中にはつけていますがすべてBSDです。
1. XMLRPC
主な関数のSendは可変個数の引数の関数で、一つ目はXMLRPCのメソッド名二つ目以後はそのメソッドの引数になっていて、戻り値は戻り値をCollectionに入れたものです。
※戻り値のParamは必ず一つになるので、Collectionにはしたくなかったんですが、型が違う戻り値を返すのにはこうするしかなかった。
Option Explicit

'Copyright (c) 2011 Yuji OKAZAKI. All rights reserved.

Dim m_URL As String
Dim m_user As String
Dim m_pw As String
Dim m_paramConv As XMLParam
'参照設定が無くても使えるようにする
'http://support.microsoft.com/kb/290761/ja
'Dim m_xmlSv As MSXML2.ServerXMLHTTP
'Dim m_xmlSv As MSXML2.xmlHttp
Dim m_xmlSv As Variant
Dim m_fUserXmlServer As Boolean

Private Sub Class_Initialize()
Set m_paramConv = New XMLParam
End Sub

Public Sub init(URL As String, user As String, pw As String, Optional fUserXmlServer As Boolean = False)
m_URL = URL
m_user = user
m_pw = pw
m_fUserXmlServer = fUserXmlServer
If m_fUserXmlServer Then
Set m_xmlSv = CreateObject("MSXML2.ServerXMLHTTP")
Else
Set m_xmlSv = CreateObject("MSXML2.XMLHTTP")
End If
End Sub

Function CreateParamStr(val As Variant) As String
CreateParamStr = m_paramConv.CreateParamStr(val)
End Function

'接続してレスポンスを得ます
Function Send(ParamArray val()) As Collection
Dim method As String, params As String
method = val(0)
Dim i As Integer
For i = 1 To UBound(val) '最初の一つは捨てる
params = params & "" & CreateParamStr(val(i)) & ""
Next
Set Send = Send0(method, params)
End Function

Function Send0(method As String, params As String) As Collection
m_xmlSv.Open "POST", m_URL, False, m_user, m_pw
m_xmlSv.setRequestHeader "Method", "POST " & m_URL & " HTTP/1.1"
m_xmlSv.setRequestHeader "Content-Type", "text/xml"
If m_fUserXmlServer Then
m_xmlSv.setOption 2, 13056 'SXH_OPTION_SELECT_CLIENT_SSL_CERT
End If
If method <> "" Then
Dim work As Variant
'sendの引数はVariantでないとダメらしい。
work = "" & _
"" & _
"" & method & "" & _
"" & params & "" & _
"
"
Call m_xmlSv.Send(work)
End If
DoEvents
Dim errorMessage As String
errorMessage = CheckError()
If errorMessage <> "" Then
Debug.Print "==========Error========="
Debug.Print "----------Param---------"
Debug.Print work
Debug.Print "--------Response--------"
Debug.Print m_xmlSv.responsetext
Debug.Print "------Error Message-----"
Debug.Print errorMessage
err.Raise vbObjectError + 515, , errorMessage
End If
Dim p, v, c
Set p = m_xmlSv.responseXML.getElementsByTagName("param")
Set c = New Collection
For Each v In p
'paramは仕様上一つだけど値を返す時にcollection以外だと難しいのでこうする
'Variantに文字列を返すとそれをsetするところでエラーが出るどう対処すればいいかわからないので
c.add m_paramConv.CreateRetVal(v.ChildNodes(0))
Next
Set Send0 = c
End Function

'Responseがエラーかどうかを判断します。
'エラーだった場合は何らかの文字列を返します。
Private Function CheckError() As String
CheckError = ""
Dim Members
Set Members = m_xmlSv.responseXML.getElementsByTagName("fault")
Dim errorMessage As String
errorMessage = ""
If m_xmlSv.status <= 100 Or m_xmlSv.status > 200 Then
'認証に失敗したとかHTTPにアクセスするときまでの問題の処理
errorMessage = "Error:" & m_xmlSv.statusText & "(" & m_xmlSv.status & ")"
ElseIf Members.Length = 0 Then
'faultが無かった場合はちゃんとXMLでレスポンスがあったか確認します.
If m_xmlSv.responseXML.getElementsByTagName("methodResponse").Length > 0 Then
Exit Function
End If
errorMessage = "Not an XML response."
Else
'faultがあった場合エラーメッセージをまとめます
Set Members = m_xmlSv.responseXML.getElementsByTagName("member")
Dim i
For i = 0 To Members.Length - 1
Dim oNodeList
Set oNodeList = Members.Item(i).ChildNodes
If oNodeList.Item(0).text = "faultCode" Then
errorMessage = errorMessage & "Code=" & oNodeList.Item(1).text
End If
If oNodeList.Item(0).text = "faultString" Then
errorMessage = errorMessage & ":" & oNodeList.Item(1).text
End If
Next
End If
CheckError = errorMessage
End Function

2. Map
マップです。VBAのCollectionからキーが取れれば何の問題もないんですが、それができないので二つのCollectionをまとめて、キーと値を保存するようにしました。

'Copyright (c) 2011 Yuji OKAZAKI. All rights reserved.

Option Explicit

Public Keys As Collection
Public Values As Collection

Public Sub Class_Initialize()
Set Keys = New Collection
Set Values = New Collection
End Sub

Public Sub add(Value As Variant, key As String)
Keys.add key, key
Values.add Value, key
End Sub

Public Sub remove(key As String)
Keys.remove key
Values.remove key
End Sub

Public Sub update(Value As Variant, key As String)
On Error Resume Next
remove key
On Error GoTo 0
add Value, key
End Sub

3. XMLParam
ソース中に書いてあるようにVBAの型とXMLRPCの型の相互の変換を行います。


'Copyright (c) 2011 Yuji OKAZAKI. All rights reserved.

Option Explicit

'XMLRPCのデータとVBAのデータの変換を行うクラスです
'i4,int <-> Long,Integer,Byte
'Base64 <- Byte()
'struct <-> Map
'array(s) <-> Collection,?()
'string <-> String
'double <-> Double,Float
'boolean <-> Boolean
'dateTime.iso8601 <-> Date
'エラー;
' method Error number Desciption
' CreateParamStr vbObjectError + 516 変換できない型
' CreateRetVal vbObjectError + 517 仕様にない型

Dim m_date As ISO8601Date
Dim m_base64 As Base64EncDec
Dim m_encode As Long
Dim m_decode As Long

Private Sub Class_Initialize()
Set m_date = New ISO8601Date
Set m_base64 = New Base64EncDec
m_encode = 15
m_decode = 0
End Sub

Function CreateParamStr(val As Variant) As String
Dim strParam
Dim strType As String
strType = typeName(val)
Select Case strType
Case "Byte()"
Dim buf() As Byte
buf = val
strParam = "" & m_base64.encodeBase64(buf) & ""
Case "String"
If m_encode And 1 = 1 Then
val = Replace(val, "&", "&")
End If
If m_encode And 2 = 2 Then
val = Replace(val, "<", "<")
End If
If m_encode And 4 = 4 Then
val = Replace(val, ">", ">")
End If
If m_encode And 8 = 8 Then
val = Replace(val, """", """)
End If
If m_encode And 16 = 16 Then
val = Replace(val, " ", " ")
End If
strParam = "" & val & ""
Case "Double", "Single"
strParam = "" & val & ""
Case "Byte", "Integer", "Long"
strParam = "" & val & ""
Case "Boolean"
If val = False Then
strParam = "0"
Else
strParam = "1"
End If
Case "Date"
Dim d As Date
d = val
strParam = "" & m_date.DateToISODate(d) & ""
Case "Map"
Dim m As Map
Set m = val
Dim k
For Each k In m.Keys
strParam = strParam & "" & k & "" & CreateParamStr(m.Values(k)) & "" & vbCrLf
Next
'valueが一つも無くてもstructになっていないと引数として使用できない。
strParam = "" & strParam & ""
Case "Collection"
Dim a
For Each a In val
strParam = strParam & CreateParamStr(a) & vbCrLf
Next
If InStr(strParam, "") > 0 Or InStr(strParam, "") > 0 Then
strParam = "" & strParam & ""
Else
strParam = "" & strParam & ""
End If
Case Else
If Right(strType, 2) = "()" Then 'VBAの配列の場合はArrayにする
Dim a1
For Each a1 In val
strParam = strParam & CreateParamStr(a1) & vbCrLf
Next
If InStr(strParam, "") > 0 Or InStr(strParam, "") > 0 Then
strParam = "" & strParam & ""
Else
strParam = "" & strParam & ""
End If
Else
err.Raise vbObjectError + 516, "Paramertr error is occurred in CreateParamStr."
End If
End Select
CreateParamStr = strParam
End Function

Function CreateRetVal(val As Variant) As Variant
Dim n As String, v As String, nn
' If val.BaseName <> "value" Then
' Debug.Print "引数のBaseNameはvalueになるようにしてください"
' End If
Set val = val.ChildNodes(0)
Select Case val.BaseName
Case "array", "arrays"
Dim i As Integer
Set CreateRetVal = New Collection
'dataは読み捨てる
Set val = val.ChildNodes(0)
For i = 0 To val.ChildNodes.Length - 1
CreateRetVal.add CreateRetVal(val.ChildNodes(i))
Next
Case "struct"
Dim colV As Map
Set colV = New Map
For Each nn In val.ChildNodes
Dim childNode
Set childNode = nn.ChildNodes(0)
colV.add CreateRetVal(nn.ChildNodes(1)), childNode.ChildNodes(0).text
Next
Set CreateRetVal = colV
Case "int", "i4"
CreateRetVal = CLng(val.text)
Case "string"
Dim strWork As String
strWork = val.text
If m_decode And 16 = 16 Then
strWork = Replace(strWork, " ", " ")
End If
If m_decode And 8 = 8 Then
strWork = Replace(strWork, """, """")
End If
If m_decode And 4 = 4 Then
strWork = Replace(strWork, ">", ">")
End If
If m_decode And 2 = 2 Then
strWork = Replace(strWork, "<", "<")
End If
If m_decode And 1 = 1 Then
strWork = Replace(strWork, "&", "&")
End If
CreateRetVal = strWork
Case "double"
'CDbl
CreateRetVal = CDbl(val.text)
Case "boolean"
CreateRetVal = CBool(val.text)
Case "dateTime.iso8601"
Dim d As ISO8601Date
Set d = New ISO8601Date
CreateRetVal = d.ISODateToDate(val.text)
Case "base64"
CreateRetVal = m_base64.decodeBase64(val.text)
Case Else
err.Raise vbObjectError + 517, , "Unknown type"
End Select
End Function

4. ISO8601Date
時差を考慮しXMLRPCとVBAの間の日付を変換する。


'Copyright (c) 2011 Yuji OKAZAKI. All rights reserved.

Option Explicit

'XMLRPCの日付をExcelの日付に変換します。
Dim m_timeBias As Double '時差(h)

Private Sub Class_Initialize()
m_timeBias = 1# / 60# * GetTimeZoneBias()
End Sub

'時差(m)を取得します
Private Function GetTimeZoneBias() As Integer
Dim TizSet As Object, Tiz As Object, Locator As Object
Set Locator = CreateObject("WbemScripting.SWbemLocator")
Set TizSet = Locator.ConnectServer.ExecQuery("Select * From Win32_TimeZone")
GetTimeZoneBias = 0
For Each Tiz In TizSet
GetTimeZoneBias = Tiz.bias
Exit Function
Next
End Function

'時間を文字列の時間に変換します
'時差なしのXMLRPCの時刻をDateに変換します。
' date:20090317T16:10:12のようなXMLの時刻の文字列
Function ISODateToDate(text As String) As Date
Dim d As Date
If Len(text) = 17 Then
On Error GoTo FORMAT_ERR
d = DateSerial(Mid$(text, 1, 4), Mid$(text, 5, 2), Mid$(text, 7, 2))
d = DateAdd("h", Int(Mid$(text, 10, 2)), d)
d = DateAdd("n", Int(Mid$(text, 13, 2)), d)
d = DateAdd("s", Int(Mid$(text, 16, 2)), d)
d = DateAdd("h", m_timeBias, d)
ISODateToDate = d
On Error GoTo 0
Exit Function
End If
FORMAT_ERR:
err.Raise vbObjectError + 515, , "Error: Date format is wrong (" & text & ")."
End Function

'時間を文字列の時間に変換します
'時差なしのXMLRPCの時刻をDateに変換します。
' date:20090317T16:10:12のようなXMLの時刻の文字列
Function DateToISODate(d As Date) As String
d = DateAdd("h", -m_timeBias, d)
DateToISODate = Format(d, "yyyymmddThh:nn:ss")
End Function

5. Base64EncDec
バイナリデータをBase64でエンコードとデコードする


'Copyright (c) 2011 Yuji OKAZAKI. All rights reserved.

Option Explicit

Private Function encodeBase64Byte(d As Long) As Long
If d < 26 Then
encodeBase64Byte = Asc("A") + d
ElseIf d < 52 Then
encodeBase64Byte = Asc("a") + (d - 26)
ElseIf d < 62 Then
encodeBase64Byte = Asc("0") + (d - 52)
ElseIf d < 63 Then
encodeBase64Byte = Asc("+")
ElseIf d < 64 Then
encodeBase64Byte = Asc("/")
End If
End Function

Private Function decodeBase64Byte(b As Byte) As Long
If b >= Asc("A") And b <= Asc("Z") Then
decodeBase64Byte = b - Asc("A")
ElseIf b >= Asc("a") And b <= Asc("z") Then
decodeBase64Byte = b - Asc("a") + 26
ElseIf b >= Asc("0") And b <= Asc("9") Then
decodeBase64Byte = b - Asc("0") + 52
ElseIf b = Asc("+") Then
decodeBase64Byte = 62
ElseIf b = Asc("/") Then
decodeBase64Byte = 63
End If
End Function


Public Function encodeBase64(ByRef buf() As Byte) As String
encodeBase64 = ""
Dim fileSize As Long
fileSize = UBound(buf)
Dim d(4) As Long, e(4) As Long
Dim pos As Long, posL As Integer, l As Long
For pos = 0 To fileSize
l = l * (2 ^ 8) + buf(pos)
posL = posL + 1
If posL = 3 Then
posL = 0
d(1) = CLng((l And (63 * (2 ^ 18))) / (2 ^ 18))
d(2) = CLng((l And (63 * (2 ^ 12))) / (2 ^ 12))
d(3) = CLng((l And (63 * (2 ^ 6))) / (2 ^ 6))
d(4) = CLng(l And 63)
e(1) = encodeBase64Byte(d(1))
e(2) = encodeBase64Byte(d(2))
e(3) = encodeBase64Byte(d(3))
e(4) = encodeBase64Byte(d(4))
encodeBase64 = encodeBase64 + Chr(CByte(e(1))) + Chr(CByte(e(2))) + Chr(CByte(e(3))) + Chr(CByte(e(4)))
l = 0
End If
Next
If posL >= 1 Then
If posL = 1 Then
l = l * (2 ^ 16)
Else
l = l * (2 ^ 8)
End If
d(1) = CLng((l And (63 * (2 ^ 18))) / (2 ^ 18))
d(2) = CLng((l And (63 * (2 ^ 12))) / (2 ^ 12))
d(3) = CLng((l And (63 * (2 ^ 6))) / (2 ^ 6))
e(1) = encodeBase64Byte(d(1))
e(2) = encodeBase64Byte(d(2))
e(3) = encodeBase64Byte(d(3))
If posL = 1 Then
encodeBase64 = encodeBase64 + Chr(CByte(e(1))) + Chr(CByte(e(2))) + "=="
Else
encodeBase64 = encodeBase64 + Chr(CByte(e(1))) + Chr(CByte(e(2))) + Chr(CByte(e(3))) + "="
End If
End If
End Function

Public Function decodeBase64(strText As String) As Variant
Dim buf() As Byte
Dim strSize As Long, bufSize As Long
strSize = Len(strText)
bufSize = strSize
ReDim buf(bufSize)
Dim pos As Long, posBuf As Long
Dim posL As Integer
For pos = 1 To strSize
Dim l As Long, l2 As Long, b As Byte, s As String
Do
b = Asc(Mid(strText, pos, 1))
If b = 10 Then
'改行の場合は読み飛ばす
pos = pos + 1
ElseIf b = Asc("=") Then
Exit For
Else
Exit Do
End If
Loop
'デコードした値は6ビット
l = l * (2 ^ 6) + decodeBase64Byte(b)
posL = posL + 1
If posL = 4 Then
'6*4=24bit集まったら,3Byte分出力する
On Error Resume Next
buf(posBuf) = CByte((l / (2 ^ 16)) And 255)
buf(posBuf + 1) = CByte((l / (2 ^ 8)) And 255)
buf(posBuf + 2) = CByte((l And 255))
On Error GoTo 0
l = 0
posBuf = posBuf + 3
posL = 0
End If
Next
If posL = 3 Then
l = l * (2 ^ 6)
buf(posBuf) = CByte((l / (2 ^ 16)) And 255)
buf(posBuf + 1) = CByte((l / (2 ^ 8)) And 255)
posBuf = posBuf + 2
ElseIf posL = 2 Then
l = l * (2 ^ 12)
buf(posBuf) = CByte((l / (2 ^ 16)) And 255)
posBuf = posBuf + 1
End If
ReDim Preserve buf(posBuf - 1)
decodeBase64 = buf
End Function

6. TracXMLRPC
過去のものとの違いは、milestoneやresolution等をproperty setで設定できるようにした。添付ファイルの取得に対応。などなど


'Copyright (c) 2009-2011 Yuji OKAZAKI. All rights reserved.

Option Explicit

Dim m_xmlrpc As XMLRPC
Dim m_URL As String
Dim m_projectName As String

Dim m_priority As Collection
Dim m_resolution As Collection
Dim m_severity As Collection
Dim m_type As Collection
'statusは更新できない
Dim m_status As Collection

Dim m_component As Map
Dim m_version As Map
Dim m_milestone As Map

Dim m_field As Map

'クラスを初期化します.各引数は次のように指定してください
'URL:http://localhost/trac
'projectName:SampleProject
'user:admin
'pw:admin
Public Sub init(URL As String, projectName As String, user As String, pw As String, Optional fUserXmlServer As Boolean = False)
m_projectName = projectName
m_URL = URL
If Mid(m_URL, Len(m_URL) - 1) <> "/" Then
m_URL = m_URL & "/"
End If
Set m_xmlrpc = New XMLRPC
If m_projectName <> "" Then
m_xmlrpc.init m_URL & m_projectName & "/login/xmlrpc", user, pw, fUserXmlServer
Else
m_xmlrpc.init m_URL & "login/xmlrpc", user, pw, fUserXmlServer
End If
Set m_milestone = Nothing
Set m_status = Nothing
Set m_version = Nothing
Set m_component = Nothing
Set m_priority = Nothing
Set m_resolution = Nothing
Set m_severity = Nothing
Set m_type = Nothing
Set m_field = Nothing
End Sub

'IDを指定してチケットの情報を取得します
'戻り値がマップに変更になりました
Public Function getTicket(id As Long) As Map
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.get", id)
'取得した結果はCollectionなので一つ目の要素を取得して、
'次のArrayで返ってくる値の中から、id,createtime,timeを捨てます。
Set getTicket = result.Item(1).Item(4)
'idは捨てたので追加します
getTicket.add id, "id"
End Function

Public Function createTicket(summary As String, description As String, attributes As Map, notify As Boolean) As Long
Dim result As Collection
createTicket = 0
On Error Resume Next
'attributeの中にあっても意味がないものを削除
attributes.remove "id"
attributes.remove "summary"
attributes.remove "description"
attributes.remove "time"
attributes.remove "createtime"
On Error GoTo 0
Set result = m_xmlrpc.Send("ticket.create", summary, description, attributes, notify)
createTicket = result.Item(1)
End Function

Public Function updateTicket(id As Long, comment As String, attributes As Map, Optional notify As Boolean = False) As Map
On Error Resume Next
'attributeの中にあっても意味がないものを削除
attributes.remove "id"
attributes.remove "comment"
attributes.remove "time"
attributes.remove "changetime"
On Error GoTo 0
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.update", id, comment, attributes, notify)
Set updateTicket = result.Item(1).Item(4)
updateTicket.add id, "id"
End Function

Public Function getActions(id As Long) As Collection
Set getActions = m_xmlrpc.Send("ticket.getActions", id).Item(1) '変更されたチケットのIDが返ってくる
End Function

'チケットに対する変更内容が配列で取得できます
'配列の要素も配列になっていて、
'変更時刻、更新者、変更項目、旧値、新値になっている。
Public Function changeLog(id As Long) As Collection
Set changeLog = m_xmlrpc.Send("ticket.changeLog", id).Item(1) '変更されたチケットのIDが返ってくる
End Function

'チケット(Map)のCollectionを返します.
'チケットの情報がstructなので、Mapになりました
Public Function queryTicket(query As String) As Collection
Dim params As String
Set queryTicket = New Collection
If query = "" Then
params = ""
Else
params = "" & query & ""
End If
Dim result As Collection
Set result = m_xmlrpc.Send("ticket.query", query)
Dim n
For Each n In result.Item(1)
Dim ticket As Map, R As Collection
Set R = m_xmlrpc.Send("ticket.get", n)
Set ticket = R.Item(1).Item(4)
ticket.add n, "id"
queryTicket.add ticket, "" & n
Next
End Function

'==============================================================================

'milestone等の情報を取得します.指定できるのは次のものです.
'"resolution","milestone","version","component","priority","severity"
Private Function getAllArray(method As String, methodSub As String) As Map
Dim nms As Collection
Set getAllArray = New Map
Set nms = m_xmlrpc.Send(method)
Dim nm
For Each nm In nms.Item(1)
Dim c As Collection
Set c = m_xmlrpc.Send(methodSub, nm)
If c.Count <> 1 Then 'データが取得できない
err.Raise 0, "", ""
Else
getAllArray.add c.Item(1), "" & nm
End If
Next
End Function

Private Function ticketEnumGetAll(enumName As String) As Collection
Set ticketEnumGetAll = Send("ticket." & enumName & ".getAll").Item(1)
End Function

Public Property Get priority() As Collection
If m_priority Is Nothing Then
Set m_priority = ticketEnumGetAll("priority")
End If
Set priority = m_priority
End Property

Public Property Get resolution() As Collection
If m_resolution Is Nothing Then
Set m_resolution = ticketEnumGetAll("resolution")
End If
Set resolution = m_resolution
End Property

Public Property Get severity() As Collection
If m_severity Is Nothing Then
Set m_severity = ticketEnumGetAll("severity")
End If
Set severity = m_severity
End Property

Public Property Get ticketType() As Collection
If m_type Is Nothing Then
Set m_type = ticketEnumGetAll("type")
End If
Set ticketType = m_type
End Property


'表示順はCollectionの中の順になります。
'デフォルト値は設定できないので、どうなるのかわかりません
'名前は変更ではなく削除してからの追加なので、注意してください
Private Function ticketEnumReset(enumNm As String, colOld As Collection, colNew As Collection) As Collection
Dim itemName
Dim nm As String
'今あるものを削除する
For Each itemName In colOld
nm = itemName
Call Send("ticket." & enumNm & ".delete", nm)
Next
'追加する
Dim p As Integer
p = 1
For Each itemName In colNew
nm = itemName
Dim res As Collection
Set res = Send("ticket." & enumNm & ".create", nm, p)
p = p + 1
Next
Set ticketEnumReset = ticketEnumGetAll(enumNm)
End Function

Public Property Set priority(colNew As Collection)
Set m_priority = ticketEnumReset("priority", m_priority, colNew)
End Property

Public Property Set resolution(colNew As Collection)
Set m_resolution = ticketEnumReset("resolution", m_resolution, colNew)
End Property

Public Property Set severity(colNew As Collection)
Set m_severity = ticketEnumReset("severity", m_severity, colNew)
End Property

Public Property Set ticketType(colNew As Collection)
Set m_type = ticketEnumReset("type", m_type, colNew)
End Property

'=======================================================================
'
Private Function ticketModelGetAll(modelName As String) As Map
Set ticketModelGetAll = getAllArray("ticket." & modelName & ".getAll", "ticket." & modelName & ".get")
End Function

'二重のマップになる
'キーはマイルストン名
'due, completed, description, name
'dueのデフォルト値は0なので注意
Public Property Get milestone() As Map
If m_milestone Is Nothing Then
Set m_milestone = ticketModelGetAll("milestone")
End If
Set milestone = m_milestone
End Property

'二重のマップになる
'キーはバージョン名
'time, description, name
'timeのデフォルト値は0なので注意
Public Property Get version() As Map
If m_version Is Nothing Then
Set m_version = ticketModelGetAll("version")
End If
Set version = m_version
End Property

'二重のマップになる
'キーはコンポーネント名
'owner, description, name
'dueのデフォルト値は0なので注意
Public Property Get component() As Map
If m_component Is Nothing Then
Set m_component = ticketModelGetAll("component")
End If
Set component = m_component
End Property

'statusは更新できない
Public Property Get status() As Collection
If m_status Is Nothing Then
Set m_status = ticketEnumGetAll("status")
End If
Set status = m_status
End Property


'表示順はCollectionの中の順になります。
'デフォルト値は設定できないので、どうなるのかわかりません
'名前は変更ではなく削除してからの追加なので、注意してください
Private Function ticketModelReset(enumNm As String, colOld As Map, mapNew As Map) As Map
Dim itemName
Dim nm As String
'今あるものを削除する
On Error Resume Next
For Each itemName In colOld.Keys
nm = itemName
Call Send("ticket." & enumNm & ".delete", nm)
Next
On Error GoTo 0
'追加する
For Each itemName In mapNew.Keys
nm = itemName
Dim res As Collection
Set res = Send("ticket." & enumNm & ".create", nm, mapNew.Values.Item(nm))
Next
Set ticketModelReset = ticketModelGetAll(enumNm)
End Function

Public Property Set milestone(mapNew As Map)
Set m_milestone = ticketModelReset("milestone", m_milestone, mapNew)
End Property

Public Property Set version(mapNew As Map)
Set m_versione = ticketModelReset("version", m_version, mapNew)
End Property

Public Property Set componect(mapNew As Map)
Set m_componect = ticketModelReset("componect", m_componect, mapNew)
End Property


'==========================================================
Public Property Get field() As Map
If m_field Is Nothing Then
Dim res As Collection, f
Set m_field = New Map
Set res = m_xmlrpc.Send("ticket.getTicketFields")
For Each f In res.Item(1)
m_field.add f, f.Values.Item("name")
Next
End If
Set field = m_field
End Property

'============================================================================
Public Function TicketPutAttachment(id As Long, path As String, fileName As String, description As String) As String
Dim n As Long, fileSize As Long
n = FreeFile
fileSize = FileLen(path)
Dim buf() As Byte
ReDim buf(fileSize - 1)
Open path For Binary As #n
Get #n, , buf
Close #n

Dim c As Collection
Set c = m_xmlrpc.Send("ticket.putAttachment", id, fileName, description, buf)
TicketPutAttachment = c.Item(1)
End Function

'" ticket.listAttachments"
Public Function TicketListAttachments(id As Long) As Collection
Dim c As Collection
Set c = m_xmlrpc.Send("ticket.listAttachments", id)
Set TicketListAttachments = c.Item(1)
End Function

Public Function TicketGetAttachment(id As Long, path As String, fileName As String) As Collection
Dim c As Collection
Set c = m_xmlrpc.Send("ticket.getAttachment", id, fileName)

On Error Resume Next
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
FSO.DeleteFile path
Set FSO = Nothing
On Error GoTo 0

Dim n As Long
n = FreeFile
Dim buff() As Byte
Open path For Binary Access Write As #n
buff = c.Item(1)
Put #n, , buff
Close #n
Set TicketGetAttachment = c
End Function

'=======================================================================
'指定時刻以後に変更のあったチケットのidを配列で返します
Public Function getRecentChanges(since As Date) As Collection
'変更されたチケットのIDが返ってくる
Set getRecentChanges = m_xmlrpc.Send("ticket.getRecentChanges", since).Item(1)
End Function

'接続してレスポンスを得ます
Function Send(ParamArray val()) As Collection
Dim method As String, params As String
method = val(0)
Dim i As Integer
For i = 1 To UBound(val) '最初の一つは捨てる
params = params & "" & m_xmlrpc.CreateParamStr(val(i)) & ""
Next
Set Send = m_xmlrpc.Send0(method, params)
End Function

ほかのExcel連携の部分ができたらもう少し直してTrac-Hacksにあげますが、一応現状のものを上げておきます
「trac.xlsm」をダウンロード
何が起こっても責任とれないので、どうなってもいいところで使ってみてください。


| | コメント (0) | トラックバック (0)

«TracのカスタムフィールドとSubmitPolicyの設定をExcelの表から作る