##// END OF EJS Templates
Fixes "too few arguments" error on activerecord error translation (#2626)....
Jean-Philippe Lang -
r2424:b9e95e7a7085
parent child
Show More
@@ -1,721 +1,721
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 'coderay'
18 require 'coderay'
19 require 'coderay/helpers/file_type'
19 require 'coderay/helpers/file_type'
20 require 'forwardable'
20 require 'forwardable'
21 require 'cgi'
21 require 'cgi'
22
22
23 module ApplicationHelper
23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions
24 include Redmine::WikiFormatting::Macros::Definitions
25 include GravatarHelper::PublicMethods
25 include GravatarHelper::PublicMethods
26
26
27 extend Forwardable
27 extend Forwardable
28 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
28 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
29
29
30 def current_role
30 def current_role
31 @current_role ||= User.current.role_for_project(@project)
31 @current_role ||= User.current.role_for_project(@project)
32 end
32 end
33
33
34 # Return true if user is authorized for controller/action, otherwise false
34 # Return true if user is authorized for controller/action, otherwise false
35 def authorize_for(controller, action)
35 def authorize_for(controller, action)
36 User.current.allowed_to?({:controller => controller, :action => action}, @project)
36 User.current.allowed_to?({:controller => controller, :action => action}, @project)
37 end
37 end
38
38
39 # Display a link if user is authorized
39 # Display a link if user is authorized
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
42 end
42 end
43
43
44 # Display a link to remote if user is authorized
44 # Display a link to remote if user is authorized
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
46 url = options[:url] || {}
46 url = options[:url] || {}
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
48 end
48 end
49
49
50 # Display a link to user's account page
50 # Display a link to user's account page
51 def link_to_user(user, options={})
51 def link_to_user(user, options={})
52 (user && !user.anonymous?) ? link_to(user.name(options[:format]), :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
52 (user && !user.anonymous?) ? link_to(user.name(options[:format]), :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
53 end
53 end
54
54
55 def link_to_issue(issue, options={})
55 def link_to_issue(issue, options={})
56 options[:class] ||= ''
56 options[:class] ||= ''
57 options[:class] << ' issue'
57 options[:class] << ' issue'
58 options[:class] << ' closed' if issue.closed?
58 options[:class] << ' closed' if issue.closed?
59 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
59 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
60 end
60 end
61
61
62 # Generates a link to an attachment.
62 # Generates a link to an attachment.
63 # Options:
63 # Options:
64 # * :text - Link text (default to attachment filename)
64 # * :text - Link text (default to attachment filename)
65 # * :download - Force download (default: false)
65 # * :download - Force download (default: false)
66 def link_to_attachment(attachment, options={})
66 def link_to_attachment(attachment, options={})
67 text = options.delete(:text) || attachment.filename
67 text = options.delete(:text) || attachment.filename
68 action = options.delete(:download) ? 'download' : 'show'
68 action = options.delete(:download) ? 'download' : 'show'
69
69
70 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
70 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
71 end
71 end
72
72
73 def toggle_link(name, id, options={})
73 def toggle_link(name, id, options={})
74 onclick = "Element.toggle('#{id}'); "
74 onclick = "Element.toggle('#{id}'); "
75 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
75 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
76 onclick << "return false;"
76 onclick << "return false;"
77 link_to(name, "#", :onclick => onclick)
77 link_to(name, "#", :onclick => onclick)
78 end
78 end
79
79
80 def image_to_function(name, function, html_options = {})
80 def image_to_function(name, function, html_options = {})
81 html_options.symbolize_keys!
81 html_options.symbolize_keys!
82 tag(:input, html_options.merge({
82 tag(:input, html_options.merge({
83 :type => "image", :src => image_path(name),
83 :type => "image", :src => image_path(name),
84 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
84 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
85 }))
85 }))
86 end
86 end
87
87
88 def prompt_to_remote(name, text, param, url, html_options = {})
88 def prompt_to_remote(name, text, param, url, html_options = {})
89 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
89 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
90 link_to name, {}, html_options
90 link_to name, {}, html_options
91 end
91 end
92
92
93 def format_date(date)
93 def format_date(date)
94 return nil unless date
94 return nil unless date
95 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
95 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
96 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
96 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
97 date.strftime(@date_format)
97 date.strftime(@date_format)
98 end
98 end
99
99
100 def format_time(time, include_date = true)
100 def format_time(time, include_date = true)
101 return nil unless time
101 return nil unless time
102 time = time.to_time if time.is_a?(String)
102 time = time.to_time if time.is_a?(String)
103 zone = User.current.time_zone
103 zone = User.current.time_zone
104 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
104 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
105 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
105 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
106 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
106 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
107 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
107 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
108 end
108 end
109
109
110 def format_activity_title(text)
110 def format_activity_title(text)
111 h(truncate_single_line(text, 100))
111 h(truncate_single_line(text, 100))
112 end
112 end
113
113
114 def format_activity_day(date)
114 def format_activity_day(date)
115 date == Date.today ? l(:label_today).titleize : format_date(date)
115 date == Date.today ? l(:label_today).titleize : format_date(date)
116 end
116 end
117
117
118 def format_activity_description(text)
118 def format_activity_description(text)
119 h(truncate(text.to_s, 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
119 h(truncate(text.to_s, 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
120 end
120 end
121
121
122 def distance_of_date_in_words(from_date, to_date = 0)
122 def distance_of_date_in_words(from_date, to_date = 0)
123 from_date = from_date.to_date if from_date.respond_to?(:to_date)
123 from_date = from_date.to_date if from_date.respond_to?(:to_date)
124 to_date = to_date.to_date if to_date.respond_to?(:to_date)
124 to_date = to_date.to_date if to_date.respond_to?(:to_date)
125 distance_in_days = (to_date - from_date).abs
125 distance_in_days = (to_date - from_date).abs
126 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
126 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
127 end
127 end
128
128
129 def due_date_distance_in_words(date)
129 def due_date_distance_in_words(date)
130 if date
130 if date
131 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
131 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
132 end
132 end
133 end
133 end
134
134
135 def render_page_hierarchy(pages, node=nil)
135 def render_page_hierarchy(pages, node=nil)
136 content = ''
136 content = ''
137 if pages[node]
137 if pages[node]
138 content << "<ul class=\"pages-hierarchy\">\n"
138 content << "<ul class=\"pages-hierarchy\">\n"
139 pages[node].each do |page|
139 pages[node].each do |page|
140 content << "<li>"
140 content << "<li>"
141 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
141 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
142 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
142 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
143 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
143 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
144 content << "</li>\n"
144 content << "</li>\n"
145 end
145 end
146 content << "</ul>\n"
146 content << "</ul>\n"
147 end
147 end
148 content
148 content
149 end
149 end
150
150
151 # Renders flash messages
151 # Renders flash messages
152 def render_flash_messages
152 def render_flash_messages
153 s = ''
153 s = ''
154 flash.each do |k,v|
154 flash.each do |k,v|
155 s << content_tag('div', v, :class => "flash #{k}")
155 s << content_tag('div', v, :class => "flash #{k}")
156 end
156 end
157 s
157 s
158 end
158 end
159
159
160 # Renders the project quick-jump box
160 # Renders the project quick-jump box
161 def render_project_jump_box
161 def render_project_jump_box
162 # Retrieve them now to avoid a COUNT query
162 # Retrieve them now to avoid a COUNT query
163 projects = User.current.projects.all
163 projects = User.current.projects.all
164 if projects.any?
164 if projects.any?
165 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
165 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
166 "<option selected='selected'>#{ l(:label_jump_to_a_project) }</option>" +
166 "<option selected='selected'>#{ l(:label_jump_to_a_project) }</option>" +
167 '<option disabled="disabled">---</option>'
167 '<option disabled="disabled">---</option>'
168 s << project_tree_options_for_select(projects) do |p|
168 s << project_tree_options_for_select(projects) do |p|
169 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
169 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
170 end
170 end
171 s << '</select>'
171 s << '</select>'
172 s
172 s
173 end
173 end
174 end
174 end
175
175
176 def project_tree_options_for_select(projects, options = {})
176 def project_tree_options_for_select(projects, options = {})
177 s = ''
177 s = ''
178 project_tree(projects) do |project, level|
178 project_tree(projects) do |project, level|
179 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
179 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
180 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
180 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
181 tag_options.merge!(yield(project)) if block_given?
181 tag_options.merge!(yield(project)) if block_given?
182 s << content_tag('option', name_prefix + h(project), tag_options)
182 s << content_tag('option', name_prefix + h(project), tag_options)
183 end
183 end
184 s
184 s
185 end
185 end
186
186
187 # Yields the given block for each project with its level in the tree
187 # Yields the given block for each project with its level in the tree
188 def project_tree(projects, &block)
188 def project_tree(projects, &block)
189 ancestors = []
189 ancestors = []
190 projects.sort_by(&:lft).each do |project|
190 projects.sort_by(&:lft).each do |project|
191 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
191 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
192 ancestors.pop
192 ancestors.pop
193 end
193 end
194 yield project, ancestors.size
194 yield project, ancestors.size
195 ancestors << project
195 ancestors << project
196 end
196 end
197 end
197 end
198
198
199 def project_nested_ul(projects, &block)
199 def project_nested_ul(projects, &block)
200 s = ''
200 s = ''
201 if projects.any?
201 if projects.any?
202 ancestors = []
202 ancestors = []
203 projects.sort_by(&:lft).each do |project|
203 projects.sort_by(&:lft).each do |project|
204 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
204 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
205 s << "<ul>\n"
205 s << "<ul>\n"
206 else
206 else
207 ancestors.pop
207 ancestors.pop
208 s << "</li>"
208 s << "</li>"
209 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
209 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
210 ancestors.pop
210 ancestors.pop
211 s << "</ul></li>\n"
211 s << "</ul></li>\n"
212 end
212 end
213 end
213 end
214 s << "<li>"
214 s << "<li>"
215 s << yield(project).to_s
215 s << yield(project).to_s
216 ancestors << project
216 ancestors << project
217 end
217 end
218 s << ("</li></ul>\n" * ancestors.size)
218 s << ("</li></ul>\n" * ancestors.size)
219 end
219 end
220 s
220 s
221 end
221 end
222
222
223 # Truncates and returns the string as a single line
223 # Truncates and returns the string as a single line
224 def truncate_single_line(string, *args)
224 def truncate_single_line(string, *args)
225 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
225 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
226 end
226 end
227
227
228 def html_hours(text)
228 def html_hours(text)
229 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
229 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
230 end
230 end
231
231
232 def authoring(created, author, options={})
232 def authoring(created, author, options={})
233 time_tag = @project.nil? ? content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) :
233 time_tag = @project.nil? ? content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created)) :
234 link_to(distance_of_time_in_words(Time.now, created),
234 link_to(distance_of_time_in_words(Time.now, created),
235 {:controller => 'projects', :action => 'activity', :id => @project, :from => created.to_date},
235 {:controller => 'projects', :action => 'activity', :id => @project, :from => created.to_date},
236 :title => format_time(created))
236 :title => format_time(created))
237 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
237 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
238 l(options[:label] || :label_added_time_by, author_tag, time_tag)
238 l(options[:label] || :label_added_time_by, author_tag, time_tag)
239 end
239 end
240
240
241 def l_or_humanize(s, options={})
241 def l_or_humanize(s, options={})
242 k = "#{options[:prefix]}#{s}".to_sym
242 k = "#{options[:prefix]}#{s}".to_sym
243 l_has_string?(k) ? l(k) : s.to_s.humanize
243 l_has_string?(k) ? l(k) : s.to_s.humanize
244 end
244 end
245
245
246 def day_name(day)
246 def day_name(day)
247 l(:general_day_names).split(',')[day-1]
247 l(:general_day_names).split(',')[day-1]
248 end
248 end
249
249
250 def month_name(month)
250 def month_name(month)
251 l(:actionview_datehelper_select_month_names).split(',')[month-1]
251 l(:actionview_datehelper_select_month_names).split(',')[month-1]
252 end
252 end
253
253
254 def syntax_highlight(name, content)
254 def syntax_highlight(name, content)
255 type = CodeRay::FileType[name]
255 type = CodeRay::FileType[name]
256 type ? CodeRay.scan(content, type).html : h(content)
256 type ? CodeRay.scan(content, type).html : h(content)
257 end
257 end
258
258
259 def to_path_param(path)
259 def to_path_param(path)
260 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
260 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
261 end
261 end
262
262
263 def pagination_links_full(paginator, count=nil, options={})
263 def pagination_links_full(paginator, count=nil, options={})
264 page_param = options.delete(:page_param) || :page
264 page_param = options.delete(:page_param) || :page
265 url_param = params.dup
265 url_param = params.dup
266 # don't reuse query params if filters are present
266 # don't reuse query params if filters are present
267 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
267 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
268
268
269 html = ''
269 html = ''
270 if paginator.current.previous
270 if paginator.current.previous
271 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
271 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
272 end
272 end
273
273
274 html << (pagination_links_each(paginator, options) do |n|
274 html << (pagination_links_each(paginator, options) do |n|
275 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
275 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
276 end || '')
276 end || '')
277
277
278 if paginator.current.next
278 if paginator.current.next
279 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
279 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
280 end
280 end
281
281
282 unless count.nil?
282 unless count.nil?
283 html << [
283 html << [
284 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
284 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
285 per_page_links(paginator.items_per_page)
285 per_page_links(paginator.items_per_page)
286 ].compact.join(' | ')
286 ].compact.join(' | ')
287 end
287 end
288
288
289 html
289 html
290 end
290 end
291
291
292 def per_page_links(selected=nil)
292 def per_page_links(selected=nil)
293 url_param = params.dup
293 url_param = params.dup
294 url_param.clear if url_param.has_key?(:set_filter)
294 url_param.clear if url_param.has_key?(:set_filter)
295
295
296 links = Setting.per_page_options_array.collect do |n|
296 links = Setting.per_page_options_array.collect do |n|
297 n == selected ? n : link_to_remote(n, {:update => "content",
297 n == selected ? n : link_to_remote(n, {:update => "content",
298 :url => params.dup.merge(:per_page => n),
298 :url => params.dup.merge(:per_page => n),
299 :method => :get},
299 :method => :get},
300 {:href => url_for(url_param.merge(:per_page => n))})
300 {:href => url_for(url_param.merge(:per_page => n))})
301 end
301 end
302 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
302 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
303 end
303 end
304
304
305 def breadcrumb(*args)
305 def breadcrumb(*args)
306 elements = args.flatten
306 elements = args.flatten
307 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
307 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
308 end
308 end
309
309
310 def other_formats_links(&block)
310 def other_formats_links(&block)
311 concat('<p class="other-formats">' + l(:label_export_to), block.binding)
311 concat('<p class="other-formats">' + l(:label_export_to), block.binding)
312 yield Redmine::Views::OtherFormatsBuilder.new(self)
312 yield Redmine::Views::OtherFormatsBuilder.new(self)
313 concat('</p>', block.binding)
313 concat('</p>', block.binding)
314 end
314 end
315
315
316 def page_header_title
316 def page_header_title
317 if @project.nil? || @project.new_record?
317 if @project.nil? || @project.new_record?
318 h(Setting.app_title)
318 h(Setting.app_title)
319 else
319 else
320 b = []
320 b = []
321 ancestors = (@project.root? ? [] : @project.ancestors.visible)
321 ancestors = (@project.root? ? [] : @project.ancestors.visible)
322 if ancestors.any?
322 if ancestors.any?
323 root = ancestors.shift
323 root = ancestors.shift
324 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
324 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
325 if ancestors.size > 2
325 if ancestors.size > 2
326 b << '&#8230;'
326 b << '&#8230;'
327 ancestors = ancestors[-2, 2]
327 ancestors = ancestors[-2, 2]
328 end
328 end
329 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
329 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
330 end
330 end
331 b << h(@project)
331 b << h(@project)
332 b.join(' &#187; ')
332 b.join(' &#187; ')
333 end
333 end
334 end
334 end
335
335
336 def html_title(*args)
336 def html_title(*args)
337 if args.empty?
337 if args.empty?
338 title = []
338 title = []
339 title << @project.name if @project
339 title << @project.name if @project
340 title += @html_title if @html_title
340 title += @html_title if @html_title
341 title << Setting.app_title
341 title << Setting.app_title
342 title.compact.join(' - ')
342 title.compact.join(' - ')
343 else
343 else
344 @html_title ||= []
344 @html_title ||= []
345 @html_title += args
345 @html_title += args
346 end
346 end
347 end
347 end
348
348
349 def accesskey(s)
349 def accesskey(s)
350 Redmine::AccessKeys.key_for s
350 Redmine::AccessKeys.key_for s
351 end
351 end
352
352
353 # Formats text according to system settings.
353 # Formats text according to system settings.
354 # 2 ways to call this method:
354 # 2 ways to call this method:
355 # * with a String: textilizable(text, options)
355 # * with a String: textilizable(text, options)
356 # * with an object and one of its attribute: textilizable(issue, :description, options)
356 # * with an object and one of its attribute: textilizable(issue, :description, options)
357 def textilizable(*args)
357 def textilizable(*args)
358 options = args.last.is_a?(Hash) ? args.pop : {}
358 options = args.last.is_a?(Hash) ? args.pop : {}
359 case args.size
359 case args.size
360 when 1
360 when 1
361 obj = options[:object]
361 obj = options[:object]
362 text = args.shift
362 text = args.shift
363 when 2
363 when 2
364 obj = args.shift
364 obj = args.shift
365 text = obj.send(args.shift).to_s
365 text = obj.send(args.shift).to_s
366 else
366 else
367 raise ArgumentError, 'invalid arguments to textilizable'
367 raise ArgumentError, 'invalid arguments to textilizable'
368 end
368 end
369 return '' if text.blank?
369 return '' if text.blank?
370
370
371 only_path = options.delete(:only_path) == false ? false : true
371 only_path = options.delete(:only_path) == false ? false : true
372
372
373 # when using an image link, try to use an attachment, if possible
373 # when using an image link, try to use an attachment, if possible
374 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
374 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
375
375
376 if attachments
376 if attachments
377 attachments = attachments.sort_by(&:created_on).reverse
377 attachments = attachments.sort_by(&:created_on).reverse
378 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
378 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
379 style = $1
379 style = $1
380 filename = $6.downcase
380 filename = $6.downcase
381 # search for the picture in attachments
381 # search for the picture in attachments
382 if found = attachments.detect { |att| att.filename.downcase == filename }
382 if found = attachments.detect { |att| att.filename.downcase == filename }
383 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
383 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
384 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
384 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
385 alt = desc.blank? ? nil : "(#{desc})"
385 alt = desc.blank? ? nil : "(#{desc})"
386 "!#{style}#{image_url}#{alt}!"
386 "!#{style}#{image_url}#{alt}!"
387 else
387 else
388 m
388 m
389 end
389 end
390 end
390 end
391 end
391 end
392
392
393 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
393 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
394
394
395 # different methods for formatting wiki links
395 # different methods for formatting wiki links
396 case options[:wiki_links]
396 case options[:wiki_links]
397 when :local
397 when :local
398 # used for local links to html files
398 # used for local links to html files
399 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
399 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
400 when :anchor
400 when :anchor
401 # used for single-file wiki export
401 # used for single-file wiki export
402 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
402 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
403 else
403 else
404 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
404 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
405 end
405 end
406
406
407 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
407 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
408
408
409 # Wiki links
409 # Wiki links
410 #
410 #
411 # Examples:
411 # Examples:
412 # [[mypage]]
412 # [[mypage]]
413 # [[mypage|mytext]]
413 # [[mypage|mytext]]
414 # wiki links can refer other project wikis, using project name or identifier:
414 # wiki links can refer other project wikis, using project name or identifier:
415 # [[project:]] -> wiki starting page
415 # [[project:]] -> wiki starting page
416 # [[project:|mytext]]
416 # [[project:|mytext]]
417 # [[project:mypage]]
417 # [[project:mypage]]
418 # [[project:mypage|mytext]]
418 # [[project:mypage|mytext]]
419 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
419 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
420 link_project = project
420 link_project = project
421 esc, all, page, title = $1, $2, $3, $5
421 esc, all, page, title = $1, $2, $3, $5
422 if esc.nil?
422 if esc.nil?
423 if page =~ /^([^\:]+)\:(.*)$/
423 if page =~ /^([^\:]+)\:(.*)$/
424 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
424 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
425 page = $2
425 page = $2
426 title ||= $1 if page.blank?
426 title ||= $1 if page.blank?
427 end
427 end
428
428
429 if link_project && link_project.wiki
429 if link_project && link_project.wiki
430 # extract anchor
430 # extract anchor
431 anchor = nil
431 anchor = nil
432 if page =~ /^(.+?)\#(.+)$/
432 if page =~ /^(.+?)\#(.+)$/
433 page, anchor = $1, $2
433 page, anchor = $1, $2
434 end
434 end
435 # check if page exists
435 # check if page exists
436 wiki_page = link_project.wiki.find_page(page)
436 wiki_page = link_project.wiki.find_page(page)
437 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
437 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
438 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
438 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
439 else
439 else
440 # project or wiki doesn't exist
440 # project or wiki doesn't exist
441 all
441 all
442 end
442 end
443 else
443 else
444 all
444 all
445 end
445 end
446 end
446 end
447
447
448 # Redmine links
448 # Redmine links
449 #
449 #
450 # Examples:
450 # Examples:
451 # Issues:
451 # Issues:
452 # #52 -> Link to issue #52
452 # #52 -> Link to issue #52
453 # Changesets:
453 # Changesets:
454 # r52 -> Link to revision 52
454 # r52 -> Link to revision 52
455 # commit:a85130f -> Link to scmid starting with a85130f
455 # commit:a85130f -> Link to scmid starting with a85130f
456 # Documents:
456 # Documents:
457 # document#17 -> Link to document with id 17
457 # document#17 -> Link to document with id 17
458 # document:Greetings -> Link to the document with title "Greetings"
458 # document:Greetings -> Link to the document with title "Greetings"
459 # document:"Some document" -> Link to the document with title "Some document"
459 # document:"Some document" -> Link to the document with title "Some document"
460 # Versions:
460 # Versions:
461 # version#3 -> Link to version with id 3
461 # version#3 -> Link to version with id 3
462 # version:1.0.0 -> Link to version named "1.0.0"
462 # version:1.0.0 -> Link to version named "1.0.0"
463 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
463 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
464 # Attachments:
464 # Attachments:
465 # attachment:file.zip -> Link to the attachment of the current object named file.zip
465 # attachment:file.zip -> Link to the attachment of the current object named file.zip
466 # Source files:
466 # Source files:
467 # source:some/file -> Link to the file located at /some/file in the project's repository
467 # source:some/file -> Link to the file located at /some/file in the project's repository
468 # source:some/file@52 -> Link to the file's revision 52
468 # source:some/file@52 -> Link to the file's revision 52
469 # source:some/file#L120 -> Link to line 120 of the file
469 # source:some/file#L120 -> Link to line 120 of the file
470 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
470 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
471 # export:some/file -> Force the download of the file
471 # export:some/file -> Force the download of the file
472 # Forum messages:
472 # Forum messages:
473 # message#1218 -> Link to message with id 1218
473 # message#1218 -> Link to message with id 1218
474 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
474 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
475 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
475 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
476 link = nil
476 link = nil
477 if esc.nil?
477 if esc.nil?
478 if prefix.nil? && sep == 'r'
478 if prefix.nil? && sep == 'r'
479 if project && (changeset = project.changesets.find_by_revision(oid))
479 if project && (changeset = project.changesets.find_by_revision(oid))
480 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
480 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
481 :class => 'changeset',
481 :class => 'changeset',
482 :title => truncate_single_line(changeset.comments, 100))
482 :title => truncate_single_line(changeset.comments, 100))
483 end
483 end
484 elsif sep == '#'
484 elsif sep == '#'
485 oid = oid.to_i
485 oid = oid.to_i
486 case prefix
486 case prefix
487 when nil
487 when nil
488 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
488 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
489 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
489 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
490 :class => (issue.closed? ? 'issue closed' : 'issue'),
490 :class => (issue.closed? ? 'issue closed' : 'issue'),
491 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
491 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
492 link = content_tag('del', link) if issue.closed?
492 link = content_tag('del', link) if issue.closed?
493 end
493 end
494 when 'document'
494 when 'document'
495 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
495 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
496 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
496 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
497 :class => 'document'
497 :class => 'document'
498 end
498 end
499 when 'version'
499 when 'version'
500 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
500 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
501 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
501 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
502 :class => 'version'
502 :class => 'version'
503 end
503 end
504 when 'message'
504 when 'message'
505 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
505 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
506 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
506 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
507 :controller => 'messages',
507 :controller => 'messages',
508 :action => 'show',
508 :action => 'show',
509 :board_id => message.board,
509 :board_id => message.board,
510 :id => message.root,
510 :id => message.root,
511 :anchor => (message.parent ? "message-#{message.id}" : nil)},
511 :anchor => (message.parent ? "message-#{message.id}" : nil)},
512 :class => 'message'
512 :class => 'message'
513 end
513 end
514 end
514 end
515 elsif sep == ':'
515 elsif sep == ':'
516 # removes the double quotes if any
516 # removes the double quotes if any
517 name = oid.gsub(%r{^"(.*)"$}, "\\1")
517 name = oid.gsub(%r{^"(.*)"$}, "\\1")
518 case prefix
518 case prefix
519 when 'document'
519 when 'document'
520 if project && document = project.documents.find_by_title(name)
520 if project && document = project.documents.find_by_title(name)
521 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
521 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
522 :class => 'document'
522 :class => 'document'
523 end
523 end
524 when 'version'
524 when 'version'
525 if project && version = project.versions.find_by_name(name)
525 if project && version = project.versions.find_by_name(name)
526 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
526 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
527 :class => 'version'
527 :class => 'version'
528 end
528 end
529 when 'commit'
529 when 'commit'
530 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
530 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
531 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
531 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
532 :class => 'changeset',
532 :class => 'changeset',
533 :title => truncate_single_line(changeset.comments, 100)
533 :title => truncate_single_line(changeset.comments, 100)
534 end
534 end
535 when 'source', 'export'
535 when 'source', 'export'
536 if project && project.repository
536 if project && project.repository
537 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
537 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
538 path, rev, anchor = $1, $3, $5
538 path, rev, anchor = $1, $3, $5
539 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
539 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
540 :path => to_path_param(path),
540 :path => to_path_param(path),
541 :rev => rev,
541 :rev => rev,
542 :anchor => anchor,
542 :anchor => anchor,
543 :format => (prefix == 'export' ? 'raw' : nil)},
543 :format => (prefix == 'export' ? 'raw' : nil)},
544 :class => (prefix == 'export' ? 'source download' : 'source')
544 :class => (prefix == 'export' ? 'source download' : 'source')
545 end
545 end
546 when 'attachment'
546 when 'attachment'
547 if attachments && attachment = attachments.detect {|a| a.filename == name }
547 if attachments && attachment = attachments.detect {|a| a.filename == name }
548 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
548 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
549 :class => 'attachment'
549 :class => 'attachment'
550 end
550 end
551 end
551 end
552 end
552 end
553 end
553 end
554 leading + (link || "#{prefix}#{sep}#{oid}")
554 leading + (link || "#{prefix}#{sep}#{oid}")
555 end
555 end
556
556
557 text
557 text
558 end
558 end
559
559
560 # Same as Rails' simple_format helper without using paragraphs
560 # Same as Rails' simple_format helper without using paragraphs
561 def simple_format_without_paragraph(text)
561 def simple_format_without_paragraph(text)
562 text.to_s.
562 text.to_s.
563 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
563 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
564 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
564 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
565 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
565 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
566 end
566 end
567
567
568 def error_messages_for(object_name, options = {})
568 def error_messages_for(object_name, options = {})
569 options = options.symbolize_keys
569 options = options.symbolize_keys
570 object = instance_variable_get("@#{object_name}")
570 object = instance_variable_get("@#{object_name}")
571 if object && !object.errors.empty?
571 if object && !object.errors.empty?
572 # build full_messages here with controller current language
572 # build full_messages here with controller current language
573 full_messages = []
573 full_messages = []
574 object.errors.each do |attr, msg|
574 object.errors.each do |attr, msg|
575 next if msg.nil?
575 next if msg.nil?
576 msg = msg.first if msg.is_a? Array
576 msg = [msg] unless msg.is_a?(Array)
577 if attr == "base"
577 if attr == "base"
578 full_messages << l(msg)
578 full_messages << l(*msg)
579 else
579 else
580 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
580 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(*msg) unless attr == "custom_values"
581 end
581 end
582 end
582 end
583 # retrieve custom values error messages
583 # retrieve custom values error messages
584 if object.errors[:custom_values]
584 if object.errors[:custom_values]
585 object.custom_values.each do |v|
585 object.custom_values.each do |v|
586 v.errors.each do |attr, msg|
586 v.errors.each do |attr, msg|
587 next if msg.nil?
587 next if msg.nil?
588 msg = msg.first if msg.is_a? Array
588 msg = [msg] unless msg.is_a?(Array)
589 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
589 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(*msg)
590 end
590 end
591 end
591 end
592 end
592 end
593 content_tag("div",
593 content_tag("div",
594 content_tag(
594 content_tag(
595 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
595 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
596 ) +
596 ) +
597 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
597 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
598 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
598 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
599 )
599 )
600 else
600 else
601 ""
601 ""
602 end
602 end
603 end
603 end
604
604
605 def lang_options_for_select(blank=true)
605 def lang_options_for_select(blank=true)
606 (blank ? [["(auto)", ""]] : []) +
606 (blank ? [["(auto)", ""]] : []) +
607 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
607 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
608 end
608 end
609
609
610 def label_tag_for(name, option_tags = nil, options = {})
610 def label_tag_for(name, option_tags = nil, options = {})
611 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
611 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
612 content_tag("label", label_text)
612 content_tag("label", label_text)
613 end
613 end
614
614
615 def labelled_tabular_form_for(name, object, options, &proc)
615 def labelled_tabular_form_for(name, object, options, &proc)
616 options[:html] ||= {}
616 options[:html] ||= {}
617 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
617 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
618 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
618 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
619 end
619 end
620
620
621 def back_url_hidden_field_tag
621 def back_url_hidden_field_tag
622 back_url = params[:back_url] || request.env['HTTP_REFERER']
622 back_url = params[:back_url] || request.env['HTTP_REFERER']
623 back_url = CGI.unescape(back_url.to_s)
623 back_url = CGI.unescape(back_url.to_s)
624 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
624 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
625 end
625 end
626
626
627 def check_all_links(form_name)
627 def check_all_links(form_name)
628 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
628 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
629 " | " +
629 " | " +
630 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
630 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
631 end
631 end
632
632
633 def progress_bar(pcts, options={})
633 def progress_bar(pcts, options={})
634 pcts = [pcts, pcts] unless pcts.is_a?(Array)
634 pcts = [pcts, pcts] unless pcts.is_a?(Array)
635 pcts[1] = pcts[1] - pcts[0]
635 pcts[1] = pcts[1] - pcts[0]
636 pcts << (100 - pcts[1] - pcts[0])
636 pcts << (100 - pcts[1] - pcts[0])
637 width = options[:width] || '100px;'
637 width = options[:width] || '100px;'
638 legend = options[:legend] || ''
638 legend = options[:legend] || ''
639 content_tag('table',
639 content_tag('table',
640 content_tag('tr',
640 content_tag('tr',
641 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
641 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
642 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
642 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
643 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
643 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
644 ), :class => 'progress', :style => "width: #{width};") +
644 ), :class => 'progress', :style => "width: #{width};") +
645 content_tag('p', legend, :class => 'pourcent')
645 content_tag('p', legend, :class => 'pourcent')
646 end
646 end
647
647
648 def context_menu_link(name, url, options={})
648 def context_menu_link(name, url, options={})
649 options[:class] ||= ''
649 options[:class] ||= ''
650 if options.delete(:selected)
650 if options.delete(:selected)
651 options[:class] << ' icon-checked disabled'
651 options[:class] << ' icon-checked disabled'
652 options[:disabled] = true
652 options[:disabled] = true
653 end
653 end
654 if options.delete(:disabled)
654 if options.delete(:disabled)
655 options.delete(:method)
655 options.delete(:method)
656 options.delete(:confirm)
656 options.delete(:confirm)
657 options.delete(:onclick)
657 options.delete(:onclick)
658 options[:class] << ' disabled'
658 options[:class] << ' disabled'
659 url = '#'
659 url = '#'
660 end
660 end
661 link_to name, url, options
661 link_to name, url, options
662 end
662 end
663
663
664 def calendar_for(field_id)
664 def calendar_for(field_id)
665 include_calendar_headers_tags
665 include_calendar_headers_tags
666 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
666 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
667 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
667 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
668 end
668 end
669
669
670 def include_calendar_headers_tags
670 def include_calendar_headers_tags
671 unless @calendar_headers_tags_included
671 unless @calendar_headers_tags_included
672 @calendar_headers_tags_included = true
672 @calendar_headers_tags_included = true
673 content_for :header_tags do
673 content_for :header_tags do
674 javascript_include_tag('calendar/calendar') +
674 javascript_include_tag('calendar/calendar') +
675 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
675 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
676 javascript_include_tag('calendar/calendar-setup') +
676 javascript_include_tag('calendar/calendar-setup') +
677 stylesheet_link_tag('calendar')
677 stylesheet_link_tag('calendar')
678 end
678 end
679 end
679 end
680 end
680 end
681
681
682 def content_for(name, content = nil, &block)
682 def content_for(name, content = nil, &block)
683 @has_content ||= {}
683 @has_content ||= {}
684 @has_content[name] = true
684 @has_content[name] = true
685 super(name, content, &block)
685 super(name, content, &block)
686 end
686 end
687
687
688 def has_content?(name)
688 def has_content?(name)
689 (@has_content && @has_content[name]) || false
689 (@has_content && @has_content[name]) || false
690 end
690 end
691
691
692 # Returns the avatar image tag for the given +user+ if avatars are enabled
692 # Returns the avatar image tag for the given +user+ if avatars are enabled
693 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
693 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
694 def avatar(user, options = { })
694 def avatar(user, options = { })
695 if Setting.gravatar_enabled?
695 if Setting.gravatar_enabled?
696 email = nil
696 email = nil
697 if user.respond_to?(:mail)
697 if user.respond_to?(:mail)
698 email = user.mail
698 email = user.mail
699 elsif user.to_s =~ %r{<(.+?)>}
699 elsif user.to_s =~ %r{<(.+?)>}
700 email = $1
700 email = $1
701 end
701 end
702 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
702 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
703 end
703 end
704 end
704 end
705
705
706 private
706 private
707
707
708 def wiki_helper
708 def wiki_helper
709 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
709 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
710 extend helper
710 extend helper
711 return self
711 return self
712 end
712 end
713
713
714 def link_to_remote_content_update(text, url_params)
714 def link_to_remote_content_update(text, url_params)
715 link_to_remote(text,
715 link_to_remote(text,
716 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
716 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
717 {:href => url_for(:params => url_params)}
717 {:href => url_for(:params => url_params)}
718 )
718 )
719 end
719 end
720
720
721 end
721 end
General Comments 0
You need to be logged in to leave comments. Login now