最近給公司的 redmine 寫了個提升 issue 流程的自動化程度插件。javascript
redmine 官網上的開發者文檔並很少,即便看完後,許多時候你也不知道接下來須要怎麼作。好在 redmine 是用「約定高於配置」的 Rails 開發的,其命名相對規範。有必要時,能夠猜想下對應的接口叫什麼名字。實在摸不到門,閱讀 redmine 和網上開源出來的 redmine 插件代碼也是條路。html
爲了節省有一樣需求的同行的時間,拯救咱們日益凋亡的腦細胞,我在這裏特地整理了開發過程當中摸索出來的一些小技巧。前端
. ├── app │ ├── controllers │ ├── models │ └── views │ └── settings ├── assets │ └── javascripts ├── config │ └── routes.rb ├── init.rb ├── lib │ └── $plugin
其中 init.rb
是插件的入口。插件的邏輯主要放到 lib/$plugin
裏面。其中 $plugin 應該是你的插件的名字。app
目錄功能跟 Rails 的同名目錄同樣。好比你想在 redmine 中引入新的模型,或者加個路由,能夠分別到 app/models
和 app/controllers
添加。不過別忘了修改 config/routes.rb
中的規則。要加個郵件模板,也是到 app/views
下加。至於 assets
裏的資源文件,會在應用啓動時拷貝到 public/plugin_assets/$plugin
路徑下。java
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 console
或 pry
裏試試看了。app
開發 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 調用鍵,細節參見文檔。
真正足夠複雜的插件都須要一個配置界面 —— 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.rb
和 app/controllers
裏面的內容,redmine 插件能夠添加屬於本身的路由:
RedmineApp::Application.routes.draw do get '/xxx', :to => 'plugin#xxx' end
而後你能夠在 app/models
放置本身的 model,用它來存儲配置值。這就跟開發獨立的 Rails 應用差很少。