Mercurial > hg > soundsoftware-site
comparison lib/redmine/menu_manager.rb @ 1115:433d4f72a19b redmine-2.2
Update to Redmine SVN revision 11137 on 2.2-stable branch
author | Chris Cannam |
---|---|
date | Mon, 07 Jan 2013 12:01:42 +0000 |
parents | cbb26bc654de |
children | 622f24f53b42 261b3d9a4903 |
comparison
equal
deleted
inserted
replaced
929:5f33065ddc4b | 1115:433d4f72a19b |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 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. |
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 '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 @childrenHash ||= {} | |
30 @last_items_count = 0 | |
31 extend(InstanceMethods) | |
32 end | |
33 end | |
34 end | |
35 | |
36 module InstanceMethods | |
37 # Adds the specified child node to the receiver node. The child node's | |
38 # parent is set to be the receiver. The child is added as the first child in | |
39 # the current list of children for the receiver node. | |
40 def prepend(child) | |
41 raise "Child already added" if @childrenHash.has_key?(child.name) | |
42 | |
43 @childrenHash[child.name] = child | |
44 @children = [child] + @children | |
45 child.parent = self | |
46 return child | |
47 | |
48 end | |
49 | |
50 # Adds the specified child node to the receiver node. The child node's | |
51 # parent is set to be the receiver. The child is added at the position | |
52 # into the current list of children for the receiver node. | |
53 def add_at(child, position) | |
54 raise "Child already added" if @childrenHash.has_key?(child.name) | |
55 | |
56 @childrenHash[child.name] = child | |
57 @children = @children.insert(position, child) | |
58 child.parent = self | |
59 return child | |
60 | |
61 end | |
62 | |
63 def add_last(child) | |
64 raise "Child already added" if @childrenHash.has_key?(child.name) | |
65 | |
66 @childrenHash[child.name] = child | |
67 @children << child | |
68 @last_items_count += 1 | |
69 child.parent = self | |
70 return child | |
71 | |
72 end | |
73 | |
74 # Adds the specified child node to the receiver node. The child node's | |
75 # parent is set to be the receiver. The child is added as the last child in | |
76 # the current list of children for the receiver node. | |
77 def add(child) | |
78 raise "Child already added" if @childrenHash.has_key?(child.name) | |
79 | |
80 @childrenHash[child.name] = child | |
81 position = @children.size - @last_items_count | |
82 @children.insert(position, child) | |
83 child.parent = self | |
84 return child | |
85 | |
86 end | |
87 | |
88 # Wrapp remove! making sure to decrement the last_items counter if | |
89 # the removed child was a last item | |
90 def remove!(child) | |
91 @last_items_count -= +1 if child && child.last | |
92 super | |
93 end | |
94 | |
95 | |
96 # Will return the position (zero-based) of the current child in | |
97 # it's parent | |
98 def position | |
99 self.parent.children.index(self) | |
100 end | |
101 end | |
102 end | |
103 Tree::TreeNode.send(:include, TreeNodePatch) | |
104 | |
105 module Redmine | 18 module Redmine |
106 module MenuManager | 19 module MenuManager |
107 class MenuError < StandardError #:nodoc: | 20 class MenuError < StandardError #:nodoc: |
108 end | 21 end |
109 | 22 |
167 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project) | 80 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project) |
168 end | 81 end |
169 | 82 |
170 def display_main_menu?(project) | 83 def display_main_menu?(project) |
171 menu_name = project && !project.new_record? ? :project_menu : :application_menu | 84 menu_name = project && !project.new_record? ? :project_menu : :application_menu |
172 Redmine::MenuManager.items(menu_name).size > 1 # 1 element is the root | 85 Redmine::MenuManager.items(menu_name).children.present? |
173 end | 86 end |
174 | 87 |
175 def render_menu(menu, project=nil) | 88 def render_menu(menu, project=nil) |
176 links = [] | 89 links = [] |
177 menu_items_for(menu, project) do |node| | 90 menu_items_for(menu, project) do |node| |
179 end | 92 end |
180 links.empty? ? nil : content_tag('ul', links.join("\n").html_safe) | 93 links.empty? ? nil : content_tag('ul', links.join("\n").html_safe) |
181 end | 94 end |
182 | 95 |
183 def render_menu_node(node, project=nil) | 96 def render_menu_node(node, project=nil) |
184 if node.hasChildren? || !node.child_menus.nil? | 97 if node.children.present? || !node.child_menus.nil? |
185 return render_menu_node_with_children(node, project) | 98 return render_menu_node_with_children(node, project) |
186 else | 99 else |
187 caption, url, selected = extract_node_details(node, project) | 100 caption, url, selected = extract_node_details(node, project) |
188 return content_tag('li', | 101 return content_tag('li', |
189 render_single_menu_node(node, caption, url, selected)) | 102 render_single_menu_node(node, caption, url, selected)) |
197 html << '<li>' | 110 html << '<li>' |
198 # Parent | 111 # Parent |
199 html << render_single_menu_node(node, caption, url, selected) | 112 html << render_single_menu_node(node, caption, url, selected) |
200 | 113 |
201 # Standard children | 114 # Standard children |
202 standard_children_list = "".tap do |child_html| | 115 standard_children_list = "".html_safe.tap do |child_html| |
203 node.children.each do |child| | 116 node.children.each do |child| |
204 child_html << render_menu_node(child, project) | 117 child_html << render_menu_node(child, project) |
205 end | 118 end |
206 end | 119 end |
207 | 120 |
211 unattached_children_list = render_unattached_children_menu(node, project) | 124 unattached_children_list = render_unattached_children_menu(node, project) |
212 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? |
213 | 126 |
214 html << '</li>' | 127 html << '</li>' |
215 end | 128 end |
216 return html.join("\n") | 129 return html.join("\n").html_safe |
217 end | 130 end |
218 | 131 |
219 # Returns a list of unattached children menu items | 132 # Returns a list of unattached children menu items |
220 def render_unattached_children_menu(node, project) | 133 def render_unattached_children_menu(node, project) |
221 return nil unless node.child_menus | 134 return nil unless node.child_menus |
222 | 135 |
223 "".tap do |child_html| | 136 "".html_safe.tap do |child_html| |
224 unattached_children = node.child_menus.call(project) | 137 unattached_children = node.child_menus.call(project) |
225 # Tree nodes support #each so we need to do object detection | 138 # Tree nodes support #each so we need to do object detection |
226 if unattached_children.is_a? Array | 139 if unattached_children.is_a? Array |
227 unattached_children.each do |child| | 140 unattached_children.each do |child| |
228 child_html << content_tag(:li, render_unattached_menu_item(child, project)) | 141 child_html << content_tag(:li, render_unattached_menu_item(child, project)) |
304 mapper | 217 mapper |
305 end | 218 end |
306 end | 219 end |
307 | 220 |
308 def items(menu_name) | 221 def items(menu_name) |
309 @items[menu_name.to_sym] || Tree::TreeNode.new(:root, {}) | 222 @items[menu_name.to_sym] || MenuNode.new(:root, {}) |
310 end | 223 end |
311 end | 224 end |
312 | 225 |
313 class Mapper | 226 class Mapper |
314 def initialize(menu, items) | 227 def initialize(menu, items) |
315 items[menu] ||= Tree::TreeNode.new(:root, {}) | 228 items[menu] ||= MenuNode.new(:root, {}) |
316 @menu = menu | 229 @menu = menu |
317 @menu_items = items[menu] | 230 @menu_items = items[menu] |
318 end | 231 end |
319 | |
320 @@last_items_count = Hash.new {|h,k| h[k] = 0} | |
321 | 232 |
322 # Adds an item at the end of the menu. Available options: | 233 # Adds an item at the end of the menu. Available options: |
323 # * param: the parameter name that is used for the project id (default is :id) | 234 # * param: the parameter name that is used for the project id (default is :id) |
324 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true | 235 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true |
325 # * caption that can be: | 236 # * caption that can be: |
396 end | 307 end |
397 end | 308 end |
398 end | 309 end |
399 end | 310 end |
400 | 311 |
401 class MenuItem < Tree::TreeNode | 312 class MenuNode |
313 include Enumerable | |
314 attr_accessor :parent | |
315 attr_reader :last_items_count, :name | |
316 | |
317 def initialize(name, content = nil) | |
318 @name = name | |
319 @children = [] | |
320 @last_items_count = 0 | |
321 end | |
322 | |
323 def children | |
324 if block_given? | |
325 @children.each {|child| yield child} | |
326 else | |
327 @children | |
328 end | |
329 end | |
330 | |
331 # Returns the number of descendants + 1 | |
332 def size | |
333 @children.inject(1) {|sum, node| sum + node.size} | |
334 end | |
335 | |
336 def each &block | |
337 yield self | |
338 children { |child| child.each(&block) } | |
339 end | |
340 | |
341 # Adds a child at first position | |
342 def prepend(child) | |
343 add_at(child, 0) | |
344 end | |
345 | |
346 # Adds a child at given position | |
347 def add_at(child, position) | |
348 raise "Child already added" if find {|node| node.name == child.name} | |
349 | |
350 @children = @children.insert(position, child) | |
351 child.parent = self | |
352 child | |
353 end | |
354 | |
355 # Adds a child as last child | |
356 def add_last(child) | |
357 add_at(child, -1) | |
358 @last_items_count += 1 | |
359 child | |
360 end | |
361 | |
362 # Adds a child | |
363 def add(child) | |
364 position = @children.size - @last_items_count | |
365 add_at(child, position) | |
366 end | |
367 alias :<< :add | |
368 | |
369 # Removes a child | |
370 def remove!(child) | |
371 @children.delete(child) | |
372 @last_items_count -= +1 if child && child.last | |
373 child.parent = nil | |
374 child | |
375 end | |
376 | |
377 # Returns the position for this node in it's parent | |
378 def position | |
379 self.parent.children.index(self) | |
380 end | |
381 | |
382 # Returns the root for this node | |
383 def root | |
384 root = self | |
385 root = root.parent while root.parent | |
386 root | |
387 end | |
388 end | |
389 | |
390 class MenuItem < MenuNode | |
402 include Redmine::I18n | 391 include Redmine::I18n |
403 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last | 392 attr_reader :name, :url, :param, :condition, :parent, :child_menus, :last |
404 | 393 |
405 def initialize(name, url, options) | 394 def initialize(name, url, options) |
406 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call) | 395 raise ArgumentError, "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call) |