Redmine3.2プラグイン開発入門 (1) - 新規画面を追加する

昨年末Redmineのプラグイン開発方法について調べる機会があり、
冬休みに復習したので備忘録としてまとめておきます！
[参考にしたページ](http://redmine.jp/tech_note/plugins/developer/)にはRedmineのプラグイン開発でできることとして、

- 新規画面の追加
- 既存画面に項目を追加
- Redmine本体のメソッドの置き換えによる動作のカスタマイズ

とありますので、3回に分けて1つずつやってみました！
今回は1つ目の**新規画面の追加**についてです。

# 開発環境
以下の環境で試しました。Redmineのインストール等は割愛します。

- CentOS 6.6
- Ruby 2.2.3
- Rails 4.2.5
- Redmine 3.2

# 新規画面の追加
Redmineのプラグインではプロジェクトのメニューやアプリケーションメニューに新しい画面を追加できます。
今回はサンプルとして、[PivotTable.js](https://github.com/nicolaskruchten/pivottable)を使ってチケットをドラッグ&ドロップで簡単にピボット分析できる機能を追加してみます。
最終的に以下のような画面ができます。

![00.png](https://qiita-image-store.s3.amazonaws.com/0/17731/80ecfe5a-7655-e32f-04f0-6e8507c5f9d0.png)

※ちなみにPivotTable.jsを使った[プラグイン](https://github.com/deecay/redmine_pivot_table)はすでにあったりします。

## 1. ひな形の作成
Redmineにはあらかじめプラグインのひな形を作成するコマンドが用意されているので、それを使います。

```bash:
# 引数にプラグイン名
bundle exec rails generate redmine_plugin Sample
```

controllerやmodelを作りたい場合も、以下のようにコマンドが用意されています。

```bash:
# controller
# 引数はplugin名, controller名, action名
bundle exec rails generate redmine_plugin_controller sample test index
# model
# 引数はplugin名, model名, カラム情報
bundle exec rails generate redmine_plugin_model sample test title:string content:text
```

プラグインのひな形はpluginsディレクトリ以下に作成されます。
Sampleというプラグインを作成した場合、sampleというディレクトリが作られます。

## 2. 基本情報の編集
sampleディレクトリの直下にinit.rbというファイルがあります。
ここにプラグインの基本情報を載せます。

```rb:init.rb
Redmine::Plugin.register :sample do
  name 'Sample plugin'
  author 'saitoxu'
  description 'This is a sample plugin'
  version '0.0.1'
  url 'http://example.com/path/to/plugin'
  author_url 'http://example.com/about'
end
```

ここでRedmineを再起動すると、プラグインのページに以下のように表示されます。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/17731/cd727572-bbe5-3b84-059f-a7c969578310.png)


## 3. controllerの追加
以下のコマンドでcontrollerを新規に作ります。

```bash:
bundle exec rails generate redmine_plugin_controller sample pivot index
```

今回はプラグインのページを表示するメソッドとPivotTable.jsにajaxでチケットのデータを渡すメソッドの2つが必要です。

```rb:app/controllers/pivot_controller.rb
class PivotController < ApplicationController
  unloadable

  # プラグインのトップページ
  def index
  end

  # チケットのjsonデータ
  def list
    issues = convert_issues(Issue.where(project_id: "#{@project.id}"))
    render json: issues
  end

  private

  # チケットのデータをPivotTable.js用に変換
  def convert_issues(org_issues)
    issues = []
    org_issues.each { |org_issue|
      tracker  = org_issue.tracker.nil? ? nil : org_issue.tracker.name
      category = org_issue.category.nil? ? nil : org_issue.category.name
      status   = org_issue.status.nil? ? nil : org_issue.status.name
      priority = org_issue.priority.nil? ? nil : org_issue.priority.name
      assignee = org_issue.assigned_to.nil? ? nil : org_issue.assigned_to.name
      author   = org_issue.author.nil? ? nil : org_issue.author.name

      issue = {
        tracker:  tracker,
        category: category,
        status:   status,
        priority: priority,
        assignee: assignee,
        author:   author
      }

      issues.push(issue)
    }
    return issues
  end
end
```

## 4. 権限の設定
Redmineのメイン機能同様、プラグインでも機能ごとに細かく権限を設定できます。
権限の設定はinit.rbで行います。
今回はピボット分析画面の閲覧権限だけ設定できるようにします。

```rb:init.rb
Redmine::Plugin.register :sample do
  
  ...

  # 引数はplugin名
  project_module :sample do
    # 引数は権限名, controllerとactionからなるhash
    permission :view_pivot, :pivot => [:index]
  end
end
```

これで以下のようにロール毎に権限を付与するかどうか設定できるようになります。

![02.png](https://qiita-image-store.s3.amazonaws.com/0/17731/b41deb39-bb65-f955-dead-666c7aa1c88c.png)


controllerの方にはindexメソッドを呼ぶ前にauthorizeメソッドを呼ぶようにし、上記の閲覧権限があるユーザだけ実行できるようにします。

```rb:app/controllers/pivot_controller.rb
class PivotController < ApplicationController
  unloadable
  before_filter :authorize, only: :index

  # ...

end
```

## 5. メニューへの追加
次にプラグインのメニューをプロジェクトメニューに追加します。

```rb:init.rb
Redmine::Plugin.register :sample do

  # ...

  # 引数はメニューのタイプ, メニュー名, メニュークリック時に呼び出されるaction, オプション
  menu :project_menu, :pivot, { :controller => 'pivot', :action => 'index' }, :param => :project_id
end
```

第一引数のメニューのタイプについては[こちら](http://www.r-labs.org/projects/r-labs/wiki/GuideSampleSpec)が参考になります。

これでプロジェクトメニューに表示されるようにはなりますが、
プラグインの画面選択前に内部的にプロジェクトを選択する必要があるのでcontrollerに以下を追記します。

```rb:app/controllers/pivot_controller.rb
class PivotController < ApplicationController
  unloadable
  before_filter :find_project # これ
  before_filter :authorize, only: :index

  # ...

  private

  # ...

  # プロジェクトの選択
  def find_project
    @project = Project.find(params[:project_id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end
end
```


## 6. ルーティングの設定
次に、ルーティングの設定をします。

```rb:config/routes.rb
Rails.application.routes.draw do
  resources :projects do
    get 'pivot'      => 'pivot#index'
    get 'pivot/list' => 'pivot#list'
  end
end
```

ここまでで画面が正しく表示されるようになりました。

![03.png](https://qiita-image-store.s3.amazonaws.com/0/17731/7838355a-26f1-63e2-747e-3f56de64904e.png)


## 7. 画面の編集
次にviewを編集します。
といっても今回はPivotTable.jsを読みこむだけです。

```erb:app/views/pivot/index.html.erb
<%= stylesheet_link_tag 'pivot', :plugin => 'sample', :media => 'all' %>
<%= javascript_include_tag 'pivot', :plugin => 'sample', :media => 'all' %>
<div id="output"></div>
<script type="text/javascript">
  $.getJSON("pivot/list.json", function(data) {
    $("#output").pivotUI(data);
  });
</script>
```

view内で指定した`pivot.js`と`pivot.css`はそれぞれ`assets/javascripts`と`assets/stylesheets`以下に配置します。
※PivotTable.jsをダウンロードしてください。

## 8. 多言語化対応
言語別のYAMLファイルにシンボルとメッセージを記述します。
メニュー名は`label_メニュー名`、モジュール名は`project_module_モジュール名`、権限名は`permission_権限名`と入力すると自動で対応してくれます。

```yaml:config/locales/ja.yml
ja:
  label_pivot: ピボット
  project_module_sample: サンプル
  permission_view_pivot: ピボットテーブルの閲覧
```

以上で完成です！

# 終わりに
小さなプラグインであれば、けっこう簡単に作成できることが分かりました。
次はRedmineの既存画面に新しい項目を追加する方法についてまとめたいと思います！

# 参考
- [プラグイン チュートリアル — Redmine Guide 日本語訳](http://redmine.jp/guide/Plugin_Tutorial/)
- [プラグイン開発ガイド - r-labs](http://www.r-labs.org/projects/r-labs/wiki/%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E9%96%8B%E7%99%BA%E3%82%AC%E3%82%A4%E3%83%89)