##// END OF EJS Templates
Prevent blank menu caption....
Jean-Philippe Lang -
r1648:937cee726905
parent child
Show More
@@ -1,178 +1,180
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'gloc'
18 require 'gloc'
19
19
20 module Redmine
20 module Redmine
21 module MenuManager
21 module MenuManager
22 module MenuController
22 module MenuController
23 def self.included(base)
23 def self.included(base)
24 base.extend(ClassMethods)
24 base.extend(ClassMethods)
25 end
25 end
26
26
27 module ClassMethods
27 module ClassMethods
28 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
28 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
29 mattr_accessor :menu_items
29 mattr_accessor :menu_items
30
30
31 # Set the menu item name for a controller or specific actions
31 # Set the menu item name for a controller or specific actions
32 # Examples:
32 # Examples:
33 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
33 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
34 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
34 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
35 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
35 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
36 #
36 #
37 # The default menu item name for a controller is controller_name by default
37 # The default menu item name for a controller is controller_name by default
38 # Eg. the default menu item name for ProjectsController is :projects
38 # Eg. the default menu item name for ProjectsController is :projects
39 def menu_item(id, options = {})
39 def menu_item(id, options = {})
40 if actions = options[:only]
40 if actions = options[:only]
41 actions = [] << actions unless actions.is_a?(Array)
41 actions = [] << actions unless actions.is_a?(Array)
42 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
42 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
43 else
43 else
44 menu_items[controller_name.to_sym][:default] = id
44 menu_items[controller_name.to_sym][:default] = id
45 end
45 end
46 end
46 end
47 end
47 end
48
48
49 def menu_items
49 def menu_items
50 self.class.menu_items
50 self.class.menu_items
51 end
51 end
52
52
53 # Returns the menu item name according to the current action
53 # Returns the menu item name according to the current action
54 def current_menu_item
54 def current_menu_item
55 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
55 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
56 menu_items[controller_name.to_sym][:default]
56 menu_items[controller_name.to_sym][:default]
57 end
57 end
58 end
58 end
59
59
60 module MenuHelper
60 module MenuHelper
61 # Returns the current menu item name
61 # Returns the current menu item name
62 def current_menu_item
62 def current_menu_item
63 @controller.current_menu_item
63 @controller.current_menu_item
64 end
64 end
65
65
66 # Renders the application main menu
66 # Renders the application main menu
67 def render_main_menu(project)
67 def render_main_menu(project)
68 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
68 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
69 end
69 end
70
70
71 def render_menu(menu, project=nil)
71 def render_menu(menu, project=nil)
72 links = []
72 links = []
73 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
73 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
74 unless item.condition && !item.condition.call(project)
74 unless item.condition && !item.condition.call(project)
75 url = case item.url
75 url = case item.url
76 when Hash
76 when Hash
77 project.nil? ? item.url : {item.param => project}.merge(item.url)
77 project.nil? ? item.url : {item.param => project}.merge(item.url)
78 when Symbol
78 when Symbol
79 send(item.url)
79 send(item.url)
80 else
80 else
81 item.url
81 item.url
82 end
82 end
83 caption = item.caption(project)
83 caption = item.caption(project)
84 caption = l(caption) if caption.is_a?(Symbol)
84 caption = l(caption) if caption.is_a?(Symbol)
85 links << content_tag('li',
85 links << content_tag('li',
86 link_to(h(caption), url, (current_menu_item == item.name ? item.html_options.merge(:class => 'selected') : item.html_options)))
86 link_to(h(caption), url, (current_menu_item == item.name ? item.html_options.merge(:class => 'selected') : item.html_options)))
87 end
87 end
88 end
88 end
89 links.empty? ? nil : content_tag('ul', links.join("\n"))
89 links.empty? ? nil : content_tag('ul', links.join("\n"))
90 end
90 end
91 end
91 end
92
92
93 class << self
93 class << self
94 def map(menu_name)
94 def map(menu_name)
95 @items ||= {}
95 @items ||= {}
96 mapper = Mapper.new(menu_name.to_sym, @items)
96 mapper = Mapper.new(menu_name.to_sym, @items)
97 yield mapper
97 yield mapper
98 end
98 end
99
99
100 def items(menu_name)
100 def items(menu_name)
101 @items[menu_name.to_sym] || []
101 @items[menu_name.to_sym] || []
102 end
102 end
103
103
104 def allowed_items(menu_name, user, project)
104 def allowed_items(menu_name, user, project)
105 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
105 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
106 end
106 end
107 end
107 end
108
108
109 class Mapper
109 class Mapper
110 def initialize(menu, items)
110 def initialize(menu, items)
111 items[menu] ||= []
111 items[menu] ||= []
112 @menu = menu
112 @menu = menu
113 @menu_items = items[menu]
113 @menu_items = items[menu]
114 end
114 end
115
115
116 @@last_items_count = Hash.new {|h,k| h[k] = 0}
116 @@last_items_count = Hash.new {|h,k| h[k] = 0}
117
117
118 # Adds an item at the end of the menu. Available options:
118 # Adds an item at the end of the menu. Available options:
119 # * param: the parameter name that is used for the project id (default is :id)
119 # * param: the parameter name that is used for the project id (default is :id)
120 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
120 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
121 # * caption that can be:
121 # * caption that can be:
122 # * a localized string Symbol
122 # * a localized string Symbol
123 # * a String
123 # * a String
124 # * a Proc that can take the project as argument
124 # * a Proc that can take the project as argument
125 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
125 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
126 # * last: menu item will stay at the end (eg. :last => true)
126 # * last: menu item will stay at the end (eg. :last => true)
127 # * html_options: a hash of html options that are passed to link_to
127 # * html_options: a hash of html options that are passed to link_to
128 def push(name, url, options={})
128 def push(name, url, options={})
129 options = options.dup
129 options = options.dup
130
130
131 # menu item position
131 # menu item position
132 if before = options.delete(:before)
132 if before = options.delete(:before)
133 position = @menu_items.index {|i| i.name == before}
133 position = @menu_items.index {|i| i.name == before}
134 elsif after = options.delete(:after)
134 elsif after = options.delete(:after)
135 position = @menu_items.index {|i| i.name == after}
135 position = @menu_items.index {|i| i.name == after}
136 position += 1 unless position.nil?
136 position += 1 unless position.nil?
137 elsif options.delete(:last)
137 elsif options.delete(:last)
138 position = @menu_items.size
138 position = @menu_items.size
139 @@last_items_count[@menu] += 1
139 @@last_items_count[@menu] += 1
140 end
140 end
141 # default position
141 # default position
142 position ||= @menu_items.size - @@last_items_count[@menu]
142 position ||= @menu_items.size - @@last_items_count[@menu]
143
143
144 @menu_items.insert(position, MenuItem.new(name, url, options))
144 @menu_items.insert(position, MenuItem.new(name, url, options))
145 end
145 end
146
146
147 # Removes a menu item
147 # Removes a menu item
148 def delete(name)
148 def delete(name)
149 @menu_items.delete_if {|i| i.name == name}
149 @menu_items.delete_if {|i| i.name == name}
150 end
150 end
151 end
151 end
152
152
153 class MenuItem
153 class MenuItem
154 include GLoc
154 include GLoc
155 attr_reader :name, :url, :param, :condition, :html_options
155 attr_reader :name, :url, :param, :condition, :html_options
156
156
157 def initialize(name, url, options)
157 def initialize(name, url, options)
158 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
158 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
159 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
159 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
160 @name = name
160 @name = name
161 @url = url
161 @url = url
162 @condition = options[:if]
162 @condition = options[:if]
163 @param = options[:param] || :id
163 @param = options[:param] || :id
164 @caption = options[:caption]
164 @caption = options[:caption]
165 @html_options = options[:html] || {}
165 @html_options = options[:html] || {}
166 end
166 end
167
167
168 def caption(project=nil)
168 def caption(project=nil)
169 if @caption.is_a?(Proc)
169 if @caption.is_a?(Proc)
170 @caption.call(project)
170 c = @caption.call(project).to_s
171 c = @name.to_s.humanize if c.blank?
172 c
171 else
173 else
172 # check if localized string exists on first render (after GLoc strings are loaded)
174 # check if localized string exists on first render (after GLoc strings are loaded)
173 @caption_key ||= (@caption || (l_has_string?("label_#{@name}".to_sym) ? "label_#{@name}".to_sym : @name.to_s.humanize))
175 @caption_key ||= (@caption || (l_has_string?("label_#{@name}".to_sym) ? "label_#{@name}".to_sym : @name.to_s.humanize))
174 end
176 end
175 end
177 end
176 end
178 end
177 end
179 end
178 end
180 end
General Comments 0
You need to be logged in to leave comments. Login now