Patch #4250 » 0002-Add-support-for-unattached-menus-generated-dynamical.patch
| lib/redmine/menu_manager.rb | ||
|---|---|---|
| 95 | 95 | |
| 96 | 96 |
module Redmine |
| 97 | 97 |
module MenuManager |
| 98 |
class MenuError < StandardError #:nodoc: |
|
| 99 |
end |
|
| 100 |
|
|
| 98 | 101 |
module MenuController |
| 99 | 102 |
def self.included(base) |
| 100 | 103 |
base.extend(ClassMethods) |
| ... | ... | |
| 164 | 167 |
end |
| 165 | 168 | |
| 166 | 169 |
def render_menu_node(node, project=nil) |
| 170 |
if node.hasChildren? || !node.child_menus.nil? |
|
| 171 |
return render_menu_node_with_children(node, project) |
|
| 172 |
else |
|
| 173 |
caption, url, selected = extract_node_details(node, project) |
|
| 174 |
return content_tag('li',
|
|
| 175 |
render_single_menu_node(node, caption, url, selected)) |
|
| 176 |
end |
|
| 177 |
end |
|
| 178 | ||
| 179 |
def render_menu_node_with_children(node, project=nil) |
|
| 167 | 180 |
caption, url, selected = extract_node_details(node, project) |
| 168 |
if node.hasChildren? |
|
| 169 |
html = []
|
|
| 181 | ||
| 182 |
html = returning [] do |html|
|
|
| 170 | 183 |
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) |
|
| 184 |
# Parent |
|
| 185 |
html << render_single_menu_node(node, caption, url, selected) |
|
| 186 | ||
| 187 |
# Standard children |
|
| 188 |
standard_children_list = returning "" do |child_html| |
|
| 189 |
node.children.each do |child| |
|
| 190 |
child_html << render_menu_node(child, project) |
|
| 191 |
end |
|
| 175 | 192 |
end |
| 176 |
html << ' </ul>' |
|
| 193 | ||
| 194 |
html << content_tag(:ul, standard_children_list, :class => 'menu-children') unless standard_children_list.empty? |
|
| 195 | ||
| 196 |
# Unattached children |
|
| 197 |
unattached_children_list = render_unattached_children_menu(node, project) |
|
| 198 |
html << content_tag(:ul, unattached_children_list, :class => 'menu-children unattached') unless unattached_children_list.blank? |
|
| 199 | ||
| 177 | 200 |
html << '</li>' |
| 178 |
return html.join("\n")
|
|
| 179 |
else |
|
| 180 |
return content_tag('li',
|
|
| 181 |
render_single_menu_node(node, caption, url, selected)) |
|
| 201 |
end |
|
| 202 |
return html.join("\n")
|
|
| 203 |
end |
|
| 204 | ||
| 205 |
# Returns a list of unattached children menu items |
|
| 206 |
def render_unattached_children_menu(node, project) |
|
| 207 |
return nil unless node.child_menus |
|
| 208 | ||
| 209 |
returning "" do |child_html| |
|
| 210 |
unattached_children = node.child_menus.call(project) |
|
| 211 |
# Tree nodes support #each so we need to do object detection |
|
| 212 |
if unattached_children.is_a? Array |
|
| 213 |
unattached_children.each do |child| |
|
| 214 |
child_html << content_tag(:li, render_unattached_menu_item(child, project)) |
|
| 215 |
end |
|
| 216 |
else |
|
| 217 |
raise MenuError, ":child_menus must be an array of MenuItems" |
|
| 218 |
end |
|
| 182 | 219 |
end |
| 183 | 220 |
end |
| 184 | 221 | |
| 185 | 222 |
def render_single_menu_node(item, caption, url, selected) |
| 186 | 223 |
link_to(h(caption), url, item.html_options(:selected => selected)) |
| 187 | 224 |
end |
| 225 | ||
| 226 |
def render_unattached_menu_item(menu_item, project) |
|
| 227 |
raise MenuError, ":child_menus must be an array of MenuItems" unless menu_item.is_a? MenuItem |
|
| 228 | ||
| 229 |
if User.current.allowed_to?(menu_item.url, project) |
|
| 230 |
link_to(h(menu_item.caption), |
|
| 231 |
menu_item.url, |
|
| 232 |
menu_item.html_options) |
|
| 233 |
end |
|
| 234 |
end |
|
| 188 | 235 |
|
| 189 | 236 |
def menu_items_for(menu, project=nil) |
| 190 | 237 |
items = [] |
| ... | ... | |
| 336 | 383 |
|
| 337 | 384 |
class MenuItem < Tree::TreeNode |
| 338 | 385 |
include Redmine::I18n |
| 339 |
attr_reader :name, :url, :param, :condition, :parent_menu |
|
| 386 |
attr_reader :name, :url, :param, :condition, :parent_menu, :child_menus
|
|
| 340 | 387 |
|
| 341 | 388 |
def initialize(name, url, options) |
| 342 | 389 |
raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
|
| 343 | 390 |
raise ArgumentError, "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
|
| 344 | 391 |
raise ArgumentError, "Cannot set the :parent_menu to be the same as this item" if options[:parent_menu] == name.to_sym |
| 392 |
raise ArgumentError, "Invalid option :child_menus for menu item '#{name}'" if options[:child_menus] && !options[:child_menus].respond_to?(:call)
|
|
| 345 | 393 |
@name = name |
| 346 | 394 |
@url = url |
| 347 | 395 |
@condition = options[:if] |
| ... | ... | |
| 351 | 399 |
# Adds a unique class to each menu item based on its name |
| 352 | 400 |
@html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
|
| 353 | 401 |
@parent_menu = options[:parent_menu] |
| 402 |
@child_menus = options[:child_menus] |
|
| 354 | 403 |
super @name.to_sym |
| 355 | 404 |
end |
| 356 | 405 |
|
| test/unit/lib/redmine/menu_manager/menu_helper_test.rb | ||
|---|---|---|
| 101 | 101 |
|
| 102 | 102 |
end |
| 103 | 103 | |
| 104 |
def test_render_menu_node_with_child_menus |
|
| 105 |
User.current = User.find(2) |
|
| 106 |
|
|
| 107 |
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, |
|
| 108 |
'/test', |
|
| 109 |
{
|
|
| 110 |
:child_menus => Proc.new {|p|
|
|
| 111 |
child_menus = [] |
|
| 112 |
3.times do |time| |
|
| 113 |
child_menus << Redmine::MenuManager::MenuItem.new("test_child_#{time}",
|
|
| 114 |
{:controller => 'issues', :action => 'index'},
|
|
| 115 |
{})
|
|
| 116 |
end |
|
| 117 |
child_menus |
|
| 118 |
} |
|
| 119 |
}) |
|
| 120 |
@response.body = render_menu_node(parent_node, Project.find(1)) |
|
| 121 | ||
| 122 |
assert_select("li") do
|
|
| 123 |
assert_select("a.parent-node", "Parent node")
|
|
| 124 |
assert_select("ul") do
|
|
| 125 |
assert_select("li a.test-child-0", "Test child 0")
|
|
| 126 |
assert_select("li a.test-child-1", "Test child 1")
|
|
| 127 |
assert_select("li a.test-child-2", "Test child 2")
|
|
| 128 |
end |
|
| 129 |
end |
|
| 130 |
end |
|
| 131 | ||
| 132 |
def test_render_menu_node_with_nested_items_and_child_menus |
|
| 133 |
User.current = User.find(2) |
|
| 134 | ||
| 135 |
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, |
|
| 136 |
'/test', |
|
| 137 |
{
|
|
| 138 |
:child_menus => Proc.new {|p|
|
|
| 139 |
child_menus = [] |
|
| 140 |
3.times do |time| |
|
| 141 |
child_menus << Redmine::MenuManager::MenuItem.new("test_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
|
|
| 142 |
end |
|
| 143 |
child_menus |
|
| 144 |
} |
|
| 145 |
}) |
|
| 146 | ||
| 147 |
parent_node << Redmine::MenuManager::MenuItem.new(:child_node, |
|
| 148 |
'/test', |
|
| 149 |
{
|
|
| 150 |
:child_menus => Proc.new {|p|
|
|
| 151 |
child_menus = [] |
|
| 152 |
6.times do |time| |
|
| 153 |
child_menus << Redmine::MenuManager::MenuItem.new("test_dynamic_child_#{time}", {:controller => 'issues', :action => 'index'}, {})
|
|
| 154 |
end |
|
| 155 |
child_menus |
|
| 156 |
} |
|
| 157 |
}) |
|
| 158 | ||
| 159 |
@response.body = render_menu_node(parent_node, Project.find(1)) |
|
| 160 | ||
| 161 |
assert_select("li") do
|
|
| 162 |
assert_select("a.parent-node", "Parent node")
|
|
| 163 |
assert_select("ul") do
|
|
| 164 |
assert_select("li a.child-node", "Child node")
|
|
| 165 |
assert_select("ul") do
|
|
| 166 |
assert_select("li a.test-dynamic-child-0", "Test dynamic child 0")
|
|
| 167 |
assert_select("li a.test-dynamic-child-1", "Test dynamic child 1")
|
|
| 168 |
assert_select("li a.test-dynamic-child-2", "Test dynamic child 2")
|
|
| 169 |
assert_select("li a.test-dynamic-child-3", "Test dynamic child 3")
|
|
| 170 |
assert_select("li a.test-dynamic-child-4", "Test dynamic child 4")
|
|
| 171 |
assert_select("li a.test-dynamic-child-5", "Test dynamic child 5")
|
|
| 172 |
end |
|
| 173 |
assert_select("li a.test-child-0", "Test child 0")
|
|
| 174 |
assert_select("li a.test-child-1", "Test child 1")
|
|
| 175 |
assert_select("li a.test-child-2", "Test child 2")
|
|
| 176 |
end |
|
| 177 |
end |
|
| 178 |
end |
|
| 179 | ||
| 180 |
def test_render_menu_node_with_child_menus_without_an_array |
|
| 181 |
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, |
|
| 182 |
'/test', |
|
| 183 |
{
|
|
| 184 |
:child_menus => Proc.new {|p| Redmine::MenuManager::MenuItem.new("test_child", "/testing", {})}
|
|
| 185 |
}) |
|
| 186 | ||
| 187 |
assert_raises Redmine::MenuManager::MenuError, ":child_menus must be an array of MenuItems" do |
|
| 188 |
@response.body = render_menu_node(parent_node, Project.find(1)) |
|
| 189 |
end |
|
| 190 |
end |
|
| 191 |
|
|
| 192 |
def test_render_menu_node_with_incorrect_child_menus |
|
| 193 |
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, |
|
| 194 |
'/test', |
|
| 195 |
{
|
|
| 196 |
:child_menus => Proc.new {|p| ["a string"] }
|
|
| 197 |
}) |
|
| 198 | ||
| 199 |
assert_raises Redmine::MenuManager::MenuError, ":child_menus must be an array of MenuItems" do |
|
| 200 |
@response.body = render_menu_node(parent_node, Project.find(1)) |
|
| 201 |
end |
|
| 202 | ||
| 203 |
end |
|
| 204 | ||
| 104 | 205 |
def test_menu_items_for_should_yield_all_items_if_passed_a_block |
| 105 | 206 |
menu_name = :test_menu_items_for_should_yield_all_items_if_passed_a_block |
| 106 | 207 |
Redmine::MenuManager.map menu_name do |menu| |
| test/unit/lib/redmine/menu_manager/menu_item_test.rb | ||
|---|---|---|
| 92 | 92 |
}) |
| 93 | 93 |
end |
| 94 | 94 | |
| 95 |
def test_new_menu_item_should_require_a_proc_to_use_the_child_menus_option |
|
| 96 |
assert_raises ArgumentError do |
|
| 97 |
Redmine::MenuManager::MenuItem.new(:test_error, '/test', |
|
| 98 |
{
|
|
| 99 |
:child_menus => ['not_a_proc'] |
|
| 100 |
}) |
|
| 101 |
end |
|
| 102 | ||
| 103 |
assert Redmine::MenuManager::MenuItem.new(:test_good_child_menus, '/test', |
|
| 104 |
{
|
|
| 105 |
:child_menus => Proc.new{}
|
|
| 106 |
}) |
|
| 107 |
end |
|
| 108 | ||
| 95 | 109 |
def test_new_should_not_allow_setting_the_parent_menu_item_to_the_current_item |
| 96 | 110 |
assert_raises ArgumentError do |
| 97 | 111 |
Redmine::MenuManager::MenuItem.new(:test_error, '/test', { :parent_menu => :test_error })
|
| test/unit/lib/redmine/menu_manager_test.rb | ||
|---|---|---|
| 25 | 25 |
context "MenuManager#items" do |
| 26 | 26 |
should "be tested" |
| 27 | 27 |
end |
| 28 | ||
| 29 |
should "be tested" do |
|
| 30 |
assert true |
|
| 31 |
end |
|
| 28 | 32 |
end |