##// END OF EJS Templates
Do not store query object in session but id or filters only. This allows to use Rails 2.0 cookie based sessions....
Do not store query object in session but id or filters only. This allows to use Rails 2.0 cookie based sessions. git-svn-id: http://redmine.rubyforge.org/svn/trunk@1027 e93f8b46-1217-0410-a6f0-8f06a7374b81

File last commit:

r982:d1a3fbea4087
r1014:5bc7dc77e24a
Show More
migrate_from_trac.rake
555 lines | 21.6 KiB | text/x-ruby | RubyLexer
/ lib / tasks / migrate_from_trac.rake
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 # redMine - project management software
# Copyright (C) 2006-2007 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'active_record'
require 'iconv'
require 'pp'
namespace :redmine do
desc 'Trac migration script'
task :migrate_from_trac => :environment do
module TracMigrate
Jean-Philippe Lang
Trac importer:...
r933 TICKET_MAP = []
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678
DEFAULT_STATUS = IssueStatus.default
assigned_status = IssueStatus.find_by_position(2)
resolved_status = IssueStatus.find_by_position(3)
feedback_status = IssueStatus.find_by_position(4)
closed_status = IssueStatus.find :first, :conditions => { :is_closed => true }
STATUS_MAPPING = {'new' => DEFAULT_STATUS,
'reopened' => feedback_status,
'assigned' => assigned_status,
'closed' => closed_status
}
priorities = Enumeration.get_values('IPRI')
DEFAULT_PRIORITY = priorities[0]
PRIORITY_MAPPING = {'lowest' => priorities[0],
'low' => priorities[0],
'normal' => priorities[1],
'high' => priorities[2],
'highest' => priorities[3]
}
TRACKER_BUG = Tracker.find_by_position(1)
TRACKER_FEATURE = Tracker.find_by_position(2)
DEFAULT_TRACKER = TRACKER_BUG
TRACKER_MAPPING = {'defect' => TRACKER_BUG,
'enhancement' => TRACKER_FEATURE,
'task' => TRACKER_FEATURE,
'patch' =>TRACKER_FEATURE
}
Jean-Philippe Lang
Fixed Mantis importer: projects trackers and modules assignment...
r923
roles = Role.find(:all, :conditions => {:builtin => 0}, :order => 'position ASC')
manager_role = roles[0]
developer_role = roles[1]
DEFAULT_ROLE = roles.last
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 ROLE_MAPPING = {'admin' => manager_role,
'developer' => developer_role
}
class TracComponent < ActiveRecord::Base
set_table_name :component
end
class TracMilestone < ActiveRecord::Base
set_table_name :milestone
def due
if read_attribute(:due) > 0
Time.at(read_attribute(:due)).to_date
else
nil
end
end
end
class TracTicketCustom < ActiveRecord::Base
set_table_name :ticket_custom
end
class TracAttachment < ActiveRecord::Base
set_table_name :attachment
set_inheritance_column :none
def time; Time.at(read_attribute(:time)) end
def original_filename
filename
end
def content_type
Redmine::MimeType.of(filename) || ''
end
def exist?
File.file? trac_fullpath
end
def read
File.open("#{trac_fullpath}", 'rb').read
end
private
def trac_fullpath
attachment_type = read_attribute(:type)
trac_file = filename.gsub( /[^a-zA-Z0-9\-_\.!~*']/n ) {|x| sprintf('%%%02x', x[0]) }
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 "#{TracMigrate.trac_attachments_directory}/#{attachment_type}/#{id}/#{trac_file}"
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
end
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 class TracTicket < ActiveRecord::Base
set_table_name :ticket
set_inheritance_column :none
# ticket changes: only migrate status changes and comments
has_many :changes, :class_name => "TracTicketChange", :foreign_key => :ticket
has_many :attachments, :class_name => "TracAttachment", :foreign_key => :id, :conditions => "#{TracMigrate::TracAttachment.table_name}.type = 'ticket'"
has_many :customs, :class_name => "TracTicketCustom", :foreign_key => :ticket
def ticket_type
read_attribute(:type)
end
def summary
read_attribute(:summary).blank? ? "(no subject)" : read_attribute(:summary)
end
def description
read_attribute(:description).blank? ? summary : read_attribute(:description)
end
def time; Time.at(read_attribute(:time)) end
end
class TracTicketChange < ActiveRecord::Base
set_table_name :ticket_change
def time; Time.at(read_attribute(:time)) end
end
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 class TracWikiPage < ActiveRecord::Base
set_table_name :wiki
Jean-Philippe Lang
Fixed Trac importer error with Rails 2.0: readonly? is defined by ActiveRecord....
r982
def self.columns
# Hides readonly Trac field to prevent clash with AR readonly? method (Rails 2.0)
super.select {|column| column.name.to_s != 'readonly'}
end
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
class TracPermission < ActiveRecord::Base
set_table_name :permission
end
def self.find_or_create_user(username, project_member = false)
u = User.find_by_login(username)
if !u
# Create a new user if not found
mail = username[0,limit_for(User, 'mail')]
mail = "#{mail}@foo.bar" unless mail.include?("@")
u = User.new :firstname => username[0,limit_for(User, 'firstname')].gsub(/[^\w\s\'\-]/i, '-'),
:lastname => '-',
:mail => mail.gsub(/[^-@a-z0-9\.]/i, '-')
u.login = username[0,limit_for(User, 'login')].gsub(/[^a-z0-9_\-@\.]/i, '-')
u.password = 'trac'
u.admin = true if TracPermission.find_by_username_and_action(username, 'admin')
# finally, a default user is used if the new user is not valid
u = User.find(:first) unless u.save
end
# Make sure he is a member of the project
if project_member && !u.member_of?(@target_project)
role = DEFAULT_ROLE
if u.admin
role = ROLE_MAPPING['admin']
elsif TracPermission.find_by_username_and_action(username, 'developer')
role = ROLE_MAPPING['developer']
end
Jean-Philippe Lang
Fixed Mantis importer: projects trackers and modules assignment...
r923 Member.create(:user => u, :project => @target_project, :role => role)
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 u.reload
end
u
end
# Basic wiki syntax conversion
def self.convert_wiki_text(text)
# Titles
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 text = text.gsub(/^(\=+)\s(.+)\s(\=+)/) {|s| "\nh#{$1.length}. #{$2}\n"}
# External Links
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 text = text.gsub(/\[(http[^\s]+)\s+([^\]]+)\]/) {|s| "\"#{$2}\":#{$1}"}
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 # Internal Links
Jean-Philippe Lang
Trac importer:...
r933 text = text.gsub(/\[\[BR\]\]/, "\n") # This has to go before the rules below
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 text = text.gsub(/\[\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
text = text.gsub(/\[wiki:\"(.+)\".*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
text = text.gsub(/\[wiki:([^\s\]]+).*\]/) {|s| "[[#{$1.delete(',./?;|:')}]]"}
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 # Revisions links
text = text.gsub(/\[(\d+)\]/, 'r\1')
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 # Ticket number re-writing
text = text.gsub(/#(\d+)/) do |s|
TICKET_MAP[$1.to_i] ||= $1
Jean-Philippe Lang
Trac importer:...
r933 "\##{TICKET_MAP[$1.to_i] || $1}"
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 end
# Preformatted blocks
text = text.gsub(/\{\{\{/, '<pre>')
text = text.gsub(/\}\}\}/, '</pre>')
# Highlighting
text = text.gsub(/'''''([^\s])/, '_*\1')
text = text.gsub(/([^\s])'''''/, '\1*_')
text = text.gsub(/'''/, '*')
text = text.gsub(/''/, '_')
text = text.gsub(/__/, '+')
text = text.gsub(/~~/, '-')
text = text.gsub(/`/, '@')
text = text.gsub(/,,/, '~')
# Lists
text = text.gsub(/^([ ]+)\* /) {|s| '*' * $1.length + " "}
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 text
end
def self.migrate
Jean-Philippe Lang
Trac importer:...
r933 establish_connection
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 # Quick database test
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 TracComponent.count
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 migrated_components = 0
migrated_milestones = 0
migrated_tickets = 0
migrated_custom_values = 0
migrated_ticket_attachments = 0
migrated_wiki_edits = 0
# Components
print "Migrating components"
issues_category_map = {}
TracComponent.find(:all).each do |component|
print '.'
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 STDOUT.flush
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 c = IssueCategory.new :project => @target_project,
:name => encode(component.name[0, limit_for(IssueCategory, 'name')])
next unless c.save
issues_category_map[component.name] = c
migrated_components += 1
end
puts
# Milestones
print "Migrating milestones"
version_map = {}
TracMilestone.find(:all).each do |milestone|
print '.'
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 STDOUT.flush
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 v = Version.new :project => @target_project,
:name => encode(milestone.name[0, limit_for(Version, 'name')]),
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 :description => encode(milestone.description.to_s[0, limit_for(Version, 'description')]),
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 :effective_date => milestone.due
next unless v.save
version_map[milestone.name] = v
migrated_milestones += 1
end
puts
# Custom fields
# TODO: read trac.ini instead
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 print "Migrating custom fields"
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 custom_field_map = {}
TracTicketCustom.find_by_sql("SELECT DISTINCT name FROM #{TracTicketCustom.table_name}").each do |field|
print '.'
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 STDOUT.flush
# Redmine custom field name
field_name = encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize
# Find if the custom already exists in Redmine
f = IssueCustomField.find_by_name(field_name)
# Or create a new one
f ||= IssueCustomField.create(:name => encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize,
:field_format => 'string')
next if f.new_record?
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 f.trackers = Tracker.find(:all)
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 f.projects << @target_project
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 custom_field_map[field.name] = f
end
puts
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683
# Trac 'resolution' field as a Redmine custom field
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 r = IssueCustomField.find(:first, :conditions => { :name => "Resolution" })
r = IssueCustomField.new(:name => 'Resolution',
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 :field_format => 'list',
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 :is_filter => true) if r.nil?
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 r.trackers = Tracker.find(:all)
r.projects << @target_project
r.possible_values = %w(fixed invalid wontfix duplicate worksforme)
custom_field_map['resolution'] = r if r.save
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678
# Tickets
print "Migrating tickets"
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 TracTicket.find(:all, :order => 'id ASC').each do |ticket|
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 print '.'
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 STDOUT.flush
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 i = Issue.new :project => @target_project,
:subject => encode(ticket.summary[0, limit_for(Issue, 'subject')]),
:description => convert_wiki_text(encode(ticket.description)),
:priority => PRIORITY_MAPPING[ticket.priority] || DEFAULT_PRIORITY,
:created_on => ticket.time
i.author = find_or_create_user(ticket.reporter)
i.category = issues_category_map[ticket.component] unless ticket.component.blank?
i.fixed_version = version_map[ticket.milestone] unless ticket.milestone.blank?
i.status = STATUS_MAPPING[ticket.status] || DEFAULT_STATUS
i.tracker = TRACKER_MAPPING[ticket.ticket_type] || DEFAULT_TRACKER
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 i.custom_values << CustomValue.new(:custom_field => custom_field_map['resolution'], :value => ticket.resolution) unless ticket.resolution.blank?
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 i.id = ticket.id unless Issue.exists?(ticket.id)
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 next unless i.save
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 TICKET_MAP[ticket.id] = i.id
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 migrated_tickets += 1
# Owner
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 unless ticket.owner.blank?
i.assigned_to = find_or_create_user(ticket.owner, true)
i.save
end
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 # Comments and status/resolution changes
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 ticket.changes.group_by(&:time).each do |time, changeset|
status_change = changeset.select {|change| change.field == 'status'}.first
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 resolution_change = changeset.select {|change| change.field == 'resolution'}.first
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 comment_change = changeset.select {|change| change.field == 'comment'}.first
n = Journal.new :notes => (comment_change ? convert_wiki_text(encode(comment_change.newvalue)) : ''),
:created_on => time
n.user = find_or_create_user(changeset.first.author)
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 n.journalized = i
Jean-Philippe Lang
Trac importer now migrates status changes....
r682 if status_change &&
STATUS_MAPPING[status_change.oldvalue] &&
STATUS_MAPPING[status_change.newvalue] &&
(STATUS_MAPPING[status_change.oldvalue] != STATUS_MAPPING[status_change.newvalue])
n.details << JournalDetail.new(:property => 'attr',
:prop_key => 'status_id',
:old_value => STATUS_MAPPING[status_change.oldvalue].id,
:value => STATUS_MAPPING[status_change.newvalue].id)
end
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 if resolution_change
n.details << JournalDetail.new(:property => 'cf',
:prop_key => custom_field_map['resolution'].id,
:old_value => resolution_change.oldvalue,
:value => resolution_change.newvalue)
end
n.save unless n.details.empty? && n.notes.blank?
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
# Attachments
ticket.attachments.each do |attachment|
next unless attachment.exist?
a = Attachment.new :created_on => attachment.time
a.file = attachment
a.author = find_or_create_user(attachment.author)
a.container = i
migrated_ticket_attachments += 1 if a.save
end
# Custom fields
ticket.customs.each do |custom|
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 next if custom_field_map[custom.name].nil?
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 v = CustomValue.new :custom_field => custom_field_map[custom.name],
:value => custom.value
v.customized = i
next unless v.save
migrated_custom_values += 1
end
end
puts
# Wiki
print "Migrating wiki"
@target_project.wiki.destroy if @target_project.wiki
@target_project.reload
wiki = Wiki.new(:project => @target_project, :start_page => 'WikiStart')
if wiki.save
TracWikiPage.find(:all, :order => 'name, version').each do |page|
print '.'
Jean-Philippe Lang
Trac importer improvements (by Mat Trudel):...
r918 STDOUT.flush
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 p = wiki.find_or_new_page(page.name)
p.content = WikiContent.new(:page => p) if p.new_record?
p.content.text = page.text
p.content.author = find_or_create_user(page.author) unless page.author.blank? || page.author == 'trac'
p.content.comments = page.comment
p.new_record? ? p.save : p.content.save
migrated_wiki_edits += 1 unless p.content.new_record?
end
wiki.reload
wiki.pages.each do |page|
page.content.text = convert_wiki_text(page.content.text)
page.content.save
end
end
puts
puts
puts "Components: #{migrated_components}/#{TracComponent.count}"
puts "Milestones: #{migrated_milestones}/#{TracMilestone.count}"
puts "Tickets: #{migrated_tickets}/#{TracTicket.count}"
puts "Ticket files: #{migrated_ticket_attachments}/" + TracAttachment.count("type = 'ticket'").to_s
puts "Custom values: #{migrated_custom_values}/#{TracTicketCustom.count}"
puts "Wiki edits: #{migrated_wiki_edits}/#{TracWikiPage.count}"
end
def self.limit_for(klass, attribute)
klass.columns_hash[attribute.to_s].limit
end
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 def self.encoding(charset)
@ic = Iconv.new('UTF-8', charset)
rescue Iconv::InvalidEncoding
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 puts "Invalid encoding!"
return false
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 def self.set_trac_directory(path)
Jean-Philippe Lang
Trac importer:...
r933 @@trac_directory = path
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 raise "This directory doesn't exist!" unless File.directory?(path)
raise "#{trac_attachments_directory} doesn't exist!" unless File.directory?(trac_attachments_directory)
Jean-Philippe Lang
Trac importer:...
r933 @@trac_directory
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 rescue Exception => e
puts e
return false
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 def self.trac_directory
Jean-Philippe Lang
Trac importer:...
r933 @@trac_directory
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881
def self.set_trac_adapter(adapter)
Jean-Philippe Lang
Trac importer:...
r933 return false if adapter.blank?
raise "Unknown adapter: #{adapter}!" unless %w(sqlite sqlite3 mysql postgresql).include?(adapter)
# If adapter is sqlite or sqlite3, make sure that trac.db exists
raise "#{trac_db_path} doesn't exist!" if %w(sqlite sqlite3).include?(adapter) && !File.exist?(trac_db_path)
@@trac_adapter = adapter
rescue Exception => e
puts e
return false
end
def self.set_trac_db_host(host)
return nil if host.blank?
@@trac_db_host = host
end
def self.set_trac_db_port(port)
return nil if port.to_i == 0
@@trac_db_port = port.to_i
end
def self.set_trac_db_name(name)
return nil if name.blank?
@@trac_db_name = name
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881 end
Jean-Philippe Lang
Trac importer:...
r933
def self.set_trac_db_username(username)
@@trac_db_username = username
end
def self.set_trac_db_password(password)
@@trac_db_password = password
end
mattr_reader :trac_directory, :trac_adapter, :trac_db_host, :trac_db_port, :trac_db_name, :trac_db_username, :trac_db_password
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 def self.trac_db_path; "#{trac_directory}/db/trac.db" end
def self.trac_attachments_directory; "#{trac_directory}/attachments" end
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 def self.target_project_identifier(identifier)
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 project = Project.find_by_identifier(identifier)
if !project
# create the target project
project = Project.new :name => identifier.humanize,
:description => identifier.humanize
project.identifier = identifier
puts "Unable to create a project with identifier '#{identifier}'!" unless project.save
Jean-Philippe Lang
Trac importer: user can now choose between sqlite and sqlite3 adapter for Trac database....
r881 # enable issues and wiki for the created project
project.enabled_module_names = ['issue_tracking', 'wiki']
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 end
Jean-Philippe Lang
Trac importer:...
r933 project.trackers << TRACKER_BUG
project.trackers << TRACKER_FEATURE
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 @target_project = project.new_record? ? nil : project
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
Jean-Philippe Lang
Trac importer:...
r933 def self.connection_params
if %w(sqlite sqlite3).include?(trac_adapter)
{:adapter => trac_adapter,
:database => trac_db_path}
else
{:adapter => trac_adapter,
:database => trac_db_name,
:host => trac_db_host,
:port => trac_db_port,
:username => trac_db_username,
:password => trac_db_password}
end
end
def self.establish_connection
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 constants.each do |const|
klass = const_get(const)
next unless klass.respond_to? 'establish_connection'
Jean-Philippe Lang
Trac importer:...
r933 klass.establish_connection connection_params
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
end
private
def self.encode(text)
@ic.iconv text
rescue
text
end
end
puts
Jean-Philippe Lang
Trac importer:...
r933 puts "WARNING: a new project will be added to Redmine during this process."
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 print "Are you sure you want to continue ? [y/N] "
break unless STDIN.gets.match(/^y$/i)
puts
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681
def prompt(text, options = {}, &block)
default = options[:default] || ''
while true
print "#{text} [#{default}]: "
value = STDIN.gets.chomp!
value = default if value.blank?
break if yield value
end
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 end
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681
Jean-Philippe Lang
Trac importer:...
r933 DEFAULT_PORTS = {'mysql' => 3306, 'postgresl' => 5432}
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 prompt('Trac directory') {|directory| TracMigrate.set_trac_directory directory}
Jean-Philippe Lang
Trac importer:...
r933 prompt('Trac database adapter (sqlite, sqlite3, mysql, postgresql)', :default => 'sqlite') {|adapter| TracMigrate.set_trac_adapter adapter}
unless %w(sqlite sqlite3).include?(TracMigrate.trac_adapter)
prompt('Trac database host', :default => 'localhost') {|host| TracMigrate.set_trac_db_host host}
prompt('Trac database port', :default => DEFAULT_PORTS[TracMigrate.trac_adapter]) {|port| TracMigrate.set_trac_db_port port}
prompt('Trac database name') {|name| TracMigrate.set_trac_db_name name}
prompt('Trac database username') {|username| TracMigrate.set_trac_db_username username}
prompt('Trac database password') {|password| TracMigrate.set_trac_db_password password}
end
Jean-Philippe Lang
Trac importer: 'resolution' field imported with history as a custom field....
r683 prompt('Trac database encoding', :default => 'UTF-8') {|encoding| TracMigrate.encoding encoding}
Jean-Philippe Lang
Trac importer now checks the existence of trac.db and attachments directory before processing....
r681 prompt('Target project identifier') {|identifier| TracMigrate.target_project_identifier identifier}
Jean-Philippe Lang
Trac importer initial commit. The script migrates:...
r678 puts
TracMigrate.migrate
end
end