redmine 插件开发非官方指南

最近给公司的 redmine 写了个提升 issue 流程的自动化程度插件。javascript

redmine 官网上的开发者文档并很少,即便看完后,许多时候你也不知道接下来须要怎么作。好在 redmine 是用“约定高于配置”的 Rails 开发的,其命名相对规范。有必要时,能够猜想下对应的接口叫什么名字。实在摸不到门,阅读 redmine 和网上开源出来的 redmine 插件代码也是条路。html

为了节省有一样需求的同行的时间,拯救咱们日益凋亡的脑细胞,我在这里特地整理了开发过程当中摸索出来的一些小技巧。前端

redmine 插件目录结构

.
├── app
│   ├── controllers
│   ├── models
│   └── views
│       └── settings
├── assets
│   └── javascripts
├── config
│   └── routes.rb
├── init.rb
├── lib
│   └── $plugin

其中 init.rb 是插件的入口。插件的逻辑主要放到 lib/$plugin 里面。其中 $plugin 应该是你的插件的名字。app 目录功能跟 Rails 的同名目录同样。好比你想在 redmine 中引入新的模型,或者加个路由,能够分别到 app/modelsapp/controllers 添加。不过别忘了修改 config/routes.rb 中的规则。要加个邮件模板,也是到 app/views 下加。至于 assets 里的资源文件,会在应用启动时拷贝到 public/plugin_assets/$plugin 路径下。java

redmine 主要模型

Rails 数据库表有两种,一种命名是模型名的复数;另外一种是 模型A复数_模型B复数,存储两个模型间的关系。数据库

因此当咱们列出数据库上的表时,咱们便获得一份 redmine 模型清单。api

public | attachments                         | table | redmine
 public | auth_sources                        | table | redmine
 public | boards                              | table | redmine
 public | changes                             | table | redmine
 public | changeset_parents                   | table | redmine
 public | changesets                          | table | redmine
 public | changesets_issues                   | table | redmine
 public | comments                            | table | redmine
 public | custom_field_enumerations           | table | redmine
 public | custom_fields                       | table | redmine
 public | custom_fields_projects              | table | redmine
 public | custom_fields_roles                 | table | redmine
 public | custom_fields_trackers              | table | redmine
 public | custom_values                       | table | redmine
 public | documents                           | table | redmine
 public | email_addresses                     | table | redmine
 public | enabled_modules                     | table | redmine
 public | enumerations                        | table | redmine
 public | groups_users                        | table | redmine
 public | import_items                        | table | redmine
 public | imports                             | table | redmine
 public | issue_categories                    | table | redmine
 public | issue_relations                     | table | redmine
 public | issue_statuses                      | table | redmine
 public | issues                              | table | redmine
 public | journal_details                     | table | redmine
 public | journals                            | table | redmine
 public | member_roles                        | table | redmine
 public | members                             | table | redmine
 public | messages                            | table | redmine
 public | news                                | table | redmine
 public | open_id_authentication_associations | table | redmine
 public | open_id_authentication_nonces       | table | redmine
 public | projects                            | table | redmine
 public | projects_trackers                   | table | redmine
 public | queries                             | table | redmine
 public | queries_roles                       | table | redmine
 public | repositories                        | table | redmine
 public | roles                               | table | redmine
 public | roles_managed_roles                 | table | redmine
 public | schema_migrations                   | table | redmine
 public | settings                            | table | redmine
 public | time_entries                        | table | redmine
 public | tokens                              | table | redmine
 public | trackers                            | table | redmine
 public | user_preferences                    | table | redmine
 public | users                               | table | redmine
 public | versions                            | table | redmine
 public | watchers                            | table | redmine
 public | wiki_content_versions               | table | redmine
 public | wiki_contents                       | table | redmine
 public | wiki_pages                          | table | redmine
 public | wiki_redirects                      | table | redmine
 public | wikis                               | table | redmine

许多模型名均可以见名知意,好比 issue 对应中文界面上的 问题、tracker 对应中文界面上的 追踪。
不过有些命名和其中文译名差异不小,好比 journal 对应的是问题页面下的 说明、custom_field 对应的是 自定义属性。
这只能多在 rails consolepry 里试试看了。app

hooks 和 patch

开发 redmine 插件基本靠两组 API,一个是 hook,另外一个是 patch。
前者可让你在 controller/view 特定的阶段插入本身的逻辑;后者能够覆盖掉原有的逻辑,抑或给 model 特定的阶段插入less

对应的官方文档:
hook:http://www.redmine.org/projec...
patch:http://www.redmine.org/projec...ui

我就补充下文档没有的。插件

这二者须要在 init.rb 里完成初始化:

...
require '$plugin/hooks' # 引入 `lib/$plugin/hooks.rb`,让 hooks 生效

...
Rails.application.config.to_prepare do
  # 打上 Monkey Patch
  unless Issue.include?(AutoLicense::IssuePatch)
    Issue.send(:include, AutoLicense::IssuePatch)
  end
  ...

patch 主要的应用场景是覆盖掉 model 的 after_save 回调,触发本身的自动化逻辑。

def self.included(base)
      base.send(:include, InstanceMethods)
      base.class_eval do
        after_save :after_save_trigger
      end
    end

    module InstanceMethods
      def after_save_trigger
        p self # 这里的 self 是具体的 model 实例

固然覆盖掉其余 Rails callback,好比 xx_create/xx_destroy,也是挺有用的。

hook 主要的应用场景是在 view 或 controller 执行流程中嵌入本身的逻辑。好比在特定 controller action 中加代码:

class PluginControllerHookListener < Redmine::Hook::Listener
    def controller_issues_new_before_save(context)
      p context[:request].host_with_port
      # context 里面能够拿到 controller 上下文相关的变量,例如 :params
    end

还能够在特定的 view 里加代码:

class PluginViewHookListener < Redmine::Hook::ViewListener
    def view_issues_new_top(context)
      js = <<-EOF
      console.log('Hello World')
      EOF
      "<script type=\"text/javascript\">#{javascript_cdata_section(js)}</script>"

固然若是想自定义前端代码,除了以字符串的形式插入,还能够直接引入整个资源文件。前面说到,

至于 assets 里的资源文件,会在应用启动时拷贝到 public/plugin_assets/$plugin 路径下。

因此把想要引入的资源文件放到 assets 目录下,在插入字符串中以 plugin_assets/$plugin/your_asset_file 路径引用便可。

顺便说一下,redmine 提供了一套 RESTful API:http://www.redmine.org/projec...
注意调用该接口须要提供用户的 API 调用键,细节参见文档。

redmine plugin setting

真正足够复杂的插件都须要一个配置界面 —— John Doe

redmine 给每一个插件分配了一个配置页面,在 管理>插件>配置 里面,其对应的路由是 /settings/plugin/$plugin
插件开发者须要提供一个表单,经过这个表单,管理员可以给插件的一些配置项赋值。

这么作仅需两步:
第一步,须要准备一个表单模板,命名为 app/views/settings/_$plugin.html.erb

<table>
  <tbody>
    <tr>
        <td> 配置1 </td>
        <td>
          <input type="text" id="settings_field1"
            value="<%= settings['field1'] %>"
            name="settings[field1]" >
        </td>
    </tr>
    ...

第二步,在插件代码中访问配置值:

settings = Setting.plugin_$plugin
p settings['field1']

固然,若是插件所需的配置项不是简单的 KV 结构,你能够选择本身实现一套独立的配置机制。

经过修改 config/routes.rbapp/controllers 里面的内容,redmine 插件能够添加属于本身的路由:

RedmineApp::Application.routes.draw do
  get '/xxx', :to => 'plugin#xxx'
end

而后你能够在 app/models 放置本身的 model,用它来存储配置值。这就跟开发独立的 Rails 应用差很少。

相关文章
相关标签/搜索