この記事は、Ruby on Railsフレームワークの基本にすでに精通しており、Redmineのプラグインの開発を開始したい人に役立つと思います。
まず第一に、すべてのRedmineプラグインを2つのカテゴリに分ける価値があります。
最初のものには、標準のRedmineの機能に実際には影響しないプラグインが含まれています。 実際、これらはRedmine内の通常のRailsアプリケーションであり、難易度はほとんどないため、ほとんど関心がありません。 Redmineの公式Webサイトには、投票プラグインの作成方法を詳しく説明した素晴らしいチュートリアルがあります 。
プラグインが組み込み機能を変更する必要がある場合、すべてが少し複雑になります!
Redmineプラグインのフォルダー構造を作成するチームから始めましょう。 プラグインをLuxury Buttonsと呼びましょう。 Redmineのルートフォルダーに移動し、フォルダー構造を作成するコマンドを実行します。
$cd /usr/share/srv-redmine/redmine-2.3 $rails generate redmine_plugin LuxuryButtons
コマンドを実行すると、luxury_buttonsフォルダーが次の構造でpluginsフォルダーに表示されます。
libフォルダーに、プラグインの名前に一致するフォルダーをすぐに追加する必要があります。 luxury_buttonsフォルダー(以降、パッチフォルダーと呼びます )。 このフォルダには、将来、さまざまなRedmineメソッドのパッチファイルが含まれます。
プラグインを呼び出したときにこのフォルダーに名前を付けたのはなぜですか? これは単なる推奨事項であり、フォルダを別の方法で呼び出すことができますが、ここで最初の落とし穴が発生します。別のプラグインでこのフォルダの名前が一致し、パッチファイルの名前が一致する場合、パッチファイルの1つは適用されません! したがって、 パッチフォルダにはプラグインと同じ名前を付けることをお勧めします。 この方法により、エラーの発生が最小限に抑えられます!
プラグインがビューに何かを追加する必要がある場合。
標準のRedmineビューに何かを追加する必要があるとしましょう。 これを行う最も簡単で最も間違った方法は、プラグイン内のビューを書き換えることです。 通常、これはビューファイルをRedmineカーネルからプラグインの適切なディレクトリにコピーし、このファイルを編集することにより行われます。 たとえば、プラグインの1つで、リクエスト保存フォームでビューを書き換えます。
なぜそんなにひどくするのか:
- あなたは、あなたの見解の関連性を絶えず監視する運命にあります。 このビューのRedmineの新しいバージョンで何かが変更されると、この機能は失われます。 ビューの関連性を追跡することは非常に注意が必要です。
- 同じビューを書き換える別のプラグインが表示される場合、ビューまたは別のプラグインのビューが適用されます。 どのビューを適用するかは、プラグインの順序によって異なります。
したがって、代替方法を使用することをお勧めします。
フック
ビューのフックは、コンテンツをビューに埋め込むことができるコード行です。 フックを見つけるには、すべてのRedmineファイルで「フック」サブストリングを検索する必要があります。または、 このプレートを使用できます。
フックフック
すべてのビューフック接続を単一のファイルに保存しようとします。 このファイルは、次のようにinit.rbに含める必要があります。
require 'luxury_buttons/view_hooks'
ファイル自体の内容は次のようになります。
module LuxuryButtons module LuxuryButtons class Hooks < Redmine::Hook::ViewListener render_on( :view_issues_form_details_top, :partial => 'lu_buttons/view_issues_form_details_top') render_on( :view_layouts_base_html_head, :partial => 'lu_buttons/page_header') render_on( :view_issues_show_description_bottom, :partial => "lu_buttons/button_bar" ) render_on( :view_issues_history_journal_bottom, :partial => "lu_buttons/journal_detail") end end end
最初のモジュールの名前はプラグインの名前、2番目のモジュールの名前はパッチフォルダの名前と一致する必要があります 。
クラス内には、どのフックでどのテンプレートをレンダリングするかを示す関数があります。
2つのプラグインが同じフックを使用する場合、最初と2番目のプラグインの両方のコンテンツがビューに表示されます。 つまり フックは相互に書き換えません。
フックには2つの問題があります。
- フックはそうでないかもしれません。
- ビューから何かを削除する必要がある場合があり、フックでは追加のみが許可されます。
これらの問題を解決する唯一の方法は、ビューが既にレンダリングされているときにjQueryを使用してページのコンテンツを変更することです。
このための最も簡単な方法は、「view_layouts_base_html_head」フックを使用することです。これにより、コンテンツをページヘッダーに挿入できます。 特定のDOM要素をカットまたは追加するロジックにjsファイルを接続するためのリンクを挿入する必要があります。 このjsファイルが必要のないページにロードされないように、そのロードを条件式にロードすることをお勧めします。 つまり アクションとコントローラーによってファイルのダウンロードを遮断します。 例:
<% if controller_name == 'issues' && action_name == 'update' %> <%= javascript_include_tag :luxury_buttons_common, :plugin => :luxury_buttons %> <% end %>
プラグインのasset / javascriptフォルダーには、ファイル「luxury_buttons_common.js」が含まれている必要があります。
jQuery(document).ready(function(){ // });
jsファイル接続文字列を埋め込むには、「view_layouts_base_html_head」フックではなく、必要な限られた数のページにコンテンツを埋め込む特定のフックを使用する方が適切な場合があります。 たとえば、タスクページで何かを追加または切り取る必要がある場合は、「view_issues_form_details_bottom」フックを使用できます。
この場合、ファイルがドキュメント本文ではなくヘッダーに接続されるように、次の構成を使用する必要があります。
<% content_for :header_tags do %> <%= javascript_include_tag :luxury_buttons_common, :plugin => :luxury_buttons %> <% end %>
確かに、プラグインの「content_for」メソッドでは、バージョンごとに問題が発生します。
モデル、コントローラー、ヘルパーのメソッドを変更する方法。
メソッドの変更(パッチ)は、多くの点でビューの変更に似ており、同様の問題をもたらします。
コントローラーとモデルのフック
コントローラーとモデルにもフックがあります。 接続方法が異なります。 init.rbには、特定のフックを接続する行が必要です。 たとえば、新しいタスクを保存する前に呼び出されるフック:
require 'luxury_buttons/controller_issues_new_before_save_hook'
パッチディレクトリには、ファイル「controller_issues_new_before_save_hook.rb」が含まれている必要があります。たとえば、次の内容が含まれています。
module LuxuryButtons class ControllerIssuesNewBeforeSaveHook < Redmine::Hook::ViewListener def controller_issues_new_before_save(context={}) if context[:params] && context[:params][:issue] if (not context[:params][:issue][:assigned_to_id].nil?) and context[:params][:issue][:assigned_to_id].to_s=='' context[:issue].assigned_to_id = context[:issue].author_id if context[:issue].new_record? and Setting.plugin_luxury_buttons['assign_to_author'] end end '' end end end
モジュールの名前は、プラグインの名前、クラスの名前、ファイルの名前と一致する必要があります。
この場合、著者に新しいタスクを自動的に割り当てる可能性を認識しています。
パッチ方法
ビューのように、Redmineに必要なフックが常に存在するとは限りません。 そして、モデル、ヘルパー、またはコントローラーのメソッドにパッチを適用する必要があります。
最初に、init.rbにパッチファイルを含める必要があります。 たとえば、Issueモデルのread_only_attribute_namesメソッドにパッチを適用する必要があります。
Rails.application.config.to_prepare do Issue.send(:include, LuxuryButtons::IssuePatch) end
パッチフォルダーには、「issue_patch.rb」というファイルがあります。およそ次の内容です。
module LuxuryButtons module IssuePatch def self.included(base) base.extend(ClassMethods) base.send(:include, InstanceMethods) base.class_eval do alias_method_chain :read_only_attribute_names, :luxury_buttons end end module ClassMethods end module InstanceMethods def read_only_attribute_names_with_luxury_buttons(user) attribute = read_only_attribute_names_without_luxury_buttons(user) if Setting.plugin_luxury_buttons['hidden_fields_into_new_issue_form'] && new_record? hidden_fields = Setting.plugin_luxury_buttons['hidden_fields_into_new_issue_form'] attribute += hidden_fields attribute end attribute end end end end
建設業
alias_method_chain :read_only_attribute_names, :luxury_buttons
2つのread_only_attribute_names_with_luxury_buttonsおよびread_only_attribute_names_without_luxury_buttonsメソッドを生成します。
これで、標準のread_only_attribute_namesモデルメソッドの代わりに最初のメソッドが呼び出され、2番目のメソッドは標準のread_only_attribute_namesメソッドのエイリアスになります。
2つの方法を組み合わせることにより、標準のRedmineの方法にパッチを適用できます。 この例では、値の配列を返す標準のRedmineメソッドを最初に呼び出し、次にこの配列に値を追加します。
新しいバージョンの標準Redmineメソッドで何かが変更された場合、標準Redmineメソッドを単に書き直して独自のロジックを追加するよりも、パッチが正しく機能する可能性が非常に高くなります。
重要! Redmine のUserモデルへのパッチ適用にはいくつかの問題があります。 正しいパッチを適用するには、次のファイルを明示的に含める必要があります。
require_dependency 'project' require_dependency 'principal' require_dependency 'user'
この記事には、Redmineのプラグインの作成について述べたいことのすべてが含まれているわけではありません。 基本的な方法論と落とし穴を集めようとしました。 この記事がお役に立てば幸いです。