Project

General

Profile

Patch #3614 » export_issues_to_pdf_csv.diff

Alex Mendes Matt Keniston, 2009-07-14 17:07

View differences:

app/helpers/issues_helper.rb (working copy)
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18
# Matthew Keniston & Alex Mendes 
19
# BackOffice Associates 
20
# 7/7/09 fixes for: 1) export to PDF/CSV ignoring custom queries; 2) query display problems
21

  
18 22
require 'csv'
19 23

  
20 24
module IssuesHelper
......
129 133
    end
130 134
  end
131 135
  
132
  def issues_to_csv(issues, project = nil)
136
  def retrieveHeaders(query, custom_fields)
137
		headers = Array.new(query.column_names.length())
138
		0.upto(query.column_names.length() - 1) do |counter|
139
		headerName = nil
140
		columnName = query.column_names[counter].to_s()
141
			case columnName
142
				when 'status'
143
					headerName = l(:field_status)
144
				when 'project'
145
					headerName = l(:field_project)
146
				when 'tracker'
147
					headerName = l(:field_tracker)
148
				when 'priority'
149
					headerName = l(:field_priority)
150
				when 'subject'
151
					headerName = l(:field_subject)
152
				when 'assigned_to'
153
					headerName = l(:field_assigned_to)
154
				when 'category'
155
					headerName = l(:field_category)
156
				when 'fixed_version'
157
					headerName = l(:field_fixed_version)
158
				when 'author'
159
					headerName = l(:field_author)
160
				when 'start_date'
161
					headerName = l(:field_start_date)
162
				when 'due_date'
163
					headerName = l(:field_due_date)
164
				when 'done_ratio'
165
					headerName = l(:field_done_ratio)
166
				when 'estimated_hours'
167
					headerName = l(:field_estimated_hours)
168
				when 'created_on'
169
					headerName = l(:field_created_on)
170
				when 'updated_on'
171
					headerName = l(:field_updated_on)
172
				else
173
					#custom field case...
174
					
175
					#parse the custom column id # from the column header
176
					id = getIDFromCustomColName(columnName)
177
				
178
					#match it with a custom field id to extract the correct header and field data
179
					custom_fields.each do |custom_field| #cycle through all possible custom fields
180
						if id == custom_field.id.to_s()	
181
							headerName = custom_field.name.to_s()
182
							break
183
						end
184
					end
185
				end
186
		headers[counter] = headerName != nil ? headerName : ''
187
		end
188
  return headers
189
  end
190
  
191
  def retrieveFieldData(issues, query, decimal_separator,custom_fields)
192
	fields = Array.new(query.column_names.length()){[]}
193
	#cycle through each issue (row) in the table..
194
		issues.each do |issue|
195
			#cycle through each column in the row...
196
			#loading the column names from the passed query...
197
			#in order to load the correct column data for each issue --> fields2[column][issue]
198
			#this method also loads the headers for each column --> headers2[column]
199
			0.upto(query.column_names.length() - 1) do |counter|
200
				columnName = query.column_names[counter].to_s()
201
				case columnName
202
				when 'status'
203
					fieldData = issue.status.name
204
				when 'project'
205
					fieldData = issue.project.name
206
				when 'tracker'
207
					fieldData = issue.tracker.name
208
				when 'priority'
209
					fieldData = issue.priority.name
210
				when 'subject'
211
					fieldData= issue.subject
212
				when 'assigned_to'
213
					fieldData = issue.assigned_to
214
				when 'category'
215
					fieldData = issue.category
216
				when 'fixed_version'
217
					fieldData  = issue.fixed_version
218
				when 'author'
219
					fieldData = issue.author.name
220
				when 'start_date'
221
					fieldData  = format_date(issue.start_date)
222
				when 'due_date'
223
					fieldData = format_date(issue.due_date)
224
				when 'done_ratio'
225
					fieldData = issue.done_ratio
226
				when 'estimated_hours'
227
					fieldData = issue.estimated_hours.to_s.gsub('.', decimal_separator)
228
				when 'created_on'
229
					fieldData = format_time(issue.created_on)
230
				when 'updated_on'
231
					fieldData = format_time(issue.updated_on)
232
				else
233
					#custom field case...
234
					
235
					#parse the custom column id # from the column header
236
					id = getIDFromCustomColName(columnName)
237
				
238
					#match it with a custom field id to extract the correct header and field data
239
					custom_fields.each do |custom_field| #cycle through all possible custom fields
240
						if id == custom_field.id.to_s()	
241
							fieldData = show_value(issue.custom_value_for(custom_field))
242
							break
243
						end
244
					end
245
				end #end case
246
				fields[counter][issue.id] = fieldData
247
			end #end upto
248
		end	#end issues.each
249
	return fields
250
	end
251
	
252
  #Method changed 7/7/09 
253
  def issues_to_csv(issues, query, project = nil)
133 254
    ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')    
134 255
    decimal_separator = l(:general_csv_decimal_separator)
135 256
    export = StringIO.new
136 257
    CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
137
      # csv header fields
138
      headers = [ "#",
258
		# csv header fields
259
		
260
		#if the user has specified columns in the query...
261
		if(@query.column_names != nil) 
262
			headers = Array.new(query.column_names.length()) #array for column headers
263
			fields = Array.new(query.column_names.length()){[]} #array for column fields
264
			custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
265
		
266
			# - _ - Retrieve the header and column data.
267
			
268
			fields = retrieveFieldData(issues,query,decimal_separator,custom_fields)
269
			headers = retrieveHeaders(query,custom_fields)
270
			#fields[col][row] is a multi-dimensional array which holds all the field data for the csv table.
271
			#...The first index is the column, and the second is the issue or row.
272
			#
273
			#headers[col] is an array which holds data for the column headers
274
	
275
			# - _ - Now, Print the headers and fields
276
	 
277
			#Add bug number to the front of column headers list, via new array headersTemp
278
			#Make '#' the first entry, then append with the 'headers' array
279
			headersTemp = Array.new(1)
280
			headersTemp[0] = "#"
281
			0.upto(headers.length()-1) do |counter|
282
				headersTemp << headers[counter]
283
			end
284
	 
285
			#print headers, via headersTemp
286
			csv << headersTemp.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
287
	 
288
			#add bug number to the field arrays, via a new array called together
289
			together = Array.new(issues.length())
290
			issues.each do |issue|	#for each issue...
291
				together[issue.id] = [issue.id.to_s()]   #add the issue id
292
				#for each column in the issue...
293
				0.upto(@query.column_names.length() - 1) do |counter|
294
					# construct the issue rows from the columns.
295
				   together[issue.id] << fields[counter][issue.id]
296
				end
297
				#print the fields to the screen, issue at a time, via together
298
				csv << together[issue.id].collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
299
			end
300
		else # if no fields specified --> then display default fields in csv
301
			# csv default header fields
302
			headers = [ "#",
139 303
                  l(:field_status), 
140 304
                  l(:field_project),
141
                  l(:field_tracker),
305
				  l(:field_tracker),
142 306
                  l(:field_priority),
143 307
                  l(:field_subject),
144
                  l(:field_assigned_to),
308
                  l(:field_assigned_to),               
145 309
                  l(:field_category),
146 310
                  l(:field_fixed_version),
147 311
                  l(:field_author),
......
156 320
      # otherwise export custom fields marked as "For all projects"
157 321
      custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
158 322
      custom_fields.each {|f| headers << f.name}
159
      # Description in the last column
323
			# Description in the last column
160 324
      headers << l(:field_description)
161
      csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
162
      # csv lines
163
      issues.each do |issue|
164
        fields = [issue.id,
165
                  issue.status.name, 
166
                  issue.project.name,
167
                  issue.tracker.name, 
168
                  issue.priority.name,
169
                  issue.subject,
170
                  issue.assigned_to,
171
                  issue.category,
172
                  issue.fixed_version,
173
                  issue.author.name,
174
                  format_date(issue.start_date),
175
                  format_date(issue.due_date),
176
                  issue.done_ratio,
177
                  issue.estimated_hours.to_s.gsub('.', decimal_separator),
178
                  format_time(issue.created_on),  
179
                  format_time(issue.updated_on)
180
                  ]
325
			csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
326
			# csv lines
327
			issues.each do |issue|
328
				fields = [issue.id,
329
                issue.status.name, 
330
                issue.project.name,
331
			    issue.tracker.name, 
332
			    issue.priority.name,
333
				issue.subject,
334
				issue.assigned_to,
335
                issue.category,
336
                issue.fixed_version,
337
                issue.author.name,
338
                format_date(issue.start_date),
339
                format_date(issue.due_date),
340
                issue.done_ratio,
341
                issue.estimated_hours.to_s.gsub('.', decimal_separator),
342
                format_time(issue.created_on),  
343
			    format_time(issue.updated_on)
344
					]
181 345
        custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) }
182 346
        fields << issue.description
183
        csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
184
      end
347
				csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
348
			end
349
		end
185 350
    end
186 351
    export.rewind
187 352
    export
app/helpers/custom_fields_helper.rb (working copy)
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18
# Matthew Keniston & Alex Mendes 
19
# BackOffice Associates 
20
# 7/7/09 fixes for: 1) export to PDF/CSV ignoring custom queries; 2) query display problems
21

  
18 22
module CustomFieldsHelper
19 23

  
20 24
  def custom_fields_tabs
......
85 89
  def custom_field_formats_for_select
86 90
    CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
87 91
  end
92
  
93
 #Method Added 7/7/09
94
     #Extracts the id from a custom column name
95
     #E.g. "cf_6" becomes "6", which is the id for the custom column
96
     def getIDFromCustomColName(colName)
97
     	return colName.gsub(/.*_(.*)/, '\1')
98
     end
99
	  
100
#Method Added 7/7/09
101
     #Capitalizes each word in a title
102
      def capitalizeEachWord(title)
103
	    spaceFlag = nil  
104
	    newTitle = "" 	
105
            0.upto(title.length() - 1) do |cntr|
106
			     char = title.slice(cntr..cntr)
107
				 if(cntr == 0) 
108
				     char.capitalize!
109
	             elsif(char == " ")
110
                     spaceFlag = true  
111
                 elsif(spaceFlag)   
112
				     char.capitalize!
113
					 spaceFlag = nil
114
                 end  		 
115
				  newTitle = newTitle + char    
116
			end
117
	    return newTitle
118
      end
119
	 
120
#Method Added 7/7/09
121
     #Returns the type of the custom field or the column name for default fields
122
     def getType(issue,pos) 
123
	   fieldType = ""
124
	   
125
	   if (@query.column_names != nil) # make sure we have user query
126
	    
127
	    col = @query.column_names[pos].to_s()
128
		   
129
	    if (issue.respond_to?(col))
130
		  fieldType = col.to_s()
131
		  
132
		else 	# custom field 
133
		  custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
134
		  columnName = @query.column_names[pos].to_s()
135
		  id = getIDFromCustomColName(columnName)
136
		  custom_fields.each do |custom_field| 
137
			  if id == custom_field.id.to_s()	
138
					fieldType = custom_field.field_format.to_s()
139
					break
140
			  end
141
			end 
142
        end
143
      end		 
144
	  return fieldType
145
    end	
146

  
147
#Method Added 7/2/09	  
148
	  #Given an issue and a column this returns the correct data 
149
      def getValue(issue, pos)
150
	  	 fieldValue = ""
151
	     col = @query.column_names[pos].to_s()
152
		 
153
		 #Default field
154
	     if(issue.respond_to?(col))
155
		   type = (issue.send(col)).type.to_s()
156
	       case type
157
		  	when "Time"
158
		       fieldValue = format_time(issue.send(col))
159
		  	when "Date" 
160
		  	   fieldValue = format_date(issue.send(col))
161
		    when "Float", "String", "int", "Fixnum", ""
162
		       fieldValue = issue.send(col).to_s()
163
		  	when "Text", "Tracker","IssueStatus", "IssueCategory", "Enumeration", "Version", "User", "Project"
164
		       fieldValue = issue.send(col).name
165
		    end
166
		   
167
		 #Custom Field  
168
		 else 	
169
		   custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
170
		   columnName = @query.column_names[pos].to_s()
171
		   id = getIDFromCustomColName(columnName)
172
		   custom_fields.each do |custom_field| 
173
			    if id == custom_field.id.to_s()	
174
				  	  type = custom_field.field_format.to_s()
175
					  fieldValue =  show_value(issue.custom_value_for(custom_field))
176
					  break
177
			    end
178
		   end
179

  
180
			#If type is a boolean value
181
            if type == "bool"
182
            	fieldValue = (fieldValue == "1") ? "YES" : "NO"
183
            end				
184
          end
185
          
186
          return fieldValue
187
	  end
88 188
end
app/controllers/issues_controller.rb (working copy)
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18
# Matthew Keniston & Alex Mendes 
19
# BackOffice Associates 
20
# 7/7/09 fixes for: 1) export to PDF/CSV ignoring custom queries; 2) query display problems
21

  
18 22
class IssuesController < ApplicationController
19 23
  menu_item :new_issue, :only => :new
20 24
  
......
77 81
          render :template => 'issues/index.rhtml', :layout => !request.xhr?
78 82
        }
79 83
        format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
80
        format.csv  { send_data(issues_to_csv(@issues, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
81
        format.pdf  { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
84
        #Changed 7/7/09 - call smarter csv and pdf functions
85
        format.csv  { send_data(issues_to_csv(@issues, @query, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
86
#       format.csv  { send_data(issues_to_csv(@issues, @project).read,         :type => 'text/csv; header=present', :filename => 'export.csv') }
87
        format.pdf  { send_data(issues_to_pdf(@issues, @query, @project),      :type => 'application/pdf',          :filename => 'export.pdf') }
88
#       format.pdf  { send_data(issues_to_pdf(@issues, @project, @query),              :type => 'application/pdf',          :filename => 'export.pdf') }
89

  
82 90
      end
83 91
    else
84 92
      # Send html if the query is not valid
app/views/issues/_list.rhtml (working copy)
25 25
	<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %>">
26 26
	    <td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
27 27
		<td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
28
        <% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>
28
        <!-- Changed 7/8/09 - support smarter css class for custom fields -->
29
		
30
		<% pos = 0 %>  
31
		<% query.columns.each do |column| %>
32
		   <% value = column.name %>
33
			
34
	       <% if query.column_names != nil  %>  <!-- it's a user query, so may contain custom fields -->
35
		      <% value = getType(issue, pos) %>
36
			  <% pos = pos + 1 %>
37
           <% end %>
38
		   
39
           <%= content_tag 'td', column_content(column, issue), :class => value  %>	   
40
        <% end -%>
29 41
	</tr>
30 42
	<% end -%>
31 43
	</tbody>
lib/redmine/export/pdf.rb (working copy)
15 15
# along with this program; if not, write to the Free Software
16 16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 17

  
18
# Matthew Keniston & Alex Mendes 
19
# BackOffice Associates 
20
# 7/7/09 fixes for: 1) export to PDF/CSV ignoring custom queries; 2) query display problems
21

  
18 22
require 'iconv'
19 23
require 'rfpdf/fpdf'
20 24
require 'rfpdf/chinese'
......
22 26
module Redmine
23 27
  module Export
24 28
    module PDF
29
#Added 7/2/09 
30
#*****************************************************************************
31
	  MAXCOLSIZE = 60  #Maximum column size before wrappping (string length)
32
	  MAXPDFWIDTH = 250 #Approximate width of pdf document
33
#*****************************************************************************
25 34
      include ActionView::Helpers::TextHelper
26 35
      include ActionView::Helpers::NumberHelper
27 36
      
......
95 104
          end || ''
96 105
          super w,h,txt,border,ln,align,fill,link
97 106
        end
98
        
107
		
108
        #Method Changed 7/2/09
99 109
        def Footer
110
		  tempSize = getFontSize()
111
		  tempFamily = getFontFamily() 
112
		  tempStyle = getFontStyle()
100 113
          SetFont(@font_for_footer, 'I', 8)
101 114
          SetY(-15)
102 115
          SetX(15)
......
104 117
          SetY(-15)
105 118
          SetX(-30)
106 119
          Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
120
		  SetFont(tempFamily, tempStyle, tempSize)
107 121
        end
108 122
      end
109
      
123
	  
124
      #Method Added 7/2/09
125
	  def printColumnHeadersPDF(query, issues, colWidth, row_height, fontSize, pdf, maxCustomContent) 
126
	         # headers
127
             pdf.SetFontStyle('B',fontSize)
128
             pdf.SetFillColor(230, 230, 230)
129
			 
130
	         #Print the column header
131
			  pdf.Cell(20, row_height, "#", 0, 0, 'L', 1)
132
			  
133
		       0.upto(@query.column_names.length - 1) do |cntr|
134
 				 colName = query.column_names[cntr].to_s()
135
				 re = /^cf.*/ #identifier for the column names of custom fields
136
				 
137
				  # custom field 
138
			      if (re.match(colName))
139

  
140
				   custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
141
				   id = getIDFromCustomColName(colName)
142
				   custom_fields.each do |custom_field| #find the name
143
						if id == custom_field.id.to_s()	
144
							  colName = custom_field.name.to_s()
145
							  break
146
						end
147
				     end
148
				  end
149
			       
150
				   colName = colName.gsub(/[_]/, ' ').to_s() # convert underscores to spaces
151
				   colName = capitalizeEachWord(colName)
152
				   pdf.Cell(colWidth[cntr], row_height, colName, 0, 0, 'L', 1)
153
		        end
154
				
155
	  end
156
	  
157
	  #Method Added 7/2/09
158
	  def printFieldsPDF(query, issues, colWidth, row_height, fontSize, pdf, rowHeightMultiCell, maxMultiColWidth)
159
		
160
	           pdf.Line(10, pdf.GetY, 287, pdf.GetY)
161
               pdf.Ln
162
               pdf.Line(10, pdf.GetY, 287, pdf.GetY)
163
               pdf.SetY(pdf.GetY() + 1)
164
               pdf.SetFontStyle('',fontSize)
165
               pdf.SetFillColor(255, 255, 255)
166
			  
167
		       #Print the rows 
168
               issues.each do |issue|
169
                  flag = 0 #How many lines occur during a text wrapping
170
                  flagPageBreak = 0	#Check if a page break has occurred
171
                  pageBreakPos = 0  #Position after the page break used for restoring
172
				  
173
			      pdf.Cell(20, row_height, issue.id.to_s, 0, 0, 'L', 1)
174
				  
175
		          0.upto(query.column_names.length - 1) do |cntr|
176
				  
177
				     if(getValue(issue,cntr).length < MAXCOLSIZE)  #No text wrapping
178
					 	pdf.SetFontStyle('',fontSize)
179
		                pdf.Cell(colWidth[cntr], row_height, getValue(issue,cntr))  
180
				     else #For text wrapping
181
					    beforeX = pdf.GetX() #Before X position
182
						beforeY = pdf.GetY() #Before Y position
183
                        pad = 1 #Extra Height of muli-cells
184
						
185
						pdf.SetY(pdf.GetY() + pad)
186
                        pdf.SetX(beforeX) 						
187
					    pdf.MultiCell(colWidth[cntr], rowHeightMultiCell, getValue(issue,cntr))
188
						
189
						afterX = pdf.GetX()
190
						pdf.SetY(pdf.GetY() + pad) 
191
						pdf.SetX(afterX) 
192
						
193
						currentPage = pdf.getPage()   
194
						afterY = pdf.GetY() #After Y position
195
						dif = afterY - beforeY #Difference after text wrapping
196
						if(afterY < beforeY)
197
						     pdf.setPage(pdf.getPage - 1) 
198
							 flagPageBreak = 1
199
							 if(pageBreakPos < afterY)
200
							     pageBreakPos = afterY
201
							 end 			
202
						end      
203
						
204
						#Count the number of lines skipped
205
					    if(dif/rowHeightMultiCell > flag)
206
                          flag = 0						
207
						  while (dif > 0)  
208
                            dif = dif - rowHeightMultiCell
209
							flag = flag + 1
210
                          end								 
211
                        end								
212
						
213
					    pdf.SetY(beforeY)
214
					    pdf.SetX(beforeX + maxMultiColWidth)
215
					    
216
					 end
217
				  end
218
				  	if(flagPageBreak == 1) 	#Restore Page after break
219
					    pdf.setPage(pdf.getPage() + 1) 
220
					    pdf.SetY(pageBreakPos - row_height)
221
					    flagPageBreak = 0
222
				    else
223
                        pdf.Line(10, pdf.GetY, 287, pdf.GetY)					
224
					end
225
                  #Restore after wrapping
226
				  if(flag > 0)
227
                        pdf.SetY(pdf.GetY() + rowHeightMultiCell * flag)				  
228
			      else 
229
					  	pdf.SetY(pdf.GetY() + 10)
230
				  end 
231
               end
232
	  end
233
	  
234
#Method Added 7/7/09	 	  
235
	  def printUserQuery(pdf, row_height)
236
	  
237
	         rowHeightMultiCell = 4 #Default height for multi-cell				
238
		     fontSize = 10  #Font size before shinking
239
		     maxMultiColWidth = 120 #Maximium column width before wrapping 
240
			 maxCustomContent = 0 #Issue that contains the most custom fields 
241
			 
242
			 arrMaxColLen = Array.new(@query.column_names.length) #Column Width in terms of characters based on the longest string
243
			 colWidth = Array.new(@query.column_names.length)  #Contains the width of each column in pdf units
244
		
245
		     
246
			 #Create an array containing the lengths of the largest fields in each column
247
		     0.upto(@query.column_names.length - 1) do |cntr|
248
		         currentMax = 0;  
249
		         @issues.each do |issue|
250
		             valueLength = getValue(issue,cntr).length()
251
			         if(valueLength > currentMax)
252
      			        currentMax = valueLength 
253
			         end
254
			         arrMaxColLen[cntr] = currentMax
255
		 	     end
256
	         end
257
             			 
258
             #See if any of the headers are longer than the largest value			 
259
		     0.upto(@query.column_names.length - 1) do |cntr|
260
			     len = 0 #Length of the column name header
261
				 re = /^cf.*/ #Identify column fields by their column headers
262
				 
263
				 #Finds the length of the column name header 
264
			     if (!re.match(@query.column_names[cntr].to_s()))
265
	   	            len = @query.column_names[cntr].to_s().length()
266
			     else #Custom Field 
267
				    max = 0  
268
				      0.upto(@issues.length - 1) do |issue|
269
                        if(@issues[issue].custom_values.length() > max) 
270
                           max = @issues[issue].custom_values.length()
271
						   maxCustomContent = issue #Issue that contains the most custom fields 
272
                        end 
273
					  end
274
					  columnNameId = getIDFromCustomColName(@query.column_names[cntr].to_s())
275
					  0.upto(@issues[maxCustomContent].custom_values.length - 1) do |custom_value|
276
						customField = @issues[maxCustomContent].custom_values[custom_value].custom_field
277
						
278
				       if (customField.id.to_s() == columnNameId)
279
                          len = customField.name.length() #Length of the header of the custom fields
280
                       end 
281
                      end					  
282
  			     end
283
				 
284
                  #Check if column name headers are larger than the fields				 
285
				 if(arrMaxColLen[cntr] < len)
286
			        arrMaxColLen[cntr] = len 
287
			     end
288
	           end
289
			   
290
			   #Set each column width given the value lengths  
291
			   0.upto(@query.column_names.length - 1) do |cntr|
292
			     ourColumnWidth = arrMaxColLen[cntr] * 1.5 + 10 
293
			     
294
			     colWidth[cntr] = (ourColumnWidth <  maxMultiColWidth) ? ourColumnWidth : maxMultiColWidth
295
			   end
296
			   
297
			   #Total column length of the whole page
298
			   total = 0
299
			   0.upto(colWidth.length - 1) do |cntr|
300
			      total = total + colWidth[cntr] 
301
			   end
302
			   
303
			   #Scales the font sizes based on column data width
304
			   percent = 1.0 			   
305
			   if(total > MAXPDFWIDTH) 
306
			      percent = MAXPDFWIDTH.to_f()/total.to_f()
307
				  maxMultiColWidth = percent * maxMultiColWidth
308
				  0.upto(@query.column_names.length - 1) do |cntr|
309
                      colWidth[cntr] = colWidth[cntr] * percent
310
                  end					  
311
				  fontSize = fontSize * 0.75 * percent
312
			      pdf.SetFontStyle('B',fontSize)
313
			   end 
314
				
315
			   printColumnHeadersPDF(@query, @issues, colWidth, row_height, fontSize, pdf, maxCustomContent) 
316
			   printFieldsPDF       (@query, @issues, colWidth, row_height, fontSize, pdf, rowHeightMultiCell, maxMultiColWidth)
317
	  end
318
	  
110 319
      # Returns a PDF string of a list of issues
111
      def issues_to_pdf(issues, project, query)
320
      def issues_to_pdf(issues, query, project)
112 321
        pdf = IFPDF.new(current_language)
113
        title = project ? "#{project} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
322
        if(query.name != "_") 
323
		   title = project ? "#{project} - #{@query.name.to_s()}"  : " #{@query.name.to_s()}"
324
		else 
325
		   title = project ? "#{project} - #{l(:label_issue_plural)}"  : "#{l(:label_issue_plural)}"
326
		end
114 327
        pdf.SetTitle(title)
115 328
        pdf.AliasNbPages
116 329
        pdf.footer_date = format_date(Date.today)
......
122 335
        pdf.Cell(190,10, title)
123 336
        pdf.Ln
124 337
        
125
        # headers
126
        pdf.SetFontStyle('B',10)
127
        pdf.SetFillColor(230, 230, 230)
128
        pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
129
        pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
130
        pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
131
        pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
132
        pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
133
        pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
134
        pdf.Cell(0, row_height, l(:field_subject), 0, 0, 'L', 1)
135
        pdf.Line(10, pdf.GetY, 287, pdf.GetY)
136
        pdf.Ln
137
        pdf.Line(10, pdf.GetY, 287, pdf.GetY)
138
        pdf.SetY(pdf.GetY() + 1)
338
	    # headers
339
		#If there are column names
340
		if(@query.column_names != nil) 		
341
             printUserQuery(pdf, row_height)
342
		else #If there are no columns
343
          pdf.SetFontStyle('B',10)
344
          pdf.SetFillColor(230, 230, 230)
345
          pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
346
          pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
347
          pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
348
          pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
349
          pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
350
          pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
351
          pdf.Cell(0, row_height,  l(:field_subject), 0, 0, 'L', 1)
352
          pdf.Line(10, pdf.GetY, 287, pdf.GetY)
353
          pdf.Ln
354
          pdf.Line(10, pdf.GetY, 287, pdf.GetY)
355
          pdf.SetY(pdf.GetY() + 1)
139 356
        
140
        # rows
141
        pdf.SetFontStyle('',9)
142
        pdf.SetFillColor(255, 255, 255)
143
        group = false
144
        issues.each do |issue|
145
          if query.grouped? && issue.send(query.group_by) != group
146
            group = issue.send(query.group_by)
147
            pdf.SetFontStyle('B',10)
148
            pdf.Cell(0, row_height, "#{group.blank? ? 'None' : group.to_s}", 0, 1, 'L')
357
          # rows
358
          pdf.SetFontStyle('',9)
359
          pdf.SetFillColor(255, 255, 255)
360
          group = false
361
          issues.each do |issue|
362
            if query.grouped? && issue.send(query.group_by) != group
363
              group = issue.send(query.group_by)
364
              pdf.SetFontStyle('B',10)
365
              pdf.Cell(0, row_height, "#{group.blank? ? 'None' : group.to_s}", 0, 1, 'L')
366
              pdf.Line(10, pdf.GetY, 287, pdf.GetY)
367
              pdf.SetY(pdf.GetY() + 0.5)
368
              pdf.Line(10, pdf.GetY, 287, pdf.GetY)
369
              pdf.SetY(pdf.GetY() + 1)
370
              pdf.SetFontStyle('',9)
371
            end
372
            pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
373
            pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
374
            pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
375
            pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
376
            pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
377
            pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
378
            pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
149 379
            pdf.Line(10, pdf.GetY, 287, pdf.GetY)
150
            pdf.SetY(pdf.GetY() + 0.5)
151
            pdf.Line(10, pdf.GetY, 287, pdf.GetY)
152 380
            pdf.SetY(pdf.GetY() + 1)
153
            pdf.SetFontStyle('',9)
154 381
          end
155
          pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
156
          pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
157
          pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
158
          pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
159
          pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
160
          pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
161
          pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
162
          pdf.Line(10, pdf.GetY, 287, pdf.GetY)
163
          pdf.SetY(pdf.GetY() + 1)
164
        end
382
	     end
165 383
        pdf.Output
166 384
      end
167 385
      
vendor/plugins/rfpdf/lib/rfpdf/fpdf.rb (working copy)
19 19
#   Handle '\n' at the beginning of a string
20 20
# Bookmarks contributed by Sylvain Lafleur.
21 21

  
22
# Matthew Keniston & Alex Mendes 
23
# BackOffice Associates 
24
# 7/7/09 fixes for: 1) export to PDF/CSV ignoring custom queries; 2) query display problems
25

  
22 26
require 'date'
23 27
require 'zlib'
24 28

  
......
279 283
    end
280 284

  
281 285
    def AddPage(orientation='')
286
#Added 7/2/09
287
#************************************
288
	    #If next page already exists
289
	    if(getPage() < @pages.length() - 1) 
290
		     setPage(getPage() + 1)	
291
             @y = 0 + @tMargin 
292
#************************************
293
        else
282 294
        # Start a new page
283 295
        self.Open if @state==0
284 296
        family=@FontFamily
......
333 345
        end
334 346
        @TextColor=tc
335 347
        @ColorFlag=cf
348
		end
336 349
    end
337 350

  
338 351
    def Header
......
872 885
            @y=@y+h
873 886
        end
874 887
    end
888
  
889
    #Added 7/2/09 for pdf.rb 	
890
#***********************
891
    def  getFontFamily
892
	    @FontFamily
893
	end 
894
	
895
	def  getFontStyle
896
	    @FontStyle
897
	end 
898
	
899
	def getFontSize
900
    	@FontSizePt
901
    end
902
    def getPage()
903
	    @page
904
	end
905
	
906
	def setPage(page) 
907
	    @page = page
908
	end
909
#**********************
875 910

  
876 911
    def GetX
877 912
        # Get x position
public/stylesheets/application.css (working copy)
94 94
tr.project td.name a { padding-left: 16px; white-space:nowrap; }
95 95
tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; }
96 96

  
97

  
98
 /*** Changed 7/7/09 - nowrap was a bad idea! ***/
99
tr.issue { text-align: center; white-space: normal; }
100
/*
97 101
tr.issue { text-align: center; white-space: nowrap; }
98 102
tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
103
*/
104

  
105
/*** Changed 7/7/09 - Added left alignment for text-based custom columns ***/
106
tr.issue td.subject, tr.issue td.text, tr.issue td.string { text-align: left; }
107
/*
99 108
tr.issue td.subject { text-align: left; }
109
*/
110

  
100 111
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
101 112

  
102 113
tr.entry { border: 1px solid #f8f8f8; }
(1-1/4)