Redmine プラグイン開発入門

git も使えるプロジェクト管理ソフトウェア Redmine
プラグインの開発方法のドキュメントが
日本語で見つからなかったので書いてみる。


ちなみに英語のドキュメントはこちら。
http://www.redmine.org/wiki/redmine/Plugin_Tutorial

今回作るプラグイン

プロジェクトごとのチケットの「作成者」と「担当者」を
それぞれチケット数が多い順に並べることで
貢献度を測るためのプラグイン。動作イメージは↓

環境準備

1. Redmine を入手する
(個人的には Subversion からのチェックアウトを推奨)
展開したディレクトリは以後 RAILS_ROOT とする


2. データベースの設定をする

$ cd RAILS_ROOT
$ mysql -u root
mysql> create database redmine;
mysql> exit
$ cp config\database.yml.example config\database.yml
$ rake db:migrate RAILS_ENV="production" 


3. サーバを立ち上げて確認する

$ ruby script/server -e production

http://localhost:3000/ にアクセスし admin/admin でログインできることを確認する。

プラグイン開発

1. 雛形を作る

$ cd RAILS_ROOT
$ ruby script/generate redmine_plugin Contributions

以下のディレクトリにプラグインの雛形が作成される。

RAILS_ROOT/vendor/plugins/redmine_contributions

ここからは上記のディレクトリを PLUGIN_ROOT と呼ぶ。


2. init.rb を編集する
init.rb にプラグインの基本的な情報を書き込む。
PLUGIN_ROOT/init.rb

require 'redmine'

Redmine::Plugin.register :redmine_contributions do
  name 'Redmine Contributions plugin'
  author 'mallowlabs'
  description 'This is a plugin witch shows user contributions'
  version '0.0.1'

  permission :contributions, {:contributions => [:index]}, :public => true
  menu :project_menu, :contributions, { :controller => 'contributions', :action => 'index' }, :caption => :contribution, :last => true, :param => :project_id
end

ポイントは,menu メソッドの第一引数を :project_menu にすること。
こうすることでプロジェクトのタブメニューに表示される。
permisson メソッドはよくわかってないけどこう書くと公開になる。


3. Controller を書く
Redmineプラグインはいってみれば rails アプリケーション。
通常のアプリケーションを作るように Controller を作る。
ただし、redmine_plugin_controller というものを generate する。
引数は、前から順番に、プラグイン名、コントローラ名、アクション名(複数可) である。

$ ruby script/generate redmine_plugin_controller Contributions contributions index

PLUGIN_ROOT/app/controllers/contributions_controller.rb

class ContributionsController < ApplicationController
  before_filter :find_project, :authorize, :only => :index

  def index
    author_hash = {}
    assign_hash = {}
    @project.issues.each do |issue|
      user = issue.author
      author_hash[user] = 0 unless author_hash[user]
      author_hash[user] += 1

      user = issue.assigned_to
      if user
        assign_hash[user] = 0 unless assign_hash[user]
        assign_hash[user] += 1
      end
    end
    @author_table = author_hash.to_a.sort do |a, b|
      (b[1] <=> a[1]) * 2 + (a[0] <=> b[0])
    end
    @assign_table = assign_hash.to_a.sort do |a, b|
      (b[1] <=> a[1]) * 2 + (a[0] <=> b[0])
    end
  end

  private
  def find_project
    @project = Project.find(params[:project_id])
  end
end

上記の before_filter と private メソッドはほぼ必須。
この @project が Model の Project に対応しており、
RAILS_ROOT/app/models/project.rb に対応している。


例えば以下のようにするとプロジェクトの情報を自由自在に取得できる。

@project.issues # チケット取得
@project.users  # ユーザ情報取得
@project.repository # リポジトリの情報を取得

それぞれがどのようなメソッドを持つかは Model のソースを読むべし。


5. View を書く
PLUGIN_ROOT/app/views/contributions/index.html.erb

<h2><%= l(:contribution) %></h2>
<table>
  <tr>
    <td style="width:50%;vertical-align:top;">
      <table class="list">
        <tr>
          <th><%= l(:ticket_author) %></th>
          <th><%= l(:ticket_count) %></th>
        </tr>
        <% @author_table.each do |user, count| %>
        <tr class="<%= cycle('odd', 'even')%>">
          <td><%= link_to h(user), :controller => 'account', :action => 'show', :id => user.id %></td>
          <td align="right"><%= count %></td>
        </tr>
        <% end %>
      </table>
    </td>
    <td style="width:50%;vertical-align:top;">
      <table class="list">
        <tr>
          <th><%= l(:ticket_assigned) %></th>
          <th><%= l(:ticket_count) %></th>
        </tr>
        <% @assign_table.each do |user, count| %>
        <tr class="<%= cycle('odd', 'even')%>">
          <td><%= link_to h(user), :controller => 'account', :action => 'show', :id => user.id %></td>
          <td align="right"><%= count %></td>
        </tr>
        <% end %>
      </table>
    </td>
  </tr>
</table>

コントローラから渡されたデータを Table にして表示するだけ。
l(:contribution) は後述。


5. 国際化
init.rb の以下の記述や

:caption => :contribution

View の以下の記述は

l(:contribution)

国際化のための記述である。
言語ファイルを作成し、国際化したい箇所をシンボルで指定することにより
簡単に国際化することができる。


ということで言語ファイルを作成する。
PLUGIN_ROOT/lang/ja.yml

# Japanese strings go here
contribution: "貢献"
ticket_author: "作成者"
ticket_assigned: "担当者"
ticket_count: "チケット数"

これだけ。簡単。
もちろん en.yml も編集しておくこと。


6. 動作確認
ここまでで Redmine にアクセスし、
プロジェクトのタブメニューから「貢献」を選ぶと
上のスクリーンショットの画面が表示される(はず)。

まとめ

つまるところ以下の手順でプラグインが開発できる。

  1. script/generate redmine_plugin でプラグインの雛形を作る
  2. init.rb を編集する
  3. Contoroller を書く(@project を基点にプロジェクトの情報を得る)
  4. View を書く
  5. 言語ファイルを書く

困ったときは Redmine 自体のソースコードを読むことで
問題が解決することが多いように感じた。