diff options
-rw-r--r-- | app/models/mailer.rb | 12 | ||||
-rw-r--r-- | doc/CHANGELOG | 1 | ||||
-rw-r--r-- | lib/redmine/hook.rb | 59 | ||||
-rw-r--r-- | lib/redmine/utils.rb | 38 | ||||
-rw-r--r-- | test/unit/lib/redmine/hook_test.rb | 91 | ||||
-rw-r--r-- | test/unit/repository_test.rb | 1 |
6 files changed, 183 insertions, 19 deletions
diff --git a/app/models/mailer.rb b/app/models/mailer.rb index d52199092..554261533 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -22,6 +22,12 @@ class Mailer < ActionMailer::Base include ActionController::UrlWriter + def self.default_url_options + h = Setting.host_name + h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank? + { :host => h, :protocol => Setting.protocol } + end + def issue_add(issue) redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, @@ -198,12 +204,6 @@ class Mailer < ActionMailer::Base set_language_if_valid Setting.default_language from Setting.mail_from - # URL options - h = Setting.host_name - h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank? - default_url_options[:host] = h - default_url_options[:protocol] = Setting.protocol - # Common headers headers 'X-Mailer' => 'Redmine', 'X-Redmine-Host' => Setting.host_name, diff --git a/doc/CHANGELOG b/doc/CHANGELOG index c197420da..3e176656c 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -14,6 +14,7 @@ http://www.redmine.org/ * One click filter action in activity view * Clickable/linkable line #'s while browsing the repo or viewing a file * Links to versions on files list +* Added request and controller objects to the hooks by default * Fixed: exporting an issue with attachments to PDF raises an error * Fixed: "too few arguments" error may occur on activerecord error translation * Fixed: "Default columns Displayed on the Issues list" setting is not easy to read diff --git a/lib/redmine/hook.rb b/lib/redmine/hook.rb index 9ee494c21..a2f57c15b 100644 --- a/lib/redmine/hook.rb +++ b/lib/redmine/hook.rb @@ -17,6 +17,8 @@ module Redmine module Hook + include ActionController::UrlWriter + @@listener_classes = [] @@listeners = nil @@hook_listeners = {} @@ -55,11 +57,12 @@ module Redmine # Calls a hook. # Returns the listeners response. def call_hook(hook, context={}) - response = '' - hook_listeners(hook).each do |listener| - response << listener.send(hook, context).to_s + returning [] do |response| + hls = hook_listeners(hook) + if hls.any? + hls.each {|listener| response << listener.send(hook, context)} + end end - response end end @@ -73,8 +76,9 @@ module Redmine Redmine::Hook.add_listener(child) super end + end - + # Listener class used for views hooks. # Listeners that inherit this class will include various helpers by default. class ViewListener < Listener @@ -91,17 +95,54 @@ module Redmine include ActionView::Helpers::TextHelper include ActionController::UrlWriter include ApplicationHelper + + # Default to creating links using only the path. Subclasses can + # change this default as needed + def self.default_url_options + {:only_path => true } + end + + # Helper method to directly render a partial using the context: + # + # class MyHook < Redmine::Hook::ViewListener + # render_on :view_issues_show_details_bottom, :partial => "show_more_data" + # end + # + def self.render_on(hook, options={}) + define_method hook do |context| + context[:controller].send(:render_to_string, {:locals => context}.merge(options)) + end + end end - # Helper module included in ApplicationHelper so that hooks can be called - # in views like this: + # Helper module included in ApplicationHelper and ActionControllerso that + # hooks can be called in views like this: + # # <%= call_hook(:some_hook) %> # <%= call_hook(:another_hook, :foo => 'bar' %> # - # Current project is automatically added to the call context. + # Or in controllers like: + # call_hook(:some_hook) + # call_hook(:another_hook, :foo => 'bar' + # + # Hooks added to views will be concatenated into a string. Hooks added to + # controllers will return an array of results. + # + # Several objects are automatically added to the call context: + # + # * project => current project + # * request => Request instance + # * controller => current Controller instance + # module Helper def call_hook(hook, context={}) - Redmine::Hook.call_hook(hook, {:project => @project}.merge(context)) + if is_a?(ActionController::Base) + default_context = {:controller => self, :project => @project, :request => request} + Redmine::Hook.call_hook(hook, default_context.merge(context)) + else + default_context = {:controller => controller, :project => @project, :request => request} + Redmine::Hook.call_hook(hook, default_context.merge(context)).join(' ') + end end end end diff --git a/lib/redmine/utils.rb b/lib/redmine/utils.rb new file mode 100644 index 000000000..02f9d3e63 --- /dev/null +++ b/lib/redmine/utils.rb @@ -0,0 +1,38 @@ +# Redmine - project management software +# Copyright (C) 2006-2009 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module Utils + class << self + # Returns the relative root url of the application + def relative_url_root + ActionController::Base.respond_to?('relative_url_root') ? + ActionController::Base.relative_url_root.to_s : + ActionController::AbstractRequest.relative_url_root.to_s + end + + # Sets the relative root url of the application + def relative_url_root=(arg) + if ActionController::Base.respond_to?('relative_url_root=') + ActionController::Base.relative_url_root=arg + else + ActionController::AbstractRequest.relative_url_root=arg + end + end + end + end +end diff --git a/test/unit/lib/redmine/hook_test.rb b/test/unit/lib/redmine/hook_test.rb index 0d76374b6..9313a36c7 100644 --- a/test/unit/lib/redmine/hook_test.rb +++ b/test/unit/lib/redmine/hook_test.rb @@ -19,8 +19,10 @@ require File.dirname(__FILE__) + '/../../../test_helper' class Redmine::Hook::ManagerTest < Test::Unit::TestCase + fixtures :issues + # Some hooks that are manually registered in these tests - class TestHook < Redmine::Hook::Listener; end + class TestHook < Redmine::Hook::ViewListener; end class TestHook1 < TestHook def view_layouts_base_html_head(context) @@ -39,10 +41,27 @@ class Redmine::Hook::ManagerTest < Test::Unit::TestCase "Context keys: #{context.keys.collect(&:to_s).sort.join(', ')}." end end + + class TestLinkToHook < TestHook + def view_layouts_base_html_head(context) + link_to('Issues', :controller => 'issues') + end + end + + class TestHookHelperController < ActionController::Base + include Redmine::Hook::Helper + end + + class TestHookHelperView < ActionView::Base + include Redmine::Hook::Helper + end + Redmine::Hook.clear_listeners def setup @hook_module = Redmine::Hook + @hook_helper = TestHookHelperController.new + @view_hook_helper = TestHookHelperView.new(RAILS_ROOT + '/app/views') end def teardown @@ -67,17 +86,81 @@ class Redmine::Hook::ManagerTest < Test::Unit::TestCase def test_call_hook @hook_module.add_listener(TestHook1) - assert_equal 'Test hook 1 listener.', @hook_module.call_hook(:view_layouts_base_html_head) + assert_equal ['Test hook 1 listener.'], @hook_helper.call_hook(:view_layouts_base_html_head) end def test_call_hook_with_context @hook_module.add_listener(TestHook3) - assert_equal 'Context keys: bar, foo.', @hook_module.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') + assert_equal ['Context keys: bar, controller, foo, project, request.'], + @hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') end def test_call_hook_with_multiple_listeners @hook_module.add_listener(TestHook1) @hook_module.add_listener(TestHook2) - assert_equal 'Test hook 1 listener.Test hook 2 listener.', @hook_module.call_hook(:view_layouts_base_html_head) + assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], @hook_helper.call_hook(:view_layouts_base_html_head) + end + + # Context: Redmine::Hook::Helper.call_hook default_url + def test_call_hook_default_url_options + @hook_module.add_listener(TestLinkToHook) + + assert_equal ['<a href="/issues">Issues</a>'], @hook_helper.call_hook(:view_layouts_base_html_head) + end + + # Context: Redmine::Hook::Helper.call_hook + def test_call_hook_with_project_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /project/i, @hook_helper.call_hook(:view_layouts_base_html_head)[0] + end + + def test_call_hook_from_controller_with_controller_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /controller/i, @hook_helper.call_hook(:view_layouts_base_html_head)[0] end + + def test_call_hook_from_controller_with_request_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /request/i, @hook_helper.call_hook(:view_layouts_base_html_head)[0] + end + + def test_call_hook_from_view_with_project_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /project/i, @view_hook_helper.call_hook(:view_layouts_base_html_head) + end + + def test_call_hook_from_view_with_controller_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /controller/i, @view_hook_helper.call_hook(:view_layouts_base_html_head) + end + + def test_call_hook_from_view_with_request_added_to_context + @hook_module.add_listener(TestHook3) + assert_match /request/i, @view_hook_helper.call_hook(:view_layouts_base_html_head) + end + + def test_call_hook_from_view_should_join_responses_with_a_space + @hook_module.add_listener(TestHook1) + @hook_module.add_listener(TestHook2) + assert_equal 'Test hook 1 listener. Test hook 2 listener.', + @view_hook_helper.call_hook(:view_layouts_base_html_head) + end + + def test_call_hook_should_not_change_the_default_url_for_email_notifications + issue = Issue.find(1) + + ActionMailer::Base.deliveries.clear + Mailer.deliver_issue_add(issue) + mail = ActionMailer::Base.deliveries.last + + @hook_module.add_listener(TestLinkToHook) + @hook_helper.call_hook(:view_layouts_base_html_head) + + ActionMailer::Base.deliveries.clear + Mailer.deliver_issue_add(issue) + mail2 = ActionMailer::Base.deliveries.last + + assert_equal mail.body, mail2.body + end end + diff --git a/test/unit/repository_test.rb b/test/unit/repository_test.rb index 6d4073c79..937f35439 100644 --- a/test/unit/repository_test.rb +++ b/test/unit/repository_test.rb @@ -67,6 +67,7 @@ class RepositoryTest < Test::Unit::TestCase def test_scan_changesets_for_issue_ids Setting.default_language = 'en' + set_language_if_valid('en') # choosing a status to apply to fix issues Setting.commit_fix_status_id = IssueStatus.find(:first, :conditions => ["is_closed = ?", true]).id |