Patch #4250 » 0001-Converted-Menus-to-a-Tree-structure-to-allow-submenu.patch
config/environment.rb | ||
---|---|---|
50 | 50 |
# It will automatically turn deliveries on |
51 | 51 |
config.action_mailer.perform_deliveries = false |
52 | 52 | |
53 |
config.gem 'rubytree', :lib => 'tree' |
|
54 |
|
|
53 | 55 |
# Load any local configuration that is kept out of source control |
54 | 56 |
# (e.g. gems, patches). |
55 | 57 |
if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb')) |
lib/redmine/menu_manager.rb | ||
---|---|---|
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 |
require 'tree' # gem install rubytree |
|
19 | ||
20 |
# Monkey patch the TreeNode to add on a few more methods :nodoc: |
|
21 |
module TreeNodePatch |
|
22 |
def self.included(base) |
|
23 |
base.class_eval do |
|
24 |
attr_reader :last_items_count |
|
25 |
|
|
26 |
alias :old_initilize :initialize |
|
27 |
def initialize(name, content = nil) |
|
28 |
old_initilize(name, content) |
|
29 |
@last_items_count = 0 |
|
30 |
extend(InstanceMethods) |
|
31 |
end |
|
32 |
end |
|
33 |
end |
|
34 |
|
|
35 |
module InstanceMethods |
|
36 |
# Adds the specified child node to the receiver node. The child node's |
|
37 |
# parent is set to be the receiver. The child is added as the first child in |
|
38 |
# the current list of children for the receiver node. |
|
39 |
def prepend(child) |
|
40 |
raise "Child already added" if @childrenHash.has_key?(child.name) |
|
41 | ||
42 |
@childrenHash[child.name] = child |
|
43 |
@children = [child] + @children |
|
44 |
child.parent = self |
|
45 |
return child |
|
46 | ||
47 |
end |
|
48 | ||
49 |
# Adds the specified child node to the receiver node. The child node's |
|
50 |
# parent is set to be the receiver. The child is added at the position |
|
51 |
# into the current list of children for the receiver node. |
|
52 |
def add_at(child, position) |
|
53 |
raise "Child already added" if @childrenHash.has_key?(child.name) |
|
54 | ||
55 |
@childrenHash[child.name] = child |
|
56 |
@children = @children.insert(position, child) |
|
57 |
child.parent = self |
|
58 |
return child |
|
59 | ||
60 |
end |
|
61 | ||
62 |
def add_last(child) |
|
63 |
raise "Child already added" if @childrenHash.has_key?(child.name) |
|
64 | ||
65 |
@childrenHash[child.name] = child |
|
66 |
@children << child |
|
67 |
@last_items_count += 1 |
|
68 |
child.parent = self |
|
69 |
return child |
|
70 | ||
71 |
end |
|
72 | ||
73 |
# Adds the specified child node to the receiver node. The child node's |
|
74 |
# parent is set to be the receiver. The child is added as the last child in |
|
75 |
# the current list of children for the receiver node. |
|
76 |
def add(child) |
|
77 |
raise "Child already added" if @childrenHash.has_key?(child.name) |
|
78 | ||
79 |
@childrenHash[child.name] = child |
|
80 |
position = @children.size - @last_items_count |
|
81 |
@children.insert(position, child) |
|
82 |
child.parent = self |
|
83 |
return child |
|
84 | ||
85 |
end |
|
86 | ||
87 |
# Will return the position (zero-based) of the current child in |
|
88 |
# it's parent |
|
89 |
def position |
|
90 |
self.parent.children.index(self) |
|
91 |
end |
|
92 |
end |
|
93 |
end |
|
94 |
Tree::TreeNode.send(:include, TreeNodePatch) |
|
95 | ||
18 | 96 |
module Redmine |
19 | 97 |
module MenuManager |
20 | 98 |
module MenuController |
... | ... | |
79 | 157 |
|
80 | 158 |
def render_menu(menu, project=nil) |
81 | 159 |
links = [] |
82 |
menu_items_for(menu, project) do |item, caption, url, selected| |
|
83 |
links << content_tag('li', |
|
84 |
link_to(h(caption), url, item.html_options(:selected => selected))) |
|
160 |
menu_items_for(menu, project) do |node| |
|
161 |
links << render_menu_node(node, project) |
|
85 | 162 |
end |
86 | 163 |
links.empty? ? nil : content_tag('ul', links.join("\n")) |
87 | 164 |
end |
88 | 165 | |
166 |
def render_menu_node(node, project=nil) |
|
167 |
caption, url, selected = extract_node_details(node, project) |
|
168 |
if node.hasChildren? |
|
169 |
html = [] |
|
170 |
html << '<li>' |
|
171 |
html << render_single_menu_node(node, caption, url, selected) # parent |
|
172 |
html << ' <ul>' |
|
173 |
node.children.each do |child| |
|
174 |
html << render_menu_node(child, project) |
|
175 |
end |
|
176 |
html << ' </ul>' |
|
177 |
html << '</li>' |
|
178 |
return html.join("\n") |
|
179 |
else |
|
180 |
return content_tag('li', |
|
181 |
render_single_menu_node(node, caption, url, selected)) |
|
182 |
end |
|
183 |
end |
|
184 | ||
185 |
def render_single_menu_node(item, caption, url, selected) |
|
186 |
link_to(h(caption), url, item.html_options(:selected => selected)) |
|
187 |
end |
|
188 |
|
|
89 | 189 |
def menu_items_for(menu, project=nil) |
90 | 190 |
items = [] |
91 |
Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item| |
|
92 |
unless item.condition && !item.condition.call(project) |
|
93 |
url = case item.url |
|
94 |
when Hash |
|
95 |
project.nil? ? item.url : {item.param => project}.merge(item.url) |
|
96 |
when Symbol |
|
97 |
send(item.url) |
|
98 |
else |
|
99 |
item.url |
|
100 |
end |
|
101 |
caption = item.caption(project) |
|
191 |
Redmine::MenuManager.items(menu).root.children.each do |node| |
|
192 |
if allowed_node?(node, User.current, project) |
|
102 | 193 |
if block_given? |
103 |
yield item, caption, url, (current_menu_item == item.name)
|
|
194 |
yield node
|
|
104 | 195 |
else |
105 |
items << [item, caption, url, (current_menu_item == item.name)]
|
|
196 |
items << node # TODO: not used?
|
|
106 | 197 |
end |
107 | 198 |
end |
108 | 199 |
end |
109 | 200 |
return block_given? ? nil : items |
110 | 201 |
end |
202 | ||
203 |
def extract_node_details(node, project=nil) |
|
204 |
item = node |
|
205 |
url = case item.url |
|
206 |
when Hash |
|
207 |
project.nil? ? item.url : {item.param => project}.merge(item.url) |
|
208 |
when Symbol |
|
209 |
send(item.url) |
|
210 |
else |
|
211 |
item.url |
|
212 |
end |
|
213 |
caption = item.caption(project) |
|
214 |
return [caption, url, (current_menu_item == item.name)] |
|
215 |
end |
|
216 | ||
217 |
# Checks if a user is allowed to access the menu item by: |
|
218 |
# |
|
219 |
# * Checking the conditions of the item |
|
220 |
# * Checking the url target (project only) |
|
221 |
def allowed_node?(node, user, project) |
|
222 |
if node.condition && !node.condition.call(project) |
|
223 |
# Condition that doesn't pass |
|
224 |
return false |
|
225 |
end |
|
226 | ||
227 |
if project |
|
228 |
return user && user.allowed_to?(node.url, project) |
|
229 |
else |
|
230 |
# outside a project, all menu items allowed |
|
231 |
return true |
|
232 |
end |
|
233 |
end |
|
111 | 234 |
end |
112 | 235 |
|
113 | 236 |
class << self |
... | ... | |
122 | 245 |
end |
123 | 246 |
|
124 | 247 |
def items(menu_name) |
125 |
@items[menu_name.to_sym] || [] |
|
126 |
end |
|
127 |
|
|
128 |
def allowed_items(menu_name, user, project) |
|
129 |
project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name) |
|
248 |
@items[menu_name.to_sym] || Tree::TreeNode.new(:root, {}) |
|
130 | 249 |
end |
131 | 250 |
end |
132 | 251 |
|
133 | 252 |
class Mapper |
134 | 253 |
def initialize(menu, items) |
135 |
items[menu] ||= []
|
|
254 |
items[menu] ||= Tree::TreeNode.new(:root, {})
|
|
136 | 255 |
@menu = menu |
137 | 256 |
@menu_items = items[menu] |
138 | 257 |
end |
... | ... | |
151 | 270 |
# * html_options: a hash of html options that are passed to link_to |
152 | 271 |
def push(name, url, options={}) |
153 | 272 |
options = options.dup |
154 |
|
|
273 | ||
274 |
if options[:parent_menu] |
|
275 |
subtree = self.find(options[:parent_menu]) |
|
276 |
if subtree |
|
277 |
target_root = subtree |
|
278 |
else |
|
279 |
target_root = @menu_items.root |
|
280 |
end |
|
281 | ||
282 |
else |
|
283 |
target_root = @menu_items.root |
|
284 |
end |
|
285 | ||
155 | 286 |
# menu item position |
156 |
if before = options.delete(:before) |
|
157 |
position = @menu_items.collect(&:name).index(before) |
|
287 |
if first = options.delete(:first) |
|
288 |
target_root.prepend(MenuItem.new(name, url, options)) |
|
289 |
elsif before = options.delete(:before) |
|
290 | ||
291 |
if exists?(before) |
|
292 |
target_root.add_at(MenuItem.new(name, url, options), position_of(before)) |
|
293 |
else |
|
294 |
target_root.add(MenuItem.new(name, url, options)) |
|
295 |
end |
|
296 | ||
158 | 297 |
elsif after = options.delete(:after) |
159 |
position = @menu_items.collect(&:name).index(after) |
|
160 |
position += 1 unless position.nil? |
|
298 | ||
299 |
if exists?(after) |
|
300 |
target_root.add_at(MenuItem.new(name, url, options), position_of(after) + 1) |
|
301 |
else |
|
302 |
target_root.add(MenuItem.new(name, url, options)) |
|
303 |
end |
|
304 |
|
|
161 | 305 |
elsif options.delete(:last) |
162 |
position = @menu_items.size |
|
163 |
@@last_items_count[@menu] += 1 |
|
306 |
target_root.add_last(MenuItem.new(name, url, options)) |
|
307 |
else |
|
308 |
target_root.add(MenuItem.new(name, url, options)) |
|
164 | 309 |
end |
165 |
# default position |
|
166 |
position ||= @menu_items.size - @@last_items_count[@menu] |
|
167 |
|
|
168 |
@menu_items.insert(position, MenuItem.new(name, url, options)) |
|
169 | 310 |
end |
170 | 311 |
|
171 | 312 |
# Removes a menu item |
172 | 313 |
def delete(name) |
173 |
@menu_items.delete_if {|i| i.name == name} |
|
314 |
if found = self.find(name) |
|
315 |
@menu_items.remove!(found) |
|
316 |
end |
|
317 |
end |
|
318 | ||
319 |
# Checks if a menu item exists |
|
320 |
def exists?(name) |
|
321 |
@menu_items.any? {|node| node.name == name} |
|
322 |
end |
|
323 | ||
324 |
def find(name) |
|
325 |
@menu_items.find {|node| node.name == name} |
|
326 |
end |
|
327 | ||
328 |
def position_of(name) |
|
329 |
@menu_items.each do |node| |
|
330 |
if node.name == name |
|
331 |
return node.position |
|
332 |
end |
|
333 |
end |
|
174 | 334 |
end |
175 | 335 |
end |
176 | 336 |
|
177 |
class MenuItem |
|
337 |
class MenuItem < Tree::TreeNode
|
|
178 | 338 |
include Redmine::I18n |
179 |
attr_reader :name, :url, :param, :condition |
|
339 |
attr_reader :name, :url, :param, :condition, :parent_menu
|
|
180 | 340 |
|
181 | 341 |
def initialize(name, url, options) |
182 |
raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call) |
|
183 |
raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash) |
|
342 |
raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call) |
|
343 |
raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash) |
|
344 |
raise ArgumentError, "Cannot set the :parent_menu to be the same as this item" if options[:parent_menu] == name.to_sym |
|
184 | 345 |
@name = name |
185 | 346 |
@url = url |
186 | 347 |
@condition = options[:if] |
... | ... | |
189 | 350 |
@html_options = options[:html] || {} |
190 | 351 |
# Adds a unique class to each menu item based on its name |
191 | 352 |
@html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ') |
353 |
@parent_menu = options[:parent_menu] |
|
354 |
super @name.to_sym |
|
192 | 355 |
end |
193 | 356 |
|
194 | 357 |
def caption(project=nil) |
test/unit/lib/redmine/menu_manager/mapper_test.rb | ||
---|---|---|
1 |
# Redmine - project management software |
|
2 |
# Copyright (C) 2006-2009 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
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 |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | ||
18 |
require File.dirname(__FILE__) + '/../../../../test_helper' |
|
19 | ||
20 |
class Redmine::MenuManager::MapperTest < Test::Unit::TestCase |
|
21 |
context "Mapper#initialize" do |
|
22 |
should "be tested" |
|
23 |
end |
|
24 | ||
25 |
def test_push_onto_root |
|
26 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
27 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
28 | ||
29 |
menu_mapper.exists?(:test_overview) |
|
30 |
end |
|
31 | ||
32 |
def test_push_onto_parent |
|
33 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
34 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
35 |
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview} |
|
36 | ||
37 |
assert menu_mapper.exists?(:test_child) |
|
38 |
assert_equal :test_child, menu_mapper.find(:test_child).name |
|
39 |
end |
|
40 | ||
41 |
def test_push_onto_grandparent |
|
42 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
43 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
44 |
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview} |
|
45 |
menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_child} |
|
46 | ||
47 |
assert menu_mapper.exists?(:test_grandchild) |
|
48 |
grandchild = menu_mapper.find(:test_grandchild) |
|
49 |
assert_equal :test_grandchild, grandchild.name |
|
50 |
assert_equal :test_child, grandchild.parent_menu |
|
51 |
end |
|
52 | ||
53 |
def test_push_first |
|
54 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
55 |
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {} |
|
56 |
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {} |
|
57 |
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {} |
|
58 |
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {} |
|
59 |
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true} |
|
60 | ||
61 |
root = menu_mapper.find(:root) |
|
62 |
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| |
|
64 |
assert_not_nil root.children[position] |
|
65 |
assert_equal name, root.children[position].name |
|
66 |
end |
|
67 |
|
|
68 |
end |
|
69 |
|
|
70 |
def test_push_before |
|
71 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
72 |
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {} |
|
73 |
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {} |
|
74 |
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {} |
|
75 |
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {} |
|
76 |
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth} |
|
77 | ||
78 |
root = menu_mapper.find(:root) |
|
79 |
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| |
|
81 |
assert_not_nil root.children[position] |
|
82 |
assert_equal name, root.children[position].name |
|
83 |
end |
|
84 |
|
|
85 |
end |
|
86 | ||
87 |
def test_push_after |
|
88 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
89 |
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {} |
|
90 |
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {} |
|
91 |
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {} |
|
92 |
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {} |
|
93 |
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third} |
|
94 | ||
95 |
|
|
96 |
root = menu_mapper.find(:root) |
|
97 |
assert_equal 5, root.children.size |
|
98 |
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name| |
|
99 |
assert_not_nil root.children[position] |
|
100 |
assert_equal name, root.children[position].name |
|
101 |
end |
|
102 |
|
|
103 |
end |
|
104 | ||
105 |
def test_push_last |
|
106 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
107 |
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {} |
|
108 |
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {} |
|
109 |
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {} |
|
110 |
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true} |
|
111 |
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {} |
|
112 | ||
113 |
root = menu_mapper.find(:root) |
|
114 |
assert_equal 5, root.children.size |
|
115 |
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name| |
|
116 |
assert_not_nil root.children[position] |
|
117 |
assert_equal name, root.children[position].name |
|
118 |
end |
|
119 |
|
|
120 |
end |
|
121 |
|
|
122 |
def test_exists_for_child_node |
|
123 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
124 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
125 |
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview } |
|
126 | ||
127 |
assert menu_mapper.exists?(:test_child) |
|
128 |
end |
|
129 | ||
130 |
def test_exists_for_invalid_node |
|
131 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
132 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
133 | ||
134 |
assert !menu_mapper.exists?(:nothing) |
|
135 |
end |
|
136 | ||
137 |
def test_find |
|
138 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
139 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
140 | ||
141 |
item = menu_mapper.find(:test_overview) |
|
142 |
assert_equal :test_overview, item.name |
|
143 |
assert_equal({:controller => 'projects', :action => 'show'}, item.url) |
|
144 |
end |
|
145 | ||
146 |
def test_find_missing |
|
147 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
148 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
149 | ||
150 |
item = menu_mapper.find(:nothing) |
|
151 |
assert_equal nil, item |
|
152 |
end |
|
153 | ||
154 |
def test_delete |
|
155 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
156 |
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {} |
|
157 |
assert_not_nil menu_mapper.delete(:test_overview) |
|
158 | ||
159 |
assert_nil menu_mapper.find(:test_overview) |
|
160 |
end |
|
161 | ||
162 |
def test_delete_missing |
|
163 |
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {}) |
|
164 |
assert_nil menu_mapper.delete(:test_missing) |
|
165 |
end |
|
166 |
end |
test/unit/lib/redmine/menu_manager/menu_helper_test.rb | ||
---|---|---|
1 |
# Redmine - project management software |
|
2 |
# Copyright (C) 2006-2009 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
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 |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | ||
18 |
require File.dirname(__FILE__) + '/../../../../test_helper' |
|
19 | ||
20 | ||
21 | ||
22 |
class Redmine::MenuManager::MenuHelperTest < HelperTestCase |
|
23 |
include Redmine::MenuManager::MenuHelper |
|
24 |
include ActionController::Assertions::SelectorAssertions |
|
25 |
fixtures :users, :members, :projects, :enabled_modules |
|
26 | ||
27 |
# Used by assert_select |
|
28 |
def html_document |
|
29 |
HTML::Document.new(@response.body) |
|
30 |
end |
|
31 |
|
|
32 |
def setup |
|
33 |
super |
|
34 |
@response = ActionController::TestResponse.new |
|
35 |
# Stub the current menu item in the controller |
|
36 |
def @controller.current_menu_item |
|
37 |
:index |
|
38 |
end |
|
39 |
end |
|
40 |
|
|
41 | ||
42 |
context "MenuManager#current_menu_item" do |
|
43 |
should "be tested" |
|
44 |
end |
|
45 | ||
46 |
context "MenuManager#render_main_menu" do |
|
47 |
should "be tested" |
|
48 |
end |
|
49 | ||
50 |
context "MenuManager#render_menu" do |
|
51 |
should "be tested" |
|
52 |
end |
|
53 | ||
54 |
context "MenuManager#menu_item_and_children" do |
|
55 |
should "be tested" |
|
56 |
end |
|
57 | ||
58 |
context "MenuManager#extract_node_details" do |
|
59 |
should "be tested" |
|
60 |
end |
|
61 | ||
62 |
def test_render_single_menu_node |
|
63 |
node = Redmine::MenuManager::MenuItem.new(:testing, '/test', { }) |
|
64 |
@response.body = render_single_menu_node(node, 'This is a test', node.url, false) |
|
65 | ||
66 |
assert_select("a.testing", "This is a test") |
|
67 |
end |
|
68 | ||
69 |
def test_render_menu_node |
|
70 |
single_node = Redmine::MenuManager::MenuItem.new(:single_node, '/test', { }) |
|
71 |
@response.body = render_menu_node(single_node, nil) |
|
72 | ||
73 |
assert_select("li") do |
|
74 |
assert_select("a.single-node", "Single node") |
|
75 |
end |
|
76 |
end |
|
77 |
|
|
78 |
def test_render_menu_node_with_nested_items |
|
79 |
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, '/test', { }) |
|
80 |
parent_node << Redmine::MenuManager::MenuItem.new(:child_one_node, '/test', { }) |
|
81 |
parent_node << Redmine::MenuManager::MenuItem.new(:child_two_node, '/test', { }) |
|
82 |
parent_node << |
|
83 |
Redmine::MenuManager::MenuItem.new(:child_three_node, '/test', { }) << |
|
84 |
Redmine::MenuManager::MenuItem.new(:child_three_inner_node, '/test', { }) |
|
85 | ||
86 |
@response.body = render_menu_node(parent_node, nil) |
|
87 | ||
88 |
assert_select("li") do |
|
89 |
assert_select("a.parent-node", "Parent node") |
|
90 |
assert_select("ul") do |
|
91 |
assert_select("li a.child-one-node", "Child one node") |
|
92 |
assert_select("li a.child-two-node", "Child two node") |
|
93 |
assert_select("li") do |
|
94 |
assert_select("a.child-three-node", "Child three node") |
|
95 |
assert_select("ul") do |
|
96 |
assert_select("li a.child-three-inner-node", "Child three inner node") |
|
97 |
end |
|
98 |
end |
|
99 |
end |
|
100 |
end |
|
101 |
|
|
102 |
end |
|
103 | ||
104 |
def test_menu_items_for_should_yield_all_items_if_passed_a_block |
|
105 |
menu_name = :test_menu_items_for_should_yield_all_items_if_passed_a_block |
|
106 |
Redmine::MenuManager.map menu_name do |menu| |
|
107 |
menu.push(:a_menu, '/', { }) |
|
108 |
menu.push(:a_menu_2, '/', { }) |
|
109 |
menu.push(:a_menu_3, '/', { }) |
|
110 |
end |
|
111 | ||
112 |
items_yielded = [] |
|
113 |
menu_items_for(menu_name) do |item| |
|
114 |
items_yielded << item |
|
115 |
end |
|
116 |
|
|
117 |
assert_equal 3, items_yielded.size |
|
118 |
end |
|
119 | ||
120 |
def test_menu_items_for_should_return_all_items |
|
121 |
menu_name = :test_menu_items_for_should_return_all_items |
|
122 |
Redmine::MenuManager.map menu_name do |menu| |
|
123 |
menu.push(:a_menu, '/', { }) |
|
124 |
menu.push(:a_menu_2, '/', { }) |
|
125 |
menu.push(:a_menu_3, '/', { }) |
|
126 |
end |
|
127 | ||
128 |
items = menu_items_for(menu_name) |
|
129 |
assert_equal 3, items.size |
|
130 |
end |
|
131 | ||
132 |
def test_menu_items_for_should_skip_unallowed_items_on_a_project |
|
133 |
menu_name = :test_menu_items_for_should_skip_unallowed_items_on_a_project |
|
134 |
Redmine::MenuManager.map menu_name do |menu| |
|
135 |
menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { }) |
|
136 |
menu.push(:a_menu_2, {:controller => 'issues', :action => 'index' }, { }) |
|
137 |
menu.push(:unallowed, {:controller => 'issues', :action => 'unallowed' }, { }) |
|
138 |
end |
|
139 | ||
140 |
User.current = User.find(2) |
|
141 |
|
|
142 |
items = menu_items_for(menu_name, Project.find(1)) |
|
143 |
assert_equal 2, items.size |
|
144 |
end |
|
145 |
|
|
146 |
def test_menu_items_for_should_skip_items_that_fail_the_conditions |
|
147 |
menu_name = :test_menu_items_for_should_skip_items_that_fail_the_conditions |
|
148 |
Redmine::MenuManager.map menu_name do |menu| |
|
149 |
menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { }) |
|
150 |
menu.push(:unallowed, |
|
151 |
{:controller => 'issues', :action => 'index' }, |
|
152 |
{ :if => Proc.new { false }}) |
|
153 |
end |
|
154 | ||
155 |
User.current = User.find(2) |
|
156 |
|
|
157 |
items = menu_items_for(menu_name, Project.find(1)) |
|
158 |
assert_equal 1, items.size |
|
159 |
end |
|
160 | ||
161 |
end |
test/unit/lib/redmine/menu_manager/menu_item_test.rb | ||
---|---|---|
1 |
# Redmine - project management software |
|
2 |
# Copyright (C) 2006-2009 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
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 |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | ||
18 |
require File.dirname(__FILE__) + '/../../../../test_helper' |
|
19 | ||
20 |
module RedmineMenuTestHelper |
|
21 |
# Helpers |
|
22 |
def get_menu_item(menu_name, item_name) |
|
23 |
Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym} |
|
24 |
end |
|
25 |
end |
|
26 | ||
27 |
class Redmine::MenuManager::MenuItemTest < Test::Unit::TestCase |
|
28 |
include RedmineMenuTestHelper |
|
29 | ||
30 |
Redmine::MenuManager.map :test_menu do |menu| |
|
31 |
menu.push(:parent_menu, '/test', { }) |
|
32 |
menu.push(:child_menu, '/test', { :parent_menu => :parent_menu}) |
|
33 |
menu.push(:child2_menu, '/test', { :parent_menu => :parent_menu}) |
|
34 |
end |
|
35 |
|
|
36 |
context "MenuItem#caption" do |
|
37 |
should "be tested" |
|
38 |
end |
|
39 | ||
40 |
context "MenuItem#html_options" do |
|
41 |
should "be tested" |
|
42 |
end |
|
43 | ||
44 |
# context new menu item |
|
45 |
def test_new_menu_item_should_require_a_name |
|
46 |
assert_raises ArgumentError do |
|
47 |
Redmine::MenuManager::MenuItem.new |
|
48 |
end |
|
49 |
end |
|
50 | ||
51 |
def test_new_menu_item_should_require_an_url |
|
52 |
assert_raises ArgumentError do |
|
53 |
Redmine::MenuManager::MenuItem.new(:test_missing_url) |
|
54 |
end |
|
55 |
end |
|
56 | ||
57 |
def test_new_menu_item_should_require_the_options |
|
58 |
assert_raises ArgumentError do |
|
59 |
Redmine::MenuManager::MenuItem.new(:test_missing_options, '/test') |
|
60 |
end |
|
61 |
end |
|
62 | ||
63 |
def test_new_menu_item_with_all_required_parameters |
|
64 |
assert Redmine::MenuManager::MenuItem.new(:test_good_menu, '/test', {}) |
|
65 |
end |
|
66 | ||
67 |
def test_new_menu_item_should_require_a_proc_to_use_for_the_if_condition |
|
68 |
assert_raises ArgumentError do |
|
69 |
Redmine::MenuManager::MenuItem.new(:test_error, '/test', |
|
70 |
{ |
|
71 |
:if => ['not_a_proc'] |
|
72 |
}) |
|
73 |
end |
|
74 | ||
75 |
assert Redmine::MenuManager::MenuItem.new(:test_good_if, '/test', |
|
76 |
{ |
|
77 |
:if => Proc.new{} |
|
78 |
}) |
|
79 |
end |
|
80 | ||
81 |
def test_new_menu_item_should_allow_a_hash_for_extra_html_options |
|
82 |
assert_raises ArgumentError do |
|
83 |
Redmine::MenuManager::MenuItem.new(:test_error, '/test', |
|
84 |
{ |
|
85 |
:html => ['not_a_hash'] |
|
86 |
}) |
|
87 |
end |
|
88 | ||
89 |
assert Redmine::MenuManager::MenuItem.new(:test_good_html, '/test', |
|
90 |
{ |
|
91 |
:html => { :onclick => 'doSomething'} |
|
92 |
}) |
|
93 |
end |
|
94 | ||
95 |
def test_new_should_not_allow_setting_the_parent_menu_item_to_the_current_item |
|
96 |
assert_raises ArgumentError do |
|
97 |
Redmine::MenuManager::MenuItem.new(:test_error, '/test', { :parent_menu => :test_error }) |
|
98 |
end |
|
99 |
end |
|
100 | ||
101 |
def test_has_children |
|
102 |
parent_item = get_menu_item(:test_menu, :parent_menu) |
|
103 |
assert parent_item.hasChildren? |
|
104 |
assert_equal 2, parent_item.children.size |
|
105 |
assert_equal get_menu_item(:test_menu, :child_menu), parent_item.children[0] |
|
106 |
assert_equal get_menu_item(:test_menu, :child2_menu), parent_item.children[1] |
|
107 |
end |
|
108 |
end |
test/unit/lib/redmine/menu_manager_test.rb | ||
---|---|---|
1 |
# Redmine - project management software |
|
2 |
# Copyright (C) 2006-2009 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
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 |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | ||
18 |
require File.dirname(__FILE__) + '/../../../test_helper' |
|
19 | ||
20 |
class Redmine::MenuManagerTest < Test::Unit::TestCase |
|
21 |
context "MenuManager#map" do |
|
22 |
should "be tested" |
|
23 |
end |
|
24 | ||
25 |
context "MenuManager#items" do |
|
26 |
should "be tested" |
|
27 |
end |
|
28 |
end |
test/unit/lib/redmine_test.rb | ||
---|---|---|
1 |
# Redmine - project management software |
|
2 |
# Copyright (C) 2006-2009 Jean-Philippe Lang |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or |
|
5 |
# modify it under the terms of the GNU General Public License |
|
6 |
# as published by the Free Software Foundation; either version 2 |
|
7 |
# of the License, or (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
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 |
|
16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | ||
18 |
require File.dirname(__FILE__) + '/../../test_helper' |
|
19 | ||
20 |
module RedmineMenuTestHelper |
|
21 |
# Assertions |
|
22 |
def assert_number_of_items_in_menu(menu_name, count) |
|
23 |
assert Redmine::MenuManager.items(menu_name).size >= count, "Menu has less than #{count} items" |
|
24 |
end |
|
25 | ||
26 |
def assert_menu_contains_item_named(menu_name, item_name) |
|
27 |
assert Redmine::MenuManager.items(menu_name).collect(&:name).include?(item_name.to_sym), "Menu did not have an item named #{item_name}" |
|
28 |
end |
|
29 | ||
30 |
# Helpers |
|
31 |
def get_menu_item(menu_name, item_name) |
|
32 |
Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym} |
|
33 |
end |
|
34 |
end |
|
35 | ||
36 |
class RedmineTest < Test::Unit::TestCase |
|
37 |
include RedmineMenuTestHelper |
|
38 | ||
39 |
def test_top_menu |
|
40 |
assert_number_of_items_in_menu :top_menu, 5 |
|
41 |
assert_menu_contains_item_named :top_menu, :home |
|
42 |
assert_menu_contains_item_named :top_menu, :my_page |
|
43 |
assert_menu_contains_item_named :top_menu, :projects |
|
44 |
assert_menu_contains_item_named :top_menu, :administration |
|
45 |
assert_menu_contains_item_named :top_menu, :help |
|
46 |
end |
|
47 | ||
48 |
def test_account_menu |
|
49 |
assert_number_of_items_in_menu :account_menu, 4 |
|
50 |
assert_menu_contains_item_named :account_menu, :login |
|
51 |
assert_menu_contains_item_named :account_menu, :register |
|
52 |
assert_menu_contains_item_named :account_menu, :my_account |
|
53 |
assert_menu_contains_item_named :account_menu, :logout |
|
54 |
end |
|
55 | ||
56 |
def test_application_menu |
|
57 |
assert_number_of_items_in_menu :application_menu, 0 |
|
58 |
end |
|
59 | ||
60 |
def test_admin_menu |
|
61 |
assert_number_of_items_in_menu :admin_menu, 0 |
|
62 |
end |
|
63 | ||
64 |
def test_project_menu |
|
65 |
assert_number_of_items_in_menu :project_menu, 12 |
|
66 |
assert_menu_contains_item_named :project_menu, :overview |
|
67 |
assert_menu_contains_item_named :project_menu, :activity |
|
68 |
assert_menu_contains_item_named :project_menu, :roadmap |
|
69 |
assert_menu_contains_item_named :project_menu, :issues |
|
70 |
assert_menu_contains_item_named :project_menu, :new_issue |
|
71 |
assert_menu_contains_item_named :project_menu, :news |
|
72 |
assert_menu_contains_item_named :project_menu, :documents |
|
73 |
assert_menu_contains_item_named :project_menu, :wiki |
|
74 |
assert_menu_contains_item_named :project_menu, :boards |
|
75 |
assert_menu_contains_item_named :project_menu, :files |
|
76 |
assert_menu_contains_item_named :project_menu, :repository |
|
77 |
assert_menu_contains_item_named :project_menu, :settings |
|
78 |
end |
|
79 | ||
80 |
def test_new_issue_should_have_root_as_a_parent |
|
81 |
new_issue = get_menu_item(:project_menu, :new_issue) |
|
82 |
assert_equal :root, new_issue.parent.name |
|
83 |
end |
|
84 |
end |