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