專案

一般

個人檔案

動作

Redmine 外掛鉤子

Redmine 支援鉤子的概念。它是一個 API,允許外部程式碼以簡潔的方式擴展核心 Redmine 功能。鉤子允許外掛作者註冊回調函式,這些函式會在 Redmine 程式碼到達程式碼中的特定點時依次執行。

這裡有一個有效鉤子的列表。但找到它們的最佳方法是查看程式碼,找到您要擴展的位置,並在附近搜尋對鉤子的呼叫。

擴展或替換 Redmine 程式碼的其他方法是

基本原理

如上所述,當鉤子被呼叫時,它會執行先前註冊的回調函式。這些函式必須完全接受一個參數:一個提供一些上下文資訊的雜湊。這個上下文雜湊將始終包含在回調函式中執行某些有用操作所需的數據。在控制器或視圖鉤子的情況下(見下文),它至少包含以下資訊
  • :controller => 對當前控制器實例的引用
  • :project => 當前專案(如果由控制器設定),
  • :request => 當前的請求對象,其中包含有關當前 Web 請求的大量資訊

此外,雜湊還將包含一些特定於各個鉤子的數據。這些數據直接傳遞到您在 Redmine 程式碼中找到的 call_hook 呼叫中。

模型鉤子將不包含預設數據,因為它不適用於此處。這些鉤子只會包含傳遞給 call_hook 呼叫的數據。

鉤子類型

基本上,目前有三種類型的鉤子
  • 視圖鉤子
  • 控制器鉤子
  • 模型鉤子

雖然這兩種類型使用完全相同的 API,但它們的使用案例卻根本不同。

視圖鉤子

視圖鉤子在渲染視圖的 HTML 程式碼時執行。這允許外掛作者將一些自訂 HTML 程式碼插入到視圖中的一些合理位置。回調函式的返回值會被轉換為字串並包含在視圖中。存在用於渲染單個部分的快捷方式。請參閱下面的範例。

控制器鉤子(Controller hooks)

控制器鉤子的數量比視圖鉤子少。通常使用 額外的過濾器 或擴展模型類就足夠了,因為控制器動作應該(並且大多數時候)非常短,並且不做太多事情。 但是,有一些較長的動作會使用鉤子。 要正確使用這些鉤子,必須了解上下文哈希中的對象只是被引用。 因此,如果您就地更改對象,則更改將在實際的控制器中(以及稍後的視圖中)可用。 請考慮以下簡化示例

假設以下函數已註冊到 do_something 鉤子。 有關如何實現的資訊,請參見下文。

def do_something(context={ })
  context[:issue].subject = "Nothing to fix" 
end

現在考慮一個具有以下代碼的控制器動作

issue = Issue.find(1)
# issue.subject is "Fix me" 
call_hook(:do_something, :issue => issue)
# issue.subject is now "Nothing to fix" 

如您所見,鉤子函數可以就地更改 issue 對象。 但是,不可能完全替換對象,因為這會破壞對象引用。

模型鉤子(Model hooks)

Redmine 中的模型鉤子非常少。 模型代碼中的大多數擴展都可以通過添加新方法或通過創造性地應用 alias_method_chain 模式來封裝現有方法來完成。 鉤子的使用方法與控制器鉤子相同。

將函數註冊到鉤子

視圖鉤子(View hooks)

以下示例將把一個函數鉤入 view_issues_form_details_bottom 鉤子。 這可以用於在問題編輯表單中添加一些額外的字段。

  1. 在您的插件(假設名為 my_plugin)中,在 lib/my_plugin/hooks.rb 中創建以下類。 您可以在同一個類中註冊到多個鉤子。
    module MyPlugin
      class Hooks < Redmine::Hook::ViewListener
        # This just renders the partial in
        # app/views/hooks/my_plugin/_view_issues_form_details_bottom.rhtml
        # The contents of the context hash is made available as local variables to the partial.
        #
        # Additional context fields
        #   :issue  => the issue this is edited
        #   :f      => the form object to create additional fields
        render_on :view_issues_form_details_bottom,
                  :partial => 'hooks/my_plugin/view_issues_form_details_bottom'
      end
    end
    
    以下類別與上述類別的功能完全相同,但使用的是方法而不是較短的 render_on 輔助方法。 方法的名稱決定了它註冊到哪個回調。
    module MyPlugin
      class Hooks < Redmine::Hook::ViewListener
        def view_issues_form_details_bottom(context={ })
          # the controller parameter is part of the current params object
          # This will render the partial into a string and return it.
          context[:controller].send(:render_to_string, {
            :partial => "hooks/my_plugin/view_issues_form_details_bottom",
            :locals => context
          })
    
          # Instead of the above statement, you could return any string generated
          # by your code. That string will be included into the view
        end
      end
    end
    
  2. 在您的 init.rb 中,請確保 require 包含鉤子的文件。 它看起來應該像這樣
    require 'redmine'
    
    # This is the important line.
    # It requires the file in lib/my_plugin/hooks.rb
    require_dependency 'my_plugin/hooks'
    
    Redmine::Plugin.register :my_plugin do
      [...]
    end
    

控制器和模型鉤子(Controller and Model hooks)

您可以像視圖鉤子一樣將方法註冊到控制器和模型鉤子。 請始終記得在您的 init.rb 中 require 鉤子類別。 請參閱以下示例

module MyPlugin
  class Hooks < Redmine::Hook::ViewListener
    def controller_issues_bulk_edit_before_save(context={ })
      # set my_attribute on the issue to a default value if not set explictly
      context[:issue].my_attribute ||= "default" 
    end
  end
end

其他示例

可以在以下位置找到一些其他的實際示例

視圖鉤子

輔助方法鉤子

控制器鉤子

待辦事項

  • 如何將過濾器添加到現有控制器?
  • 如何使用 alias_method_chain 覆寫方法
    • 實例方法
    • 類別方法
    • 初始化
    • 模組

Joshua Smith超過 10 年前 更新 · 14 個修訂