5 |
5 |
# modify it under the terms of the GNU General Public License
|
6 |
6 |
# as published by the Free Software Foundation; either version 2
|
7 |
7 |
# of the License, or (at your option) any later version.
|
8 |
|
#
|
|
8 |
#
|
9 |
9 |
# This program is distributed in the hope that it will be useful,
|
10 |
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11 |
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
... | ... | |
38 |
38 |
attr_accessor :query
|
39 |
39 |
attr_accessor :project
|
40 |
40 |
attr_accessor :view
|
41 |
|
|
|
41 |
|
42 |
42 |
def initialize(options={})
|
43 |
43 |
options = options.dup
|
44 |
|
|
|
44 |
|
45 |
45 |
if options[:year] && options[:year].to_i >0
|
46 |
46 |
@year_from = options[:year].to_i
|
47 |
47 |
if options[:month] && options[:month].to_i >=1 && options[:month].to_i <= 12
|
... | ... | |
53 |
53 |
@month_from ||= Date.today.month
|
54 |
54 |
@year_from ||= Date.today.year
|
55 |
55 |
end
|
56 |
|
|
|
56 |
|
57 |
57 |
zoom = (options[:zoom] || User.current.pref[:gantt_zoom]).to_i
|
58 |
|
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2
|
|
58 |
@zoom = (zoom > 0 && zoom < 5) ? zoom : 2
|
59 |
59 |
months = (options[:months] || User.current.pref[:gantt_months]).to_i
|
60 |
60 |
@months = (months > 0 && months < 25) ? months : 6
|
61 |
|
|
|
61 |
|
62 |
62 |
# Save gantt parameters as user preference (zoom and months count)
|
63 |
63 |
if (User.current.logged? && (@zoom != User.current.pref[:gantt_zoom] || @months != User.current.pref[:gantt_months]))
|
64 |
64 |
User.current.pref[:gantt_zoom], User.current.pref[:gantt_months] = @zoom, @months
|
65 |
65 |
User.current.preference.save
|
66 |
66 |
end
|
67 |
|
|
|
67 |
|
68 |
68 |
@date_from = Date.civil(@year_from, @month_from, 1)
|
69 |
69 |
@date_to = (@date_from >> @months) - 1
|
70 |
|
|
|
70 |
|
71 |
71 |
@subjects = ''
|
72 |
72 |
@lines = ''
|
|
73 |
@calendars = ''
|
73 |
74 |
@number_of_rows = nil
|
74 |
|
|
|
75 |
|
75 |
76 |
@issue_ancestors = []
|
76 |
|
|
|
77 |
|
77 |
78 |
@truncated = false
|
78 |
79 |
if options.has_key?(:max_rows)
|
79 |
80 |
@max_rows = options[:max_rows]
|
... | ... | |
85 |
86 |
def common_params
|
86 |
87 |
{ :controller => 'gantts', :action => 'show', :project_id => @project }
|
87 |
88 |
end
|
88 |
|
|
|
89 |
|
89 |
90 |
def params
|
90 |
91 |
common_params.merge({ :zoom => zoom, :year => year_from, :month => month_from, :months => months })
|
91 |
92 |
end
|
92 |
|
|
|
93 |
|
93 |
94 |
def params_previous
|
94 |
95 |
common_params.merge({:year => (date_from << months).year, :month => (date_from << months).month, :zoom => zoom, :months => months })
|
95 |
96 |
end
|
96 |
|
|
|
97 |
|
97 |
98 |
def params_next
|
98 |
99 |
common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
|
99 |
100 |
end
|
... | ... | |
102 |
103 |
# Returns the number of rows that will be rendered on the Gantt chart
|
103 |
104 |
def number_of_rows
|
104 |
105 |
return @number_of_rows if @number_of_rows
|
105 |
|
|
|
106 |
|
106 |
107 |
rows = if @project
|
107 |
108 |
number_of_rows_on_project(@project)
|
108 |
109 |
else
|
... | ... | |
110 |
111 |
total += number_of_rows_on_project(project)
|
111 |
112 |
end
|
112 |
113 |
end
|
113 |
|
|
|
114 |
|
114 |
115 |
rows > @max_rows ? @max_rows : rows
|
115 |
116 |
end
|
116 |
117 |
|
... | ... | |
154 |
155 |
render(options.merge(:only => :lines)) unless @lines_rendered
|
155 |
156 |
@lines
|
156 |
157 |
end
|
157 |
|
|
|
158 |
|
|
159 |
# Renders the calendars of the Gantt chart, the right side
|
|
160 |
def calendars(options={})
|
|
161 |
render(options.merge(:only => :calendars)) unless @calendars_rendered
|
|
162 |
@calendars
|
|
163 |
end
|
|
164 |
|
158 |
165 |
def render(options={})
|
159 |
166 |
options = {:indent => 4, :render => :subject, :format => :html}.merge(options)
|
160 |
|
|
161 |
|
@subjects = '' unless options[:only] == :lines
|
162 |
|
@lines = '' unless options[:only] == :subjects
|
|
167 |
|
|
168 |
if options[:format] == :html
|
|
169 |
@subjects = '' unless options[:only] == :lines && options[:only] == :calendars
|
|
170 |
@lines = '' unless options[:only] == :subjects && options[:only] == :calendars
|
|
171 |
@calendars = '' unless options[:only] == :lines && options[:only] == :subjects
|
|
172 |
else
|
|
173 |
@subjects = '' unless options[:only] == :lines
|
|
174 |
@lines = '' unless options[:only] == :subjects
|
|
175 |
end
|
163 |
176 |
@number_of_rows = 0
|
164 |
|
|
|
177 |
|
165 |
178 |
if @project
|
166 |
179 |
render_project(@project, options)
|
167 |
180 |
else
|
... | ... | |
170 |
183 |
break if abort?
|
171 |
184 |
end
|
172 |
185 |
end
|
|
186 |
|
|
187 |
if options[:format] == :html
|
|
188 |
@subjects_rendered = true unless options[:only] == :lines && options[:only] == :calendars
|
|
189 |
@lines_rendered = true unless options[:only] == :subjects && options[:only] == :calendars
|
|
190 |
@calendars_rendered = true unless options[:only] == :lines && options[:only] == :subjects
|
|
191 |
else
|
|
192 |
@subjects_rendered = true unless options[:only] == :lines
|
|
193 |
@lines_rendered = true unless options[:only] == :subjects
|
|
194 |
end
|
173 |
195 |
|
174 |
|
@subjects_rendered = true unless options[:only] == :lines
|
175 |
|
@lines_rendered = true unless options[:only] == :subjects
|
176 |
|
|
177 |
196 |
render_end(options)
|
178 |
197 |
end
|
179 |
198 |
|
... | ... | |
182 |
201 |
options[:indent_increment] = 20 unless options.key? :indent_increment
|
183 |
202 |
options[:top_increment] = 20 unless options.key? :top_increment
|
184 |
203 |
|
185 |
|
subject_for_project(project, options) unless options[:only] == :lines
|
186 |
|
line_for_project(project, options) unless options[:only] == :subjects
|
187 |
|
|
|
204 |
if options[:format] == :html
|
|
205 |
subject_for_project(project, options) unless options[:only] == :lines && options[:only] == :calendars
|
|
206 |
line_for_project(project, options) unless options[:only] == :subjects && options[:only] == :calendars
|
|
207 |
calendar_for_project(project, options) unless options[:only] == :lines && options[:only] == :subjects
|
|
208 |
else
|
|
209 |
subject_for_project(project, options) unless options[:only] == :lines
|
|
210 |
line_for_project(project, options) unless options[:only] == :subjects
|
|
211 |
end
|
|
212 |
|
188 |
213 |
options[:top] += options[:top_increment]
|
189 |
214 |
options[:indent] += options[:indent_increment]
|
190 |
215 |
@number_of_rows += 1
|
191 |
216 |
return if abort?
|
192 |
|
|
|
217 |
|
193 |
218 |
# Second, Issues without a version
|
194 |
219 |
issues = project.issues.for_gantt.without_version.with_query(@query).all(:limit => current_limit)
|
195 |
220 |
sort_issues!(issues)
|
... | ... | |
216 |
241 |
|
217 |
242 |
def render_issues(issues, options={})
|
218 |
243 |
@issue_ancestors = []
|
219 |
|
|
|
244 |
|
220 |
245 |
issues.each do |i|
|
221 |
|
subject_for_issue(i, options) unless options[:only] == :lines
|
222 |
|
line_for_issue(i, options) unless options[:only] == :subjects
|
223 |
|
|
|
246 |
if options[:format] == :html
|
|
247 |
subject_for_issue(i, options) unless options[:only] == :lines && options[:only] == :calendars
|
|
248 |
line_for_issue(i, options) unless options[:only] == :subjects && options[:only] == :calendars
|
|
249 |
calendar_for_issue(i, options) unless options[:only] == :lines && options[:only] == :subjects
|
|
250 |
else
|
|
251 |
subject_for_issue(i, options) unless options[:only] == :lines
|
|
252 |
line_for_issue(i, options) unless options[:only] == :subjects
|
|
253 |
end
|
|
254 |
|
224 |
255 |
options[:top] += options[:top_increment]
|
225 |
256 |
@number_of_rows += 1
|
226 |
257 |
break if abort?
|
227 |
258 |
end
|
228 |
|
|
|
259 |
|
229 |
260 |
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
230 |
261 |
end
|
231 |
262 |
|
232 |
263 |
def render_version(version, options={})
|
233 |
264 |
# Version header
|
234 |
|
subject_for_version(version, options) unless options[:only] == :lines
|
235 |
|
line_for_version(version, options) unless options[:only] == :subjects
|
236 |
|
|
|
265 |
if options[:format] == :html
|
|
266 |
subject_for_version(version, options) unless options[:only] == :lines && options[:only] == :calendars
|
|
267 |
line_for_version(version, options) unless options[:only] == :subjects && options[:only] == :calendars
|
|
268 |
calendar_for_version(version, options) unless options[:only] == :lines && options[:only] == :subjects
|
|
269 |
else
|
|
270 |
subject_for_version(version, options) unless options[:only] == :lines
|
|
271 |
line_for_version(version, options) unless options[:only] == :subjects
|
|
272 |
end
|
|
273 |
|
237 |
274 |
options[:top] += options[:top_increment]
|
238 |
275 |
@number_of_rows += 1
|
239 |
276 |
return if abort?
|
240 |
|
|
|
277 |
|
241 |
278 |
# Remove the project requirement for Versions because it will
|
242 |
279 |
# restrict issues to only be on the current project. This
|
243 |
280 |
# ends up missing issues which are assigned to shared versions.
|
244 |
281 |
@query.project = nil if @query.project
|
245 |
|
|
|
282 |
|
246 |
283 |
issues = version.fixed_issues.for_gantt.with_query(@query).all(:limit => current_limit)
|
247 |
284 |
if issues
|
248 |
285 |
sort_issues!(issues)
|
... | ... | |
252 |
289 |
options[:indent] -= options[:indent_increment]
|
253 |
290 |
end
|
254 |
291 |
end
|
255 |
|
|
|
292 |
|
256 |
293 |
def render_end(options={})
|
257 |
294 |
case options[:format]
|
258 |
|
when :pdf
|
|
295 |
when :pdf
|
259 |
296 |
options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
|
260 |
297 |
end
|
261 |
298 |
end
|
... | ... | |
280 |
317 |
if project.is_a?(Project) && project.start_date && project.due_date
|
281 |
318 |
options[:zoom] ||= 1
|
282 |
319 |
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
283 |
|
|
|
320 |
|
284 |
321 |
coords = coordinates(project.start_date, project.due_date, nil, options[:zoom])
|
285 |
322 |
label = h(project)
|
286 |
|
|
|
323 |
|
287 |
324 |
case options[:format]
|
288 |
325 |
when :html
|
289 |
|
html_task(options, coords, :css => "project task", :label => label, :markers => true)
|
|
326 |
html_task(options, coords, :css => "project task", :label => label, :markers => true, :id => project.id, :kind => "p")
|
290 |
327 |
when :image
|
291 |
328 |
image_task(options, coords, :label => label, :markers => true, :height => 3)
|
292 |
329 |
when :pdf
|
... | ... | |
318 |
355 |
if version.is_a?(Version) && version.start_date && version.due_date
|
319 |
356 |
options[:zoom] ||= 1
|
320 |
357 |
options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom]
|
321 |
|
|
|
358 |
|
322 |
359 |
coords = coordinates(version.start_date, version.due_date, version.completed_pourcent, options[:zoom])
|
323 |
360 |
label = "#{h version } #{h version.completed_pourcent.to_i.to_s}%"
|
324 |
361 |
label = h("#{version.project} -") + label unless @project && @project == version.project
|
325 |
362 |
|
326 |
363 |
case options[:format]
|
327 |
364 |
when :html
|
328 |
|
html_task(options, coords, :css => "version task", :label => label, :markers => true)
|
|
365 |
html_task(options, coords, :css => "version task", :label => label, :markers => true, :id => version.id, :kind => "v")
|
329 |
366 |
when :image
|
330 |
367 |
image_task(options, coords, :label => label, :markers => true, :height => 3)
|
331 |
368 |
when :pdf
|
... | ... | |
342 |
379 |
@issue_ancestors.pop
|
343 |
380 |
options[:indent] -= options[:indent_increment]
|
344 |
381 |
end
|
345 |
|
|
|
382 |
|
346 |
383 |
output = case options[:format]
|
347 |
384 |
when :html
|
348 |
385 |
css_classes = ''
|
349 |
386 |
css_classes << ' issue-overdue' if issue.overdue?
|
350 |
387 |
css_classes << ' issue-behind-schedule' if issue.behind_schedule?
|
351 |
388 |
css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to
|
352 |
|
|
|
389 |
|
353 |
390 |
subject = "<span class='#{css_classes}'>"
|
354 |
391 |
if issue.assigned_to.present?
|
355 |
392 |
assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name
|
... | ... | |
369 |
406 |
@issue_ancestors << issue
|
370 |
407 |
options[:indent] += options[:indent_increment]
|
371 |
408 |
end
|
372 |
|
|
|
409 |
|
373 |
410 |
output
|
374 |
411 |
end
|
375 |
412 |
|
... | ... | |
378 |
415 |
if issue.is_a?(Issue) && issue.due_before
|
379 |
416 |
coords = coordinates(issue.start_date, issue.due_before, issue.done_ratio, options[:zoom])
|
380 |
417 |
label = "#{ issue.status.name } #{ issue.done_ratio }%"
|
381 |
|
|
|
418 |
if !issue.due_date && issue.fixed_version
|
|
419 |
if options[:format] == :html
|
|
420 |
label += "- <strong>#{h(issue.fixed_version.name)}</strong>"
|
|
421 |
else
|
|
422 |
label += "-#{h(issue.fixed_version.name)}"
|
|
423 |
end
|
|
424 |
end
|
|
425 |
|
382 |
426 |
case options[:format]
|
383 |
427 |
when :html
|
384 |
|
html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?)
|
|
428 |
html_task(options, coords, :css => "task " + (issue.leaf? ? 'leaf' : 'parent'), :label => label, :issue => issue, :markers => !issue.leaf?, :id => issue.id, :kind => "i")
|
385 |
429 |
when :image
|
386 |
430 |
image_task(options, coords, :label => label)
|
387 |
431 |
when :pdf
|
... | ... | |
393 |
437 |
end
|
394 |
438 |
end
|
395 |
439 |
|
396 |
|
# Generates a gantt image
|
|
440 |
# Generates a gantt image
|
397 |
441 |
# Only defined if RMagick is avalaible
|
398 |
442 |
def to_image(format='PNG')
|
399 |
|
date_to = (@date_from >> @months)-1
|
|
443 |
date_to = (@date_from >> @months)-1
|
400 |
444 |
show_weeks = @zoom > 1
|
401 |
445 |
show_days = @zoom > 2
|
402 |
|
|
|
446 |
|
403 |
447 |
subject_width = 400
|
404 |
448 |
header_heigth = 18
|
405 |
449 |
# width of one day in pixels
|
... | ... | |
408 |
452 |
g_height = 20 * number_of_rows + 30
|
409 |
453 |
headers_heigth = (show_weeks ? 2*header_heigth : header_heigth)
|
410 |
454 |
height = g_height + headers_heigth
|
411 |
|
|
|
455 |
|
412 |
456 |
imgl = Magick::ImageList.new
|
413 |
457 |
imgl.new_image(subject_width+g_width+1, height)
|
414 |
458 |
gc = Magick::Draw.new
|
415 |
|
|
|
459 |
|
416 |
460 |
# Subjects
|
417 |
461 |
gc.stroke('transparent')
|
418 |
462 |
subjects(:image => gc, :top => (headers_heigth + 20), :indent => 4, :format => :image)
|
419 |
|
|
|
463 |
|
420 |
464 |
# Months headers
|
421 |
465 |
month_f = @date_from
|
422 |
466 |
left = subject_width
|
423 |
|
@months.times do
|
|
467 |
@months.times do
|
424 |
468 |
width = ((month_f >> 1) - month_f) * zoom
|
425 |
469 |
gc.fill('white')
|
426 |
470 |
gc.stroke('grey')
|
... | ... | |
433 |
477 |
left = left + width
|
434 |
478 |
month_f = month_f >> 1
|
435 |
479 |
end
|
436 |
|
|
|
480 |
|
437 |
481 |
# Weeks headers
|
438 |
482 |
if show_weeks
|
439 |
483 |
left = subject_width
|
... | ... | |
465 |
509 |
week_f = week_f+7
|
466 |
510 |
end
|
467 |
511 |
end
|
468 |
|
|
|
512 |
|
469 |
513 |
# Days details (week-end in grey)
|
470 |
514 |
if show_days
|
471 |
515 |
left = subject_width
|
472 |
516 |
height = g_height + header_heigth - 1
|
473 |
517 |
wday = @date_from.cwday
|
474 |
|
(date_to - @date_from + 1).to_i.times do
|
|
518 |
(date_to - @date_from + 1).to_i.times do
|
475 |
519 |
width = zoom
|
476 |
520 |
gc.fill(wday == 6 || wday == 7 ? '#eee' : 'white')
|
477 |
521 |
gc.stroke('#ddd')
|
... | ... | |
482 |
526 |
wday = 1 if wday > 7
|
483 |
527 |
end
|
484 |
528 |
end
|
485 |
|
|
|
529 |
|
486 |
530 |
# border
|
487 |
531 |
gc.fill('transparent')
|
488 |
532 |
gc.stroke('grey')
|
... | ... | |
490 |
534 |
gc.rectangle(0, 0, subject_width+g_width, headers_heigth)
|
491 |
535 |
gc.stroke('black')
|
492 |
536 |
gc.rectangle(0, 0, subject_width+g_width, g_height+ headers_heigth-1)
|
493 |
|
|
|
537 |
|
494 |
538 |
# content
|
495 |
539 |
top = headers_heigth + 20
|
496 |
540 |
|
497 |
541 |
gc.stroke('transparent')
|
498 |
542 |
lines(:image => gc, :top => top, :zoom => zoom, :subject_width => subject_width, :format => :image)
|
499 |
|
|
|
543 |
|
500 |
544 |
# today red line
|
501 |
545 |
if Date.today >= @date_from and Date.today <= date_to
|
502 |
546 |
gc.stroke('red')
|
503 |
547 |
x = (Date.today-@date_from+1)*zoom + subject_width
|
504 |
|
gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
|
505 |
|
end
|
506 |
|
|
|
548 |
gc.line(x, headers_heigth, x, headers_heigth + g_height-1)
|
|
549 |
end
|
|
550 |
|
507 |
551 |
gc.draw(imgl)
|
508 |
552 |
imgl.format = format
|
509 |
553 |
imgl.to_blob
|
... | ... | |
520 |
564 |
pdf.Cell(PDF::LeftPaneWidth, 20, project.to_s)
|
521 |
565 |
pdf.Ln
|
522 |
566 |
pdf.SetFontStyle('B',9)
|
523 |
|
|
|
567 |
|
524 |
568 |
subject_width = PDF::LeftPaneWidth
|
525 |
569 |
header_heigth = 5
|
526 |
|
|
|
570 |
|
527 |
571 |
headers_heigth = header_heigth
|
528 |
572 |
show_weeks = false
|
529 |
573 |
show_days = false
|
530 |
|
|
|
574 |
|
531 |
575 |
if self.months < 7
|
532 |
576 |
show_weeks = true
|
533 |
577 |
headers_heigth = 2*header_heigth
|
... | ... | |
536 |
580 |
headers_heigth = 3*header_heigth
|
537 |
581 |
end
|
538 |
582 |
end
|
539 |
|
|
|
583 |
|
540 |
584 |
g_width = PDF.right_pane_width
|
541 |
585 |
zoom = (g_width) / (self.date_to - self.date_from + 1)
|
542 |
586 |
g_height = 120
|
543 |
587 |
t_height = g_height + headers_heigth
|
544 |
|
|
|
588 |
|
545 |
589 |
y_start = pdf.GetY
|
546 |
|
|
|
590 |
|
547 |
591 |
# Months headers
|
548 |
592 |
month_f = self.date_from
|
549 |
593 |
left = subject_width
|
550 |
594 |
height = header_heigth
|
551 |
|
self.months.times do
|
552 |
|
width = ((month_f >> 1) - month_f) * zoom
|
|
595 |
self.months.times do
|
|
596 |
width = ((month_f >> 1) - month_f) * zoom
|
553 |
597 |
pdf.SetY(y_start)
|
554 |
598 |
pdf.SetX(left)
|
555 |
599 |
pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
|
556 |
600 |
left = left + width
|
557 |
601 |
month_f = month_f >> 1
|
558 |
|
end
|
559 |
|
|
|
602 |
end
|
|
603 |
|
560 |
604 |
# Weeks headers
|
561 |
605 |
if show_weeks
|
562 |
606 |
left = subject_width
|
... | ... | |
582 |
626 |
week_f = week_f+7
|
583 |
627 |
end
|
584 |
628 |
end
|
585 |
|
|
|
629 |
|
586 |
630 |
# Days headers
|
587 |
631 |
if show_days
|
588 |
632 |
left = subject_width
|
589 |
633 |
height = header_heigth
|
590 |
634 |
wday = self.date_from.cwday
|
591 |
635 |
pdf.SetFontStyle('B',7)
|
592 |
|
(self.date_to - self.date_from + 1).to_i.times do
|
|
636 |
(self.date_to - self.date_from + 1).to_i.times do
|
593 |
637 |
width = zoom
|
594 |
638 |
pdf.SetY(y_start + 2 * header_heigth)
|
595 |
639 |
pdf.SetX(left)
|
... | ... | |
599 |
643 |
wday = 1 if wday > 7
|
600 |
644 |
end
|
601 |
645 |
end
|
602 |
|
|
|
646 |
|
603 |
647 |
pdf.SetY(y_start)
|
604 |
648 |
pdf.SetX(15)
|
605 |
649 |
pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
|
606 |
|
|
|
650 |
|
607 |
651 |
# Tasks
|
608 |
652 |
top = headers_heigth + y_start
|
609 |
653 |
options = {
|
... | ... | |
620 |
664 |
render(options)
|
621 |
665 |
pdf.Output
|
622 |
666 |
end
|
623 |
|
|
|
667 |
|
|
668 |
def edit(pms)
|
|
669 |
id = pms[:id]
|
|
670 |
kind = id.slice!(0).chr
|
|
671 |
begin
|
|
672 |
case kind
|
|
673 |
when 'i'
|
|
674 |
@issue = Issue.find(pms[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
|
|
675 |
when 'p'
|
|
676 |
@issue = Project.find(pms[:id])
|
|
677 |
when 'v'
|
|
678 |
@issue = Version.find(pms[:id], :include => [:project])
|
|
679 |
end
|
|
680 |
rescue ActiveRecord::RecordNotFound
|
|
681 |
return "issue not found : #{pms[:id]}", 400
|
|
682 |
end
|
|
683 |
|
|
684 |
if !@issue.start_date || !@issue.due_before
|
|
685 |
#render :text=>l(:notice_locking_conflict), :status=>400
|
|
686 |
return l(:notice_locking_conflict), 400
|
|
687 |
end
|
|
688 |
@issue.init_journal(User.current)
|
|
689 |
date_from = Date.parse(pms[:date_from])
|
|
690 |
old_start_date = @issue.start_date
|
|
691 |
o = get_issue_position(@issue, pms[:zoom])
|
|
692 |
text_for_revert = "#{kind}#{id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
693 |
|
|
694 |
if pms[:day]
|
|
695 |
#bar moved
|
|
696 |
day = pms[:day].to_i
|
|
697 |
duration = @issue.due_before - @issue.start_date
|
|
698 |
@issue.start_date = date_from + day
|
|
699 |
@issue.due_date = @issue.start_date + duration.to_i if @issue.due_date
|
|
700 |
elsif pms[:start_date]
|
|
701 |
#start date changed
|
|
702 |
start_date = Date.parse(pms[:start_date])
|
|
703 |
if @issue.start_date == start_date
|
|
704 |
#render :text=>""
|
|
705 |
return "", 200 #nothing has changed
|
|
706 |
end
|
|
707 |
@issue.start_date = start_date
|
|
708 |
@issue.due_date = start_date if @issue.due_date && start_date > @issue.due_date
|
|
709 |
elsif pms[:due_date]
|
|
710 |
#due date changed
|
|
711 |
due_date = Date.parse(pms[:due_date])
|
|
712 |
if @issue.due_date == due_date
|
|
713 |
#render :text=>""
|
|
714 |
return "", 200 #nothing has changed
|
|
715 |
end
|
|
716 |
@issue.due_date = due_date
|
|
717 |
@issue.start_date = due_date if due_date < @issue.start_date
|
|
718 |
end
|
|
719 |
fv = @issue.fixed_version
|
|
720 |
if fv && fv.effective_date && !@issue.due_date && fv.effective_date < @issue.start_date
|
|
721 |
@issue.start_date = old_start_date
|
|
722 |
end
|
|
723 |
|
|
724 |
begin
|
|
725 |
@issue.save!
|
|
726 |
o = get_issue_position(@issue, pms[:zoom])
|
|
727 |
text = "#{kind}#{id}=#{format_date(@issue.start_date)},#{@issue.start_date},#{format_date(@issue.due_before)},#{@issue.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
728 |
|
|
729 |
prj_map = {}
|
|
730 |
text = set_project_data(@issue.project, pms[:zoom], text, prj_map)
|
|
731 |
version_map = {}
|
|
732 |
text = set_version_data(@issue.fixed_version, pms[:zoom], text, version_map)
|
|
733 |
|
|
734 |
#check dependencies
|
|
735 |
issues = @issue.all_precedes_issues
|
|
736 |
issues.each do |i|
|
|
737 |
o = get_issue_position(i, pms[:zoom])
|
|
738 |
text += "|i#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]}"
|
|
739 |
text = set_project_data(i.project, pms[:zoom], text, prj_map)
|
|
740 |
text = set_version_data(i.fixed_version, pms[:zoom], text, version_map)
|
|
741 |
end
|
|
742 |
|
|
743 |
#check parent
|
|
744 |
is = @issue
|
|
745 |
while
|
|
746 |
pid = is.parent_issue_id
|
|
747 |
break if !pid
|
|
748 |
i = Issue.find(pid)
|
|
749 |
o = get_issue_position(i, pms[:zoom])
|
|
750 |
text += "|i#{i.id}=#{format_date(i.start_date)},#{i.start_date},#{format_date(i.due_before)},#{i.due_before},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
|
|
751 |
text = set_project_data(i.project, pms[:zoom], text, prj_map)
|
|
752 |
text = set_version_data(i.fixed_version, pms[:zoom], text, version_map)
|
|
753 |
is = i
|
|
754 |
end
|
|
755 |
#render :text=>text
|
|
756 |
return text, 200
|
|
757 |
rescue => e
|
|
758 |
#render :text=>@issue.errors.full_messages.join("\n") + "|" + text_for_revert , :status=>400
|
|
759 |
if @issue.errors.full_messages.to_s == ""
|
|
760 |
return e.to_s + "\n" + [$!,$@.join("\n")].join("\n") + "\n" + @issue.errors.full_messages.join("\n") + "|" + text_for_revert, 400
|
|
761 |
else
|
|
762 |
return @issue.errors.full_messages.join("\n") + "|" + text_for_revert, 400
|
|
763 |
end
|
|
764 |
end
|
|
765 |
end
|
|
766 |
|
624 |
767 |
private
|
625 |
|
|
|
768 |
|
626 |
769 |
def coordinates(start_date, end_date, progress, zoom=nil)
|
627 |
770 |
zoom ||= @zoom
|
628 |
|
|
|
771 |
|
629 |
772 |
coords = {}
|
630 |
773 |
if start_date && end_date && start_date < self.date_to && end_date > self.date_from
|
631 |
774 |
if start_date > self.date_from
|
... | ... | |
640 |
783 |
else
|
641 |
784 |
coords[:bar_end] = self.date_to - self.date_from + 1
|
642 |
785 |
end
|
643 |
|
|
|
786 |
|
644 |
787 |
if progress
|
645 |
788 |
progress_date = start_date + (end_date - start_date) * (progress / 100.0)
|
646 |
789 |
if progress_date > self.date_from && progress_date > start_date
|
... | ... | |
650 |
793 |
coords[:bar_progress_end] = self.date_to - self.date_from + 1
|
651 |
794 |
end
|
652 |
795 |
end
|
653 |
|
|
|
796 |
|
654 |
797 |
if progress_date < Date.today
|
655 |
798 |
late_date = [Date.today, end_date].min
|
656 |
799 |
if late_date > self.date_from && late_date > start_date
|
... | ... | |
663 |
806 |
end
|
664 |
807 |
end
|
665 |
808 |
end
|
666 |
|
|
|
809 |
|
667 |
810 |
# Transforms dates into pixels witdh
|
668 |
811 |
coords.keys.each do |key|
|
669 |
812 |
coords[key] = (coords[key] * zoom).floor
|
... | ... | |
675 |
818 |
def sort_issues!(issues)
|
676 |
819 |
issues.sort! { |a, b| gantt_issue_compare(a, b, issues) }
|
677 |
820 |
end
|
678 |
|
|
|
821 |
|
679 |
822 |
# TODO: top level issues should be sorted by start date
|
680 |
823 |
def gantt_issue_compare(x, y, issues)
|
681 |
824 |
if x.root_id == y.root_id
|
... | ... | |
684 |
827 |
x.root_id <=> y.root_id
|
685 |
828 |
end
|
686 |
829 |
end
|
687 |
|
|
|
830 |
|
688 |
831 |
def current_limit
|
689 |
832 |
if @max_rows
|
690 |
833 |
@max_rows - @number_of_rows
|
... | ... | |
692 |
835 |
nil
|
693 |
836 |
end
|
694 |
837 |
end
|
695 |
|
|
|
838 |
|
696 |
839 |
def abort?
|
697 |
840 |
if @max_rows && @number_of_rows >= @max_rows
|
698 |
841 |
@truncated = true
|
699 |
842 |
end
|
700 |
843 |
end
|
701 |
|
|
|
844 |
|
702 |
845 |
def pdf_new_page?(options)
|
703 |
846 |
if options[:top] > 180
|
704 |
847 |
options[:pdf].Line(15, options[:top], PDF::TotalWidth, options[:top])
|
... | ... | |
707 |
850 |
options[:pdf].Line(15, options[:top] - 0.1, PDF::TotalWidth, options[:top] - 0.1)
|
708 |
851 |
end
|
709 |
852 |
end
|
710 |
|
|
|
853 |
|
711 |
854 |
def html_subject(params, subject, options={})
|
712 |
855 |
output = "<div class=' #{options[:css] }' style='position: absolute;line-height:1.2em;height:16px;top:#{params[:top]}px;left:#{params[:indent]}px;overflow:hidden;'>"
|
713 |
856 |
output << subject
|
... | ... | |
715 |
858 |
@subjects << output
|
716 |
859 |
output
|
717 |
860 |
end
|
718 |
|
|
|
861 |
|
719 |
862 |
def pdf_subject(params, subject, options={})
|
720 |
863 |
params[:pdf].SetY(params[:top])
|
721 |
864 |
params[:pdf].SetX(15)
|
722 |
|
|
|
865 |
|
723 |
866 |
char_limit = PDF::MaxCharactorsForSubject - params[:indent]
|
724 |
867 |
params[:pdf].Cell(params[:subject_width]-15, 5, (" " * params[:indent]) + subject.to_s.sub(/^(.{#{char_limit}}[^\s]*\s).*$/, '\1 (...)'), "LR")
|
725 |
|
|
|
868 |
|
726 |
869 |
params[:pdf].SetY(params[:top])
|
727 |
870 |
params[:pdf].SetX(params[:subject_width])
|
728 |
871 |
params[:pdf].Cell(params[:g_width], 5, "", "LR")
|
729 |
872 |
end
|
730 |
|
|
|
873 |
|
731 |
874 |
def image_subject(params, subject, options={})
|
732 |
875 |
params[:image].fill('black')
|
733 |
876 |
params[:image].stroke('transparent')
|
734 |
877 |
params[:image].stroke_width(1)
|
735 |
878 |
params[:image].text(params[:indent], params[:top] + 2, subject)
|
736 |
879 |
end
|
737 |
|
|
|
880 |
|
738 |
881 |
def html_task(params, coords, options={})
|
739 |
882 |
output = ''
|
740 |
883 |
# Renders the task bar, with progress and late
|
741 |
884 |
if coords[:bar_start] && coords[:bar_end]
|
742 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_todo'> </div>"
|
743 |
|
|
|
885 |
i_width = coords[:bar_end] - coords[:bar_start] - 2
|
|
886 |
output << "<div id='ev_#{options[:kind]}#{options[:id]}' style='position:absolute;left:#{coords[:bar_start]}px;top:#{params[:top]}px;padding-top:3px;height:18px;width:#{ i_width + 100}px;' #{options[:kind] == 'i' ? "class='handle'" : ""}>"
|
|
887 |
output << "<div id='task_todo_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ i_width}px;' class='#{options[:css]} task_todo'> </div>"
|
|
888 |
|
744 |
889 |
if coords[:bar_late_end]
|
745 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_late_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_late'> </div>"
|
|
890 |
l_width = coords[:bar_late_end] - coords[:bar_start] - 2
|
|
891 |
output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ l_width}px;' class='#{ l_width == 0 ? options[:css] + " task_none" : options[:css] + " task_late"}'> </div>"
|
|
892 |
else
|
|
893 |
output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'> </div>"
|
746 |
894 |
end
|
747 |
895 |
if coords[:bar_progress_end]
|
748 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_progress_end] - coords[:bar_start] - 2}px;' class='#{options[:css]} task_done'> </div>"
|
|
896 |
d_width = coords[:bar_progress_end] - coords[:bar_start] - 2
|
|
897 |
output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:#{ d_width}px;' class='#{ d_width == 0 ? options[:css] + " task_none" : options[:css] + " task_done"}'> </div>"
|
|
898 |
else
|
|
899 |
output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'> </div>"
|
749 |
900 |
end
|
|
901 |
output << "</div>"
|
|
902 |
else
|
|
903 |
output << "<div id='ev_#{options[:kind]}#{options[:id]}' style='position:absolute;left:0px;top:#{params[:top]}px;padding-top:3px;height:18px;width:0px;' #{options[:kind] == 'i' ? "class='handle'" : ""}>"
|
|
904 |
output << "<div id='task_todo_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css]} task_todo'> </div>"
|
|
905 |
output << "<div id='task_late_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'> </div>"
|
|
906 |
output << "<div id='task_done_#{options[:kind]}#{options[:id]}' style='float:left:0px; width:0px;' class='#{ options[:css] + " task_none"}'> </div>"
|
|
907 |
output << "</div>"
|
750 |
908 |
end
|
751 |
909 |
# Renders the markers
|
752 |
910 |
if options[:markers]
|
753 |
911 |
if coords[:start]
|
754 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'> </div>"
|
|
912 |
output << "<div id='marker_start_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ coords[:start] }px;width:15px;' class='#{options[:css]} marker starting'> </div>"
|
755 |
913 |
end
|
756 |
914 |
if coords[:end]
|
757 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'> </div>"
|
|
915 |
output << "<div id='marker_end_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ coords[:end] + params[:zoom] }px;width:15px;' class='#{options[:css]} marker ending'> </div>"
|
758 |
916 |
end
|
|
917 |
else
|
|
918 |
output << view.draggable_element("ev_#{options[:kind]}#{options[:id]}", :revert =>false, :scroll=>"'gantt-container'", :constraint => "'horizontal'", :snap=>params[:zoom],:onEnd=>'function( draggable, event ) {issue_moved(draggable.element);}')
|
759 |
919 |
end
|
760 |
920 |
# Renders the label on the right
|
761 |
921 |
if options[:label]
|
762 |
|
output << "<div style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>"
|
|
922 |
output << "<div id='label_#{options[:kind]}#{options[:id]}' style='top:#{ params[:top] }px;left:#{ (coords[:bar_end] || 0) + 8 }px;' class='#{options[:css]} label'>"
|
763 |
923 |
output << options[:label]
|
764 |
924 |
output << "</div>"
|
765 |
925 |
end
|
766 |
926 |
# Renders the tooltip
|
767 |
927 |
if options[:issue] && coords[:bar_start] && coords[:bar_end]
|
768 |
|
output << "<div class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>"
|
|
928 |
output << "<div id='tt_#{options[:kind]}#{options[:id]}' class='tooltip' style='position: absolute;top:#{ params[:top] }px;left:#{ coords[:bar_start] }px;width:#{ coords[:bar_end] - coords[:bar_start] }px;height:12px;'>"
|
769 |
929 |
output << '<span class="tip">'
|
770 |
930 |
output << view.render_issue_tooltip(options[:issue])
|
771 |
931 |
output << "</span></div>"
|
... | ... | |
773 |
933 |
@lines << output
|
774 |
934 |
output
|
775 |
935 |
end
|
776 |
|
|
|
936 |
|
777 |
937 |
def pdf_task(params, coords, options={})
|
778 |
938 |
height = options[:height] || 2
|
779 |
|
|
|
939 |
|
780 |
940 |
# Renders the task bar, with progress and late
|
781 |
941 |
if coords[:bar_start] && coords[:bar_end]
|
782 |
942 |
params[:pdf].SetY(params[:top]+1.5)
|
783 |
943 |
params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
|
784 |
944 |
params[:pdf].SetFillColor(200,200,200)
|
785 |
945 |
params[:pdf].Cell(coords[:bar_end] - coords[:bar_start], height, "", 0, 0, "", 1)
|
786 |
|
|
|
946 |
|
787 |
947 |
if coords[:bar_late_end]
|
788 |
948 |
params[:pdf].SetY(params[:top]+1.5)
|
789 |
949 |
params[:pdf].SetX(params[:subject_width] + coords[:bar_start])
|
... | ... | |
803 |
963 |
params[:pdf].SetY(params[:top] + 1)
|
804 |
964 |
params[:pdf].SetX(params[:subject_width] + coords[:start] - 1)
|
805 |
965 |
params[:pdf].SetFillColor(50,50,200)
|
806 |
|
params[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
|
966 |
params[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
807 |
967 |
end
|
808 |
968 |
if coords[:end]
|
809 |
969 |
params[:pdf].SetY(params[:top] + 1)
|
810 |
970 |
params[:pdf].SetX(params[:subject_width] + coords[:end] - 1)
|
811 |
971 |
params[:pdf].SetFillColor(50,50,200)
|
812 |
|
params[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
|
972 |
params[:pdf].Cell(2, 2, "", 0, 0, "", 1)
|
813 |
973 |
end
|
814 |
974 |
end
|
815 |
975 |
# Renders the label on the right
|
... | ... | |
821 |
981 |
|
822 |
982 |
def image_task(params, coords, options={})
|
823 |
983 |
height = options[:height] || 6
|
824 |
|
|
|
984 |
|
825 |
985 |
# Renders the task bar, with progress and late
|
826 |
986 |
if coords[:bar_start] && coords[:bar_end]
|
827 |
987 |
params[:image].fill('#aaa')
|
828 |
988 |
params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_end], params[:top] - height)
|
829 |
|
|
|
989 |
|
830 |
990 |
if coords[:bar_late_end]
|
831 |
991 |
params[:image].fill('#f66')
|
832 |
992 |
params[:image].rectangle(params[:subject_width] + coords[:bar_start], params[:top], params[:subject_width] + coords[:bar_late_end], params[:top] - height)
|
... | ... | |
857 |
1017 |
params[:image].text(params[:subject_width] + (coords[:bar_end] || 0) + 5,params[:top] + 1, options[:label])
|
858 |
1018 |
end
|
859 |
1019 |
end
|
|
1020 |
|
|
1021 |
##
|
|
1022 |
## for edit gantt
|
|
1023 |
##
|
|
1024 |
def set_project_data(prj, zoom, text, prj_map = {})
|
|
1025 |
if !prj
|
|
1026 |
return text
|
|
1027 |
end
|
|
1028 |
if !prj_map[prj.id]
|
|
1029 |
o = get_project_position(prj, zoom)
|
|
1030 |
text += "|p#{prj.id}=#{format_date(prj.start_date)},#{prj.start_date},#{format_date(prj.due_date)},#{prj.due_date},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
|
|
1031 |
prj_map[prj.id] = prj
|
|
1032 |
end
|
|
1033 |
text = set_project_data(prj.parent, zoom, text, prj_map)
|
|
1034 |
end
|
|
1035 |
|
|
1036 |
def set_version_data(version, zoom, text, version_map = {})
|
|
1037 |
if !version
|
|
1038 |
return text
|
|
1039 |
end
|
|
1040 |
if !version_map[version.id]
|
|
1041 |
o = get_version_position(version, zoom)
|
|
1042 |
text += "|v#{version.id}=#{format_date(version.start_date)},#{version.start_date},#{format_date(version.due_date)},#{version.due_date},#{o[0]},#{o[1]},#{o[2]},#{o[3]},#{o[4]},#{o[5]}"
|
|
1043 |
version_map[version.id] = version
|
|
1044 |
end
|
|
1045 |
return text
|
|
1046 |
end
|
|
1047 |
|
|
1048 |
def get_pos(coords)
|
|
1049 |
i_left = 0
|
|
1050 |
i_width = 0
|
|
1051 |
l_width = 0
|
|
1052 |
d_width = 0
|
|
1053 |
if coords[:bar_start]
|
|
1054 |
i_left = coords[:bar_start]
|
|
1055 |
if coords[:bar_end]
|
|
1056 |
i_width = coords[:bar_end] - coords[:bar_start] - 2
|
|
1057 |
i_width = 0 if i_width < 0
|
|
1058 |
end
|
|
1059 |
if coords[:bar_late_end]
|
|
1060 |
l_width = coords[:bar_late_end] - coords[:bar_start] - 2
|
|
1061 |
end
|
|
1062 |
if coords[:bar_progress_end]
|
|
1063 |
d_width = coords[:bar_progress_end] - coords[:bar_start] - 2
|
|
1064 |
end
|
|
1065 |
end
|
|
1066 |
return i_left, i_width, l_width, d_width
|
|
1067 |
end
|
|
1068 |
|
|
1069 |
def get_issue_position(issue, zoom_str)
|
|
1070 |
z = zoom_str.to_i
|
|
1071 |
zoom = 1
|
|
1072 |
z.times { zoom = zoom * 2}
|
|
1073 |
id = issue.due_before
|
|
1074 |
if id && @date_to < id
|
|
1075 |
id = @date_to
|
|
1076 |
end
|
|
1077 |
coords = coordinates(issue.start_date, id, issue.done_ratio, zoom)
|
|
1078 |
|
|
1079 |
i_left, i_width, l_width, d_width = get_pos(coords)
|
|
1080 |
if coords[:end]
|
|
1081 |
return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
|
|
1082 |
else
|
|
1083 |
return i_left, i_width, l_width, d_width, coords[:start], nil
|
|
1084 |
end
|
|
1085 |
end
|
|
1086 |
|
|
1087 |
def get_project_position(project, zoom_str)
|
|
1088 |
z = zoom_str.to_i
|
|
1089 |
zoom = 1
|
|
1090 |
z.times { zoom = zoom * 2}
|
|
1091 |
pd = project.due_date
|
|
1092 |
if pd && @date_to < pd
|
|
1093 |
pd = @date_to
|
|
1094 |
end
|
|
1095 |
coords = coordinates(project.start_date, pd, nil, zoom)
|
|
1096 |
i_left, i_width, l_width, d_width = get_pos(coords)
|
|
1097 |
if coords[:end]
|
|
1098 |
return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
|
|
1099 |
else
|
|
1100 |
return i_left, i_width, l_width, d_width, coords[:start], nil
|
|
1101 |
end
|
|
1102 |
end
|
|
1103 |
|
|
1104 |
def get_version_position(version, zoom_str)
|
|
1105 |
z = zoom_str.to_i
|
|
1106 |
zoom = 1
|
|
1107 |
z.times { zoom = zoom * 2}
|
|
1108 |
vd = version.due_date
|
|
1109 |
if vd && @date_to < vd
|
|
1110 |
vd = @date_to
|
|
1111 |
end
|
|
1112 |
coords = coordinates(version.start_date, vd, version.completed_pourcent, zoom)
|
|
1113 |
i_left, i_width, l_width, d_width = get_pos(coords)
|
|
1114 |
if coords[:end]
|
|
1115 |
return i_left, i_width, l_width, d_width, coords[:start], coords[:end] + zoom
|
|
1116 |
else
|
|
1117 |
return i_left, i_width, l_width, d_width, coords[:start], nil
|
|
1118 |
end
|
|
1119 |
end
|
|
1120 |
|
|
1121 |
def calendar_for_issue(issue, options)
|
|
1122 |
# Skip issues that don't have a due_before (due_date or version's due_date)
|
|
1123 |
if issue.is_a?(Issue) && issue.due_before
|
|
1124 |
|
|
1125 |
case options[:format]
|
|
1126 |
when :html
|
|
1127 |
@calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
|
|
1128 |
start_date = issue.start_date
|
|
1129 |
if start_date
|
|
1130 |
@calendars << "<span id='i#{issue.id}_start_date_str'>"
|
|
1131 |
@calendars << format_date(start_date)
|
|
1132 |
@calendars << "</span>"
|
|
1133 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_hidden_start_date' value='#{start_date}' />"
|
|
1134 |
if issue.leaf?
|
|
1135 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_start_date' value='#{start_date}' />#{view.g_calendar_for('i' + issue.id.to_s + '_start_date')}"
|
|
1136 |
else
|
|
1137 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_start_date' value='#{start_date}' /> "
|
|
1138 |
end
|
|
1139 |
@calendars << observe_date_field("i#{issue.id}", 'start')
|
|
1140 |
end
|
|
1141 |
due_date = issue.due_date
|
|
1142 |
if due_date
|
|
1143 |
@calendars << "<span id='i#{issue.id}_due_date_str'>"
|
|
1144 |
@calendars << format_date(due_date)
|
|
1145 |
@calendars << "</span>"
|
|
1146 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_hidden_due_date' value='#{due_date}' />"
|
|
1147 |
if issue.leaf?
|
|
1148 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_due_date' value='#{due_date}' />#{view.g_calendar_for('i' + issue.id.to_s + '_due_date')}"
|
|
1149 |
else
|
|
1150 |
@calendars << "<input type='hidden' size='12' id='i#{issue.id}_due_date' value='#{due_date}' />"
|
|
1151 |
end
|
|
1152 |
@calendars << observe_date_field("i#{issue.id}", 'due')
|
|
1153 |
end
|
|
1154 |
@calendars << "</div>"
|
|
1155 |
when :image
|
|
1156 |
#nop
|
|
1157 |
when :pdf
|
|
1158 |
#nop
|
|
1159 |
end
|
|
1160 |
else
|
|
1161 |
ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
|
|
1162 |
''
|
|
1163 |
end
|
|
1164 |
end
|
|
1165 |
|
|
1166 |
def calendar_for_version(version, options)
|
|
1167 |
# Skip version that don't have a due_before (due_date or version's due_date)
|
|
1168 |
if version.is_a?(Version) && version.start_date && version.due_date
|
|
1169 |
|
|
1170 |
case options[:format]
|
|
1171 |
when :html
|
|
1172 |
@calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
|
|
1173 |
@calendars << "<span id='v#{version.id}_start_date_str'>"
|
|
1174 |
@calendars << format_date(version.effective_date)
|
|
1175 |
@calendars << "</span>"
|
|
1176 |
@calendars << "</div>"
|
|
1177 |
when :image
|
|
1178 |
#nop
|
|
1179 |
when :pdf
|
|
1180 |
#nop
|
|
1181 |
end
|
|
1182 |
else
|
|
1183 |
ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before"
|
|
1184 |
''
|
|
1185 |
end
|
|
1186 |
end
|
|
1187 |
|
|
1188 |
def calendar_for_project(project, options)
|
|
1189 |
case options[:format]
|
|
1190 |
when :html
|
|
1191 |
@calendars << "<div style='position: absolute;line-height:1.2em;height:16px;top:#{options[:top]}px;left:4px;overflow:hidden;'>"
|
|
1192 |
@calendars << "<span id='p#{project.id}_start_date_str'>"
|
|
1193 |
@calendars << format_date(project.start_date) if project.start_date
|
|
1194 |
@calendars << "</span>"
|
|
1195 |
@calendars << " "
|
|
1196 |
@calendars << "<span id='p#{project.id}_due_date_str'>"
|
|
1197 |
@calendars << format_date(project.due_date) if project.due_date
|
|
1198 |
@calendars << "</span>"
|
|
1199 |
@calendars << "</div>"
|
|
1200 |
when :image
|
|
1201 |
# nop
|
|
1202 |
when :pdf
|
|
1203 |
# nop
|
|
1204 |
end
|
|
1205 |
end
|
|
1206 |
|
|
1207 |
def observe_date_field(id, type)
|
|
1208 |
output = ''
|
|
1209 |
prj_id = ''
|
|
1210 |
prj_id = @project.to_param if @project
|
|
1211 |
output << "<script type='text/javascript'>\n"
|
|
1212 |
output << "//<![CDATA[\n"
|
|
1213 |
output << "new Form.Element.Observer('#{id}_#{type}_date', 0.25,\n"
|
|
1214 |
output << " function(element, value) {\n"
|
|
1215 |
output << " if (value == document.getElementById('#{id}_hidden_#{type}_date').value) {\n"
|
|
1216 |
output << " return ;\n"
|
|
1217 |
output << " }\n"
|
|
1218 |
output << " new Ajax.Request('#{view.url_for(:controller=>:gantts, :action => :edit_gantt, :id=>id, :date_from=>self.date_from.strftime("%Y-%m-%d"), :date_to=>self.date_to.strftime("%Y-%m-%d"), :zoom=>self.zoom, :escape => false, :project_id=>prj_id)}', {asynchronous:true, evalScripts:true, onFailure:function(request){handle_failure(request.responseText)}, onSuccess:function(request){change_dates(request.responseText)}, parameters:'#{type}_date=' + encodeURIComponent(value)});"
|
|
1219 |
output << " })\n"
|
|
1220 |
output << "//]]>\n"
|
|
1221 |
output << "</script>"
|
|
1222 |
end
|
860 |
1223 |
end
|
861 |
1224 |
end
|
862 |
1225 |
end
|