##// END OF EJS Templates
Introduce virtual MenuNodes (#15880)....
Jean-Philippe Lang -
r15119:53710d80fc88
parent child
Show More
@@ -1,455 +1,470
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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
18 module Redmine
19 module MenuManager
19 module MenuManager
20 class MenuError < StandardError #:nodoc:
20 class MenuError < StandardError #:nodoc:
21 end
21 end
22
22
23 module MenuController
23 module MenuController
24 def self.included(base)
24 def self.included(base)
25 base.extend(ClassMethods)
25 base.extend(ClassMethods)
26 end
26 end
27
27
28 module ClassMethods
28 module ClassMethods
29 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
29 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
30 mattr_accessor :menu_items
30 mattr_accessor :menu_items
31
31
32 # Set the menu item name for a controller or specific actions
32 # Set the menu item name for a controller or specific actions
33 # Examples:
33 # Examples:
34 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
34 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
35 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
35 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
36 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
36 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
37 #
37 #
38 # The default menu item name for a controller is controller_name by default
38 # The default menu item name for a controller is controller_name by default
39 # Eg. the default menu item name for ProjectsController is :projects
39 # Eg. the default menu item name for ProjectsController is :projects
40 def menu_item(id, options = {})
40 def menu_item(id, options = {})
41 if actions = options[:only]
41 if actions = options[:only]
42 actions = [] << actions unless actions.is_a?(Array)
42 actions = [] << actions unless actions.is_a?(Array)
43 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
43 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
44 else
44 else
45 menu_items[controller_name.to_sym][:default] = id
45 menu_items[controller_name.to_sym][:default] = id
46 end
46 end
47 end
47 end
48 end
48 end
49
49
50 def menu_items
50 def menu_items
51 self.class.menu_items
51 self.class.menu_items
52 end
52 end
53
53
54 # Returns the menu item name according to the current action
54 # Returns the menu item name according to the current action
55 def current_menu_item
55 def current_menu_item
56 @current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
56 @current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
57 menu_items[controller_name.to_sym][:default]
57 menu_items[controller_name.to_sym][:default]
58 end
58 end
59
59
60 # Redirects user to the menu item of the given project
60 # Redirects user to the menu item of the given project
61 # Returns false if user is not authorized
61 # Returns false if user is not authorized
62 def redirect_to_project_menu_item(project, name)
62 def redirect_to_project_menu_item(project, name)
63 item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s}
63 item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s}
64 if item && item.allowed?(User.current, project)
64 if item && item.allowed?(User.current, project)
65 redirect_to({item.param => project}.merge(item.url))
65 redirect_to({item.param => project}.merge(item.url))
66 return true
66 return true
67 end
67 end
68 false
68 false
69 end
69 end
70 end
70 end
71
71
72 module MenuHelper
72 module MenuHelper
73 # Returns the current menu item name
73 # Returns the current menu item name
74 def current_menu_item
74 def current_menu_item
75 controller.current_menu_item
75 controller.current_menu_item
76 end
76 end
77
77
78 # Renders the application main menu
78 # Renders the application main menu
79 def render_main_menu(project)
79 def render_main_menu(project)
80 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
80 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
81 end
81 end
82
82
83 def display_main_menu?(project)
83 def display_main_menu?(project)
84 menu_name = project && !project.new_record? ? :project_menu : :application_menu
84 menu_name = project && !project.new_record? ? :project_menu : :application_menu
85 Redmine::MenuManager.items(menu_name).children.present?
85 Redmine::MenuManager.items(menu_name).children.present?
86 end
86 end
87
87
88 def render_menu(menu, project=nil)
88 def render_menu(menu, project=nil)
89 links = []
89 links = []
90 menu_items_for(menu, project) do |node|
90 menu_items_for(menu, project) do |node|
91 links << render_menu_node(node, project)
91 links << render_menu_node(node, project)
92 end
92 end
93 links.empty? ? nil : content_tag('ul', links.join.html_safe)
93 links.empty? ? nil : content_tag('ul', links.join.html_safe)
94 end
94 end
95
95
96 def render_menu_node(node, project=nil)
96 def render_menu_node(node, project=nil)
97 if node.children.present? || !node.child_menus.nil?
97 if node.children.present? || !node.child_menus.nil?
98 return render_menu_node_with_children(node, project)
98 return render_menu_node_with_children(node, project)
99 else
99 else
100 caption, url, selected = extract_node_details(node, project)
100 caption, url, selected = extract_node_details(node, project)
101 return content_tag('li',
101 return content_tag('li',
102 render_single_menu_node(node, caption, url, selected))
102 render_single_menu_node(node, caption, url, selected))
103 end
103 end
104 end
104 end
105
105
106 def render_menu_node_with_children(node, project=nil)
106 def render_menu_node_with_children(node, project=nil)
107 caption, url, selected = extract_node_details(node, project)
107 caption, url, selected = extract_node_details(node, project)
108
108
109 html = [].tap do |html|
109 html = [].tap do |html|
110 html << '<li>'
110 html << '<li>'
111 # Parent
111 # Parent
112 html << render_single_menu_node(node, caption, url, selected)
112 html << render_single_menu_node(node, caption, url, selected)
113
113
114 # Standard children
114 # Standard children
115 standard_children_list = "".html_safe.tap do |child_html|
115 standard_children_list = "".html_safe.tap do |child_html|
116 node.children.each do |child|
116 node.children.each do |child|
117 child_html << render_menu_node(child, project) if allowed_node?(child, User.current, project)
117 child_html << render_menu_node(child, project) if allowed_node?(child, User.current, project)
118 end
118 end
119 end
119 end
120
120
121 html << content_tag(:ul, standard_children_list, :class => 'menu-children') unless standard_children_list.empty?
121 html << content_tag(:ul, standard_children_list, :class => 'menu-children') unless standard_children_list.empty?
122
122
123 # Unattached children
123 # Unattached children
124 unattached_children_list = render_unattached_children_menu(node, project)
124 unattached_children_list = render_unattached_children_menu(node, project)
125 html << content_tag(:ul, unattached_children_list, :class => 'menu-children unattached') unless unattached_children_list.blank?
125 html << content_tag(:ul, unattached_children_list, :class => 'menu-children unattached') unless unattached_children_list.blank?
126
126
127 html << '</li>'
127 html << '</li>'
128 end
128 end
129 return html.join("\n").html_safe
129 return html.join("\n").html_safe
130 end
130 end
131
131
132 # Returns a list of unattached children menu items
132 # Returns a list of unattached children menu items
133 def render_unattached_children_menu(node, project)
133 def render_unattached_children_menu(node, project)
134 return nil unless node.child_menus
134 return nil unless node.child_menus
135
135
136 "".html_safe.tap do |child_html|
136 "".html_safe.tap do |child_html|
137 unattached_children = node.child_menus.call(project)
137 unattached_children = node.child_menus.call(project)
138 # Tree nodes support #each so we need to do object detection
138 # Tree nodes support #each so we need to do object detection
139 if unattached_children.is_a? Array
139 if unattached_children.is_a? Array
140 unattached_children.each do |child|
140 unattached_children.each do |child|
141 child_html << content_tag(:li, render_unattached_menu_item(child, project)) if allowed_node?(child, User.current, project)
141 child_html << content_tag(:li, render_unattached_menu_item(child, project)) if allowed_node?(child, User.current, project)
142 end
142 end
143 else
143 else
144 raise MenuError, ":child_menus must be an array of MenuItems"
144 raise MenuError, ":child_menus must be an array of MenuItems"
145 end
145 end
146 end
146 end
147 end
147 end
148
148
149 def render_single_menu_node(item, caption, url, selected)
149 def render_single_menu_node(item, caption, url, selected)
150 link_to(h(caption), url, item.html_options(:selected => selected))
150 options = item.html_options(:selected => selected)
151
152 # virtual nodes are only there for their children to be displayed in the menu
153 # and should not do anything on click, except if otherwise defined elsewhere
154 if url.blank?
155 url = '#'
156 options.reverse_merge!(:onclick => 'return false;')
157 end
158 link_to(h(caption), url, options)
151 end
159 end
152
160
153 def render_unattached_menu_item(menu_item, project)
161 def render_unattached_menu_item(menu_item, project)
154 raise MenuError, ":child_menus must be an array of MenuItems" unless menu_item.is_a? MenuItem
162 raise MenuError, ":child_menus must be an array of MenuItems" unless menu_item.is_a? MenuItem
155
163
156 if menu_item.allowed?(User.current, project)
164 if menu_item.allowed?(User.current, project)
157 link_to(menu_item.caption, menu_item.url, menu_item.html_options)
165 link_to(menu_item.caption, menu_item.url, menu_item.html_options)
158 end
166 end
159 end
167 end
160
168
161 def menu_items_for(menu, project=nil)
169 def menu_items_for(menu, project=nil)
162 items = []
170 items = []
163 Redmine::MenuManager.items(menu).root.children.each do |node|
171 Redmine::MenuManager.items(menu).root.children.each do |node|
164 if node.allowed?(User.current, project)
172 if node.allowed?(User.current, project)
165 if block_given?
173 if block_given?
166 yield node
174 yield node
167 else
175 else
168 items << node # TODO: not used?
176 items << node # TODO: not used?
169 end
177 end
170 end
178 end
171 end
179 end
172 return block_given? ? nil : items
180 return block_given? ? nil : items
173 end
181 end
174
182
175 def extract_node_details(node, project=nil)
183 def extract_node_details(node, project=nil)
176 item = node
184 item = node
177 url = case item.url
185 url = case item.url
178 when Hash
186 when Hash
179 project.nil? ? item.url : {item.param => project}.merge(item.url)
187 project.nil? ? item.url : {item.param => project}.merge(item.url)
180 when Symbol
188 when Symbol
181 if project
189 if project
182 send(item.url, project)
190 send(item.url, project)
183 else
191 else
184 send(item.url)
192 send(item.url)
185 end
193 end
186 else
194 else
187 item.url
195 item.url
188 end
196 end
189 caption = item.caption(project)
197 caption = item.caption(project)
190 return [caption, url, (current_menu_item == item.name)]
198 return [caption, url, (current_menu_item == item.name)]
191 end
199 end
192
200
193 # See MenuItem#allowed?
201 # See MenuItem#allowed?
194 def allowed_node?(node, user, project)
202 def allowed_node?(node, user, project)
195 raise MenuError, ":child_menus must be an array of MenuItems" unless node.is_a? MenuItem
203 raise MenuError, ":child_menus must be an array of MenuItems" unless node.is_a? MenuItem
196 node.allowed?(user, project)
204 node.allowed?(user, project)
197 end
205 end
198 end
206 end
199
207
200 class << self
208 class << self
201 def map(menu_name)
209 def map(menu_name)
202 @items ||= {}
210 @items ||= {}
203 mapper = Mapper.new(menu_name.to_sym, @items)
211 mapper = Mapper.new(menu_name.to_sym, @items)
204 if block_given?
212 if block_given?
205 yield mapper
213 yield mapper
206 else
214 else
207 mapper
215 mapper
208 end
216 end
209 end
217 end
210
218
211 def items(menu_name)
219 def items(menu_name)
212 @items[menu_name.to_sym] || MenuNode.new(:root, {})
220 @items[menu_name.to_sym] || MenuNode.new(:root, {})
213 end
221 end
214 end
222 end
215
223
216 class Mapper
224 class Mapper
217 attr_reader :menu, :menu_items
225 attr_reader :menu, :menu_items
218
226
219 def initialize(menu, items)
227 def initialize(menu, items)
220 items[menu] ||= MenuNode.new(:root, {})
228 items[menu] ||= MenuNode.new(:root, {})
221 @menu = menu
229 @menu = menu
222 @menu_items = items[menu]
230 @menu_items = items[menu]
223 end
231 end
224
232
225 # Adds an item at the end of the menu. Available options:
233 # Adds an item at the end of the menu. Available options:
226 # * param: the parameter name that is used for the project id (default is :id)
234 # * param: the parameter name that is used for the project id (default is :id)
227 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
235 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
228 # * caption that can be:
236 # * caption that can be:
229 # * a localized string Symbol
237 # * a localized string Symbol
230 # * a String
238 # * a String
231 # * a Proc that can take the project as argument
239 # * a Proc that can take the project as argument
232 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
240 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
233 # * parent: menu item will be added as a child of another named menu (eg. :parent => :issues)
241 # * parent: menu item will be added as a child of another named menu (eg. :parent => :issues)
234 # * children: a Proc that is called before rendering the item. The Proc should return an array of MenuItems, which will be added as children to this item.
242 # * children: a Proc that is called before rendering the item. The Proc should return an array of MenuItems, which will be added as children to this item.
235 # eg. :children => Proc.new {|project| [Redmine::MenuManager::MenuItem.new(...)] }
243 # eg. :children => Proc.new {|project| [Redmine::MenuManager::MenuItem.new(...)] }
236 # * last: menu item will stay at the end (eg. :last => true)
244 # * last: menu item will stay at the end (eg. :last => true)
237 # * html_options: a hash of html options that are passed to link_to
245 # * html_options: a hash of html options that are passed to link_to
238 def push(name, url, options={})
246 def push(name, url, options={})
239 options = options.dup
247 options = options.dup
240
248
241 if options[:parent]
249 if options[:parent]
242 subtree = self.find(options[:parent])
250 subtree = self.find(options[:parent])
243 if subtree
251 if subtree
244 target_root = subtree
252 target_root = subtree
245 else
253 else
246 target_root = @menu_items.root
254 target_root = @menu_items.root
247 end
255 end
248
256
249 else
257 else
250 target_root = @menu_items.root
258 target_root = @menu_items.root
251 end
259 end
252
260
253 # menu item position
261 # menu item position
254 if first = options.delete(:first)
262 if first = options.delete(:first)
255 target_root.prepend(MenuItem.new(name, url, options))
263 target_root.prepend(MenuItem.new(name, url, options))
256 elsif before = options.delete(:before)
264 elsif before = options.delete(:before)
257
265
258 if exists?(before)
266 if exists?(before)
259 target_root.add_at(MenuItem.new(name, url, options), position_of(before))
267 target_root.add_at(MenuItem.new(name, url, options), position_of(before))
260 else
268 else
261 target_root.add(MenuItem.new(name, url, options))
269 target_root.add(MenuItem.new(name, url, options))
262 end
270 end
263
271
264 elsif after = options.delete(:after)
272 elsif after = options.delete(:after)
265
273
266 if exists?(after)
274 if exists?(after)
267 target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1)
275 target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1)
268 else
276 else
269 target_root.add(MenuItem.new(name, url, options))
277 target_root.add(MenuItem.new(name, url, options))
270 end
278 end
271
279
272 elsif options[:last] # don't delete, needs to be stored
280 elsif options[:last] # don't delete, needs to be stored
273 target_root.add_last(MenuItem.new(name, url, options))
281 target_root.add_last(MenuItem.new(name, url, options))
274 else
282 else
275 target_root.add(MenuItem.new(name, url, options))
283 target_root.add(MenuItem.new(name, url, options))
276 end
284 end
277 end
285 end
278
286
279 # Removes a menu item
287 # Removes a menu item
280 def delete(name)
288 def delete(name)
281 if found = self.find(name)
289 if found = self.find(name)
282 @menu_items.remove!(found)
290 @menu_items.remove!(found)
283 end
291 end
284 end
292 end
285
293
286 # Checks if a menu item exists
294 # Checks if a menu item exists
287 def exists?(name)
295 def exists?(name)
288 @menu_items.any? {|node| node.name == name}
296 @menu_items.any? {|node| node.name == name}
289 end
297 end
290
298
291 def find(name)
299 def find(name)
292 @menu_items.find {|node| node.name == name}
300 @menu_items.find {|node| node.name == name}
293 end
301 end
294
302
295 def position_of(name)
303 def position_of(name)
296 @menu_items.each do |node|
304 @menu_items.each do |node|
297 if node.name == name
305 if node.name == name
298 return node.position
306 return node.position
299 end
307 end
300 end
308 end
301 end
309 end
302 end
310 end
303
311
304 class MenuNode
312 class MenuNode
305 include Enumerable
313 include Enumerable
306 attr_accessor :parent
314 attr_accessor :parent
307 attr_reader :last_items_count, :name
315 attr_reader :last_items_count, :name
308
316
309 def initialize(name, content = nil)
317 def initialize(name, content = nil)
310 @name = name
318 @name = name
311 @children = []
319 @children = []
312 @last_items_count = 0
320 @last_items_count = 0
313 end
321 end
314
322
315 def children
323 def children
316 if block_given?
324 if block_given?
317 @children.each {|child| yield child}
325 @children.each {|child| yield child}
318 else
326 else
319 @children
327 @children
320 end
328 end
321 end
329 end
322
330
323 # Returns the number of descendants + 1
331 # Returns the number of descendants + 1
324 def size
332 def size
325 @children.inject(1) {|sum, node| sum + node.size}
333 @children.inject(1) {|sum, node| sum + node.size}
326 end
334 end
327
335
328 def each &block
336 def each &block
329 yield self
337 yield self
330 children { |child| child.each(&block) }
338 children { |child| child.each(&block) }
331 end
339 end
332
340
333 # Adds a child at first position
341 # Adds a child at first position
334 def prepend(child)
342 def prepend(child)
335 add_at(child, 0)
343 add_at(child, 0)
336 end
344 end
337
345
338 # Adds a child at given position
346 # Adds a child at given position
339 def add_at(child, position)
347 def add_at(child, position)
340 raise "Child already added" if find {|node| node.name == child.name}
348 raise "Child already added" if find {|node| node.name == child.name}
341
349
342 @children = @children.insert(position, child)
350 @children = @children.insert(position, child)
343 child.parent = self
351 child.parent = self
344 child
352 child
345 end
353 end
346
354
347 # Adds a child as last child
355 # Adds a child as last child
348 def add_last(child)
356 def add_last(child)
349 add_at(child, -1)
357 add_at(child, -1)
350 @last_items_count += 1
358 @last_items_count += 1
351 child
359 child
352 end
360 end
353
361
354 # Adds a child
362 # Adds a child
355 def add(child)
363 def add(child)
356 position = @children.size - @last_items_count
364 position = @children.size - @last_items_count
357 add_at(child, position)
365 add_at(child, position)
358 end
366 end
359 alias :<< :add
367 alias :<< :add
360
368
361 # Removes a child
369 # Removes a child
362 def remove!(child)
370 def remove!(child)
363 @children.delete(child)
371 @children.delete(child)
364 @last_items_count -= +1 if child && child.last
372 @last_items_count -= +1 if child && child.last
365 child.parent = nil
373 child.parent = nil
366 child
374 child
367 end
375 end
368
376
369 # Returns the position for this node in it's parent
377 # Returns the position for this node in it's parent
370 def position
378 def position
371 self.parent.children.index(self)
379 self.parent.children.index(self)
372 end
380 end
373
381
374 # Returns the root for this node
382 # Returns the root for this node
375 def root
383 def root
376 root = self
384 root = self
377 root = root.parent while root.parent
385 root = root.parent while root.parent
378 root
386 root
379 end
387 end
380 end
388 end
381
389
382 class MenuItem < MenuNode
390 class MenuItem < MenuNode
383 include Redmine::I18n
391 include Redmine::I18n
384 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last, :permission
392 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last, :permission
385
393
386 def initialize(name, url, options={})
394 def initialize(name, url, options={})
387 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
395 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
388 raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
396 raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
389 raise ArgumentError, "Cannot set the :parent to be the same as this item" if options[:parent] == name.to_sym
397 raise ArgumentError, "Cannot set the :parent to be the same as this item" if options[:parent] == name.to_sym
390 raise ArgumentError, "Invalid option :children for menu item '#{name}'" if options[:children] && !options[:children].respond_to?(:call)
398 raise ArgumentError, "Invalid option :children for menu item '#{name}'" if options[:children] && !options[:children].respond_to?(:call)
391 @name = name
399 @name = name
392 @url = url
400 @url = url
393 @condition = options[:if]
401 @condition = options[:if]
394 @permission = options[:permission]
402 @permission = options[:permission]
395 @permission ||= false if options.key?(:permission)
403 @permission ||= false if options.key?(:permission)
396 @param = options[:param] || :id
404 @param = options[:param] || :id
397 @caption = options[:caption]
405 @caption = options[:caption]
398 @html_options = options[:html] || {}
406 @html_options = options[:html] || {}
399 # Adds a unique class to each menu item based on its name
407 # Adds a unique class to each menu item based on its name
400 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
408 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
401 @parent = options[:parent]
409 @parent = options[:parent]
402 @child_menus = options[:children]
410 @child_menus = options[:children]
403 @last = options[:last] || false
411 @last = options[:last] || false
404 super @name.to_sym
412 super @name.to_sym
405 end
413 end
406
414
407 def caption(project=nil)
415 def caption(project=nil)
408 if @caption.is_a?(Proc)
416 if @caption.is_a?(Proc)
409 c = @caption.call(project).to_s
417 c = @caption.call(project).to_s
410 c = @name.to_s.humanize if c.blank?
418 c = @name.to_s.humanize if c.blank?
411 c
419 c
412 else
420 else
413 if @caption.nil?
421 if @caption.nil?
414 l_or_humanize(name, :prefix => 'label_')
422 l_or_humanize(name, :prefix => 'label_')
415 else
423 else
416 @caption.is_a?(Symbol) ? l(@caption) : @caption
424 @caption.is_a?(Symbol) ? l(@caption) : @caption
417 end
425 end
418 end
426 end
419 end
427 end
420
428
421 def html_options(options={})
429 def html_options(options={})
422 if options[:selected]
430 if options[:selected]
423 o = @html_options.dup
431 o = @html_options.dup
424 o[:class] += ' selected'
432 o[:class] += ' selected'
425 o
433 o
426 else
434 else
427 @html_options
435 @html_options
428 end
436 end
429 end
437 end
430
438
431 # Checks if a user is allowed to access the menu item by:
439 # Checks if a user is allowed to access the menu item by:
432 #
440 #
433 # * Checking the permission or the url target (project only)
441 # * Checking the permission or the url target (project only)
434 # * Checking the conditions of the item
442 # * Checking the conditions of the item
435 def allowed?(user, project)
443 def allowed?(user, project)
436 if user && project
444 if url.blank?
445 # this is a virtual node that is only there for its children to be diplayed in the menu
446 # it is considered an allowed node if at least one of the children is allowed
447 all_children = children
448 all_children += child_menus.call(project) if child_menus
449 return true if all_children.detect{|child| child.allowed?(user, project) }
450 return false
451 elsif user && project
437 if permission
452 if permission
438 unless user.allowed_to?(permission, project)
453 unless user.allowed_to?(permission, project)
439 return false
454 return false
440 end
455 end
441 elsif permission.nil? && url.is_a?(Hash)
456 elsif permission.nil? && url.is_a?(Hash)
442 unless user.allowed_to?(url, project)
457 unless user.allowed_to?(url, project)
443 return false
458 return false
444 end
459 end
445 end
460 end
446 end
461 end
447 if condition && !condition.call(project)
462 if condition && !condition.call(project)
448 # Condition that doesn't pass
463 # Condition that doesn't pass
449 return false
464 return false
450 end
465 end
451 return true
466 return true
452 end
467 end
453 end
468 end
454 end
469 end
455 end
470 end
@@ -1,306 +1,341
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 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 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19
19
20 class Redmine::MenuManager::MenuHelperTest < ActionView::TestCase
20 class Redmine::MenuManager::MenuHelperTest < ActionView::TestCase
21
21
22 include Redmine::MenuManager::MenuHelper
22 include Redmine::MenuManager::MenuHelper
23 include ERB::Util
23 include ERB::Util
24 fixtures :users, :members, :projects, :enabled_modules, :roles, :member_roles
24 fixtures :users, :members, :projects, :enabled_modules, :roles, :member_roles
25
25
26 def setup
26 def setup
27 setup_with_controller
27 setup_with_controller
28 # Stub the current menu item in the controller
28 # Stub the current menu item in the controller
29 def current_menu_item
29 def current_menu_item
30 :index
30 :index
31 end
31 end
32 end
32 end
33
33
34 def test_render_single_menu_node
34 def test_render_single_menu_node
35 node = Redmine::MenuManager::MenuItem.new(:testing, '/test', { })
35 node = Redmine::MenuManager::MenuItem.new(:testing, '/test', { })
36 @output_buffer = render_single_menu_node(node, 'This is a test', node.url, false)
36 @output_buffer = render_single_menu_node(node, 'This is a test', node.url, false)
37
37
38 assert_select("a.testing", "This is a test")
38 assert_select("a.testing", "This is a test")
39 end
39 end
40
40
41 def test_render_menu_node
41 def test_render_menu_node
42 single_node = Redmine::MenuManager::MenuItem.new(:single_node, '/test', { })
42 single_node = Redmine::MenuManager::MenuItem.new(:single_node, '/test', { })
43 @output_buffer = render_menu_node(single_node, nil)
43 @output_buffer = render_menu_node(single_node, nil)
44
44
45 assert_select("li") do
45 assert_select("li") do
46 assert_select("a.single-node", "Single node")
46 assert_select("a.single-node", "Single node")
47 end
47 end
48 end
48 end
49
49
50 def test_render_menu_node_with_symbol_as_url
50 def test_render_menu_node_with_symbol_as_url
51 node = Redmine::MenuManager::MenuItem.new(:testing, :issues_path)
51 node = Redmine::MenuManager::MenuItem.new(:testing, :issues_path)
52 @output_buffer = render_menu_node(node, nil)
52 @output_buffer = render_menu_node(node, nil)
53
53
54 assert_select 'a[href="/issues"]', "Testing"
54 assert_select 'a[href="/issues"]', "Testing"
55 end
55 end
56
56
57 def test_render_menu_node_with_symbol_as_url_and_project
57 def test_render_menu_node_with_symbol_as_url_and_project
58 node = Redmine::MenuManager::MenuItem.new(:testing, :project_issues_path)
58 node = Redmine::MenuManager::MenuItem.new(:testing, :project_issues_path)
59 @output_buffer = render_menu_node(node, Project.find(1))
59 @output_buffer = render_menu_node(node, Project.find(1))
60
60
61 assert_select 'a[href="/projects/ecookbook/issues"]', "Testing"
61 assert_select 'a[href="/projects/ecookbook/issues"]', "Testing"
62 end
62 end
63
63
64 def test_render_menu_node_with_nested_items
64 def test_render_menu_node_with_nested_items
65 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, '/test', { })
65 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, '/test', { })
66 parent_node << Redmine::MenuManager::MenuItem.new(:child_one_node, '/test', { })
66 parent_node << Redmine::MenuManager::MenuItem.new(:child_one_node, '/test', { })
67 parent_node << Redmine::MenuManager::MenuItem.new(:child_two_node, '/test', { })
67 parent_node << Redmine::MenuManager::MenuItem.new(:child_two_node, '/test', { })
68 parent_node <<
68 parent_node <<
69 Redmine::MenuManager::MenuItem.new(:child_three_node, '/test', { }) <<
69 Redmine::MenuManager::MenuItem.new(:child_three_node, '/test', { }) <<
70 Redmine::MenuManager::MenuItem.new(:child_three_inner_node, '/test', { })
70 Redmine::MenuManager::MenuItem.new(:child_three_inner_node, '/test', { })
71
71
72 @output_buffer = render_menu_node(parent_node, nil)
72 @output_buffer = render_menu_node(parent_node, nil)
73
73
74 assert_select("li") do
74 assert_select("li") do
75 assert_select("a.parent-node", "Parent node")
75 assert_select("a.parent-node", "Parent node")
76 assert_select("ul") do
76 assert_select("ul") do
77 assert_select("li a.child-one-node", "Child one node")
77 assert_select("li a.child-one-node", "Child one node")
78 assert_select("li a.child-two-node", "Child two node")
78 assert_select("li a.child-two-node", "Child two node")
79 assert_select("li") do
79 assert_select("li") do
80 assert_select("a.child-three-node", "Child three node")
80 assert_select("a.child-three-node", "Child three node")
81 assert_select("ul") do
81 assert_select("ul") do
82 assert_select("li a.child-three-inner-node", "Child three inner node")
82 assert_select("li a.child-three-inner-node", "Child three inner node")
83 end
83 end
84 end
84 end
85 end
85 end
86 end
86 end
87
87
88 end
88 end
89
89
90 def test_render_menu_node_with_children
90 def test_render_menu_node_with_children
91 User.current = User.find(2)
91 User.current = User.find(2)
92
92
93 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
93 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
94 '/test',
94 '/test',
95 {
95 {
96 :children => Proc.new {|p|
96 :children => Proc.new {|p|
97 children = []
97 children = []
98 3.times do |time|
98 3.times do |time|
99 children << Redmine::MenuManager::MenuItem.new("test_child_#{time}",
99 children << Redmine::MenuManager::MenuItem.new("test_child_#{time}",
100 {:controller => 'issues', :action => 'index'},
100 {:controller => 'issues', :action => 'index'},
101 {})
101 {})
102 end
102 end
103 children
103 children
104 }
104 }
105 })
105 })
106 @output_buffer = render_menu_node(parent_node, Project.find(1))
106 @output_buffer = render_menu_node(parent_node, Project.find(1))
107
107
108 assert_select("li") do
108 assert_select("li") do
109 assert_select("a.parent-node", "Parent node")
109 assert_select("a.parent-node", "Parent node")
110 assert_select("ul") do
110 assert_select("ul") do
111 assert_select("li a.test-child-0", "Test child 0")
111 assert_select("li a.test-child-0", "Test child 0")
112 assert_select("li a.test-child-1", "Test child 1")
112 assert_select("li a.test-child-1", "Test child 1")
113 assert_select("li a.test-child-2", "Test child 2")
113 assert_select("li a.test-child-2", "Test child 2")
114 end
114 end
115 end
115 end
116 end
116 end
117
117
118 def test_render_menu_node_with_nested_items_and_children
118 def test_render_menu_node_with_nested_items_and_children
119 User.current = User.find(2)
119 User.current = User.find(2)
120
120
121 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
121 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
122 {:controller => 'issues', :action => 'index'},
122 {:controller => 'issues', :action => 'index'},
123 {
123 {
124 :children => Proc.new {|p|
124 :children => Proc.new {|p|
125 children = []
125 children = []
126 3.times do |time|
126 3.times do |time|
127 children << Redmine::MenuManager::MenuItem.new("test_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
127 children << Redmine::MenuManager::MenuItem.new("test_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
128 end
128 end
129 children
129 children
130 }
130 }
131 })
131 })
132
132
133 parent_node << Redmine::MenuManager::MenuItem.new(:child_node,
133 parent_node << Redmine::MenuManager::MenuItem.new(:child_node,
134 {:controller => 'issues', :action => 'index'},
134 {:controller => 'issues', :action => 'index'},
135 {
135 {
136 :children => Proc.new {|p|
136 :children => Proc.new {|p|
137 children = []
137 children = []
138 6.times do |time|
138 6.times do |time|
139 children << Redmine::MenuManager::MenuItem.new("test_dynamic_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
139 children << Redmine::MenuManager::MenuItem.new("test_dynamic_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
140 end
140 end
141 children
141 children
142 }
142 }
143 })
143 })
144
144
145 @output_buffer = render_menu_node(parent_node, Project.find(1))
145 @output_buffer = render_menu_node(parent_node, Project.find(1))
146
146
147 assert_select("li") do
147 assert_select("li") do
148 assert_select("a.parent-node", "Parent node")
148 assert_select("a.parent-node", "Parent node")
149 assert_select("ul") do
149 assert_select("ul") do
150 assert_select("li a.child-node", "Child node")
150 assert_select("li a.child-node", "Child node")
151 assert_select("ul") do
151 assert_select("ul") do
152 assert_select("li a.test-dynamic-child-0", "Test dynamic child 0")
152 assert_select("li a.test-dynamic-child-0", "Test dynamic child 0")
153 assert_select("li a.test-dynamic-child-1", "Test dynamic child 1")
153 assert_select("li a.test-dynamic-child-1", "Test dynamic child 1")
154 assert_select("li a.test-dynamic-child-2", "Test dynamic child 2")
154 assert_select("li a.test-dynamic-child-2", "Test dynamic child 2")
155 assert_select("li a.test-dynamic-child-3", "Test dynamic child 3")
155 assert_select("li a.test-dynamic-child-3", "Test dynamic child 3")
156 assert_select("li a.test-dynamic-child-4", "Test dynamic child 4")
156 assert_select("li a.test-dynamic-child-4", "Test dynamic child 4")
157 assert_select("li a.test-dynamic-child-5", "Test dynamic child 5")
157 assert_select("li a.test-dynamic-child-5", "Test dynamic child 5")
158 end
158 end
159 assert_select("li a.test-child-0", "Test child 0")
159 assert_select("li a.test-child-0", "Test child 0")
160 assert_select("li a.test-child-1", "Test child 1")
160 assert_select("li a.test-child-1", "Test child 1")
161 assert_select("li a.test-child-2", "Test child 2")
161 assert_select("li a.test-child-2", "Test child 2")
162 end
162 end
163 end
163 end
164 end
164 end
165
165
166 def test_render_menu_node_with_allowed_and_unallowed_unattached_children
166 def test_render_menu_node_with_allowed_and_unallowed_unattached_children
167 User.current = User.find(2)
167 User.current = User.find(2)
168
168
169 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
169 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
170 {:controller => 'issues', :action => 'index'},
170 {:controller => 'issues', :action => 'index'},
171 {
171 {
172 :children => Proc.new {|p|
172 :children => Proc.new {|p|
173 [
173 [
174 Redmine::MenuManager::MenuItem.new("test_child_allowed", {:controller => 'issues', :action => 'index'}, {}),
174 Redmine::MenuManager::MenuItem.new("test_child_allowed", {:controller => 'issues', :action => 'index'}, {}),
175 Redmine::MenuManager::MenuItem.new("test_child_unallowed", {:controller => 'issues', :action => 'unallowed'}, {}),
175 Redmine::MenuManager::MenuItem.new("test_child_unallowed", {:controller => 'issues', :action => 'unallowed'}, {}),
176 ]
176 ]
177 }
177 }
178 })
178 })
179
179
180 @output_buffer = render_menu_node(parent_node, Project.find(1))
180 @output_buffer = render_menu_node(parent_node, Project.find(1))
181
181
182 assert_select("li") do
182 assert_select("li") do
183 assert_select("a.parent-node", "Parent node")
183 assert_select("a.parent-node", "Parent node")
184 assert_select("ul.menu-children.unattached") do
184 assert_select("ul.menu-children.unattached") do
185 assert_select("li a.test-child-allowed", "Test child allowed")
185 assert_select("li a.test-child-allowed", "Test child allowed")
186 assert_select("li a.test-child-unallowed", false)
186 assert_select("li a.test-child-unallowed", false)
187 end
187 end
188 end
188 end
189 end
189 end
190
190
191 def test_render_menu_node_with_allowed_and_unallowed_standard_children
191 def test_render_menu_node_with_allowed_and_unallowed_standard_children
192 User.current = User.find(6)
192 User.current = User.find(6)
193
193
194 Redmine::MenuManager.map :some_menu do |menu|
194 Redmine::MenuManager.map :some_menu do |menu|
195 menu.push(:parent_node, {:controller => 'issues', :action => 'index'}, { })
195 menu.push(:parent_node, {:controller => 'issues', :action => 'index'}, { })
196 menu.push(:test_child_allowed, {:controller => 'issues', :action => 'index'}, {:parent => :parent_node})
196 menu.push(:test_child_allowed, {:controller => 'issues', :action => 'index'}, {:parent => :parent_node})
197 menu.push(:test_child_unallowed, {:controller => 'issues', :action => 'new'}, {:parent => :parent_node})
197 menu.push(:test_child_unallowed, {:controller => 'issues', :action => 'new'}, {:parent => :parent_node})
198 end
198 end
199
199
200 @output_buffer = render_menu(:some_menu, Project.find(1))
200 @output_buffer = render_menu(:some_menu, Project.find(1))
201
201
202 assert_select("li") do
202 assert_select("li") do
203 assert_select("a.parent-node", "Parent node")
203 assert_select("a.parent-node", "Parent node")
204 assert_select("ul.menu-children.unattached", false)
204 assert_select("ul.menu-children.unattached", false)
205 assert_select("ul.menu-children") do
205 assert_select("ul.menu-children") do
206 assert_select("li a.test-child-allowed", "Test child allowed")
206 assert_select("li a.test-child-allowed", "Test child allowed")
207 assert_select("li a.test-child-unallowed", false)
207 assert_select("li a.test-child-unallowed", false)
208 end
208 end
209 end
209 end
210 end
210 end
211
212 def test_render_empty_virtual_menu_node_with_children
213
214 # only empty item with no click target
215 Redmine::MenuManager.map :menu1 do |menu|
216 menu.push(:parent_node, nil, { })
217 end
218
219 # parent with unallowed unattached child
220 Redmine::MenuManager.map :menu2 do |menu|
221 menu.push(:parent_node, nil, {:children => Proc.new {|p|
222 [Redmine::MenuManager::MenuItem.new("test_child_unallowed", {:controller => 'issues', :action => 'new'}, {})]
223 } })
224 end
225
226 # parent with unallowed standard child
227 Redmine::MenuManager.map :menu3 do |menu|
228 menu.push(:parent_node, nil, {})
229 menu.push(:test_child_unallowed, {:controller =>'issues', :action => 'new'}, {:parent => :parent_node})
230 end
231
232 # should not be displayed to anonymous
233 User.current = User.find(6)
234 assert_nil render_menu(:menu1, Project.find(1))
235 assert_nil render_menu(:menu2, Project.find(1))
236 assert_nil render_menu(:menu3, Project.find(1))
237
238 # should be displayed to an admin
239 User.current = User.find(1)
240 @output_buffer = render_menu(:menu2, Project.find(1))
241 assert_select("ul li a.parent-node", "Parent node")
242 @output_buffer = render_menu(:menu3, Project.find(1))
243 assert_select("ul li a.parent-node", "Parent node")
244
245 end
211
246
212 def test_render_menu_node_with_children_without_an_array
247 def test_render_menu_node_with_children_without_an_array
213 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
248 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
214 '/test',
249 '/test',
215 {
250 {
216 :children => Proc.new {|p| Redmine::MenuManager::MenuItem.new("test_child", "/testing", {})}
251 :children => Proc.new {|p| Redmine::MenuManager::MenuItem.new("test_child", "/testing", {})}
217 })
252 })
218
253
219 assert_raises Redmine::MenuManager::MenuError, ":children must be an array of MenuItems" do
254 assert_raises Redmine::MenuManager::MenuError, ":children must be an array of MenuItems" do
220 @output_buffer = render_menu_node(parent_node, Project.find(1))
255 @output_buffer = render_menu_node(parent_node, Project.find(1))
221 end
256 end
222 end
257 end
223
258
224 def test_render_menu_node_with_incorrect_children
259 def test_render_menu_node_with_incorrect_children
225 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
260 parent_node = Redmine::MenuManager::MenuItem.new(:parent_node,
226 '/test',
261 '/test',
227 {
262 {
228 :children => Proc.new {|p| ["a string"] }
263 :children => Proc.new {|p| ["a string"] }
229 })
264 })
230
265
231 assert_raises Redmine::MenuManager::MenuError, ":children must be an array of MenuItems" do
266 assert_raises Redmine::MenuManager::MenuError, ":children must be an array of MenuItems" do
232 @output_buffer = render_menu_node(parent_node, Project.find(1))
267 @output_buffer = render_menu_node(parent_node, Project.find(1))
233 end
268 end
234
269
235 end
270 end
236
271
237 def test_menu_items_for_should_yield_all_items_if_passed_a_block
272 def test_menu_items_for_should_yield_all_items_if_passed_a_block
238 menu_name = :test_menu_items_for_should_yield_all_items_if_passed_a_block
273 menu_name = :test_menu_items_for_should_yield_all_items_if_passed_a_block
239 Redmine::MenuManager.map menu_name do |menu|
274 Redmine::MenuManager.map menu_name do |menu|
240 menu.push(:a_menu, '/', { })
275 menu.push(:a_menu, '/', { })
241 menu.push(:a_menu_2, '/', { })
276 menu.push(:a_menu_2, '/', { })
242 menu.push(:a_menu_3, '/', { })
277 menu.push(:a_menu_3, '/', { })
243 end
278 end
244
279
245 items_yielded = []
280 items_yielded = []
246 menu_items_for(menu_name) do |item|
281 menu_items_for(menu_name) do |item|
247 items_yielded << item
282 items_yielded << item
248 end
283 end
249
284
250 assert_equal 3, items_yielded.size
285 assert_equal 3, items_yielded.size
251 end
286 end
252
287
253 def test_menu_items_for_should_return_all_items
288 def test_menu_items_for_should_return_all_items
254 menu_name = :test_menu_items_for_should_return_all_items
289 menu_name = :test_menu_items_for_should_return_all_items
255 Redmine::MenuManager.map menu_name do |menu|
290 Redmine::MenuManager.map menu_name do |menu|
256 menu.push(:a_menu, '/', { })
291 menu.push(:a_menu, '/', { })
257 menu.push(:a_menu_2, '/', { })
292 menu.push(:a_menu_2, '/', { })
258 menu.push(:a_menu_3, '/', { })
293 menu.push(:a_menu_3, '/', { })
259 end
294 end
260
295
261 items = menu_items_for(menu_name)
296 items = menu_items_for(menu_name)
262 assert_equal 3, items.size
297 assert_equal 3, items.size
263 end
298 end
264
299
265 def test_menu_items_for_should_skip_unallowed_items_on_a_project
300 def test_menu_items_for_should_skip_unallowed_items_on_a_project
266 menu_name = :test_menu_items_for_should_skip_unallowed_items_on_a_project
301 menu_name = :test_menu_items_for_should_skip_unallowed_items_on_a_project
267 Redmine::MenuManager.map menu_name do |menu|
302 Redmine::MenuManager.map menu_name do |menu|
268 menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
303 menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
269 menu.push(:a_menu_2, {:controller => 'issues', :action => 'index' }, { })
304 menu.push(:a_menu_2, {:controller => 'issues', :action => 'index' }, { })
270 menu.push(:unallowed, {:controller => 'issues', :action => 'unallowed' }, { })
305 menu.push(:unallowed, {:controller => 'issues', :action => 'unallowed' }, { })
271 end
306 end
272
307
273 User.current = User.find(2)
308 User.current = User.find(2)
274
309
275 items = menu_items_for(menu_name, Project.find(1))
310 items = menu_items_for(menu_name, Project.find(1))
276 assert_equal 2, items.size
311 assert_equal 2, items.size
277 end
312 end
278
313
279 def test_menu_items_for_should_skip_items_that_fail_the_permission
314 def test_menu_items_for_should_skip_items_that_fail_the_permission
280 menu_name = :test_menu_items_for_should_skip_items_that_fail_the_permission
315 menu_name = :test_menu_items_for_should_skip_items_that_fail_the_permission
281 Redmine::MenuManager.map menu_name do |menu|
316 Redmine::MenuManager.map menu_name do |menu|
282 menu.push(:a_menu, :project_issues_path)
317 menu.push(:a_menu, :project_issues_path)
283 menu.push(:unallowed, :project_issues_path, :permission => :unallowed)
318 menu.push(:unallowed, :project_issues_path, :permission => :unallowed)
284 end
319 end
285
320
286 User.current = User.find(2)
321 User.current = User.find(2)
287
322
288 items = menu_items_for(menu_name, Project.find(1))
323 items = menu_items_for(menu_name, Project.find(1))
289 assert_equal 1, items.size
324 assert_equal 1, items.size
290 end
325 end
291
326
292 def test_menu_items_for_should_skip_items_that_fail_the_conditions
327 def test_menu_items_for_should_skip_items_that_fail_the_conditions
293 menu_name = :test_menu_items_for_should_skip_items_that_fail_the_conditions
328 menu_name = :test_menu_items_for_should_skip_items_that_fail_the_conditions
294 Redmine::MenuManager.map menu_name do |menu|
329 Redmine::MenuManager.map menu_name do |menu|
295 menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
330 menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
296 menu.push(:unallowed,
331 menu.push(:unallowed,
297 {:controller => 'issues', :action => 'index' },
332 {:controller => 'issues', :action => 'index' },
298 { :if => Proc.new { false }})
333 { :if => Proc.new { false }})
299 end
334 end
300
335
301 User.current = User.find(2)
336 User.current = User.find(2)
302
337
303 items = menu_items_for(menu_name, Project.find(1))
338 items = menu_items_for(menu_name, Project.find(1))
304 assert_equal 1, items.size
339 assert_equal 1, items.size
305 end
340 end
306 end
341 end
General Comments 0
You need to be logged in to leave comments. Login now