##// END OF EJS Templates
Refactoring ApplicationHelper#link_to_issue....
Jean-Philippe Lang -
r2926:cbeeaa4d4d47
parent child
Show More
@@ -1,680 +1,700
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'coderay'
18 require 'coderay'
19 require 'coderay/helpers/file_type'
19 require 'coderay/helpers/file_type'
20 require 'forwardable'
20 require 'forwardable'
21 require 'cgi'
21 require 'cgi'
22
22
23 module ApplicationHelper
23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions
24 include Redmine::WikiFormatting::Macros::Definitions
25 include Redmine::I18n
25 include Redmine::I18n
26 include GravatarHelper::PublicMethods
26 include GravatarHelper::PublicMethods
27
27
28 extend Forwardable
28 extend Forwardable
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30
30
31 # Return true if user is authorized for controller/action, otherwise false
31 # Return true if user is authorized for controller/action, otherwise false
32 def authorize_for(controller, action)
32 def authorize_for(controller, action)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
34 end
34 end
35
35
36 # Display a link if user is authorized
36 # Display a link if user is authorized
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
39 end
39 end
40
40
41 # Display a link to remote if user is authorized
41 # Display a link to remote if user is authorized
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
43 url = options[:url] || {}
43 url = options[:url] || {}
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
45 end
45 end
46
46
47 # Displays a link to user's account page if active
47 # Displays a link to user's account page if active
48 def link_to_user(user, options={})
48 def link_to_user(user, options={})
49 if user.is_a?(User)
49 if user.is_a?(User)
50 name = h(user.name(options[:format]))
50 name = h(user.name(options[:format]))
51 if user.active?
51 if user.active?
52 link_to name, :controller => 'users', :action => 'show', :id => user
52 link_to name, :controller => 'users', :action => 'show', :id => user
53 else
53 else
54 name
54 name
55 end
55 end
56 else
56 else
57 h(user.to_s)
57 h(user.to_s)
58 end
58 end
59 end
59 end
60
60
61 # Displays a link to +issue+ with its subject.
62 # Examples:
63 #
64 # link_to_issue(issue) # => Defect #6: This is the subject
65 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
66 # link_to_issue(issue, :subject => false) # => Defect #6
67 #
61 def link_to_issue(issue, options={})
68 def link_to_issue(issue, options={})
62 options[:class] ||= issue.css_classes
69 title = nil
63 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
70 subject = nil
71 if options[:subject] == false
72 title = truncate(issue.subject, :length => 60)
73 else
74 subject = issue.subject
75 if options[:truncate]
76 subject = truncate(subject, :length => options[:truncate])
77 end
78 end
79 s = link_to "#{issue.tracker} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
80 :class => issue.css_classes,
81 :title => title
82 s << ": #{h subject}" if subject
83 s
64 end
84 end
65
85
66 # Generates a link to an attachment.
86 # Generates a link to an attachment.
67 # Options:
87 # Options:
68 # * :text - Link text (default to attachment filename)
88 # * :text - Link text (default to attachment filename)
69 # * :download - Force download (default: false)
89 # * :download - Force download (default: false)
70 def link_to_attachment(attachment, options={})
90 def link_to_attachment(attachment, options={})
71 text = options.delete(:text) || attachment.filename
91 text = options.delete(:text) || attachment.filename
72 action = options.delete(:download) ? 'download' : 'show'
92 action = options.delete(:download) ? 'download' : 'show'
73
93
74 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
94 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
75 end
95 end
76
96
77 def toggle_link(name, id, options={})
97 def toggle_link(name, id, options={})
78 onclick = "Element.toggle('#{id}'); "
98 onclick = "Element.toggle('#{id}'); "
79 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
99 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
80 onclick << "return false;"
100 onclick << "return false;"
81 link_to(name, "#", :onclick => onclick)
101 link_to(name, "#", :onclick => onclick)
82 end
102 end
83
103
84 def image_to_function(name, function, html_options = {})
104 def image_to_function(name, function, html_options = {})
85 html_options.symbolize_keys!
105 html_options.symbolize_keys!
86 tag(:input, html_options.merge({
106 tag(:input, html_options.merge({
87 :type => "image", :src => image_path(name),
107 :type => "image", :src => image_path(name),
88 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
108 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
89 }))
109 }))
90 end
110 end
91
111
92 def prompt_to_remote(name, text, param, url, html_options = {})
112 def prompt_to_remote(name, text, param, url, html_options = {})
93 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
113 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
94 link_to name, {}, html_options
114 link_to name, {}, html_options
95 end
115 end
96
116
97 def format_activity_title(text)
117 def format_activity_title(text)
98 h(truncate_single_line(text, :length => 100))
118 h(truncate_single_line(text, :length => 100))
99 end
119 end
100
120
101 def format_activity_day(date)
121 def format_activity_day(date)
102 date == Date.today ? l(:label_today).titleize : format_date(date)
122 date == Date.today ? l(:label_today).titleize : format_date(date)
103 end
123 end
104
124
105 def format_activity_description(text)
125 def format_activity_description(text)
106 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
126 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
107 end
127 end
108
128
109 def due_date_distance_in_words(date)
129 def due_date_distance_in_words(date)
110 if date
130 if date
111 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))
112 end
132 end
113 end
133 end
114
134
115 def render_page_hierarchy(pages, node=nil)
135 def render_page_hierarchy(pages, node=nil)
116 content = ''
136 content = ''
117 if pages[node]
137 if pages[node]
118 content << "<ul class=\"pages-hierarchy\">\n"
138 content << "<ul class=\"pages-hierarchy\">\n"
119 pages[node].each do |page|
139 pages[node].each do |page|
120 content << "<li>"
140 content << "<li>"
121 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},
122 :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))
123 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]
124 content << "</li>\n"
144 content << "</li>\n"
125 end
145 end
126 content << "</ul>\n"
146 content << "</ul>\n"
127 end
147 end
128 content
148 content
129 end
149 end
130
150
131 # Renders flash messages
151 # Renders flash messages
132 def render_flash_messages
152 def render_flash_messages
133 s = ''
153 s = ''
134 flash.each do |k,v|
154 flash.each do |k,v|
135 s << content_tag('div', v, :class => "flash #{k}")
155 s << content_tag('div', v, :class => "flash #{k}")
136 end
156 end
137 s
157 s
138 end
158 end
139
159
140 # Renders tabs and their content
160 # Renders tabs and their content
141 def render_tabs(tabs)
161 def render_tabs(tabs)
142 if tabs.any?
162 if tabs.any?
143 render :partial => 'common/tabs', :locals => {:tabs => tabs}
163 render :partial => 'common/tabs', :locals => {:tabs => tabs}
144 else
164 else
145 content_tag 'p', l(:label_no_data), :class => "nodata"
165 content_tag 'p', l(:label_no_data), :class => "nodata"
146 end
166 end
147 end
167 end
148
168
149 # Renders the project quick-jump box
169 # Renders the project quick-jump box
150 def render_project_jump_box
170 def render_project_jump_box
151 # Retrieve them now to avoid a COUNT query
171 # Retrieve them now to avoid a COUNT query
152 projects = User.current.projects.all
172 projects = User.current.projects.all
153 if projects.any?
173 if projects.any?
154 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
174 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
155 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
175 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
156 '<option value="" disabled="disabled">---</option>'
176 '<option value="" disabled="disabled">---</option>'
157 s << project_tree_options_for_select(projects, :selected => @project) do |p|
177 s << project_tree_options_for_select(projects, :selected => @project) do |p|
158 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
178 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
159 end
179 end
160 s << '</select>'
180 s << '</select>'
161 s
181 s
162 end
182 end
163 end
183 end
164
184
165 def project_tree_options_for_select(projects, options = {})
185 def project_tree_options_for_select(projects, options = {})
166 s = ''
186 s = ''
167 project_tree(projects) do |project, level|
187 project_tree(projects) do |project, level|
168 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
188 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
169 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
189 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
170 tag_options.merge!(yield(project)) if block_given?
190 tag_options.merge!(yield(project)) if block_given?
171 s << content_tag('option', name_prefix + h(project), tag_options)
191 s << content_tag('option', name_prefix + h(project), tag_options)
172 end
192 end
173 s
193 s
174 end
194 end
175
195
176 # Yields the given block for each project with its level in the tree
196 # Yields the given block for each project with its level in the tree
177 def project_tree(projects, &block)
197 def project_tree(projects, &block)
178 ancestors = []
198 ancestors = []
179 projects.sort_by(&:lft).each do |project|
199 projects.sort_by(&:lft).each do |project|
180 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
200 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
181 ancestors.pop
201 ancestors.pop
182 end
202 end
183 yield project, ancestors.size
203 yield project, ancestors.size
184 ancestors << project
204 ancestors << project
185 end
205 end
186 end
206 end
187
207
188 def project_nested_ul(projects, &block)
208 def project_nested_ul(projects, &block)
189 s = ''
209 s = ''
190 if projects.any?
210 if projects.any?
191 ancestors = []
211 ancestors = []
192 projects.sort_by(&:lft).each do |project|
212 projects.sort_by(&:lft).each do |project|
193 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
213 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
194 s << "<ul>\n"
214 s << "<ul>\n"
195 else
215 else
196 ancestors.pop
216 ancestors.pop
197 s << "</li>"
217 s << "</li>"
198 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
218 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
199 ancestors.pop
219 ancestors.pop
200 s << "</ul></li>\n"
220 s << "</ul></li>\n"
201 end
221 end
202 end
222 end
203 s << "<li>"
223 s << "<li>"
204 s << yield(project).to_s
224 s << yield(project).to_s
205 ancestors << project
225 ancestors << project
206 end
226 end
207 s << ("</li></ul>\n" * ancestors.size)
227 s << ("</li></ul>\n" * ancestors.size)
208 end
228 end
209 s
229 s
210 end
230 end
211
231
212 def principals_check_box_tags(name, principals)
232 def principals_check_box_tags(name, principals)
213 s = ''
233 s = ''
214 principals.sort.each do |principal|
234 principals.sort.each do |principal|
215 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
235 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
216 end
236 end
217 s
237 s
218 end
238 end
219
239
220 # Truncates and returns the string as a single line
240 # Truncates and returns the string as a single line
221 def truncate_single_line(string, *args)
241 def truncate_single_line(string, *args)
222 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
242 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
223 end
243 end
224
244
225 def html_hours(text)
245 def html_hours(text)
226 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
246 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
227 end
247 end
228
248
229 def authoring(created, author, options={})
249 def authoring(created, author, options={})
230 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
250 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
231 end
251 end
232
252
233 def time_tag(time)
253 def time_tag(time)
234 text = distance_of_time_in_words(Time.now, time)
254 text = distance_of_time_in_words(Time.now, time)
235 if @project
255 if @project
236 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
256 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
237 else
257 else
238 content_tag('acronym', text, :title => format_time(time))
258 content_tag('acronym', text, :title => format_time(time))
239 end
259 end
240 end
260 end
241
261
242 def syntax_highlight(name, content)
262 def syntax_highlight(name, content)
243 type = CodeRay::FileType[name]
263 type = CodeRay::FileType[name]
244 type ? CodeRay.scan(content, type).html : h(content)
264 type ? CodeRay.scan(content, type).html : h(content)
245 end
265 end
246
266
247 def to_path_param(path)
267 def to_path_param(path)
248 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
268 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
249 end
269 end
250
270
251 def pagination_links_full(paginator, count=nil, options={})
271 def pagination_links_full(paginator, count=nil, options={})
252 page_param = options.delete(:page_param) || :page
272 page_param = options.delete(:page_param) || :page
253 url_param = params.dup
273 url_param = params.dup
254 # don't reuse query params if filters are present
274 # don't reuse query params if filters are present
255 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
275 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
256
276
257 html = ''
277 html = ''
258 if paginator.current.previous
278 if paginator.current.previous
259 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
279 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
260 end
280 end
261
281
262 html << (pagination_links_each(paginator, options) do |n|
282 html << (pagination_links_each(paginator, options) do |n|
263 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
283 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
264 end || '')
284 end || '')
265
285
266 if paginator.current.next
286 if paginator.current.next
267 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
287 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
268 end
288 end
269
289
270 unless count.nil?
290 unless count.nil?
271 html << [
291 html << [
272 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
292 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
273 per_page_links(paginator.items_per_page)
293 per_page_links(paginator.items_per_page)
274 ].compact.join(' | ')
294 ].compact.join(' | ')
275 end
295 end
276
296
277 html
297 html
278 end
298 end
279
299
280 def per_page_links(selected=nil)
300 def per_page_links(selected=nil)
281 url_param = params.dup
301 url_param = params.dup
282 url_param.clear if url_param.has_key?(:set_filter)
302 url_param.clear if url_param.has_key?(:set_filter)
283
303
284 links = Setting.per_page_options_array.collect do |n|
304 links = Setting.per_page_options_array.collect do |n|
285 n == selected ? n : link_to_remote(n, {:update => "content",
305 n == selected ? n : link_to_remote(n, {:update => "content",
286 :url => params.dup.merge(:per_page => n),
306 :url => params.dup.merge(:per_page => n),
287 :method => :get},
307 :method => :get},
288 {:href => url_for(url_param.merge(:per_page => n))})
308 {:href => url_for(url_param.merge(:per_page => n))})
289 end
309 end
290 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
310 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
291 end
311 end
292
312
293 def reorder_links(name, url)
313 def reorder_links(name, url)
294 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
314 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
295 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
315 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
296 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
316 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
297 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
317 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
298 end
318 end
299
319
300 def breadcrumb(*args)
320 def breadcrumb(*args)
301 elements = args.flatten
321 elements = args.flatten
302 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
322 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
303 end
323 end
304
324
305 def other_formats_links(&block)
325 def other_formats_links(&block)
306 concat('<p class="other-formats">' + l(:label_export_to))
326 concat('<p class="other-formats">' + l(:label_export_to))
307 yield Redmine::Views::OtherFormatsBuilder.new(self)
327 yield Redmine::Views::OtherFormatsBuilder.new(self)
308 concat('</p>')
328 concat('</p>')
309 end
329 end
310
330
311 def page_header_title
331 def page_header_title
312 if @project.nil? || @project.new_record?
332 if @project.nil? || @project.new_record?
313 h(Setting.app_title)
333 h(Setting.app_title)
314 else
334 else
315 b = []
335 b = []
316 ancestors = (@project.root? ? [] : @project.ancestors.visible)
336 ancestors = (@project.root? ? [] : @project.ancestors.visible)
317 if ancestors.any?
337 if ancestors.any?
318 root = ancestors.shift
338 root = ancestors.shift
319 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
339 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
320 if ancestors.size > 2
340 if ancestors.size > 2
321 b << '&#8230;'
341 b << '&#8230;'
322 ancestors = ancestors[-2, 2]
342 ancestors = ancestors[-2, 2]
323 end
343 end
324 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
344 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
325 end
345 end
326 b << h(@project)
346 b << h(@project)
327 b.join(' &#187; ')
347 b.join(' &#187; ')
328 end
348 end
329 end
349 end
330
350
331 def html_title(*args)
351 def html_title(*args)
332 if args.empty?
352 if args.empty?
333 title = []
353 title = []
334 title << @project.name if @project
354 title << @project.name if @project
335 title += @html_title if @html_title
355 title += @html_title if @html_title
336 title << Setting.app_title
356 title << Setting.app_title
337 title.select {|t| !t.blank? }.join(' - ')
357 title.select {|t| !t.blank? }.join(' - ')
338 else
358 else
339 @html_title ||= []
359 @html_title ||= []
340 @html_title += args
360 @html_title += args
341 end
361 end
342 end
362 end
343
363
344 def accesskey(s)
364 def accesskey(s)
345 Redmine::AccessKeys.key_for s
365 Redmine::AccessKeys.key_for s
346 end
366 end
347
367
348 # Formats text according to system settings.
368 # Formats text according to system settings.
349 # 2 ways to call this method:
369 # 2 ways to call this method:
350 # * with a String: textilizable(text, options)
370 # * with a String: textilizable(text, options)
351 # * with an object and one of its attribute: textilizable(issue, :description, options)
371 # * with an object and one of its attribute: textilizable(issue, :description, options)
352 def textilizable(*args)
372 def textilizable(*args)
353 options = args.last.is_a?(Hash) ? args.pop : {}
373 options = args.last.is_a?(Hash) ? args.pop : {}
354 case args.size
374 case args.size
355 when 1
375 when 1
356 obj = options[:object]
376 obj = options[:object]
357 text = args.shift
377 text = args.shift
358 when 2
378 when 2
359 obj = args.shift
379 obj = args.shift
360 text = obj.send(args.shift).to_s
380 text = obj.send(args.shift).to_s
361 else
381 else
362 raise ArgumentError, 'invalid arguments to textilizable'
382 raise ArgumentError, 'invalid arguments to textilizable'
363 end
383 end
364 return '' if text.blank?
384 return '' if text.blank?
365
385
366 only_path = options.delete(:only_path) == false ? false : true
386 only_path = options.delete(:only_path) == false ? false : true
367
387
368 # when using an image link, try to use an attachment, if possible
388 # when using an image link, try to use an attachment, if possible
369 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
389 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
370
390
371 if attachments
391 if attachments
372 attachments = attachments.sort_by(&:created_on).reverse
392 attachments = attachments.sort_by(&:created_on).reverse
373 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
393 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
374 style = $1
394 style = $1
375 filename = $6.downcase
395 filename = $6.downcase
376 # search for the picture in attachments
396 # search for the picture in attachments
377 if found = attachments.detect { |att| att.filename.downcase == filename }
397 if found = attachments.detect { |att| att.filename.downcase == filename }
378 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
398 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
379 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
399 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
380 alt = desc.blank? ? nil : "(#{desc})"
400 alt = desc.blank? ? nil : "(#{desc})"
381 "!#{style}#{image_url}#{alt}!"
401 "!#{style}#{image_url}#{alt}!"
382 else
402 else
383 m
403 m
384 end
404 end
385 end
405 end
386 end
406 end
387
407
388 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
408 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
389
409
390 # different methods for formatting wiki links
410 # different methods for formatting wiki links
391 case options[:wiki_links]
411 case options[:wiki_links]
392 when :local
412 when :local
393 # used for local links to html files
413 # used for local links to html files
394 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
414 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
395 when :anchor
415 when :anchor
396 # used for single-file wiki export
416 # used for single-file wiki export
397 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
417 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
398 else
418 else
399 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
419 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
400 end
420 end
401
421
402 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
422 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
403
423
404 # Wiki links
424 # Wiki links
405 #
425 #
406 # Examples:
426 # Examples:
407 # [[mypage]]
427 # [[mypage]]
408 # [[mypage|mytext]]
428 # [[mypage|mytext]]
409 # wiki links can refer other project wikis, using project name or identifier:
429 # wiki links can refer other project wikis, using project name or identifier:
410 # [[project:]] -> wiki starting page
430 # [[project:]] -> wiki starting page
411 # [[project:|mytext]]
431 # [[project:|mytext]]
412 # [[project:mypage]]
432 # [[project:mypage]]
413 # [[project:mypage|mytext]]
433 # [[project:mypage|mytext]]
414 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
434 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
415 link_project = project
435 link_project = project
416 esc, all, page, title = $1, $2, $3, $5
436 esc, all, page, title = $1, $2, $3, $5
417 if esc.nil?
437 if esc.nil?
418 if page =~ /^([^\:]+)\:(.*)$/
438 if page =~ /^([^\:]+)\:(.*)$/
419 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
439 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
420 page = $2
440 page = $2
421 title ||= $1 if page.blank?
441 title ||= $1 if page.blank?
422 end
442 end
423
443
424 if link_project && link_project.wiki
444 if link_project && link_project.wiki
425 # extract anchor
445 # extract anchor
426 anchor = nil
446 anchor = nil
427 if page =~ /^(.+?)\#(.+)$/
447 if page =~ /^(.+?)\#(.+)$/
428 page, anchor = $1, $2
448 page, anchor = $1, $2
429 end
449 end
430 # check if page exists
450 # check if page exists
431 wiki_page = link_project.wiki.find_page(page)
451 wiki_page = link_project.wiki.find_page(page)
432 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
452 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
433 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
453 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
434 else
454 else
435 # project or wiki doesn't exist
455 # project or wiki doesn't exist
436 all
456 all
437 end
457 end
438 else
458 else
439 all
459 all
440 end
460 end
441 end
461 end
442
462
443 # Redmine links
463 # Redmine links
444 #
464 #
445 # Examples:
465 # Examples:
446 # Issues:
466 # Issues:
447 # #52 -> Link to issue #52
467 # #52 -> Link to issue #52
448 # Changesets:
468 # Changesets:
449 # r52 -> Link to revision 52
469 # r52 -> Link to revision 52
450 # commit:a85130f -> Link to scmid starting with a85130f
470 # commit:a85130f -> Link to scmid starting with a85130f
451 # Documents:
471 # Documents:
452 # document#17 -> Link to document with id 17
472 # document#17 -> Link to document with id 17
453 # document:Greetings -> Link to the document with title "Greetings"
473 # document:Greetings -> Link to the document with title "Greetings"
454 # document:"Some document" -> Link to the document with title "Some document"
474 # document:"Some document" -> Link to the document with title "Some document"
455 # Versions:
475 # Versions:
456 # version#3 -> Link to version with id 3
476 # version#3 -> Link to version with id 3
457 # version:1.0.0 -> Link to version named "1.0.0"
477 # version:1.0.0 -> Link to version named "1.0.0"
458 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
478 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
459 # Attachments:
479 # Attachments:
460 # attachment:file.zip -> Link to the attachment of the current object named file.zip
480 # attachment:file.zip -> Link to the attachment of the current object named file.zip
461 # Source files:
481 # Source files:
462 # source:some/file -> Link to the file located at /some/file in the project's repository
482 # source:some/file -> Link to the file located at /some/file in the project's repository
463 # source:some/file@52 -> Link to the file's revision 52
483 # source:some/file@52 -> Link to the file's revision 52
464 # source:some/file#L120 -> Link to line 120 of the file
484 # source:some/file#L120 -> Link to line 120 of the file
465 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
485 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
466 # export:some/file -> Force the download of the file
486 # export:some/file -> Force the download of the file
467 # Forum messages:
487 # Forum messages:
468 # message#1218 -> Link to message with id 1218
488 # message#1218 -> Link to message with id 1218
469 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
489 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
470 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
490 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
471 link = nil
491 link = nil
472 if esc.nil?
492 if esc.nil?
473 if prefix.nil? && sep == 'r'
493 if prefix.nil? && sep == 'r'
474 if project && (changeset = project.changesets.find_by_revision(oid))
494 if project && (changeset = project.changesets.find_by_revision(oid))
475 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
495 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
476 :class => 'changeset',
496 :class => 'changeset',
477 :title => truncate_single_line(changeset.comments, :length => 100))
497 :title => truncate_single_line(changeset.comments, :length => 100))
478 end
498 end
479 elsif sep == '#'
499 elsif sep == '#'
480 oid = oid.to_i
500 oid = oid.to_i
481 case prefix
501 case prefix
482 when nil
502 when nil
483 if issue = Issue.visible.find_by_id(oid, :include => :status)
503 if issue = Issue.visible.find_by_id(oid, :include => :status)
484 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
504 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
485 :class => (issue.closed? ? 'issue closed' : 'issue'),
505 :class => (issue.closed? ? 'issue closed' : 'issue'),
486 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
506 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
487 link = content_tag('del', link) if issue.closed?
507 link = content_tag('del', link) if issue.closed?
488 end
508 end
489 when 'document'
509 when 'document'
490 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
510 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
491 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
511 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
492 :class => 'document'
512 :class => 'document'
493 end
513 end
494 when 'version'
514 when 'version'
495 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
515 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
496 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
516 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
497 :class => 'version'
517 :class => 'version'
498 end
518 end
499 when 'message'
519 when 'message'
500 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
520 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
501 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
521 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
502 :controller => 'messages',
522 :controller => 'messages',
503 :action => 'show',
523 :action => 'show',
504 :board_id => message.board,
524 :board_id => message.board,
505 :id => message.root,
525 :id => message.root,
506 :anchor => (message.parent ? "message-#{message.id}" : nil)},
526 :anchor => (message.parent ? "message-#{message.id}" : nil)},
507 :class => 'message'
527 :class => 'message'
508 end
528 end
509 end
529 end
510 elsif sep == ':'
530 elsif sep == ':'
511 # removes the double quotes if any
531 # removes the double quotes if any
512 name = oid.gsub(%r{^"(.*)"$}, "\\1")
532 name = oid.gsub(%r{^"(.*)"$}, "\\1")
513 case prefix
533 case prefix
514 when 'document'
534 when 'document'
515 if project && document = project.documents.find_by_title(name)
535 if project && document = project.documents.find_by_title(name)
516 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
536 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
517 :class => 'document'
537 :class => 'document'
518 end
538 end
519 when 'version'
539 when 'version'
520 if project && version = project.versions.find_by_name(name)
540 if project && version = project.versions.find_by_name(name)
521 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
541 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
522 :class => 'version'
542 :class => 'version'
523 end
543 end
524 when 'commit'
544 when 'commit'
525 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
545 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
526 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
546 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
527 :class => 'changeset',
547 :class => 'changeset',
528 :title => truncate_single_line(changeset.comments, :length => 100)
548 :title => truncate_single_line(changeset.comments, :length => 100)
529 end
549 end
530 when 'source', 'export'
550 when 'source', 'export'
531 if project && project.repository
551 if project && project.repository
532 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
552 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
533 path, rev, anchor = $1, $3, $5
553 path, rev, anchor = $1, $3, $5
534 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
554 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
535 :path => to_path_param(path),
555 :path => to_path_param(path),
536 :rev => rev,
556 :rev => rev,
537 :anchor => anchor,
557 :anchor => anchor,
538 :format => (prefix == 'export' ? 'raw' : nil)},
558 :format => (prefix == 'export' ? 'raw' : nil)},
539 :class => (prefix == 'export' ? 'source download' : 'source')
559 :class => (prefix == 'export' ? 'source download' : 'source')
540 end
560 end
541 when 'attachment'
561 when 'attachment'
542 if attachments && attachment = attachments.detect {|a| a.filename == name }
562 if attachments && attachment = attachments.detect {|a| a.filename == name }
543 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
563 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
544 :class => 'attachment'
564 :class => 'attachment'
545 end
565 end
546 end
566 end
547 end
567 end
548 end
568 end
549 leading + (link || "#{prefix}#{sep}#{oid}")
569 leading + (link || "#{prefix}#{sep}#{oid}")
550 end
570 end
551
571
552 text
572 text
553 end
573 end
554
574
555 # Same as Rails' simple_format helper without using paragraphs
575 # Same as Rails' simple_format helper without using paragraphs
556 def simple_format_without_paragraph(text)
576 def simple_format_without_paragraph(text)
557 text.to_s.
577 text.to_s.
558 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
578 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
559 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
579 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
560 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
580 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
561 end
581 end
562
582
563 def lang_options_for_select(blank=true)
583 def lang_options_for_select(blank=true)
564 (blank ? [["(auto)", ""]] : []) +
584 (blank ? [["(auto)", ""]] : []) +
565 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
585 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
566 end
586 end
567
587
568 def label_tag_for(name, option_tags = nil, options = {})
588 def label_tag_for(name, option_tags = nil, options = {})
569 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
589 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
570 content_tag("label", label_text)
590 content_tag("label", label_text)
571 end
591 end
572
592
573 def labelled_tabular_form_for(name, object, options, &proc)
593 def labelled_tabular_form_for(name, object, options, &proc)
574 options[:html] ||= {}
594 options[:html] ||= {}
575 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
595 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
576 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
596 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
577 end
597 end
578
598
579 def back_url_hidden_field_tag
599 def back_url_hidden_field_tag
580 back_url = params[:back_url] || request.env['HTTP_REFERER']
600 back_url = params[:back_url] || request.env['HTTP_REFERER']
581 back_url = CGI.unescape(back_url.to_s)
601 back_url = CGI.unescape(back_url.to_s)
582 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
602 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
583 end
603 end
584
604
585 def check_all_links(form_name)
605 def check_all_links(form_name)
586 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
606 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
587 " | " +
607 " | " +
588 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
608 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
589 end
609 end
590
610
591 def progress_bar(pcts, options={})
611 def progress_bar(pcts, options={})
592 pcts = [pcts, pcts] unless pcts.is_a?(Array)
612 pcts = [pcts, pcts] unless pcts.is_a?(Array)
593 pcts[1] = pcts[1] - pcts[0]
613 pcts[1] = pcts[1] - pcts[0]
594 pcts << (100 - pcts[1] - pcts[0])
614 pcts << (100 - pcts[1] - pcts[0])
595 width = options[:width] || '100px;'
615 width = options[:width] || '100px;'
596 legend = options[:legend] || ''
616 legend = options[:legend] || ''
597 content_tag('table',
617 content_tag('table',
598 content_tag('tr',
618 content_tag('tr',
599 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
619 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
600 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
620 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
601 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
621 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
602 ), :class => 'progress', :style => "width: #{width};") +
622 ), :class => 'progress', :style => "width: #{width};") +
603 content_tag('p', legend, :class => 'pourcent')
623 content_tag('p', legend, :class => 'pourcent')
604 end
624 end
605
625
606 def context_menu_link(name, url, options={})
626 def context_menu_link(name, url, options={})
607 options[:class] ||= ''
627 options[:class] ||= ''
608 if options.delete(:selected)
628 if options.delete(:selected)
609 options[:class] << ' icon-checked disabled'
629 options[:class] << ' icon-checked disabled'
610 options[:disabled] = true
630 options[:disabled] = true
611 end
631 end
612 if options.delete(:disabled)
632 if options.delete(:disabled)
613 options.delete(:method)
633 options.delete(:method)
614 options.delete(:confirm)
634 options.delete(:confirm)
615 options.delete(:onclick)
635 options.delete(:onclick)
616 options[:class] << ' disabled'
636 options[:class] << ' disabled'
617 url = '#'
637 url = '#'
618 end
638 end
619 link_to name, url, options
639 link_to name, url, options
620 end
640 end
621
641
622 def calendar_for(field_id)
642 def calendar_for(field_id)
623 include_calendar_headers_tags
643 include_calendar_headers_tags
624 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
644 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
625 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
645 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
626 end
646 end
627
647
628 def include_calendar_headers_tags
648 def include_calendar_headers_tags
629 unless @calendar_headers_tags_included
649 unless @calendar_headers_tags_included
630 @calendar_headers_tags_included = true
650 @calendar_headers_tags_included = true
631 content_for :header_tags do
651 content_for :header_tags do
632 javascript_include_tag('calendar/calendar') +
652 javascript_include_tag('calendar/calendar') +
633 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
653 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
634 javascript_include_tag('calendar/calendar-setup') +
654 javascript_include_tag('calendar/calendar-setup') +
635 stylesheet_link_tag('calendar')
655 stylesheet_link_tag('calendar')
636 end
656 end
637 end
657 end
638 end
658 end
639
659
640 def content_for(name, content = nil, &block)
660 def content_for(name, content = nil, &block)
641 @has_content ||= {}
661 @has_content ||= {}
642 @has_content[name] = true
662 @has_content[name] = true
643 super(name, content, &block)
663 super(name, content, &block)
644 end
664 end
645
665
646 def has_content?(name)
666 def has_content?(name)
647 (@has_content && @has_content[name]) || false
667 (@has_content && @has_content[name]) || false
648 end
668 end
649
669
650 # Returns the avatar image tag for the given +user+ if avatars are enabled
670 # Returns the avatar image tag for the given +user+ if avatars are enabled
651 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
671 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
652 def avatar(user, options = { })
672 def avatar(user, options = { })
653 if Setting.gravatar_enabled?
673 if Setting.gravatar_enabled?
654 options.merge!({:ssl => Setting.protocol == 'https'})
674 options.merge!({:ssl => Setting.protocol == 'https'})
655 email = nil
675 email = nil
656 if user.respond_to?(:mail)
676 if user.respond_to?(:mail)
657 email = user.mail
677 email = user.mail
658 elsif user.to_s =~ %r{<(.+?)>}
678 elsif user.to_s =~ %r{<(.+?)>}
659 email = $1
679 email = $1
660 end
680 end
661 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
681 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
662 end
682 end
663 end
683 end
664
684
665 private
685 private
666
686
667 def wiki_helper
687 def wiki_helper
668 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
688 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
669 extend helper
689 extend helper
670 return self
690 return self
671 end
691 end
672
692
673 def link_to_remote_content_update(text, url_params)
693 def link_to_remote_content_update(text, url_params)
674 link_to_remote(text,
694 link_to_remote(text,
675 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
695 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
676 {:href => url_for(:params => url_params)}
696 {:href => url_for(:params => url_params)}
677 )
697 )
678 end
698 end
679
699
680 end
700 end
@@ -1,199 +1,199
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 IssuesHelper
18 module IssuesHelper
19 include ApplicationHelper
19 include ApplicationHelper
20
20
21 def render_issue_tooltip(issue)
21 def render_issue_tooltip(issue)
22 @cached_label_start_date ||= l(:field_start_date)
22 @cached_label_start_date ||= l(:field_start_date)
23 @cached_label_due_date ||= l(:field_due_date)
23 @cached_label_due_date ||= l(:field_due_date)
24 @cached_label_assigned_to ||= l(:field_assigned_to)
24 @cached_label_assigned_to ||= l(:field_assigned_to)
25 @cached_label_priority ||= l(:field_priority)
25 @cached_label_priority ||= l(:field_priority)
26
26
27 link_to_issue(issue) + ": #{h(issue.subject)}<br /><br />" +
27 link_to_issue(issue) + "<br /><br />" +
28 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
28 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
29 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
29 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
30 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
30 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
31 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
31 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
32 end
32 end
33
33
34 def render_custom_fields_rows(issue)
34 def render_custom_fields_rows(issue)
35 return if issue.custom_field_values.empty?
35 return if issue.custom_field_values.empty?
36 ordered_values = []
36 ordered_values = []
37 half = (issue.custom_field_values.size / 2.0).ceil
37 half = (issue.custom_field_values.size / 2.0).ceil
38 half.times do |i|
38 half.times do |i|
39 ordered_values << issue.custom_field_values[i]
39 ordered_values << issue.custom_field_values[i]
40 ordered_values << issue.custom_field_values[i + half]
40 ordered_values << issue.custom_field_values[i + half]
41 end
41 end
42 s = "<tr>\n"
42 s = "<tr>\n"
43 n = 0
43 n = 0
44 ordered_values.compact.each do |value|
44 ordered_values.compact.each do |value|
45 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
45 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
46 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
46 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
47 n += 1
47 n += 1
48 end
48 end
49 s << "</tr>\n"
49 s << "</tr>\n"
50 s
50 s
51 end
51 end
52
52
53 def sidebar_queries
53 def sidebar_queries
54 unless @sidebar_queries
54 unless @sidebar_queries
55 # User can see public queries and his own queries
55 # User can see public queries and his own queries
56 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
56 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
57 # Project specific queries and global queries
57 # Project specific queries and global queries
58 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
58 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
59 @sidebar_queries = Query.find(:all,
59 @sidebar_queries = Query.find(:all,
60 :select => 'id, name',
60 :select => 'id, name',
61 :order => "name ASC",
61 :order => "name ASC",
62 :conditions => visible.conditions)
62 :conditions => visible.conditions)
63 end
63 end
64 @sidebar_queries
64 @sidebar_queries
65 end
65 end
66
66
67 def show_detail(detail, no_html=false)
67 def show_detail(detail, no_html=false)
68 case detail.property
68 case detail.property
69 when 'attr'
69 when 'attr'
70 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
70 label = l(("field_" + detail.prop_key.to_s.gsub(/\_id$/, "")).to_sym)
71 case detail.prop_key
71 case detail.prop_key
72 when 'due_date', 'start_date'
72 when 'due_date', 'start_date'
73 value = format_date(detail.value.to_date) if detail.value
73 value = format_date(detail.value.to_date) if detail.value
74 old_value = format_date(detail.old_value.to_date) if detail.old_value
74 old_value = format_date(detail.old_value.to_date) if detail.old_value
75 when 'project_id'
75 when 'project_id'
76 p = Project.find_by_id(detail.value) and value = p.name if detail.value
76 p = Project.find_by_id(detail.value) and value = p.name if detail.value
77 p = Project.find_by_id(detail.old_value) and old_value = p.name if detail.old_value
77 p = Project.find_by_id(detail.old_value) and old_value = p.name if detail.old_value
78 when 'status_id'
78 when 'status_id'
79 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
79 s = IssueStatus.find_by_id(detail.value) and value = s.name if detail.value
80 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
80 s = IssueStatus.find_by_id(detail.old_value) and old_value = s.name if detail.old_value
81 when 'tracker_id'
81 when 'tracker_id'
82 t = Tracker.find_by_id(detail.value) and value = t.name if detail.value
82 t = Tracker.find_by_id(detail.value) and value = t.name if detail.value
83 t = Tracker.find_by_id(detail.old_value) and old_value = t.name if detail.old_value
83 t = Tracker.find_by_id(detail.old_value) and old_value = t.name if detail.old_value
84 when 'assigned_to_id'
84 when 'assigned_to_id'
85 u = User.find_by_id(detail.value) and value = u.name if detail.value
85 u = User.find_by_id(detail.value) and value = u.name if detail.value
86 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
86 u = User.find_by_id(detail.old_value) and old_value = u.name if detail.old_value
87 when 'priority_id'
87 when 'priority_id'
88 e = IssuePriority.find_by_id(detail.value) and value = e.name if detail.value
88 e = IssuePriority.find_by_id(detail.value) and value = e.name if detail.value
89 e = IssuePriority.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
89 e = IssuePriority.find_by_id(detail.old_value) and old_value = e.name if detail.old_value
90 when 'category_id'
90 when 'category_id'
91 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
91 c = IssueCategory.find_by_id(detail.value) and value = c.name if detail.value
92 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
92 c = IssueCategory.find_by_id(detail.old_value) and old_value = c.name if detail.old_value
93 when 'fixed_version_id'
93 when 'fixed_version_id'
94 v = Version.find_by_id(detail.value) and value = v.name if detail.value
94 v = Version.find_by_id(detail.value) and value = v.name if detail.value
95 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
95 v = Version.find_by_id(detail.old_value) and old_value = v.name if detail.old_value
96 when 'estimated_hours'
96 when 'estimated_hours'
97 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
97 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
98 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
98 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
99 end
99 end
100 when 'cf'
100 when 'cf'
101 custom_field = CustomField.find_by_id(detail.prop_key)
101 custom_field = CustomField.find_by_id(detail.prop_key)
102 if custom_field
102 if custom_field
103 label = custom_field.name
103 label = custom_field.name
104 value = format_value(detail.value, custom_field.field_format) if detail.value
104 value = format_value(detail.value, custom_field.field_format) if detail.value
105 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
105 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
106 end
106 end
107 when 'attachment'
107 when 'attachment'
108 label = l(:label_attachment)
108 label = l(:label_attachment)
109 end
109 end
110 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
110 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
111
111
112 label ||= detail.prop_key
112 label ||= detail.prop_key
113 value ||= detail.value
113 value ||= detail.value
114 old_value ||= detail.old_value
114 old_value ||= detail.old_value
115
115
116 unless no_html
116 unless no_html
117 label = content_tag('strong', label)
117 label = content_tag('strong', label)
118 old_value = content_tag("i", h(old_value)) if detail.old_value
118 old_value = content_tag("i", h(old_value)) if detail.old_value
119 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
119 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
120 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
120 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
121 # Link to the attachment if it has not been removed
121 # Link to the attachment if it has not been removed
122 value = link_to_attachment(a)
122 value = link_to_attachment(a)
123 else
123 else
124 value = content_tag("i", h(value)) if value
124 value = content_tag("i", h(value)) if value
125 end
125 end
126 end
126 end
127
127
128 if !detail.value.blank?
128 if !detail.value.blank?
129 case detail.property
129 case detail.property
130 when 'attr', 'cf'
130 when 'attr', 'cf'
131 if !detail.old_value.blank?
131 if !detail.old_value.blank?
132 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
132 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
133 else
133 else
134 l(:text_journal_set_to, :label => label, :value => value)
134 l(:text_journal_set_to, :label => label, :value => value)
135 end
135 end
136 when 'attachment'
136 when 'attachment'
137 l(:text_journal_added, :label => label, :value => value)
137 l(:text_journal_added, :label => label, :value => value)
138 end
138 end
139 else
139 else
140 l(:text_journal_deleted, :label => label, :old => old_value)
140 l(:text_journal_deleted, :label => label, :old => old_value)
141 end
141 end
142 end
142 end
143
143
144 def issues_to_csv(issues, project = nil)
144 def issues_to_csv(issues, project = nil)
145 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
145 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
146 decimal_separator = l(:general_csv_decimal_separator)
146 decimal_separator = l(:general_csv_decimal_separator)
147 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
147 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
148 # csv header fields
148 # csv header fields
149 headers = [ "#",
149 headers = [ "#",
150 l(:field_status),
150 l(:field_status),
151 l(:field_project),
151 l(:field_project),
152 l(:field_tracker),
152 l(:field_tracker),
153 l(:field_priority),
153 l(:field_priority),
154 l(:field_subject),
154 l(:field_subject),
155 l(:field_assigned_to),
155 l(:field_assigned_to),
156 l(:field_category),
156 l(:field_category),
157 l(:field_fixed_version),
157 l(:field_fixed_version),
158 l(:field_author),
158 l(:field_author),
159 l(:field_start_date),
159 l(:field_start_date),
160 l(:field_due_date),
160 l(:field_due_date),
161 l(:field_done_ratio),
161 l(:field_done_ratio),
162 l(:field_estimated_hours),
162 l(:field_estimated_hours),
163 l(:field_created_on),
163 l(:field_created_on),
164 l(:field_updated_on)
164 l(:field_updated_on)
165 ]
165 ]
166 # Export project custom fields if project is given
166 # Export project custom fields if project is given
167 # otherwise export custom fields marked as "For all projects"
167 # otherwise export custom fields marked as "For all projects"
168 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
168 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
169 custom_fields.each {|f| headers << f.name}
169 custom_fields.each {|f| headers << f.name}
170 # Description in the last column
170 # Description in the last column
171 headers << l(:field_description)
171 headers << l(:field_description)
172 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
172 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
173 # csv lines
173 # csv lines
174 issues.each do |issue|
174 issues.each do |issue|
175 fields = [issue.id,
175 fields = [issue.id,
176 issue.status.name,
176 issue.status.name,
177 issue.project.name,
177 issue.project.name,
178 issue.tracker.name,
178 issue.tracker.name,
179 issue.priority.name,
179 issue.priority.name,
180 issue.subject,
180 issue.subject,
181 issue.assigned_to,
181 issue.assigned_to,
182 issue.category,
182 issue.category,
183 issue.fixed_version,
183 issue.fixed_version,
184 issue.author.name,
184 issue.author.name,
185 format_date(issue.start_date),
185 format_date(issue.start_date),
186 format_date(issue.due_date),
186 format_date(issue.due_date),
187 issue.done_ratio,
187 issue.done_ratio,
188 issue.estimated_hours.to_s.gsub('.', decimal_separator),
188 issue.estimated_hours.to_s.gsub('.', decimal_separator),
189 format_time(issue.created_on),
189 format_time(issue.created_on),
190 format_time(issue.updated_on)
190 format_time(issue.updated_on)
191 ]
191 ]
192 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
192 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
193 fields << issue.description
193 fields << issue.description
194 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
194 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
195 end
195 end
196 end
196 end
197 export
197 export
198 end
198 end
199 end
199 end
@@ -1,173 +1,173
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 TimelogHelper
18 module TimelogHelper
19 include ApplicationHelper
19 include ApplicationHelper
20
20
21 def render_timelog_breadcrumb
21 def render_timelog_breadcrumb
22 links = []
22 links = []
23 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
23 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
24 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
24 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
25 links << link_to_issue(@issue) if @issue
25 links << link_to_issue(@issue, :subject => false) if @issue
26 breadcrumb links
26 breadcrumb links
27 end
27 end
28
28
29 # Returns a collection of activities for a select field. time_entry
29 # Returns a collection of activities for a select field. time_entry
30 # is optional and will be used to check if the selected TimeEntryActivity
30 # is optional and will be used to check if the selected TimeEntryActivity
31 # is active.
31 # is active.
32 def activity_collection_for_select_options(time_entry=nil, project=nil)
32 def activity_collection_for_select_options(time_entry=nil, project=nil)
33 project ||= @project
33 project ||= @project
34 if project.nil?
34 if project.nil?
35 activities = TimeEntryActivity.active
35 activities = TimeEntryActivity.active
36 else
36 else
37 activities = project.activities
37 activities = project.activities
38 end
38 end
39
39
40 collection = []
40 collection = []
41 if time_entry && time_entry.activity && !time_entry.activity.active?
41 if time_entry && time_entry.activity && !time_entry.activity.active?
42 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
42 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
43 else
43 else
44 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
44 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
45 end
45 end
46 activities.each { |a| collection << [a.name, a.id] }
46 activities.each { |a| collection << [a.name, a.id] }
47 collection
47 collection
48 end
48 end
49
49
50 def select_hours(data, criteria, value)
50 def select_hours(data, criteria, value)
51 if value.to_s.empty?
51 if value.to_s.empty?
52 data.select {|row| row[criteria].blank? }
52 data.select {|row| row[criteria].blank? }
53 else
53 else
54 data.select {|row| row[criteria] == value}
54 data.select {|row| row[criteria] == value}
55 end
55 end
56 end
56 end
57
57
58 def sum_hours(data)
58 def sum_hours(data)
59 sum = 0
59 sum = 0
60 data.each do |row|
60 data.each do |row|
61 sum += row['hours'].to_f
61 sum += row['hours'].to_f
62 end
62 end
63 sum
63 sum
64 end
64 end
65
65
66 def options_for_period_select(value)
66 def options_for_period_select(value)
67 options_for_select([[l(:label_all_time), 'all'],
67 options_for_select([[l(:label_all_time), 'all'],
68 [l(:label_today), 'today'],
68 [l(:label_today), 'today'],
69 [l(:label_yesterday), 'yesterday'],
69 [l(:label_yesterday), 'yesterday'],
70 [l(:label_this_week), 'current_week'],
70 [l(:label_this_week), 'current_week'],
71 [l(:label_last_week), 'last_week'],
71 [l(:label_last_week), 'last_week'],
72 [l(:label_last_n_days, 7), '7_days'],
72 [l(:label_last_n_days, 7), '7_days'],
73 [l(:label_this_month), 'current_month'],
73 [l(:label_this_month), 'current_month'],
74 [l(:label_last_month), 'last_month'],
74 [l(:label_last_month), 'last_month'],
75 [l(:label_last_n_days, 30), '30_days'],
75 [l(:label_last_n_days, 30), '30_days'],
76 [l(:label_this_year), 'current_year']],
76 [l(:label_this_year), 'current_year']],
77 value)
77 value)
78 end
78 end
79
79
80 def entries_to_csv(entries)
80 def entries_to_csv(entries)
81 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
81 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
82 decimal_separator = l(:general_csv_decimal_separator)
82 decimal_separator = l(:general_csv_decimal_separator)
83 custom_fields = TimeEntryCustomField.find(:all)
83 custom_fields = TimeEntryCustomField.find(:all)
84 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
84 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
85 # csv header fields
85 # csv header fields
86 headers = [l(:field_spent_on),
86 headers = [l(:field_spent_on),
87 l(:field_user),
87 l(:field_user),
88 l(:field_activity),
88 l(:field_activity),
89 l(:field_project),
89 l(:field_project),
90 l(:field_issue),
90 l(:field_issue),
91 l(:field_tracker),
91 l(:field_tracker),
92 l(:field_subject),
92 l(:field_subject),
93 l(:field_hours),
93 l(:field_hours),
94 l(:field_comments)
94 l(:field_comments)
95 ]
95 ]
96 # Export custom fields
96 # Export custom fields
97 headers += custom_fields.collect(&:name)
97 headers += custom_fields.collect(&:name)
98
98
99 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
99 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
100 # csv lines
100 # csv lines
101 entries.each do |entry|
101 entries.each do |entry|
102 fields = [format_date(entry.spent_on),
102 fields = [format_date(entry.spent_on),
103 entry.user,
103 entry.user,
104 entry.activity,
104 entry.activity,
105 entry.project,
105 entry.project,
106 (entry.issue ? entry.issue.id : nil),
106 (entry.issue ? entry.issue.id : nil),
107 (entry.issue ? entry.issue.tracker : nil),
107 (entry.issue ? entry.issue.tracker : nil),
108 (entry.issue ? entry.issue.subject : nil),
108 (entry.issue ? entry.issue.subject : nil),
109 entry.hours.to_s.gsub('.', decimal_separator),
109 entry.hours.to_s.gsub('.', decimal_separator),
110 entry.comments
110 entry.comments
111 ]
111 ]
112 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
112 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
113
113
114 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
114 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
115 end
115 end
116 end
116 end
117 export
117 export
118 end
118 end
119
119
120 def format_criteria_value(criteria, value)
120 def format_criteria_value(criteria, value)
121 value.blank? ? l(:label_none) : ((k = @available_criterias[criteria][:klass]) ? k.find_by_id(value.to_i) : format_value(value, @available_criterias[criteria][:format]))
121 value.blank? ? l(:label_none) : ((k = @available_criterias[criteria][:klass]) ? k.find_by_id(value.to_i) : format_value(value, @available_criterias[criteria][:format]))
122 end
122 end
123
123
124 def report_to_csv(criterias, periods, hours)
124 def report_to_csv(criterias, periods, hours)
125 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
125 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
126 # Column headers
126 # Column headers
127 headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
127 headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
128 headers += periods
128 headers += periods
129 headers << l(:label_total)
129 headers << l(:label_total)
130 csv << headers.collect {|c| to_utf8(c) }
130 csv << headers.collect {|c| to_utf8(c) }
131 # Content
131 # Content
132 report_criteria_to_csv(csv, criterias, periods, hours)
132 report_criteria_to_csv(csv, criterias, periods, hours)
133 # Total row
133 # Total row
134 row = [ l(:label_total) ] + [''] * (criterias.size - 1)
134 row = [ l(:label_total) ] + [''] * (criterias.size - 1)
135 total = 0
135 total = 0
136 periods.each do |period|
136 periods.each do |period|
137 sum = sum_hours(select_hours(hours, @columns, period.to_s))
137 sum = sum_hours(select_hours(hours, @columns, period.to_s))
138 total += sum
138 total += sum
139 row << (sum > 0 ? "%.2f" % sum : '')
139 row << (sum > 0 ? "%.2f" % sum : '')
140 end
140 end
141 row << "%.2f" %total
141 row << "%.2f" %total
142 csv << row
142 csv << row
143 end
143 end
144 export
144 export
145 end
145 end
146
146
147 def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
147 def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
148 hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
148 hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
149 hours_for_value = select_hours(hours, criterias[level], value)
149 hours_for_value = select_hours(hours, criterias[level], value)
150 next if hours_for_value.empty?
150 next if hours_for_value.empty?
151 row = [''] * level
151 row = [''] * level
152 row << to_utf8(format_criteria_value(criterias[level], value))
152 row << to_utf8(format_criteria_value(criterias[level], value))
153 row += [''] * (criterias.length - level - 1)
153 row += [''] * (criterias.length - level - 1)
154 total = 0
154 total = 0
155 periods.each do |period|
155 periods.each do |period|
156 sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
156 sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
157 total += sum
157 total += sum
158 row << (sum > 0 ? "%.2f" % sum : '')
158 row << (sum > 0 ? "%.2f" % sum : '')
159 end
159 end
160 row << "%.2f" %total
160 row << "%.2f" %total
161 csv << row
161 csv << row
162
162
163 if criterias.length > level + 1
163 if criterias.length > level + 1
164 report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
164 report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
165 end
165 end
166 end
166 end
167 end
167 end
168
168
169 def to_utf8(s)
169 def to_utf8(s)
170 @ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
170 @ic ||= Iconv.new(l(:general_csv_encoding), 'UTF-8')
171 begin; @ic.iconv(s.to_s); rescue; s.to_s; end
171 begin; @ic.iconv(s.to_s); rescue; s.to_s; end
172 end
172 end
173 end
173 end
@@ -1,39 +1,39
1 <table class="cal">
1 <table class="cal">
2 <thead>
2 <thead>
3 <tr><td></td><% 7.times do |i| %><th><%= day_name( (calendar.first_wday+i)%7 ) %></th><% end %></tr>
3 <tr><td></td><% 7.times do |i| %><th><%= day_name( (calendar.first_wday+i)%7 ) %></th><% end %></tr>
4 </thead>
4 </thead>
5 <tbody>
5 <tbody>
6 <tr>
6 <tr>
7 <% day = calendar.startdt
7 <% day = calendar.startdt
8 while day <= calendar.enddt %>
8 while day <= calendar.enddt %>
9 <%= "<th>#{day.cweek}</th>" if day.cwday == calendar.first_wday %>
9 <%= "<th>#{day.cweek}</th>" if day.cwday == calendar.first_wday %>
10 <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>">
10 <td class="<%= day.month==calendar.month ? 'even' : 'odd' %><%= ' today' if Date.today == day %>">
11 <p class="day-num"><%= day.day %></p>
11 <p class="day-num"><%= day.day %></p>
12 <% calendar.events_on(day).each do |i| %>
12 <% calendar.events_on(day).each do |i| %>
13 <% if i.is_a? Issue %>
13 <% if i.is_a? Issue %>
14 <div class="<%= i.css_classes %> tooltip">
14 <div class="<%= i.css_classes %> tooltip">
15 <%= if day == i.start_date && day == i.due_date
15 <%= if day == i.start_date && day == i.due_date
16 image_tag('arrow_bw.png')
16 image_tag('arrow_bw.png')
17 elsif day == i.start_date
17 elsif day == i.start_date
18 image_tag('arrow_from.png')
18 image_tag('arrow_from.png')
19 elsif day == i.due_date
19 elsif day == i.due_date
20 image_tag('arrow_to.png')
20 image_tag('arrow_to.png')
21 end %>
21 end %>
22 <%= h("#{i.project} -") unless @project && @project == i.project %>
22 <%= h("#{i.project} -") unless @project && @project == i.project %>
23 <%= link_to_issue i %>: <%= h(truncate(i.subject, :length => 30)) %>
23 <%= link_to_issue i, :truncate => 30 %>
24 <span class="tip"><%= render_issue_tooltip i %></span>
24 <span class="tip"><%= render_issue_tooltip i %></span>
25 </div>
25 </div>
26 <% else %>
26 <% else %>
27 <span class="icon icon-package">
27 <span class="icon icon-package">
28 <%= h("#{i.project} -") unless @project && @project == i.project %>
28 <%= h("#{i.project} -") unless @project && @project == i.project %>
29 <%= link_to_version i%>
29 <%= link_to_version i%>
30 </span>
30 </span>
31 <% end %>
31 <% end %>
32 <% end %>
32 <% end %>
33 </td>
33 </td>
34 <%= '</tr><tr>' if day.cwday==calendar.last_wday and day!=calendar.enddt %>
34 <%= '</tr><tr>' if day.cwday==calendar.last_wday and day!=calendar.enddt %>
35 <% day = day + 1
35 <% day = day + 1
36 end %>
36 end %>
37 </tr>
37 </tr>
38 </tbody>
38 </tbody>
39 </table>
39 </table>
@@ -1,32 +1,33
1 <div class="contextual">
1 <div class="contextual">
2 <% if authorize_for('issue_relations', 'new') %>
2 <% if authorize_for('issue_relations', 'new') %>
3 <%= toggle_link l(:button_add), 'new-relation-form'%>
3 <%= toggle_link l(:button_add), 'new-relation-form'%>
4 <% end %>
4 <% end %>
5 </div>
5 </div>
6
6
7 <p><strong><%=l(:label_related_issues)%></strong></p>
7 <p><strong><%=l(:label_related_issues)%></strong></p>
8
8
9 <% if @issue.relations.any? %>
9 <% if @issue.relations.any? %>
10 <table style="width:100%">
10 <table style="width:100%">
11 <% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
11 <% @issue.relations.select {|r| r.other_issue(@issue).visible? }.each do |relation| %>
12 <tr>
12 <tr>
13 <td><%= l(relation.label_for(@issue)) %> <%= "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)})" if relation.delay && relation.delay != 0 %>
13 <td><%= l(relation.label_for(@issue)) %> <%= "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)})" if relation.delay && relation.delay != 0 %>
14 <%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %> <%= link_to_issue relation.other_issue(@issue) %></td>
14 <%= h(relation.other_issue(@issue).project) + ' - ' if Setting.cross_project_issue_relations? %>
15 <td><%=h relation.other_issue(@issue).subject %></td>
15 <%= link_to_issue relation.other_issue(@issue) %>
16 </td>
16 <td><%= relation.other_issue(@issue).status.name %></td>
17 <td><%= relation.other_issue(@issue).status.name %></td>
17 <td><%= format_date(relation.other_issue(@issue).start_date) %></td>
18 <td><%= format_date(relation.other_issue(@issue).start_date) %></td>
18 <td><%= format_date(relation.other_issue(@issue).due_date) %></td>
19 <td><%= format_date(relation.other_issue(@issue).due_date) %></td>
19 <td><%= link_to_remote(image_tag('delete.png'), { :url => {:controller => 'issue_relations', :action => 'destroy', :issue_id => @issue, :id => relation},
20 <td><%= link_to_remote(image_tag('delete.png'), { :url => {:controller => 'issue_relations', :action => 'destroy', :issue_id => @issue, :id => relation},
20 :method => :post
21 :method => :post
21 }, :title => l(:label_relation_delete)) if authorize_for('issue_relations', 'destroy') %></td>
22 }, :title => l(:label_relation_delete)) if authorize_for('issue_relations', 'destroy') %></td>
22 </tr>
23 </tr>
23 <% end %>
24 <% end %>
24 </table>
25 </table>
25 <% end %>
26 <% end %>
26
27
27 <% remote_form_for(:relation, @relation,
28 <% remote_form_for(:relation, @relation,
28 :url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue},
29 :url => {:controller => 'issue_relations', :action => 'new', :issue_id => @issue},
29 :method => :post,
30 :method => :post,
30 :html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %>
31 :html => {:id => 'new-relation-form', :style => (@relation ? '' : 'display: none;')}) do |f| %>
31 <%= render :partial => 'issue_relations/form', :locals => {:f => f}%>
32 <%= render :partial => 'issue_relations/form', :locals => {:f => f}%>
32 <% end %>
33 <% end %>
@@ -1,251 +1,251
1 <h2><%= l(:label_gantt) %></h2>
1 <h2><%= l(:label_gantt) %></h2>
2
2
3 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %>
3 <% form_tag(params.merge(:month => nil, :year => nil, :months => nil), :id => 'query_form') do %>
4 <fieldset id="filters" class="collapsible">
4 <fieldset id="filters" class="collapsible">
5 <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
5 <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
6 <div>
6 <div>
7 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
7 <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
8 </div>
8 </div>
9 </fieldset>
9 </fieldset>
10
10
11 <p style="float:right;">
11 <p style="float:right;">
12 <%= if @gantt.zoom < 4
12 <%= if @gantt.zoom < 4
13 link_to_remote image_tag('zoom_in.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom+1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom+1)))}
13 link_to_remote image_tag('zoom_in.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom+1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom+1)))}
14 else
14 else
15 image_tag 'zoom_in_g.png'
15 image_tag 'zoom_in_g.png'
16 end %>
16 end %>
17 <%= if @gantt.zoom > 1
17 <%= if @gantt.zoom > 1
18 link_to_remote image_tag('zoom_out.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom-1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom-1)))}
18 link_to_remote image_tag('zoom_out.png'), {:url => @gantt.params.merge(:zoom => (@gantt.zoom-1)), :update => 'content'}, {:href => url_for(@gantt.params.merge(:zoom => (@gantt.zoom-1)))}
19 else
19 else
20 image_tag 'zoom_out_g.png'
20 image_tag 'zoom_out_g.png'
21 end %>
21 end %>
22 </p>
22 </p>
23
23
24 <p class="buttons">
24 <p class="buttons">
25 <%= text_field_tag 'months', @gantt.months, :size => 2 %>
25 <%= text_field_tag 'months', @gantt.months, :size => 2 %>
26 <%= l(:label_months_from) %>
26 <%= l(:label_months_from) %>
27 <%= select_month(@gantt.month_from, :prefix => "month", :discard_type => true) %>
27 <%= select_month(@gantt.month_from, :prefix => "month", :discard_type => true) %>
28 <%= select_year(@gantt.year_from, :prefix => "year", :discard_type => true) %>
28 <%= select_year(@gantt.year_from, :prefix => "year", :discard_type => true) %>
29 <%= hidden_field_tag 'zoom', @gantt.zoom %>
29 <%= hidden_field_tag 'zoom', @gantt.zoom %>
30
30
31 <%= link_to_remote l(:button_apply),
31 <%= link_to_remote l(:button_apply),
32 { :url => { :set_filter => (@query.new_record? ? 1 : nil) },
32 { :url => { :set_filter => (@query.new_record? ? 1 : nil) },
33 :update => "content",
33 :update => "content",
34 :with => "Form.serialize('query_form')"
34 :with => "Form.serialize('query_form')"
35 }, :class => 'icon icon-checked' %>
35 }, :class => 'icon icon-checked' %>
36
36
37 <%= link_to_remote l(:button_clear),
37 <%= link_to_remote l(:button_clear),
38 { :url => { :set_filter => (@query.new_record? ? 1 : nil) },
38 { :url => { :set_filter => (@query.new_record? ? 1 : nil) },
39 :update => "content",
39 :update => "content",
40 }, :class => 'icon icon-reload' if @query.new_record? %>
40 }, :class => 'icon icon-reload' if @query.new_record? %>
41 </p>
41 </p>
42 <% end %>
42 <% end %>
43
43
44 <%= error_messages_for 'query' %>
44 <%= error_messages_for 'query' %>
45 <% if @query.valid? %>
45 <% if @query.valid? %>
46 <% zoom = 1
46 <% zoom = 1
47 @gantt.zoom.times { zoom = zoom * 2 }
47 @gantt.zoom.times { zoom = zoom * 2 }
48
48
49 subject_width = 330
49 subject_width = 330
50 header_heigth = 18
50 header_heigth = 18
51
51
52 headers_height = header_heigth
52 headers_height = header_heigth
53 show_weeks = false
53 show_weeks = false
54 show_days = false
54 show_days = false
55
55
56 if @gantt.zoom >1
56 if @gantt.zoom >1
57 show_weeks = true
57 show_weeks = true
58 headers_height = 2*header_heigth
58 headers_height = 2*header_heigth
59 if @gantt.zoom > 2
59 if @gantt.zoom > 2
60 show_days = true
60 show_days = true
61 headers_height = 3*header_heigth
61 headers_height = 3*header_heigth
62 end
62 end
63 end
63 end
64
64
65 g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
65 g_width = (@gantt.date_to - @gantt.date_from + 1)*zoom
66 g_height = [(20 * @gantt.events.length + 6)+150, 206].max
66 g_height = [(20 * @gantt.events.length + 6)+150, 206].max
67 t_height = g_height + headers_height
67 t_height = g_height + headers_height
68 %>
68 %>
69
69
70 <table width="100%" style="border:0; border-collapse: collapse;">
70 <table width="100%" style="border:0; border-collapse: collapse;">
71 <tr>
71 <tr>
72 <td style="width:<%= subject_width %>px; padding:0px;">
72 <td style="width:<%= subject_width %>px; padding:0px;">
73
73
74 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
74 <div style="position:relative;height:<%= t_height + 24 %>px;width:<%= subject_width + 1 %>px;">
75 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
75 <div style="right:-2px;width:<%= subject_width %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr"></div>
76 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
76 <div style="right:-2px;width:<%= subject_width %>px;height:<%= t_height %>px;border-left: 1px solid #c0c0c0;overflow:hidden;" class="gantt_hdr"></div>
77 <%
77 <%
78 #
78 #
79 # Tasks subjects
79 # Tasks subjects
80 #
80 #
81 top = headers_height + 8
81 top = headers_height + 8
82 @gantt.events.each do |i| %>
82 @gantt.events.each do |i| %>
83 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
83 <div style="position: absolute;line-height:1.2em;height:16px;top:<%= top %>px;left:4px;overflow:hidden;"><small>
84 <% if i.is_a? Issue %>
84 <% if i.is_a? Issue %>
85 <%= h("#{i.project} -") unless @project && @project == i.project %>
85 <%= h("#{i.project} -") unless @project && @project == i.project %>
86 <%= link_to_issue i %>: <%=h i.subject %>
86 <%= link_to_issue i %>
87 <% else %>
87 <% else %>
88 <span class="icon icon-package">
88 <span class="icon icon-package">
89 <%= h("#{i.project} -") unless @project && @project == i.project %>
89 <%= h("#{i.project} -") unless @project && @project == i.project %>
90 <%= link_to_version i %>
90 <%= link_to_version i %>
91 </span>
91 </span>
92 <% end %>
92 <% end %>
93 </small></div>
93 </small></div>
94 <% top = top + 20
94 <% top = top + 20
95 end %>
95 end %>
96 </div>
96 </div>
97 </td>
97 </td>
98 <td>
98 <td>
99
99
100 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
100 <div style="position:relative;height:<%= t_height + 24 %>px;overflow:auto;">
101 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
101 <div style="width:<%= g_width-1 %>px;height:<%= headers_height %>px;background: #eee;" class="gantt_hdr">&nbsp;</div>
102 <%
102 <%
103 #
103 #
104 # Months headers
104 # Months headers
105 #
105 #
106 month_f = @gantt.date_from
106 month_f = @gantt.date_from
107 left = 0
107 left = 0
108 height = (show_weeks ? header_heigth : header_heigth + g_height)
108 height = (show_weeks ? header_heigth : header_heigth + g_height)
109 @gantt.months.times do
109 @gantt.months.times do
110 width = ((month_f >> 1) - month_f) * zoom - 1
110 width = ((month_f >> 1) - month_f) * zoom - 1
111 %>
111 %>
112 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
112 <div style="left:<%= left %>px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
113 <%= link_to "#{month_f.year}-#{month_f.month}", @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
113 <%= link_to "#{month_f.year}-#{month_f.month}", @gantt.params.merge(:year => month_f.year, :month => month_f.month), :title => "#{month_name(month_f.month)} #{month_f.year}"%>
114 </div>
114 </div>
115 <%
115 <%
116 left = left + width + 1
116 left = left + width + 1
117 month_f = month_f >> 1
117 month_f = month_f >> 1
118 end %>
118 end %>
119
119
120 <%
120 <%
121 #
121 #
122 # Weeks headers
122 # Weeks headers
123 #
123 #
124 if show_weeks
124 if show_weeks
125 left = 0
125 left = 0
126 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
126 height = (show_days ? header_heigth-1 : header_heigth-1 + g_height)
127 if @gantt.date_from.cwday == 1
127 if @gantt.date_from.cwday == 1
128 # @date_from is monday
128 # @date_from is monday
129 week_f = @gantt.date_from
129 week_f = @gantt.date_from
130 else
130 else
131 # find next monday after @date_from
131 # find next monday after @date_from
132 week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
132 week_f = @gantt.date_from + (7 - @gantt.date_from.cwday + 1)
133 width = (7 - @gantt.date_from.cwday + 1) * zoom-1
133 width = (7 - @gantt.date_from.cwday + 1) * zoom-1
134 %>
134 %>
135 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
135 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">&nbsp;</div>
136 <%
136 <%
137 left = left + width+1
137 left = left + width+1
138 end %>
138 end %>
139 <%
139 <%
140 while week_f <= @gantt.date_to
140 while week_f <= @gantt.date_to
141 width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom -1 : (@gantt.date_to - week_f + 1) * zoom-1
141 width = (week_f + 6 <= @gantt.date_to) ? 7 * zoom -1 : (@gantt.date_to - week_f + 1) * zoom-1
142 %>
142 %>
143 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
143 <div style="left:<%= left %>px;top:19px;width:<%= width %>px;height:<%= height %>px;" class="gantt_hdr">
144 <small><%= week_f.cweek if width >= 16 %></small>
144 <small><%= week_f.cweek if width >= 16 %></small>
145 </div>
145 </div>
146 <%
146 <%
147 left = left + width+1
147 left = left + width+1
148 week_f = week_f+7
148 week_f = week_f+7
149 end
149 end
150 end %>
150 end %>
151
151
152 <%
152 <%
153 #
153 #
154 # Days headers
154 # Days headers
155 #
155 #
156 if show_days
156 if show_days
157 left = 0
157 left = 0
158 height = g_height + header_heigth - 1
158 height = g_height + header_heigth - 1
159 wday = @gantt.date_from.cwday
159 wday = @gantt.date_from.cwday
160 (@gantt.date_to - @gantt.date_from + 1).to_i.times do
160 (@gantt.date_to - @gantt.date_from + 1).to_i.times do
161 width = zoom - 1
161 width = zoom - 1
162 %>
162 %>
163 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
163 <div style="left:<%= left %>px;top:37px;width:<%= width %>px;height:<%= height %>px;font-size:0.7em;<%= "background:#f1f1f1;" if wday > 5 %>" class="gantt_hdr">
164 <%= day_name(wday).first %>
164 <%= day_name(wday).first %>
165 </div>
165 </div>
166 <%
166 <%
167 left = left + width+1
167 left = left + width+1
168 wday = wday + 1
168 wday = wday + 1
169 wday = 1 if wday > 7
169 wday = 1 if wday > 7
170 end
170 end
171 end %>
171 end %>
172
172
173 <%
173 <%
174 #
174 #
175 # Tasks
175 # Tasks
176 #
176 #
177 top = headers_height + 10
177 top = headers_height + 10
178 @gantt.events.each do |i|
178 @gantt.events.each do |i|
179 if i.is_a? Issue
179 if i.is_a? Issue
180 i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
180 i_start_date = (i.start_date >= @gantt.date_from ? i.start_date : @gantt.date_from )
181 i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
181 i_end_date = (i.due_before <= @gantt.date_to ? i.due_before : @gantt.date_to )
182
182
183 i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
183 i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
184 i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
184 i_done_date = (i_done_date <= @gantt.date_from ? @gantt.date_from : i_done_date )
185 i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
185 i_done_date = (i_done_date >= @gantt.date_to ? @gantt.date_to : i_done_date )
186
186
187 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
187 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
188
188
189 i_left = ((i_start_date - @gantt.date_from)*zoom).floor
189 i_left = ((i_start_date - @gantt.date_from)*zoom).floor
190 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
190 i_width = ((i_end_date - i_start_date + 1)*zoom).floor - 2 # total width of the issue (- 2 for left and right borders)
191 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
191 d_width = ((i_done_date - i_start_date)*zoom).floor - 2 # done width
192 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
192 l_width = i_late_date ? ((i_late_date - i_start_date+1)*zoom).floor - 2 : 0 # delay width
193 %>
193 %>
194 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
194 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;" class="task task_todo">&nbsp;</div>
195 <% if l_width > 0 %>
195 <% if l_width > 0 %>
196 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
196 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= l_width %>px;" class="task task_late">&nbsp;</div>
197 <% end %>
197 <% end %>
198 <% if d_width > 0 %>
198 <% if d_width > 0 %>
199 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
199 <div style="top:<%= top %>px;left:<%= i_left %>px;width:<%= d_width %>px;" class="task task_done">&nbsp;</div>
200 <% end %>
200 <% end %>
201 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
201 <div style="top:<%= top %>px;left:<%= i_left + i_width + 5 %>px;background:#fff;" class="task">
202 <%= i.status.name %>
202 <%= i.status.name %>
203 <%= (i.done_ratio).to_i %>%
203 <%= (i.done_ratio).to_i %>%
204 </div>
204 </div>
205 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
205 <div class="tooltip" style="position: absolute;top:<%= top %>px;left:<%= i_left %>px;width:<%= i_width %>px;height:12px;">
206 <span class="tip">
206 <span class="tip">
207 <%= render_issue_tooltip i %>
207 <%= render_issue_tooltip i %>
208 </span></div>
208 </span></div>
209 <% else
209 <% else
210 i_left = ((i.start_date - @gantt.date_from)*zoom).floor
210 i_left = ((i.start_date - @gantt.date_from)*zoom).floor
211 %>
211 %>
212 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
212 <div style="top:<%= top %>px;left:<%= i_left %>px;width:15px;" class="task milestone">&nbsp;</div>
213 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
213 <div style="top:<%= top %>px;left:<%= i_left + 12 %>px;background:#fff;" class="task">
214 <%= h("#{i.project} -") unless @project && @project == i.project %>
214 <%= h("#{i.project} -") unless @project && @project == i.project %>
215 <strong><%=h i %></strong>
215 <strong><%=h i %></strong>
216 </div>
216 </div>
217 <% end %>
217 <% end %>
218 <% top = top + 20
218 <% top = top + 20
219 end %>
219 end %>
220
220
221 <%
221 <%
222 #
222 #
223 # Today red line (excluded from cache)
223 # Today red line (excluded from cache)
224 #
224 #
225 if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
225 if Date.today >= @gantt.date_from and Date.today <= @gantt.date_to %>
226 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@gantt.date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
226 <div style="position: absolute;height:<%= g_height %>px;top:<%= headers_height + 1 %>px;left:<%= ((Date.today-@gantt.date_from+1)*zoom).floor()-1 %>px;width:10px;border-left: 1px dashed red;">&nbsp;</div>
227 <% end %>
227 <% end %>
228
228
229 </div>
229 </div>
230 </td>
230 </td>
231 </tr>
231 </tr>
232 </table>
232 </table>
233
233
234 <table width="100%">
234 <table width="100%">
235 <tr>
235 <tr>
236 <td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
236 <td align="left"><%= link_to_remote ('&#171; ' + l(:label_previous)), {:url => @gantt.params_previous, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_previous)} %></td>
237 <td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
237 <td align="right"><%= link_to_remote (l(:label_next) + ' &#187;'), {:url => @gantt.params_next, :update => 'content', :complete => 'window.scrollTo(0,0)'}, {:href => url_for(@gantt.params_next)} %></td>
238 </tr>
238 </tr>
239 </table>
239 </table>
240
240
241 <% other_formats_links do |f| %>
241 <% other_formats_links do |f| %>
242 <%= f.link_to 'PDF', :url => @gantt.params %>
242 <%= f.link_to 'PDF', :url => @gantt.params %>
243 <%= f.link_to('PNG', :url => @gantt.params) if @gantt.respond_to?('to_image') %>
243 <%= f.link_to('PNG', :url => @gantt.params) if @gantt.respond_to?('to_image') %>
244 <% end %>
244 <% end %>
245 <% end # query.valid? %>
245 <% end # query.valid? %>
246
246
247 <% content_for :sidebar do %>
247 <% content_for :sidebar do %>
248 <%= render :partial => 'issues/sidebar' %>
248 <%= render :partial => 'issues/sidebar' %>
249 <% end %>
249 <% end %>
250
250
251 <% html_title(l(:label_gantt)) -%>
251 <% html_title(l(:label_gantt)) -%>
@@ -1,52 +1,52
1 <h3><%=l(:label_spent_time)%> (<%= l(:label_last_n_days, 7) %>)</h3>
1 <h3><%=l(:label_spent_time)%> (<%= l(:label_last_n_days, 7) %>)</h3>
2 <%
2 <%
3 entries = TimeEntry.find(:all,
3 entries = TimeEntry.find(:all,
4 :conditions => ["#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", @user.id, Date.today - 6, Date.today],
4 :conditions => ["#{TimeEntry.table_name}.user_id = ? AND #{TimeEntry.table_name}.spent_on BETWEEN ? AND ?", @user.id, Date.today - 6, Date.today],
5 :include => [:activity, :project, {:issue => [:tracker, :status]}],
5 :include => [:activity, :project, {:issue => [:tracker, :status]}],
6 :order => "#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC")
6 :order => "#{TimeEntry.table_name}.spent_on DESC, #{Project.table_name}.name ASC, #{Tracker.table_name}.position ASC, #{Issue.table_name}.id ASC")
7 entries_by_day = entries.group_by(&:spent_on)
7 entries_by_day = entries.group_by(&:spent_on)
8 %>
8 %>
9
9
10 <div class="total-hours">
10 <div class="total-hours">
11 <p><%= l(:label_total) %>: <%= html_hours("%.2f" % entries.sum(&:hours).to_f) %></p>
11 <p><%= l(:label_total) %>: <%= html_hours("%.2f" % entries.sum(&:hours).to_f) %></p>
12 </div>
12 </div>
13
13
14 <% if entries.any? %>
14 <% if entries.any? %>
15 <table class="list time-entries">
15 <table class="list time-entries">
16 <thead>
16 <thead>
17 <th><%= l(:label_activity) %></th>
17 <th><%= l(:label_activity) %></th>
18 <th><%= l(:label_project) %></th>
18 <th><%= l(:label_project) %></th>
19 <th><%= l(:field_comments) %></th>
19 <th><%= l(:field_comments) %></th>
20 <th><%= l(:field_hours) %></th>
20 <th><%= l(:field_hours) %></th>
21 <th></th>
21 <th></th>
22 </thead>
22 </thead>
23 <tbody>
23 <tbody>
24 <% entries_by_day.keys.sort.reverse.each do |day| %>
24 <% entries_by_day.keys.sort.reverse.each do |day| %>
25 <tr class="odd">
25 <tr class="odd">
26 <td><strong><%= day == Date.today ? l(:label_today).titleize : format_date(day) %></strong></td>
26 <td><strong><%= day == Date.today ? l(:label_today).titleize : format_date(day) %></strong></td>
27 <td colspan="2"></td>
27 <td colspan="2"></td>
28 <td class="hours"><em><%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %></em></td>
28 <td class="hours"><em><%= html_hours("%.2f" % entries_by_day[day].sum(&:hours).to_f) %></em></td>
29 <td></td>
29 <td></td>
30 </tr>
30 </tr>
31 <% entries_by_day[day].each do |entry| -%>
31 <% entries_by_day[day].each do |entry| -%>
32 <tr class="time-entry" style="border-bottom: 1px solid #f5f5f5;">
32 <tr class="time-entry" style="border-bottom: 1px solid #f5f5f5;">
33 <td class="activity"><%=h entry.activity %></td>
33 <td class="activity"><%=h entry.activity %></td>
34 <td class="subject"><%=h entry.project %> <%= ' - ' + link_to_issue(entry.issue, :title => h("#{entry.issue.subject} (#{entry.issue.status})")) if entry.issue %></td>
34 <td class="subject"><%=h entry.project %> <%= ' - ' + link_to_issue(entry.issue, :truncate => 50) if entry.issue %></td>
35 <td class="comments"><%=h entry.comments %></td>
35 <td class="comments"><%=h entry.comments %></td>
36 <td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
36 <td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
37 <td align="center">
37 <td align="center">
38 <% if entry.editable_by?(@user) -%>
38 <% if entry.editable_by?(@user) -%>
39 <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry},
39 <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry},
40 :title => l(:button_edit) %>
40 :title => l(:button_edit) %>
41 <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry},
41 <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry},
42 :confirm => l(:text_are_you_sure),
42 :confirm => l(:text_are_you_sure),
43 :method => :post,
43 :method => :post,
44 :title => l(:button_delete) %>
44 :title => l(:button_delete) %>
45 <% end -%>
45 <% end -%>
46 </td>
46 </td>
47 </tr>
47 </tr>
48 <% end -%>
48 <% end -%>
49 <% end -%>
49 <% end -%>
50 </tbody>
50 </tbody>
51 </table>
51 </table>
52 <% end %>
52 <% end %>
@@ -1,42 +1,42
1 <h2><%=l(:label_change_log)%></h2>
1 <h2><%=l(:label_change_log)%></h2>
2
2
3 <% if @versions.empty? %>
3 <% if @versions.empty? %>
4 <p class="nodata"><%= l(:label_no_data) %></p>
4 <p class="nodata"><%= l(:label_no_data) %></p>
5 <% end %>
5 <% end %>
6
6
7 <% @versions.each do |version| %>
7 <% @versions.each do |version| %>
8 <a name="<%= version.name %>"><h3 class="icon22 icon22-package"><%= version.name %></h3></a>
8 <a name="<%= version.name %>"><h3 class="icon22 icon22-package"><%= version.name %></h3></a>
9 <% if version.effective_date %>
9 <% if version.effective_date %>
10 <p><%= format_date(version.effective_date) %></p>
10 <p><%= format_date(version.effective_date) %></p>
11 <% end %>
11 <% end %>
12 <p><%=h version.description %></p>
12 <p><%=h version.description %></p>
13 <% issues = version.fixed_issues.find(:all,
13 <% issues = version.fixed_issues.find(:all,
14 :include => [:status, :tracker],
14 :include => [:status, :tracker],
15 :conditions => ["#{IssueStatus.table_name}.is_closed=? AND #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", true],
15 :conditions => ["#{IssueStatus.table_name}.is_closed=? AND #{Issue.table_name}.tracker_id in (#{@selected_tracker_ids.join(',')})", true],
16 :order => "#{Tracker.table_name}.position") unless @selected_tracker_ids.empty?
16 :order => "#{Tracker.table_name}.position") unless @selected_tracker_ids.empty?
17 issues ||= []
17 issues ||= []
18 %>
18 %>
19 <% if !issues.empty? %>
19 <% if !issues.empty? %>
20 <ul>
20 <ul>
21 <% issues.each do |issue| %>
21 <% issues.each do |issue| %>
22 <li><%= link_to_issue(issue) %>: <%=h issue.subject %></li>
22 <li><%= link_to_issue(issue) %></li>
23 <% end %>
23 <% end %>
24 </ul>
24 </ul>
25 <% end %>
25 <% end %>
26 <% end %>
26 <% end %>
27
27
28 <% content_for :sidebar do %>
28 <% content_for :sidebar do %>
29 <% form_tag({},:method => :get) do %>
29 <% form_tag({},:method => :get) do %>
30 <h3><%= l(:label_change_log) %></h3>
30 <h3><%= l(:label_change_log) %></h3>
31 <% @trackers.each do |tracker| %>
31 <% @trackers.each do |tracker| %>
32 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
32 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s) %>
33 <%= tracker.name %></label><br />
33 <%= tracker.name %></label><br />
34 <% end %>
34 <% end %>
35 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
35 <p><%= submit_tag l(:button_apply), :class => 'button-small' %></p>
36 <% end %>
36 <% end %>
37
37
38 <h3><%= l(:label_version_plural) %></h3>
38 <h3><%= l(:label_version_plural) %></h3>
39 <% @versions.each do |version| %>
39 <% @versions.each do |version| %>
40 <%= link_to version.name, :anchor => version.name %><br />
40 <%= link_to version.name, :anchor => version.name %><br />
41 <% end %>
41 <% end %>
42 <% end %>
42 <% end %>
@@ -1,51 +1,51
1 <h2><%=l(:label_roadmap)%></h2>
1 <h2><%=l(:label_roadmap)%></h2>
2
2
3 <% if @versions.empty? %>
3 <% if @versions.empty? %>
4 <p class="nodata"><%= l(:label_no_data) %></p>
4 <p class="nodata"><%= l(:label_no_data) %></p>
5 <% else %>
5 <% else %>
6 <div id="roadmap">
6 <div id="roadmap">
7 <% @versions.each do |version| %>
7 <% @versions.each do |version| %>
8 <%= tag 'a', :name => version.name %>
8 <%= tag 'a', :name => version.name %>
9 <h3 class="icon22 icon22-package"><%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %></h3>
9 <h3 class="icon22 icon22-package"><%= link_to h(version.name), :controller => 'versions', :action => 'show', :id => version %></h3>
10 <%= render :partial => 'versions/overview', :locals => {:version => version} %>
10 <%= render :partial => 'versions/overview', :locals => {:version => version} %>
11 <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %>
11 <%= render(:partial => "wiki/content", :locals => {:content => version.wiki_page.content}) if version.wiki_page %>
12
12
13 <% issues = version.fixed_issues.find(:all,
13 <% issues = version.fixed_issues.find(:all,
14 :include => [:status, :tracker],
14 :include => [:status, :tracker],
15 :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
15 :conditions => ["tracker_id in (#{@selected_tracker_ids.join(',')})"],
16 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty?
16 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") unless @selected_tracker_ids.empty?
17 issues ||= []
17 issues ||= []
18 %>
18 %>
19 <% if issues.size > 0 %>
19 <% if issues.size > 0 %>
20 <fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend>
20 <fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend>
21 <ul>
21 <ul>
22 <%- issues.each do |issue| -%>
22 <%- issues.each do |issue| -%>
23 <li><%= link_to_issue(issue) %>: <%=h issue.subject %></li>
23 <li><%= link_to_issue(issue) %></li>
24 <%- end -%>
24 <%- end -%>
25 </ul>
25 </ul>
26 </fieldset>
26 </fieldset>
27 <% end %>
27 <% end %>
28 <%= call_hook :view_projects_roadmap_version_bottom, :version => version %>
28 <%= call_hook :view_projects_roadmap_version_bottom, :version => version %>
29 <% end %>
29 <% end %>
30 </div>
30 </div>
31 <% end %>
31 <% end %>
32
32
33 <% content_for :sidebar do %>
33 <% content_for :sidebar do %>
34 <% form_tag({}, :method => :get) do %>
34 <% form_tag({}, :method => :get) do %>
35 <h3><%= l(:label_roadmap) %></h3>
35 <h3><%= l(:label_roadmap) %></h3>
36 <% @trackers.each do |tracker| %>
36 <% @trackers.each do |tracker| %>
37 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s), :id => nil %>
37 <label><%= check_box_tag "tracker_ids[]", tracker.id, (@selected_tracker_ids.include? tracker.id.to_s), :id => nil %>
38 <%= tracker.name %></label><br />
38 <%= tracker.name %></label><br />
39 <% end %>
39 <% end %>
40 <br />
40 <br />
41 <label for="completed"><%= check_box_tag "completed", 1, params[:completed] %> <%= l(:label_show_completed_versions) %></label>
41 <label for="completed"><%= check_box_tag "completed", 1, params[:completed] %> <%= l(:label_show_completed_versions) %></label>
42 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
42 <p><%= submit_tag l(:button_apply), :class => 'button-small', :name => nil %></p>
43 <% end %>
43 <% end %>
44
44
45 <h3><%= l(:label_version_plural) %></h3>
45 <h3><%= l(:label_version_plural) %></h3>
46 <% @versions.each do |version| %>
46 <% @versions.each do |version| %>
47 <%= link_to version.name, "##{version.name}" %><br />
47 <%= link_to version.name, "##{version.name}" %><br />
48 <% end %>
48 <% end %>
49 <% end %>
49 <% end %>
50
50
51 <% html_title(l(:label_roadmap)) %>
51 <% html_title(l(:label_roadmap)) %>
@@ -1,59 +1,59
1 <div class="contextual">
1 <div class="contextual">
2 &#171;
2 &#171;
3 <% unless @changeset.previous.nil? -%>
3 <% unless @changeset.previous.nil? -%>
4 <%= link_to l(:label_previous), :controller => 'repositories', :action => 'revision', :id => @project, :rev => @changeset.previous.revision %>
4 <%= link_to l(:label_previous), :controller => 'repositories', :action => 'revision', :id => @project, :rev => @changeset.previous.revision %>
5 <% else -%>
5 <% else -%>
6 <%= l(:label_previous) %>
6 <%= l(:label_previous) %>
7 <% end -%>
7 <% end -%>
8 |
8 |
9 <% unless @changeset.next.nil? -%>
9 <% unless @changeset.next.nil? -%>
10 <%= link_to l(:label_next), :controller => 'repositories', :action => 'revision', :id => @project, :rev => @changeset.next.revision %>
10 <%= link_to l(:label_next), :controller => 'repositories', :action => 'revision', :id => @project, :rev => @changeset.next.revision %>
11 <% else -%>
11 <% else -%>
12 <%= l(:label_next) %>
12 <%= l(:label_next) %>
13 <% end -%>
13 <% end -%>
14 &#187;&nbsp;
14 &#187;&nbsp;
15
15
16 <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
16 <% form_tag({:controller => 'repositories', :action => 'revision', :id => @project, :rev => nil}, :method => :get) do %>
17 <%= text_field_tag 'rev', @rev[0,8], :size => 8 %>
17 <%= text_field_tag 'rev', @rev[0,8], :size => 8 %>
18 <%= submit_tag 'OK', :name => nil %>
18 <%= submit_tag 'OK', :name => nil %>
19 <% end %>
19 <% end %>
20 </div>
20 </div>
21
21
22 <h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
22 <h2><%= l(:label_revision) %> <%= format_revision(@changeset.revision) %></h2>
23
23
24 <p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
24 <p><% if @changeset.scmid %>ID: <%= @changeset.scmid %><br /><% end %>
25 <span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p>
25 <span class="author"><%= authoring(@changeset.committed_on, @changeset.author) %></span></p>
26
26
27 <%= textilizable @changeset.comments %>
27 <%= textilizable @changeset.comments %>
28
28
29 <% if @changeset.issues.visible.any? %>
29 <% if @changeset.issues.visible.any? %>
30 <h3><%= l(:label_related_issues) %></h3>
30 <h3><%= l(:label_related_issues) %></h3>
31 <ul>
31 <ul>
32 <% @changeset.issues.visible.each do |issue| %>
32 <% @changeset.issues.visible.each do |issue| %>
33 <li><%= link_to_issue issue %>: <%=h issue.subject %></li>
33 <li><%= link_to_issue issue %></li>
34 <% end %>
34 <% end %>
35 </ul>
35 </ul>
36 <% end %>
36 <% end %>
37
37
38 <% if User.current.allowed_to?(:browse_repository, @project) %>
38 <% if User.current.allowed_to?(:browse_repository, @project) %>
39 <h3><%= l(:label_attachment_plural) %></h3>
39 <h3><%= l(:label_attachment_plural) %></h3>
40 <ul id="changes-legend">
40 <ul id="changes-legend">
41 <li class="change change-A"><%= l(:label_added) %></li>
41 <li class="change change-A"><%= l(:label_added) %></li>
42 <li class="change change-M"><%= l(:label_modified) %></li>
42 <li class="change change-M"><%= l(:label_modified) %></li>
43 <li class="change change-C"><%= l(:label_copied) %></li>
43 <li class="change change-C"><%= l(:label_copied) %></li>
44 <li class="change change-R"><%= l(:label_renamed) %></li>
44 <li class="change change-R"><%= l(:label_renamed) %></li>
45 <li class="change change-D"><%= l(:label_deleted) %></li>
45 <li class="change change-D"><%= l(:label_deleted) %></li>
46 </ul>
46 </ul>
47
47
48 <p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %></p>
48 <p><%= link_to(l(:label_view_diff), :action => 'diff', :id => @project, :path => "", :rev => @changeset.revision) if @changeset.changes.any? %></p>
49
49
50 <div class="changeset-changes">
50 <div class="changeset-changes">
51 <%= render_changeset_changes %>
51 <%= render_changeset_changes %>
52 </div>
52 </div>
53 <% end %>
53 <% end %>
54
54
55 <% content_for :header_tags do %>
55 <% content_for :header_tags do %>
56 <%= stylesheet_link_tag "scm" %>
56 <%= stylesheet_link_tag "scm" %>
57 <% end %>
57 <% end %>
58
58
59 <% html_title("#{l(:label_revision)} #{@changeset.revision}") -%>
59 <% html_title("#{l(:label_revision)} #{@changeset.revision}") -%>
@@ -1,41 +1,41
1 <table class="list time-entries">
1 <table class="list time-entries">
2 <thead>
2 <thead>
3 <tr>
3 <tr>
4 <%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
4 <%= sort_header_tag('spent_on', :caption => l(:label_date), :default_order => 'desc') %>
5 <%= sort_header_tag('user', :caption => l(:label_member)) %>
5 <%= sort_header_tag('user', :caption => l(:label_member)) %>
6 <%= sort_header_tag('activity', :caption => l(:label_activity)) %>
6 <%= sort_header_tag('activity', :caption => l(:label_activity)) %>
7 <%= sort_header_tag('project', :caption => l(:label_project)) %>
7 <%= sort_header_tag('project', :caption => l(:label_project)) %>
8 <%= sort_header_tag('issue', :caption => l(:label_issue), :default_order => 'desc') %>
8 <%= sort_header_tag('issue', :caption => l(:label_issue), :default_order => 'desc') %>
9 <th><%= l(:field_comments) %></th>
9 <th><%= l(:field_comments) %></th>
10 <%= sort_header_tag('hours', :caption => l(:field_hours)) %>
10 <%= sort_header_tag('hours', :caption => l(:field_hours)) %>
11 <th></th>
11 <th></th>
12 </tr>
12 </tr>
13 </thead>
13 </thead>
14 <tbody>
14 <tbody>
15 <% entries.each do |entry| -%>
15 <% entries.each do |entry| -%>
16 <tr class="time-entry <%= cycle("odd", "even") %>">
16 <tr class="time-entry <%= cycle("odd", "even") %>">
17 <td class="spent_on"><%= format_date(entry.spent_on) %></td>
17 <td class="spent_on"><%= format_date(entry.spent_on) %></td>
18 <td class="user"><%=h entry.user %></td>
18 <td class="user"><%=h entry.user %></td>
19 <td class="activity"><%=h entry.activity %></td>
19 <td class="activity"><%=h entry.activity %></td>
20 <td class="project"><%=h entry.project %></td>
20 <td class="project"><%=h entry.project %></td>
21 <td class="subject">
21 <td class="subject">
22 <% if entry.issue -%>
22 <% if entry.issue -%>
23 <%= link_to_issue entry.issue %>: <%= h(truncate(entry.issue.subject, :length => 50)) -%>
23 <%= link_to_issue entry.issue, :truncate => 50 -%>
24 <% end -%>
24 <% end -%>
25 </td>
25 </td>
26 <td class="comments"><%=h entry.comments %></td>
26 <td class="comments"><%=h entry.comments %></td>
27 <td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
27 <td class="hours"><%= html_hours("%.2f" % entry.hours) %></td>
28 <td align="center">
28 <td align="center">
29 <% if entry.editable_by?(User.current) -%>
29 <% if entry.editable_by?(User.current) -%>
30 <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry, :project_id => nil},
30 <%= link_to image_tag('edit.png'), {:controller => 'timelog', :action => 'edit', :id => entry, :project_id => nil},
31 :title => l(:button_edit) %>
31 :title => l(:button_edit) %>
32 <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry, :project_id => nil},
32 <%= link_to image_tag('delete.png'), {:controller => 'timelog', :action => 'destroy', :id => entry, :project_id => nil},
33 :confirm => l(:text_are_you_sure),
33 :confirm => l(:text_are_you_sure),
34 :method => :post,
34 :method => :post,
35 :title => l(:button_delete) %>
35 :title => l(:button_delete) %>
36 <% end -%>
36 <% end -%>
37 </td>
37 </td>
38 </tr>
38 </tr>
39 <% end -%>
39 <% end -%>
40 </tbody>
40 </tbody>
41 </table>
41 </table>
@@ -1,51 +1,51
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %>
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %>
3 <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
3 <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
4 </div>
4 </div>
5
5
6 <h2><%= h(@version.name) %></h2>
6 <h2><%= h(@version.name) %></h2>
7
7
8 <div id="version-summary">
8 <div id="version-summary">
9 <% if @version.estimated_hours > 0 || User.current.allowed_to?(:view_time_entries, @project) %>
9 <% if @version.estimated_hours > 0 || User.current.allowed_to?(:view_time_entries, @project) %>
10 <fieldset><legend><%= l(:label_time_tracking) %></legend>
10 <fieldset><legend><%= l(:label_time_tracking) %></legend>
11 <table>
11 <table>
12 <tr>
12 <tr>
13 <td width="130px" align="right"><%= l(:field_estimated_hours) %></td>
13 <td width="130px" align="right"><%= l(:field_estimated_hours) %></td>
14 <td width="240px" class="total-hours"width="130px" align="right"><%= html_hours(l_hours(@version.estimated_hours)) %></td>
14 <td width="240px" class="total-hours"width="130px" align="right"><%= html_hours(l_hours(@version.estimated_hours)) %></td>
15 </tr>
15 </tr>
16 <% if User.current.allowed_to?(:view_time_entries, @project) %>
16 <% if User.current.allowed_to?(:view_time_entries, @project) %>
17 <tr>
17 <tr>
18 <td width="130px" align="right"><%= l(:label_spent_time) %></td>
18 <td width="130px" align="right"><%= l(:label_spent_time) %></td>
19 <td width="240px" class="total-hours"><%= html_hours(l_hours(@version.spent_hours)) %></td>
19 <td width="240px" class="total-hours"><%= html_hours(l_hours(@version.spent_hours)) %></td>
20 </tr>
20 </tr>
21 <% end %>
21 <% end %>
22 </table>
22 </table>
23 </fieldset>
23 </fieldset>
24 <% end %>
24 <% end %>
25
25
26 <div id="status_by">
26 <div id="status_by">
27 <%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %>
27 <%= render_issue_status_by(@version, params[:status_by]) if @version.fixed_issues.count > 0 %>
28 </div>
28 </div>
29 </div>
29 </div>
30
30
31 <div id="roadmap">
31 <div id="roadmap">
32 <%= render :partial => 'versions/overview', :locals => {:version => @version} %>
32 <%= render :partial => 'versions/overview', :locals => {:version => @version} %>
33 <%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %>
33 <%= render(:partial => "wiki/content", :locals => {:content => @version.wiki_page.content}) if @version.wiki_page %>
34
34
35 <% issues = @version.fixed_issues.find(:all,
35 <% issues = @version.fixed_issues.find(:all,
36 :include => [:status, :tracker],
36 :include => [:status, :tracker],
37 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %>
37 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id") %>
38 <% if issues.size > 0 %>
38 <% if issues.size > 0 %>
39 <fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend>
39 <fieldset class="related-issues"><legend><%= l(:label_related_issues) %></legend>
40 <ul>
40 <ul>
41 <% issues.each do |issue| -%>
41 <% issues.each do |issue| -%>
42 <li><%= link_to_issue(issue) %>: <%=h issue.subject %></li>
42 <li><%= link_to_issue(issue) %></li>
43 <% end -%>
43 <% end -%>
44 </ul>
44 </ul>
45 </fieldset>
45 </fieldset>
46 <% end %>
46 <% end %>
47 </div>
47 </div>
48
48
49 <%= call_hook :view_versions_show_bottom, :version => @version %>
49 <%= call_hook :view_versions_show_bottom, :version => @version %>
50
50
51 <% html_title @version.name %>
51 <% html_title @version.name %>
General Comments 0
You need to be logged in to leave comments. Login now