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 |
|