##// END OF EJS Templates
Un-inline conditions....
Jean-Philippe Lang -
r14367:18579303a20f
parent child
Show More
@@ -1,521 +1,524
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module IssuesHelper
20 module IssuesHelper
21 include ApplicationHelper
21 include ApplicationHelper
22 include Redmine::Export::PDF::IssuesPdfHelper
22 include Redmine::Export::PDF::IssuesPdfHelper
23
23
24 def issue_list(issues, &block)
24 def issue_list(issues, &block)
25 ancestors = []
25 ancestors = []
26 issues.each do |issue|
26 issues.each do |issue|
27 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
27 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
28 ancestors.pop
28 ancestors.pop
29 end
29 end
30 yield issue, ancestors.size
30 yield issue, ancestors.size
31 ancestors << issue unless issue.leaf?
31 ancestors << issue unless issue.leaf?
32 end
32 end
33 end
33 end
34
34
35 def grouped_issue_list(issues, query, issue_count_by_group, &block)
35 def grouped_issue_list(issues, query, issue_count_by_group, &block)
36 previous_group, first = false, true
36 previous_group, first = false, true
37 totals_by_group = query.totalable_columns.inject({}) do |h, column|
37 totals_by_group = query.totalable_columns.inject({}) do |h, column|
38 h[column] = query.total_by_group_for(column)
38 h[column] = query.total_by_group_for(column)
39 h
39 h
40 end
40 end
41 issue_list(issues) do |issue, level|
41 issue_list(issues) do |issue, level|
42 group_name = group_count = nil
42 group_name = group_count = nil
43 if query.grouped? && ((group = query.group_by_column.value(issue)) != previous_group || first)
43 if query.grouped?
44 if group.blank? && group != false
44 group = query.group_by_column.value(issue)
45 group_name = "(#{l(:label_blank_value)})"
45 if first || group != previous_group
46 else
46 if group.blank? && group != false
47 group_name = column_content(query.group_by_column, issue)
47 group_name = "(#{l(:label_blank_value)})"
48 else
49 group_name = format_object(group)
50 end
51 group_name ||= ""
52 group_count = issue_count_by_group[group]
53 group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe
48 end
54 end
49 group_name ||= ""
50 group_count = issue_count_by_group[group]
51 group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe
52 end
55 end
53 yield issue, level, group_name, group_count, group_totals
56 yield issue, level, group_name, group_count, group_totals
54 previous_group, first = group, false
57 previous_group, first = group, false
55 end
58 end
56 end
59 end
57
60
58 # Renders a HTML/CSS tooltip
61 # Renders a HTML/CSS tooltip
59 #
62 #
60 # To use, a trigger div is needed. This is a div with the class of "tooltip"
63 # To use, a trigger div is needed. This is a div with the class of "tooltip"
61 # that contains this method wrapped in a span with the class of "tip"
64 # that contains this method wrapped in a span with the class of "tip"
62 #
65 #
63 # <div class="tooltip"><%= link_to_issue(issue) %>
66 # <div class="tooltip"><%= link_to_issue(issue) %>
64 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
67 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
65 # </div>
68 # </div>
66 #
69 #
67 def render_issue_tooltip(issue)
70 def render_issue_tooltip(issue)
68 @cached_label_status ||= l(:field_status)
71 @cached_label_status ||= l(:field_status)
69 @cached_label_start_date ||= l(:field_start_date)
72 @cached_label_start_date ||= l(:field_start_date)
70 @cached_label_due_date ||= l(:field_due_date)
73 @cached_label_due_date ||= l(:field_due_date)
71 @cached_label_assigned_to ||= l(:field_assigned_to)
74 @cached_label_assigned_to ||= l(:field_assigned_to)
72 @cached_label_priority ||= l(:field_priority)
75 @cached_label_priority ||= l(:field_priority)
73 @cached_label_project ||= l(:field_project)
76 @cached_label_project ||= l(:field_project)
74
77
75 link_to_issue(issue) + "<br /><br />".html_safe +
78 link_to_issue(issue) + "<br /><br />".html_safe +
76 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
79 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />".html_safe +
77 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
80 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />".html_safe +
78 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
81 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />".html_safe +
79 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
82 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />".html_safe +
80 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
83 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />".html_safe +
81 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
84 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}".html_safe
82 end
85 end
83
86
84 def issue_heading(issue)
87 def issue_heading(issue)
85 h("#{issue.tracker} ##{issue.id}")
88 h("#{issue.tracker} ##{issue.id}")
86 end
89 end
87
90
88 def render_issue_subject_with_tree(issue)
91 def render_issue_subject_with_tree(issue)
89 s = ''
92 s = ''
90 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
93 ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
91 ancestors.each do |ancestor|
94 ancestors.each do |ancestor|
92 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
95 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
93 end
96 end
94 s << '<div>'
97 s << '<div>'
95 subject = h(issue.subject)
98 subject = h(issue.subject)
96 if issue.is_private?
99 if issue.is_private?
97 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
100 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
98 end
101 end
99 s << content_tag('h3', subject)
102 s << content_tag('h3', subject)
100 s << '</div>' * (ancestors.size + 1)
103 s << '</div>' * (ancestors.size + 1)
101 s.html_safe
104 s.html_safe
102 end
105 end
103
106
104 def render_descendants_tree(issue)
107 def render_descendants_tree(issue)
105 s = '<form><table class="list issues">'
108 s = '<form><table class="list issues">'
106 issue_list(issue.descendants.visible.preload(:status, :priority, :tracker).sort_by(&:lft)) do |child, level|
109 issue_list(issue.descendants.visible.preload(:status, :priority, :tracker).sort_by(&:lft)) do |child, level|
107 css = "issue issue-#{child.id} hascontextmenu"
110 css = "issue issue-#{child.id} hascontextmenu"
108 css << " idnt idnt-#{level}" if level > 0
111 css << " idnt idnt-#{level}" if level > 0
109 s << content_tag('tr',
112 s << content_tag('tr',
110 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
113 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
111 content_tag('td', link_to_issue(child, :project => (issue.project_id != child.project_id)), :class => 'subject', :style => 'width: 50%') +
114 content_tag('td', link_to_issue(child, :project => (issue.project_id != child.project_id)), :class => 'subject', :style => 'width: 50%') +
112 content_tag('td', h(child.status)) +
115 content_tag('td', h(child.status)) +
113 content_tag('td', link_to_user(child.assigned_to)) +
116 content_tag('td', link_to_user(child.assigned_to)) +
114 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
117 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
115 :class => css)
118 :class => css)
116 end
119 end
117 s << '</table></form>'
120 s << '</table></form>'
118 s.html_safe
121 s.html_safe
119 end
122 end
120
123
121 def issue_estimated_hours_details(issue)
124 def issue_estimated_hours_details(issue)
122 if issue.total_estimated_hours.present?
125 if issue.total_estimated_hours.present?
123 if issue.total_estimated_hours == issue.estimated_hours
126 if issue.total_estimated_hours == issue.estimated_hours
124 l_hours_short(issue.estimated_hours)
127 l_hours_short(issue.estimated_hours)
125 else
128 else
126 s = issue.estimated_hours.present? ? l_hours_short(issue.estimated_hours) : ""
129 s = issue.estimated_hours.present? ? l_hours_short(issue.estimated_hours) : ""
127 s << " (#{l(:label_total)}: #{l_hours_short(issue.total_estimated_hours)})"
130 s << " (#{l(:label_total)}: #{l_hours_short(issue.total_estimated_hours)})"
128 s.html_safe
131 s.html_safe
129 end
132 end
130 end
133 end
131 end
134 end
132
135
133 def issue_spent_hours_details(issue)
136 def issue_spent_hours_details(issue)
134 if issue.total_spent_hours > 0
137 if issue.total_spent_hours > 0
135 if issue.total_spent_hours == issue.spent_hours
138 if issue.total_spent_hours == issue.spent_hours
136 link_to(l_hours_short(issue.spent_hours), issue_time_entries_path(issue))
139 link_to(l_hours_short(issue.spent_hours), issue_time_entries_path(issue))
137 else
140 else
138 s = issue.spent_hours > 0 ? l_hours_short(issue.spent_hours) : ""
141 s = issue.spent_hours > 0 ? l_hours_short(issue.spent_hours) : ""
139 s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), issue_time_entries_path(issue)})"
142 s << " (#{l(:label_total)}: #{link_to l_hours_short(issue.total_spent_hours), issue_time_entries_path(issue)})"
140 s.html_safe
143 s.html_safe
141 end
144 end
142 end
145 end
143 end
146 end
144
147
145 # Returns an array of error messages for bulk edited issues
148 # Returns an array of error messages for bulk edited issues
146 def bulk_edit_error_messages(issues)
149 def bulk_edit_error_messages(issues)
147 messages = {}
150 messages = {}
148 issues.each do |issue|
151 issues.each do |issue|
149 issue.errors.full_messages.each do |message|
152 issue.errors.full_messages.each do |message|
150 messages[message] ||= []
153 messages[message] ||= []
151 messages[message] << issue
154 messages[message] << issue
152 end
155 end
153 end
156 end
154 messages.map { |message, issues|
157 messages.map { |message, issues|
155 "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
158 "#{message}: " + issues.map {|i| "##{i.id}"}.join(', ')
156 }
159 }
157 end
160 end
158
161
159 # Returns a link for adding a new subtask to the given issue
162 # Returns a link for adding a new subtask to the given issue
160 def link_to_new_subtask(issue)
163 def link_to_new_subtask(issue)
161 attrs = {
164 attrs = {
162 :tracker_id => issue.tracker,
165 :tracker_id => issue.tracker,
163 :parent_issue_id => issue
166 :parent_issue_id => issue
164 }
167 }
165 link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
168 link_to(l(:button_add), new_project_issue_path(issue.project, :issue => attrs))
166 end
169 end
167
170
168 class IssueFieldsRows
171 class IssueFieldsRows
169 include ActionView::Helpers::TagHelper
172 include ActionView::Helpers::TagHelper
170
173
171 def initialize
174 def initialize
172 @left = []
175 @left = []
173 @right = []
176 @right = []
174 end
177 end
175
178
176 def left(*args)
179 def left(*args)
177 args.any? ? @left << cells(*args) : @left
180 args.any? ? @left << cells(*args) : @left
178 end
181 end
179
182
180 def right(*args)
183 def right(*args)
181 args.any? ? @right << cells(*args) : @right
184 args.any? ? @right << cells(*args) : @right
182 end
185 end
183
186
184 def size
187 def size
185 @left.size > @right.size ? @left.size : @right.size
188 @left.size > @right.size ? @left.size : @right.size
186 end
189 end
187
190
188 def to_html
191 def to_html
189 html = ''.html_safe
192 html = ''.html_safe
190 blank = content_tag('th', '') + content_tag('td', '')
193 blank = content_tag('th', '') + content_tag('td', '')
191 size.times do |i|
194 size.times do |i|
192 left = @left[i] || blank
195 left = @left[i] || blank
193 right = @right[i] || blank
196 right = @right[i] || blank
194 html << content_tag('tr', left + right)
197 html << content_tag('tr', left + right)
195 end
198 end
196 html
199 html
197 end
200 end
198
201
199 def cells(label, text, options={})
202 def cells(label, text, options={})
200 content_tag('th', "#{label}:", options) + content_tag('td', text, options)
203 content_tag('th', "#{label}:", options) + content_tag('td', text, options)
201 end
204 end
202 end
205 end
203
206
204 def issue_fields_rows
207 def issue_fields_rows
205 r = IssueFieldsRows.new
208 r = IssueFieldsRows.new
206 yield r
209 yield r
207 r.to_html
210 r.to_html
208 end
211 end
209
212
210 def render_custom_fields_rows(issue)
213 def render_custom_fields_rows(issue)
211 values = issue.visible_custom_field_values
214 values = issue.visible_custom_field_values
212 return if values.empty?
215 return if values.empty?
213 ordered_values = []
216 ordered_values = []
214 half = (values.size / 2.0).ceil
217 half = (values.size / 2.0).ceil
215 half.times do |i|
218 half.times do |i|
216 ordered_values << values[i]
219 ordered_values << values[i]
217 ordered_values << values[i + half]
220 ordered_values << values[i + half]
218 end
221 end
219 s = "<tr>\n"
222 s = "<tr>\n"
220 n = 0
223 n = 0
221 ordered_values.compact.each do |value|
224 ordered_values.compact.each do |value|
222 css = "cf_#{value.custom_field.id}"
225 css = "cf_#{value.custom_field.id}"
223 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
226 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
224 s << "\t<th class=\"#{css}\">#{ custom_field_name_tag(value.custom_field) }:</th><td class=\"#{css}\">#{ h(show_value(value)) }</td>\n"
227 s << "\t<th class=\"#{css}\">#{ custom_field_name_tag(value.custom_field) }:</th><td class=\"#{css}\">#{ h(show_value(value)) }</td>\n"
225 n += 1
228 n += 1
226 end
229 end
227 s << "</tr>\n"
230 s << "</tr>\n"
228 s.html_safe
231 s.html_safe
229 end
232 end
230
233
231 # Returns the path for updating the issue form
234 # Returns the path for updating the issue form
232 # with project as the current project
235 # with project as the current project
233 def update_issue_form_path(project, issue)
236 def update_issue_form_path(project, issue)
234 options = {:format => 'js'}
237 options = {:format => 'js'}
235 if issue.new_record?
238 if issue.new_record?
236 if project
239 if project
237 new_project_issue_path(project, options)
240 new_project_issue_path(project, options)
238 else
241 else
239 new_issue_path(options)
242 new_issue_path(options)
240 end
243 end
241 else
244 else
242 edit_issue_path(issue, options)
245 edit_issue_path(issue, options)
243 end
246 end
244 end
247 end
245
248
246 # Returns the number of descendants for an array of issues
249 # Returns the number of descendants for an array of issues
247 def issues_descendant_count(issues)
250 def issues_descendant_count(issues)
248 ids = issues.reject(&:leaf?).map {|issue| issue.descendants.ids}.flatten.uniq
251 ids = issues.reject(&:leaf?).map {|issue| issue.descendants.ids}.flatten.uniq
249 ids -= issues.map(&:id)
252 ids -= issues.map(&:id)
250 ids.size
253 ids.size
251 end
254 end
252
255
253 def issues_destroy_confirmation_message(issues)
256 def issues_destroy_confirmation_message(issues)
254 issues = [issues] unless issues.is_a?(Array)
257 issues = [issues] unless issues.is_a?(Array)
255 message = l(:text_issues_destroy_confirmation)
258 message = l(:text_issues_destroy_confirmation)
256
259
257 descendant_count = issues_descendant_count(issues)
260 descendant_count = issues_descendant_count(issues)
258 if descendant_count > 0
261 if descendant_count > 0
259 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
262 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
260 end
263 end
261 message
264 message
262 end
265 end
263
266
264 # Returns an array of users that are proposed as watchers
267 # Returns an array of users that are proposed as watchers
265 # on the new issue form
268 # on the new issue form
266 def users_for_new_issue_watchers(issue)
269 def users_for_new_issue_watchers(issue)
267 users = issue.watcher_users
270 users = issue.watcher_users
268 if issue.project.users.count <= 20
271 if issue.project.users.count <= 20
269 users = (users + issue.project.users.sort).uniq
272 users = (users + issue.project.users.sort).uniq
270 end
273 end
271 users
274 users
272 end
275 end
273
276
274 def sidebar_queries
277 def sidebar_queries
275 unless @sidebar_queries
278 unless @sidebar_queries
276 @sidebar_queries = IssueQuery.visible.
279 @sidebar_queries = IssueQuery.visible.
277 order("#{Query.table_name}.name ASC").
280 order("#{Query.table_name}.name ASC").
278 # Project specific queries and global queries
281 # Project specific queries and global queries
279 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
282 where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
280 to_a
283 to_a
281 end
284 end
282 @sidebar_queries
285 @sidebar_queries
283 end
286 end
284
287
285 def query_links(title, queries)
288 def query_links(title, queries)
286 return '' if queries.empty?
289 return '' if queries.empty?
287 # links to #index on issues/show
290 # links to #index on issues/show
288 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
291 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
289
292
290 content_tag('h3', title) + "\n" +
293 content_tag('h3', title) + "\n" +
291 content_tag('ul',
294 content_tag('ul',
292 queries.collect {|query|
295 queries.collect {|query|
293 css = 'query'
296 css = 'query'
294 css << ' selected' if query == @query
297 css << ' selected' if query == @query
295 content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
298 content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
296 }.join("\n").html_safe,
299 }.join("\n").html_safe,
297 :class => 'queries'
300 :class => 'queries'
298 ) + "\n"
301 ) + "\n"
299 end
302 end
300
303
301 def render_sidebar_queries
304 def render_sidebar_queries
302 out = ''.html_safe
305 out = ''.html_safe
303 out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
306 out << query_links(l(:label_my_queries), sidebar_queries.select(&:is_private?))
304 out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
307 out << query_links(l(:label_query_plural), sidebar_queries.reject(&:is_private?))
305 out
308 out
306 end
309 end
307
310
308 def email_issue_attributes(issue, user)
311 def email_issue_attributes(issue, user)
309 items = []
312 items = []
310 %w(author status priority assigned_to category fixed_version).each do |attribute|
313 %w(author status priority assigned_to category fixed_version).each do |attribute|
311 unless issue.disabled_core_fields.include?(attribute+"_id")
314 unless issue.disabled_core_fields.include?(attribute+"_id")
312 items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
315 items << "#{l("field_#{attribute}")}: #{issue.send attribute}"
313 end
316 end
314 end
317 end
315 issue.visible_custom_field_values(user).each do |value|
318 issue.visible_custom_field_values(user).each do |value|
316 items << "#{value.custom_field.name}: #{show_value(value, false)}"
319 items << "#{value.custom_field.name}: #{show_value(value, false)}"
317 end
320 end
318 items
321 items
319 end
322 end
320
323
321 def render_email_issue_attributes(issue, user, html=false)
324 def render_email_issue_attributes(issue, user, html=false)
322 items = email_issue_attributes(issue, user)
325 items = email_issue_attributes(issue, user)
323 if html
326 if html
324 content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
327 content_tag('ul', items.map{|s| content_tag('li', s)}.join("\n").html_safe)
325 else
328 else
326 items.map{|s| "* #{s}"}.join("\n")
329 items.map{|s| "* #{s}"}.join("\n")
327 end
330 end
328 end
331 end
329
332
330 # Returns the textual representation of a journal details
333 # Returns the textual representation of a journal details
331 # as an array of strings
334 # as an array of strings
332 def details_to_strings(details, no_html=false, options={})
335 def details_to_strings(details, no_html=false, options={})
333 options[:only_path] = (options[:only_path] == false ? false : true)
336 options[:only_path] = (options[:only_path] == false ? false : true)
334 strings = []
337 strings = []
335 values_by_field = {}
338 values_by_field = {}
336 details.each do |detail|
339 details.each do |detail|
337 if detail.property == 'cf'
340 if detail.property == 'cf'
338 field = detail.custom_field
341 field = detail.custom_field
339 if field && field.multiple?
342 if field && field.multiple?
340 values_by_field[field] ||= {:added => [], :deleted => []}
343 values_by_field[field] ||= {:added => [], :deleted => []}
341 if detail.old_value
344 if detail.old_value
342 values_by_field[field][:deleted] << detail.old_value
345 values_by_field[field][:deleted] << detail.old_value
343 end
346 end
344 if detail.value
347 if detail.value
345 values_by_field[field][:added] << detail.value
348 values_by_field[field][:added] << detail.value
346 end
349 end
347 next
350 next
348 end
351 end
349 end
352 end
350 strings << show_detail(detail, no_html, options)
353 strings << show_detail(detail, no_html, options)
351 end
354 end
352 if values_by_field.present?
355 if values_by_field.present?
353 multiple_values_detail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
356 multiple_values_detail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
354 values_by_field.each do |field, changes|
357 values_by_field.each do |field, changes|
355 if changes[:added].any?
358 if changes[:added].any?
356 detail = multiple_values_detail.new('cf', field.id.to_s, field)
359 detail = multiple_values_detail.new('cf', field.id.to_s, field)
357 detail.value = changes[:added]
360 detail.value = changes[:added]
358 strings << show_detail(detail, no_html, options)
361 strings << show_detail(detail, no_html, options)
359 end
362 end
360 if changes[:deleted].any?
363 if changes[:deleted].any?
361 detail = multiple_values_detail.new('cf', field.id.to_s, field)
364 detail = multiple_values_detail.new('cf', field.id.to_s, field)
362 detail.old_value = changes[:deleted]
365 detail.old_value = changes[:deleted]
363 strings << show_detail(detail, no_html, options)
366 strings << show_detail(detail, no_html, options)
364 end
367 end
365 end
368 end
366 end
369 end
367 strings
370 strings
368 end
371 end
369
372
370 # Returns the textual representation of a single journal detail
373 # Returns the textual representation of a single journal detail
371 def show_detail(detail, no_html=false, options={})
374 def show_detail(detail, no_html=false, options={})
372 multiple = false
375 multiple = false
373 show_diff = false
376 show_diff = false
374
377
375 case detail.property
378 case detail.property
376 when 'attr'
379 when 'attr'
377 field = detail.prop_key.to_s.gsub(/\_id$/, "")
380 field = detail.prop_key.to_s.gsub(/\_id$/, "")
378 label = l(("field_" + field).to_sym)
381 label = l(("field_" + field).to_sym)
379 case detail.prop_key
382 case detail.prop_key
380 when 'due_date', 'start_date'
383 when 'due_date', 'start_date'
381 value = format_date(detail.value.to_date) if detail.value
384 value = format_date(detail.value.to_date) if detail.value
382 old_value = format_date(detail.old_value.to_date) if detail.old_value
385 old_value = format_date(detail.old_value.to_date) if detail.old_value
383
386
384 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
387 when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
385 'priority_id', 'category_id', 'fixed_version_id'
388 'priority_id', 'category_id', 'fixed_version_id'
386 value = find_name_by_reflection(field, detail.value)
389 value = find_name_by_reflection(field, detail.value)
387 old_value = find_name_by_reflection(field, detail.old_value)
390 old_value = find_name_by_reflection(field, detail.old_value)
388
391
389 when 'estimated_hours'
392 when 'estimated_hours'
390 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
393 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
391 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
394 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
392
395
393 when 'parent_id'
396 when 'parent_id'
394 label = l(:field_parent_issue)
397 label = l(:field_parent_issue)
395 value = "##{detail.value}" unless detail.value.blank?
398 value = "##{detail.value}" unless detail.value.blank?
396 old_value = "##{detail.old_value}" unless detail.old_value.blank?
399 old_value = "##{detail.old_value}" unless detail.old_value.blank?
397
400
398 when 'is_private'
401 when 'is_private'
399 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
402 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
400 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
403 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
401
404
402 when 'description'
405 when 'description'
403 show_diff = true
406 show_diff = true
404 end
407 end
405 when 'cf'
408 when 'cf'
406 custom_field = detail.custom_field
409 custom_field = detail.custom_field
407 if custom_field
410 if custom_field
408 label = custom_field.name
411 label = custom_field.name
409 if custom_field.format.class.change_as_diff
412 if custom_field.format.class.change_as_diff
410 show_diff = true
413 show_diff = true
411 else
414 else
412 multiple = custom_field.multiple?
415 multiple = custom_field.multiple?
413 value = format_value(detail.value, custom_field) if detail.value
416 value = format_value(detail.value, custom_field) if detail.value
414 old_value = format_value(detail.old_value, custom_field) if detail.old_value
417 old_value = format_value(detail.old_value, custom_field) if detail.old_value
415 end
418 end
416 end
419 end
417 when 'attachment'
420 when 'attachment'
418 label = l(:label_attachment)
421 label = l(:label_attachment)
419 when 'relation'
422 when 'relation'
420 if detail.value && !detail.old_value
423 if detail.value && !detail.old_value
421 rel_issue = Issue.visible.find_by_id(detail.value)
424 rel_issue = Issue.visible.find_by_id(detail.value)
422 value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.value}" :
425 value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.value}" :
423 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
426 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
424 elsif detail.old_value && !detail.value
427 elsif detail.old_value && !detail.value
425 rel_issue = Issue.visible.find_by_id(detail.old_value)
428 rel_issue = Issue.visible.find_by_id(detail.old_value)
426 old_value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.old_value}" :
429 old_value = rel_issue.nil? ? "#{l(:label_issue)} ##{detail.old_value}" :
427 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
430 (no_html ? rel_issue : link_to_issue(rel_issue, :only_path => options[:only_path]))
428 end
431 end
429 relation_type = IssueRelation::TYPES[detail.prop_key]
432 relation_type = IssueRelation::TYPES[detail.prop_key]
430 label = l(relation_type[:name]) if relation_type
433 label = l(relation_type[:name]) if relation_type
431 end
434 end
432 call_hook(:helper_issues_show_detail_after_setting,
435 call_hook(:helper_issues_show_detail_after_setting,
433 {:detail => detail, :label => label, :value => value, :old_value => old_value })
436 {:detail => detail, :label => label, :value => value, :old_value => old_value })
434
437
435 label ||= detail.prop_key
438 label ||= detail.prop_key
436 value ||= detail.value
439 value ||= detail.value
437 old_value ||= detail.old_value
440 old_value ||= detail.old_value
438
441
439 unless no_html
442 unless no_html
440 label = content_tag('strong', label)
443 label = content_tag('strong', label)
441 old_value = content_tag("i", h(old_value)) if detail.old_value
444 old_value = content_tag("i", h(old_value)) if detail.old_value
442 if detail.old_value && detail.value.blank? && detail.property != 'relation'
445 if detail.old_value && detail.value.blank? && detail.property != 'relation'
443 old_value = content_tag("del", old_value)
446 old_value = content_tag("del", old_value)
444 end
447 end
445 if detail.property == 'attachment' && value.present? &&
448 if detail.property == 'attachment' && value.present? &&
446 atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i}
449 atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i}
447 # Link to the attachment if it has not been removed
450 # Link to the attachment if it has not been removed
448 value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
451 value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
449 if options[:only_path] != false && atta.is_text?
452 if options[:only_path] != false && atta.is_text?
450 value += link_to(
453 value += link_to(
451 image_tag('magnifier.png'),
454 image_tag('magnifier.png'),
452 :controller => 'attachments', :action => 'show',
455 :controller => 'attachments', :action => 'show',
453 :id => atta, :filename => atta.filename
456 :id => atta, :filename => atta.filename
454 )
457 )
455 end
458 end
456 else
459 else
457 value = content_tag("i", h(value)) if value
460 value = content_tag("i", h(value)) if value
458 end
461 end
459 end
462 end
460
463
461 if show_diff
464 if show_diff
462 s = l(:text_journal_changed_no_detail, :label => label)
465 s = l(:text_journal_changed_no_detail, :label => label)
463 unless no_html
466 unless no_html
464 diff_link = link_to 'diff',
467 diff_link = link_to 'diff',
465 {:controller => 'journals', :action => 'diff', :id => detail.journal_id,
468 {:controller => 'journals', :action => 'diff', :id => detail.journal_id,
466 :detail_id => detail.id, :only_path => options[:only_path]},
469 :detail_id => detail.id, :only_path => options[:only_path]},
467 :title => l(:label_view_diff)
470 :title => l(:label_view_diff)
468 s << " (#{ diff_link })"
471 s << " (#{ diff_link })"
469 end
472 end
470 s.html_safe
473 s.html_safe
471 elsif detail.value.present?
474 elsif detail.value.present?
472 case detail.property
475 case detail.property
473 when 'attr', 'cf'
476 when 'attr', 'cf'
474 if detail.old_value.present?
477 if detail.old_value.present?
475 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
478 l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
476 elsif multiple
479 elsif multiple
477 l(:text_journal_added, :label => label, :value => value).html_safe
480 l(:text_journal_added, :label => label, :value => value).html_safe
478 else
481 else
479 l(:text_journal_set_to, :label => label, :value => value).html_safe
482 l(:text_journal_set_to, :label => label, :value => value).html_safe
480 end
483 end
481 when 'attachment', 'relation'
484 when 'attachment', 'relation'
482 l(:text_journal_added, :label => label, :value => value).html_safe
485 l(:text_journal_added, :label => label, :value => value).html_safe
483 end
486 end
484 else
487 else
485 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
488 l(:text_journal_deleted, :label => label, :old => old_value).html_safe
486 end
489 end
487 end
490 end
488
491
489 # Find the name of an associated record stored in the field attribute
492 # Find the name of an associated record stored in the field attribute
490 def find_name_by_reflection(field, id)
493 def find_name_by_reflection(field, id)
491 unless id.present?
494 unless id.present?
492 return nil
495 return nil
493 end
496 end
494 @detail_value_name_by_reflection ||= Hash.new do |hash, key|
497 @detail_value_name_by_reflection ||= Hash.new do |hash, key|
495 association = Issue.reflect_on_association(key.first.to_sym)
498 association = Issue.reflect_on_association(key.first.to_sym)
496 name = nil
499 name = nil
497 if association
500 if association
498 record = association.klass.find_by_id(key.last)
501 record = association.klass.find_by_id(key.last)
499 if record
502 if record
500 name = record.name.force_encoding('UTF-8')
503 name = record.name.force_encoding('UTF-8')
501 end
504 end
502 end
505 end
503 hash[key] = name
506 hash[key] = name
504 end
507 end
505 @detail_value_name_by_reflection[[field, id]]
508 @detail_value_name_by_reflection[[field, id]]
506 end
509 end
507
510
508 # Renders issue children recursively
511 # Renders issue children recursively
509 def render_api_issue_children(issue, api)
512 def render_api_issue_children(issue, api)
510 return if issue.leaf?
513 return if issue.leaf?
511 api.array :children do
514 api.array :children do
512 issue.children.each do |child|
515 issue.children.each do |child|
513 api.issue(:id => child.id) do
516 api.issue(:id => child.id) do
514 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
517 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
515 api.subject child.subject
518 api.subject child.subject
516 render_api_issue_children(child, api)
519 render_api_issue_children(child, api)
517 end
520 end
518 end
521 end
519 end
522 end
520 end
523 end
521 end
524 end
General Comments 0
You need to be logged in to leave comments. Login now