##// END OF EJS Templates
Added Redmine::Plugin::Hook::Manager.clear_listeners to remove all hook listeners.
Eric Davis -
r1704:cb485c92efb4
parent child
Show More
@@ -1,242 +1,249
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module Redmine #:nodoc:
18 module Redmine #:nodoc:
19
19
20 # Base class for Redmine plugins.
20 # Base class for Redmine plugins.
21 # Plugins are registered using the <tt>register</tt> class method that acts as the public constructor.
21 # Plugins are registered using the <tt>register</tt> class method that acts as the public constructor.
22 #
22 #
23 # Redmine::Plugin.register :example do
23 # Redmine::Plugin.register :example do
24 # name 'Example plugin'
24 # name 'Example plugin'
25 # author 'John Smith'
25 # author 'John Smith'
26 # description 'This is an example plugin for Redmine'
26 # description 'This is an example plugin for Redmine'
27 # version '0.0.1'
27 # version '0.0.1'
28 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
28 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
29 # end
29 # end
30 #
30 #
31 # === Plugin attributes
31 # === Plugin attributes
32 #
32 #
33 # +settings+ is an optional attribute that let the plugin be configurable.
33 # +settings+ is an optional attribute that let the plugin be configurable.
34 # It must be a hash with the following keys:
34 # It must be a hash with the following keys:
35 # * <tt>:default</tt>: default value for the plugin settings
35 # * <tt>:default</tt>: default value for the plugin settings
36 # * <tt>:partial</tt>: path of the configuration partial view, relative to the plugin <tt>app/views</tt> directory
36 # * <tt>:partial</tt>: path of the configuration partial view, relative to the plugin <tt>app/views</tt> directory
37 # Example:
37 # Example:
38 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
38 # settings :default => {'foo'=>'bar'}, :partial => 'settings/settings'
39 # In this example, the settings partial will be found here in the plugin directory: <tt>app/views/settings/_settings.rhtml</tt>.
39 # In this example, the settings partial will be found here in the plugin directory: <tt>app/views/settings/_settings.rhtml</tt>.
40 #
40 #
41 # When rendered, the plugin settings value is available as the local variable +settings+
41 # When rendered, the plugin settings value is available as the local variable +settings+
42 class Plugin
42 class Plugin
43 @registered_plugins = {}
43 @registered_plugins = {}
44 class << self
44 class << self
45 attr_reader :registered_plugins
45 attr_reader :registered_plugins
46 private :new
46 private :new
47
47
48 def def_field(*names)
48 def def_field(*names)
49 class_eval do
49 class_eval do
50 names.each do |name|
50 names.each do |name|
51 define_method(name) do |*args|
51 define_method(name) do |*args|
52 args.empty? ? instance_variable_get("@#{name}") : instance_variable_set("@#{name}", *args)
52 args.empty? ? instance_variable_get("@#{name}") : instance_variable_set("@#{name}", *args)
53 end
53 end
54 end
54 end
55 end
55 end
56 end
56 end
57 end
57 end
58 def_field :name, :description, :author, :version, :settings
58 def_field :name, :description, :author, :version, :settings
59
59
60 # Plugin constructor
60 # Plugin constructor
61 def self.register(name, &block)
61 def self.register(name, &block)
62 p = new
62 p = new
63 p.instance_eval(&block)
63 p.instance_eval(&block)
64 Plugin.registered_plugins[name] = p
64 Plugin.registered_plugins[name] = p
65 end
65 end
66
66
67 # Adds an item to the given +menu+.
67 # Adds an item to the given +menu+.
68 # The +id+ parameter (equals to the project id) is automatically added to the url.
68 # The +id+ parameter (equals to the project id) is automatically added to the url.
69 # menu :project_menu, :plugin_example, { :controller => 'example', :action => 'say_hello' }, :caption => 'Sample'
69 # menu :project_menu, :plugin_example, { :controller => 'example', :action => 'say_hello' }, :caption => 'Sample'
70 #
70 #
71 # +name+ parameter can be: :top_menu, :account_menu, :application_menu or :project_menu
71 # +name+ parameter can be: :top_menu, :account_menu, :application_menu or :project_menu
72 #
72 #
73 def menu(name, item, url, options={})
73 def menu(name, item, url, options={})
74 Redmine::MenuManager.map(name) {|menu| menu.push item, url, options}
74 Redmine::MenuManager.map(name) {|menu| menu.push item, url, options}
75 end
75 end
76
76
77 # Defines a permission called +name+ for the given +actions+.
77 # Defines a permission called +name+ for the given +actions+.
78 #
78 #
79 # The +actions+ argument is a hash with controllers as keys and actions as values (a single value or an array):
79 # The +actions+ argument is a hash with controllers as keys and actions as values (a single value or an array):
80 # permission :destroy_contacts, { :contacts => :destroy }
80 # permission :destroy_contacts, { :contacts => :destroy }
81 # permission :view_contacts, { :contacts => [:index, :show] }
81 # permission :view_contacts, { :contacts => [:index, :show] }
82 #
82 #
83 # The +options+ argument can be used to make the permission public (implicitly given to any user)
83 # The +options+ argument can be used to make the permission public (implicitly given to any user)
84 # or to restrict users the permission can be given to.
84 # or to restrict users the permission can be given to.
85 #
85 #
86 # Examples
86 # Examples
87 # # A permission that is implicitly given to any user
87 # # A permission that is implicitly given to any user
88 # # This permission won't appear on the Roles & Permissions setup screen
88 # # This permission won't appear on the Roles & Permissions setup screen
89 # permission :say_hello, { :example => :say_hello }, :public => true
89 # permission :say_hello, { :example => :say_hello }, :public => true
90 #
90 #
91 # # A permission that can be given to any user
91 # # A permission that can be given to any user
92 # permission :say_hello, { :example => :say_hello }
92 # permission :say_hello, { :example => :say_hello }
93 #
93 #
94 # # A permission that can be given to registered users only
94 # # A permission that can be given to registered users only
95 # permission :say_hello, { :example => :say_hello }, :require => loggedin
95 # permission :say_hello, { :example => :say_hello }, :require => loggedin
96 #
96 #
97 # # A permission that can be given to project members only
97 # # A permission that can be given to project members only
98 # permission :say_hello, { :example => :say_hello }, :require => member
98 # permission :say_hello, { :example => :say_hello }, :require => member
99 def permission(name, actions, options = {})
99 def permission(name, actions, options = {})
100 if @project_module
100 if @project_module
101 Redmine::AccessControl.map {|map| map.project_module(@project_module) {|map|map.permission(name, actions, options)}}
101 Redmine::AccessControl.map {|map| map.project_module(@project_module) {|map|map.permission(name, actions, options)}}
102 else
102 else
103 Redmine::AccessControl.map {|map| map.permission(name, actions, options)}
103 Redmine::AccessControl.map {|map| map.permission(name, actions, options)}
104 end
104 end
105 end
105 end
106
106
107 # Defines a project module, that can be enabled/disabled for each project.
107 # Defines a project module, that can be enabled/disabled for each project.
108 # Permissions defined inside +block+ will be bind to the module.
108 # Permissions defined inside +block+ will be bind to the module.
109 #
109 #
110 # project_module :things do
110 # project_module :things do
111 # permission :view_contacts, { :contacts => [:list, :show] }, :public => true
111 # permission :view_contacts, { :contacts => [:list, :show] }, :public => true
112 # permission :destroy_contacts, { :contacts => :destroy }
112 # permission :destroy_contacts, { :contacts => :destroy }
113 # end
113 # end
114 def project_module(name, &block)
114 def project_module(name, &block)
115 @project_module = name
115 @project_module = name
116 self.instance_eval(&block)
116 self.instance_eval(&block)
117 @project_module = nil
117 @project_module = nil
118 end
118 end
119
119
120 # Registers a +method+ to be called when Redmine runs a hook called
120 # Registers a +method+ to be called when Redmine runs a hook called
121 # +hook_name+
121 # +hook_name+
122 #
122 #
123 # # Run puts whenever the issue_show hook is called
123 # # Run puts whenever the issue_show hook is called
124 # add_hook :issue_show, Proc.new { puts 'Hello' }
124 # add_hook :issue_show, Proc.new { puts 'Hello' }
125 #
125 #
126 # # Call the class method +my_method+ passing in all the context
126 # # Call the class method +my_method+ passing in all the context
127 # add_hook :issue_show, Proc.new {|context| MyPlugin.my_method(context)}
127 # add_hook :issue_show, Proc.new {|context| MyPlugin.my_method(context)}
128 def add_hook(hook_name, method)
128 def add_hook(hook_name, method)
129 Redmine::Plugin::Hook::Manager.add_listener(hook_name, method)
129 Redmine::Plugin::Hook::Manager.add_listener(hook_name, method)
130 end
130 end
131
131
132 # Registers an activity provider.
132 # Registers an activity provider.
133 #
133 #
134 # Options:
134 # Options:
135 # * <tt>:class_name</tt> - one or more model(s) that provide these events (inferred from event_type by default)
135 # * <tt>:class_name</tt> - one or more model(s) that provide these events (inferred from event_type by default)
136 # * <tt>:default</tt> - setting this option to false will make the events not displayed by default
136 # * <tt>:default</tt> - setting this option to false will make the events not displayed by default
137 #
137 #
138 # A model can provide several activity event types.
138 # A model can provide several activity event types.
139 #
139 #
140 # Examples:
140 # Examples:
141 # register :news
141 # register :news
142 # register :scrums, :class_name => 'Meeting'
142 # register :scrums, :class_name => 'Meeting'
143 # register :issues, :class_name => ['Issue', 'Journal']
143 # register :issues, :class_name => ['Issue', 'Journal']
144 #
144 #
145 # Retrieving events:
145 # Retrieving events:
146 # Associated model(s) must implement the find_events class method.
146 # Associated model(s) must implement the find_events class method.
147 # ActiveRecord models can use acts_as_activity_provider as a way to implement this class method.
147 # ActiveRecord models can use acts_as_activity_provider as a way to implement this class method.
148 #
148 #
149 # The following call should return all the scrum events visible by current user that occured in the 5 last days:
149 # The following call should return all the scrum events visible by current user that occured in the 5 last days:
150 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today)
150 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today)
151 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today, :project => foo) # events for project foo only
151 # Meeting.find_events('scrums', User.current, 5.days.ago, Date.today, :project => foo) # events for project foo only
152 #
152 #
153 # Note that :view_scrums permission is required to view these events in the activity view.
153 # Note that :view_scrums permission is required to view these events in the activity view.
154 def activity_provider(*args)
154 def activity_provider(*args)
155 Redmine::Activity.register(*args)
155 Redmine::Activity.register(*args)
156 end
156 end
157
157
158 # Returns +true+ if the plugin can be configured.
158 # Returns +true+ if the plugin can be configured.
159 def configurable?
159 def configurable?
160 settings && settings.is_a?(Hash) && !settings[:partial].blank?
160 settings && settings.is_a?(Hash) && !settings[:partial].blank?
161 end
161 end
162
162
163 # Hook is used to allow plugins to hook into Redmine at specific sections
163 # Hook is used to allow plugins to hook into Redmine at specific sections
164 # to change it's behavior. See +Redmine::Plugin.add_hook+ for details.
164 # to change it's behavior. See +Redmine::Plugin.add_hook+ for details.
165 class Hook
165 class Hook
166 class Manager
166 class Manager
167 # Hooks and the procs added
167 # Hooks and the procs added
168 @@hooks = {
168 @@hooks = {
169 :issue_show => [],
169 :issue_show => [],
170 :issue_edit => [],
170 :issue_edit => [],
171 :issue_bulk_edit => [],
171 :issue_bulk_edit => [],
172 :issue_bulk_edit_save => [],
172 :issue_bulk_edit_save => [],
173 :issue_update => [],
173 :issue_update => [],
174 :project_member_list_header => [],
174 :project_member_list_header => [],
175 :project_member_list_column_three => [],
175 :project_member_list_column_three => [],
176 :issues_helper_show_details => []
176 :issues_helper_show_details => []
177 }
177 }
178
178
179 cattr_reader :hooks
179 cattr_reader :hooks
180
180
181 class << self
181 class << self
182
182
183 def valid_hook?(hook_name)
183 def valid_hook?(hook_name)
184 return @@hooks.has_key?(hook_name)
184 return @@hooks.has_key?(hook_name)
185 end
185 end
186
186
187 # Add +method+ to +hook_name+
187 # Add +method+ to +hook_name+
188 def add_listener(hook_name, method)
188 def add_listener(hook_name, method)
189 if valid_hook?(hook_name)
189 if valid_hook?(hook_name)
190 @@hooks[hook_name.to_sym] << method
190 @@hooks[hook_name.to_sym] << method
191 puts "Listener added for #{hook_name.to_s}"
191 puts "Listener added for #{hook_name.to_s}"
192 end
192 end
193 end
193 end
194
195 # Removes all listeners
196 def clear_listeners()
197 @@hooks.each do |hook, registrations|
198 @@hooks[hook] = []
199 end
200 end
194
201
195 # Run all the hooks for +hook_name+ passing in +context+
202 # Run all the hooks for +hook_name+ passing in +context+
196 def call_hook(hook_name, context = { })
203 def call_hook(hook_name, context = { })
197 response = ''
204 response = ''
198 @@hooks[hook_name.to_sym].each do |method|
205 @@hooks[hook_name.to_sym].each do |method|
199 response += method.call(context)
206 response += method.call(context)
200 end
207 end
201 response
208 response
202 end
209 end
203
210
204 # Are hooks registered for +hook_name+
211 # Are hooks registered for +hook_name+
205 def hook_registered?(hook_name)
212 def hook_registered?(hook_name)
206 return @@hooks[hook_name.to_sym].size > 0
213 return @@hooks[hook_name.to_sym].size > 0
207 end
214 end
208 end
215 end
209 end
216 end
210
217
211 # Base class for Redmin Plugin hooks.
218 # Base class for Redmin Plugin hooks.
212 class Base
219 class Base
213
220
214 # Class level access to Rails' helper methods.
221 # Class level access to Rails' helper methods.
215 def self.help
222 def self.help
216 Helper.instance
223 Helper.instance
217 end
224 end
218
225
219 # Includes several Helper methods to be used in the class
226 # Includes several Helper methods to be used in the class
220 class Helper # :nodoc:
227 class Helper # :nodoc:
221 include Singleton
228 include Singleton
222 include ERB::Util
229 include ERB::Util
223 include ActionView::Helpers::TagHelper
230 include ActionView::Helpers::TagHelper
224 include ActionView::Helpers::FormHelper
231 include ActionView::Helpers::FormHelper
225 include ActionView::Helpers::FormTagHelper
232 include ActionView::Helpers::FormTagHelper
226 include ActionView::Helpers::FormOptionsHelper
233 include ActionView::Helpers::FormOptionsHelper
227 include ActionView::Helpers::JavaScriptHelper
234 include ActionView::Helpers::JavaScriptHelper
228 include ActionView::Helpers::PrototypeHelper
235 include ActionView::Helpers::PrototypeHelper
229 include ActionView::Helpers::NumberHelper
236 include ActionView::Helpers::NumberHelper
230 include ActionView::Helpers::UrlHelper
237 include ActionView::Helpers::UrlHelper
231
238
232 include ActionController::UrlWriter
239 include ActionController::UrlWriter
233
240
234 def protect_against_forgery? # :nodoc:
241 def protect_against_forgery? # :nodoc:
235 false
242 false
236 end
243 end
237
244
238 end
245 end
239 end
246 end
240 end
247 end
241 end
248 end
242 end
249 end
General Comments 0
You need to be logged in to leave comments. Login now