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