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