| 1 |
|
# Redmine - project management software
|
|
1 |
# redMine - project management software
|
| 2 |
2 |
# Copyright (C) 2006-2007 Jean-Philippe Lang
|
| 3 |
|
# Copyright (C) 2007-2011 Trac/Redmine Community
|
| 4 |
|
# References:
|
| 5 |
|
# - http://www.redmine.org/boards/1/topics/12273 (Trac Importer Patch Coordination)
|
| 6 |
|
# - http://github.com/landy2005/Redmine-migrate-from-Trac
|
| 7 |
3 |
#
|
| 8 |
4 |
# This program is free software; you can redistribute it and/or
|
| 9 |
5 |
# modify it under the terms of the GNU General Public License
|
| ... | ... | |
| 56 |
52 |
'blocker' => priorities[4]
|
| 57 |
53 |
}
|
| 58 |
54 |
|
| 59 |
|
TRACKER_BUG = Tracker.find_by_name('Bug')
|
| 60 |
|
TRACKER_FEATURE = Tracker.find_by_name('Feature')
|
| 61 |
|
TRACKER_SUPPORT = Tracker.find_by_name('Support')
|
|
55 |
TRACKER_BUG = Tracker.find_by_position(1)
|
|
56 |
TRACKER_FEATURE = Tracker.find_by_position(2)
|
|
57 |
# Add a fourth issue type for tasks as we use them heavily
|
|
58 |
t = Tracker.find_by_name('Task')
|
|
59 |
if !t
|
|
60 |
t = Tracker.create(:name => 'Task', :is_in_chlog => true, :is_in_roadmap => false, :position => 4)
|
|
61 |
t.workflows.copy(Tracker.find(1))
|
|
62 |
end
|
|
63 |
TRACKER_TASK = t
|
| 62 |
64 |
DEFAULT_TRACKER = TRACKER_BUG
|
| 63 |
65 |
TRACKER_MAPPING = {'defect' => TRACKER_BUG,
|
| 64 |
66 |
'enhancement' => TRACKER_FEATURE,
|
| 65 |
|
'task' => TRACKER_SUPPORT,
|
|
67 |
'task' => TRACKER_TASK,
|
| 66 |
68 |
'patch' =>TRACKER_FEATURE
|
| 67 |
69 |
}
|
| 68 |
70 |
|
| ... | ... | |
| 248 |
250 |
set_table_name :session_attribute
|
| 249 |
251 |
end
|
| 250 |
252 |
|
| 251 |
|
# TODO put your Login Mapping in this method and rename method below
|
| 252 |
|
# def self.find_or_create_user(username, project_member = false)
|
| 253 |
|
# TRAC_REDMINE_LOGIN_MAP = []
|
| 254 |
|
# return TRAC_REDMINE_LOGIN_MAP[username]
|
| 255 |
|
# OR more hard-coded:
|
| 256 |
|
# if username == 'TracX'
|
| 257 |
|
# username = 'RedmineX'
|
| 258 |
|
# elsif username == 'gilles'
|
| 259 |
|
# username = 'gcornu'
|
| 260 |
|
# #elseif ...
|
| 261 |
|
# else
|
| 262 |
|
# username = 'gcornu'
|
| 263 |
|
# end
|
| 264 |
|
# return User.find_by_login(username)
|
| 265 |
|
# end
|
| 266 |
|
|
| 267 |
253 |
def self.find_or_create_user(username, project_member = false)
|
| 268 |
254 |
return User.anonymous if username.blank?
|
| 269 |
255 |
|
| ... | ... | |
| 370 |
356 |
:name => encode(milestone.name[0, limit_for(Version, 'name')]),
|
| 371 |
357 |
:description => nil,
|
| 372 |
358 |
:wiki_page_title => milestone.name.to_s,
|
| 373 |
|
:effective_date => (!milestone.completed.blank? ? milestone.completed : (!milestone.due.blank? ? milestone.due : nil))
|
|
359 |
:effective_date => milestone.completed
|
| 374 |
360 |
|
| 375 |
361 |
next unless v.save
|
| 376 |
362 |
version_map[milestone.name] = v
|
| ... | ... | |
| 385 |
371 |
#print "Migrating custom fields"
|
| 386 |
372 |
custom_field_map = {}
|
| 387 |
373 |
TracTicketCustom.find_by_sql("SELECT DISTINCT name FROM #{TracTicketCustom.table_name}").each do |field|
|
| 388 |
|
# use line below and adapt the WHERE condifiton, if you want to skip some unused custom fields
|
| 389 |
|
# TracTicketCustom.find_by_sql("SELECT DISTINCT name FROM #{TracTicketCustom.table_name} WHERE name NOT IN ('duration', 'software')").each do |field|
|
| 390 |
374 |
#print '.' # Maybe not needed this out?
|
| 391 |
375 |
#STDOUT.flush
|
| 392 |
376 |
# Redmine custom field name
|
| 393 |
377 |
field_name = encode(field.name[0, limit_for(IssueCustomField, 'name')]).humanize
|
| 394 |
|
|
| 395 |
|
# # Ugly hack to skip custom field 'Browser', which is in 'list' format...
|
| 396 |
|
# next if field_name == 'browser'
|
| 397 |
|
|
| 398 |
378 |
# Find if the custom already exists in Redmine
|
| 399 |
379 |
f = IssueCustomField.find_by_name(field_name)
|
| 400 |
380 |
# Ugly hack to handle billable checkbox. Would require to read the ini file to be cleaner
|
| ... | ... | |
| 414 |
394 |
end
|
| 415 |
395 |
#puts
|
| 416 |
396 |
|
| 417 |
|
# # Trac custom field 'Browser' field as a Redmine custom field
|
| 418 |
|
# b = IssueCustomField.find(:first, :conditions => { :name => "Browser" })
|
| 419 |
|
# b = IssueCustomField.new(:name => 'Browser',
|
| 420 |
|
# :field_format => 'list',
|
| 421 |
|
# :is_filter => true) if b.nil?
|
| 422 |
|
# b.trackers << [TRACKER_BUG, TRACKER_FEATURE, TRACKER_SUPPORT]
|
| 423 |
|
# b.projects << @target_project
|
| 424 |
|
# b.possible_values = (b.possible_values + %w(IE6 IE7 IE8 IE9 Firefox Chrome Safari Opera)).flatten.compact.uniq
|
| 425 |
|
# b.save!
|
| 426 |
|
# custom_field_map['browser'] = b
|
| 427 |
|
|
| 428 |
397 |
# Trac 'resolution' field as a Redmine custom field
|
| 429 |
398 |
r = IssueCustomField.find(:first, :conditions => { :name => "Resolution" })
|
| 430 |
399 |
r = IssueCustomField.new(:name => 'Resolution',
|
| 431 |
400 |
:field_format => 'list',
|
| 432 |
401 |
:is_filter => true) if r.nil?
|
| 433 |
|
r.trackers << [TRACKER_BUG, TRACKER_FEATURE, TRACKER_SUPPORT]
|
|
402 |
r.trackers = Tracker.find(:all)
|
| 434 |
403 |
r.projects << @target_project
|
| 435 |
404 |
r.possible_values = (r.possible_values + %w(fixed invalid wontfix duplicate worksforme)).flatten.compact.uniq
|
| 436 |
405 |
r.save!
|
| ... | ... | |
| 446 |
415 |
k.save!
|
| 447 |
416 |
custom_field_map['keywords'] = k
|
| 448 |
417 |
|
| 449 |
|
# Trac 'version' field as a Redmine custom field, taking advantage of feature #2096 (available since Redmine 1.2.0)
|
| 450 |
|
v = IssueCustomField.find(:first, :conditions => { :name => "Found in Version" })
|
| 451 |
|
v = IssueCustomField.new(:name => 'Found in Version',
|
| 452 |
|
:field_format => 'version',
|
| 453 |
|
:is_filter => true) if v.nil?
|
| 454 |
|
# Only apply to BUG tracker (?)
|
| 455 |
|
v.trackers << TRACKER_BUG
|
| 456 |
|
#v.trackers << [TRACKER_BUG, TRACKER_FEATURE]
|
| 457 |
|
|
| 458 |
|
# Affect custom field to current Project
|
| 459 |
|
v.projects << @target_project
|
| 460 |
|
|
| 461 |
|
v.save!
|
| 462 |
|
custom_field_map['found_in_version'] = v
|
| 463 |
|
|
| 464 |
418 |
# Trac ticket id as a Redmine custom field
|
| 465 |
419 |
tid = IssueCustomField.find(:first, :conditions => { :name => "TracID" })
|
| 466 |
420 |
tid = IssueCustomField.new(:name => 'TracID',
|
| 467 |
421 |
:field_format => 'string',
|
| 468 |
422 |
:is_filter => true) if tid.nil?
|
| 469 |
|
tid.trackers << [TRACKER_BUG, TRACKER_FEATURE, TRACKER_SUPPORT]
|
|
423 |
tid.trackers = Tracker.find(:all)
|
| 470 |
424 |
tid.projects << @target_project
|
| 471 |
425 |
tid.save!
|
| 472 |
426 |
custom_field_map['tracid'] = tid
|
| ... | ... | |
| 488 |
442 |
i.fixed_version = version_map[ticket.milestone] unless ticket.milestone.blank?
|
| 489 |
443 |
i.status = STATUS_MAPPING[ticket.status] || DEFAULT_STATUS
|
| 490 |
444 |
i.tracker = TRACKER_MAPPING[ticket.ticket_type] || DEFAULT_TRACKER
|
| 491 |
|
# Use the Redmine-genereated new ticket ID anyway (no Ticket ID recycling)
|
| 492 |
|
#i.id = ticket.id unless Issue.exists?(ticket.id)
|
|
445 |
i.id = ticket.id unless Issue.exists?(ticket.id)
|
| 493 |
446 |
next unless Time.fake(ticket.changetime) { i.save }
|
| 494 |
447 |
TICKET_MAP[ticket.id] = i.id
|
| 495 |
448 |
migrated_tickets += 1
|
| ... | ... | |
| 500 |
453 |
Time.fake(ticket.changetime) { i.save }
|
| 501 |
454 |
end
|
| 502 |
455 |
# Handle CC field
|
| 503 |
|
# Feature disabled (CC field almost never used, No time to validate/test this recent improvments from A. Callegaro)
|
| 504 |
|
# ticket.cc.split(',').each do |email|
|
| 505 |
|
# w = Watcher.new :watchable_type => 'Issue',
|
| 506 |
|
# :watchable_id => i.id,
|
| 507 |
|
# :user_id => find_or_create_user(email.strip).id
|
| 508 |
|
# w.save
|
| 509 |
|
# end
|
|
456 |
ticket.cc.split(',').each do |email|
|
|
457 |
w = Watcher.new :watchable_type => 'Issue',
|
|
458 |
:watchable_id => i.id,
|
|
459 |
:user_id => find_or_create_user(email.strip).id
|
|
460 |
w.save
|
|
461 |
end
|
| 510 |
462 |
|
| 511 |
463 |
# Necessary to handle direct link to note from timelogs and putting the right start time in issue
|
| 512 |
464 |
noteid = 1
|
| ... | ... | |
| 659 |
611 |
if custom_field_map['tracid']
|
| 660 |
612 |
custom_values[custom_field_map['tracid'].id] = ticket.id
|
| 661 |
613 |
end
|
| 662 |
|
|
| 663 |
|
if !ticket.version.blank? && custom_field_map['found_in_version']
|
| 664 |
|
found_in = version_map[ticket.version]
|
| 665 |
|
if !found_in.nil?
|
| 666 |
|
puts "Issue #{i.id} found in #{found_in.name.to_s} (#{found_in.id.to_s}) - trac: #{ticket.version}"
|
| 667 |
|
else
|
| 668 |
|
#TODO: add better error management here...
|
| 669 |
|
puts "Issue #{i.id} : ouch... - trac: #{ticket.version}"
|
| 670 |
|
end
|
| 671 |
|
custom_values[custom_field_map['found_in_version'].id] = found_in.id.to_s
|
| 672 |
|
STDOUT.flush
|
| 673 |
|
end
|
| 674 |
|
|
| 675 |
614 |
i.custom_field_values = custom_values
|
| 676 |
615 |
i.save_custom_field_values
|
| 677 |
616 |
end
|
| ... | ... | |
| 738 |
677 |
puts if wiki_pages_count < wiki_pages_total
|
| 739 |
678 |
|
| 740 |
679 |
who = " in Issues"
|
| 741 |
|
#issues_total = TICKET_MAP.length #works with Ruby <= 1.8.6
|
| 742 |
|
issues_total = TICKET_MAP.count #works with Ruby >= 1.8.7
|
|
680 |
issues_total = TICKET_MAP.count
|
| 743 |
681 |
TICKET_MAP.each do |newId|
|
| 744 |
682 |
issues_count += 1
|
| 745 |
683 |
simplebar(who, issues_count, issues_total)
|
| ... | ... | |
| 759 |
697 |
puts if issues_count < issues_total
|
| 760 |
698 |
|
| 761 |
699 |
who = " in Milestone descriptions"
|
| 762 |
|
#milestone_wiki_total = milestone_wiki.length #works with Ruby <= 1.8.6
|
| 763 |
|
milestone_wiki_total = milestone_wiki.count #works with Ruby >= 1.8.7
|
|
700 |
milestone_wiki_total = milestone_wiki.count
|
| 764 |
701 |
milestone_wiki.each do |name|
|
| 765 |
702 |
milestone_wiki_count += 1
|
| 766 |
703 |
simplebar(who, milestone_wiki_count, milestone_wiki_total)
|
| ... | ... | |
| 865 |
802 |
project.identifier = identifier
|
| 866 |
803 |
puts "Unable to create a project with identifier '#{identifier}'!" unless project.save
|
| 867 |
804 |
# enable issues and wiki for the created project
|
| 868 |
|
# Enable only a minimal set of modules by default
|
| 869 |
|
project.enabled_module_names = ['issue_tracking', 'wiki']
|
|
805 |
# Enable all project modules by default
|
|
806 |
project.enabled_module_names = ['issue_tracking', 'wiki', 'time_tracking', 'news', 'documents', 'files', 'repository', 'boards', 'calendar', 'gantt']
|
| 870 |
807 |
else
|
| 871 |
808 |
puts
|
| 872 |
809 |
puts "This project already exists in your Redmine database."
|
| ... | ... | |
| 876 |
813 |
end
|
| 877 |
814 |
project.trackers << TRACKER_BUG unless project.trackers.include?(TRACKER_BUG)
|
| 878 |
815 |
project.trackers << TRACKER_FEATURE unless project.trackers.include?(TRACKER_FEATURE)
|
| 879 |
|
project.trackers << TRACKER_SUPPORT unless project.trackers.include?(TRACKER_SUPPORT)
|
|
816 |
# Add Task type to the project
|
|
817 |
project.trackers << TRACKER_TASK unless project.trackers.include?(TRACKER_TASK)
|
| 880 |
818 |
@target_project = project.new_record? ? nil : project
|
| 881 |
819 |
@target_project.reload
|
| 882 |
820 |
end
|
| ... | ... | |
| 952 |
890 |
|
| 953 |
891 |
|
| 954 |
892 |
desc 'Subversion migration script'
|
| 955 |
|
task :migrate_svn_commit_properties => :environment do
|
|
893 |
task :migrate_from_trac_svn => :environment do
|
| 956 |
894 |
|
| 957 |
895 |
require 'redmine/scm/adapters/abstract_adapter'
|
| 958 |
896 |
require 'redmine/scm/adapters/subversion_adapter'
|
| ... | ... | |
| 964 |
902 |
TICKET_MAP = []
|
| 965 |
903 |
|
| 966 |
904 |
class Commit
|
| 967 |
|
attr_accessor :revision, :message, :author
|
|
905 |
attr_accessor :revision, :message
|
| 968 |
906 |
|
| 969 |
907 |
def initialize(attributes={})
|
| 970 |
|
self.author = attributes[:author] || ""
|
| 971 |
908 |
self.message = attributes[:message] || ""
|
| 972 |
909 |
self.revision = attributes[:revision]
|
| 973 |
910 |
end
|
| 974 |
911 |
end
|
| 975 |
912 |
|
| 976 |
913 |
class SvnExtendedAdapter < Redmine::Scm::Adapters::SubversionAdapter
|
| 977 |
|
|
| 978 |
|
def set_author(path=nil, revision=nil, author=nil)
|
| 979 |
|
path ||= ''
|
| 980 |
|
|
| 981 |
|
cmd = "#{SVN_BIN} propset svn:author --quiet --revprop -r #{revision} \"#{author}\" "
|
| 982 |
|
cmd << credentials_string
|
| 983 |
|
cmd << ' ' + target(URI.escape(path))
|
| 984 |
|
|
| 985 |
|
shellout(cmd) do |io|
|
| 986 |
|
begin
|
| 987 |
|
loop do
|
| 988 |
|
line = io.readline
|
| 989 |
|
puts line
|
| 990 |
|
end
|
| 991 |
|
rescue EOFError
|
| 992 |
|
end
|
| 993 |
|
end
|
| 994 |
|
|
| 995 |
|
raise if $? && $?.exitstatus != 0
|
| 996 |
|
|
| 997 |
|
end
|
| 998 |
914 |
|
| 999 |
915 |
def set_message(path=nil, revision=nil, msg=nil)
|
| 1000 |
916 |
path ||= ''
|
| ... | ... | |
| 1044 |
960 |
commits << Commit.new(
|
| 1045 |
961 |
{
|
| 1046 |
962 |
:revision => logentry.attributes['revision'].to_i,
|
| 1047 |
|
:message => logentry.elements['msg'].text,
|
| 1048 |
|
:author => logentry.elements['author'].text
|
|
963 |
:message => logentry.elements['msg'].text
|
| 1049 |
964 |
})
|
| 1050 |
965 |
end
|
| 1051 |
966 |
rescue => e
|
| ... | ... | |
| 1059 |
974 |
|
| 1060 |
975 |
end
|
| 1061 |
976 |
|
| 1062 |
|
def self.migrate_authors
|
| 1063 |
|
svn = self.scm
|
| 1064 |
|
commits = svn.messages(@svn_url)
|
| 1065 |
|
commits.each do |commit|
|
| 1066 |
|
orig_author_name = commit.author
|
| 1067 |
|
new_author_name = orig_author_name
|
| 1068 |
|
|
| 1069 |
|
# TODO put your Trac/SVN/Redmine username mapping here:
|
| 1070 |
|
if (commit.author == 'TracX')
|
| 1071 |
|
new_author_name = 'RedmineY'
|
| 1072 |
|
elsif (commit.author == 'gilles')
|
| 1073 |
|
new_author_name = 'gcornu'
|
| 1074 |
|
#elsif (commit.author == 'seco')
|
| 1075 |
|
#...
|
| 1076 |
|
else
|
| 1077 |
|
new_author_name = 'RedmineY'
|
| 1078 |
|
end
|
| 1079 |
|
|
| 1080 |
|
if (new_author_name != orig_author_name)
|
| 1081 |
|
scm.set_author(@svn_url, commit.revision, new_author_name)
|
| 1082 |
|
puts "r#{commit.revision} - Author replaced: #{orig_author_name} -> #{new_author_name}"
|
| 1083 |
|
else
|
| 1084 |
|
puts "r#{commit.revision} - Author kept: #{orig_author_name} unchanged "
|
| 1085 |
|
end
|
| 1086 |
|
end
|
| 1087 |
|
end
|
| 1088 |
|
|
| 1089 |
|
def self.migrate_messages
|
|
977 |
def self.migrate
|
| 1090 |
978 |
|
| 1091 |
979 |
project = Project.find(@@redmine_project)
|
| 1092 |
980 |
if !project
|
| ... | ... | |
| 1120 |
1008 |
|
| 1121 |
1009 |
if newText != commit.message
|
| 1122 |
1010 |
puts "Updating message #{commit.revision}"
|
| 1123 |
|
|
| 1124 |
|
# Marcel Nadje enhancement, see http://www.redmine.org/issues/2748#note-3
|
| 1125 |
|
# Hint: enable charset conversion if needed...
|
| 1126 |
|
#newText = Iconv.conv('CP1252', 'UTF-8', newText)
|
| 1127 |
|
|
| 1128 |
1011 |
scm.set_message(@svn_url, commit.revision, newText)
|
| 1129 |
1012 |
end
|
| 1130 |
1013 |
end
|
| ... | ... | |
| 1154 |
1037 |
end
|
| 1155 |
1038 |
|
| 1156 |
1039 |
def self.scm
|
| 1157 |
|
# Thomas Recloux fix, see http://www.redmine.org/issues/2748#note-1
|
| 1158 |
|
# The constructor of the SvnExtendedAdapter has ony got four parameters,
|
| 1159 |
|
# => parameters 5,6 and 7 removed
|
|
1040 |
# Bugfix, with redmine 1.0.1 (Debian's) it wasn't working anymore
|
| 1160 |
1041 |
@scm ||= SvnExtendedAdapter.new @@svn_url, @@svn_url, @@svn_username, @@svn_password
|
| 1161 |
|
#@scm ||= SvnExtendedAdapter.new @@svn_url, @@svn_url, @@svn_username, @@svn_password, 0, "", nil
|
| 1162 |
1042 |
@scm
|
| 1163 |
1043 |
end
|
| 1164 |
1044 |
end
|
| 1165 |
1045 |
|
| 1166 |
1046 |
puts
|
|
1047 |
if Redmine::DefaultData::Loader.no_data?
|
|
1048 |
puts "Redmine configuration need to be loaded before importing data."
|
|
1049 |
puts "Please, run this first:"
|
|
1050 |
puts
|
|
1051 |
puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\""
|
|
1052 |
exit
|
|
1053 |
end
|
|
1054 |
|
|
1055 |
puts "WARNING: all commit messages with references to trac pages will be modified"
|
|
1056 |
print "Are you sure you want to continue ? [y/N] "
|
|
1057 |
break unless STDIN.gets.match(/^y$/i)
|
|
1058 |
puts
|
|
1059 |
|
| 1167 |
1060 |
prompt('Subversion repository url') {|repository| SvnMigrate.set_svn_url repository.strip}
|
| 1168 |
1061 |
prompt('Subversion repository username') {|username| SvnMigrate.set_svn_username username}
|
| 1169 |
1062 |
prompt('Subversion repository password') {|password| SvnMigrate.set_svn_password password}
|
|
1063 |
prompt('Redmine project identifier') {|identifier| SvnMigrate.set_redmine_project_identifier identifier}
|
| 1170 |
1064 |
puts
|
| 1171 |
|
|
| 1172 |
|
author_migration_enabled = unsafe_prompt('1) Start Migration of SVN Commit Authors (y,n)?', {:default => 'n'}) == 'y'
|
| 1173 |
|
puts
|
| 1174 |
|
if author_migration_enabled
|
| 1175 |
|
puts "WARNING: Some (maybe all) commit authors will be replaced"
|
| 1176 |
|
print "Are you sure you want to continue ? [y/N] "
|
| 1177 |
|
break unless STDIN.gets.match(/^y$/i)
|
| 1178 |
|
|
| 1179 |
|
SvnMigrate.migrate_authors
|
| 1180 |
|
end
|
| 1181 |
1065 |
|
| 1182 |
|
message_migration_enabled = unsafe_prompt('2) Start Migration of SVN Commit Messages (y,n)?', {:default => 'n'}) == 'y'
|
| 1183 |
|
puts
|
| 1184 |
|
if message_migration_enabled
|
| 1185 |
|
if Redmine::DefaultData::Loader.no_data?
|
| 1186 |
|
puts "Redmine configuration need to be loaded before importing data."
|
| 1187 |
|
puts "Please, run this first:"
|
| 1188 |
|
puts
|
| 1189 |
|
puts " rake redmine:load_default_data RAILS_ENV=\"#{ENV['RAILS_ENV']}\""
|
| 1190 |
|
exit
|
| 1191 |
|
end
|
| 1192 |
|
|
| 1193 |
|
puts "WARNING: all commit messages with references to trac pages will be modified"
|
| 1194 |
|
print "Are you sure you want to continue ? [y/N] "
|
| 1195 |
|
break unless STDIN.gets.match(/^y$/i)
|
| 1196 |
|
puts
|
| 1197 |
|
|
| 1198 |
|
prompt('Redmine project identifier') {|identifier| SvnMigrate.set_redmine_project_identifier identifier}
|
| 1199 |
|
puts
|
| 1200 |
|
|
| 1201 |
|
SvnMigrate.migrate_messages
|
| 1202 |
|
end
|
|
1066 |
SvnMigrate.migrate
|
|
1067 |
|
| 1203 |
1068 |
end
|
| 1204 |
1069 |
|
| 1205 |
1070 |
# Prompt
|
| ... | ... | |
| 1214 |
1079 |
end
|
| 1215 |
1080 |
end
|
| 1216 |
1081 |
|
| 1217 |
|
# Sorry, I had troubles to intagrate 'prompt' and quickly went this way...
|
| 1218 |
|
def unsafe_prompt(text, options = {})
|
| 1219 |
|
default = options[:default] || ''
|
| 1220 |
|
print "#{text} [#{default}]: "
|
| 1221 |
|
value = STDIN.gets.chomp!
|
| 1222 |
|
value = default if value.blank?
|
| 1223 |
|
value
|
| 1224 |
|
end
|
| 1225 |
|
|
| 1226 |
1082 |
# Basic wiki syntax conversion
|
| 1227 |
1083 |
def convert_wiki_text_mapping(text, ticket_map = [])
|
| 1228 |
1084 |
# Hide links
|
| ... | ... | |
| 1385 |
1241 |
text = text.gsub(/''/, '_')
|
| 1386 |
1242 |
text = text.gsub(/__/, '+')
|
| 1387 |
1243 |
text = text.gsub(/~~/, '-')
|
| 1388 |
|
text = text.gsub(/`/, '@')
|
| 1389 |
1244 |
text = text.gsub(/,,/, '~')
|
| 1390 |
1245 |
# Tables
|
| 1391 |
1246 |
text = text.gsub(/\|\|/, '|')
|
| ... | ... | |
| 1403 |
1258 |
# TOC (is right-aligned, because that in Trac)
|
| 1404 |
1259 |
text = text.gsub(/\[\[TOC(?:\((.*?)\))?\]\]/m) {|s| "{{>toc}}\n"}
|
| 1405 |
1260 |
|
| 1406 |
|
# Thomas Recloux enhancements, see http://www.redmine.org/issues/2748#note-1
|
| 1407 |
|
# Redmine needs a space between keywords "refs,ref,fix" and the issue number (#1234) in subversion commit messages.
|
| 1408 |
|
# TODO: rewrite it in a more regex-style way
|
| 1409 |
|
|
| 1410 |
|
text = text.gsub("refs#", "refs #")
|
| 1411 |
|
text = text.gsub("Refs#", "refs #")
|
| 1412 |
|
text = text.gsub("REFS#", "refs #")
|
| 1413 |
|
text = text.gsub("ref#", "refs #")
|
| 1414 |
|
text = text.gsub("Ref#", "refs #")
|
| 1415 |
|
text = text.gsub("REF#", "refs #")
|
| 1416 |
|
|
| 1417 |
|
text = text.gsub("fix#", "fixes #")
|
| 1418 |
|
text = text.gsub("Fix#", "fixes #")
|
| 1419 |
|
text = text.gsub("FIX#", "fixes #")
|
| 1420 |
|
text = text.gsub("fixes#", "fixes #")
|
| 1421 |
|
text = text.gsub("Fixes#", "fixes #")
|
| 1422 |
|
text = text.gsub("FIXES#", "fixes #")
|
| 1423 |
|
|
| 1424 |
1261 |
# Restore and convert code blocks
|
| 1425 |
1262 |
text = code_convert(text)
|
| 1426 |
1263 |
|