Compare commits

...

17 Commits

Author SHA1 Message Date
Eric Davis
99ea01ec93 Added test coverage for Redmine::Plugin.add_hook 2008-07-28 17:30:32 -07:00
Eric Davis
e87140a719 More unit tests to cover hook_registered? 2008-07-28 17:12:51 -07:00
Eric Davis
ca8fb4026e More unit tests for the plugins. Fixed bug where a plugin's hook method could return nil. 2008-07-28 17:10:22 -07:00
Eric Davis
e14b86453e Implementing more unit tests for the plugin hooks 2008-07-28 17:02:23 -07:00
Eric Davis
cb485c92ef Added Redmine::Plugin::Hook::Manager.clear_listeners to remove all hook listeners. 2008-07-28 17:01:59 -07:00
Eric Davis
e7309d8c57 Added tests for Redmine::Plugin::Hook::Base 2008-07-28 16:40:18 -07:00
Eric Davis
d6808130dc Added test stubs for testing the Plugin API 2008-07-28 16:26:46 -07:00
Eric Davis
c5242b4386 Merge branch 'master' into plugin-hooks
Conflicts:

	lib/redmine/plugin.rb
2008-07-28 16:14:10 -07:00
Eric Davis
6615002df2 Added Number helper 2008-07-23 21:04:56 -07:00
Eric Davis
08058e6a02 Added documentation 2008-07-23 21:04:51 -07:00
Eric Davis
8995245a0c Added Base class for Plugin Hooks. #1296 2008-07-23 21:03:10 -07:00
Eric Davis
04434cd6ef Changed Hook API to use a Manager class. #1296 2008-07-23 21:01:43 -07:00
Eric Davis
fe22ef95a8 Added new hook for the issues_helper.show_details 2008-07-23 17:30:27 -07:00
Eric Davis
355143ca18 Added hooks for the member page. #1147 2008-07-23 17:27:06 -07:00
Eric Davis
5e1bcc6b24 Added support for saving a bulk edit. #1147 2008-07-23 17:27:01 -07:00
Eric Davis
00659ab8c5 Added hooks to issue_edit, issue_bulk_edit, and issue_show. #1147 2008-07-23 17:26:53 -07:00
Eric Davis
404e6164cb Adding Redmine::Plugin::Hook class to register and use hooks
#1147
2008-07-23 17:26:43 -07:00
9 changed files with 261 additions and 2 deletions

View File

@@ -223,7 +223,6 @@ class IssuesController < ApplicationController
assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.versions.find_by_id(params[:fixed_version_id])
unsaved_issue_ids = []
@issues.each do |issue|
journal = issue.init_journal(User.current, params[:notes])
@@ -234,6 +233,9 @@ class IssuesController < ApplicationController
issue.start_date = params[:start_date] unless params[:start_date].blank?
issue.due_date = params[:due_date] unless params[:due_date].blank?
issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
Redmine::Plugin::Hook::Manager.call_hook(:issue_bulk_edit_save, {:params => params, :issue => issue })
# Don't save any change to the issue if the user is not authorized to apply the requested status
if (status.nil? || (issue.status.new_status_allowed_to?(status, current_role, issue.tracker) && issue.status = status)) && issue.save
# Send notification for each issue (if changed)

View File

@@ -86,7 +86,9 @@ module IssuesHelper
when 'attachment'
label = l(:label_attachment)
end
Redmine::Plugin::Hook::Manager.call_hook(:issues_helper_show_details, {:detail => detail, :label => label, :value => value, :old_value => old_value })
label ||= detail.prop_key
value ||= detail.value
old_value ||= detail.old_value

View File

@@ -48,4 +48,6 @@
<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
<% end %>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_edit, {:project => @project, :issue => @issue, :form => f }) %>
<%= wikitoolbar_for 'issue_description' %>

View File

@@ -38,6 +38,7 @@
<label><%= l(:field_done_ratio) %>:
<%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label>
</p>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_bulk_edit, {:project => @project, :issue => @issues }) %>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>

View File

@@ -53,6 +53,8 @@
<%end
end %>
</tr>
<%= Redmine::Plugin::Hook::Manager.call_hook(:issue_show, {:project => @project, :issue => @issue}) %>
</table>
<hr />

View File

@@ -9,6 +9,7 @@
<thead>
<th><%= l(:label_user) %></th>
<th><%= l(:label_role) %></th>
<%= Redmine::Plugin::Hook::Manager.call_hook(:project_member_list_header, {:project => @project }) %>
<th style="width:15%"></th>
</thead>
<tbody>
@@ -24,6 +25,8 @@
<% end %>
<% end %>
</td>
<%= Redmine::Plugin::Hook::Manager.call_hook(:project_member_list_column_three, {:project => @project, :member => member }) %>
<td align="center">
<%= link_to_remote l(:button_delete), { :url => {:controller => 'members', :action => 'destroy', :id => member},
:method => :post

View File

@@ -117,6 +117,18 @@ module Redmine #:nodoc:
@project_module = nil
end
# Registers a +method+ to be called when Redmine runs a hook called
# +hook_name+
#
# # Run puts whenever the issue_show hook is called
# add_hook :issue_show, Proc.new { puts 'Hello' }
#
# # Call the class method +my_method+ passing in all the context
# add_hook :issue_show, Proc.new {|context| MyPlugin.my_method(context)}
def add_hook(hook_name, method)
Redmine::Plugin::Hook::Manager.add_listener(hook_name, method)
end
# Registers an activity provider.
#
# Options:
@@ -147,5 +159,92 @@ module Redmine #:nodoc:
def configurable?
settings && settings.is_a?(Hash) && !settings[:partial].blank?
end
# Hook is used to allow plugins to hook into Redmine at specific sections
# to change it's behavior. See +Redmine::Plugin.add_hook+ for details.
class Hook
class Manager
# Hooks and the procs added
@@hooks = {
:issue_show => [],
:issue_edit => [],
:issue_bulk_edit => [],
:issue_bulk_edit_save => [],
:issue_update => [],
:project_member_list_header => [],
:project_member_list_column_three => [],
:issues_helper_show_details => []
}
cattr_reader :hooks
class << self
def valid_hook?(hook_name)
return @@hooks.has_key?(hook_name)
end
# Add +method+ to +hook_name+
def add_listener(hook_name, method)
if valid_hook?(hook_name)
@@hooks[hook_name.to_sym] << method
puts "Listener added for #{hook_name.to_s}"
end
end
# Removes all listeners
def clear_listeners()
@@hooks.each do |hook, registrations|
@@hooks[hook] = []
end
end
# Run all the hooks for +hook_name+ passing in +context+
def call_hook(hook_name, context = { })
response = ''
@@hooks[hook_name.to_sym].each do |method|
method_response = method.call(context)
response += method_response unless method_response.nil?
end
response
end
# Are hooks registered for +hook_name+
def hook_registered?(hook_name)
return @@hooks[hook_name.to_sym].size > 0
end
end
end
# Base class for Redmin Plugin hooks.
class Base
# Class level access to Rails' helper methods.
def self.help
Helper.instance
end
# Includes several Helper methods to be used in the class
class Helper # :nodoc:
include Singleton
include ERB::Util
include ActionView::Helpers::TagHelper
include ActionView::Helpers::FormHelper
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::FormOptionsHelper
include ActionView::Helpers::JavaScriptHelper
include ActionView::Helpers::PrototypeHelper
include ActionView::Helpers::NumberHelper
include ActionView::Helpers::UrlHelper
include ActionController::UrlWriter
def protect_against_forgery? # :nodoc:
false
end
end
end
end
end
end

View File

@@ -0,0 +1,128 @@
require File.dirname(__FILE__) + '/../../../test_helper'
class Redmine::Plugin::Hook::ManagerTest < Test::Unit::TestCase
def setup
@manager = Redmine::Plugin::Hook::Manager
end
def teardown
@manager.clear_listeners
end
def test_sanity
assert true
end
def test_hook_format
assert_kind_of Hash, @manager::hooks
@manager::hooks.each do |hook, registrations|
assert_kind_of Symbol, hook
assert_kind_of Array, registrations
assert_equal 0, registrations.length
end
end
def test_valid_hook
assert @manager::valid_hook?(:issue_show)
end
def test_invalid_hook
assert_equal false, @manager::valid_hook?(:an_invalid_hook_name)
end
def test_clear_listeners
assert_equal 0, @manager::hooks[:issue_show].length
@manager.add_listener(:issue_show, Proc.new { } )
@manager.add_listener(:issue_show, Proc.new { } )
@manager.add_listener(:issue_show, Proc.new { } )
@manager.add_listener(:issue_show, Proc.new { } )
assert_equal 4, @manager::hooks[:issue_show].length
@manager.clear_listeners
assert_equal 0, @manager::hooks[:issue_show].length
end
def test_add_listener
assert_equal 0, @manager::hooks[:issue_show].length
@manager.add_listener(:issue_show, Proc.new { } )
assert_equal 1, @manager::hooks[:issue_show].length
end
def test_add_invalid_listener
hooks = @manager::hooks
@manager.add_listener(:invalid, Proc.new { } )
assert_equal hooks, @manager::hooks
end
def test_call_hook_with_response
function = Proc.new { return 'response' }
@manager.add_listener(:issue_show, function)
assert_equal 'response', @manager.call_hook(:issue_show)
end
def test_call_multiple_hooks_with_response
function1 = Proc.new { return 'First Call.' }
function2 = Proc.new { return 'Second Call.' }
function3 = Proc.new { return 'Third Call.' }
@manager.add_listener(:issue_show, function1)
@manager.add_listener(:issue_show, function2)
@manager.add_listener(:issue_show, function3)
assert_equal 'First Call.Second Call.Third Call.', @manager.call_hook(:issue_show)
end
def test_call_hook_without_response
function = Proc.new { }
@manager.add_listener(:issue_show, function)
assert_equal '', @manager.call_hook(:issue_show)
end
def test_call_multiple_hooks_without_responses
function1 = Proc.new { }
function2 = Proc.new { }
function3 = Proc.new { }
@manager.add_listener(:issue_show, function1)
@manager.add_listener(:issue_show, function2)
@manager.add_listener(:issue_show, function3)
assert_equal '', @manager.call_hook(:issue_show)
end
def test_hook_registered_yes
@manager.add_listener(:issue_show, Proc.new { })
assert @manager.hook_registered?(:issue_show)
end
def test_hook_registered_no
assert_equal false, @manager.hook_registered?(:issue_show)
end
end
class Redmine::Plugin::Hook::BaseTest < Test::Unit::TestCase
def test_sanity
assert true
end
def test_help_should_be_a_singleton
assert Redmine::Plugin::Hook::Base::Helper.include?(Singleton)
end
def test_helper_should_include_actionview_helpers
[ActionView::Helpers::TagHelper,
ActionView::Helpers::FormHelper,
ActionView::Helpers::FormTagHelper,
ActionView::Helpers::FormOptionsHelper,
ActionView::Helpers::JavaScriptHelper,
ActionView::Helpers::PrototypeHelper,
ActionView::Helpers::NumberHelper,
ActionView::Helpers::UrlHelper].each do |helper|
assert Redmine::Plugin::Hook::Base::Helper.include?(helper), "#{helper} wasn't included."
end
end
end

View File

@@ -0,0 +1,20 @@
require File.dirname(__FILE__) + '/../../../test_helper'
class Redmine::PluginTest < Test::Unit::TestCase
def test_sanity
assert true
end
def test_add_hook
assert_equal false, Redmine::Plugin::Hook::Manager.hook_registered?(:issue_show)
Redmine::Plugin.add_hook(:issue_show, Proc.new { })
assert Redmine::Plugin::Hook::Manager.hook_registered?(:issue_show)
end
def test_add_hook_invalid
assert_equal false, Redmine::Plugin::Hook::Manager.hook_registered?(:invalid)
Redmine::Plugin.add_hook(:invalid, Proc.new { })
assert Redmine::Plugin::Hook::Manager.hook_registered?(:invalid)
end
end