如何在redmine中编辑/创建自定义字段时触发函数



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

我们想要构建什么

问题是,每当创建或更新另一个自定义字段时,我们都要更新redmine中的一个自定义域(让我们将其命名为"Target_CF"(。我们正在寻找增加Target_CF的可能值,这样我们就可以选择所有自定义字段的名称。当然,我们希望在不直接编辑Redmine的Core的情况下实现这一点,所以我们认为开发插件将是最好的方法。

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

我们现在的处境

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

  • :controller_custom_fields_new_after_save
  • :controller_custom_fields_edit_after_save

到目前为止,我们有以下目录/文件结构:

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

此代码的结果为"无"。我的意思是,没有错误,没有更新,什么都没有。编辑/创建另一个自定义字段后,我们的可能值没有变化。我们确信有一些我们不知道的东西,一些概念或工作流程,正因为如此,我们做得太糟糕了。请帮助我们了解我们缺少什么。

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

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

最后,我们发现了如何扩展基本控制器的方法。我会把它贴在这里,希望这对任何发现我们同样疑虑的人都有用。经过进一步的研究,我们得出结论,为了不直接修改核心方法,必须扩展基本控制器。

这是我们最终的目录/文件结构:

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

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

我们的最终工作代码

初始化.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

解释了功能

正如我们在问题中所述,每次用户从Redmine创建/修改/删除自定义字段时,我们都需要更新Target_CF可能的值。

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

因为这个补丁的最终动作是更新另一个自定义字段,这也触发了我们的功能,导致了一个无限循环。为了防止这种情况,我们将我们的功能链接到名称不是"Target_CF"的其他所有自定义字段。有点生疏,但我们找不到更好的方法。

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

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

最新更新