19 |
19 |
|
20 |
20 |
module IssuesHelper
|
21 |
21 |
include ApplicationHelper
|
22 |
|
include Redmine::Export::PDF::IssuesPdfHelper
|
23 |
22 |
|
24 |
23 |
def issue_list(issues, &block)
|
25 |
24 |
ancestors = []
|
... | ... | |
32 |
31 |
end
|
33 |
32 |
end
|
34 |
33 |
|
35 |
|
def grouped_issue_list(issues, query, issue_count_by_group, &block)
|
36 |
|
previous_group, first = false, true
|
37 |
|
issue_list(issues) do |issue, level|
|
38 |
|
group_name = group_count = nil
|
39 |
|
if query.grouped? && ((group = query.group_by_column.value(issue)) != previous_group || first)
|
40 |
|
if group.blank? && group != false
|
41 |
|
group_name = "(#{l(:label_blank_value)})"
|
42 |
|
else
|
43 |
|
group_name = column_content(query.group_by_column, issue)
|
44 |
|
end
|
45 |
|
group_name ||= ""
|
46 |
|
group_count = issue_count_by_group[group]
|
47 |
|
end
|
48 |
|
yield issue, level, group_name, group_count
|
49 |
|
previous_group, first = group, false
|
50 |
|
end
|
51 |
|
end
|
52 |
|
|
53 |
34 |
# Renders a HTML/CSS tooltip
|
54 |
35 |
#
|
55 |
36 |
# To use, a trigger div is needed. This is a div with the class of "tooltip"
|
... | ... | |
82 |
63 |
|
83 |
64 |
def render_issue_subject_with_tree(issue)
|
84 |
65 |
s = ''
|
85 |
|
ancestors = issue.root? ? [] : issue.ancestors.visible.to_a
|
|
66 |
ancestors = issue.root? ? [] : issue.ancestors.visible.all
|
86 |
67 |
ancestors.each do |ancestor|
|
87 |
68 |
s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
|
88 |
69 |
end
|
... | ... | |
103 |
84 |
css << " idnt idnt-#{level}" if level > 0
|
104 |
85 |
s << content_tag('tr',
|
105 |
86 |
content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
|
106 |
|
content_tag('td', link_to_issue(child, :project => (issue.project_id != child.project_id)), :class => 'subject', :style => 'width: 50%') +
|
|
87 |
content_tag('td', link_to_issue(child, :truncate => 60, :project => (issue.project_id != child.project_id)), :class => 'subject') +
|
107 |
88 |
content_tag('td', h(child.status)) +
|
108 |
89 |
content_tag('td', link_to_user(child.assigned_to)) +
|
109 |
90 |
content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
|
... | ... | |
181 |
162 |
def render_custom_fields_rows(issue)
|
182 |
163 |
values = issue.visible_custom_field_values
|
183 |
164 |
return if values.empty?
|
|
165 |
|
|
166 |
#Don't take long text fields
|
|
167 |
non_longtext_values = []
|
|
168 |
values.compact.each do |value|
|
|
169 |
if !(value.custom_field.format.is_a?(Redmine::FieldFormat::TextFormat)) then
|
|
170 |
non_longtext_values << value
|
|
171 |
end
|
|
172 |
end
|
|
173 |
|
184 |
174 |
ordered_values = []
|
185 |
|
half = (values.size / 2.0).ceil
|
|
175 |
half = (non_longtext_values.size / 2.0).ceil
|
186 |
176 |
half.times do |i|
|
187 |
|
ordered_values << values[i]
|
188 |
|
ordered_values << values[i + half]
|
|
177 |
ordered_values << non_longtext_values[i]
|
|
178 |
ordered_values << non_longtext_values[i + half]
|
189 |
179 |
end
|
190 |
180 |
s = "<tr>\n"
|
191 |
181 |
n = 0
|
192 |
182 |
ordered_values.compact.each do |value|
|
193 |
183 |
css = "cf_#{value.custom_field.id}"
|
194 |
184 |
s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
|
195 |
|
s << "\t<th class=\"#{css}\">#{ h(value.custom_field.name) }:</th><td class=\"#{css}\">#{ h(show_value(value)) }</td>\n"
|
|
185 |
s << "\t<th class=\"#{css}\">#{ h(value.custom_field.name) }:</th><td class=\"#{css}\">#{ h(show_value(value)) }</td>\n"
|
196 |
186 |
n += 1
|
197 |
187 |
end
|
198 |
188 |
s << "</tr>\n"
|
199 |
189 |
s.html_safe
|
200 |
190 |
end
|
201 |
191 |
|
202 |
|
# Returns the path for updating the issue form
|
203 |
|
# with project as the current project
|
204 |
|
def update_issue_form_path(project, issue)
|
205 |
|
options = {:format => 'js'}
|
206 |
|
if issue.new_record?
|
207 |
|
if project
|
208 |
|
new_project_issue_path(project, options)
|
209 |
|
else
|
210 |
|
new_issue_path(options)
|
211 |
|
end
|
212 |
|
else
|
213 |
|
edit_issue_path(issue, options)
|
|
192 |
def render_custom_fields_long_text_rows(issue)
|
|
193 |
values = issue.visible_custom_field_values
|
|
194 |
return if values.empty?
|
|
195 |
|
|
196 |
#Only take long text fields
|
|
197 |
longtext_values = []
|
|
198 |
values.compact.each do |value|
|
|
199 |
if value.custom_field.format.is_a?(Redmine::FieldFormat::TextFormat) then
|
|
200 |
longtext_values << value
|
|
201 |
end
|
|
202 |
end
|
|
203 |
|
|
204 |
s = "<tr>\n</tr>\n"
|
|
205 |
longtext_values.compact.each do |value|
|
|
206 |
css = "cf_#{value.custom_field.id}"
|
|
207 |
s << "<tr>\n<td class=\"#{css}\"></td>\n</tr>\n"
|
|
208 |
s << "<tr>\n\t<th class=\"#{css}\">#{ h(value.custom_field.name) }:</th>\n</tr>\n"
|
|
209 |
s << "<tr>\n\t<td class=\"#{css}\">#{ h(show_value(value)) }</td>\n</tr>\n"
|
|
210 |
s << "<tr>\n<td class=\"#{css}\"></td>\n</tr>\n"
|
214 |
211 |
end
|
|
212 |
s << "<tr>\n</tr>\n"
|
|
213 |
s.html_safe
|
215 |
214 |
end
|
216 |
215 |
|
217 |
|
# Returns the number of descendants for an array of issues
|
218 |
|
def issues_descendant_count(issues)
|
219 |
|
ids = issues.reject(&:leaf?).map {|issue| issue.descendants.ids}.flatten.uniq
|
220 |
|
ids -= issues.map(&:id)
|
221 |
|
ids.size
|
222 |
|
end
|
223 |
216 |
|
224 |
217 |
def issues_destroy_confirmation_message(issues)
|
225 |
218 |
issues = [issues] unless issues.is_a?(Array)
|
226 |
219 |
message = l(:text_issues_destroy_confirmation)
|
227 |
|
|
228 |
|
descendant_count = issues_descendant_count(issues)
|
|
220 |
descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
|
229 |
221 |
if descendant_count > 0
|
230 |
|
message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
|
|
222 |
tom_fields_long_text_rowseach do |issue|
|
|
223 |
next if issue.root?
|
|
224 |
issues.each do |other_issue|
|
|
225 |
descendant_count -= 1 if issue.is_descendant_of?(other_issue)
|
|
226 |
end
|
|
227 |
end
|
|
228 |
if descendant_count > 0
|
|
229 |
message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
|
|
230 |
end
|
231 |
231 |
end
|
232 |
232 |
message
|
233 |
233 |
end
|
234 |
234 |
|
235 |
|
# Returns an array of users that are proposed as watchers
|
236 |
|
# on the new issue form
|
237 |
|
def users_for_new_issue_watchers(issue)
|
238 |
|
users = issue.watcher_users
|
239 |
|
if issue.project.users.count <= 20
|
240 |
|
users = (users + issue.project.users.sort).uniq
|
241 |
|
end
|
242 |
|
users
|
243 |
|
end
|
244 |
|
|
245 |
235 |
def sidebar_queries
|
246 |
236 |
unless @sidebar_queries
|
247 |
237 |
@sidebar_queries = IssueQuery.visible.
|
248 |
238 |
order("#{Query.table_name}.name ASC").
|
249 |
239 |
# Project specific queries and global queries
|
250 |
240 |
where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
|
251 |
|
to_a
|
|
241 |
all
|
252 |
242 |
end
|
253 |
243 |
@sidebar_queries
|
254 |
244 |
end
|
... | ... | |
337 |
327 |
# Returns the textual representation of a single journal detail
|
338 |
328 |
def show_detail(detail, no_html=false, options={})
|
339 |
329 |
multiple = false
|
340 |
|
show_diff = false
|
341 |
|
|
342 |
330 |
case detail.property
|
343 |
331 |
when 'attr'
|
344 |
332 |
field = detail.prop_key.to_s.gsub(/\_id$/, "")
|
... | ... | |
365 |
353 |
when 'is_private'
|
366 |
354 |
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
|
367 |
355 |
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
|
368 |
|
|
369 |
|
when 'description'
|
370 |
|
show_diff = true
|
371 |
356 |
end
|
372 |
357 |
when 'cf'
|
373 |
358 |
custom_field = detail.custom_field
|
374 |
359 |
if custom_field
|
|
360 |
multiple = custom_field.multiple?
|
375 |
361 |
label = custom_field.name
|
376 |
|
if custom_field.format.class.change_as_diff
|
377 |
|
show_diff = true
|
378 |
|
else
|
379 |
|
multiple = custom_field.multiple?
|
380 |
|
value = format_value(detail.value, custom_field) if detail.value
|
381 |
|
old_value = format_value(detail.old_value, custom_field) if detail.old_value
|
382 |
|
end
|
|
362 |
value = format_value(detail.value, custom_field) if detail.value
|
|
363 |
old_value = format_value(detail.old_value, custom_field) if detail.old_value
|
383 |
364 |
end
|
384 |
365 |
when 'attachment'
|
385 |
366 |
label = l(:label_attachment)
|
... | ... | |
409 |
390 |
if detail.old_value && detail.value.blank? && detail.property != 'relation'
|
410 |
391 |
old_value = content_tag("del", old_value)
|
411 |
392 |
end
|
412 |
|
if detail.property == 'attachment' && value.present? &&
|
413 |
|
atta = detail.journal.journalized.attachments.detect {|a| a.id == detail.prop_key.to_i}
|
|
393 |
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
|
414 |
394 |
# Link to the attachment if it has not been removed
|
415 |
395 |
value = link_to_attachment(atta, :download => true, :only_path => options[:only_path])
|
416 |
396 |
if options[:only_path] != false && atta.is_text?
|
... | ... | |
425 |
405 |
end
|
426 |
406 |
end
|
427 |
407 |
|
428 |
|
if show_diff
|
|
408 |
if detail.property == 'attr' && detail.prop_key == 'description'
|
429 |
409 |
s = l(:text_journal_changed_no_detail, :label => label)
|
430 |
410 |
unless no_html
|
431 |
411 |
diff_link = link_to 'diff',
|
... | ... | |
458 |
438 |
unless id.present?
|
459 |
439 |
return nil
|
460 |
440 |
end
|
461 |
|
@detail_value_name_by_reflection ||= Hash.new do |hash, key|
|
462 |
|
association = Issue.reflect_on_association(key.first.to_sym)
|
463 |
|
name = nil
|
464 |
|
if association
|
465 |
|
record = association.klass.find_by_id(key.last)
|
466 |
|
if record
|
467 |
|
name = record.name.force_encoding('UTF-8')
|
468 |
|
end
|
|
441 |
association = Issue.reflect_on_association(field.to_sym)
|
|
442 |
if association
|
|
443 |
record = association.class_name.constantize.find_by_id(id)
|
|
444 |
if record
|
|
445 |
record.name.force_encoding('UTF-8') if record.name.respond_to?(:force_encoding)
|
|
446 |
return record.name
|
469 |
447 |
end
|
470 |
|
hash[key] = name
|
471 |
448 |
end
|
472 |
|
@detail_value_name_by_reflection[[field, id]]
|
473 |
449 |
end
|
474 |
450 |
|
475 |
451 |
# Renders issue children recursively
|