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