##// END OF EJS Templates
Display project names in cross-project gantt PDF (#5904)....
Jean-Philippe Lang -
r3769:cb5e63d846b6
parent child
Show More
@@ -1,494 +1,496
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2009 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require 'iconv'
21 21 require 'rfpdf/fpdf'
22 22 require 'rfpdf/chinese'
23 23
24 24 module Redmine
25 25 module Export
26 26 module PDF
27 27 include ActionView::Helpers::TextHelper
28 28 include ActionView::Helpers::NumberHelper
29 29
30 30 class IFPDF < FPDF
31 31 include Redmine::I18n
32 32 attr_accessor :footer_date
33 33
34 34 def initialize(lang)
35 35 super()
36 36 set_language_if_valid lang
37 37 case current_language.to_s.downcase
38 38 when 'ko'
39 39 extend(PDF_Korean)
40 40 AddUHCFont()
41 41 @font_for_content = 'UHC'
42 42 @font_for_footer = 'UHC'
43 43 when 'ja'
44 44 extend(PDF_Japanese)
45 45 AddSJISFont()
46 46 @font_for_content = 'SJIS'
47 47 @font_for_footer = 'SJIS'
48 48 when 'zh'
49 49 extend(PDF_Chinese)
50 50 AddGBFont()
51 51 @font_for_content = 'GB'
52 52 @font_for_footer = 'GB'
53 53 when 'zh-tw'
54 54 extend(PDF_Chinese)
55 55 AddBig5Font()
56 56 @font_for_content = 'Big5'
57 57 @font_for_footer = 'Big5'
58 58 else
59 59 @font_for_content = 'Arial'
60 60 @font_for_footer = 'Helvetica'
61 61 end
62 62 SetCreator(Redmine::Info.app_name)
63 63 SetFont(@font_for_content)
64 64 end
65 65
66 66 def SetFontStyle(style, size)
67 67 SetFont(@font_for_content, style, size)
68 68 end
69 69
70 70 def SetTitle(txt)
71 71 txt = begin
72 72 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
73 73 hextxt = "<FEFF" # FEFF is BOM
74 74 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
75 75 hextxt << ">"
76 76 rescue
77 77 txt
78 78 end || ''
79 79 super(txt)
80 80 end
81 81
82 82 def textstring(s)
83 83 # Format a text string
84 84 if s =~ /^</ # This means the string is hex-dumped.
85 85 return s
86 86 else
87 87 return '('+escape(s)+')'
88 88 end
89 89 end
90 90
91 91 def Cell(w,h=0,txt='',border=0,ln=0,align='',fill=0,link='')
92 92 @ic ||= Iconv.new(l(:general_pdf_encoding), 'UTF-8')
93 93 # these quotation marks are not correctly rendered in the pdf
94 94 txt = txt.gsub(/[Ò€œÒ€�]/, '"') if txt
95 95 txt = begin
96 96 # 0x5c char handling
97 97 txtar = txt.split('\\')
98 98 txtar << '' if txt[-1] == ?\\
99 99 txtar.collect {|x| @ic.iconv(x)}.join('\\').gsub(/\\/, "\\\\\\\\")
100 100 rescue
101 101 txt
102 102 end || ''
103 103 super w,h,txt,border,ln,align,fill,link
104 104 end
105 105
106 106 def Footer
107 107 SetFont(@font_for_footer, 'I', 8)
108 108 SetY(-15)
109 109 SetX(15)
110 110 Cell(0, 5, @footer_date, 0, 0, 'L')
111 111 SetY(-15)
112 112 SetX(-30)
113 113 Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
114 114 end
115 115 end
116 116
117 117 # Returns a PDF string of a list of issues
118 118 def issues_to_pdf(issues, project, query)
119 119 pdf = IFPDF.new(current_language)
120 120 title = query.new_record? ? l(:label_issue_plural) : query.name
121 121 title = "#{project} - #{title}" if project
122 122 pdf.SetTitle(title)
123 123 pdf.AliasNbPages
124 124 pdf.footer_date = format_date(Date.today)
125 125 pdf.AddPage("L")
126 126
127 127 row_height = 6
128 128 col_width = []
129 129 unless query.columns.empty?
130 130 col_width = query.columns.collect {|column| column.name == :subject ? 4.0 : 1.0 }
131 131 ratio = 262.0 / col_width.inject(0) {|s,w| s += w}
132 132 col_width = col_width.collect {|w| w * ratio}
133 133 end
134 134
135 135 # title
136 136 pdf.SetFontStyle('B',11)
137 137 pdf.Cell(190,10, title)
138 138 pdf.Ln
139 139
140 140 # headers
141 141 pdf.SetFontStyle('B',8)
142 142 pdf.SetFillColor(230, 230, 230)
143 143 pdf.Cell(15, row_height, "#", 1, 0, 'L', 1)
144 144 query.columns.each_with_index do |column, i|
145 145 pdf.Cell(col_width[i], row_height, column.caption, 1, 0, 'L', 1)
146 146 end
147 147 pdf.Ln
148 148
149 149 # rows
150 150 pdf.SetFontStyle('',8)
151 151 pdf.SetFillColor(255, 255, 255)
152 152 previous_group = false
153 153 issues.each do |issue|
154 154 if query.grouped? && (group = query.group_by_column.value(issue)) != previous_group
155 155 pdf.SetFontStyle('B',9)
156 156 pdf.Cell(277, row_height,
157 157 (group.blank? ? 'None' : group.to_s) + " (#{@issue_count_by_group[group]})",
158 158 1, 1, 'L')
159 159 pdf.SetFontStyle('',8)
160 160 previous_group = group
161 161 end
162 162 pdf.Cell(15, row_height, issue.id.to_s, 1, 0, 'L', 1)
163 163 query.columns.each_with_index do |column, i|
164 164 s = if column.is_a?(QueryCustomFieldColumn)
165 165 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
166 166 show_value(cv)
167 167 else
168 168 value = issue.send(column.name)
169 169 if value.is_a?(Date)
170 170 format_date(value)
171 171 elsif value.is_a?(Time)
172 172 format_time(value)
173 173 else
174 174 value
175 175 end
176 176 end
177 177 pdf.Cell(col_width[i], row_height, s.to_s, 1, 0, 'L', 1)
178 178 end
179 179 pdf.Ln
180 180 end
181 181 if issues.size == Setting.issues_export_limit.to_i
182 182 pdf.SetFontStyle('B',10)
183 183 pdf.Cell(0, row_height, '...')
184 184 end
185 185 pdf.Output
186 186 end
187 187
188 188 # Returns a PDF string of a single issue
189 189 def issue_to_pdf(issue)
190 190 pdf = IFPDF.new(current_language)
191 191 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
192 192 pdf.AliasNbPages
193 193 pdf.footer_date = format_date(Date.today)
194 194 pdf.AddPage
195 195
196 196 pdf.SetFontStyle('B',11)
197 197 pdf.Cell(190,10, "#{issue.project} - #{issue.tracker} # #{issue.id}: #{issue.subject}")
198 198 pdf.Ln
199 199
200 200 y0 = pdf.GetY
201 201
202 202 pdf.SetFontStyle('B',9)
203 203 pdf.Cell(35,5, l(:field_status) + ":","LT")
204 204 pdf.SetFontStyle('',9)
205 205 pdf.Cell(60,5, issue.status.to_s,"RT")
206 206 pdf.SetFontStyle('B',9)
207 207 pdf.Cell(35,5, l(:field_priority) + ":","LT")
208 208 pdf.SetFontStyle('',9)
209 209 pdf.Cell(60,5, issue.priority.to_s,"RT")
210 210 pdf.Ln
211 211
212 212 pdf.SetFontStyle('B',9)
213 213 pdf.Cell(35,5, l(:field_author) + ":","L")
214 214 pdf.SetFontStyle('',9)
215 215 pdf.Cell(60,5, issue.author.to_s,"R")
216 216 pdf.SetFontStyle('B',9)
217 217 pdf.Cell(35,5, l(:field_category) + ":","L")
218 218 pdf.SetFontStyle('',9)
219 219 pdf.Cell(60,5, issue.category.to_s,"R")
220 220 pdf.Ln
221 221
222 222 pdf.SetFontStyle('B',9)
223 223 pdf.Cell(35,5, l(:field_created_on) + ":","L")
224 224 pdf.SetFontStyle('',9)
225 225 pdf.Cell(60,5, format_date(issue.created_on),"R")
226 226 pdf.SetFontStyle('B',9)
227 227 pdf.Cell(35,5, l(:field_assigned_to) + ":","L")
228 228 pdf.SetFontStyle('',9)
229 229 pdf.Cell(60,5, issue.assigned_to.to_s,"R")
230 230 pdf.Ln
231 231
232 232 pdf.SetFontStyle('B',9)
233 233 pdf.Cell(35,5, l(:field_updated_on) + ":","LB")
234 234 pdf.SetFontStyle('',9)
235 235 pdf.Cell(60,5, format_date(issue.updated_on),"RB")
236 236 pdf.SetFontStyle('B',9)
237 237 pdf.Cell(35,5, l(:field_due_date) + ":","LB")
238 238 pdf.SetFontStyle('',9)
239 239 pdf.Cell(60,5, format_date(issue.due_date),"RB")
240 240 pdf.Ln
241 241
242 242 for custom_value in issue.custom_field_values
243 243 pdf.SetFontStyle('B',9)
244 244 pdf.Cell(35,5, custom_value.custom_field.name + ":","L")
245 245 pdf.SetFontStyle('',9)
246 246 pdf.MultiCell(155,5, (show_value custom_value),"R")
247 247 end
248 248
249 249 pdf.SetFontStyle('B',9)
250 250 pdf.Cell(35,5, l(:field_subject) + ":","LTB")
251 251 pdf.SetFontStyle('',9)
252 252 pdf.Cell(155,5, issue.subject,"RTB")
253 253 pdf.Ln
254 254
255 255 pdf.SetFontStyle('B',9)
256 256 pdf.Cell(35,5, l(:field_description) + ":")
257 257 pdf.SetFontStyle('',9)
258 258 pdf.MultiCell(155,5, @issue.description,"BR")
259 259
260 260 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
261 261 pdf.Line(pdf.GetX, pdf.GetY, 170, pdf.GetY)
262 262 pdf.Ln
263 263
264 264 if issue.changesets.any? && User.current.allowed_to?(:view_changesets, issue.project)
265 265 pdf.SetFontStyle('B',9)
266 266 pdf.Cell(190,5, l(:label_associated_revisions), "B")
267 267 pdf.Ln
268 268 for changeset in issue.changesets
269 269 pdf.SetFontStyle('B',8)
270 270 pdf.Cell(190,5, format_time(changeset.committed_on) + " - " + changeset.author.to_s)
271 271 pdf.Ln
272 272 unless changeset.comments.blank?
273 273 pdf.SetFontStyle('',8)
274 274 pdf.MultiCell(190,5, changeset.comments)
275 275 end
276 276 pdf.Ln
277 277 end
278 278 end
279 279
280 280 pdf.SetFontStyle('B',9)
281 281 pdf.Cell(190,5, l(:label_history), "B")
282 282 pdf.Ln
283 283 for journal in issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
284 284 pdf.SetFontStyle('B',8)
285 285 pdf.Cell(190,5, format_time(journal.created_on) + " - " + journal.user.name)
286 286 pdf.Ln
287 287 pdf.SetFontStyle('I',8)
288 288 for detail in journal.details
289 289 pdf.Cell(190,5, "- " + show_detail(detail, true))
290 290 pdf.Ln
291 291 end
292 292 if journal.notes?
293 293 pdf.SetFontStyle('',8)
294 294 pdf.MultiCell(190,5, journal.notes)
295 295 end
296 296 pdf.Ln
297 297 end
298 298
299 299 if issue.attachments.any?
300 300 pdf.SetFontStyle('B',9)
301 301 pdf.Cell(190,5, l(:label_attachment_plural), "B")
302 302 pdf.Ln
303 303 for attachment in issue.attachments
304 304 pdf.SetFontStyle('',8)
305 305 pdf.Cell(80,5, attachment.filename)
306 306 pdf.Cell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
307 307 pdf.Cell(25,5, format_date(attachment.created_on),0,0,"R")
308 308 pdf.Cell(65,5, attachment.author.name,0,0,"R")
309 309 pdf.Ln
310 310 end
311 311 end
312 312 pdf.Output
313 313 end
314 314
315 315 # Returns a PDF string of a gantt chart
316 316 def gantt_to_pdf(gantt, project)
317 317 pdf = IFPDF.new(current_language)
318 318 pdf.SetTitle("#{l(:label_gantt)} #{project}")
319 319 pdf.AliasNbPages
320 320 pdf.footer_date = format_date(Date.today)
321 321 pdf.AddPage("L")
322 322 pdf.SetFontStyle('B',12)
323 323 pdf.SetX(15)
324 324 pdf.Cell(70, 20, project.to_s)
325 325 pdf.Ln
326 326 pdf.SetFontStyle('B',9)
327 327
328 subject_width = 70
328 subject_width = 100
329 329 header_heigth = 5
330 330
331 331 headers_heigth = header_heigth
332 332 show_weeks = false
333 333 show_days = false
334 334
335 335 if gantt.months < 7
336 336 show_weeks = true
337 337 headers_heigth = 2*header_heigth
338 338 if gantt.months < 3
339 339 show_days = true
340 340 headers_heigth = 3*header_heigth
341 341 end
342 342 end
343 343
344 g_width = 210
344 g_width = 280 - subject_width
345 345 zoom = (g_width) / (gantt.date_to - gantt.date_from + 1)
346 346 g_height = 120
347 347 t_height = g_height + headers_heigth
348 348
349 349 y_start = pdf.GetY
350 350
351 351 # Months headers
352 352 month_f = gantt.date_from
353 353 left = subject_width
354 354 height = header_heigth
355 355 gantt.months.times do
356 356 width = ((month_f >> 1) - month_f) * zoom
357 357 pdf.SetY(y_start)
358 358 pdf.SetX(left)
359 359 pdf.Cell(width, height, "#{month_f.year}-#{month_f.month}", "LTR", 0, "C")
360 360 left = left + width
361 361 month_f = month_f >> 1
362 362 end
363 363
364 364 # Weeks headers
365 365 if show_weeks
366 366 left = subject_width
367 367 height = header_heigth
368 368 if gantt.date_from.cwday == 1
369 369 # gantt.date_from is monday
370 370 week_f = gantt.date_from
371 371 else
372 372 # find next monday after gantt.date_from
373 373 week_f = gantt.date_from + (7 - gantt.date_from.cwday + 1)
374 374 width = (7 - gantt.date_from.cwday + 1) * zoom-1
375 375 pdf.SetY(y_start + header_heigth)
376 376 pdf.SetX(left)
377 377 pdf.Cell(width + 1, height, "", "LTR")
378 378 left = left + width+1
379 379 end
380 380 while week_f <= gantt.date_to
381 381 width = (week_f + 6 <= gantt.date_to) ? 7 * zoom : (gantt.date_to - week_f + 1) * zoom
382 382 pdf.SetY(y_start + header_heigth)
383 383 pdf.SetX(left)
384 384 pdf.Cell(width, height, (width >= 5 ? week_f.cweek.to_s : ""), "LTR", 0, "C")
385 385 left = left + width
386 386 week_f = week_f+7
387 387 end
388 388 end
389 389
390 390 # Days headers
391 391 if show_days
392 392 left = subject_width
393 393 height = header_heigth
394 394 wday = gantt.date_from.cwday
395 395 pdf.SetFontStyle('B',7)
396 396 (gantt.date_to - gantt.date_from + 1).to_i.times do
397 397 width = zoom
398 398 pdf.SetY(y_start + 2 * header_heigth)
399 399 pdf.SetX(left)
400 400 pdf.Cell(width, height, day_name(wday).first, "LTR", 0, "C")
401 401 left = left + width
402 402 wday = wday + 1
403 403 wday = 1 if wday > 7
404 404 end
405 405 end
406 406
407 407 pdf.SetY(y_start)
408 408 pdf.SetX(15)
409 409 pdf.Cell(subject_width+g_width-15, headers_heigth, "", 1)
410 410
411 411 # Tasks
412 412 top = headers_heigth + y_start
413 413 pdf.SetFontStyle('B',7)
414 414 gantt.events.each do |i|
415 415 pdf.SetY(top)
416 416 pdf.SetX(15)
417 417
418 418 if i.is_a? Issue
419 pdf.Cell(subject_width-15, 5, "#{i.tracker} #{i.id}: #{i.subject}".sub(/^(.{30}[^\s]*\s).*$/, '\1 (...)'), "LR")
419 text = "#{i.tracker} #{i.id}: #{i.subject}"
420 text = "#{i.project} - #{text}" unless project && project == i.project
421 pdf.Cell(subject_width-15, 5, text, "LR")
420 422 else
421 423 pdf.Cell(subject_width-15, 5, "#{l(:label_version)}: #{i.name}", "LR")
422 424 end
423 425
424 pdf.SetY(top)
426 pdf.SetY(top + 0.2)
425 427 pdf.SetX(subject_width)
426 pdf.Cell(g_width, 5, "", "LR")
427
428 pdf.SetFillColor(255, 255, 255)
429 pdf.Cell(g_width, 4.6, "", "LR", 0, "", 1)
428 430 pdf.SetY(top+1.5)
429 431
430 432 if i.is_a? Issue
431 433 i_start_date = (i.start_date >= gantt.date_from ? i.start_date : gantt.date_from )
432 434 i_end_date = (i.due_before <= gantt.date_to ? i.due_before : gantt.date_to )
433 435
434 436 i_done_date = i.start_date + ((i.due_before - i.start_date+1)*i.done_ratio/100).floor
435 437 i_done_date = (i_done_date <= gantt.date_from ? gantt.date_from : i_done_date )
436 438 i_done_date = (i_done_date >= gantt.date_to ? gantt.date_to : i_done_date )
437 439
438 440 i_late_date = [i_end_date, Date.today].min if i_start_date < Date.today
439 441
440 442 i_left = ((i_start_date - gantt.date_from)*zoom)
441 443 i_width = ((i_end_date - i_start_date + 1)*zoom)
442 444 d_width = ((i_done_date - i_start_date)*zoom)
443 445 l_width = ((i_late_date - i_start_date+1)*zoom) if i_late_date
444 446 l_width ||= 0
445 447
446 448 pdf.SetX(subject_width + i_left)
447 449 pdf.SetFillColor(200,200,200)
448 450 pdf.Cell(i_width, 2, "", 0, 0, "", 1)
449 451
450 452 if l_width > 0
451 453 pdf.SetY(top+1.5)
452 454 pdf.SetX(subject_width + i_left)
453 455 pdf.SetFillColor(255,100,100)
454 456 pdf.Cell(l_width, 2, "", 0, 0, "", 1)
455 457 end
456 458 if d_width > 0
457 459 pdf.SetY(top+1.5)
458 460 pdf.SetX(subject_width + i_left)
459 461 pdf.SetFillColor(100,100,255)
460 462 pdf.Cell(d_width, 2, "", 0, 0, "", 1)
461 463 end
462 464
463 465 pdf.SetY(top+1.5)
464 466 pdf.SetX(subject_width + i_left + i_width)
465 467 pdf.Cell(30, 2, "#{i.status} #{i.done_ratio}%")
466 468 else
467 469 i_left = ((i.start_date - gantt.date_from)*zoom)
468 470
469 471 pdf.SetX(subject_width + i_left)
470 472 pdf.SetFillColor(50,200,50)
471 473 pdf.Cell(2, 2, "", 0, 0, "", 1)
472 474
473 475 pdf.SetY(top+1.5)
474 476 pdf.SetX(subject_width + i_left + 3)
475 477 pdf.Cell(30, 2, "#{i.name}")
476 478 end
477 479
478 480 top = top + 5
479 481 pdf.SetDrawColor(200, 200, 200)
480 482 pdf.Line(15, top, subject_width+g_width, top)
481 483 if pdf.GetY() > 180
482 484 pdf.AddPage("L")
483 485 top = 20
484 486 pdf.Line(15, top, subject_width+g_width, top)
485 487 end
486 488 pdf.SetDrawColor(0, 0, 0)
487 489 end
488 490
489 491 pdf.Line(15, top, subject_width+g_width, top)
490 492 pdf.Output
491 493 end
492 494 end
493 495 end
494 496 end
General Comments 0
You need to be logged in to leave comments. Login now