##// END OF EJS Templates
Fixed: changesets titles should not be multiline in atom feeds (#1356)....
Jean-Philippe Lang -
r1477:4db45b8ceddf
parent child
Show More
@@ -1,510 +1,517
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 module ApplicationHelper
18 module ApplicationHelper
19 include Redmine::WikiFormatting::Macros::Definitions
19 include Redmine::WikiFormatting::Macros::Definitions
20
20
21 def current_role
21 def current_role
22 @current_role ||= User.current.role_for_project(@project)
22 @current_role ||= User.current.role_for_project(@project)
23 end
23 end
24
24
25 # Return true if user is authorized for controller/action, otherwise false
25 # Return true if user is authorized for controller/action, otherwise false
26 def authorize_for(controller, action)
26 def authorize_for(controller, action)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
28 end
28 end
29
29
30 # Display a link if user is authorized
30 # Display a link if user is authorized
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
33 end
33 end
34
34
35 # Display a link to user's account page
35 # Display a link to user's account page
36 def link_to_user(user)
36 def link_to_user(user)
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
38 end
38 end
39
39
40 def link_to_issue(issue, options={})
40 def link_to_issue(issue, options={})
41 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
41 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
42 end
42 end
43
43
44 def toggle_link(name, id, options={})
44 def toggle_link(name, id, options={})
45 onclick = "Element.toggle('#{id}'); "
45 onclick = "Element.toggle('#{id}'); "
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
47 onclick << "return false;"
47 onclick << "return false;"
48 link_to(name, "#", :onclick => onclick)
48 link_to(name, "#", :onclick => onclick)
49 end
49 end
50
50
51 def show_and_goto_link(name, id, options={})
51 def show_and_goto_link(name, id, options={})
52 onclick = "Element.show('#{id}'); "
52 onclick = "Element.show('#{id}'); "
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
54 onclick << "Element.scrollTo('#{id}'); "
54 onclick << "Element.scrollTo('#{id}'); "
55 onclick << "return false;"
55 onclick << "return false;"
56 link_to(name, "#", options.merge(:onclick => onclick))
56 link_to(name, "#", options.merge(:onclick => onclick))
57 end
57 end
58
58
59 def image_to_function(name, function, html_options = {})
59 def image_to_function(name, function, html_options = {})
60 html_options.symbolize_keys!
60 html_options.symbolize_keys!
61 tag(:input, html_options.merge({
61 tag(:input, html_options.merge({
62 :type => "image", :src => image_path(name),
62 :type => "image", :src => image_path(name),
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
64 }))
64 }))
65 end
65 end
66
66
67 def prompt_to_remote(name, text, param, url, html_options = {})
67 def prompt_to_remote(name, text, param, url, html_options = {})
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
69 link_to name, {}, html_options
69 link_to name, {}, html_options
70 end
70 end
71
71
72 def format_date(date)
72 def format_date(date)
73 return nil unless date
73 return nil unless date
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
76 date.strftime(@date_format)
76 date.strftime(@date_format)
77 end
77 end
78
78
79 def format_time(time, include_date = true)
79 def format_time(time, include_date = true)
80 return nil unless time
80 return nil unless time
81 time = time.to_time if time.is_a?(String)
81 time = time.to_time if time.is_a?(String)
82 zone = User.current.time_zone
82 zone = User.current.time_zone
83 if time.utc?
83 if time.utc?
84 local = zone ? zone.adjust(time) : time.getlocal
84 local = zone ? zone.adjust(time) : time.getlocal
85 else
85 else
86 local = zone ? zone.adjust(time.getutc) : time
86 local = zone ? zone.adjust(time.getutc) : time
87 end
87 end
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
91 end
91 end
92
92
93 # Truncates and returns the string as a single line
94 def truncate_single_line(string, *args)
95 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
96 end
97
93 def html_hours(text)
98 def html_hours(text)
94 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
99 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
95 end
100 end
96
101
97 def authoring(created, author)
102 def authoring(created, author)
98 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
103 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
99 l(:label_added_time_by, author || 'Anonymous', time_tag)
104 l(:label_added_time_by, author || 'Anonymous', time_tag)
100 end
105 end
101
106
102 def l_or_humanize(s)
107 def l_or_humanize(s)
103 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
108 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
104 end
109 end
105
110
106 def day_name(day)
111 def day_name(day)
107 l(:general_day_names).split(',')[day-1]
112 l(:general_day_names).split(',')[day-1]
108 end
113 end
109
114
110 def month_name(month)
115 def month_name(month)
111 l(:actionview_datehelper_select_month_names).split(',')[month-1]
116 l(:actionview_datehelper_select_month_names).split(',')[month-1]
112 end
117 end
113
118
114 def pagination_links_full(paginator, count=nil, options={})
119 def pagination_links_full(paginator, count=nil, options={})
115 page_param = options.delete(:page_param) || :page
120 page_param = options.delete(:page_param) || :page
116 url_param = params.dup
121 url_param = params.dup
117 # don't reuse params if filters are present
122 # don't reuse params if filters are present
118 url_param.clear if url_param.has_key?(:set_filter)
123 url_param.clear if url_param.has_key?(:set_filter)
119
124
120 html = ''
125 html = ''
121 html << link_to_remote(('&#171; ' + l(:label_previous)),
126 html << link_to_remote(('&#171; ' + l(:label_previous)),
122 {:update => 'content',
127 {:update => 'content',
123 :url => url_param.merge(page_param => paginator.current.previous),
128 :url => url_param.merge(page_param => paginator.current.previous),
124 :complete => 'window.scrollTo(0,0)'},
129 :complete => 'window.scrollTo(0,0)'},
125 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
130 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
126
131
127 html << (pagination_links_each(paginator, options) do |n|
132 html << (pagination_links_each(paginator, options) do |n|
128 link_to_remote(n.to_s,
133 link_to_remote(n.to_s,
129 {:url => {:params => url_param.merge(page_param => n)},
134 {:url => {:params => url_param.merge(page_param => n)},
130 :update => 'content',
135 :update => 'content',
131 :complete => 'window.scrollTo(0,0)'},
136 :complete => 'window.scrollTo(0,0)'},
132 {:href => url_for(:params => url_param.merge(page_param => n))})
137 {:href => url_for(:params => url_param.merge(page_param => n))})
133 end || '')
138 end || '')
134
139
135 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
140 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
136 {:update => 'content',
141 {:update => 'content',
137 :url => url_param.merge(page_param => paginator.current.next),
142 :url => url_param.merge(page_param => paginator.current.next),
138 :complete => 'window.scrollTo(0,0)'},
143 :complete => 'window.scrollTo(0,0)'},
139 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
144 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
140
145
141 unless count.nil?
146 unless count.nil?
142 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
147 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
143 end
148 end
144
149
145 html
150 html
146 end
151 end
147
152
148 def per_page_links(selected=nil)
153 def per_page_links(selected=nil)
149 url_param = params.dup
154 url_param = params.dup
150 url_param.clear if url_param.has_key?(:set_filter)
155 url_param.clear if url_param.has_key?(:set_filter)
151
156
152 links = Setting.per_page_options_array.collect do |n|
157 links = Setting.per_page_options_array.collect do |n|
153 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
158 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
154 {:href => url_for(url_param.merge(:per_page => n))})
159 {:href => url_for(url_param.merge(:per_page => n))})
155 end
160 end
156 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
161 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
157 end
162 end
158
163
159 def breadcrumb(*args)
164 def breadcrumb(*args)
160 content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb')
165 content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb')
161 end
166 end
162
167
163 def html_title(*args)
168 def html_title(*args)
164 if args.empty?
169 if args.empty?
165 title = []
170 title = []
166 title << @project.name if @project
171 title << @project.name if @project
167 title += @html_title if @html_title
172 title += @html_title if @html_title
168 title << Setting.app_title
173 title << Setting.app_title
169 title.compact.join(' - ')
174 title.compact.join(' - ')
170 else
175 else
171 @html_title ||= []
176 @html_title ||= []
172 @html_title += args
177 @html_title += args
173 end
178 end
174 end
179 end
175
180
176 def accesskey(s)
181 def accesskey(s)
177 Redmine::AccessKeys.key_for s
182 Redmine::AccessKeys.key_for s
178 end
183 end
179
184
180 # Formats text according to system settings.
185 # Formats text according to system settings.
181 # 2 ways to call this method:
186 # 2 ways to call this method:
182 # * with a String: textilizable(text, options)
187 # * with a String: textilizable(text, options)
183 # * with an object and one of its attribute: textilizable(issue, :description, options)
188 # * with an object and one of its attribute: textilizable(issue, :description, options)
184 def textilizable(*args)
189 def textilizable(*args)
185 options = args.last.is_a?(Hash) ? args.pop : {}
190 options = args.last.is_a?(Hash) ? args.pop : {}
186 case args.size
191 case args.size
187 when 1
192 when 1
188 obj = nil
193 obj = nil
189 text = args.shift
194 text = args.shift
190 when 2
195 when 2
191 obj = args.shift
196 obj = args.shift
192 text = obj.send(args.shift).to_s
197 text = obj.send(args.shift).to_s
193 else
198 else
194 raise ArgumentError, 'invalid arguments to textilizable'
199 raise ArgumentError, 'invalid arguments to textilizable'
195 end
200 end
196 return '' if text.blank?
201 return '' if text.blank?
197
202
198 only_path = options.delete(:only_path) == false ? false : true
203 only_path = options.delete(:only_path) == false ? false : true
199
204
200 # when using an image link, try to use an attachment, if possible
205 # when using an image link, try to use an attachment, if possible
201 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
206 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
202
207
203 if attachments
208 if attachments
204 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
209 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
205 style = $1
210 style = $1
206 filename = $6
211 filename = $6
207 rf = Regexp.new(filename, Regexp::IGNORECASE)
212 rf = Regexp.new(filename, Regexp::IGNORECASE)
208 # search for the picture in attachments
213 # search for the picture in attachments
209 if found = attachments.detect { |att| att.filename =~ rf }
214 if found = attachments.detect { |att| att.filename =~ rf }
210 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
215 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
211 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
216 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
212 alt = desc.blank? ? nil : "(#{desc})"
217 alt = desc.blank? ? nil : "(#{desc})"
213 "!#{style}#{image_url}#{alt}!"
218 "!#{style}#{image_url}#{alt}!"
214 else
219 else
215 "!#{style}#{filename}!"
220 "!#{style}#{filename}!"
216 end
221 end
217 end
222 end
218 end
223 end
219
224
220 text = (Setting.text_formatting == 'textile') ?
225 text = (Setting.text_formatting == 'textile') ?
221 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
226 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
222 simple_format(auto_link(h(text)))
227 simple_format(auto_link(h(text)))
223
228
224 # different methods for formatting wiki links
229 # different methods for formatting wiki links
225 case options[:wiki_links]
230 case options[:wiki_links]
226 when :local
231 when :local
227 # used for local links to html files
232 # used for local links to html files
228 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
233 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
229 when :anchor
234 when :anchor
230 # used for single-file wiki export
235 # used for single-file wiki export
231 format_wiki_link = Proc.new {|project, title| "##{title}" }
236 format_wiki_link = Proc.new {|project, title| "##{title}" }
232 else
237 else
233 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
238 format_wiki_link = Proc.new {|project, title| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title) }
234 end
239 end
235
240
236 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
241 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
237
242
238 # Wiki links
243 # Wiki links
239 #
244 #
240 # Examples:
245 # Examples:
241 # [[mypage]]
246 # [[mypage]]
242 # [[mypage|mytext]]
247 # [[mypage|mytext]]
243 # wiki links can refer other project wikis, using project name or identifier:
248 # wiki links can refer other project wikis, using project name or identifier:
244 # [[project:]] -> wiki starting page
249 # [[project:]] -> wiki starting page
245 # [[project:|mytext]]
250 # [[project:|mytext]]
246 # [[project:mypage]]
251 # [[project:mypage]]
247 # [[project:mypage|mytext]]
252 # [[project:mypage|mytext]]
248 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
253 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
249 link_project = project
254 link_project = project
250 esc, all, page, title = $1, $2, $3, $5
255 esc, all, page, title = $1, $2, $3, $5
251 if esc.nil?
256 if esc.nil?
252 if page =~ /^([^\:]+)\:(.*)$/
257 if page =~ /^([^\:]+)\:(.*)$/
253 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
258 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
254 page = $2
259 page = $2
255 title ||= $1 if page.blank?
260 title ||= $1 if page.blank?
256 end
261 end
257
262
258 if link_project && link_project.wiki
263 if link_project && link_project.wiki
259 # check if page exists
264 # check if page exists
260 wiki_page = link_project.wiki.find_page(page)
265 wiki_page = link_project.wiki.find_page(page)
261 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
266 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
262 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
267 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
263 else
268 else
264 # project or wiki doesn't exist
269 # project or wiki doesn't exist
265 title || page
270 title || page
266 end
271 end
267 else
272 else
268 all
273 all
269 end
274 end
270 end
275 end
271
276
272 # Redmine links
277 # Redmine links
273 #
278 #
274 # Examples:
279 # Examples:
275 # Issues:
280 # Issues:
276 # #52 -> Link to issue #52
281 # #52 -> Link to issue #52
277 # Changesets:
282 # Changesets:
278 # r52 -> Link to revision 52
283 # r52 -> Link to revision 52
279 # commit:a85130f -> Link to scmid starting with a85130f
284 # commit:a85130f -> Link to scmid starting with a85130f
280 # Documents:
285 # Documents:
281 # document#17 -> Link to document with id 17
286 # document#17 -> Link to document with id 17
282 # document:Greetings -> Link to the document with title "Greetings"
287 # document:Greetings -> Link to the document with title "Greetings"
283 # document:"Some document" -> Link to the document with title "Some document"
288 # document:"Some document" -> Link to the document with title "Some document"
284 # Versions:
289 # Versions:
285 # version#3 -> Link to version with id 3
290 # version#3 -> Link to version with id 3
286 # version:1.0.0 -> Link to version named "1.0.0"
291 # version:1.0.0 -> Link to version named "1.0.0"
287 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
292 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
288 # Attachments:
293 # Attachments:
289 # attachment:file.zip -> Link to the attachment of the current object named file.zip
294 # attachment:file.zip -> Link to the attachment of the current object named file.zip
290 # Source files:
295 # Source files:
291 # source:some/file -> Link to the file located at /some/file in the project's repository
296 # source:some/file -> Link to the file located at /some/file in the project's repository
292 # source:some/file@52 -> Link to the file's revision 52
297 # source:some/file@52 -> Link to the file's revision 52
293 # source:some/file#L120 -> Link to line 120 of the file
298 # source:some/file#L120 -> Link to line 120 of the file
294 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
299 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
295 # export:some/file -> Force the download of the file
300 # export:some/file -> Force the download of the file
296 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
301 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version|commit|source|export)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
297 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
302 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
298 link = nil
303 link = nil
299 if esc.nil?
304 if esc.nil?
300 if prefix.nil? && sep == 'r'
305 if prefix.nil? && sep == 'r'
301 if project && (changeset = project.changesets.find_by_revision(oid))
306 if project && (changeset = project.changesets.find_by_revision(oid))
302 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
307 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
303 :class => 'changeset',
308 :class => 'changeset',
304 :title => truncate(changeset.comments, 100))
309 :title => truncate_single_line(changeset.comments, 100))
305 end
310 end
306 elsif sep == '#'
311 elsif sep == '#'
307 oid = oid.to_i
312 oid = oid.to_i
308 case prefix
313 case prefix
309 when nil
314 when nil
310 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
315 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
311 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
316 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
312 :class => (issue.closed? ? 'issue closed' : 'issue'),
317 :class => (issue.closed? ? 'issue closed' : 'issue'),
313 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
318 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
314 link = content_tag('del', link) if issue.closed?
319 link = content_tag('del', link) if issue.closed?
315 end
320 end
316 when 'document'
321 when 'document'
317 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
322 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
318 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
323 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
319 :class => 'document'
324 :class => 'document'
320 end
325 end
321 when 'version'
326 when 'version'
322 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
327 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
323 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
328 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
324 :class => 'version'
329 :class => 'version'
325 end
330 end
326 end
331 end
327 elsif sep == ':'
332 elsif sep == ':'
328 # removes the double quotes if any
333 # removes the double quotes if any
329 name = oid.gsub(%r{^"(.*)"$}, "\\1")
334 name = oid.gsub(%r{^"(.*)"$}, "\\1")
330 case prefix
335 case prefix
331 when 'document'
336 when 'document'
332 if project && document = project.documents.find_by_title(name)
337 if project && document = project.documents.find_by_title(name)
333 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
338 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
334 :class => 'document'
339 :class => 'document'
335 end
340 end
336 when 'version'
341 when 'version'
337 if project && version = project.versions.find_by_name(name)
342 if project && version = project.versions.find_by_name(name)
338 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
343 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
339 :class => 'version'
344 :class => 'version'
340 end
345 end
341 when 'commit'
346 when 'commit'
342 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
347 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
343 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision}, :class => 'changeset', :title => truncate(changeset.comments, 100)
348 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
349 :class => 'changeset',
350 :title => truncate_single_line(changeset.comments, 100)
344 end
351 end
345 when 'source', 'export'
352 when 'source', 'export'
346 if project && project.repository
353 if project && project.repository
347 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
354 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
348 path, rev, anchor = $1, $3, $5
355 path, rev, anchor = $1, $3, $5
349 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
356 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :path => path,
350 :rev => rev,
357 :rev => rev,
351 :anchor => anchor,
358 :anchor => anchor,
352 :format => (prefix == 'export' ? 'raw' : nil)},
359 :format => (prefix == 'export' ? 'raw' : nil)},
353 :class => (prefix == 'export' ? 'source download' : 'source')
360 :class => (prefix == 'export' ? 'source download' : 'source')
354 end
361 end
355 when 'attachment'
362 when 'attachment'
356 if attachments && attachment = attachments.detect {|a| a.filename == name }
363 if attachments && attachment = attachments.detect {|a| a.filename == name }
357 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
364 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
358 :class => 'attachment'
365 :class => 'attachment'
359 end
366 end
360 end
367 end
361 end
368 end
362 end
369 end
363 leading + (link || "#{prefix}#{sep}#{oid}")
370 leading + (link || "#{prefix}#{sep}#{oid}")
364 end
371 end
365
372
366 text
373 text
367 end
374 end
368
375
369 # Same as Rails' simple_format helper without using paragraphs
376 # Same as Rails' simple_format helper without using paragraphs
370 def simple_format_without_paragraph(text)
377 def simple_format_without_paragraph(text)
371 text.to_s.
378 text.to_s.
372 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
379 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
373 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
380 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
374 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
381 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
375 end
382 end
376
383
377 def error_messages_for(object_name, options = {})
384 def error_messages_for(object_name, options = {})
378 options = options.symbolize_keys
385 options = options.symbolize_keys
379 object = instance_variable_get("@#{object_name}")
386 object = instance_variable_get("@#{object_name}")
380 if object && !object.errors.empty?
387 if object && !object.errors.empty?
381 # build full_messages here with controller current language
388 # build full_messages here with controller current language
382 full_messages = []
389 full_messages = []
383 object.errors.each do |attr, msg|
390 object.errors.each do |attr, msg|
384 next if msg.nil?
391 next if msg.nil?
385 msg = msg.first if msg.is_a? Array
392 msg = msg.first if msg.is_a? Array
386 if attr == "base"
393 if attr == "base"
387 full_messages << l(msg)
394 full_messages << l(msg)
388 else
395 else
389 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
396 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
390 end
397 end
391 end
398 end
392 # retrieve custom values error messages
399 # retrieve custom values error messages
393 if object.errors[:custom_values]
400 if object.errors[:custom_values]
394 object.custom_values.each do |v|
401 object.custom_values.each do |v|
395 v.errors.each do |attr, msg|
402 v.errors.each do |attr, msg|
396 next if msg.nil?
403 next if msg.nil?
397 msg = msg.first if msg.is_a? Array
404 msg = msg.first if msg.is_a? Array
398 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
405 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
399 end
406 end
400 end
407 end
401 end
408 end
402 content_tag("div",
409 content_tag("div",
403 content_tag(
410 content_tag(
404 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
411 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
405 ) +
412 ) +
406 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
413 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
407 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
414 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
408 )
415 )
409 else
416 else
410 ""
417 ""
411 end
418 end
412 end
419 end
413
420
414 def lang_options_for_select(blank=true)
421 def lang_options_for_select(blank=true)
415 (blank ? [["(auto)", ""]] : []) +
422 (blank ? [["(auto)", ""]] : []) +
416 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
423 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
417 end
424 end
418
425
419 def label_tag_for(name, option_tags = nil, options = {})
426 def label_tag_for(name, option_tags = nil, options = {})
420 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
427 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
421 content_tag("label", label_text)
428 content_tag("label", label_text)
422 end
429 end
423
430
424 def labelled_tabular_form_for(name, object, options, &proc)
431 def labelled_tabular_form_for(name, object, options, &proc)
425 options[:html] ||= {}
432 options[:html] ||= {}
426 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
433 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
427 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
434 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
428 end
435 end
429
436
430 def back_url_hidden_field_tag
437 def back_url_hidden_field_tag
431 hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
438 hidden_field_tag 'back_url', (params[:back_url] || request.env['HTTP_REFERER'])
432 end
439 end
433
440
434 def check_all_links(form_name)
441 def check_all_links(form_name)
435 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
442 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
436 " | " +
443 " | " +
437 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
444 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
438 end
445 end
439
446
440 def progress_bar(pcts, options={})
447 def progress_bar(pcts, options={})
441 pcts = [pcts, pcts] unless pcts.is_a?(Array)
448 pcts = [pcts, pcts] unless pcts.is_a?(Array)
442 pcts[1] = pcts[1] - pcts[0]
449 pcts[1] = pcts[1] - pcts[0]
443 pcts << (100 - pcts[1] - pcts[0])
450 pcts << (100 - pcts[1] - pcts[0])
444 width = options[:width] || '100px;'
451 width = options[:width] || '100px;'
445 legend = options[:legend] || ''
452 legend = options[:legend] || ''
446 content_tag('table',
453 content_tag('table',
447 content_tag('tr',
454 content_tag('tr',
448 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
455 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
449 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
456 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
450 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
457 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
451 ), :class => 'progress', :style => "width: #{width};") +
458 ), :class => 'progress', :style => "width: #{width};") +
452 content_tag('p', legend, :class => 'pourcent')
459 content_tag('p', legend, :class => 'pourcent')
453 end
460 end
454
461
455 def context_menu_link(name, url, options={})
462 def context_menu_link(name, url, options={})
456 options[:class] ||= ''
463 options[:class] ||= ''
457 if options.delete(:selected)
464 if options.delete(:selected)
458 options[:class] << ' icon-checked disabled'
465 options[:class] << ' icon-checked disabled'
459 options[:disabled] = true
466 options[:disabled] = true
460 end
467 end
461 if options.delete(:disabled)
468 if options.delete(:disabled)
462 options.delete(:method)
469 options.delete(:method)
463 options.delete(:confirm)
470 options.delete(:confirm)
464 options.delete(:onclick)
471 options.delete(:onclick)
465 options[:class] << ' disabled'
472 options[:class] << ' disabled'
466 url = '#'
473 url = '#'
467 end
474 end
468 link_to name, url, options
475 link_to name, url, options
469 end
476 end
470
477
471 def calendar_for(field_id)
478 def calendar_for(field_id)
472 include_calendar_headers_tags
479 include_calendar_headers_tags
473 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
480 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
474 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
481 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
475 end
482 end
476
483
477 def include_calendar_headers_tags
484 def include_calendar_headers_tags
478 unless @calendar_headers_tags_included
485 unless @calendar_headers_tags_included
479 @calendar_headers_tags_included = true
486 @calendar_headers_tags_included = true
480 content_for :header_tags do
487 content_for :header_tags do
481 javascript_include_tag('calendar/calendar') +
488 javascript_include_tag('calendar/calendar') +
482 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
489 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
483 javascript_include_tag('calendar/calendar-setup') +
490 javascript_include_tag('calendar/calendar-setup') +
484 stylesheet_link_tag('calendar')
491 stylesheet_link_tag('calendar')
485 end
492 end
486 end
493 end
487 end
494 end
488
495
489 def wikitoolbar_for(field_id)
496 def wikitoolbar_for(field_id)
490 return '' unless Setting.text_formatting == 'textile'
497 return '' unless Setting.text_formatting == 'textile'
491
498
492 help_link = l(:setting_text_formatting) + ': ' +
499 help_link = l(:setting_text_formatting) + ': ' +
493 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
500 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
494 :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
501 :onclick => "window.open(\"#{ compute_public_path('wiki_syntax', 'help', 'html') }\", \"\", \"resizable=yes, location=no, width=300, height=640, menubar=no, status=no, scrollbars=yes\"); return false;")
495
502
496 javascript_include_tag('jstoolbar/jstoolbar') +
503 javascript_include_tag('jstoolbar/jstoolbar') +
497 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
504 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
498 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
505 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
499 end
506 end
500
507
501 def content_for(name, content = nil, &block)
508 def content_for(name, content = nil, &block)
502 @has_content ||= {}
509 @has_content ||= {}
503 @has_content[name] = true
510 @has_content[name] = true
504 super(name, content, &block)
511 super(name, content, &block)
505 end
512 end
506
513
507 def has_content?(name)
514 def has_content?(name)
508 (@has_content && @has_content[name]) || false
515 (@has_content && @has_content[name]) || false
509 end
516 end
510 end
517 end
@@ -1,194 +1,198
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 module ProjectsHelper
18 module ProjectsHelper
19 def link_to_version(version, options = {})
19 def link_to_version(version, options = {})
20 return '' unless version && version.is_a?(Version)
20 return '' unless version && version.is_a?(Version)
21 link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
21 link_to h(version.name), { :controller => 'versions', :action => 'show', :id => version }, options
22 end
22 end
23
23
24 def format_activity_title(text)
25 h(truncate_single_line(text, 100))
26 end
27
24 def format_activity_day(date)
28 def format_activity_day(date)
25 date == Date.today ? l(:label_today).titleize : format_date(date)
29 date == Date.today ? l(:label_today).titleize : format_date(date)
26 end
30 end
27
31
28 def format_activity_description(text)
32 def format_activity_description(text)
29 h(truncate(text, 250))
33 h(truncate(text, 250))
30 end
34 end
31
35
32 def project_settings_tabs
36 def project_settings_tabs
33 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
37 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
34 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
38 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
35 {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
39 {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
36 {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
40 {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
37 {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
41 {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
38 {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
42 {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
39 {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
43 {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
40 {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural}
44 {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural}
41 ]
45 ]
42 tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
46 tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
43 end
47 end
44
48
45 # Generates a gantt image
49 # Generates a gantt image
46 # Only defined if RMagick is avalaible
50 # Only defined if RMagick is avalaible
47 def gantt_image(events, date_from, months, zoom)
51 def gantt_image(events, date_from, months, zoom)
48 date_to = (date_from >> months)-1
52 date_to = (date_from >> months)-1
49 show_weeks = zoom > 1
53 show_weeks = zoom > 1
50 show_days = zoom > 2
54 show_days = zoom > 2
51
55
52 subject_width = 320
56 subject_width = 320
53 header_heigth = 18
57 header_heigth = 18
54 # width of one day in pixels
58 # width of one day in pixels
55 zoom = zoom*2
59 zoom = zoom*2
56 g_width = (date_to - date_from + 1)*zoom
60 g_width = (date_to - date_from + 1)*zoom
57 g_height = 20 * events.length + 20
61 g_height = 20 * events.length + 20
58 headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
62 headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
59 height = g_height + headers_heigth
63 height = g_height + headers_heigth
60
64
61 imgl = Magick::ImageList.new
65 imgl = Magick::ImageList.new
62 imgl.new_image(subject_width+g_width+1, height)
66 imgl.new_image(subject_width+g_width+1, height)
63 gc = Magick::Draw.new
67 gc = Magick::Draw.new
64
68
65 # Subjects
69 # Subjects
66 top = headers_heigth + 20
70 top = headers_heigth + 20
67 gc.fill('black')
71 gc.fill('black')
68 gc.stroke('transparent')
72 gc.stroke('transparent')
69 gc.stroke_width(1)
73 gc.stroke_width(1)
70 events.each do |i|
74 events.each do |i|
71 gc.text(4, top + 2, (i.is_a?(Issue) ? i.subject : i.name))
75 gc.text(4, top + 2, (i.is_a?(Issue) ? i.subject : i.name))
72 top = top + 20
76 top = top + 20
73 end
77 end
74
78
75 # Months headers
79 # Months headers
76 month_f = date_from
80 month_f = date_from
77 left = subject_width
81 left = subject_width
78 months.times do
82 months.times do
79 width = ((month_f >> 1) - month_f) * zoom
83 width = ((month_f >> 1) - month_f) * zoom
80 gc.fill('white')
84 gc.fill('white')
81 gc.stroke('grey')
85 gc.stroke('grey')
82 gc.stroke_width(1)
86 gc.stroke_width(1)
83 gc.rectangle(left, 0, left + width, height)
87 gc.rectangle(left, 0, left + width, height)
84 gc.fill('black')
88 gc.fill('black')
85 gc.stroke('transparent')
89 gc.stroke('transparent')
86 gc.stroke_width(1)
90 gc.stroke_width(1)
87 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
91 gc.text(left.round + 8, 14, "#{month_f.year}-#{month_f.month}")
88 left = left + width
92 left = left + width
89 month_f = month_f >> 1
93 month_f = month_f >> 1
90 end
94 end
91
95
92 # Weeks headers
96 # Weeks headers
93 if show_weeks
97 if show_weeks
94 left = subject_width
98 left = subject_width
95 height = header_heigth
99 height = header_heigth
96 if date_from.cwday == 1
100 if date_from.cwday == 1
97 # date_from is monday
101 # date_from is monday
98 week_f = date_from
102 week_f = date_from
99 else
103 else
100 # find next monday after date_from
104 # find next monday after date_from
101 week_f = date_from + (7 - date_from.cwday + 1)
105 week_f = date_from + (7 - date_from.cwday + 1)
102 width = (7 - date_from.cwday + 1) * zoom
106 width = (7 - date_from.cwday + 1) * zoom
103 gc.fill('white')
107 gc.fill('white')
104 gc.stroke('grey')
108 gc.stroke('grey')
105 gc.stroke_width(1)
109 gc.stroke_width(1)
106 gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
110 gc.rectangle(left, header_heigth, left + width, 2*header_heigth + g_height-1)
107 left = left + width
111 left = left + width
108 end
112 end
109 while week_f <= date_to
113 while week_f <= date_to
110 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
114 width = (week_f + 6 <= date_to) ? 7 * zoom : (date_to - week_f + 1) * zoom
111 gc.fill('white')
115 gc.fill('white')
112 gc.stroke('grey')
116 gc.stroke('grey')
113 gc.stroke_width(1)
117 gc.stroke_width(1)
114 gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
118 gc.rectangle(left.round, header_heigth, left.round + width, 2*header_heigth + g_height-1)
115 gc.fill('black')
119 gc.fill('black')
116 gc.stroke('transparent')
120 gc.stroke('transparent')
117 gc.stroke_width(1)
121 gc.stroke_width(1)
118 gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
122 gc.text(left.round + 2, header_heigth + 14, week_f.cweek.to_s)
119 left = left + width
123 left = left + width
120 week_f = week_f+7
124 week_f = week_f+7
121 end
125 end
122 end
126 end
123
127
124 # Days details (week-end in grey)
128 # Days details (week-end in grey)
125 if show_days
129 if show_days
126 left = subject_width
130 left = subject_width
127 height = g_height + header_heigth - 1
131 height = g_height + header_heigth - 1
128 wday = date_from.cwday
132 wday = date_from.cwday
129 (date_to - date_from + 1).to_i.times do
133 (date_to - date_from + 1).to_i.times do
130 width = zoom
134 width = zoom
131 gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
135 gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
132 gc.stroke('grey')
136 gc.stroke('grey')
133 gc.stroke_width(1)
137 gc.stroke_width(1)
134 gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
138 gc.rectangle(left, 2*header_heigth, left + width, 2*header_heigth + g_height-1)
135 left = left + width
139 left = left + width
136 wday = wday + 1
140 wday = wday + 1
137 wday = 1 if wday > 7
141 wday = 1 if wday > 7
138 end
142 end
139 end
143 end
140
144
141 # border
145 # border
142 gc.fill('transparent')
146 gc.fill('transparent')
143 gc.stroke('grey')
147 gc.stroke('grey')
144 gc.stroke_width(1)
148 gc.stroke_width(1)
145 gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
149 gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
146 gc.stroke('black')
150 gc.stroke('black')
147 gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
151 gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
148
152
149 # content
153 # content
150 top = headers_heigth + 20
154 top = headers_heigth + 20
151 gc.stroke('transparent')
155 gc.stroke('transparent')
152 events.each do |i|
156 events.each do |i|
153 if i.is_a?(Issue)
157 if i.is_a?(Issue)
154 i_start_date = (i.start_date >= date_from ? i.start_date : date_from )
158 i_start_date = (i.start_date >= date_from ? i.start_date : date_from )
155 i_end_date = (i.due_date <= date_to ? i.due_date : date_to )
159 i_end_date = (i.due_date <= date_to ? i.due_date : date_to )
156 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
160 i_done_date = i.start_date + ((i.due_date - i.start_date+1)*i.done_ratio/100).floor
157 i_done_date = (i_done_date <= date_from ? date_from : i_done_date )
161 i_done_date = (i_done_date <= date_from ? date_from : i_done_date )
158 i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
162 i_done_date = (i_done_date >= date_to ? date_to : i_done_date )
159 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
163 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
160
164
161 i_left = subject_width + ((i_start_date - date_from)*zoom).floor
165 i_left = subject_width + ((i_start_date - date_from)*zoom).floor
162 i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
166 i_width = ((i_end_date - i_start_date + 1)*zoom).floor # total width of the issue
163 d_width = ((i_done_date - i_start_date)*zoom).floor # done width
167 d_width = ((i_done_date - i_start_date)*zoom).floor # done width
164 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
168 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor : 0 # delay width
165
169
166 gc.fill('grey')
170 gc.fill('grey')
167 gc.rectangle(i_left, top, i_left + i_width, top - 6)
171 gc.rectangle(i_left, top, i_left + i_width, top - 6)
168 gc.fill('red')
172 gc.fill('red')
169 gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
173 gc.rectangle(i_left, top, i_left + l_width, top - 6) if l_width > 0
170 gc.fill('blue')
174 gc.fill('blue')
171 gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
175 gc.rectangle(i_left, top, i_left + d_width, top - 6) if d_width > 0
172 gc.fill('black')
176 gc.fill('black')
173 gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
177 gc.text(i_left + i_width + 5,top + 1, "#{i.status.name} #{i.done_ratio}%")
174 else
178 else
175 i_left = subject_width + ((i.start_date - date_from)*zoom).floor
179 i_left = subject_width + ((i.start_date - date_from)*zoom).floor
176 gc.fill('green')
180 gc.fill('green')
177 gc.rectangle(i_left, top, i_left + 6, top - 6)
181 gc.rectangle(i_left, top, i_left + 6, top - 6)
178 gc.fill('black')
182 gc.fill('black')
179 gc.text(i_left + 11, top + 1, i.name)
183 gc.text(i_left + 11, top + 1, i.name)
180 end
184 end
181 top = top + 20
185 top = top + 20
182 end
186 end
183
187
184 # today red line
188 # today red line
185 if Date.today >= date_from and Date.today <= date_to
189 if Date.today >= date_from and Date.today <= date_to
186 gc.stroke('red')
190 gc.stroke('red')
187 x = (Date.today-date_from+1)*zoom + subject_width
191 x = (Date.today-date_from+1)*zoom + subject_width
188 gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
192 gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
189 end
193 end
190
194
191 gc.draw(imgl)
195 gc.draw(imgl)
192 imgl
196 imgl
193 end if Object.const_defined?(:Magick)
197 end if Object.const_defined?(:Magick)
194 end
198 end
@@ -1,27 +1,27
1 xml.instruct!
1 xml.instruct!
2 xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
2 xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
3 xml.title @title
3 xml.title truncate_single_line(@title, 100)
4 xml.link "rel" => "self", "href" => url_for(params.merge({:format => nil, :only_path => false}))
4 xml.link "rel" => "self", "href" => url_for(params.merge({:format => nil, :only_path => false}))
5 xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
5 xml.link "rel" => "alternate", "href" => url_for(:controller => 'welcome', :only_path => false)
6 xml.id url_for(:controller => 'welcome', :only_path => false)
6 xml.id url_for(:controller => 'welcome', :only_path => false)
7 xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema)
7 xml.updated((@items.first ? @items.first.event_datetime : Time.now).xmlschema)
8 xml.author { xml.name "#{Setting.app_title}" }
8 xml.author { xml.name "#{Setting.app_title}" }
9 xml.generator(:uri => Redmine::Info.url, :version => Redmine::VERSION) { xml.text! Redmine::Info.versioned_name; }
9 xml.generator(:uri => Redmine::Info.url, :version => Redmine::VERSION) { xml.text! Redmine::Info.versioned_name; }
10 @items.each do |item|
10 @items.each do |item|
11 xml.entry do
11 xml.entry do
12 url = url_for(item.event_url(:only_path => false))
12 url = url_for(item.event_url(:only_path => false))
13 xml.title truncate(item.event_title, 100)
13 xml.title truncate_single_line(item.event_title, 100)
14 xml.link "rel" => "alternate", "href" => url
14 xml.link "rel" => "alternate", "href" => url
15 xml.id url
15 xml.id url
16 xml.updated item.event_datetime.xmlschema
16 xml.updated item.event_datetime.xmlschema
17 author = item.event_author if item.respond_to?(:event_author)
17 author = item.event_author if item.respond_to?(:event_author)
18 xml.author do
18 xml.author do
19 xml.name(author)
19 xml.name(author)
20 xml.email(author.mail) if author.respond_to?(:mail) && !author.mail.blank?
20 xml.email(author.mail) if author.respond_to?(:mail) && !author.mail.blank?
21 end if author
21 end if author
22 xml.content "type" => "html" do
22 xml.content "type" => "html" do
23 xml.text! textilizable(item.event_description)
23 xml.text! textilizable(item.event_description)
24 end
24 end
25 end
25 end
26 end
26 end
27 end
27 end
@@ -1,56 +1,57
1 <h2><%= l(:label_activity) %></h2>
1 <h2><%= l(:label_activity) %></h2>
2 <p class="subtitle"><%= "#{l(:label_date_from)} #{format_date(@date_to - @days)} #{l(:label_date_to).downcase} #{format_date(@date_to-1)}" %></p>
2 <p class="subtitle"><%= "#{l(:label_date_from)} #{format_date(@date_to - @days)} #{l(:label_date_to).downcase} #{format_date(@date_to-1)}" %></p>
3
3
4 <div id="activity">
4 <div id="activity">
5 <% @events_by_day.keys.sort.reverse.each do |day| %>
5 <% @events_by_day.keys.sort.reverse.each do |day| %>
6 <h3><%= format_activity_day(day) %></h3>
6 <h3><%= format_activity_day(day) %></h3>
7 <dl>
7 <dl>
8 <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
8 <% @events_by_day[day].sort {|x,y| y.event_datetime <=> x.event_datetime }.each do |e| -%>
9 <dt class="<%= e.event_type %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
9 <dt class="<%= e.event_type %>"><span class="time"><%= format_time(e.event_datetime, false) %></span>
10 <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %> <%= link_to h(truncate(e.event_title, 100)), e.event_url %></dt>
10 <%= content_tag('span', h(e.project), :class => 'project') if @project.nil? || @project != e.project %>
11 <%= link_to format_activity_title(e.event_title), e.event_url %></dt>
11 <dd><span class="description"><%= format_activity_description(e.event_description) %></span>
12 <dd><span class="description"><%= format_activity_description(e.event_description) %></span>
12 <span class="author"><%= e.event_author if e.respond_to?(:event_author) %></span></dd>
13 <span class="author"><%= e.event_author if e.respond_to?(:event_author) %></span></dd>
13 <% end -%>
14 <% end -%>
14 </dl>
15 </dl>
15 <% end -%>
16 <% end -%>
16 </div>
17 </div>
17
18
18 <%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
19 <%= content_tag('p', l(:label_no_data), :class => 'nodata') if @events_by_day.empty? %>
19
20
20 <div style="float:left;">
21 <div style="float:left;">
21 <%= link_to_remote(('&#171; ' + l(:label_previous)),
22 <%= link_to_remote(('&#171; ' + l(:label_previous)),
22 {:update => "content", :url => params.merge(:from => @date_to - @days), :complete => 'window.scrollTo(0,0)'},
23 {:update => "content", :url => params.merge(:from => @date_to - @days), :complete => 'window.scrollTo(0,0)'},
23 {:href => url_for(params.merge(:from => @date_to - @days)),
24 {:href => url_for(params.merge(:from => @date_to - @days)),
24 :title => "#{l(:label_date_from)} #{format_date(@date_to - 2*@days)} #{l(:label_date_to).downcase} #{format_date(@date_to - @days - 1)}"}) %>
25 :title => "#{l(:label_date_from)} #{format_date(@date_to - 2*@days)} #{l(:label_date_to).downcase} #{format_date(@date_to - @days - 1)}"}) %>
25 </div>
26 </div>
26 <div style="float:right;">
27 <div style="float:right;">
27 <%= link_to_remote((l(:label_next) + ' &#187;'),
28 <%= link_to_remote((l(:label_next) + ' &#187;'),
28 {:update => "content", :url => params.merge(:from => @date_to + @days), :complete => 'window.scrollTo(0,0)'},
29 {:update => "content", :url => params.merge(:from => @date_to + @days), :complete => 'window.scrollTo(0,0)'},
29 {:href => url_for(params.merge(:from => @date_to + @days)),
30 {:href => url_for(params.merge(:from => @date_to + @days)),
30 :title => "#{l(:label_date_from)} #{format_date(@date_to)} #{l(:label_date_to).downcase} #{format_date(@date_to + @days - 1)}"}) unless @date_to >= Date.today %>
31 :title => "#{l(:label_date_from)} #{format_date(@date_to)} #{l(:label_date_to).downcase} #{format_date(@date_to + @days - 1)}"}) unless @date_to >= Date.today %>
31 </div>
32 </div>
32 &nbsp;
33 &nbsp;
33 <p class="other-formats">
34 <p class="other-formats">
34 <%= l(:label_export_to) %>
35 <%= l(:label_export_to) %>
35 <%= link_to 'Atom', params.merge(:format => :atom, :key => User.current.rss_key).delete_if{|k,v|k=="commit"}, :class => 'feed' %>
36 <%= link_to 'Atom', params.merge(:format => :atom, :key => User.current.rss_key).delete_if{|k,v|k=="commit"}, :class => 'feed' %>
36 </p>
37 </p>
37
38
38 <% content_for :header_tags do %>
39 <% content_for :header_tags do %>
39 <%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :year => nil, :month => nil, :key => User.current.rss_key)) %>
40 <%= auto_discovery_link_tag(:atom, params.merge(:format => 'atom', :year => nil, :month => nil, :key => User.current.rss_key)) %>
40 <% end %>
41 <% end %>
41
42
42 <% content_for :sidebar do %>
43 <% content_for :sidebar do %>
43 <% form_tag({}, :method => :get) do %>
44 <% form_tag({}, :method => :get) do %>
44 <h3><%= l(:label_activity) %></h3>
45 <h3><%= l(:label_activity) %></h3>
45 <p><% @event_types.each do |t| %>
46 <p><% @event_types.each do |t| %>
46 <label><%= check_box_tag "show_#{t}", 1, @scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
47 <label><%= check_box_tag "show_#{t}", 1, @scope.include?(t) %> <%= l("label_#{t.singularize}_plural")%></label><br />
47 <% end %></p>
48 <% end %></p>
48 <% if @project && @project.active_children.any? %>
49 <% if @project && @project.active_children.any? %>
49 <p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
50 <p><label><%= check_box_tag 'with_subprojects', 1, @with_subprojects %> <%=l(:label_subproject_plural)%></label></p>
50 <%= hidden_field_tag 'with_subprojects', 0 %>
51 <%= hidden_field_tag 'with_subprojects', 0 %>
51 <% end %>
52 <% end %>
52 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
53 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
53 <% end %>
54 <% end %>
54 <% end %>
55 <% end %>
55
56
56 <% html_title(l(:label_activity)) -%>
57 <% html_title(l(:label_activity)) -%>
General Comments 0
You need to be logged in to leave comments. Login now