redmine中自定义字段为edited/created时如何触发函数

How to trigger a function when a custom field is edited/created in redmine

我已经研究这个词很多个小时了,但我发现任何有用的东西。 希望您能帮助我们构建此 redmine 插件,或提供一些研究链接以帮助我们找到正确的密钥。

我们想要构建什么

问题是我们希望在创建或更新另一个自定义字段时更新 redmine 中的一个自定义字段(我们将其命名为 'Target_CF')。 我们正在寻找 Target_CF 的递增可能值,因此我们可以让 select 可以使用所有自定义字段的名称。 当然,我们想在不直接编辑Redmine的Core的情况下实现这一点,所以我们认为开发一个插件是最好的方法。

我们的插件还创建并配置了一个新的自定义字段(上面提到的那个),但我不会考虑这个问题,因为我认为它与此无关。

我们现在所在的位置

我们已经确定了一些可能对我们有用的钩子,如下所示:

到目前为止,我们有以下 directories/files 结构:

plugins/
  custom_plugin/
    init.rb
    lib/
      hooks.rb

我们写的代码

init.rb

require_dependency 'hooks'
Redmine::Plugin.register :custom_plugin do
  name 'custom_plugin'
  author 'author name'
  description 'description text'
  version '1.0.0'
end

hooks.rb

class Hooks < Redmine::Hook::ViewListener
  def controller_custom_fields_edit_after_save(context={ })
    @target_custom_field_name = "Target_CF"
    CustomField.find_by_name(@target_custom_field_name).possible_values.push(context[:custom_field].name)
  end
end

这段代码的结果是none。我的意思是,没有错误,没有更新,什么都没有。 editing/creating 另一个自定义字段后,我们的可能值没有变化。我们确信有一些我们不知道的东西,一些概念或工作流程,因此我们做的事情很糟糕。 请帮助我们理解我们所缺少的。

之前我们已经成功开发了另一个覆盖某些视图的插件。所以我们在视图相关插件方面有一些小技巧,但在控制器方面完全是零经验。

我们正在使用 Bitnami 的 Redmine 3.2.0 堆栈和 mysql 数据库。

好吧,我们终于找到了如何扩展基本控制器的方法。我会 post 在这里,希望这对任何发现我们有同样疑问的人都有用。 经过更多研究,我们得出结论,必须扩展基本控制器,以免直接修改核心方法。

这是我们最终的 directories/files 结构:

plugins/
  custom_plugin/
    init.rb
    lib/
      custom_plugin/
        issue_custom_field_patch.rb

我们之前说过我们可以使用一些钩子来注入我们想要的功能,但它似乎不适用于控制器。另一方面,我们构建了一个补丁来扩展目标 class 功能。

我们最终的工作代码

Init.rb

require 'redmine'
ActionDispatch::Callbacks.to_prepare do
  require_dependency 'issue_custom_field'
  unless IssueCustomField.included_modules.include? CustomPlugin::IssueCustomFieldPatch
    IssueCustomField.send(:include, CustomPlugin::IssueCustomFieldPatch)
  end
end

Redmine::Plugin.register :custom_plugin do
  name 'custom_plugin'
  author 'author name'
  description 'description text'
  version '1.0.0'
end

issue_custom_field_patch.rb

 module CustomPlugin
    module IssueCustomFieldPatch
        def self.included(base) # :nodoc:
            base.extend(ClassMethods)
            base.send(:include, InstanceMethods)

            base.class_eval do 
                unloadable
                after_save :update_possible_values
            end
         end
     end
module ClassMethods
 end

 module InstanceMethods
    def update_possible_values
        self.reload
        updatedPossibleValues unless self.name == "Target_CF"
    end

    private
     def updatedPossibleValues 
        @safe_attrs = ['project', 'description', 'due_date', 'category', 'status', 'assigned_to', 'priority', 'fixed_version', 'author', 'lock_version', 'created_on', 'updated_on', 'start_date', 'done_ratio', 'estimated_hours', 'is_private', 'closed_on']
         @custom_fields = IssueCustomField.all.select {|cf| !cf[:position].nil?}.collect {|cf| cf.name}
        @possible_values = @safe_attrs + @custom_fields
        CustomField.find_by_name("Target_CF").update possible_values: @possible_values
     end
 end
 CustomField.send(:include, IssueCustomFieldPatch)
end

功能说明

正如我们在问题中所述,每次用户 create/modify/removes 来自 Redmine 的自定义字段时,我们都需要更新 Target_CF 可能的值。

我们扩展了 IssueCustomField 的 class 实例方法,在每次保存后触发我们的新函数 'updatedPossibleValues'。这包括创建新的自定义字段,当然还有更新和删除现有的自定义字段。因为我们每次都重新加载我们的可能值列表,所以我们必须控制它的位置是否为零。如果是,这意味着自定义字段已被删除。

因为这个patch的最终动作是更新另一个自定义字段,这也触发了我们的函数,造成死循环。为了防止这种情况,我们将我们的功能链接到名称不是 'Target_CF' 的所有其他自定义字段。修复有点生疏,但我们找不到更好的方法。

我希望这对将来的人有用,因为他们可以投入我们花在这上面的时间的一小部分。 非常欢迎评论、修复和改进。

基于:https://www.redmine.org/projects/redmine/wiki/Plugin_Internals有点过时,但最终可以在其他资源和论坛的帮助下完成代码。