Project

General

Profile

Actions

プロジェクトで利用可能にする

プラグインをプロジェクトごとに使用できるようするためにはプロジェクトモジュールとしての登録とプロジェクトメニューへの項目の追加が必要となります。

モジュールと permission の設定

モジュールの登録ではモジュール名とアクションごとの実行する権限(permission)を指定します。

権限 アクション
データの表示 index, show
データの管理 new, edit, create, update, destory, preview

権限の指定は init.rb の Redmine::Plugin.register メソッドで行います。次の記述をブロック内に追加してください。

  project_module :standard do
    permission :view_foos, :foos => [:index, :show]
    permission :manage_foos, :foos => [:new, :edit, :create, :update, :destroy, :preview],
               :require => :member
  end

project_module メソッドの引数でモジュール名(standard)を指定し、ブロック内で permission メソッドを呼び出して権限の指定をしています。

permission メソッドの書式は次の形です。

permission(name, hash, options={})

第一引数で権限の名前を指定します。
第二引数にはコントローラ名をキー、アクション名の配列を値としたハッシュを渡します。サンプルではコントローラは一つしかありませんが、複数あることもあるので、ハッシュで指定できるようになっています。
第三引数はオプションです。

Rails では次のようなスタイルがよくとられるので慣れておきましょう。

  • メソッドのオプション的な指定は最後の引数でシンボル名をキーとしたハッシュを使ってまとめて渡す
  • ハッシュが一つのペアしか持たなかったり、最後の引数などで括弧 {} が省略できる場合は省略する

このオプションの :require の指定を使って、データ管理の権限(:manage_foos)ではプロジェクトメンバー以外の Non member や Anonymous(ログインしていない人)では使えないようにしています。

それでは Redmine を起動して、プラグインをプロジェクトで使用できるようにしましょう。

まず、管理メニューにあるロールと権限の 権限レポート でチェックをいれて権限を与えます。

次にプラグインを使いたいプロジェクトの設定で Standard モジュールを有効にします。

このときの表示は指定したモジュール名などがほぼそのまま表示されているだけですが、これを日本語に変更する方法は国際化のところで説明します。

プラグインメニューへの追加

プロジェクトでモジュールを有効にしてもまだ何も変わりません。実際にこのプラグインを使えるようにするためにはプロジェクトメニューに項目を追加する必要があります。
プロジェクトメニューにはこのプラグインで最初に表示させたいページに対応するアクションを追加します。サンプルでは foos コントロールの index アクションとなります。

メニュー項目の追加も init.rb で行います。先ほどの記述の後に以下を加えます。

  menu :project_menu, :standard, { :controller => 'foos', :action => 'index'}, :param => :project_id

メニューの項目の追加は menu メソッドを使います。

menu(menu, item, url, options={})

第 1 引数はメニューの名前です。ここではプロジェクトメニューを指定します。指定できるメニューは サンプル仕様 で説明した 4 つです。

メニュー メニュー名
トップメニュー :top_menu
アカウントメニュー :account_menu
プロジェクトメニュー :project_menu
アプリケーションメニュー :application_menu

第 2 引数はメニューの項目名です。
第 3 引数はメニューをクリックしたときのリンク先の URL を指定します。
第 4 引数はオプションです。ここでは :param オプションで :project_id のキーに プロジェクト識別名 を割り当てています。

URL の指定方法

前節のメニューで行った URL の指定方法は link_to などでの他の Rails のリンクの指定方法と同じです。
URL の指定方法についてもう少し詳しく説明しましょう。

Rails の機能

URL として文字列を指定すると単純にその文字列が url として使用されますが、ハッシュで指定するとそれを元に URL を生成します。

指定するキーはコントローラ名(:controller)、アクション名(:action)と ID名(:id) で、そのキーの値を使って次のようなアドレスとなります。

サーバアドレス/コントローラ名/アクション名/ID名

ID 名は省略可能で省略されると ID 名の部分はなくなります。
param で追加された値は http の GET メソッドによる引数として渡されます。 GET メソッドについては後述しますが、URL にパラメーターを付ける方法だと思っておいてください。
サンプルのアドレスは以下のようになります。(プロジェクトの識別名は demo にしています)

http://127.0.0.1:3000/foos/index?project_id=demo

このアドレスの指定によりコントロールのアクションが実行されることになります。この場合は FoosController の index メソッドが呼ばれます。
ID は params というハッシュクラスの変数に格納されてます。params は $ マークは付いてませんが、グローバル変数のようなものだと考えてください。

Redmine の場合

Rails のアプリケーションではこの :id にプロジェクトの識別名を入れてつかうことが多いです。
しかし、 Redmine ではチケットなどで、プロジェクトの識別名を :id を使わずに :project_id に入れて、チケットなどの ID を :id に入れています。サンプルでもそれにあわせるようにしています。

しかし、チケットを表示した場合などの URL をみてもらうと Redmine では ?project_id=demo のような表記はありません。 これは Rails のルーティング機能を使って URL を変えているためです。

なお Redmine 1.4 以降ではデフォルトのルート設定が提供されなくなりました。このため各プラグインは適切なルートを config/routes.rb に設定する必要があります。

ルーティング

URL を設定するため config/routes.rb にどのように設定するかといった指定を記述します。
ここでは、 Redmine にあわせてメニューのアドレスが次のようになるようにしたいのですが、今回は Rails の RESTful 用の仕組みである resources マクロの機能を使います。

http://127.0.0.1:3000/projects/demo/foos

config/routes.rb に以下の内容のファイルを作成して下さい。

# Plugin's routes
# See: http://guides.rubyonrails.org/routing.html

Rails.application.routes.draw do
  resources :projects do
    resources :foos
  end
end

この resources の設定で7つの標準ルートが追加されました。生成されたルートを確認するには rake routes コマンドを使います。

$ rake routes

実行結果の Foos コントローラーに関する部分を抜粋したものが下記になります。
        project_foos GET          /projects/:project_id/foos(.:format)              foos#index      
                     POST         /projects/:project_id/foos(.:format)              foos#create     
     new_project_foo GET          /projects/:project_id/foos/new(.:format)          foos#new        
    edit_project_foo GET          /projects/:project_id/foos/:id/edit(.:format)     foos#edit       
         project_foo GET          /projects/:project_id/foos/:id(.:format)          foos#show
                     PUT          /projects/:project_id/foos/:id(.:format)          foos#update    
                     DELETE       /projects/:project_id/foos/:id(.:format)          foos#destroy

この各列に示されている情報は、左から順に(オプションの)ルート名、HTTPメソッド、ルートのパス、コントローラアクションになります。

このルートのパス設定に従った URL に指定の HTTP メソッドでアクセスすることで、目的としたコントローラのアクションが実行されます。
たとえば、 /projects/demo/foos/5 に GET メソッドでアクセスすると 5 のデータ詳細を表示、PUT メソッドでアクセスすると 5 のデータを更新、DELETE メソッドでアクセスすると 5 のデータを削除します。 5 のデータを狙って新規作成といった動作は想定していないので、 create メソッドでは、 :id を指定しない URL になっています。

今回 Redmine の実装をまねてアクションの名前をつけましたが、Redmine 自身が RESTful な実装なため標準ルートをそのまま適用できました。

ただし、標準ルートには preview アクションが含まれていないので、個別に追加しましょう。
次のように修正します。

# Plugin's routes
# See: http://guides.rubyonrails.org/routing.html

Rails.application.routes.draw do
  resources :projects do
    resources :foos do
       post  'preview', :on => :collection
       put   'preview', :on => :member
    end
  end
end
$ rake routes

実行結果の Foos コントローラーに関する部分を抜粋したものが下記になります。
preview アクションのルート設定が 2つ追加されました。
preview_project_foos POST         /projects/:project_id/foos/preview(.:format)      foos#preview
 preview_project_foo PUT          /projects/:project_id/foos/:id/preview(.:format)  foos#preview
        project_foos GET          /projects/:project_id/foos(.:format)              foos#index      
                     POST         /projects/:project_id/foos(.:format)              foos#create     
     new_project_foo GET          /projects/:project_id/foos/new(.:format)          foos#new        
    edit_project_foo GET          /projects/:project_id/foos/:id/edit(.:format)     foos#edit       
         project_foo GET          /projects/:project_id/foos/:id(.:format)          foos#show
                     PUT          /projects/:project_id/foos/:id(.:format)          foos#update    
                     DELETE       /projects/:project_id/foos/:id(.:format)          foos#destroy

index ページの表示

Redmine を再起動してモジュールを追加したプロジェクトを開いてみましょう。プロジェクトメニューに Standard が追加されています。

このメニューをクリックして下さい。 index.html.erb の内容のページが表示されます。

コントローラの index メソッドの中にはまだ何も書いていません。それにもかかわらず、 index ページが表示されています。これはコントローラのアクションが実行されるとアクション名のメソッドが実行され、デフォルトの状態ではアクション名に対応する views のファイルが表示されるためです。

@project の設定

ただし、表示されたページよく見ると何か変なことに気づくと思います。これは他のプロジェクトメニューと違いプロジェクトが開かれていないためです。このページをプロジェクトに属するものするためには FoosController クラスのインスタンス変数 @project に値を設定する必要があります。

app/controllers/foos_controller.rb の FoosController クラスの末尾に次のような @project を設定する find_project メソッドの定義を追加します。外から呼ばれることはありませんので、private メソッドにしています。


class FoosController < ApplicationController 
  unloadable 
  # : 
private
  def find_project
    @project = Project.find(params[:project_id])
  rescue ActiveRecord::RecordNotFound
    render_404
  end 
end 

params[:id] にはプロジェクトの識別名が入っています。
Project はプロジェクト用のモデルクラスです。 find メソッドは引数にプロジェクト識別名を渡すと、データベースから識別名が一致するプロジェクトを探し、Project のオブジェクトを返すクラスメソッドです。

Project クラスも ActiveRecord::Base クラスを継承して作成されており、データがみつからないと ActiveRecord::RecordNotFound の例外が発生します。コントローラ内で例外が発生すると通常の http サーバの場合 Internal Error のページになってしまいます。(WEBrick では詳細なエラー情報の画面になります。)
この例外をキャッチして render_404 を実行しています。 これはファイルが見つかりませんといった http の 404 エラーのページを表示するメソッドです。

定義した find_project メソッドを index メソッドの中で呼び出せばプロジェクト内のページとして表示されます。

before_filter

アクションは index だけではありません。他のアクションのメソッドでも同様に find_project を呼び出す必要があります。じゃ、 index の find_project をコピーして、他のメソッドにもペーストしていって ... と思った人はちょっと待ってください。 それでは同じことを何度も書く羽目になります。 Rails には DRY という繰り返し禁止の方針がありました。何か繰り返しを避ける方法があるはずです。

実際、避ける方法は用意されています。それは before_filter です。これでメソッドを指定するとそのメソッドはアクションのメソッドの前に呼ばれるようになります。
こちらはコントローラクラスの先頭の方に追加します。

class FoosController < ApplicationController
  unloadable
  before_filter :find_project
    # :

authorize

before_filter はカンマで区切って複数のメソッドを指定することが出来ます。もう一つ追加してみましょう。

class FoosController < ApplicationController
  unloadable
  before_filter :find_project, :authorize
    # :

追加した authorize はページを開いた時にログインしていない場合はログインのページ行く Redmine のメソッドです。通常、プロジェクトメニューから開かれるので、ログインしていることが多いと思いますが、ブラウザで前に開いていたページをもう一度開くといった設定がしてある場合などでは直接ページが指定されることもあります。そういったときにもちゃんとログインできるようにすべてのアクションの前に実行しておく必要があります。

権限の設定で index や show では設定によってはログインしてなくても、表示できる場合があるのではと疑問を持った方もいると思いますが、そういう場合も Redmine がログインしないように上手く処理してくれます。

権限の判定には現在のプロジェクトの情報が必要となります。before_filter は追加した順に実行されますので必ず find_project, authorize の順で記述して下さい。

ここまで設定して、もう一度 [Standard] のメニューを選択してみてください。今度はプロジェクトのページとして開かれているはずです。

まだ、少し変なところがあります。メニュー項目が選択状態になっていませんね。これを解決するにはコントローラ内で menu_item を使ってメニュー項目を指定します。

  menu_item :メニュー項目のシンボル

このメソッドで指定しなくてもメニュー項目が選択状態になることがあります。実はどうしたらそうなるのかは私もよく分かりません。とりあえず、選択状態にならないときはこのメソッドで指定してみてください。

これまでの記述を追加したコントローラのファイルの中身は次のようになります。

class FoosController < ApplicationController
  unloadable
  menu_item :standard
  before_filter :find_project, :authorize

  def index
  end

  def new
  end

  def show
  end

  def edit
  end

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

これでプロジェクトメニューにきちんと登録できるようになりました。


^ << >>

Updated by NAITOH Jun over 6 years ago · 10 revisions