##// END OF EJS Templates
Add missing tests for Redmine::MenuManager::Mapper#initialize....
Jean-Baptiste Barth -
r11542:953e4b4abf4f
parent child
Show More
@@ -1,435 +1,437
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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 && User.current.allowed_to?(item.url, project) && (item.condition.nil? || item.condition.call(project))
64 if item && User.current.allowed_to?(item.url, project) && (item.condition.nil? || item.condition.call(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("\n").html_safe)
93 links.empty? ? nil : content_tag('ul', links.join("\n").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)
117 child_html << render_menu_node(child, 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))
141 child_html << content_tag(:li, render_unattached_menu_item(child, 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 link_to(h(caption), url, item.html_options(:selected => selected))
151 end
151 end
152
152
153 def render_unattached_menu_item(menu_item, project)
153 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
154 raise MenuError, ":child_menus must be an array of MenuItems" unless menu_item.is_a? MenuItem
155
155
156 if User.current.allowed_to?(menu_item.url, project)
156 if User.current.allowed_to?(menu_item.url, project)
157 link_to(h(menu_item.caption),
157 link_to(h(menu_item.caption),
158 menu_item.url,
158 menu_item.url,
159 menu_item.html_options)
159 menu_item.html_options)
160 end
160 end
161 end
161 end
162
162
163 def menu_items_for(menu, project=nil)
163 def menu_items_for(menu, project=nil)
164 items = []
164 items = []
165 Redmine::MenuManager.items(menu).root.children.each do |node|
165 Redmine::MenuManager.items(menu).root.children.each do |node|
166 if allowed_node?(node, User.current, project)
166 if allowed_node?(node, User.current, project)
167 if block_given?
167 if block_given?
168 yield node
168 yield node
169 else
169 else
170 items << node # TODO: not used?
170 items << node # TODO: not used?
171 end
171 end
172 end
172 end
173 end
173 end
174 return block_given? ? nil : items
174 return block_given? ? nil : items
175 end
175 end
176
176
177 def extract_node_details(node, project=nil)
177 def extract_node_details(node, project=nil)
178 item = node
178 item = node
179 url = case item.url
179 url = case item.url
180 when Hash
180 when Hash
181 project.nil? ? item.url : {item.param => project}.merge(item.url)
181 project.nil? ? item.url : {item.param => project}.merge(item.url)
182 when Symbol
182 when Symbol
183 send(item.url)
183 send(item.url)
184 else
184 else
185 item.url
185 item.url
186 end
186 end
187 caption = item.caption(project)
187 caption = item.caption(project)
188 return [caption, url, (current_menu_item == item.name)]
188 return [caption, url, (current_menu_item == item.name)]
189 end
189 end
190
190
191 # Checks if a user is allowed to access the menu item by:
191 # Checks if a user is allowed to access the menu item by:
192 #
192 #
193 # * Checking the url target (project only)
193 # * Checking the url target (project only)
194 # * Checking the conditions of the item
194 # * Checking the conditions of the item
195 def allowed_node?(node, user, project)
195 def allowed_node?(node, user, project)
196 if project && user && !user.allowed_to?(node.url, project)
196 if project && user && !user.allowed_to?(node.url, project)
197 return false
197 return false
198 end
198 end
199 if node.condition && !node.condition.call(project)
199 if node.condition && !node.condition.call(project)
200 # Condition that doesn't pass
200 # Condition that doesn't pass
201 return false
201 return false
202 end
202 end
203 return true
203 return true
204 end
204 end
205 end
205 end
206
206
207 class << self
207 class << self
208 def map(menu_name)
208 def map(menu_name)
209 @items ||= {}
209 @items ||= {}
210 mapper = Mapper.new(menu_name.to_sym, @items)
210 mapper = Mapper.new(menu_name.to_sym, @items)
211 if block_given?
211 if block_given?
212 yield mapper
212 yield mapper
213 else
213 else
214 mapper
214 mapper
215 end
215 end
216 end
216 end
217
217
218 def items(menu_name)
218 def items(menu_name)
219 @items[menu_name.to_sym] || MenuNode.new(:root, {})
219 @items[menu_name.to_sym] || MenuNode.new(:root, {})
220 end
220 end
221 end
221 end
222
222
223 class Mapper
223 class Mapper
224 attr_reader :menu, :menu_items
225
224 def initialize(menu, items)
226 def initialize(menu, items)
225 items[menu] ||= MenuNode.new(:root, {})
227 items[menu] ||= MenuNode.new(:root, {})
226 @menu = menu
228 @menu = menu
227 @menu_items = items[menu]
229 @menu_items = items[menu]
228 end
230 end
229
231
230 # Adds an item at the end of the menu. Available options:
232 # Adds an item at the end of the menu. Available options:
231 # * param: the parameter name that is used for the project id (default is :id)
233 # * param: the parameter name that is used for the project id (default is :id)
232 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
234 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
233 # * caption that can be:
235 # * caption that can be:
234 # * a localized string Symbol
236 # * a localized string Symbol
235 # * a String
237 # * a String
236 # * a Proc that can take the project as argument
238 # * a Proc that can take the project as argument
237 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
239 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
238 # * parent: menu item will be added as a child of another named menu (eg. :parent => :issues)
240 # * parent: menu item will be added as a child of another named menu (eg. :parent => :issues)
239 # * 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.
241 # * 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.
240 # eg. :children => Proc.new {|project| [Redmine::MenuManager::MenuItem.new(...)] }
242 # eg. :children => Proc.new {|project| [Redmine::MenuManager::MenuItem.new(...)] }
241 # * last: menu item will stay at the end (eg. :last => true)
243 # * last: menu item will stay at the end (eg. :last => true)
242 # * html_options: a hash of html options that are passed to link_to
244 # * html_options: a hash of html options that are passed to link_to
243 def push(name, url, options={})
245 def push(name, url, options={})
244 options = options.dup
246 options = options.dup
245
247
246 if options[:parent]
248 if options[:parent]
247 subtree = self.find(options[:parent])
249 subtree = self.find(options[:parent])
248 if subtree
250 if subtree
249 target_root = subtree
251 target_root = subtree
250 else
252 else
251 target_root = @menu_items.root
253 target_root = @menu_items.root
252 end
254 end
253
255
254 else
256 else
255 target_root = @menu_items.root
257 target_root = @menu_items.root
256 end
258 end
257
259
258 # menu item position
260 # menu item position
259 if first = options.delete(:first)
261 if first = options.delete(:first)
260 target_root.prepend(MenuItem.new(name, url, options))
262 target_root.prepend(MenuItem.new(name, url, options))
261 elsif before = options.delete(:before)
263 elsif before = options.delete(:before)
262
264
263 if exists?(before)
265 if exists?(before)
264 target_root.add_at(MenuItem.new(name, url, options), position_of(before))
266 target_root.add_at(MenuItem.new(name, url, options), position_of(before))
265 else
267 else
266 target_root.add(MenuItem.new(name, url, options))
268 target_root.add(MenuItem.new(name, url, options))
267 end
269 end
268
270
269 elsif after = options.delete(:after)
271 elsif after = options.delete(:after)
270
272
271 if exists?(after)
273 if exists?(after)
272 target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1)
274 target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1)
273 else
275 else
274 target_root.add(MenuItem.new(name, url, options))
276 target_root.add(MenuItem.new(name, url, options))
275 end
277 end
276
278
277 elsif options[:last] # don't delete, needs to be stored
279 elsif options[:last] # don't delete, needs to be stored
278 target_root.add_last(MenuItem.new(name, url, options))
280 target_root.add_last(MenuItem.new(name, url, options))
279 else
281 else
280 target_root.add(MenuItem.new(name, url, options))
282 target_root.add(MenuItem.new(name, url, options))
281 end
283 end
282 end
284 end
283
285
284 # Removes a menu item
286 # Removes a menu item
285 def delete(name)
287 def delete(name)
286 if found = self.find(name)
288 if found = self.find(name)
287 @menu_items.remove!(found)
289 @menu_items.remove!(found)
288 end
290 end
289 end
291 end
290
292
291 # Checks if a menu item exists
293 # Checks if a menu item exists
292 def exists?(name)
294 def exists?(name)
293 @menu_items.any? {|node| node.name == name}
295 @menu_items.any? {|node| node.name == name}
294 end
296 end
295
297
296 def find(name)
298 def find(name)
297 @menu_items.find {|node| node.name == name}
299 @menu_items.find {|node| node.name == name}
298 end
300 end
299
301
300 def position_of(name)
302 def position_of(name)
301 @menu_items.each do |node|
303 @menu_items.each do |node|
302 if node.name == name
304 if node.name == name
303 return node.position
305 return node.position
304 end
306 end
305 end
307 end
306 end
308 end
307 end
309 end
308
310
309 class MenuNode
311 class MenuNode
310 include Enumerable
312 include Enumerable
311 attr_accessor :parent
313 attr_accessor :parent
312 attr_reader :last_items_count, :name
314 attr_reader :last_items_count, :name
313
315
314 def initialize(name, content = nil)
316 def initialize(name, content = nil)
315 @name = name
317 @name = name
316 @children = []
318 @children = []
317 @last_items_count = 0
319 @last_items_count = 0
318 end
320 end
319
321
320 def children
322 def children
321 if block_given?
323 if block_given?
322 @children.each {|child| yield child}
324 @children.each {|child| yield child}
323 else
325 else
324 @children
326 @children
325 end
327 end
326 end
328 end
327
329
328 # Returns the number of descendants + 1
330 # Returns the number of descendants + 1
329 def size
331 def size
330 @children.inject(1) {|sum, node| sum + node.size}
332 @children.inject(1) {|sum, node| sum + node.size}
331 end
333 end
332
334
333 def each &block
335 def each &block
334 yield self
336 yield self
335 children { |child| child.each(&block) }
337 children { |child| child.each(&block) }
336 end
338 end
337
339
338 # Adds a child at first position
340 # Adds a child at first position
339 def prepend(child)
341 def prepend(child)
340 add_at(child, 0)
342 add_at(child, 0)
341 end
343 end
342
344
343 # Adds a child at given position
345 # Adds a child at given position
344 def add_at(child, position)
346 def add_at(child, position)
345 raise "Child already added" if find {|node| node.name == child.name}
347 raise "Child already added" if find {|node| node.name == child.name}
346
348
347 @children = @children.insert(position, child)
349 @children = @children.insert(position, child)
348 child.parent = self
350 child.parent = self
349 child
351 child
350 end
352 end
351
353
352 # Adds a child as last child
354 # Adds a child as last child
353 def add_last(child)
355 def add_last(child)
354 add_at(child, -1)
356 add_at(child, -1)
355 @last_items_count += 1
357 @last_items_count += 1
356 child
358 child
357 end
359 end
358
360
359 # Adds a child
361 # Adds a child
360 def add(child)
362 def add(child)
361 position = @children.size - @last_items_count
363 position = @children.size - @last_items_count
362 add_at(child, position)
364 add_at(child, position)
363 end
365 end
364 alias :<< :add
366 alias :<< :add
365
367
366 # Removes a child
368 # Removes a child
367 def remove!(child)
369 def remove!(child)
368 @children.delete(child)
370 @children.delete(child)
369 @last_items_count -= +1 if child && child.last
371 @last_items_count -= +1 if child && child.last
370 child.parent = nil
372 child.parent = nil
371 child
373 child
372 end
374 end
373
375
374 # Returns the position for this node in it's parent
376 # Returns the position for this node in it's parent
375 def position
377 def position
376 self.parent.children.index(self)
378 self.parent.children.index(self)
377 end
379 end
378
380
379 # Returns the root for this node
381 # Returns the root for this node
380 def root
382 def root
381 root = self
383 root = self
382 root = root.parent while root.parent
384 root = root.parent while root.parent
383 root
385 root
384 end
386 end
385 end
387 end
386
388
387 class MenuItem < MenuNode
389 class MenuItem < MenuNode
388 include Redmine::I18n
390 include Redmine::I18n
389 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last
391 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last
390
392
391 def initialize(name, url, options)
393 def initialize(name, url, options)
392 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
394 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
393 raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
395 raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
394 raise ArgumentError, "Cannot set the :parent to be the same as this item" if options[:parent] == name.to_sym
396 raise ArgumentError, "Cannot set the :parent to be the same as this item" if options[:parent] == name.to_sym
395 raise ArgumentError, "Invalid option :children for menu item '#{name}'" if options[:children] && !options[:children].respond_to?(:call)
397 raise ArgumentError, "Invalid option :children for menu item '#{name}'" if options[:children] && !options[:children].respond_to?(:call)
396 @name = name
398 @name = name
397 @url = url
399 @url = url
398 @condition = options[:if]
400 @condition = options[:if]
399 @param = options[:param] || :id
401 @param = options[:param] || :id
400 @caption = options[:caption]
402 @caption = options[:caption]
401 @html_options = options[:html] || {}
403 @html_options = options[:html] || {}
402 # Adds a unique class to each menu item based on its name
404 # Adds a unique class to each menu item based on its name
403 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
405 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
404 @parent = options[:parent]
406 @parent = options[:parent]
405 @child_menus = options[:children]
407 @child_menus = options[:children]
406 @last = options[:last] || false
408 @last = options[:last] || false
407 super @name.to_sym
409 super @name.to_sym
408 end
410 end
409
411
410 def caption(project=nil)
412 def caption(project=nil)
411 if @caption.is_a?(Proc)
413 if @caption.is_a?(Proc)
412 c = @caption.call(project).to_s
414 c = @caption.call(project).to_s
413 c = @name.to_s.humanize if c.blank?
415 c = @name.to_s.humanize if c.blank?
414 c
416 c
415 else
417 else
416 if @caption.nil?
418 if @caption.nil?
417 l_or_humanize(name, :prefix => 'label_')
419 l_or_humanize(name, :prefix => 'label_')
418 else
420 else
419 @caption.is_a?(Symbol) ? l(@caption) : @caption
421 @caption.is_a?(Symbol) ? l(@caption) : @caption
420 end
422 end
421 end
423 end
422 end
424 end
423
425
424 def html_options(options={})
426 def html_options(options={})
425 if options[:selected]
427 if options[:selected]
426 o = @html_options.dup
428 o = @html_options.dup
427 o[:class] += ' selected'
429 o[:class] += ' selected'
428 o
430 o
429 else
431 else
430 @html_options
432 @html_options
431 end
433 end
432 end
434 end
433 end
435 end
434 end
436 end
435 end
437 end
@@ -1,182 +1,193
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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::MapperTest < ActiveSupport::TestCase
20 class Redmine::MenuManager::MapperTest < ActiveSupport::TestCase
21 context "Mapper#initialize" do
21 context "Mapper#initialize" do
22 should "be tested"
22 should "define a root MenuNode if menu is not present in items" do
23 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
24 node = menu_mapper.menu_items
25 assert_not_nil node
26 assert_equal :root, node.name
27 end
28
29 should "use existing MenuNode if present" do
30 node = "foo" # just an arbitrary reference
31 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {:test_menu => node})
32 assert_equal node, menu_mapper.menu_items
33 end
23 end
34 end
24
35
25 def test_push_onto_root
36 def test_push_onto_root
26 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
37 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
27 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
38 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
28
39
29 menu_mapper.exists?(:test_overview)
40 menu_mapper.exists?(:test_overview)
30 end
41 end
31
42
32 def test_push_onto_parent
43 def test_push_onto_parent
33 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
44 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
34 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
45 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
35 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
46 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
36
47
37 assert menu_mapper.exists?(:test_child)
48 assert menu_mapper.exists?(:test_child)
38 assert_equal :test_child, menu_mapper.find(:test_child).name
49 assert_equal :test_child, menu_mapper.find(:test_child).name
39 end
50 end
40
51
41 def test_push_onto_grandparent
52 def test_push_onto_grandparent
42 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
53 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
43 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
54 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
44 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
55 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview}
45 menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent => :test_child}
56 menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent => :test_child}
46
57
47 assert menu_mapper.exists?(:test_grandchild)
58 assert menu_mapper.exists?(:test_grandchild)
48 grandchild = menu_mapper.find(:test_grandchild)
59 grandchild = menu_mapper.find(:test_grandchild)
49 assert_equal :test_grandchild, grandchild.name
60 assert_equal :test_grandchild, grandchild.name
50 assert_equal :test_child, grandchild.parent.name
61 assert_equal :test_child, grandchild.parent.name
51 end
62 end
52
63
53 def test_push_first
64 def test_push_first
54 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
65 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
55 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
66 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
56 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
67 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
57 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
68 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
58 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
69 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
59 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true}
70 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true}
60
71
61 root = menu_mapper.find(:root)
72 root = menu_mapper.find(:root)
62 assert_equal 5, root.children.size
73 assert_equal 5, root.children.size
63 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
74 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
64 assert_not_nil root.children[position]
75 assert_not_nil root.children[position]
65 assert_equal name, root.children[position].name
76 assert_equal name, root.children[position].name
66 end
77 end
67
78
68 end
79 end
69
80
70 def test_push_before
81 def test_push_before
71 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
82 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
72 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
83 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
73 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
84 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
74 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
85 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
75 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
86 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
76 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth}
87 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth}
77
88
78 root = menu_mapper.find(:root)
89 root = menu_mapper.find(:root)
79 assert_equal 5, root.children.size
90 assert_equal 5, root.children.size
80 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
91 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
81 assert_not_nil root.children[position]
92 assert_not_nil root.children[position]
82 assert_equal name, root.children[position].name
93 assert_equal name, root.children[position].name
83 end
94 end
84
95
85 end
96 end
86
97
87 def test_push_after
98 def test_push_after
88 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
99 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
89 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
100 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
90 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
101 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
91 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
102 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
92 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
103 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
93 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third}
104 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third}
94
105
95 root = menu_mapper.find(:root)
106 root = menu_mapper.find(:root)
96 assert_equal 5, root.children.size
107 assert_equal 5, root.children.size
97 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
108 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
98 assert_not_nil root.children[position]
109 assert_not_nil root.children[position]
99 assert_equal name, root.children[position].name
110 assert_equal name, root.children[position].name
100 end
111 end
101
112
102 end
113 end
103
114
104 def test_push_last
115 def test_push_last
105 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
116 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
106 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
117 menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
107 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
118 menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
108 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
119 menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
109 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true}
120 menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true}
110 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
121 menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
111
122
112 root = menu_mapper.find(:root)
123 root = menu_mapper.find(:root)
113 assert_equal 5, root.children.size
124 assert_equal 5, root.children.size
114 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
125 {0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
115 assert_not_nil root.children[position]
126 assert_not_nil root.children[position]
116 assert_equal name, root.children[position].name
127 assert_equal name, root.children[position].name
117 end
128 end
118
129
119 end
130 end
120
131
121 def test_exists_for_child_node
132 def test_exists_for_child_node
122 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
133 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
123 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
134 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
124 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview }
135 menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent => :test_overview }
125
136
126 assert menu_mapper.exists?(:test_child)
137 assert menu_mapper.exists?(:test_child)
127 end
138 end
128
139
129 def test_exists_for_invalid_node
140 def test_exists_for_invalid_node
130 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
141 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
131 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
142 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
132
143
133 assert !menu_mapper.exists?(:nothing)
144 assert !menu_mapper.exists?(:nothing)
134 end
145 end
135
146
136 def test_find
147 def test_find
137 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
148 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
138 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
149 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
139
150
140 item = menu_mapper.find(:test_overview)
151 item = menu_mapper.find(:test_overview)
141 assert_equal :test_overview, item.name
152 assert_equal :test_overview, item.name
142 assert_equal({:controller => 'projects', :action => 'show'}, item.url)
153 assert_equal({:controller => 'projects', :action => 'show'}, item.url)
143 end
154 end
144
155
145 def test_find_missing
156 def test_find_missing
146 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
157 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
147 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
158 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
148
159
149 item = menu_mapper.find(:nothing)
160 item = menu_mapper.find(:nothing)
150 assert_equal nil, item
161 assert_equal nil, item
151 end
162 end
152
163
153 def test_delete
164 def test_delete
154 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
165 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
155 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
166 menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
156 assert_not_nil menu_mapper.delete(:test_overview)
167 assert_not_nil menu_mapper.delete(:test_overview)
157
168
158 assert_nil menu_mapper.find(:test_overview)
169 assert_nil menu_mapper.find(:test_overview)
159 end
170 end
160
171
161 def test_delete_missing
172 def test_delete_missing
162 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
173 menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
163 assert_nil menu_mapper.delete(:test_missing)
174 assert_nil menu_mapper.delete(:test_missing)
164 end
175 end
165
176
166 test 'deleting all items' do
177 test 'deleting all items' do
167 # Exposed by deleting :last items
178 # Exposed by deleting :last items
168 Redmine::MenuManager.map :test_menu do |menu|
179 Redmine::MenuManager.map :test_menu do |menu|
169 menu.push :not_last, Redmine::Info.help_url
180 menu.push :not_last, Redmine::Info.help_url
170 menu.push :administration, { :controller => 'projects', :action => 'show'}, {:last => true}
181 menu.push :administration, { :controller => 'projects', :action => 'show'}, {:last => true}
171 menu.push :help, Redmine::Info.help_url, :last => true
182 menu.push :help, Redmine::Info.help_url, :last => true
172 end
183 end
173
184
174 assert_nothing_raised do
185 assert_nothing_raised do
175 Redmine::MenuManager.map :test_menu do |menu|
186 Redmine::MenuManager.map :test_menu do |menu|
176 menu.delete(:administration)
187 menu.delete(:administration)
177 menu.delete(:help)
188 menu.delete(:help)
178 menu.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
189 menu.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
179 end
190 end
180 end
191 end
181 end
192 end
182 end
193 end
General Comments 0
You need to be logged in to leave comments. Login now