Project

General

Profile

Patch #3614 » export_issues_to_pdf_csv_r2823.diff

Chris Grieger, 2009-07-29 13:18

View differences:

app/helpers/issues_helper.rb (working copy)
129 129
    end
130 130
  end
131 131
  
132
  def issues_to_csv(issues, project = nil)
132
  def retrieveHeaders(query, custom_fields)
133
		headers = Array.new(query.column_names.length())
134
		0.upto(query.column_names.length() - 1) do |counter|
135
		headerName = nil
136
		columnName = query.column_names[counter].to_s()
137
			case columnName
138
				when 'status'
139
					headerName = l(:field_status)
140
				when 'project'
141
					headerName = l(:field_project)
142
				when 'tracker'
143
					headerName = l(:field_tracker)
144
				when 'priority'
145
					headerName = l(:field_priority)
146
				when 'subject'
147
					headerName = l(:field_subject)
148
				when 'assigned_to'
149
					headerName = l(:field_assigned_to)
150
				when 'category'
151
					headerName = l(:field_category)
152
				when 'fixed_version'
153
					headerName = l(:field_fixed_version)
154
				when 'author'
155
					headerName = l(:field_author)
156
				when 'start_date'
157
					headerName = l(:field_start_date)
158
				when 'due_date'
159
					headerName = l(:field_due_date)
160
				when 'done_ratio'
161
					headerName = l(:field_done_ratio)
162
				when 'estimated_hours'
163
					headerName = l(:field_estimated_hours)
164
				when 'created_on'
165
					headerName = l(:field_created_on)
166
				when 'updated_on'
167
					headerName = l(:field_updated_on)
168
				else
169
					#custom field case...
170

  
171
					#parse the custom column id # from the column header
172
					id = getIDFromCustomColName(columnName)
173

  
174
					#match it with a custom field id to extract the correct header and field data
175
					custom_fields.each do |custom_field| #cycle through all possible custom fields
176
						if id == custom_field.id.to_s()
177
							headerName = custom_field.name.to_s()
178
							break
179
						end
180
					end
181
				end
182
		headers[counter] = headerName != nil ? headerName : ''
183
		end
184
  return headers
185
  end
186

  
187
  def retrieveFieldData(issues, query, decimal_separator,custom_fields)
188
	fields = Array.new(query.column_names.length()){[]}
189
	#cycle through each issue (row) in the table..
190
		issues.each do |issue|
191
			#cycle through each column in the row...
192
			#loading the column names from the passed query...
193
		#in order to load the correct column data for each issue --> fields2[column][issue]
194
			#this method also loads the headers for each column --> headers2[column]
195
			0.upto(query.column_names.length() - 1) do |counter|
196
				columnName = query.column_names[counter].to_s()
197
				case columnName
198
				when 'status'
199
					fieldData = issue.status.name
200
				when 'project'
201
					fieldData = issue.project.name
202
				when 'tracker'
203
					fieldData = issue.tracker.name
204
				when 'priority'
205
					fieldData = issue.priority.name
206
				when 'subject'
207
					fieldData= issue.subject
208
				when 'assigned_to'
209
					fieldData = issue.assigned_to
210
				when 'category'
211
					fieldData = issue.category
212
				when 'fixed_version'
213
					fieldData  = issue.fixed_version
214
				when 'author'
215
					fieldData = issue.author.name
216
				when 'start_date'
217
					fieldData  = format_date(issue.start_date)
218
				when 'due_date'
219
					fieldData = format_date(issue.due_date)
220
				when 'done_ratio'
221
					fieldData = issue.done_ratio
222
				when 'estimated_hours'
223
					fieldData = issue.estimated_hours.to_s.gsub('.', decimal_separator)
224
				when 'created_on'
225
					fieldData = format_time(issue.created_on)
226
				when 'updated_on'
227
					fieldData = format_time(issue.updated_on)
228
				else
229
					#custom field case...
230

  
231
					#parse the custom column id # from the column header
232
					id = getIDFromCustomColName(columnName)
233

  
234
					#match it with a custom field id to extract the correct header and field data
235
					custom_fields.each do |custom_field| #cycle through all possible custom fields
236
						if id == custom_field.id.to_s()
237
							fieldData = show_value(issue.custom_value_for(custom_field))
238
							break
239
						end
240
					end
241
				end #end case
242
				fields[counter][issue.id] = fieldData
243
			end #end upto
244
		end	#end issues.each
245
	return fields
246
	end
247

  
248
  #Method changed 7/7/09
249
  def issues_to_csv(issues, query, project = nil)
133 250
    ic = Iconv.new(l(:general_csv_encoding), 'UTF-8')    
134 251
    decimal_separator = l(:general_csv_decimal_separator)
135 252
    export = StringIO.new
136 253
    CSV::Writer.generate(export, l(:general_csv_separator)) do |csv|
137
      # csv header fields
138
      headers = [ "#",
254
     # csv header fields
255

  
256
		#if the user has specified columns in the query...
257
		if(@query.column_names != nil)
258
			headers = Array.new(query.column_names.length()) #array for column headers
259
			fields = Array.new(query.column_names.length()){[]} #array for column fields
260
			custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields
261

  
262
			# - _ - Retrieve the header and column data.
263

  
264
			fields = retrieveFieldData(issues,query,decimal_separator,custom_fields)
265
			headers = retrieveHeaders(query,custom_fields)
266
			#fields[col][row] is a multi-dimensional array which holds all the field data for the csv table.
267
			#...The first index is the column, and the second is the issue or row.
268
			#
269
			#headers[col] is an array which holds data for the column headers
270

  
271
			# - _ - Now, Print the headers and fields
272

  
273
			#Add bug number to the front of column headers list, via new array headersTemp
274
			#Make '#' the first entry, then append with the 'headers' array
275
			headersTemp = Array.new(1)
276
			headersTemp[0] = "#"
277
			0.upto(headers.length()-1) do |counter|
278
				headersTemp << headers[counter]
279
			end
280

  
281
			#print headers, via headersTemp
282
			csv << headersTemp.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
283

  
284
			#add bug number to the field arrays, via a new array called together
285
			together = Array.new(issues.length())
286
			issues.each do |issue|	#for each issue...
287
				together[issue.id] = [issue.id.to_s()]   #add the issue id
288
				#for each column in the issue...
289
				0.upto(@query.column_names.length() - 1) do |counter|
290
					# construct the issue rows from the columns.
291
				   together[issue.id] << fields[counter][issue.id]
292
				end
293
				#print the fields to the screen, issue at a time, via together
294
				csv << together[issue.id].collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
295
			end
296
		else # if no fields specified --> then display default fields in csv
297
			# csv default header fields
298
			headers = [ "#",
139 299
                  l(:field_status), 
140 300
                  l(:field_project),
141 301
                  l(:field_tracker),
......
183 343
        csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end }
184 344
      end
185 345
    end
346
    end
186 347
    export.rewind
187 348
    export
188 349
  end
app/helpers/custom_fields_helper.rb (working copy)
85 85
  def custom_field_formats_for_select
86 86
    CustomField::FIELD_FORMATS.sort {|a,b| a[1][:order]<=>b[1][:order]}.collect { |k| [ l(k[1][:name]), k[0] ] }
87 87
  end
88
end
88

  
89

  
90
 #Method Added 7/7/09
91
     #Extracts the id from a custom column name
92
     #E.g. "cf_6" becomes "6", which is the id for the custom column
93
     def getIDFromCustomColName(colName)
94
     	return colName.gsub(/.*_(.*)/, '\1')
95
     end
96

  
97
#Method Added 7/7/09
98
     #Capitalizes each word in a title
99
      def capitalizeEachWord(title)
100
	    spaceFlag = nil
101
	    newTitle = ""
102
            0.upto(title.length() - 1) do |cntr|
103
			     char = title.slice(cntr..cntr)
104
				 if(cntr == 0)
105
				     char.capitalize!
106
	             elsif(char == " ")
107
                     spaceFlag = true
108
                 elsif(spaceFlag)
109
				     char.capitalize!
110
					 spaceFlag = nil
111
                 end
112
				  newTitle = newTitle + char
113
			end
114
	    return newTitle
115
      end
116

  
117
#Method Added 7/7/09
118
     #Returns the type of the custom field or the column name for default fields
119
     def getType(issue,pos)
120
	   fieldType = ""
121

  
122
	   if (@query.column_names != nil) # make sure we have user query
123

  
124
	    col = @query.column_names[pos].to_s()
125

  
126
	    if (issue.respond_to?(col))
127
		  fieldType = col.to_s()
128

  
129
		else 	# custom field
130
		  custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
131
		  columnName = @query.column_names[pos].to_s()
132
		  id = getIDFromCustomColName(columnName)
133
		  custom_fields.each do |custom_field|
134
			  if id == custom_field.id.to_s()
135
					fieldType = custom_field.field_format.to_s()
136
					break
137
			  end
138
			end
139
        end
140
      end
141
	  return fieldType
142
    end
143

  
144
#Method Added 7/2/09
145
	  #Given an issue and a column this returns the correct data
146
      def getValue(issue, pos)
147
	  	 fieldValue = ""
148
	     col = @query.column_names[pos].to_s()
149

  
150
		 #Default field
151
	     if(issue.respond_to?(col))
152
		   type = (issue.send(col)).type.to_s()
153
	       case type
154
		  	when "Time"
155
		       fieldValue = format_time(issue.send(col))
156
		  	when "Date"
157
		  	   fieldValue = format_date(issue.send(col))
158
		    when "Float", "String", "int", "Fixnum", ""
159
		       fieldValue = issue.send(col).to_s()
160
		  	when "Text", "Tracker","IssueStatus", "IssueCategory", "Enumeration", "Version", "User", "Project"
161
		       fieldValue = issue.send(col).name
162
		    end
163

  
164
		 #Custom Field
165
		 else
166
		   custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
167
		   columnName = @query.column_names[pos].to_s()
168
		   id = getIDFromCustomColName(columnName)
169
		   custom_fields.each do |custom_field|
170
			    if id == custom_field.id.to_s()
171
				  	  type = custom_field.field_format.to_s()
172
					  fieldValue =  show_value(issue.custom_value_for(custom_field))
173
					  break
174
			    end
175
		   end
176

  
177
			#If type is a boolean value
178
            if type == "bool"
179
            	fieldValue = (fieldValue == "1") ? "YES" : "NO"
180
            end
181
          end
182

  
183
         return fieldValue
184
  end
185
end
app/controllers/issues_controller.rb (working copy)
77 77
          render :template => 'issues/index.rhtml', :layout => !request.xhr?
78 78
        }
79 79
        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') }
80
      #Changed 7/7/09 - call smarter csv and pdf functions
81
        format.csv  { send_data(issues_to_csv(@issues, @query, @project).read, :type => 'text/csv; header=present', :filename => 'export.csv') }
82
#       format.csv  { send_data(issues_to_csv(@issues, @project).read,         :type => 'text/csv; header=present', :filename => 'export.csv') }
83
        format.pdf  { send_data(issues_to_pdf(@issues, @query, @project),      :type => 'application/pdf',          :filename => 'export.pdf') }
84
#       format.pdf  { send_data(issues_to_pdf(@issues, @project, @query),              :type => 'application/pdf',          :filename => 'export.pdf') }
85

  
82 86
      end
83 87
    else
84 88
      # 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 %>
29
	</tr>
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 -%>
41
        </tr>
30 42
	<% end -%>
31 43
	</tbody>
32 44
</table>
lib/redmine/export/pdf.rb (working copy)
22 22
module Redmine
23 23
  module Export
24 24
    module PDF
25
      #Added 7/2/09
26
#*****************************************************************************
27
	  MAXCOLSIZE = 60  #Maximum column size before wrappping (string length)
28
	  MAXPDFWIDTH = 250 #Approximate width of pdf document
29
#*****************************************************************************
25 30
      include ActionView::Helpers::TextHelper
26 31
      include ActionView::Helpers::NumberHelper
27 32
      
......
96 101
          super w,h,txt,border,ln,align,fill,link
97 102
        end
98 103
        
99
        def Footer
104
        #Method Changed 7/2/09
105
         def Footer
106
		  tempSize = getFontSize()
107
		  tempFamily = getFontFamily()
108
		  tempStyle = getFontStyle()
100 109
          SetFont(@font_for_footer, 'I', 8)
101 110
          SetY(-15)
102 111
          SetX(15)
......
104 113
          SetY(-15)
105 114
          SetX(-30)
106 115
          Cell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
116
          SetFont(tempFamily, tempStyle, tempSize)
107 117
        end
108 118
      end
109
      
110
      # Returns a PDF string of a list of issues
111
      def issues_to_pdf(issues, project, query)
112
        pdf = IFPDF.new(current_language)
113
        title = project ? "#{project} - #{l(:label_issue_plural)}" : "#{l(:label_issue_plural)}"
119
            #Method Added 7/2/09
120
	  def printColumnHeadersPDF(query, issues, colWidth, row_height, fontSize, pdf, maxCustomContent)
121
	         # headers
122
             pdf.SetFontStyle('B',fontSize)
123
             pdf.SetFillColor(230, 230, 230)
124

  
125
	         #Print the column header
126
			  pdf.Cell(20, row_height, "#", 0, 0, 'L', 1)
127

  
128
		       0.upto(@query.column_names.length - 1) do |cntr|
129
 				 colName = query.column_names[cntr].to_s()
130
				 re = /^cf.*/ #identifier for the column names of custom fields
131

  
132
				  # custom field
133
			      if (re.match(colName))
134

  
135
				   custom_fields = @project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields
136
				   id = getIDFromCustomColName(colName)
137
				   custom_fields.each do |custom_field| #find the name
138
						if id == custom_field.id.to_s()
139
							  colName = custom_field.name.to_s()
140
							  break
141
						end
142
				     end
143
				  end
144

  
145
				   colName = colName.gsub(/[_]/, ' ').to_s() # convert underscores to spaces
146
				   colName = capitalizeEachWord(colName)
147
				   pdf.Cell(colWidth[cntr], row_height, colName, 0, 0, 'L', 1)
148
		        end
149

  
150
	  end
151

  
152
	  #Method Added 7/2/09
153
	  def printFieldsPDF(query, issues, colWidth, row_height, fontSize, pdf, rowHeightMultiCell, maxMultiColWidth)
154

  
155
	           pdf.Line(10, pdf.GetY, 287, pdf.GetY)
156
               pdf.Ln
157
               pdf.Line(10, pdf.GetY, 287, pdf.GetY)
158
               pdf.SetY(pdf.GetY() + 1)
159
               pdf.SetFontStyle('',fontSize)
160
               pdf.SetFillColor(255, 255, 255)
161

  
162
		       #Print the rows
163
               issues.each do |issue|
164
                  flag = 0 #How many lines occur during a text wrapping
165
                  flagPageBreak = 0	#Check if a page break has occurred
166
                  pageBreakPos = 0  #Position after the page break used for restoring
167

  
168
			      pdf.Cell(20, row_height, issue.id.to_s, 0, 0, 'L', 1)
169

  
170
		          0.upto(query.column_names.length - 1) do |cntr|
171

  
172
				     if(getValue(issue,cntr).length < MAXCOLSIZE)  #No text wrapping
173
					 	pdf.SetFontStyle('',fontSize)
174
		                pdf.Cell(colWidth[cntr], row_height, getValue(issue,cntr))
175
				     else #For text wrapping
176
					    beforeX = pdf.GetX() #Before X position
177
						beforeY = pdf.GetY() #Before Y position
178
                        pad = 1 #Extra Height of muli-cells
179

  
180
						pdf.SetY(pdf.GetY() + pad)
181
                        pdf.SetX(beforeX)
182
					    pdf.MultiCell(colWidth[cntr], rowHeightMultiCell, getValue(issue,cntr))
183

  
184
						afterX = pdf.GetX()
185
						pdf.SetY(pdf.GetY() + pad)
186
						pdf.SetX(afterX)
187

  
188
						currentPage = pdf.getPage()
189
						afterY = pdf.GetY() #After Y position
190
						dif = afterY - beforeY #Difference after text wrapping
191
						if(afterY < beforeY)
192
						     pdf.setPage(pdf.getPage - 1)
193
							 flagPageBreak = 1
194
							 if(pageBreakPos < afterY)
195
							     pageBreakPos = afterY
196
							 end
197
						end
198

  
199
						#Count the number of lines skipped
200
					    if(dif/rowHeightMultiCell > flag)
201
                          flag = 0
202
						  while (dif > 0)
203
                            dif = dif - rowHeightMultiCell
204
							flag = flag + 1
205
                          end
206
                        end
207

  
208
					    pdf.SetY(beforeY)
209
					    pdf.SetX(beforeX + maxMultiColWidth)
210

  
211
					 end
212
				  end
213
				  	if(flagPageBreak == 1) 	#Restore Page after break
214
					    pdf.setPage(pdf.getPage() + 1)
215
					    pdf.SetY(pageBreakPos - row_height)
216
					    flagPageBreak = 0
217
				    else
218
                        pdf.Line(10, pdf.GetY, 287, pdf.GetY)
219
					end
220
                  #Restore after wrapping
221
				  if(flag > 0)
222
                        pdf.SetY(pdf.GetY() + rowHeightMultiCell * flag)
223
			      else
224
					  	pdf.SetY(pdf.GetY() + 10)
225
				  end
226
               end
227
	  end
228

  
229
#Method Added 7/7/09
230
	  def printUserQuery(pdf, row_height)
231

  
232
	         rowHeightMultiCell = 4 #Default height for multi-cell
233
		     fontSize = 10  #Font size before shinking
234
		     maxMultiColWidth = 120 #Maximium column width before wrapping
235
			 maxCustomContent = 0 #Issue that contains the most custom fields
236

  
237
			 arrMaxColLen = Array.new(@query.column_names.length) #Column Width in terms of characters based on the longest string
238
			 colWidth = Array.new(@query.column_names.length)  #Contains the width of each column in pdf units
239

  
240

  
241
			 #Create an array containing the lengths of the largest fields in each column
242
		     0.upto(@query.column_names.length - 1) do |cntr|
243
		         currentMax = 0;
244
		         @issues.each do |issue|
245
		             valueLength = getValue(issue,cntr).length()
246
			         if(valueLength > currentMax)
247
      			        currentMax = valueLength
248
			         end
249
			         arrMaxColLen[cntr] = currentMax
250
		 	     end
251
	         end
252

  
253
             #See if any of the headers are longer than the largest value
254
		     0.upto(@query.column_names.length - 1) do |cntr|
255
			     len = 0 #Length of the column name header
256
				 re = /^cf.*/ #Identify column fields by their column headers
257

  
258
				 #Finds the length of the column name header
259
			     if (!re.match(@query.column_names[cntr].to_s()))
260
	   	            len = @query.column_names[cntr].to_s().length()
261
			     else #Custom Field
262
				    max = 0
263
				      0.upto(@issues.length - 1) do |issue|
264
                        if(@issues[issue].custom_values.length() > max)
265
                           max = @issues[issue].custom_values.length()
266
						   maxCustomContent = issue #Issue that contains the most custom fields
267
                        end
268
					  end
269
					  columnNameId = getIDFromCustomColName(@query.column_names[cntr].to_s())
270
					  0.upto(@issues[maxCustomContent].custom_values.length - 1) do |custom_value|
271
						customField = @issues[maxCustomContent].custom_values[custom_value].custom_field
272

  
273
				       if (customField.id.to_s() == columnNameId)
274
                          len = customField.name.length() #Length of the header of the custom fields
275
                       end
276
                      end
277
  			     end
278

  
279
                  #Check if column name headers are larger than the fields
280
				 if(arrMaxColLen[cntr] < len)
281
			        arrMaxColLen[cntr] = len
282
			     end
283
	           end
284

  
285
			   #Set each column width given the value lengths
286
			   0.upto(@query.column_names.length - 1) do |cntr|
287
			     ourColumnWidth = arrMaxColLen[cntr] * 1.5 + 10
288

  
289
			     colWidth[cntr] = (ourColumnWidth <  maxMultiColWidth) ? ourColumnWidth : maxMultiColWidth
290
			   end
291

  
292
			   #Total column length of the whole page
293
			   total = 0
294
			   0.upto(colWidth.length - 1) do |cntr|
295
			      total = total + colWidth[cntr]
296
			   end
297

  
298
			   #Scales the font sizes based on column data width
299
			   percent = 1.0
300
			   if(total > MAXPDFWIDTH)
301
			      percent = MAXPDFWIDTH.to_f()/total.to_f()
302
				  maxMultiColWidth = percent * maxMultiColWidth
303
				  0.upto(@query.column_names.length - 1) do |cntr|
304
                      colWidth[cntr] = colWidth[cntr] * percent
305
                 end
306
				  fontSize = fontSize * 0.75 * percent
307
		      pdf.SetFontStyle('B',fontSize)
308
		   end
309

  
310
			   printColumnHeadersPDF(@query, @issues, colWidth, row_height, fontSize, pdf, maxCustomContent)
311
			   printFieldsPDF       (@query, @issues, colWidth, row_height, fontSize, pdf, rowHeightMultiCell, maxMultiColWidth)
312
	  end
313

  
314
     
315
     # Returns a PDF string of a list of issues
316
     def issues_to_pdf(issues, query, project)
317
       pdf = IFPDF.new(current_language)
318
       if(query.name != "_") 
319
		   title = project ? "#{project} - #{@query.name.to_s()}"  : " #{@query.name.to_s()}"
320
       else 
321
		   title = project ? "#{project} - #{l(:label_issue_plural)}"  : "#{l(:label_issue_plural)}"
322
      end
114 323
        pdf.SetTitle(title)
115 324
        pdf.AliasNbPages
116 325
        pdf.footer_date = format_date(Date.today)
......
122 331
        pdf.Cell(190,10, title)
123 332
        pdf.Ln
124 333
        
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)
334
       # headers
335
		#If there are column names
336
		if(@query.column_names != nil)
337
             printUserQuery(pdf, row_height)
338
		else #If there are no columns
339
          pdf.SetFontStyle('B',10)
340
          pdf.SetFillColor(230, 230, 230)
341
          pdf.Cell(15, row_height, "#", 0, 0, 'L', 1)
342
          pdf.Cell(30, row_height, l(:field_tracker), 0, 0, 'L', 1)
343
          pdf.Cell(30, row_height, l(:field_status), 0, 0, 'L', 1)
344
          pdf.Cell(30, row_height, l(:field_priority), 0, 0, 'L', 1)
345
          pdf.Cell(40, row_height, l(:field_assigned_to), 0, 0, 'L', 1)
346
          pdf.Cell(25, row_height, l(:field_updated_on), 0, 0, 'L', 1)
347
          pdf.Cell(0, row_height,  l(:field_subject), 0, 0, 'L', 1)
348
          pdf.Line(10, pdf.GetY, 287, pdf.GetY)
349
          pdf.Ln
350
          pdf.Line(10, pdf.GetY, 287, pdf.GetY)
351
          pdf.SetY(pdf.GetY() + 1)
139 352
        
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')
149
            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
            pdf.SetY(pdf.GetY() + 1)
153
            pdf.SetFontStyle('',9)
353
                 # rows
354
          pdf.SetFontStyle('',9)
355
          pdf.SetFillColor(255, 255, 255)
356
          group = false
357
          issues.each do |issue|
358
            if query.grouped? && issue.send(query.group_by) != group
359
              group = issue.send(query.group_by)
360
              pdf.SetFontStyle('B',10)
361
              pdf.Cell(0, row_height, "#{group.blank? ? 'None' : group.to_s}", 0, 1, 'L')
362
              pdf.Line(10, pdf.GetY, 287, pdf.GetY)
363
              pdf.SetY(pdf.GetY() + 0.5)
364
              pdf.Line(10, pdf.GetY, 287, pdf.GetY)
365
              pdf.SetY(pdf.GetY() + 1)
366
              pdf.SetFontStyle('',9)
367
            end
368
            pdf.Cell(15, row_height, issue.id.to_s, 0, 0, 'L', 1)
369
            pdf.Cell(30, row_height, issue.tracker.name, 0, 0, 'L', 1)
370
            pdf.Cell(30, row_height, issue.status.name, 0, 0, 'L', 1)
371
            pdf.Cell(30, row_height, issue.priority.name, 0, 0, 'L', 1)
372
            pdf.Cell(40, row_height, issue.assigned_to ? issue.assigned_to.to_s : '', 0, 0, 'L', 1)
373
            pdf.Cell(25, row_height, format_date(issue.updated_on), 0, 0, 'L', 1)
374
            pdf.MultiCell(0, row_height, (project == issue.project ? issue.subject : "#{issue.project} - #{issue.subject}"))
375
           pdf.Line(10, pdf.GetY, 287, pdf.GetY)
376
           pdf.SetY(pdf.GetY() + 1)
154 377
          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
378
    end
165 379
        pdf.Output
166
      end
380
     end
167 381
      
168 382
      # Returns a PDF string of a single issue
169 383
      def issue_to_pdf(issue)
vendor/plugins/rfpdf/lib/rfpdf/fpdf.rb (working copy)
279 279
    end
280 280

  
281 281
    def AddPage(orientation='')
282
      #Added 7/2/09
283
#************************************
284
	    #If next page already exists
285
	    if(getPage() < @pages.length() - 1)
286
		     setPage(getPage() + 1)
287
             @y = 0 + @tMargin
288
#************************************
289
      else
282 290
        # Start a new page
283 291
        self.Open if @state==0
284 292
        family=@FontFamily
......
313 321
        out(fc) if fc!='0 g'
314 322
        @TextColor=tc
315 323
        @ColorFlag=cf
324
      end
316 325
        # Page header
317 326
        self.Header
318 327
        # Restore line width
......
870 879
            @y=@y+@lasth
871 880
        else
872 881
            @y=@y+h
873
        end
882
       end
883
   end
884
   
885
    #Added 7/2/09 for pdf.rb
886
#***********************
887
    def  getFontFamily
888
	    @FontFamily
889
	end
890

  
891
	def  getFontStyle
892
	    @FontStyle
893
	end
894

  
895
	def getFontSize
896
    	@FontSizePt
874 897
    end
898
    def getPage()
899
	    @page
900
	end
875 901

  
902
	def setPage(page)
903
	    @page = page
904
	end
905
#**********************
876 906
    def GetX
877 907
        # Get x position
878 908
        @x
public/stylesheets/application.css (working copy)
93 93
 
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

  
97
tr.issue { text-align: center; white-space: nowrap; }
98
tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
99
tr.issue td.subject { text-align: left; }
96
 /*** Changed 7/7/09 - nowrap was a bad idea! ***/
97
tr.issue { text-align: center; white-space: normal; }
98
/*tr.issue { text-align: center; white-space: nowrap; }
99
tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }*/
100
tr.issue td.subject, tr.issue td.text, tr.issue td.string { text-align: left; }
101
/*tr.issue td.subject { text-align: left; }*/
100 102
tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
101 103

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