36 |
36 |
end
|
37 |
37 |
|
38 |
38 |
attr_reader :year_from, :month_from, :date_from, :date_to, :zoom, :months, :truncated, :max_rows
|
|
39 |
attr_reader :detailed_groups, :version_groups
|
39 |
40 |
attr_accessor :query
|
40 |
41 |
attr_accessor :project
|
41 |
42 |
attr_accessor :view
|
... | ... | |
57 |
58 |
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2
|
58 |
59 |
months = (options[:months] || User.current.pref[:gantt_months]).to_i
|
59 |
60 |
@months = (months > 0 && months < 25) ? months : 6
|
|
61 |
detailed_groups = User.current.pref[:gantt_detailed_groups]
|
|
62 |
if options[:set_filter] == '1'
|
|
63 |
detailed_groups = (options[:not_detailed_groups].to_s == 'true') ? false : true
|
|
64 |
end
|
|
65 |
@detailed_groups = (detailed_groups.to_s == 'true') ? true : false
|
|
66 |
version_groups = User.current.pref[:gantt_version_groups].to_s
|
|
67 |
if options[:set_filter] == '1'
|
|
68 |
version_groups = options[:version_groups].to_s
|
|
69 |
end
|
|
70 |
@version_groups = (version_groups == 'true') ? true : false
|
60 |
71 |
# Save gantt parameters as user preference (zoom and months count)
|
61 |
72 |
if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] ||
|
62 |
|
@months != User.current.pref[:gantt_months]))
|
|
73 |
@months != User.current.pref[:gantt_months]) ||
|
|
74 |
@detailed_groups != User.current.pref[:gantt_detailed_groups] ||
|
|
75 |
@version_groups != User.current.pref[:gantt_version_groups])
|
63 |
76 |
User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
|
|
77 |
User.current.pref[:gantt_detailed_groups] = @detailed_groups
|
|
78 |
User.current.pref[:gantt_version_groups] = @version_groups
|
64 |
79 |
User.current.preference.save
|
65 |
80 |
end
|
66 |
81 |
@date_from = Date.civil(@year_from, @month_from, 1)
|
... | ... | |
83 |
98 |
|
84 |
99 |
def params
|
85 |
100 |
common_params.merge({:zoom => zoom, :year => year_from,
|
86 |
|
:month => month_from, :months => months})
|
|
101 |
:month => month_from, :months => months,
|
|
102 |
:not_detailed_groups => !detailed_groups,
|
|
103 |
:version_groups => version_groups})
|
87 |
104 |
end
|
88 |
105 |
|
89 |
106 |
def params_previous
|
90 |
107 |
common_params.merge({:year => (date_from << months).year,
|
91 |
108 |
:month => (date_from << months).month,
|
92 |
|
:zoom => zoom, :months => months})
|
|
109 |
:zoom => zoom, :months => months,
|
|
110 |
:not_detailed_groups => !detailed_groups,
|
|
111 |
:version_groups => version_groups})
|
93 |
112 |
end
|
94 |
113 |
|
95 |
114 |
def params_next
|
96 |
115 |
common_params.merge({:year => (date_from >> months).year,
|
97 |
116 |
:month => (date_from >> months).month,
|
98 |
|
:zoom => zoom, :months => months})
|
|
117 |
:zoom => zoom, :months => months,
|
|
118 |
:not_detailed_groups => !detailed_groups,
|
|
119 |
:version_groups => version_groups})
|
99 |
120 |
end
|
100 |
121 |
|
101 |
122 |
# Returns the number of rows that will be rendered on the Gantt chart
|
... | ... | |
168 |
189 |
project_issues(project).select {|issue| issue.fixed_version == version}
|
169 |
190 |
end
|
170 |
191 |
|
|
192 |
# Returns the issues that belong to +project+ and are grouped by +group+
|
|
193 |
def group_issues!(group, issues)
|
|
194 |
result = issues.take_while {|issue| group_name(issue) == group}
|
|
195 |
issues.reject! {|issue| group_name(issue) == group}
|
|
196 |
result
|
|
197 |
end
|
|
198 |
|
171 |
199 |
def render(options={})
|
172 |
200 |
options = {:top => 0, :top_increment => 20,
|
173 |
201 |
:indent_increment => 20, :render => :subject,
|
... | ... | |
193 |
221 |
options[:indent] += options[:indent_increment]
|
194 |
222 |
@number_of_rows += 1
|
195 |
223 |
return if abort?
|
196 |
|
issues = project_issues(project).select {|i| i.fixed_version.nil?}
|
197 |
|
sort_issues!(issues)
|
198 |
|
if issues
|
|
224 |
issues = project_issues(project).select {|i| i.fixed_version.nil? || (grouped? && !@version_groups) }
|
|
225 |
if issues && grouped?
|
|
226 |
render_groups(issues, options)
|
|
227 |
return if abort?
|
|
228 |
else
|
|
229 |
sort_issues!(issues)
|
199 |
230 |
render_issues(issues, options)
|
200 |
231 |
return if abort?
|
201 |
232 |
end
|
... | ... | |
207 |
238 |
options[:indent] -= options[:indent_increment]
|
208 |
239 |
end
|
209 |
240 |
|
210 |
|
def render_issues(issues, options={})
|
211 |
|
@issue_ancestors = []
|
212 |
|
issues.each do |i|
|
213 |
|
subject_for_issue(i, options) unless options[:only] == :lines
|
214 |
|
line_for_issue(i, options) unless options[:only] == :subjects
|
215 |
|
options[:top] += options[:top_increment]
|
216 |
|
@number_of_rows += 1
|
217 |
|
break if abort?
|
218 |
|
end
|
219 |
|
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
220 |
|
end
|
221 |
|
|
222 |
241 |
def render_version(project, version, options={})
|
223 |
242 |
# Version header
|
224 |
243 |
subject_for_version(version, options) unless options[:only] == :lines
|
225 |
244 |
line_for_version(version, options) unless options[:only] == :subjects
|
226 |
245 |
options[:top] += options[:top_increment]
|
|
246 |
options[:indent] += options[:indent_increment]
|
227 |
247 |
@number_of_rows += 1
|
228 |
|
return if abort?
|
|
248 |
return if abort? || (grouped? && !@version_groups)
|
229 |
249 |
issues = version_issues(project, version)
|
230 |
|
if issues
|
|
250 |
if issues && grouped?
|
|
251 |
render_groups(issues, options)
|
|
252 |
return if abort?
|
|
253 |
else
|
231 |
254 |
sort_issues!(issues)
|
232 |
|
# Indent issues
|
233 |
|
options[:indent] += options[:indent_increment]
|
234 |
255 |
render_issues(issues, options)
|
235 |
|
options[:indent] -= options[:indent_increment]
|
|
256 |
return if abort?
|
|
257 |
end
|
|
258 |
# Remove indent to hit the next sibling
|
|
259 |
options[:indent] -= options[:indent_increment]
|
|
260 |
end
|
|
261 |
|
|
262 |
def render_groups(issues, options={})
|
|
263 |
while !issues.empty?
|
|
264 |
# Group header
|
|
265 |
group = group_name(issues[0])
|
|
266 |
subject_for_group(group, options) unless options[:only] == :lines
|
|
267 |
options[:top] += options[:top_increment]
|
|
268 |
@number_of_rows += 1
|
|
269 |
break if abort?
|
|
270 |
gr_issues = group_issues!(group, issues)
|
|
271 |
if gr_issues
|
|
272 |
sort_issues!(gr_issues)
|
|
273 |
# Indent issues
|
|
274 |
options[:indent] += options[:indent_increment]
|
|
275 |
render_issues(gr_issues, options)
|
|
276 |
options[:indent] -= options[:indent_increment]
|
|
277 |
end
|
|
278 |
break if abort?
|
|
279 |
end
|
|
280 |
end
|
|
281 |
|
|
282 |
def render_issues(issues, options={})
|
|
283 |
@issue_ancestors = []
|
|
284 |
if !grouped? || @detailed_groups
|
|
285 |
issues.each do |i|
|
|
286 |
if i.due_before == nil
|
|
287 |
i.due_date = i.start_date
|
|
288 |
end
|
|
289 |
subject_for_issue(i, options) unless options[:only] == :lines
|
|
290 |
line_for_issue(i, options) unless options[:only] == :subjects
|
|
291 |
options[:top] += options[:top_increment]
|
|
292 |
@number_of_rows += 1
|
|
293 |
break if abort?
|
|
294 |
end
|
|
295 |
else
|
|
296 |
group_start = options[:top] - options[:top_increment]
|
|
297 |
group_max_line = 0
|
|
298 |
group_write_line = group_max_line
|
|
299 |
dates_in_line = [[]]
|
|
300 |
@number_of_rows += 1
|
|
301 |
issues.each do |i|
|
|
302 |
if i.leaf?
|
|
303 |
if i.due_before == nil
|
|
304 |
i.due_date = i.start_date
|
|
305 |
end
|
|
306 |
group_write_line = -1
|
|
307 |
dates_in_line.each_with_index do |dates, line|
|
|
308 |
if dates.find {|e| (i.start_date >= e[0] && i.start_date <= e[1]) || (i.start_date <= e[0] && i.due_before >= e[1]) } == nil
|
|
309 |
group_write_line = line
|
|
310 |
break
|
|
311 |
end
|
|
312 |
end
|
|
313 |
if group_write_line == -1
|
|
314 |
group_max_line += 1
|
|
315 |
group_write_line = group_max_line
|
|
316 |
dates_in_line.push([])
|
|
317 |
@number_of_rows += 1
|
|
318 |
end
|
|
319 |
options[:top] = group_start + (group_write_line * options[:top_increment])
|
|
320 |
subject_for_issue(i, options) unless options[:only] == :lines
|
|
321 |
line_for_issue(i, options) unless options[:only] == :subjects
|
|
322 |
dates_in_line[group_write_line].push([i.start_date, i.due_before])
|
|
323 |
end
|
|
324 |
break if abort?
|
|
325 |
end
|
|
326 |
options[:top] = group_start + ((group_max_line+1) * options[:top_increment])
|
236 |
327 |
end
|
|
328 |
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
237 |
329 |
end
|
238 |
330 |
|
239 |
331 |
def render_end(options={})
|
... | ... | |
262 |
354 |
end
|
263 |
355 |
|
264 |
356 |
def line_for_project(project, options)
|
265 |
|
# Skip versions that don't have a start_date or due date
|
|
357 |
# Skip projects that don't have a start_date or due date
|
266 |
358 |
if project.is_a?(Project) && project.start_date && project.due_date
|
267 |
359 |
options[:zoom] ||= 1
|
268 |
360 |
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
... | ... | |
282 |
374 |
end
|
283 |
375 |
end
|
284 |
376 |
|
|
377 |
def subject_for_group(group, options)
|
|
378 |
case options[:format]
|
|
379 |
when :html
|
|
380 |
html_class = ""
|
|
381 |
html_class << 'icon icon-package '
|
|
382 |
s = group.html_safe
|
|
383 |
subject = view.content_tag(:span, s,
|
|
384 |
:class => html_class).html_safe
|
|
385 |
html_subject(options, subject, :css => "version-name")
|
|
386 |
when :image
|
|
387 |
image_subject(options, group)
|
|
388 |
when :pdf
|
|
389 |
pdf_new_page?(options)
|
|
390 |
pdf_subject(options, group)
|
|
391 |
end
|
|
392 |
end
|
|
393 |
|
285 |
394 |
def subject_for_version(version, options)
|
286 |
395 |
case options[:format]
|
287 |
396 |
when :html
|
... | ... | |
332 |
441 |
end
|
333 |
442 |
output = case options[:format]
|
334 |
443 |
when :html
|
335 |
|
css_classes = ''
|
336 |
|
css_classes << ' issue-overdue' if issue.overdue?
|
337 |
|
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
338 |
|
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
339 |
|
s = "".html_safe
|
340 |
|
if issue.assigned_to.present?
|
341 |
|
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
342 |
|
s << view.avatar(issue.assigned_to,
|
343 |
|
:class => 'gravatar icon-gravatar',
|
344 |
|
:size => 10,
|
345 |
|
:title => assigned_string).to_s.html_safe
|
346 |
|
end
|
347 |
|
s << view.link_to_issue(issue).html_safe
|
348 |
|
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
349 |
|
html_subject(options, subject, :css => "issue-subject",
|
350 |
|
:title => issue.subject) + "\n"
|
|
444 |
if !grouped? || @detailed_groups
|
|
445 |
css_classes = ''
|
|
446 |
css_classes << ' issue-overdue' if issue.overdue?
|
|
447 |
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
|
448 |
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
|
449 |
s = "".html_safe
|
|
450 |
if issue.assigned_to.present?
|
|
451 |
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
|
452 |
s << view.avatar(issue.assigned_to,
|
|
453 |
:class => 'gravatar icon-gravatar',
|
|
454 |
:size => 10,
|
|
455 |
:title => assigned_string).to_s.html_safe
|
|
456 |
end
|
|
457 |
s << view.link_to_issue(issue).html_safe
|
|
458 |
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
|
459 |
html_subject(options, subject, :css => "issue-subject",
|
|
460 |
:title => issue.subject) + "\n"
|
|
461 |
end
|
351 |
462 |
when :image
|
352 |
|
image_subject(options, issue.subject)
|
|
463 |
if !grouped? || @detailed_groups
|
|
464 |
image_subject(options, issue.subject)
|
|
465 |
end
|
353 |
466 |
when :pdf
|
354 |
467 |
pdf_new_page?(options)
|
355 |
|
pdf_subject(options, issue.subject)
|
|
468 |
pdf_subject(options, (grouped? || @detailed_groups) ? ' ' : issue.subject)
|
356 |
469 |
end
|
357 |
470 |
unless issue.leaf?
|
358 |
471 |
@issue_ancestors << issue
|
... | ... | |
365 |
478 |
# Skip issues that don't have a due_before (due_date or version's due_date)
|
366 |
479 |
if issue.is_a?(Issue) && issue.due_before
|
367 |
480 |
coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
|
368 |
|
label = "#{issue.status.name} #{issue.done_ratio}%"
|
|
481 |
label = " "
|
|
482 |
if !grouped? || @detailed_groups
|
|
483 |
label = "#{issue.status.name} #{issue.done_ratio}%"
|
|
484 |
end
|
369 |
485 |
case options[:format]
|
370 |
486 |
when :html
|
371 |
487 |
html_task(options, coords,
|
... | ... | |
666 |
782 |
end
|
667 |
783 |
end
|
668 |
784 |
|
|
785 |
def grouped?
|
|
786 |
@query.grouped?
|
|
787 |
end
|
|
788 |
|
|
789 |
def new_group?(x, y)
|
|
790 |
if grouped?
|
|
791 |
value_x = @query.group_by_column.value(x)
|
|
792 |
value_y = @query.group_by_column.value(y)
|
|
793 |
value_x != value_y
|
|
794 |
else
|
|
795 |
true
|
|
796 |
end
|
|
797 |
end
|
|
798 |
|
|
799 |
def group_name(issue)
|
|
800 |
if grouped?
|
|
801 |
result = @query.group_by_column.value(issue)
|
|
802 |
if result == nil || result == ''
|
|
803 |
'None'
|
|
804 |
else
|
|
805 |
result.to_s
|
|
806 |
end
|
|
807 |
else
|
|
808 |
issue.subject
|
|
809 |
end
|
|
810 |
end
|
|
811 |
|
669 |
812 |
def pdf_new_page?(options)
|
670 |
813 |
if options[:top] > 180
|
671 |
814 |
options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
|