Patch #3614 » export_issues_to_pdf_csv_r2823.diff
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; } |