プロジェクト

全般

プロフィール

www.redmine.orgのPlugin Turorial を和訳してみます。

プラグイン チュートリアル

注意: このチュートリアルはRedmineの開発リビジョン r1786 以上を対象にしています。

新しいプラグインを作る。

プラグインの新規作成はRedmineのプラグインジェネレータを使用して行うことができます。
ジェネレータのコマンドは以下です。:

ruby script/generate redmine_plugin <plugin_name>

コマンドプロンプトを開き "cd" であなたがredmineをインストールしたディレクトリに移動し、以下のコマンドを実行してみましょう。:

% ruby script/generate redmine_plugin Polls

vendor/plugins/redmine_polls の下に以下のようなファイルとディレクトリが作られます:

      create  vendor/plugins/redmine_polls/app/controllers
      create  vendor/plugins/redmine_polls/app/helpers
      create  vendor/plugins/redmine_polls/app/models
      create  vendor/plugins/redmine_polls/app/views
      create  vendor/plugins/redmine_polls/db/migrate
      create  vendor/plugins/redmine_polls/lib/tasks
      create  vendor/plugins/redmine_polls/assets/images
      create  vendor/plugins/redmine_polls/assets/javascripts
      create  vendor/plugins/redmine_polls/assets/stylesheets
      create  vendor/plugins/redmine_polls/lang
      create  vendor/plugins/redmine_polls/README
      create  vendor/plugins/redmine_polls/init.rb
      create  vendor/plugins/redmine_polls/lang/en.yml

vendor/plugins/redmine_polls/init.rb を編集してあなたのプラグインの情報を書き込んでください。(name, author, description および version):

require 'redmine'

Redmine::Plugin.register :redmine_polls do
  name 'Polls plugin'
  author 'John Smith'
  description 'A plugin for managing polls'
  version '0.0.1'
end

そしてRedmineを起動し、ブラウザから次のアドレスを入力します。http://localhost:3000/admin/info.
ログイン後、あなたのプラグインがプラグイン一覧に加わっていることが確認できます。

モデルを作る

それではこのプラグインのモデルとして Poll を作ってみましょう。:

ruby script/generate redmine_plugin_model polls poll question:string yes:integer no:integer

このコマンドで Poll モデルおよびPollモデルに対応したマイグレーションファイルが作成されます。
(訳注:マイグレーションファイルとは、RedmineのDBにこのモデル用のテーブルを作成するためのスクリプトファイルです)

ここで注意が必要です。timestamped migrationは今のRedmineプラグインエンジンではサポートされていません。ファイル名についているタイムスタンプを"001", "002"のような名前に変更してください。

実際にデータベースへのマイグレーションを行うためには以下のコマンドを実行します。:

rake db:migrate_plugins

すべてのプラグインはそれぞれに自身のマイグレーションファイルを持っています。

それではconsoleスクリプトからPollのデータを追加してみましょう。ちゃんとテーブルが作られていることを確認できます。consoleを使用すると対話的にRedmineを動かして確認できます。また、遊びながらいろいろな情報を得ることができます。それでは2つのPollオブジェクトを作ります。

script/console
>> Poll.create(:question => "Can you see this poll ?")
>> Poll.create(:question => "And can you see this other poll ?")
>> exit

プラグインディレクトリの vendor/plugins/redmine_polls/app/models/poll.rb を編集して #vote メソッドを追加しましょう。このメソッドはコントローラから実行されるものです。:

class Poll < ActiveRecord::Base
  def vote(answer)
    increment(answer == 'yes' ? :yes : :no)
  end
end

コントローラを作成する。

この段階ではまだこのプラグインは何もすることができません。このプラグインにコントローラを追加してみましょう。コントローラの作成にはプラグインコントローラジェネレータを使用できます。文法は以下です。:

ruby script/generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]

ではコマンドプロンプトから以下のように打ってみましょう。:

% ruby script/generate redmine_plugin_controller Polls polls index vote
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/polls
      create  test/functional/
      create  app/controllers/polls_controller.rb
      create  test/functional/polls_controller_test.rb
      create  app/helpers/polls_helper.rb
      create  app/views/polls/index.html.erb
      create  app/views/polls/vote.html.erb

コントローラ PollsController と2つのアクション (#index and #vote) が作成されます。

vendor/plugins/redmine_polls/app/controllers/polls_controller.rb を編集して 2つのアクションを実装します。

class PollsController < ApplicationController
  unloadable

  def index
    @polls = Poll.find(:all)
  end

  def vote
    poll = Poll.find(params[:id])
    poll.vote(params[:answer])
    if poll.save
      flash[:notice] = 'Vote saved.'
      redirect_to :action => 'index'
    end
  end
end

そして vendor/plugins/redmine_polls/app/views/polls/index.html.erb を編集すると先ほど作成した2つのpollを表示できます。:

<h2>Polls</h2>

<% @polls.each do |poll| %>
  <p>
  <%= poll[:question] %>?
  <%= link_to 'Yes', {:action => 'vote', :id => poll[:id], :answer => 'yes'}, :method => :post %> (<%= poll[:yes] %>) /
  <%= link_to 'No', {:action => 'vote', :id => poll[:id], :answer => 'no'}, :method => :post %> (<%= poll[:no] %>)
  </p>
<% end %>

vendor/plugins/redmine_polls/app/views/polls/vote.html.erb は対応するactionから使われることがないので削除してよいです。

さあ、Redmineを再起動してブラウザから http://localhost:3000/polls にアクセスしましょう。
2つのpollが確認でき、それらに投票することができるでしょう。:

もしRedmineをプロダクションモードで動かしていない場合、pollの結果はリクエスト毎にリセットされます。これは今回の例ではpollモデルをclass変数に格納しているためです。

メニューを拡張する。

コントローラは動くようになりましたが、このままではユーザはURLを知らない限り投票画面を見ることができません。RedmineのプラグインAPIを使用するとメニューを拡張できます。アプリケーションメニューに新しい項目を追加してみましょう。

アプリケーションメニューを拡張する。

vendor/plugins/redmine_polls/init.rb を編集してplugin registration blockの最後に以下の行を追加してください。:

Redmine::Plugin.register :redmine_polls do
  [...]

  menu :application_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls'
end

文法は以下です。:

menu(menu_name, item_name, url, options={})

拡張できるメニューは4種類です。:

  • :top_menu - 最上部の左側のメニュー。
  • :account_menu - 最上部の右側のメニュー。ログイン/ログアウトがある。
  • :application_menu - プロジェクトの外でのメインメニュー。
  • :project_menu - プロジェクト内でのメインメニュー。

以下のオプションが使えます。:

  • :param - プロジェクトIDを渡すのに用いられる変数のキー。 (デフォルトは :id)
  • :if - メニュー項目のレンダリング前に呼ばれるProc。メニュー項目はこのProcの戻りがtrueの場合にのみ表示される。
  • :caption - メニューのキャプション。以下が使えます。:
    • ローカライズ文字列用シンボル
    • 文字列
    • projectを引数としたProc
  • :before, :after - メニューを挿入する場所の指定 (例 :after => :activity 訳注:「活動」ページの後ろに挿入される)
  • :last - trueに設定するとメニューの最後に追加される。 (例 :last => true)
  • :html_options - html オプションのHash。メニュー表示時の link_to に渡される。訳注: link_toはRailsのAPI

今回の例ではメニュー項目をアプリケーションメニューに追加します。アプリケーションメニューはデフォルトでは空です。
Redmineを再起動して http://localhost:3000 にアクセスしてみましょう。:

ようこそ画面のPollsタブをクリックして投票画面に行けるはずです。

プロジェクトメニューを拡張する。

さあ、今度はpollsをプロジェクト用プラグインとした場合の例です。(今回のpollモデルはそう設計されてませんが)。Pollsタブをプロジェクトメニューに追加しましょう。
init.rb を開いて先ほど追加した行を以下の2行に書き換えてください。:

Redmine::Plugin.register :redmine_polls do
  [...]

  permission :polls, {:polls => [:index, :vote]}, :public => true
  menu :project_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls', :after => :activity, :param => :project_id
end

2行目がプロジェクトメニューにPollsタブを追加する定義です。活動タブのすぐ後ろに追加します。
最初の行は PollsController の2つのアクションをpulicにするための設定です。詳細については後ほど説明します。

Redmineを再起動し、プロジェクトを表示します。:

Pollsタブをクリックしてください。プロジェクトメニューが消えてしまうことに気付きましたか?
プロジェクトメニューを表示しておくためにはコントローラのインスタンス変数 @project を設定してあげる必要があります。

PollsController を以下のように編集します。:

def index
  @project = Project.find(params[:project_id])
  @polls = Poll.find(:all) # @project.polls
end

プロジェクトIDはparamの中の :project_id に格納されています。なぜなら先ほどのメニューの定義で :param => :project_id と設定したからです。

さあ、これでPollsタブをクリックしてもプロジェクトメニューが消えなくなりました。:

パーミッションを定義する。

この状態ではすべての人が投票を行うことができます。ではパーミッションの定義をしてみましょう。
ここでは2種類のプロジェクトベースのパーミッションを定義します。ひとつはpollsの表示に関するもの、もう一つは投票に関するものです。これを行うと、パーミッションはpublicでは無くなります。(:public => true オプションは削除します).

vendor/plugins/redmine_polls/init.rb を編集して先ほどのパーミッション定義を以下の2行に置き換えます。:


  permission :view_polls, :polls => :index
  permission :vote_polls, :polls => :vote

Redmineを再起動して次のURLにアクセスしてみましょう。 http://localhost:3000/roles/report:

これで既存のロールに対してパーミッションを設定できるようになりました。

もちろん、パーミッションに応じてユーザのアクセス制御を行うためにはPollsControllerにコードを追加する必要があります。

今回の場合は :authorize フィルターを追加すること、およびこのフィルターが呼ばれる前に必ず @project に値がセットされるようにするだけです。

#index アクションでは以下のようにします。:

class PollsController < ApplicationController
  unloadable

  before_filter :find_project, :authorize, :only => :index

  [...]

  def index
    @polls = Poll.find(:all) # @project.polls
  end

  [...]

  private

  def find_project
    # @project variable must be set before calling the authorize filter
    @project = Project.find(params[:project_id])
  end
end

#vote アクションが動く前に現在のプロジェクトを取得します。これを行うと、投票は管理者もしくはこのプロジェクトの許可されたロールのユーザ以外行えなくなります。

プロジェクトモジュールを作る。

今現在、pollの機能はすべてのプロジェクトに追加されています。しかし、pollsを限られたプロジェクトのみに使わせたい場合もあるでしょう。
ここでは 'Polls' プロジェクトモジュールを作成します. これはパーミッション定義を #project_module 定義で囲むことによって実現します。

init.rb を編集してパーミッションの定義を変更します。:

  project_module :polls do
    permission :view_polls, :polls => :index
    permission :vote_polls, :polls => :vote
  end

Redmineを再起動し、適当なプロジェクトの設定メニューを開きます。そしてモジュールタブをクリックします。するとモジュールリストの最後にPolls moduleがあることが確認できます。 (デフォルトではチェックはついていません):

これでプロジェクト毎にPollsの有効・無効を選択できるようになりました。

プラグインの表示を拡張する。

スタイルシートを追加する。

それではプラグインのviewにスタイルシートを追加してみましょう。
voting.css というファイルを vendor/plugins/redmine_polls/assets/stylesheets に作成してください。:

a.vote { font-size: 120%; }
a.vote.yes { color: green; }
a.vote.no  { color: red; }

Redmineを再起動すると、assetsの下はRails Enginesによって自動的に public/plugin_assets/redmine_polls/ にコピーされます。これによってWebブラウザからこれらのファイルにアクセスできるようになります。なのでassetsの下のスタイルシートやJavascriptに変更を加えた場合には必ずRedmineの再起動が必要になります。

vendor/plugins/redmine_polls/app/views/polls/index.html.erb に以下の行を加えてください。するとスタイルシートがRedmineのヘッダーからインクルードされます。:

<% content_for :header_tags do %>
    <%= stylesheet_link_tag 'voting', :plugin => 'redmine_polls' %>
<% end %>

:plugin => 'redmine_polls' オプションを stylesheet_link_tag ヘルパーに渡します。

Javascript をプラグインのビューからインクルードするためにはjavascript_include_tag ヘルパーを同様に使います。

ページタイトルを設定する。

プラグインのviewにHTMLのタイトルを設定するには html_title ヘルパーを使います。
例:

<% html_title "Polls" -%>

プラグインをテストする

test/test_helper.rb:

テストヘルパーファイルの中に以下のように記述します。:

require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')

テストのサンプル:

#訳注:これ以後、本家のチュートリアルに追記されたテストに関する記述の訳ですが、ここまでと書いた人が違うようです。サンプルコードもPollsプラグインでは無くなってます。

requirements_controller_test.rb:

require File.dirname(__FILE__) + '/../test_helper'
require 'requirements_controller'

class RequirementsControllerTest < ActionController::TestCase
  fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
           :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
           :attachments, :custom_fields, :custom_values, :time_entries

  def setup
    @skill = Skill.new(:skill_name => 'Java')
    @project = Project.find(1)
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    User.current = nil
  end

  def test_routing
    assert_routing(
      {:method => :get, :path => '/requirements'},
      :controller => 'requirements', :action => 'index'
    )
  end

テスト用 DBを初期化する。:

以下のようなrake コマンドを実行することにより、テスト用DBを初期化することができます。:

rake db:drop:all db:create:all db:migrate db:migrate_plugins redmine:load_default_data RAILS_ENV=test

テストを実行する:

reqruirements_controller_test.rb のテストを行うには以下のコマンドを実行します。:

rake test:engines:all PLUGIN=redmine_requirements

ユーザおよびプロジェクトと共にテストする。

あなたのプラグインがプロジェクトメンバでなければ操作できない場合、functional testの最初に以下の記述を追加します。:

def setup
  @request    = ActionController::TestRequest.new
  @response   = ActionController::TestResponse.new
  User.current = nil
end

def test_index
  @request.session[:user_id] = 2
  get :index, :project_id => 1
  assert_response :success
  assert_template :index
end

この方法が正しいのはよく判りませんが、とりあえず正しく動いているように見えます。 :S