##// END OF EJS Templates
Slight optimization in issue tree rendering....
Jean-Philippe Lang -
r3506:e8729feba743
parent child
Show More
@@ -1,236 +1,236
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 issue_list(issues, &block)
21 def issue_list(issues, &block)
22 ancestors = []
22 ancestors = []
23 issues.each do |issue|
23 issues.each do |issue|
24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
25 ancestors.pop
25 ancestors.pop
26 end
26 end
27 yield issue, ancestors.size
27 yield issue, ancestors.size
28 ancestors << issue
28 ancestors << issue unless issue.leaf?
29 end
29 end
30 end
30 end
31
31
32 def render_issue_tooltip(issue)
32 def render_issue_tooltip(issue)
33 @cached_label_start_date ||= l(:field_start_date)
33 @cached_label_start_date ||= l(:field_start_date)
34 @cached_label_due_date ||= l(:field_due_date)
34 @cached_label_due_date ||= l(:field_due_date)
35 @cached_label_assigned_to ||= l(:field_assigned_to)
35 @cached_label_assigned_to ||= l(:field_assigned_to)
36 @cached_label_priority ||= l(:field_priority)
36 @cached_label_priority ||= l(:field_priority)
37
37
38 link_to_issue(issue) + "<br /><br />" +
38 link_to_issue(issue) + "<br /><br />" +
39 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
39 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
40 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
40 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
41 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
41 "<strong>#{@cached_label_assigned_to}</strong>: #{issue.assigned_to}<br />" +
42 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
42 "<strong>#{@cached_label_priority}</strong>: #{issue.priority.name}"
43 end
43 end
44
44
45 def render_issue_subject_with_tree(issue)
45 def render_issue_subject_with_tree(issue)
46 s = ''
46 s = ''
47 issue.ancestors.each do |ancestor|
47 issue.ancestors.each do |ancestor|
48 s << '<div>' + content_tag('p', link_to_issue(ancestor))
48 s << '<div>' + content_tag('p', link_to_issue(ancestor))
49 end
49 end
50 s << '<div>' + content_tag('h3', h(issue.subject))
50 s << '<div>' + content_tag('h3', h(issue.subject))
51 s << '</div>' * (issue.ancestors.size + 1)
51 s << '</div>' * (issue.ancestors.size + 1)
52 s
52 s
53 end
53 end
54
54
55 def render_descendants_tree(issue)
55 def render_descendants_tree(issue)
56 s = '<form><table class="list issues">'
56 s = '<form><table class="list issues">'
57 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
57 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
58 s << content_tag('tr',
58 s << content_tag('tr',
59 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
59 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
60 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
60 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
61 content_tag('td', h(child.status)) +
61 content_tag('td', h(child.status)) +
62 content_tag('td', link_to_user(child.assigned_to)) +
62 content_tag('td', link_to_user(child.assigned_to)) +
63 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
63 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
64 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
64 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
65 end
65 end
66 s << '</form></table>'
66 s << '</form></table>'
67 s
67 s
68 end
68 end
69
69
70 def render_custom_fields_rows(issue)
70 def render_custom_fields_rows(issue)
71 return if issue.custom_field_values.empty?
71 return if issue.custom_field_values.empty?
72 ordered_values = []
72 ordered_values = []
73 half = (issue.custom_field_values.size / 2.0).ceil
73 half = (issue.custom_field_values.size / 2.0).ceil
74 half.times do |i|
74 half.times do |i|
75 ordered_values << issue.custom_field_values[i]
75 ordered_values << issue.custom_field_values[i]
76 ordered_values << issue.custom_field_values[i + half]
76 ordered_values << issue.custom_field_values[i + half]
77 end
77 end
78 s = "<tr>\n"
78 s = "<tr>\n"
79 n = 0
79 n = 0
80 ordered_values.compact.each do |value|
80 ordered_values.compact.each do |value|
81 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
81 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
82 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
82 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
83 n += 1
83 n += 1
84 end
84 end
85 s << "</tr>\n"
85 s << "</tr>\n"
86 s
86 s
87 end
87 end
88
88
89 def sidebar_queries
89 def sidebar_queries
90 unless @sidebar_queries
90 unless @sidebar_queries
91 # User can see public queries and his own queries
91 # User can see public queries and his own queries
92 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
92 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
93 # Project specific queries and global queries
93 # Project specific queries and global queries
94 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
94 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
95 @sidebar_queries = Query.find(:all,
95 @sidebar_queries = Query.find(:all,
96 :select => 'id, name',
96 :select => 'id, name',
97 :order => "name ASC",
97 :order => "name ASC",
98 :conditions => visible.conditions)
98 :conditions => visible.conditions)
99 end
99 end
100 @sidebar_queries
100 @sidebar_queries
101 end
101 end
102
102
103 def show_detail(detail, no_html=false)
103 def show_detail(detail, no_html=false)
104 case detail.property
104 case detail.property
105 when 'attr'
105 when 'attr'
106 field = detail.prop_key.to_s.gsub(/\_id$/, "")
106 field = detail.prop_key.to_s.gsub(/\_id$/, "")
107 label = l(("field_" + field).to_sym)
107 label = l(("field_" + field).to_sym)
108 case
108 case
109 when ['due_date', 'start_date'].include?(detail.prop_key)
109 when ['due_date', 'start_date'].include?(detail.prop_key)
110 value = format_date(detail.value.to_date) if detail.value
110 value = format_date(detail.value.to_date) if detail.value
111 old_value = format_date(detail.old_value.to_date) if detail.old_value
111 old_value = format_date(detail.old_value.to_date) if detail.old_value
112
112
113 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
113 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
114 value = find_name_by_reflection(field, detail.value)
114 value = find_name_by_reflection(field, detail.value)
115 old_value = find_name_by_reflection(field, detail.old_value)
115 old_value = find_name_by_reflection(field, detail.old_value)
116
116
117 when detail.prop_key == 'estimated_hours'
117 when detail.prop_key == 'estimated_hours'
118 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
118 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
119 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
119 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
120
120
121 when detail.prop_key == 'parent_id'
121 when detail.prop_key == 'parent_id'
122 label = l(:field_parent_issue)
122 label = l(:field_parent_issue)
123 value = "##{detail.value}" unless detail.value.blank?
123 value = "##{detail.value}" unless detail.value.blank?
124 old_value = "##{detail.old_value}" unless detail.old_value.blank?
124 old_value = "##{detail.old_value}" unless detail.old_value.blank?
125 end
125 end
126 when 'cf'
126 when 'cf'
127 custom_field = CustomField.find_by_id(detail.prop_key)
127 custom_field = CustomField.find_by_id(detail.prop_key)
128 if custom_field
128 if custom_field
129 label = custom_field.name
129 label = custom_field.name
130 value = format_value(detail.value, custom_field.field_format) if detail.value
130 value = format_value(detail.value, custom_field.field_format) if detail.value
131 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
131 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
132 end
132 end
133 when 'attachment'
133 when 'attachment'
134 label = l(:label_attachment)
134 label = l(:label_attachment)
135 end
135 end
136 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
136 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
137
137
138 label ||= detail.prop_key
138 label ||= detail.prop_key
139 value ||= detail.value
139 value ||= detail.value
140 old_value ||= detail.old_value
140 old_value ||= detail.old_value
141
141
142 unless no_html
142 unless no_html
143 label = content_tag('strong', label)
143 label = content_tag('strong', label)
144 old_value = content_tag("i", h(old_value)) if detail.old_value
144 old_value = content_tag("i", h(old_value)) if detail.old_value
145 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
145 old_value = content_tag("strike", old_value) if detail.old_value and (!detail.value or detail.value.empty?)
146 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
146 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
147 # Link to the attachment if it has not been removed
147 # Link to the attachment if it has not been removed
148 value = link_to_attachment(a)
148 value = link_to_attachment(a)
149 else
149 else
150 value = content_tag("i", h(value)) if value
150 value = content_tag("i", h(value)) if value
151 end
151 end
152 end
152 end
153
153
154 if !detail.value.blank?
154 if !detail.value.blank?
155 case detail.property
155 case detail.property
156 when 'attr', 'cf'
156 when 'attr', 'cf'
157 if !detail.old_value.blank?
157 if !detail.old_value.blank?
158 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
158 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
159 else
159 else
160 l(:text_journal_set_to, :label => label, :value => value)
160 l(:text_journal_set_to, :label => label, :value => value)
161 end
161 end
162 when 'attachment'
162 when 'attachment'
163 l(:text_journal_added, :label => label, :value => value)
163 l(:text_journal_added, :label => label, :value => value)
164 end
164 end
165 else
165 else
166 l(:text_journal_deleted, :label => label, :old => old_value)
166 l(:text_journal_deleted, :label => label, :old => old_value)
167 end
167 end
168 end
168 end
169
169
170 # Find the name of an associated record stored in the field attribute
170 # Find the name of an associated record stored in the field attribute
171 def find_name_by_reflection(field, id)
171 def find_name_by_reflection(field, id)
172 association = Issue.reflect_on_association(field.to_sym)
172 association = Issue.reflect_on_association(field.to_sym)
173 if association
173 if association
174 record = association.class_name.constantize.find_by_id(id)
174 record = association.class_name.constantize.find_by_id(id)
175 return record.name if record
175 return record.name if record
176 end
176 end
177 end
177 end
178
178
179 def issues_to_csv(issues, project = nil)
179 def issues_to_csv(issues, project = nil)
180 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
180 ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')
181 decimal_separator = l(:general_csv_decimal_separator)
181 decimal_separator = l(:general_csv_decimal_separator)
182 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
182 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
183 # csv header fields
183 # csv header fields
184 headers = [ "#",
184 headers = [ "#",
185 l(:field_status),
185 l(:field_status),
186 l(:field_project),
186 l(:field_project),
187 l(:field_tracker),
187 l(:field_tracker),
188 l(:field_priority),
188 l(:field_priority),
189 l(:field_subject),
189 l(:field_subject),
190 l(:field_assigned_to),
190 l(:field_assigned_to),
191 l(:field_category),
191 l(:field_category),
192 l(:field_fixed_version),
192 l(:field_fixed_version),
193 l(:field_author),
193 l(:field_author),
194 l(:field_start_date),
194 l(:field_start_date),
195 l(:field_due_date),
195 l(:field_due_date),
196 l(:field_done_ratio),
196 l(:field_done_ratio),
197 l(:field_estimated_hours),
197 l(:field_estimated_hours),
198 l(:field_parent_issue),
198 l(:field_parent_issue),
199 l(:field_created_on),
199 l(:field_created_on),
200 l(:field_updated_on)
200 l(:field_updated_on)
201 ]
201 ]
202 # Export project custom fields if project is given
202 # Export project custom fields if project is given
203 # otherwise export custom fields marked as "For all projects"
203 # otherwise export custom fields marked as "For all projects"
204 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
204 custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
205 custom_fields.each {|f| headers << f.name}
205 custom_fields.each {|f| headers << f.name}
206 # Description in the last column
206 # Description in the last column
207 headers << l(:field_description)
207 headers << l(:field_description)
208 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
208 csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
209 # csv lines
209 # csv lines
210 issues.each do |issue|
210 issues.each do |issue|
211 fields = [issue.id,
211 fields = [issue.id,
212 issue.status.name,
212 issue.status.name,
213 issue.project.name,
213 issue.project.name,
214 issue.tracker.name,
214 issue.tracker.name,
215 issue.priority.name,
215 issue.priority.name,
216 issue.subject,
216 issue.subject,
217 issue.assigned_to,
217 issue.assigned_to,
218 issue.category,
218 issue.category,
219 issue.fixed_version,
219 issue.fixed_version,
220 issue.author.name,
220 issue.author.name,
221 format_date(issue.start_date),
221 format_date(issue.start_date),
222 format_date(issue.due_date),
222 format_date(issue.due_date),
223 issue.done_ratio,
223 issue.done_ratio,
224 issue.estimated_hours.to_s.gsub('.', decimal_separator),
224 issue.estimated_hours.to_s.gsub('.', decimal_separator),
225 issue.parent_id,
225 issue.parent_id,
226 format_time(issue.created_on),
226 format_time(issue.created_on),
227 format_time(issue.updated_on)
227 format_time(issue.updated_on)
228 ]
228 ]
229 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
229 custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
230 fields << issue.description
230 fields << issue.description
231 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
231 csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
232 end
232 end
233 end
233 end
234 export
234 export
235 end
235 end
236 end
236 end
General Comments 0
You need to be logged in to leave comments. Login now