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
|
... | ... | |
64 |
65 |
@month_from = 1
|
65 |
66 |
@year_from += 1
|
66 |
67 |
end
|
67 |
|
|
|
68 |
|
68 |
69 |
months = (options[:months] || User.current.pref[:gantt_months]).to_i
|
69 |
70 |
@months = (months > 0 && months < 48) ? months : 48
|
70 |
|
|
|
71 |
|
71 |
72 |
zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i
|
72 |
73 |
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2
|
73 |
74 |
|
74 |
|
# Save gantt parameters as user preference (zoom and months count)
|
75 |
|
if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] ||
|
76 |
|
@months != User.current.pref[:gantt_months]))
|
77 |
|
User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
|
|
75 |
detailed_groups = User.current.pref[:gantt_detailed_groups]
|
|
76 |
if options[:set_filter] == '1'
|
|
77 |
detailed_groups = (options[:not_detailed_groups].to_s == 'true') ? false : true
|
|
78 |
end
|
|
79 |
@detailed_groups = (detailed_groups.to_s == 'true') ? true : false
|
|
80 |
|
|
81 |
version_groups = User.current.pref[:gantt_version_groups].to_s
|
|
82 |
if options[:set_filter] == '1'
|
|
83 |
version_groups = options[:version_groups].to_s
|
|
84 |
end
|
|
85 |
@version_groups = (version_groups == 'true') ? true : false
|
|
86 |
|
|
87 |
# Save gantt parameters as user preference (zoom, months count and detailed grouping)
|
|
88 |
if (User.current.logged? &&
|
|
89 |
(@zoom != User.current.pref[:gantt_zoom] ||
|
|
90 |
@months != User.current.pref[:gantt_months] ||
|
|
91 |
@detailed_groups != User.current.pref[:gantt_detailed_groups] ||
|
|
92 |
@version_groups != User.current.pref[:gantt_version_groups]))
|
|
93 |
User.current.pref[:gantt_zoom] = @zoom
|
|
94 |
User.current.pref[:gantt_months] = @months
|
|
95 |
User.current.pref[:gantt_detailed_groups] = @detailed_groups
|
|
96 |
User.current.pref[:gantt_version_groups] = @version_groups
|
78 |
97 |
User.current.preference.save
|
79 |
98 |
end
|
80 |
99 |
|
... | ... | |
120 |
139 |
|
121 |
140 |
def params
|
122 |
141 |
common_params.merge({:zoom => zoom, :year => year_from,
|
123 |
|
:month => month_from, :months => months})
|
|
142 |
:month => month_from, :months => months,
|
|
143 |
:not_detailed_groups => !detailed_groups,
|
|
144 |
:version_groups => version_groups})
|
124 |
145 |
end
|
125 |
146 |
|
126 |
147 |
def params_previous
|
127 |
148 |
common_params.merge({:year => (date_from << months).year,
|
128 |
149 |
:month => (date_from << months).month,
|
129 |
|
:zoom => zoom, :months => months})
|
|
150 |
:zoom => zoom, :months => months,
|
|
151 |
:not_detailed_groups => !detailed_groups,
|
|
152 |
:version_groups => version_groups})
|
130 |
153 |
end
|
131 |
154 |
|
132 |
155 |
def params_next
|
133 |
156 |
common_params.merge({:year => (date_from >> months).year,
|
134 |
157 |
:month => (date_from >> months).month,
|
135 |
|
:zoom => zoom, :months => months})
|
|
158 |
:zoom => zoom, :months => months,
|
|
159 |
:not_detailed_groups => !detailed_groups,
|
|
160 |
:version_groups => version_groups})
|
136 |
161 |
end
|
137 |
162 |
|
138 |
163 |
# Returns the number of rows that will be rendered on the Gantt chart
|
... | ... | |
205 |
230 |
project_issues(project).select {|issue| issue.fixed_version == version}
|
206 |
231 |
end
|
207 |
232 |
|
|
233 |
# Returns the issues that belong to +project+ and are grouped by +group+
|
|
234 |
def group_issues!(group, issues)
|
|
235 |
result = issues.take_while {|issue| group_name(issue) == group}
|
|
236 |
issues.reject! {|issue| group_name(issue) == group}
|
|
237 |
result
|
|
238 |
end
|
|
239 |
|
208 |
240 |
def render(options={})
|
209 |
241 |
options = {:top => 0, :top_increment => 20,
|
210 |
242 |
:indent_increment => 20, :render => :subject,
|
... | ... | |
230 |
262 |
options[:indent] += options[:indent_increment]
|
231 |
263 |
@number_of_rows += 1
|
232 |
264 |
return if abort?
|
233 |
|
issues = project_issues(project).select {|i| i.fixed_version.nil?}
|
234 |
|
sort_issues!(issues)
|
235 |
|
if issues
|
|
265 |
issues = project_issues(project).select {|i| i.fixed_version.nil? || (grouped? && !@version_groups) }
|
|
266 |
if issues && grouped?
|
|
267 |
render_groups(issues, options)
|
|
268 |
return if abort?
|
|
269 |
else
|
|
270 |
sort_issues!(issues)
|
236 |
271 |
render_issues(issues, options)
|
237 |
272 |
return if abort?
|
238 |
273 |
end
|
... | ... | |
245 |
280 |
options[:indent] -= options[:indent_increment]
|
246 |
281 |
end
|
247 |
282 |
|
248 |
|
def render_issues(issues, options={})
|
249 |
|
@issue_ancestors = []
|
250 |
|
issues.each do |i|
|
251 |
|
subject_for_issue(i, options) unless options[:only] == :lines
|
252 |
|
line_for_issue(i, options) unless options[:only] == :subjects
|
253 |
|
options[:top] += options[:top_increment]
|
254 |
|
@number_of_rows += 1
|
255 |
|
break if abort?
|
256 |
|
end
|
257 |
|
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
258 |
|
end
|
259 |
|
|
260 |
283 |
def render_version(project, version, options={})
|
261 |
284 |
# Version header
|
262 |
285 |
subject_for_version(version, options) unless options[:only] == :lines
|
263 |
286 |
line_for_version(version, options) unless options[:only] == :subjects
|
264 |
287 |
options[:top] += options[:top_increment]
|
|
288 |
options[:indent] += options[:indent_increment]
|
265 |
289 |
@number_of_rows += 1
|
266 |
|
return if abort?
|
|
290 |
return if abort? || (grouped? && !@version_groups)
|
267 |
291 |
issues = version_issues(project, version)
|
268 |
|
if issues
|
|
292 |
if issues && grouped?
|
|
293 |
render_groups(issues, options)
|
|
294 |
return if abort?
|
|
295 |
else
|
269 |
296 |
sort_issues!(issues)
|
270 |
|
# Indent issues
|
271 |
|
options[:indent] += options[:indent_increment]
|
272 |
297 |
render_issues(issues, options)
|
273 |
|
options[:indent] -= options[:indent_increment]
|
|
298 |
return if abort?
|
274 |
299 |
end
|
|
300 |
# Remove indent to hit the next sibling
|
|
301 |
options[:indent] -= options[:indent_increment]
|
|
302 |
end
|
|
303 |
|
|
304 |
def render_groups(issues, options={})
|
|
305 |
while !issues.empty?
|
|
306 |
# Group header
|
|
307 |
group = group_name(issues[0])
|
|
308 |
subject_for_group(group, options) unless options[:only] == :lines
|
|
309 |
options[:top] += options[:top_increment]
|
|
310 |
@number_of_rows += 1
|
|
311 |
break if abort?
|
|
312 |
gr_issues = group_issues!(group, issues)
|
|
313 |
if gr_issues
|
|
314 |
sort_issues!(gr_issues)
|
|
315 |
# Indent issues
|
|
316 |
options[:indent] += options[:indent_increment]
|
|
317 |
render_issues(gr_issues, options)
|
|
318 |
options[:indent] -= options[:indent_increment]
|
|
319 |
end
|
|
320 |
break if abort?
|
|
321 |
end
|
|
322 |
end
|
|
323 |
|
|
324 |
def render_issues(issues, options={})
|
|
325 |
@issue_ancestors = []
|
|
326 |
if !grouped? || @detailed_groups
|
|
327 |
issues.each do |i|
|
|
328 |
if i.due_before == nil
|
|
329 |
i.due_date = i.start_date
|
|
330 |
end
|
|
331 |
subject_for_issue(i, options) unless options[:only] == :lines
|
|
332 |
line_for_issue(i, options) unless options[:only] == :subjects
|
|
333 |
options[:top] += options[:top_increment]
|
|
334 |
@number_of_rows += 1
|
|
335 |
break if abort?
|
|
336 |
end
|
|
337 |
else
|
|
338 |
group_start = options[:top] - options[:top_increment]
|
|
339 |
group_max_line = 0
|
|
340 |
group_write_line = group_max_line
|
|
341 |
dates_in_line = [[]]
|
|
342 |
@number_of_rows += 1
|
|
343 |
issues.each do |i|
|
|
344 |
if i.leaf?
|
|
345 |
if i.due_before == nil
|
|
346 |
i.due_date = i.start_date
|
|
347 |
end
|
|
348 |
group_write_line = -1
|
|
349 |
dates_in_line.each_with_index do |dates, line|
|
|
350 |
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
|
|
351 |
group_write_line = line
|
|
352 |
break
|
|
353 |
end
|
|
354 |
end
|
|
355 |
if group_write_line == -1
|
|
356 |
group_max_line += 1
|
|
357 |
group_write_line = group_max_line
|
|
358 |
dates_in_line.push([])
|
|
359 |
@number_of_rows += 1
|
|
360 |
end
|
|
361 |
options[:top] = group_start + (group_write_line * options[:top_increment])
|
|
362 |
subject_for_issue(i, options) unless options[:only] == :lines
|
|
363 |
line_for_issue(i, options) unless options[:only] == :subjects
|
|
364 |
dates_in_line[group_write_line].push([i.start_date, i.due_before])
|
|
365 |
end
|
|
366 |
break if abort?
|
|
367 |
end
|
|
368 |
options[:top] = group_start + ((group_max_line+1) * options[:top_increment])
|
|
369 |
end
|
|
370 |
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
275 |
371 |
end
|
276 |
372 |
|
277 |
373 |
def render_end(options={})
|
... | ... | |
300 |
396 |
end
|
301 |
397 |
|
302 |
398 |
def line_for_project(project, options)
|
303 |
|
# Skip versions that don't have a start_date or due date
|
|
399 |
# Skip projects that don't have a start_date or due date
|
304 |
400 |
if project.is_a?(Project) && project.start_date && project.due_date
|
305 |
401 |
options[:zoom] ||= 1
|
306 |
402 |
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
... | ... | |
363 |
459 |
end
|
364 |
460 |
end
|
365 |
461 |
|
|
462 |
def subject_for_group(group, options)
|
|
463 |
case options[:format]
|
|
464 |
when :html
|
|
465 |
html_class = ""
|
|
466 |
html_class << 'icon icon-package '
|
|
467 |
s = group.html_safe
|
|
468 |
subject = view.content_tag(:span, s,
|
|
469 |
:class => html_class).html_safe
|
|
470 |
html_subject(options, subject, :css => "version-name")
|
|
471 |
when :image
|
|
472 |
image_subject(options, group)
|
|
473 |
when :pdf
|
|
474 |
pdf_new_page?(options)
|
|
475 |
pdf_subject(options, group)
|
|
476 |
end
|
|
477 |
end
|
|
478 |
|
366 |
479 |
def subject_for_issue(issue, options)
|
367 |
480 |
while @issue_ancestors.any? && !issue.is_descendant_of?(@issue_ancestors.last)
|
368 |
481 |
@issue_ancestors.pop
|
... | ... | |
370 |
483 |
end
|
371 |
484 |
output = case options[:format]
|
372 |
485 |
when :html
|
373 |
|
css_classes = ''
|
374 |
|
css_classes << ' issue-overdue' if issue.overdue?
|
375 |
|
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
376 |
|
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
377 |
|
s = "".html_safe
|
378 |
|
if issue.assigned_to.present?
|
379 |
|
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
380 |
|
s << view.avatar(issue.assigned_to,
|
381 |
|
:class => 'gravatar icon-gravatar',
|
382 |
|
:size => 10,
|
383 |
|
:title => assigned_string).to_s.html_safe
|
384 |
|
end
|
385 |
|
s << view.link_to_issue(issue).html_safe
|
386 |
|
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
387 |
|
html_subject(options, subject, :css => "issue-subject",
|
388 |
|
:title => issue.subject) + "\n"
|
|
486 |
if !grouped? || @detailed_groups
|
|
487 |
css_classes = ''
|
|
488 |
css_classes << ' issue-overdue' if issue.overdue?
|
|
489 |
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
|
490 |
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
|
491 |
s = "".html_safe
|
|
492 |
if issue.assigned_to.present?
|
|
493 |
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
|
494 |
s << view.avatar(issue.assigned_to,
|
|
495 |
:class => 'gravatar icon-gravatar',
|
|
496 |
:size => 10,
|
|
497 |
:title => assigned_string).to_s.html_safe
|
|
498 |
end
|
|
499 |
s << view.link_to_issue(issue).html_safe
|
|
500 |
subject = view.content_tag(:span, s, :class => css_classes).html_safe
|
|
501 |
html_subject(options, subject, :css => "issue-subject",
|
|
502 |
:title => issue.subject) + "\n"
|
|
503 |
end
|
389 |
504 |
when :image
|
390 |
|
image_subject(options, issue.subject)
|
|
505 |
if !grouped? || @detailed_groups
|
|
506 |
image_subject(options, issue.subject)
|
|
507 |
end
|
391 |
508 |
when :pdf
|
392 |
509 |
pdf_new_page?(options)
|
393 |
|
pdf_subject(options, issue.subject)
|
|
510 |
pdf_subject(options, (grouped? || @detailed_groups) ? ' ' : issue.subject)
|
394 |
511 |
end
|
395 |
512 |
unless issue.leaf?
|
396 |
513 |
@issue_ancestors << issue
|
... | ... | |
403 |
520 |
# Skip issues that don't have a due_before (due_date or version's due_date)
|
404 |
521 |
if issue.is_a?(Issue) && issue.due_before
|
405 |
522 |
coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
|
406 |
|
label = "#{issue.status.name} #{issue.done_ratio}%"
|
|
523 |
label = " "
|
|
524 |
if !grouped? || @detailed_groups
|
|
525 |
label = "#{issue.status.name} #{issue.done_ratio}%"
|
|
526 |
end
|
407 |
527 |
case options[:format]
|
408 |
528 |
when :html
|
409 |
529 |
html_task(options, coords,
|
... | ... | |
742 |
862 |
[min, max]
|
743 |
863 |
end
|
744 |
864 |
|
|
865 |
def grouped?
|
|
866 |
@query.grouped?
|
|
867 |
end
|
|
868 |
|
|
869 |
def new_group?(x, y)
|
|
870 |
if grouped?
|
|
871 |
value_x = @query.group_by_column.value(x)
|
|
872 |
value_y = @query.group_by_column.value(y)
|
|
873 |
|
|
874 |
value_x != value_y
|
|
875 |
else
|
|
876 |
true
|
|
877 |
end
|
|
878 |
end
|
|
879 |
|
|
880 |
def group_name(issue)
|
|
881 |
if grouped?
|
|
882 |
result = @query.group_by_column.value(issue)
|
|
883 |
if result == nil || result == ''
|
|
884 |
'None'
|
|
885 |
else
|
|
886 |
result.to_s
|
|
887 |
end
|
|
888 |
else
|
|
889 |
issue.subject
|
|
890 |
end
|
|
891 |
end
|
|
892 |
|
745 |
893 |
def pdf_new_page?(options)
|
746 |
894 |
if options[:top] > 180
|
747 |
895 |
options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
|