Project

General

Profile

Patch #33722 ยป migrate_from_trac-trac-1.0.patch

Hideyuki KATO, 2020-07-13 02:49

View differences:

migrate_from_trac.rake 2020-07-11 21:34:59.000000000 +0900
17 17

  
18 18
require 'active_record'
19 19
require 'pp'
20
require 'digest/sha1'
20 21

  
21 22
namespace :redmine do
22 23
  desc 'Trac migration script'
......
71 72
      class ::Time
72 73
        class << self
73 74
          alias :real_now :now
75
          alias :real_at :at
74 76
          def now
75 77
            real_now - @fake_diff.to_i
76 78
          end
......
80 82
            @fake_diff = 0
81 83
           res
82 84
          end
85
          def at(time)
86
            # In Trac ticket #6466, timestamps
87
            # were changed from seconds since the epoch
88
            # to microseconds since the epoch.  The
89
            # Trac database version was bumped to 23 for this.
90
            if TracMigrate.database_version > 22
91
               Time.real_at(time / 1000000)
92
            else
93
               Time.real_at(time)
94
            end
95
          end
83 96
        end
84 97
      end
85 98

  
99
      class TracSystem < ActiveRecord::Base
100
        self.table_name = :system
101
      end
102
      
86 103
      class TracComponent < ActiveRecord::Base
87 104
        self.table_name = :component
88 105
      end
......
118 135

  
119 136
      class TracAttachment < ActiveRecord::Base
120 137
        self.table_name = :attachment
121
        set_inheritance_column :none
138
        self.inheritance_column = :none
122 139

  
123 140
        def time; Time.at(read_attribute(:time)) end
124 141

  
......
150 167
        end
151 168

  
152 169
      private
170

  
171
        def sha1(s)                                                                           
172
          return Digest::SHA1.hexdigest(s)                                                  
173
        end
174
                                                                                           
175
        def get_path(ticket_id, filename)                                                     
176
          t = sha1(ticket_id.to_s)                                                          
177
          f = sha1(filename)                                                                
178
          ext = File.extname(filename)                                                      
179
          a = [ t[0..2], "/", t, "/", f, ext ]                                              
180
          return a.join("")                                                                 
181
        end
182

  
153 183
        def trac_fullpath
154
          attachment_type = read_attribute(:type)
155
          #replace exotic characters with their hex representation to avoid invalid filenames
156
          trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) do |x|
157
            codepoint = x.codepoints.to_a[0]
158
            sprintf('%%%02x', codepoint)
159
          end
160
          "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}"
184
          attachment_type = read_attribute(:type)                                             
185
          ticket_id = read_attribute(:id)                                                     
186
          filename  = read_attribute(:filename)                                               
187
          path = get_path(id, filename)                                                
188
          "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{path}"
161 189
        end
162 190
      end
163 191

  
164 192
      class TracTicket < ActiveRecord::Base
165 193
        self.table_name = :ticket
166
        set_inheritance_column :none
194
        self.inheritance_column = :none
167 195

  
168 196
        # ticket changes: only migrate status changes and comments
169 197
        has_many :ticket_changes, :class_name => "TracTicketChange", :foreign_key => :ticket
170 198
        has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket
171 199

  
172 200
        def attachments
173
          TracMigrate::TracAttachment.all(:conditions => ["type = 'ticket' AND id = ?", self.id.to_s])
201
          TracMigrate::TracAttachment.where("type = 'ticket' AND id = :id", id: self.id.to_s)
174 202
        end
175 203

  
176 204
        def ticket_type
......
210 238

  
211 239
      class TracWikiPage < ActiveRecord::Base
212 240
        self.table_name = :wiki
213
        set_primary_key :name
241
        self.primary_key = 'name'
214 242

  
215 243
        def self.columns
216 244
          # Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0)
......
218 246
        end
219 247

  
220 248
        def attachments
221
          TracMigrate::TracAttachment.all(:conditions => ["type = 'wiki' AND id = ?", self.id.to_s])
249
          TracMigrate::TracAttachment.where("type = 'wiki' AND id = :id", id: self.id.to_s)
222 250
        end
223 251

  
224 252
        def time; Time.at(read_attribute(:time)) end
......
376 404
        # Quick database test
377 405
        TracComponent.count
378 406

  
407
        lookup_database_version
408
        print "Trac database version is: ", database_version, "\n" 
379 409
        migrated_components = 0
380 410
        migrated_milestones = 0
381 411
        migrated_tickets = 0
......
419 449
          p.save
420 450

  
421 451
          v = Version.new :project => @target_project,
422
                          :name => encode(milestone.name[0, limit_for(Version, 'name')]),
452
                          :name => encode(milestone.name),
423 453
                          :description => nil,
424 454
                          :wiki_page_title => milestone.name.to_s,
425 455
                          :effective_date => milestone.completed
......
469 499
          print '.'
470 500
          STDOUT.flush
471 501
          i = Issue.new :project => @target_project,
472
                          :subject => encode(ticket.summary[0, limit_for(Issue, 'subject')]),
502
                          :subject => encode(ticket.summary),
473 503
                          :description => convert_wiki_text(encode(ticket.description)),
474 504
                          :priority => PRIORITY_MAPPING[ticket.priority] || DEFAULT_PRIORITY,
475 505
                          :created_on => ticket.time
......
595 625
        puts "Components:      #{migrated_components}/#{TracComponent.count}"
596 626
        puts "Milestones:      #{migrated_milestones}/#{TracMilestone.count}"
597 627
        puts "Tickets:         #{migrated_tickets}/#{TracTicket.count}"
598
        puts "Ticket files:    #{migrated_ticket_attachments}/" + TracAttachment.count(:conditions => {:type => 'ticket'}).to_s
628
        puts "Ticket files:    #{migrated_ticket_attachments}/" + TracAttachment.where(type: 'ticket').count().to_s
599 629
        puts "Custom values:   #{migrated_custom_values}/#{TracTicketCustom.count}"
600 630
        puts "Wiki edits:      #{migrated_wiki_edits}/#{wiki_edit_count}"
601
        puts "Wiki files:      #{migrated_wiki_attachments}/" + TracAttachment.count(:conditions => {:type => 'wiki'}).to_s
631
        puts "Wiki files:      #{migrated_wiki_attachments}/" + TracAttachment.where(type: 'wiki').count().to_s
602 632
      end
603 633

  
604 634
      def self.limit_for(klass, attribute)
......
609 639
        @charset = charset
610 640
      end
611 641

  
642
      def self.lookup_database_version
643
        f = TracSystem.find_by_name("database_version")
644
        @@database_version = f.value.to_i
645
      end
646

  
647
      def self.database_version
648
        @@database_version
649
      end
650
      
612 651
      def self.set_trac_directory(path)
613 652
        @@trac_directory = path
614 653
        raise "This directory doesn't exist!" unless File.directory?(path)
......
664 703
      mattr_reader :trac_directory, :trac_adapter, :trac_db_host, :trac_db_port, :trac_db_name, :trac_db_schema, :trac_db_username, :trac_db_password
665 704

  
666 705
      def self.trac_db_path; "#{trac_directory}/db/trac.db" end
667
      def self.trac_attachments_directory; "#{trac_directory}/attachments" end
706
      def self.trac_attachments_directory; "#{trac_directory}/files/attachments" end
668 707

  
669 708
      def self.target_project_identifier(identifier)
670 709
        project = Project.find_by_identifier(identifier)
    (1-1/1)