@@ -5,12 +5,12 | |||
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 |
# |
|
|
8 | # | |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 |
# |
|
|
13 | # | |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
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. |
@@ -22,10 +22,10 require 'pp' | |||
|
22 | 22 | namespace :redmine do |
|
23 | 23 | desc 'Trac migration script' |
|
24 | 24 | task :migrate_from_trac => :environment do |
|
25 | ||
|
25 | ||
|
26 | 26 | module TracMigrate |
|
27 | 27 | TICKET_MAP = [] |
|
28 | ||
|
28 | ||
|
29 | 29 | DEFAULT_STATUS = IssueStatus.default |
|
30 | 30 | assigned_status = IssueStatus.find_by_position(2) |
|
31 | 31 | resolved_status = IssueStatus.find_by_position(3) |
@@ -36,7 +36,7 namespace :redmine do | |||
|
36 | 36 | 'assigned' => assigned_status, |
|
37 | 37 | 'closed' => closed_status |
|
38 | 38 | } |
|
39 | ||
|
39 | ||
|
40 | 40 | priorities = Enumeration.get_values('IPRI') |
|
41 | 41 | DEFAULT_PRIORITY = priorities[0] |
|
42 | 42 | PRIORITY_MAPPING = {'lowest' => priorities[0], |
@@ -51,7 +51,7 namespace :redmine do | |||
|
51 | 51 | 'critical' => priorities[3], |
|
52 | 52 | 'blocker' => priorities[4] |
|
53 | 53 | } |
|
54 | ||
|
54 | ||
|
55 | 55 | TRACKER_BUG = Tracker.find_by_position(1) |
|
56 | 56 | TRACKER_FEATURE = Tracker.find_by_position(2) |
|
57 | 57 | DEFAULT_TRACKER = TRACKER_BUG |
@@ -60,7 +60,7 namespace :redmine do | |||
|
60 | 60 | 'task' => TRACKER_FEATURE, |
|
61 | 61 | 'patch' =>TRACKER_FEATURE |
|
62 | 62 | } |
|
63 | ||
|
63 | ||
|
64 | 64 | roles = Role.find(:all, :conditions => {:builtin => 0}, :order => 'position ASC') |
|
65 | 65 | manager_role = roles[0] |
|
66 | 66 | developer_role = roles[1] |
@@ -68,7 +68,7 namespace :redmine do | |||
|
68 | 68 | ROLE_MAPPING = {'admin' => manager_role, |
|
69 | 69 | 'developer' => developer_role |
|
70 | 70 | } |
|
71 | ||
|
71 | ||
|
72 | 72 | class ::Time |
|
73 | 73 | class << self |
|
74 | 74 | alias :real_now :now |
@@ -87,10 +87,10 namespace :redmine do | |||
|
87 | 87 | class TracComponent < ActiveRecord::Base |
|
88 | 88 | set_table_name :component |
|
89 | 89 | end |
|
90 | ||
|
90 | ||
|
91 | 91 | class TracMilestone < ActiveRecord::Base |
|
92 | 92 | set_table_name :milestone |
|
93 |
# If this attribute is set a milestone has a defined target timepoint |
|
|
93 | # If this attribute is set a milestone has a defined target timepoint | |
|
94 | 94 | def due |
|
95 | 95 | if read_attribute(:due) && read_attribute(:due) > 0 |
|
96 | 96 | Time.at(read_attribute(:due)).to_date |
@@ -112,37 +112,37 namespace :redmine do | |||
|
112 | 112 | has_attribute?(:descr) ? read_attribute(:descr) : read_attribute(:description) |
|
113 | 113 | end |
|
114 | 114 | end |
|
115 | ||
|
115 | ||
|
116 | 116 | class TracTicketCustom < ActiveRecord::Base |
|
117 | 117 | set_table_name :ticket_custom |
|
118 | 118 | end |
|
119 | ||
|
119 | ||
|
120 | 120 | class TracAttachment < ActiveRecord::Base |
|
121 | 121 | set_table_name :attachment |
|
122 | 122 | set_inheritance_column :none |
|
123 | ||
|
123 | ||
|
124 | 124 | def time; Time.at(read_attribute(:time)) end |
|
125 | ||
|
125 | ||
|
126 | 126 | def original_filename |
|
127 | 127 | filename |
|
128 | 128 | end |
|
129 | ||
|
129 | ||
|
130 | 130 | def content_type |
|
131 | 131 | Redmine::MimeType.of(filename) || '' |
|
132 | 132 | end |
|
133 | ||
|
133 | ||
|
134 | 134 | def exist? |
|
135 | 135 | File.file? trac_fullpath |
|
136 | 136 | end |
|
137 | ||
|
137 | ||
|
138 | 138 | def read |
|
139 | 139 | File.open("#{trac_fullpath}", 'rb').read |
|
140 | 140 | end |
|
141 | ||
|
141 | ||
|
142 | 142 | def description |
|
143 | 143 | read_attribute(:description).to_s.slice(0,255) |
|
144 | 144 | end |
|
145 | ||
|
145 | ||
|
146 | 146 | private |
|
147 | 147 | def trac_fullpath |
|
148 | 148 | attachment_type = read_attribute(:type) |
@@ -150,11 +150,11 namespace :redmine do | |||
|
150 | 150 | "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}" |
|
151 | 151 | end |
|
152 | 152 | end |
|
153 | ||
|
153 | ||
|
154 | 154 | class TracTicket < ActiveRecord::Base |
|
155 | 155 | set_table_name :ticket |
|
156 | 156 | set_inheritance_column :none |
|
157 | ||
|
157 | ||
|
158 | 158 | # ticket changes: only migrate status changes and comments |
|
159 | 159 | has_many :changes, :class_name => "TracTicketChange", :foreign_key => :ticket |
|
160 | 160 | has_many :attachments, :class_name => "TracAttachment", |
@@ -162,29 +162,29 namespace :redmine do | |||
|
162 | 162 | " WHERE #{TracMigrate::TracAttachment.table_name}.type = 'ticket'" + |
|
163 | 163 | ' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{id}\'' |
|
164 | 164 | has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket |
|
165 | ||
|
165 | ||
|
166 | 166 | def ticket_type |
|
167 | 167 | read_attribute(:type) |
|
168 | 168 | end |
|
169 | ||
|
169 | ||
|
170 | 170 | def summary |
|
171 | 171 | read_attribute(:summary).blank? ? "(no subject)" : read_attribute(:summary) |
|
172 | 172 | end |
|
173 | ||
|
173 | ||
|
174 | 174 | def description |
|
175 | 175 | read_attribute(:description).blank? ? summary : read_attribute(:description) |
|
176 | 176 | end |
|
177 | ||
|
177 | ||
|
178 | 178 | def time; Time.at(read_attribute(:time)) end |
|
179 | 179 | def changetime; Time.at(read_attribute(:changetime)) end |
|
180 | 180 | end |
|
181 | ||
|
181 | ||
|
182 | 182 | class TracTicketChange < ActiveRecord::Base |
|
183 | 183 | set_table_name :ticket_change |
|
184 | ||
|
184 | ||
|
185 | 185 | def time; Time.at(read_attribute(:time)) end |
|
186 | 186 | end |
|
187 | ||
|
187 | ||
|
188 | 188 | TRAC_WIKI_PAGES = %w(InterMapTxt InterTrac InterWiki RecentChanges SandBox TracAccessibility TracAdmin TracBackup TracBrowser TracCgi TracChangeset \ |
|
189 | 189 | TracEnvironment TracFastCgi TracGuide TracImport TracIni TracInstall TracInterfaceCustomization \ |
|
190 | 190 | TracLinks TracLogging TracModPython TracNotification TracPermissions TracPlugins TracQuery \ |
@@ -192,35 +192,35 namespace :redmine do | |||
|
192 | 192 | TracTicketsCustomFields TracTimeline TracUnicode TracUpgrade TracWiki WikiDeletePage WikiFormatting \ |
|
193 | 193 | WikiHtml WikiMacros WikiNewPage WikiPageNames WikiProcessors WikiRestructuredText WikiRestructuredTextLinks \ |
|
194 | 194 | CamelCase TitleIndex) |
|
195 | ||
|
195 | ||
|
196 | 196 | class TracWikiPage < ActiveRecord::Base |
|
197 | 197 | set_table_name :wiki |
|
198 | 198 | set_primary_key :name |
|
199 | ||
|
199 | ||
|
200 | 200 | has_many :attachments, :class_name => "TracAttachment", |
|
201 | 201 | :finder_sql => "SELECT DISTINCT attachment.* FROM #{TracMigrate::TracAttachment.table_name}" + |
|
202 | 202 | " WHERE #{TracMigrate::TracAttachment.table_name}.type = 'wiki'" + |
|
203 | 203 | ' AND #{TracMigrate::TracAttachment.table_name}.id = \'#{id}\'' |
|
204 | ||
|
204 | ||
|
205 | 205 | def self.columns |
|
206 | 206 | # Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0) |
|
207 | 207 | super.select {|column| column.name.to_s != 'readonly'} |
|
208 | 208 | end |
|
209 | ||
|
209 | ||
|
210 | 210 | def time; Time.at(read_attribute(:time)) end |
|
211 | 211 | end |
|
212 | ||
|
212 | ||
|
213 | 213 | class TracPermission < ActiveRecord::Base |
|
214 |
set_table_name :permission |
|
|
214 | set_table_name :permission | |
|
215 | 215 | end |
|
216 | ||
|
216 | ||
|
217 | 217 | class TracSessionAttribute < ActiveRecord::Base |
|
218 | 218 | set_table_name :session_attribute |
|
219 | 219 | end |
|
220 | ||
|
220 | ||
|
221 | 221 | def self.find_or_create_user(username, project_member = false) |
|
222 | 222 | return User.anonymous if username.blank? |
|
223 | ||
|
223 | ||
|
224 | 224 | u = User.find_by_login(username) |
|
225 | 225 | if !u |
|
226 | 226 | # Create a new user if not found |
@@ -229,7 +229,7 namespace :redmine do | |||
|
229 | 229 | mail = mail_attr.value |
|
230 | 230 | end |
|
231 | 231 | mail = "#{mail}@foo.bar" unless mail.include?("@") |
|
232 | ||
|
232 | ||
|
233 | 233 | name = username |
|
234 | 234 | if name_attr = TracSessionAttribute.find_by_sid_and_name(username, 'name') |
|
235 | 235 | name = name_attr.value |
@@ -237,7 +237,7 namespace :redmine do | |||
|
237 | 237 | name =~ (/(.*)(\s+\w+)?/) |
|
238 | 238 | fn = $1.strip |
|
239 | 239 | ln = ($2 || '-').strip |
|
240 | ||
|
240 | ||
|
241 | 241 | u = User.new :mail => mail.gsub(/[^-@a-z0-9\.]/i, '-'), |
|
242 | 242 | :firstname => fn[0, limit_for(User, 'firstname')].gsub(/[^\w\s\'\-]/i, '-'), |
|
243 | 243 | :lastname => ln[0, limit_for(User, 'lastname')].gsub(/[^\w\s\'\-]/i, '-') |
@@ -261,7 +261,7 namespace :redmine do | |||
|
261 | 261 | end |
|
262 | 262 | u |
|
263 | 263 | end |
|
264 | ||
|
264 | ||
|
265 | 265 | # Basic wiki syntax conversion |
|
266 | 266 | def self.convert_wiki_text(text) |
|
267 | 267 | # Titles |
@@ -282,7 +282,7 namespace :redmine do | |||
|
282 | 282 | # [milestone:"0.1.0 Mercury"] |
|
283 | 283 | text = text.gsub(/\[milestone\:\"([^\"]+)\"\]/, 'version:"\1"') |
|
284 | 284 | text = text.gsub(/milestone\:\"([^\"]+)\"/, 'version:"\1"') |
|
285 |
# milestone:0.1.0 |
|
|
285 | # milestone:0.1.0 | |
|
286 | 286 | text = text.gsub(/\[milestone\:([^\ ]+)\]/, 'version:\1') |
|
287 | 287 | text = text.gsub(/milestone\:([^\ ]+)/, 'version:\1') |
|
288 | 288 | # Internal Links |
@@ -293,11 +293,11 namespace :redmine do | |||
|
293 | 293 | text = text.gsub(/\[wiki:([^\s\]]+)\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"} |
|
294 | 294 | text = text.gsub(/\[wiki:([^\s\]]+)\s(.*)\]/) {|s| "[[#{$1.delete(',./?;|:')}|#{$2.delete(',./?;|:')}]]"} |
|
295 | 295 | |
|
296 |
|
|
|
297 |
|
|
|
298 |
|
|
|
299 |
|
|
|
300 |
|
|
|
296 | # Links to pages UsingJustWikiCaps | |
|
297 | text = text.gsub(/([^!]|^)(^| )([A-Z][a-z]+[A-Z][a-zA-Z]+)/, '\\1\\2[[\3]]') | |
|
298 | # Normalize things that were supposed to not be links | |
|
299 | # like !NotALink | |
|
300 | text = text.gsub(/(^| )!([A-Z][A-Za-z]+)/, '\1\2') | |
|
301 | 301 | # Revisions links |
|
302 | 302 | text = text.gsub(/\[(\d+)\]/, 'r\1') |
|
303 | 303 | # Ticket number re-writing |
@@ -318,7 +318,7 namespace :redmine do | |||
|
318 | 318 | shebang_re = /^\#\!([a-z]+)/ |
|
319 | 319 | # Regular expression for end of code |
|
320 | 320 | pre_end_re = /\}\}\}/ |
|
321 | ||
|
321 | ||
|
322 | 322 | # Go through the whole text..extract it line by line |
|
323 | 323 | text = text.gsub(/^(.*)$/) do |line| |
|
324 | 324 | m_pre = pre_re.match(line) |
@@ -338,7 +338,7 namespace :redmine do | |||
|
338 | 338 | end |
|
339 | 339 | end |
|
340 | 340 | end |
|
341 |
line |
|
|
341 | line | |
|
342 | 342 | end |
|
343 | 343 | |
|
344 | 344 | # Highlighting |
@@ -349,25 +349,25 namespace :redmine do | |||
|
349 | 349 | text = text.gsub(/__/, '+') |
|
350 | 350 | text = text.gsub(/~~/, '-') |
|
351 | 351 | text = text.gsub(/`/, '@') |
|
352 |
text = text.gsub(/,,/, '~') |
|
|
352 | text = text.gsub(/,,/, '~') | |
|
353 | 353 | # Lists |
|
354 | 354 | text = text.gsub(/^([ ]+)\* /) {|s| '*' * $1.length + " "} |
|
355 | 355 | |
|
356 | 356 | text |
|
357 | 357 | end |
|
358 | ||
|
358 | ||
|
359 | 359 | def self.migrate |
|
360 | 360 | establish_connection |
|
361 | 361 | |
|
362 | 362 | # Quick database test |
|
363 | 363 | TracComponent.count |
|
364 | ||
|
364 | ||
|
365 | 365 | migrated_components = 0 |
|
366 | 366 | migrated_milestones = 0 |
|
367 | 367 | migrated_tickets = 0 |
|
368 | 368 | migrated_custom_values = 0 |
|
369 | 369 | migrated_ticket_attachments = 0 |
|
370 |
migrated_wiki_edits = 0 |
|
|
370 | migrated_wiki_edits = 0 | |
|
371 | 371 | migrated_wiki_attachments = 0 |
|
372 | 372 | |
|
373 | 373 | #Wiki system initializing... |
@@ -375,21 +375,21 namespace :redmine do | |||
|
375 | 375 | @target_project.reload |
|
376 | 376 | wiki = Wiki.new(:project => @target_project, :start_page => 'WikiStart') |
|
377 | 377 | wiki_edit_count = 0 |
|
378 | ||
|
378 | ||
|
379 | 379 | # Components |
|
380 | 380 | print "Migrating components" |
|
381 | 381 | issues_category_map = {} |
|
382 | 382 | TracComponent.find(:all).each do |component| |
|
383 |
|
|
|
384 |
|
|
|
383 | print '.' | |
|
384 | STDOUT.flush | |
|
385 | 385 | c = IssueCategory.new :project => @target_project, |
|
386 | 386 | :name => encode(component.name[0, limit_for(IssueCategory, 'name')]) |
|
387 |
|
|
|
388 |
|
|
|
389 |
|
|
|
387 | next unless c.save | |
|
388 | issues_category_map[component.name] = c | |
|
389 | migrated_components += 1 | |
|
390 | 390 | end |
|
391 | 391 | puts |
|
392 | ||
|
392 | ||
|
393 | 393 | # Milestones |
|
394 | 394 | print "Migrating milestones" |
|
395 | 395 | version_map = {} |
@@ -415,7 +415,7 namespace :redmine do | |||
|
415 | 415 | migrated_milestones += 1 |
|
416 | 416 | end |
|
417 | 417 | puts |
|
418 | ||
|
418 | ||
|
419 | 419 | # Custom fields |
|
420 | 420 | # TODO: read trac.ini instead |
|
421 | 421 | print "Migrating custom fields" |
@@ -430,14 +430,14 namespace :redmine do | |||
|
430 | 430 | # Or create a new one |
|
431 | 431 | f ||= IssueCustomField.create(:name => encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize, |
|
432 | 432 | :field_format => 'string') |
|
433 | ||
|
433 | ||
|
434 | 434 | next if f.new_record? |
|
435 | 435 | f.trackers = Tracker.find(:all) |
|
436 | 436 | f.projects << @target_project |
|
437 | 437 | custom_field_map[field.name] = f |
|
438 | 438 | end |
|
439 | 439 | puts |
|
440 | ||
|
440 | ||
|
441 | 441 | # Trac 'resolution' field as a Redmine custom field |
|
442 | 442 | r = IssueCustomField.find(:first, :conditions => { :name => "Resolution" }) |
|
443 | 443 | r = IssueCustomField.new(:name => 'Resolution', |
@@ -448,45 +448,44 namespace :redmine do | |||
|
448 | 448 | r.possible_values = (r.possible_values + %w(fixed invalid wontfix duplicate worksforme)).flatten.compact.uniq |
|
449 | 449 | r.save! |
|
450 | 450 | custom_field_map['resolution'] = r |
|
451 | ||
|
451 | ||
|
452 | 452 | # Tickets |
|
453 | 453 | print "Migrating tickets" |
|
454 | 454 | TracTicket.find(:all, :order => 'id ASC').each do |ticket| |
|
455 |
|
|
|
456 |
|
|
|
457 |
|
|
|
455 | print '.' | |
|
456 | STDOUT.flush | |
|
457 | i = Issue.new :project => @target_project, | |
|
458 | 458 | :subject => encode(ticket.summary[0, limit_for(Issue, 'subject')]), |
|
459 | 459 | :description => convert_wiki_text(encode(ticket.description)), |
|
460 | 460 | :priority => PRIORITY_MAPPING[ticket.priority] || DEFAULT_PRIORITY, |
|
461 | 461 | :created_on => ticket.time |
|
462 |
|
|
|
463 |
|
|
|
464 |
|
|
|
465 |
|
|
|
466 |
|
|
|
467 | i.custom_values << CustomValue.new(:custom_field => custom_field_map['resolution'], :value => ticket.resolution) unless ticket.resolution.blank? | |
|
468 | i.id = ticket.id unless Issue.exists?(ticket.id) | |
|
469 | next unless Time.fake(ticket.changetime) { i.save } | |
|
470 | TICKET_MAP[ticket.id] = i.id | |
|
471 | migrated_tickets += 1 | |
|
472 |
|
|
|
473 | # Owner | |
|
462 | i.author = find_or_create_user(ticket.reporter) | |
|
463 | i.category = issues_category_map[ticket.component] unless ticket.component.blank? | |
|
464 | i.fixed_version = version_map[ticket.milestone] unless ticket.milestone.blank? | |
|
465 | i.status = STATUS_MAPPING[ticket.status] || DEFAULT_STATUS | |
|
466 | i.tracker = TRACKER_MAPPING[ticket.ticket_type] || DEFAULT_TRACKER | |
|
467 | i.id = ticket.id unless Issue.exists?(ticket.id) | |
|
468 | next unless Time.fake(ticket.changetime) { i.save } | |
|
469 | TICKET_MAP[ticket.id] = i.id | |
|
470 | migrated_tickets += 1 | |
|
471 | ||
|
472 | # Owner | |
|
474 | 473 | unless ticket.owner.blank? |
|
475 | 474 | i.assigned_to = find_or_create_user(ticket.owner, true) |
|
476 | 475 | Time.fake(ticket.changetime) { i.save } |
|
477 | 476 | end |
|
478 | ||
|
479 |
|
|
|
480 |
|
|
|
477 | ||
|
478 | # Comments and status/resolution changes | |
|
479 | ticket.changes.group_by(&:time).each do |time, changeset| | |
|
481 | 480 | status_change = changeset.select {|change| change.field == 'status'}.first |
|
482 | 481 | resolution_change = changeset.select {|change| change.field == 'resolution'}.first |
|
483 | 482 | comment_change = changeset.select {|change| change.field == 'comment'}.first |
|
484 | ||
|
483 | ||
|
485 | 484 | n = Journal.new :notes => (comment_change ? convert_wiki_text(encode(comment_change.newvalue)) : ''), |
|
486 | 485 | :created_on => time |
|
487 | 486 | n.user = find_or_create_user(changeset.first.author) |
|
488 | 487 | n.journalized = i |
|
489 |
if status_change && |
|
|
488 | if status_change && | |
|
490 | 489 | STATUS_MAPPING[status_change.oldvalue] && |
|
491 | 490 | STATUS_MAPPING[status_change.newvalue] && |
|
492 | 491 | (STATUS_MAPPING[status_change.oldvalue] != STATUS_MAPPING[status_change.newvalue]) |
@@ -502,35 +501,39 namespace :redmine do | |||
|
502 | 501 | :value => resolution_change.newvalue) |
|
503 | 502 | end |
|
504 | 503 | n.save unless n.details.empty? && n.notes.blank? |
|
505 |
|
|
|
506 | ||
|
507 |
|
|
|
508 |
|
|
|
509 |
|
|
|
504 | end | |
|
505 | ||
|
506 | # Attachments | |
|
507 | ticket.attachments.each do |attachment| | |
|
508 | next unless attachment.exist? | |
|
510 | 509 | a = Attachment.new :created_on => attachment.time |
|
511 | 510 | a.file = attachment |
|
512 | 511 | a.author = find_or_create_user(attachment.author) |
|
513 | 512 | a.container = i |
|
514 | 513 | a.description = attachment.description |
|
515 | 514 | migrated_ticket_attachments += 1 if a.save |
|
516 |
|
|
|
517 | ||
|
518 |
|
|
|
519 |
|
|
|
520 |
|
|
|
521 | v = CustomValue.new :custom_field => custom_field_map[custom.name], | |
|
522 | :value => custom.value | |
|
523 | v.customized = i | |
|
524 | next unless v.save | |
|
515 | end | |
|
516 | ||
|
517 | # Custom fields | |
|
518 | custom_values = ticket.customs.inject({}) do |h, custom| | |
|
519 | if custom_field = custom_field_map[custom.name] | |
|
520 | h[custom_field.id] = custom.value | |
|
525 | 521 | migrated_custom_values += 1 |
|
526 |
|
|
|
522 | end | |
|
523 | h | |
|
524 | end | |
|
525 | if custom_field_map['resolution'] && !ticket.resolution.blank? | |
|
526 | custom_values[custom_field_map['resolution'].id] = ticket.resolution | |
|
527 | end | |
|
528 | i.custom_field_values = custom_values | |
|
529 | i.save_custom_field_values | |
|
527 | 530 | end |
|
528 | ||
|
531 | ||
|
529 | 532 | # update issue id sequence if needed (postgresql) |
|
530 | 533 | Issue.connection.reset_pk_sequence!(Issue.table_name) if Issue.connection.respond_to?('reset_pk_sequence!') |
|
531 | 534 | puts |
|
532 | ||
|
533 |
# Wiki |
|
|
535 | ||
|
536 | # Wiki | |
|
534 | 537 | print "Migrating wiki" |
|
535 | 538 | if wiki.save |
|
536 | 539 | TracWikiPage.find(:all, :order => 'name, version').each do |page| |
@@ -545,10 +548,10 namespace :redmine do | |||
|
545 | 548 | p.content.author = find_or_create_user(page.author) unless page.author.blank? || page.author == 'trac' |
|
546 | 549 | p.content.comments = page.comment |
|
547 | 550 | Time.fake(page.time) { p.new_record? ? p.save : p.content.save } |
|
548 | ||
|
551 | ||
|
549 | 552 | next if p.content.new_record? |
|
550 |
migrated_wiki_edits += 1 |
|
|
551 | ||
|
553 | migrated_wiki_edits += 1 | |
|
554 | ||
|
552 | 555 | # Attachments |
|
553 | 556 | page.attachments.each do |attachment| |
|
554 | 557 | next unless attachment.exist? |
@@ -561,7 +564,7 namespace :redmine do | |||
|
561 | 564 | migrated_wiki_attachments += 1 if a.save |
|
562 | 565 | end |
|
563 | 566 | end |
|
564 | ||
|
567 | ||
|
565 | 568 | wiki.reload |
|
566 | 569 | wiki.pages.each do |page| |
|
567 | 570 | page.content.text = convert_wiki_text(page.content.text) |
@@ -569,7 +572,7 namespace :redmine do | |||
|
569 | 572 | end |
|
570 | 573 | end |
|
571 | 574 | puts |
|
572 | ||
|
575 | ||
|
573 | 576 | puts |
|
574 | 577 | puts "Components: #{migrated_components}/#{TracComponent.count}" |
|
575 | 578 | puts "Milestones: #{migrated_milestones}/#{TracMilestone.count}" |
@@ -579,18 +582,18 namespace :redmine do | |||
|
579 | 582 | puts "Wiki edits: #{migrated_wiki_edits}/#{wiki_edit_count}" |
|
580 | 583 | puts "Wiki files: #{migrated_wiki_attachments}/" + TracAttachment.count(:conditions => {:type => 'wiki'}).to_s |
|
581 | 584 | end |
|
582 | ||
|
585 | ||
|
583 | 586 | def self.limit_for(klass, attribute) |
|
584 | 587 | klass.columns_hash[attribute.to_s].limit |
|
585 | 588 | end |
|
586 | ||
|
589 | ||
|
587 | 590 | def self.encoding(charset) |
|
588 | 591 | @ic = Iconv.new('UTF-8', charset) |
|
589 | 592 | rescue Iconv::InvalidEncoding |
|
590 | 593 | puts "Invalid encoding!" |
|
591 | 594 | return false |
|
592 | 595 | end |
|
593 | ||
|
596 | ||
|
594 | 597 | def self.set_trac_directory(path) |
|
595 | 598 | @@trac_directory = path |
|
596 | 599 | raise "This directory doesn't exist!" unless File.directory?(path) |
@@ -615,7 +618,7 namespace :redmine do | |||
|
615 | 618 | puts e |
|
616 | 619 | return false |
|
617 | 620 | end |
|
618 | ||
|
621 | ||
|
619 | 622 | def self.set_trac_db_host(host) |
|
620 | 623 | return nil if host.blank? |
|
621 | 624 | @@trac_db_host = host |
@@ -625,7 +628,7 namespace :redmine do | |||
|
625 | 628 | return nil if port.to_i == 0 |
|
626 | 629 | @@trac_db_port = port.to_i |
|
627 | 630 | end |
|
628 | ||
|
631 | ||
|
629 | 632 | def self.set_trac_db_name(name) |
|
630 | 633 | return nil if name.blank? |
|
631 | 634 | @@trac_db_name = name |
@@ -634,22 +637,22 namespace :redmine do | |||
|
634 | 637 | def self.set_trac_db_username(username) |
|
635 | 638 | @@trac_db_username = username |
|
636 | 639 | end |
|
637 | ||
|
640 | ||
|
638 | 641 | def self.set_trac_db_password(password) |
|
639 | 642 | @@trac_db_password = password |
|
640 | 643 | end |
|
641 | ||
|
644 | ||
|
642 | 645 | def self.set_trac_db_schema(schema) |
|
643 | 646 | @@trac_db_schema = schema |
|
644 | 647 | end |
|
645 | 648 | |
|
646 | 649 | mattr_reader :trac_directory, :trac_adapter, :trac_db_host, :trac_db_port, :trac_db_name, :trac_db_schema, :trac_db_username, :trac_db_password |
|
647 | ||
|
650 | ||
|
648 | 651 | def self.trac_db_path; "#{trac_directory}/db/trac.db" end |
|
649 | 652 | def self.trac_attachments_directory; "#{trac_directory}/attachments" end |
|
650 | ||
|
653 | ||
|
651 | 654 | def self.target_project_identifier(identifier) |
|
652 |
project = Project.find_by_identifier(identifier) |
|
|
655 | project = Project.find_by_identifier(identifier) | |
|
653 | 656 | if !project |
|
654 | 657 | # create the target project |
|
655 | 658 | project = Project.new :name => identifier.humanize, |
@@ -662,16 +665,16 namespace :redmine do | |||
|
662 | 665 | puts |
|
663 | 666 | puts "This project already exists in your Redmine database." |
|
664 | 667 | print "Are you sure you want to append data to this project ? [Y/n] " |
|
665 |
exit if STDIN.gets.match(/^n$/i) |
|
|
668 | exit if STDIN.gets.match(/^n$/i) | |
|
666 | 669 | end |
|
667 | 670 | project.trackers << TRACKER_BUG unless project.trackers.include?(TRACKER_BUG) |
|
668 | 671 | project.trackers << TRACKER_FEATURE unless project.trackers.include?(TRACKER_FEATURE) |
|
669 | 672 | @target_project = project.new_record? ? nil : project |
|
670 | 673 | end |
|
671 | ||
|
674 | ||
|
672 | 675 | def self.connection_params |
|
673 | 676 | if %w(sqlite sqlite3).include?(trac_adapter) |
|
674 |
{:adapter => trac_adapter, |
|
|
677 | {:adapter => trac_adapter, | |
|
675 | 678 | :database => trac_db_path} |
|
676 | 679 | else |
|
677 | 680 | {:adapter => trac_adapter, |
@@ -684,7 +687,7 namespace :redmine do | |||
|
684 | 687 | } |
|
685 | 688 | end |
|
686 | 689 | end |
|
687 | ||
|
690 | ||
|
688 | 691 | def self.establish_connection |
|
689 | 692 | constants.each do |const| |
|
690 | 693 | klass = const_get(const) |
@@ -692,7 +695,7 namespace :redmine do | |||
|
692 | 695 | klass.establish_connection connection_params |
|
693 | 696 | end |
|
694 | 697 | end |
|
695 | ||
|
698 | ||
|
696 | 699 | private |
|
697 | 700 | def self.encode(text) |
|
698 | 701 | @ic.iconv text |
@@ -700,7 +703,7 namespace :redmine do | |||
|
700 | 703 | text |
|
701 | 704 | end |
|
702 | 705 | end |
|
703 | ||
|
706 | ||
|
704 | 707 | puts |
|
705 | 708 | if Redmine::DefaultData::Loader.no_data? |
|
706 | 709 | puts "Redmine configuration need to be loaded before importing data." |
@@ -709,10 +712,10 namespace :redmine do | |||
|
709 | 712 | puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\"" |
|
710 | 713 | exit |
|
711 | 714 | end |
|
712 | ||
|
715 | ||
|
713 | 716 | puts "WARNING: a new project will be added to Redmine during this process." |
|
714 | 717 | print "Are you sure you want to continue ? [y/N] " |
|
715 |
break unless STDIN.gets.match(/^y$/i) |
|
|
718 | break unless STDIN.gets.match(/^y$/i) | |
|
716 | 719 | puts |
|
717 | 720 | |
|
718 | 721 | def prompt(text, options = {}, &block) |
@@ -724,9 +727,9 namespace :redmine do | |||
|
724 | 727 | break if yield value |
|
725 | 728 | end |
|
726 | 729 | end |
|
727 | ||
|
730 | ||
|
728 | 731 | DEFAULT_PORTS = {'mysql' => 3306, 'postgresql' => 5432} |
|
729 | ||
|
732 | ||
|
730 | 733 | prompt('Trac directory') {|directory| TracMigrate.set_trac_directory directory.strip} |
|
731 | 734 | prompt('Trac database adapter (sqlite, sqlite3, mysql, postgresql)', :default => 'sqlite') {|adapter| TracMigrate.set_trac_adapter adapter} |
|
732 | 735 | unless %w(sqlite sqlite3).include?(TracMigrate.trac_adapter) |
@@ -740,7 +743,7 namespace :redmine do | |||
|
740 | 743 | prompt('Trac database encoding', :default => 'UTF-8') {|encoding| TracMigrate.encoding encoding} |
|
741 | 744 | prompt('Target project identifier') {|identifier| TracMigrate.target_project_identifier identifier} |
|
742 | 745 | puts |
|
743 | ||
|
746 | ||
|
744 | 747 | TracMigrate.migrate |
|
745 | 748 | end |
|
746 | 749 | end |
General Comments 0
You need to be logged in to leave comments.
Login now