專案

一般

個人檔案

動作

外掛程式教學 » 歷史 » 修訂版 66

« 上一版 | 修訂版 66/119 (差異) | 下一版 »
Etienne Massip, 2012-05-25 11:13


外掛程式教學

備註:要遵循本教學,您需要執行 Redmine 開發版 r1786 或更高版本。

建立新的外掛程式

您可能需要設定 RAILS_ENV 變數才能使用以下命令

$ export RAILS_ENV="production" 

在 Windows 上

set RAILS_ENV=production

可以使用 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/config/locales
      create  vendor/plugins/redmine_polls/test
      create  vendor/plugins/redmine_polls/README.rdoc
      create  vendor/plugins/redmine_polls/init.rb
      create  vendor/plugins/redmine_polls/config/locales/en.yml
      create  vendor/plugins/redmine_polls/test/test_helper.rb

編輯 vendor/plugins/redmine_polls/init.rb 以調整外掛程式資訊(名稱、作者、描述和版本)

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

然後重新啟動應用程式並將您的瀏覽器指向 https://127.0.0.1:3000/admin/plugins
登入後,您應該會在外掛程式清單中看到您的新外掛程式

產生模型

目前外掛程式不會儲存任何內容。讓我們為外掛程式建立一個簡單的 Poll 模型。語法為

   ruby script/generate redmine_plugin_model <plugin_name> <model_name> [<fields>]

因此,請前往命令提示字元並執行

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

這將建立 Poll 模型和對應的移轉檔案。

**請注意,您可能必須重新命名您的移轉。** 實際的 Redmine 外掛程式引擎 (Engines) 不支援帶有時間戳記的移轉。如果您的移轉名稱帶有時間戳記,請使用「001」、「002」等重新命名。

mv vendor/plugins/redmine_polls/db/migrate/20091009211553_create_polls.rb vendor/plugins/redmine_polls/db/migrate/001_create_polls.rb

如果您已經在 plugin_schema_info 中使用時間戳記版本號建立了資料庫表格記錄,則必須更改它以反映新的版本號,否則移轉將會掛起。

使用以下命令移轉資料庫

rake db:migrate_plugins

請注意,每個外掛程式都有自己的一組移轉。

讓我們在控制台中新增一些投票,以便我們有一些可以使用的東西。控制台是您可以互動式地工作和檢查 Redmine 環境的地方,並且對於到處玩耍非常有幫助。但現在我們只需要建立兩個 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

產生控制器

警告:從 1.4.0 版本開始,Redmine 不再提供預設的萬用字元路由 (':controller/:action/:id')。插件需要在其 config/routes.rb 檔案中宣告所需的路由。

目前,這個插件沒有做任何事情。所以讓我們為我們的插件建立一個控制器。
我們可以使用插件控制器產生器來完成。語法如下:

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 控制器,包含兩個動作 (#index#vote)。

redmine_polls 目錄中編輯 vendor/plugins/redmine_polls/app/controllers/polls_controller.rb,實作這兩個動作。

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,這個檔案將會顯示現有的投票。

<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,因為對應的動作沒有進行任何渲染。

現在,重新啟動應用程式並將您的瀏覽器指向 https://127.0.0.1:3000/polls
您應該會看到兩個投票,並且您應該可以對它們進行投票。

請注意,如果您沒有在生產環境模式下執行應用程式,則每次請求時投票結果都會重置,因為在這個範例中,我們的投票「模型」是儲存在一個類別變數中。

翻譯

*.yml 翻譯檔案的位置取決於您正在執行的 Redmine 版本。

版本 路徑
< 0.9 .../redmine_polls/lang
>= 0.9 .../redmine_polls/config/locales

如果您希望您的插件在兩個版本中都能正常運作,則需要在兩個位置都放置相同的翻譯檔案。

擴展選單

我們的控制器可以正常運作,但使用者必須知道網址才能看到投票。使用 Redmine 插件 API,您可以擴展標準選單。
讓我們在應用程式選單中新增一個項目。

擴展應用程式選單

在插件目錄的根目錄下編輯 vendor/plugins/redmine_polls/init.rb,在插件註冊區塊的末尾新增以下行:

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

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

語法如下:

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

您可以擴展五個選單:

  • :top_menu - 左上角的選單
  • :account_menu - 右上角包含登入/登出連結的選單
  • :application_menu - 使用者不在專案內時顯示的主選單
  • :project_menu - 使用者在專案內時顯示的主選單
  • :admin_menu - 在管理頁面上顯示的選單(只能插入在「設定」之後、「插件」之前)

可用的選項有:

  • :param - 用於專案 ID 的參數鍵(預設為 :id
  • :if - 在渲染項目之前呼叫的 Proc,僅當其傳回 true 時才會顯示項目
  • :caption - 選單標題,可以是:
    • 一個本地化的字串符號
    • 一個字串
    • 一個可以將專案作為參數的 Proc
  • :before:after - 指定選單項目應插入的位置(例如,:after => :activity
  • :first:last - 如果設定為 true,則項目將保持在選單的開頭/結尾(例如,:last => true
  • :html - 傳遞給 link_to 的 HTML 選項雜湊,用於渲染選單項目

在我們的範例中,我們在應用程式選單中新增了一個項目,預設情況下該項目是空的。
重新啟動應用程式並前往 https://127.0.0.1:3000

現在,您可以從歡迎畫面點選「投票」標籤來存取投票。

擴展專案選單

現在,讓我們假設投票是在專案級別定義的(即使在我們的範例投票模型中並非如此)。因此,我們希望將「投票」標籤添加到專案選單中。
開啟 init.rb 並將剛才添加的那一行替換為以下兩行

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

第二行將我們的「投票」標籤添加到專案選單中,就在「活動」標籤之後。
第一行是必需的,它聲明 PollsController 中的兩個動作是公開的。稍後我們會詳細說明這一點。

再次重新啟動應用程式並前往您的其中一個專案

如果您點選「投票」標籤,您應該會注意到專案選單不再顯示。
要顯示專案選單,您必須初始化控制器的實例變數 @project

編輯您的 PollsController 來執行此操作

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

專案 ID 可在 :project_id 參數中找到,因為上述選單項目聲明中使用了 :param => :project_id 選項。

現在,您應該可以在查看投票時看到專案選單

添加新的權限

目前,任何人都可以參與投票。讓我們通過更改權限聲明使其更具可配置性。
我們將聲明兩個基於專案的權限,一個用於查看投票,另一個用於投票。這些權限不再是公開的(已移除 :public => true 選項)。

編輯 vendor/plugins/redmine_polls/init.rb 將先前的權限聲明替換為以下兩行


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

重新啟動應用程式並前往 https://127.0.0.1:3000/roles/report

您現在可以將這些權限賦予您現有的角色。

當然,需要在 PollsController 中添加一些程式碼,以便根據當前用户的權限實際保護動作。
為此,我們只需要附加 :authorize 過濾器,並確保在調用此過濾器之前正確設置了 Herve Harster 實例變數。

以下是 #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 動作之前檢索當前專案。
之後,只有管理員用户或在專案中具有適當角色的用户才能查看和參與投票。

如果您想以多語言方式顯示權限的符號,則需要在語言文件中添加必要的文本標籤。
只需在 Redmine 版本的正確翻譯目錄中創建一個 *.yml 文件,並使用如下標籤填充它


  permission_view_polls: View Polls
  permission_vote_polls: Vote Polls

在此範例中,創建的文件名為 en.yml,但也可以使用所有其他受支持的語言文件。
如您在上面的範例中所見,標籤由權限符號 :view_polls:vote_polls 組成,並在前面添加了 permission_

重新啟動您的應用程式並指向權限部分。

創建專案模組

目前,投票功能已添加到您的所有專案中。但您可能只想為某些專案啟用投票。
因此,讓我們創建一個「投票」專案模組。這是通過將權限聲明包裝在對 #project_module 的調用中來完成的。

編輯 init.rb 並更改權限聲明

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

重新啟動應用程式並前往您的其中一個專案設定。
點選「模組」標籤。您應該會在模組列表的最後看到「投票」模組(預設為停用)

您現在可以在專案層級啟用/停用投票。

改進外掛程式檢視

新增樣式表

讓我們先從將樣式表新增到外掛程式檢視開始。
vendor/plugins/redmine_polls/assets/stylesheets 目錄中建立一個名為 voting.css 的檔案

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

啟動應用程式時,Rails 引擎會自動將外掛程式資產複製到 public/plugin_assets/redmine_polls/,以便透過網路伺服器存取。因此,對外掛程式樣式表或 JavaScript 的任何更改都需要重新啟動應用程式。

然後,將以下行附加到 vendor/plugins/redmine_polls/app/views/polls/index.html.erb 的末尾,以便 Redmine 將您的樣式表包含在頁面標題中

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

請注意,呼叫 stylesheet_link_tag 輔助函式時需要使用 :plugin => 'redmine_polls' 選項。

可以使用相同方式透過 javascript_include_tag 輔助函式將 JavaScript 包含在外掛程式檢視中。

設定頁面標題

您可以使用 html_title 輔助函式從檢視內部設定 HTML 標題。
範例

<% html_title "Polls" %>

測試您的外掛程式

test/test_helper.rb:

以下是我的測試輔助函式檔案的內容

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

範例測試:

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
end

初始化測試資料庫:

我發現使用以下 rake 呼叫直接初始化測試資料庫最簡單

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

執行測試:

為了執行 requirements_controller_test.rb,我使用了以下命令

rake test:engines:all PLUGIN=redmine_requirements

使用權限進行測試

如果您的外掛程式需要專案成員資格,請在功能測試的開頭新增以下內容

def test_index
  @request.session[:user_id] = 2
  ...
end

如果您的外掛程式需要特定權限,您可以將其新增到使用者角色,如下所示(在固定裝置中查詢哪個角色適合該使用者)

def test_index
  Role.find(1).add_permission! :my_permission
  ...
end

您可以啟用/停用特定模組,如下所示

def test_index
  Project.find(1).enabled_module_names = [:mymodule]
  ...
end

約 12 年前由 Etienne Massip 更新 · 2012-05-25 11:13 的版本 · 66 個版本