プロジェクト

全般

プロフィール

GuideForProject » 履歴 » リビジョン 8

リビジョン 7 (Mitsuyoshi Yoshida, 2012/01/19 22:51) → リビジョン 8/10 (NAITOH Jun, 2013/03/06 23:47)

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

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


 h2. モジュールと permission の設定 

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

 |_. 権限         |_. アクション         | 
 | データの表示 | index, view, preview view          | 
 | データの管理 | new, edit, destory | 

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

 <pre><code class="ruby"> 
   project_module :standard do 
     permission :view_foos, :foos => [:index, :show, :preview] :show] 
     permission :manage_foos, :foos => [:new, :edit, :destroy], 
                :require => :member 
   end 
 </code></pre> 

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

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

 <pre><code class="ruby"> 
 permission(name, hash, options={}) 
 </code></pre> 

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

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

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

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

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

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

 !permission.png! 

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

 !module_add.png! 

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


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

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

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

 <pre><code class="ruby"> 
   menu :project_menu, :standard, { :controller => 'foos', :action => 'index'}, :param => :project_id 
 </code></pre> 

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

 <pre><code class="ruby"> 
 menu(menu, item, url, options={}) 
 </code></pre> 

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

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

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


 h2. URL の指定方法 

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


 h2. Rails の機能 

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

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

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

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

 <pre> 
 http://127.0.0.1:3000/foos/index?project_id=demo 
 </pre> 

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


 h3. Redmine の場合 

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

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

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

 


 h3. routes 

 URL を設定するため を変更するには config/routes.rb にどのように設定するかといった指定を記述します。 にどのように変更するかといった指定を記述します。 
 ここでは、 Redmine にあわせてメニューのアドレスが次のようになるようにします。 

 <pre> 
 http://127.0.0.1:3000/projects/demo/foos/index 
 </pre> 

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

 <pre><code class="ruby"> 
 # Plugin's routes 
 # See: http://guides.rubyonrails.org/routing.html 
 Rails.application.routes.draw ActionController::Routing::Routes.draw do |map| 
   match 'projects/:project_id/foos/:action', :to map.with_options :controller => 'foos' do |foos| 
     foos.connect 'projects/:project_id/foos', :action => 'index' 
     foos.connect 'projects/:project_id/foos/:id', :action => 'show', :id => /\d+/ 
     foos.connect 'projects/:project_id/foos/:action/:id' 
   match 'projects/:project_id/foos/:action/:id', :to => 'foos' end 
 end 
 </code></pre> 



 h2. index ページの表示 

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

 !sample_proj_menu.png! 

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

 !index_no_project.png! 

 コントローラの index メソッドの中にはまだ何も書いていません。それにもかかわらず、 index ページが表示されています。これはコントローラのアクションが実行されるとアクション名のメソッドが実行され、デフォルトの状態ではアクション名に対応する views のファイルが表示されるためです。 
 スケルトンとして生成されたファイルは index.html.erb ですが、検索されるファイルはアクション名の後は .rhtml, .erb のような Erb の html を表す拡張子であれば何でもかまいません。 


 h2. @project の設定 

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



 app/controllers/foos_controller.rb の 

 FoosController クラスの末尾に次のような @project を設定する *find_project* メソッドの定義を追加します。外から呼ばれることはありませんので、private メソッドにしています。  
 <pre><code class="ruby">  
 class FoosController < ApplicationController  
   unloadable  
   # :  
 private 
   def find_project 
     @project = Project.find(params[:project_id]) 
   rescue ActiveRecord::RecordNotFound 
     render_404 
   end  
 end  
 </code></pre>  

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

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

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


 h2. before_filter 

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

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

 <pre><code class="ruby"> 
 class FoosController < ApplicationController 
   unloadable 
   before_filter :find_project 
     # : 
 </code></pre> 


 h2. authorize 

 before_filter はカンマで区切って複数のメソッドを指定することが出来ます。もう一つ追加してみましょう。 
 <pre><code class="ruby"> 
 class FoosController < ApplicationController 
   unloadable 
   before_filter :find_project, :authorize 
     # : 
 </code></pre> 

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

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

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

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

 !index_no_tab.png! 

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

 <pre><code class="ruby"> 
   menu_item :メニュー項目のシンボル 
 </code></pre> 

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

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

 <pre><code class="ruby"> 
 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[:id]) 
   rescue ActiveRecord::RecordNotFound 
     render_404 
   end 
 end 
 </code></pre> 

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

 !index_tab.png! 

 ---- 

 | [[プラグイン開発ガイド|^]] | [[GuideControlSkelton|<<]] | [[GuideI18n|>>]] |