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