專案

一般

個人檔案

動作

外掛內部

本頁面將用於集中存放有關 Redmine 外掛開發的資訊。

覆寫 Redmine 核心

您可以在 Redmine 中覆寫視圖,但不能覆寫控制器或模型。如果您嘗試覆寫一個虛構外掛「MyPlugin」的控制器(或模型)和視圖,以下是 Redmine/Rails 的運作方式:

控制器(或模型)

  1. Rails 啟動並載入其所有框架
  2. Rails 開始載入外掛中的程式碼
  3. Rails 在 MyPlugin 中找到「IssueController」,並看到它定義了一個「show」動作
  4. Rails 載入所有其他外掛
  5. Rails 然後從「../app」載入應用程式
  6. Rails 再次找到「IssueController」,並看到它也定義了一個「show」動作
  7. Rails(或者更確切地說是 Ruby)使用「../app」中的「show」動作覆寫外掛中的「show」動作
  8. Rails 完成載入並提供請求

視圖

視圖載入非常相似,但有一個小差別(因為 Redmine 對 Engines 的修補程式)

  1. Rails 啟動並載入其所有框架
  2. Rails 開始載入外掛中的程式碼
  3. Rails 在「../vendor/plugins/my_plugin/app/views」中找到一個視圖目錄,並將其「前置」到視圖路徑中
  4. Rails 載入所有其他外掛
  5. Rails 然後從「../app」載入應用程式
  6. Rails 完成載入並提供請求
  7. 請求進入,需要呈現視圖
  8. Rails 尋找匹配的範本,並載入外掛的範本,因為它被「前置」到視圖路徑中
  9. Rails 呈現外掛的視圖

由於使用 Ruby 的方式(透過包含模組)擴充模型和控制器非常容易,因此 Redmine 不應該(也不會)維護用於覆寫核心模型和/或控制器的 API。另一方面,視圖很棘手(因為 Rails 的魔力),因此用於覆寫它們的 API 更有用(因此在 Redmine 中實現)。

要覆寫現有的 Redmine 核心視圖,只需建立一個與「../app/views/」中的視圖檔案同名的檔案,Redmine 就會使用它。例如,要覆寫專案索引頁面,請將檔案新增到「../vendor/plugins/my_plugin/app/views/projects/index.html.erb」。

擴充 Redmine 核心

如上所述:您很少需要覆寫模型/控制器。相反,您應該
  • 向模型/控制器新增新方法,或
  • 包裝現有方法。

新增方法

在 Eric Davis 的「預算外掛」中可以找到一個「新增方法」的快速範例。在這裡,他向 Issue 新增了一個名為「deliverable_subject」的新方法,並聲明了一個關係。

module IssuePatch
  def self.included(base) # :nodoc:
    base.send(:include, InstanceMethods)
  end

  module InstanceMethods
    # Wraps the association to get the Deliverable subject.  Needed for the 
    # Query and filtering
    def deliverable_subject
      unless self.deliverable.nil?
        return self.deliverable.subject
      end
    end
  end
end

包裝現有方法

注意!
alias_method_chain 模式在 Rails 5 中已棄用,因此此技術僅適用於 4.0.0 以下版本的 Redmine。

一個關於**封裝現有方法**的快速範例可以在 Eric Davis 的 Rate 插件 中找到。他在這裡使用 alias_method_chain 來掛鉤 UsersHelper 並封裝 user_settings_tabs 方法。因此,當 Redmine 核心呼叫 user_settings_tabs 時,程式碼路徑如下所示

  1. Redmine 核心呼叫 UsersHelper#user_settings_tabs
  2. UsersHelper#user_settings_tabs 執行(實際上是 UsersHelper#user_settings_tabs_with_rate_tab
  3. UsersHelper#user_settings_tabs_with_rate_tab 呼叫原始的 UsersHelper#user_settings_tabs(已重新命名為 UsersHelper#user_settings_tabs_without_rate_tab
  4. 然後將一個新的雜湊新增到結果中
  5. UsersHelper#user_settings_tabs_with_rate_tab 將組合後的結果返回給 Redmine 核心,然後進行渲染
module RateUsersHelperPatch
  def self.included(base) # :nodoc:
    base.send(:include, InstanceMethods)

    base.class_eval do
      alias_method_chain :user_settings_tabs, :rate_tab
    end
  end

  module InstanceMethods
    # Adds a rates tab to the user administration page
    def user_settings_tabs_with_rate_tab
      tabs = user_settings_tabs_without_rate_tab
      tabs << { :name => 'rates', :partial => 'users/rates', :label => :rate_label_rate_history}
      return tabs
    end
  end
end

請務必注意,這種封裝每個方法只能執行一次。在多個插件使用此技巧的情況下,只有最後一次評估 alias_method_chain 才會有效,而之前的所有評估都將被忽略。

alias_method_chain 是一個相當進階的方法,但它也非常強大。

在 Redmine 插件中使用 Rails 回調

例如,當您想要掛鉤到所有已儲存/建立的問題時,最好使用 Rails 回調,而不是 Redmine 掛鉤。主要原因是當建立新問題時,不會觸發 :controller_issues_edit_before_save 掛鉤。
例如,請參閱 Eric Davis 的「看板插件」中的實現
  1. http://github.com/edavis10/redmine_kanban/blob/000cf175795c18033caa43082c4e4d0a9f989623/init.rb#L10
  2. http://github.com/edavis10/redmine_kanban/blob/000cf175795c18033caa43082c4e4d0a9f989623/lib/redmine_kanban/issue_patch.rb#L13

這將確保每次儲存問題(新增或更新)時都會執行 issue.update_kanban_from_issue

如果您只想掛鉤到新問題,則可以使用 before_create 回調而不是 after_save 回調。如果您想確保在執行程式碼之前確實已成功儲存問題,則最好使用 after_create 回調。

掛鉤到我的頁面

常見問題

  • 為什麼我的區塊的下拉式選單沒有本地化?根據慣例,下拉式方塊中條目的名稱是由插件的語言環境檔案中的條目組成。此條目的名稱必須與「我的網站」區塊檔案名稱相同,例如 redmine/vendor/plugins/<myplugin_folder>/app/views/my/blocks/<myblocks_view_file_name>.erb。因此,您需要在語言環境中新增一行「<myblocks_view_file_name>: <在此處放置我的區塊配置中下拉式項目的翻譯>」,例如 redmine/vendor/plugins/<myplugin_folder>/config/locale/zh-TW.yml。

如果語言環境檔案中沒有定義此字串,則一律會使用沒有副檔名的檔案名稱 <myblocks_view_file_name> 作為下拉式選單中的標籤。

參考

Jakob Fix將近 2 年前 更新 · 24 個版本