summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Migrate.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla/Migrate.pm')
-rw-r--r--Bugzilla/Migrate.pm1201
1 files changed, 612 insertions, 589 deletions
diff --git a/Bugzilla/Migrate.pm b/Bugzilla/Migrate.pm
index 925a01355..74bce149f 100644
--- a/Bugzilla/Migrate.pm
+++ b/Bugzilla/Migrate.pm
@@ -20,7 +20,7 @@ use Bugzilla::Install::Requirements ();
use Bugzilla::Install::Util qw(indicate_progress);
use Bugzilla::Product;
use Bugzilla::Util qw(get_text trim generate_random_password);
-use Bugzilla::User ();
+use Bugzilla::User ();
use Bugzilla::Status ();
use Bugzilla::Version;
@@ -37,10 +37,10 @@ use constant REQUIRED_MODULES => [];
use constant NON_COMMENT_FIELDS => ();
use constant CONFIG_VARS => (
- {
- name => 'translate_fields',
- default => {},
- desc => <<'END',
+ {
+ name => 'translate_fields',
+ default => {},
+ desc => <<'END',
# This maps field names in your bug-tracker to Bugzilla field names. If a field
# has the same name in your bug-tracker and Bugzilla (case-insensitively), it
# doesn't need a mapping here. If a field isn't listed here and doesn't have
@@ -64,11 +64,11 @@ use constant CONFIG_VARS => (
# variable by default, then that field will be automatically created by
# the migrator and you don't have to worry about it.
END
- },
- {
- name => 'translate_values',
- default => {},
- desc => <<'END',
+ },
+ {
+ name => 'translate_values',
+ default => {},
+ desc => <<'END',
# This configuration variable allows you to say that a particular field
# value in your current bug-tracker should be translated to a different
# value when it's imported into Bugzilla.
@@ -108,22 +108,22 @@ END
#
# Values that don't get translated will be imported as-is.
END
- },
- {
- name => 'starting_bug_id',
- default => 0,
- desc => <<'END',
+ },
+ {
+ name => 'starting_bug_id',
+ default => 0,
+ desc => <<'END',
# What bug ID do you want the first imported bug to get? If you set this to
# 0, then the imported bug ids will just start right after the current
# bug ids. If you use this configuration variable, you must make sure that
# nobody else is using your Bugzilla while you run the migration, or a new
# bug filed by a user might take this ID instead.
END
- },
- {
- name => 'timezone',
- default => 'local',
- desc => <<'END',
+ },
+ {
+ name => 'timezone',
+ default => 'local',
+ desc => <<'END',
# If migrate.pl comes across any dates without timezones, while doing the
# migration, what timezone should we assume those dates are in?
# The best format for this variable is something like "America/Los Angeles".
@@ -133,7 +133,7 @@ END
# The special value "local" means "use the same timezone as the system I
# am running this script on now".
END
- },
+ },
);
use constant USER_FIELDS => qw(user assigned_to qa_contact reporter cc);
@@ -143,42 +143,44 @@ use constant USER_FIELDS => qw(user assigned_to qa_contact reporter cc);
#########################
sub do_migration {
- my $self = shift;
- my $dbh = Bugzilla->dbh;
- # On MySQL, setting serial values implicitly commits a transaction,
- # so we want to do it up here, outside of any transaction. This also
- # has the advantage of loading the config before anything else is done.
- if ($self->config('starting_bug_id')) {
- $dbh->bz_set_next_serial_value('bugs', 'bug_id',
- $self->config('starting_bug_id'));
- }
- $dbh->bz_start_transaction();
-
- # Read Other Database
- my $users = $self->users;
- my $products = $self->products;
- my $bugs = $self->bugs;
- $self->after_read();
-
- $self->translate_all_bugs($bugs);
-
- Bugzilla->set_user(Bugzilla::User->super_user);
-
- # Insert into Bugzilla
- $self->before_insert();
- $self->insert_users($users);
- $self->insert_products($products);
- $self->create_custom_fields();
- $self->create_legal_values($bugs);
- $self->insert_bugs($bugs);
- $self->after_insert();
- if ($self->dry_run) {
- $dbh->bz_rollback_transaction();
- $self->reset_serial_values();
- }
- else {
- $dbh->bz_commit_transaction();
- }
+ my $self = shift;
+ my $dbh = Bugzilla->dbh;
+
+ # On MySQL, setting serial values implicitly commits a transaction,
+ # so we want to do it up here, outside of any transaction. This also
+ # has the advantage of loading the config before anything else is done.
+ if ($self->config('starting_bug_id')) {
+ $dbh->bz_set_next_serial_value('bugs', 'bug_id',
+ $self->config('starting_bug_id'));
+ }
+ $dbh->bz_start_transaction();
+
+ # Read Other Database
+ my $users = $self->users;
+ my $products = $self->products;
+ my $bugs = $self->bugs;
+ $self->after_read();
+
+ $self->translate_all_bugs($bugs);
+
+ Bugzilla->set_user(Bugzilla::User->super_user);
+
+ # Insert into Bugzilla
+ $self->before_insert();
+ $self->insert_users($users);
+ $self->insert_products($products);
+ $self->create_custom_fields();
+ $self->create_legal_values($bugs);
+ $self->insert_bugs($bugs);
+ $self->after_insert();
+
+ if ($self->dry_run) {
+ $dbh->bz_rollback_transaction();
+ $self->reset_serial_values();
+ }
+ else {
+ $dbh->bz_commit_transaction();
+ }
}
################
@@ -186,24 +188,23 @@ sub do_migration {
################
sub new {
- my ($class) = @_;
- my $self = { };
- bless $self, $class;
- return $self;
+ my ($class) = @_;
+ my $self = {};
+ bless $self, $class;
+ return $self;
}
sub load {
- my ($class, $from) = @_;
- my $libdir = bz_locations()->{libpath};
- my @migration_modules = glob("$libdir/Bugzilla/Migrate/*");
- my ($module) = grep { basename($_) =~ /^\Q$from\E\.pm$/i }
- @migration_modules;
- if (!$module) {
- ThrowUserError('migrate_from_invalid', { from => $from });
- }
- require $module;
- my $canonical_name = _canonical_name($module);
- return "Bugzilla::Migrate::$canonical_name"->new;
+ my ($class, $from) = @_;
+ my $libdir = bz_locations()->{libpath};
+ my @migration_modules = glob("$libdir/Bugzilla/Migrate/*");
+ my ($module) = grep { basename($_) =~ /^\Q$from\E\.pm$/i } @migration_modules;
+ if (!$module) {
+ ThrowUserError('migrate_from_invalid', {from => $from});
+ }
+ require $module;
+ my $canonical_name = _canonical_name($module);
+ return "Bugzilla::Migrate::$canonical_name"->new;
}
#############
@@ -211,67 +212,67 @@ sub load {
#############
sub name {
- my $self = shift;
- return _canonical_name(ref $self);
+ my $self = shift;
+ return _canonical_name(ref $self);
}
sub dry_run {
- my ($self, $value) = @_;
- if (scalar(@_) > 1) {
- $self->{dry_run} = $value;
- }
- return $self->{dry_run} || 0;
+ my ($self, $value) = @_;
+ if (scalar(@_) > 1) {
+ $self->{dry_run} = $value;
+ }
+ return $self->{dry_run} || 0;
}
sub verbose {
- my ($self, $value) = @_;
- if (scalar(@_) > 1) {
- $self->{verbose} = $value;
- }
- return $self->{verbose} || 0;
+ my ($self, $value) = @_;
+ if (scalar(@_) > 1) {
+ $self->{verbose} = $value;
+ }
+ return $self->{verbose} || 0;
}
sub debug {
- my ($self, $value, $level) = @_;
- $level ||= 1;
- if ($self->verbose >= $level) {
- $value = Dumper($value) if ref $value;
- print STDERR $value, "\n";
- }
+ my ($self, $value, $level) = @_;
+ $level ||= 1;
+ if ($self->verbose >= $level) {
+ $value = Dumper($value) if ref $value;
+ print STDERR $value, "\n";
+ }
}
sub bug_fields {
- my $self = shift;
- $self->{bug_fields} ||= Bugzilla->fields({ by_name => 1 });
- return $self->{bug_fields};
+ my $self = shift;
+ $self->{bug_fields} ||= Bugzilla->fields({by_name => 1});
+ return $self->{bug_fields};
}
sub users {
- my $self = shift;
- if (!exists $self->{users}) {
- print get_text('migrate_reading_users'), "\n";
- $self->{users} = $self->_read_users();
- }
- return $self->{users};
+ my $self = shift;
+ if (!exists $self->{users}) {
+ print get_text('migrate_reading_users'), "\n";
+ $self->{users} = $self->_read_users();
+ }
+ return $self->{users};
}
sub products {
- my $self = shift;
- if (!exists $self->{products}) {
- print get_text('migrate_reading_products'), "\n";
- $self->{products} = $self->_read_products();
- }
- return $self->{products};
+ my $self = shift;
+ if (!exists $self->{products}) {
+ print get_text('migrate_reading_products'), "\n";
+ $self->{products} = $self->_read_products();
+ }
+ return $self->{products};
}
sub bugs {
- my $self = shift;
- if (!exists $self->{bugs}) {
- print get_text('migrate_reading_bugs'), "\n";
- $self->{bugs} = $self->_read_bugs();
- }
- return $self->{bugs};
+ my $self = shift;
+ if (!exists $self->{bugs}) {
+ print get_text('migrate_reading_bugs'), "\n";
+ $self->{bugs} = $self->_read_bugs();
+ }
+ return $self->{bugs};
}
###########
@@ -279,49 +280,49 @@ sub bugs {
###########
sub check_requirements {
- my $self = shift;
- my $missing = Bugzilla::Install::Requirements::_check_missing(
- $self->REQUIRED_MODULES, 1);
- my %results = (
- apache => [],
- pass => @$missing ? 0 : 1,
- missing => $missing,
- any_missing => @$missing ? 1 : 0,
- hide_all => 1,
- # These are just for compatibility with print_module_instructions
- one_dbd => 1,
- optional => [],
- );
- Bugzilla::Install::Requirements::print_module_instructions(
- \%results, 1);
- exit(1) if @$missing;
+ my $self = shift;
+ my $missing
+ = Bugzilla::Install::Requirements::_check_missing($self->REQUIRED_MODULES, 1);
+ my %results = (
+ apache => [],
+ pass => @$missing ? 0 : 1,
+ missing => $missing,
+ any_missing => @$missing ? 1 : 0,
+ hide_all => 1,
+
+ # These are just for compatibility with print_module_instructions
+ one_dbd => 1,
+ optional => [],
+ );
+ Bugzilla::Install::Requirements::print_module_instructions(\%results, 1);
+ exit(1) if @$missing;
}
sub reset_serial_values {
- my $self = shift;
- return if $self->{serial_values_reset};
- my $dbh = Bugzilla->dbh;
- my %reset = (
- 'bugs' => 'bug_id',
- 'attachments' => 'attach_id',
- 'profiles' => 'userid',
- 'longdescs' => 'comment_id',
- 'products' => 'id',
- 'components' => 'id',
- 'versions' => 'id',
- 'milestones' => 'id',
- );
- my @select_fields = grep { $_->is_select } (values %{ $self->bug_fields });
- foreach my $field (@select_fields) {
- next if $field->is_abnormal;
- $reset{$field->name} = 'id';
- }
-
- while (my ($table, $column) = each %reset) {
- $dbh->bz_set_next_serial_value($table, $column);
- }
-
- $self->{serial_values_reset} = 1;
+ my $self = shift;
+ return if $self->{serial_values_reset};
+ my $dbh = Bugzilla->dbh;
+ my %reset = (
+ 'bugs' => 'bug_id',
+ 'attachments' => 'attach_id',
+ 'profiles' => 'userid',
+ 'longdescs' => 'comment_id',
+ 'products' => 'id',
+ 'components' => 'id',
+ 'versions' => 'id',
+ 'milestones' => 'id',
+ );
+ my @select_fields = grep { $_->is_select } (values %{$self->bug_fields});
+ foreach my $field (@select_fields) {
+ next if $field->is_abnormal;
+ $reset{$field->name} = 'id';
+ }
+
+ while (my ($table, $column) = each %reset) {
+ $dbh->bz_set_next_serial_value($table, $column);
+ }
+
+ $self->{serial_values_reset} = 1;
}
###################
@@ -329,160 +330,167 @@ sub reset_serial_values {
###################
sub translate_all_bugs {
- my ($self, $bugs) = @_;
- print get_text('migrate_translating_bugs'), "\n";
- # We modify the array in place so that $self->bugs will return the
- # modified bugs, in case $self->before_insert wants them.
- my $num_bugs = scalar(@$bugs);
- for (my $i = 0; $i < $num_bugs; $i++) {
- $bugs->[$i] = $self->translate_bug($bugs->[$i]);
- }
+ my ($self, $bugs) = @_;
+ print get_text('migrate_translating_bugs'), "\n";
+
+ # We modify the array in place so that $self->bugs will return the
+ # modified bugs, in case $self->before_insert wants them.
+ my $num_bugs = scalar(@$bugs);
+ for (my $i = 0; $i < $num_bugs; $i++) {
+ $bugs->[$i] = $self->translate_bug($bugs->[$i]);
+ }
}
sub translate_bug {
- my ($self, $fields) = @_;
- my (%bug, %other_fields);
- my $original_status;
- foreach my $field (keys %$fields) {
- my $value = delete $fields->{$field};
- my $bz_field = $self->translate_field($field);
- if ($bz_field) {
- $bug{$bz_field} = $self->translate_value($bz_field, $value);
- if ($bz_field eq 'bug_status') {
- $original_status = $value;
- }
- }
- else {
- $other_fields{$field} = $value;
- }
+ my ($self, $fields) = @_;
+ my (%bug, %other_fields);
+ my $original_status;
+ foreach my $field (keys %$fields) {
+ my $value = delete $fields->{$field};
+ my $bz_field = $self->translate_field($field);
+ if ($bz_field) {
+ $bug{$bz_field} = $self->translate_value($bz_field, $value);
+ if ($bz_field eq 'bug_status') {
+ $original_status = $value;
+ }
}
-
- if (defined $original_status and !defined $bug{resolution}
- and $self->map_value('bug_status_resolution', $original_status))
- {
- $bug{resolution} = $self->map_value('bug_status_resolution',
- $original_status);
+ else {
+ $other_fields{$field} = $value;
}
+ }
- $bug{comment} = $self->_generate_description(\%bug, \%other_fields);
+ if ( defined $original_status
+ and !defined $bug{resolution}
+ and $self->map_value('bug_status_resolution', $original_status))
+ {
+ $bug{resolution} = $self->map_value('bug_status_resolution', $original_status);
+ }
- return wantarray ? (\%bug, \%other_fields) : \%bug;
+ $bug{comment} = $self->_generate_description(\%bug, \%other_fields);
+
+ return wantarray ? (\%bug, \%other_fields) : \%bug;
}
sub _generate_description {
- my ($self, $bug, $fields) = @_;
-
- my $description = "";
- foreach my $field (sort keys %$fields) {
- next if grep($_ eq $field, $self->NON_COMMENT_FIELDS);
- my $value = delete $fields->{$field};
- next if $value eq '';
- $description .= "$field: $value\n";
- }
- $description .= "\n" if $description;
-
- return $description . $bug->{comment};
+ my ($self, $bug, $fields) = @_;
+
+ my $description = "";
+ foreach my $field (sort keys %$fields) {
+ next if grep($_ eq $field, $self->NON_COMMENT_FIELDS);
+ my $value = delete $fields->{$field};
+ next if $value eq '';
+ $description .= "$field: $value\n";
+ }
+ $description .= "\n" if $description;
+
+ return $description . $bug->{comment};
}
sub translate_field {
- my ($self, $field) = @_;
- my $mapped = $self->config('translate_fields')->{$field};
- return $mapped if defined $mapped;
- ($mapped) = grep { lc($_) eq lc($field) } (keys %{ $self->bug_fields });
- return $mapped;
+ my ($self, $field) = @_;
+ my $mapped = $self->config('translate_fields')->{$field};
+ return $mapped if defined $mapped;
+ ($mapped) = grep { lc($_) eq lc($field) } (keys %{$self->bug_fields});
+ return $mapped;
}
sub parse_date {
- my ($self, $date) = @_;
- my @time = strptime($date);
- # Handle times with timezones that strptime doesn't know about.
- if (!scalar @time) {
- $date =~ s/\s+\S+$//;
- @time = strptime($date);
+ my ($self, $date) = @_;
+ my @time = strptime($date);
+
+ # Handle times with timezones that strptime doesn't know about.
+ if (!scalar @time) {
+ $date =~ s/\s+\S+$//;
+ @time = strptime($date);
+ }
+ my $tz;
+ if ($time[6]) {
+ $tz = Bugzilla->local_timezone->offset_as_string($time[6]);
+ }
+ else {
+ $tz = $self->config('timezone');
+ $tz =~ s/\s/_/g;
+ if ($tz eq 'local') {
+ $tz = Bugzilla->local_timezone;
}
- my $tz;
- if ($time[6]) {
- $tz = Bugzilla->local_timezone->offset_as_string($time[6]);
- }
- else {
- $tz = $self->config('timezone');
- $tz =~ s/\s/_/g;
- if ($tz eq 'local') {
- $tz = Bugzilla->local_timezone;
- }
- }
- my $dt = DateTime->new({
- year => $time[5] + 1900,
- month => $time[4] + 1,
- day => $time[3],
- hour => $time[2],
- minute => $time[1],
- second => int($time[0]),
- time_zone => $tz,
- });
- $dt->set_time_zone(Bugzilla->local_timezone);
- return $dt->iso8601;
+ }
+ my $dt = DateTime->new({
+ year => $time[5] + 1900,
+ month => $time[4] + 1,
+ day => $time[3],
+ hour => $time[2],
+ minute => $time[1],
+ second => int($time[0]),
+ time_zone => $tz,
+ });
+ $dt->set_time_zone(Bugzilla->local_timezone);
+ return $dt->iso8601;
}
sub translate_value {
- my ($self, $field, $value) = @_;
+ my ($self, $field, $value) = @_;
- if (!defined $value) {
- warn("Got undefined value for $field\n");
- $value = '';
- }
+ if (!defined $value) {
+ warn("Got undefined value for $field\n");
+ $value = '';
+ }
- if (ref($value) eq 'ARRAY') {
- return [ map($self->translate_value($field, $_), @$value) ];
- }
+ if (ref($value) eq 'ARRAY') {
+ return [map($self->translate_value($field, $_), @$value)];
+ }
- if (defined $self->map_value($field, $value)) {
- return $self->map_value($field, $value);
- }
-
- if (grep($_ eq $field, USER_FIELDS)) {
- if (defined $self->map_value('user', $value)) {
- return $self->map_value('user', $value);
- }
- }
+ if (defined $self->map_value($field, $value)) {
+ return $self->map_value($field, $value);
+ }
- my $field_obj = $self->bug_fields->{$field};
- if ($field eq 'creation_ts'
- or $field eq 'delta_ts'
- or ($field_obj and
- ($field_obj->type == FIELD_TYPE_DATETIME
- or $field_obj->type == FIELD_TYPE_DATE)))
- {
- $value = trim($value);
- return undef if !$value;
- return $self->parse_date($value);
+ if (grep($_ eq $field, USER_FIELDS)) {
+ if (defined $self->map_value('user', $value)) {
+ return $self->map_value('user', $value);
}
-
- return $value;
+ }
+
+ my $field_obj = $self->bug_fields->{$field};
+ if (
+ $field eq 'creation_ts'
+ or $field eq 'delta_ts'
+ or (
+ $field_obj
+ and
+ ($field_obj->type == FIELD_TYPE_DATETIME or $field_obj->type == FIELD_TYPE_DATE)
+ )
+ )
+ {
+ $value = trim($value);
+ return undef if !$value;
+ return $self->parse_date($value);
+ }
+
+ return $value;
}
sub map_value {
- my ($self, $field, $value) = @_;
- return $self->_value_map->{$field}->{lc($value)};
+ my ($self, $field, $value) = @_;
+ return $self->_value_map->{$field}->{lc($value)};
}
sub _value_map {
- my $self = shift;
- if (!defined $self->{_value_map}) {
- # Lowercase all values to make them case-insensitive.
- my %map;
- my $translation = $self->config('translate_values');
- foreach my $field (keys %$translation) {
- my $value_mapping = $translation->{$field};
- foreach my $value (keys %$value_mapping) {
- $map{$field}->{lc($value)} = $value_mapping->{$value};
- }
- }
- $self->{_value_map} = \%map;
+ my $self = shift;
+ if (!defined $self->{_value_map}) {
+
+ # Lowercase all values to make them case-insensitive.
+ my %map;
+ my $translation = $self->config('translate_values');
+ foreach my $field (keys %$translation) {
+ my $value_mapping = $translation->{$field};
+ foreach my $value (keys %$value_mapping) {
+ $map{$field}->{lc($value)} = $value_mapping->{$value};
+ }
}
- return $self->{_value_map};
+ $self->{_value_map} = \%map;
+ }
+ return $self->{_value_map};
}
#################
@@ -490,386 +498,401 @@ sub _value_map {
#################
sub config {
- my ($self, $var) = @_;
- if (!exists $self->{config}) {
- $self->{config} = $self->read_config;
- }
- return $self->{config}->{$var};
+ my ($self, $var) = @_;
+ if (!exists $self->{config}) {
+ $self->{config} = $self->read_config;
+ }
+ return $self->{config}->{$var};
}
sub config_file_name {
- my $self = shift;
- my $name = $self->name;
- my $dir = bz_locations()->{datadir};
- return "$dir/migrate-$name.cfg"
+ my $self = shift;
+ my $name = $self->name;
+ my $dir = bz_locations()->{datadir};
+ return "$dir/migrate-$name.cfg";
}
sub read_config {
- my ($self) = @_;
- my $file = $self->config_file_name;
- if (!-e $file) {
- $self->write_config();
- ThrowUserError('migrate_config_created', { file => $file });
- }
- open(my $fh, "<", $file) || die "$file: $!";
- my $safe = new Safe;
- $safe->rdo($file);
- my @read_symbols = map($_->{name}, $self->CONFIG_VARS);
- my %config;
- foreach my $var (@read_symbols) {
- my $glob = $safe->varglob($var);
- $config{$var} = $$glob;
- }
- return \%config;
+ my ($self) = @_;
+ my $file = $self->config_file_name;
+ if (!-e $file) {
+ $self->write_config();
+ ThrowUserError('migrate_config_created', {file => $file});
+ }
+ open(my $fh, "<", $file) || die "$file: $!";
+ my $safe = new Safe;
+ $safe->rdo($file);
+ my @read_symbols = map($_->{name}, $self->CONFIG_VARS);
+ my %config;
+ foreach my $var (@read_symbols) {
+ my $glob = $safe->varglob($var);
+ $config{$var} = $$glob;
+ }
+ return \%config;
}
sub write_config {
- my ($self) = @_;
- my $file = $self->config_file_name;
- open(my $fh, ">", $file) || die "$file: $!";
- # Fixed indentation
- local $Data::Dumper::Indent = 1;
- local $Data::Dumper::Quotekeys = 0;
- local $Data::Dumper::Sortkeys = 1;
- foreach my $var ($self->CONFIG_VARS) {
- print $fh "\n", $var->{desc},
- Data::Dumper->Dump([$var->{default}], [$var->{name}]);
- }
- close($fh);
+ my ($self) = @_;
+ my $file = $self->config_file_name;
+ open(my $fh, ">", $file) || die "$file: $!";
+
+ # Fixed indentation
+ local $Data::Dumper::Indent = 1;
+ local $Data::Dumper::Quotekeys = 0;
+ local $Data::Dumper::Sortkeys = 1;
+ foreach my $var ($self->CONFIG_VARS) {
+ print $fh "\n", $var->{desc},
+ Data::Dumper->Dump([$var->{default}], [$var->{name}]);
+ }
+ close($fh);
}
####################################
# Default Implementations of Hooks #
####################################
-sub after_insert {}
-sub before_insert {}
-sub after_read {}
+sub after_insert { }
+sub before_insert { }
+sub after_read { }
#############
# Inserters #
#############
sub insert_users {
- my ($self, $users) = @_;
- foreach my $user (@$users) {
- next if new Bugzilla::User({ name => $user->{login_name} });
- my $generated_password;
- if (!defined $user->{cryptpassword}) {
- $generated_password = lc(generate_random_password());
- $user->{cryptpassword} = $generated_password;
- }
- my $created = Bugzilla::User->create($user);
- print get_text('migrate_user_created',
- { created => $created,
- password => $generated_password }), "\n";
+ my ($self, $users) = @_;
+ foreach my $user (@$users) {
+ next if new Bugzilla::User({name => $user->{login_name}});
+ my $generated_password;
+ if (!defined $user->{cryptpassword}) {
+ $generated_password = lc(generate_random_password());
+ $user->{cryptpassword} = $generated_password;
}
+ my $created = Bugzilla::User->create($user);
+ print get_text('migrate_user_created',
+ {created => $created, password => $generated_password}),
+ "\n";
+ }
}
# XXX This should also insert Classifications.
sub insert_products {
- my ($self, $products) = @_;
- foreach my $product (@$products) {
- my $components = delete $product->{components};
-
- my $created_prod = new Bugzilla::Product({ name => $product->{name} });
- if (!$created_prod) {
- $created_prod = Bugzilla::Product->create($product);
- print get_text('migrate_product_created',
- { created => $created_prod }), "\n";
- }
+ my ($self, $products) = @_;
+ foreach my $product (@$products) {
+ my $components = delete $product->{components};
+
+ my $created_prod = new Bugzilla::Product({name => $product->{name}});
+ if (!$created_prod) {
+ $created_prod = Bugzilla::Product->create($product);
+ print get_text('migrate_product_created', {created => $created_prod}), "\n";
+ }
- foreach my $component (@$components) {
- next if new Bugzilla::Component({ product => $created_prod,
- name => $component->{name} });
- my $created_comp = Bugzilla::Component->create(
- { %$component, product => $created_prod });
- print ' ', get_text('migrate_component_created',
- { comp => $created_comp,
- product => $created_prod }), "\n";
- }
+ foreach my $component (@$components) {
+ next
+ if new Bugzilla::Component({
+ product => $created_prod, name => $component->{name}
+ });
+ my $created_comp
+ = Bugzilla::Component->create({%$component, product => $created_prod});
+ print ' ',
+ get_text('migrate_component_created',
+ {comp => $created_comp, product => $created_prod}),
+ "\n";
}
+ }
}
sub create_custom_fields {
- my $self = shift;
- foreach my $field (keys %{ $self->CUSTOM_FIELDS }) {
- next if new Bugzilla::Field({ name => $field });
- my %values = %{ $self->CUSTOM_FIELDS->{$field} };
- # We set these all here for the dry-run case.
- my $created = { %values, name => $field, custom => 1 };
- if (!$self->dry_run) {
- $created = Bugzilla::Field->create($created);
- }
- print get_text('migrate_field_created', { field => $created }), "\n";
+ my $self = shift;
+ foreach my $field (keys %{$self->CUSTOM_FIELDS}) {
+ next if new Bugzilla::Field({name => $field});
+ my %values = %{$self->CUSTOM_FIELDS->{$field}};
+
+ # We set these all here for the dry-run case.
+ my $created = {%values, name => $field, custom => 1};
+ if (!$self->dry_run) {
+ $created = Bugzilla::Field->create($created);
}
- delete $self->{bug_fields};
+ print get_text('migrate_field_created', {field => $created}), "\n";
+ }
+ delete $self->{bug_fields};
}
sub create_legal_values {
- my ($self, $bugs) = @_;
- my @select_fields = grep($_->is_select, values %{ $self->bug_fields });
-
- # Get all the values in use on all the bugs we're importing.
- my (%values, %product_values);
- foreach my $bug (@$bugs) {
- foreach my $field (@select_fields) {
- my $name = $field->name;
- next if !defined $bug->{$name};
- $values{$name}->{$bug->{$name}} = 1;
- }
- foreach my $field (qw(version target_milestone)) {
- # Fix per-product bug values here, because it's easier than
- # doing it during _insert_bugs.
- if (!defined $bug->{$field} or trim($bug->{$field}) eq '') {
- my $accessor = $field;
- $accessor =~ s/^target_//; $accessor .= "s";
- my $product = Bugzilla::Product->check($bug->{product});
- $bug->{$field} = $product->$accessor->[0]->name;
- next;
- }
- $product_values{$bug->{product}}->{$field}->{$bug->{$field}} = 1;
- }
- }
+ my ($self, $bugs) = @_;
+ my @select_fields = grep($_->is_select, values %{$self->bug_fields});
+ # Get all the values in use on all the bugs we're importing.
+ my (%values, %product_values);
+ foreach my $bug (@$bugs) {
foreach my $field (@select_fields) {
- next if $field->is_abnormal;
- my $name = $field->name;
- foreach my $value (keys %{ $values{$name} }) {
- next if Bugzilla::Field::Choice->type($field)->new({ name => $value });
- Bugzilla::Field::Choice->type($field)->create({ value => $value });
- print get_text('migrate_value_created',
- { field => $field, value => $value }), "\n";
- }
+ my $name = $field->name;
+ next if !defined $bug->{$name};
+ $values{$name}->{$bug->{$name}} = 1;
}
+ foreach my $field (qw(version target_milestone)) {
+
+ # Fix per-product bug values here, because it's easier than
+ # doing it during _insert_bugs.
+ if (!defined $bug->{$field} or trim($bug->{$field}) eq '') {
+ my $accessor = $field;
+ $accessor =~ s/^target_//;
+ $accessor .= "s";
+ my $product = Bugzilla::Product->check($bug->{product});
+ $bug->{$field} = $product->$accessor->[0]->name;
+ next;
+ }
+ $product_values{$bug->{product}}->{$field}->{$bug->{$field}} = 1;
+ }
+ }
+
+ foreach my $field (@select_fields) {
+ next if $field->is_abnormal;
+ my $name = $field->name;
+ foreach my $value (keys %{$values{$name}}) {
+ next if Bugzilla::Field::Choice->type($field)->new({name => $value});
+ Bugzilla::Field::Choice->type($field)->create({value => $value});
+ print get_text('migrate_value_created', {field => $field, value => $value}),
+ "\n";
+ }
+ }
+
+ foreach my $product (keys %product_values) {
+ my $prod_obj = Bugzilla::Product->check($product);
+ foreach my $version (keys %{$product_values{$product}->{version}}) {
+ next if new Bugzilla::Version({product => $prod_obj, name => $version});
+ my $created
+ = Bugzilla::Version->create({product => $prod_obj, value => $version});
+ my $field = $self->bug_fields->{version};
+ print get_text('migrate_value_created',
+ {product => $prod_obj, field => $field, value => $created->name}),
+ "\n";
+ }
+ foreach my $milestone (keys %{$product_values{$product}->{target_milestone}}) {
+ next if new Bugzilla::Milestone({product => $prod_obj, name => $milestone});
+ my $created
+ = Bugzilla::Milestone->create({product => $prod_obj, value => $milestone});
+ my $field = $self->bug_fields->{target_milestone};
+ print get_text('migrate_value_created',
+ {product => $prod_obj, field => $field, value => $created->name}),
+ "\n";
- foreach my $product (keys %product_values) {
- my $prod_obj = Bugzilla::Product->check($product);
- foreach my $version (keys %{ $product_values{$product}->{version} }) {
- next if new Bugzilla::Version({ product => $prod_obj,
- name => $version });
- my $created = Bugzilla::Version->create({ product => $prod_obj,
- value => $version });
- my $field = $self->bug_fields->{version};
- print get_text('migrate_value_created', { product => $prod_obj,
- field => $field,
- value => $created->name }), "\n";
- }
- foreach my $milestone (keys %{ $product_values{$product}->{target_milestone} }) {
- next if new Bugzilla::Milestone({ product => $prod_obj,
- name => $milestone });
- my $created = Bugzilla::Milestone->create(
- { product => $prod_obj, value => $milestone });
- my $field = $self->bug_fields->{target_milestone};
- print get_text('migrate_value_created', { product => $prod_obj,
- field => $field,
- value => $created->name }), "\n";
-
- }
}
+ }
}
sub insert_bugs {
- my ($self, $bugs) = @_;
- my $dbh = Bugzilla->dbh;
- print get_text('migrate_creating_bugs'), "\n";
-
- my $init_statuses = Bugzilla::Status->can_change_to();
- my %allowed_statuses = map { lc($_->name) => 1 } @$init_statuses;
- # Bypass the question of whether or not we can file UNCONFIRMED
- # in any product by simply picking a non-UNCONFIRMED status as our
- # default for bugs that don't have a status specified.
- my $default_status = first { $_->name ne 'UNCONFIRMED' } @$init_statuses;
- # Use the first resolution that's not blank.
- my $default_resolution =
- first { $_->name ne '' }
- @{ $self->bug_fields->{resolution}->legal_values };
-
- # Set the values of any required drop-down fields that aren't set.
- my @standard_drop_downs = grep { !$_->custom and $_->is_select }
- (values %{ $self->bug_fields });
- # Make bug_status get set before resolution.
- @standard_drop_downs = sort { $a->name cmp $b->name } @standard_drop_downs;
- # Cache all statuses for setting the resolution.
- my %statuses = map { lc($_->name) => $_ } Bugzilla::Status->get_all;
-
- my $total = scalar @$bugs;
- my $count = 1;
- foreach my $bug (@$bugs) {
- my $comments = delete $bug->{comments};
- my $history = delete $bug->{history};
- my $attachments = delete $bug->{attachments};
-
- $self->debug($bug, 3);
-
- foreach my $field (@standard_drop_downs) {
- next if $field->is_abnormal;
- my $field_name = $field->name;
- if (!defined $bug->{$field_name}) {
- # If there's a default value for this, then just let create()
- # pick it.
- next if grep($_->is_default, @{ $field->legal_values });
- # Otherwise, pick the first valid value if this is a required
- # field.
- if ($field_name eq 'bug_status') {
- $bug->{bug_status} = $default_status;
- }
- elsif ($field_name eq 'resolution') {
- my $status = $statuses{lc($bug->{bug_status})};
- if (!$status->is_open) {
- $bug->{resolution} = $default_resolution;
- }
- }
- else {
- $bug->{$field_name} = $field->legal_values->[0]->name;
- }
- }
+ my ($self, $bugs) = @_;
+ my $dbh = Bugzilla->dbh;
+ print get_text('migrate_creating_bugs'), "\n";
+
+ my $init_statuses = Bugzilla::Status->can_change_to();
+ my %allowed_statuses = map { lc($_->name) => 1 } @$init_statuses;
+
+ # Bypass the question of whether or not we can file UNCONFIRMED
+ # in any product by simply picking a non-UNCONFIRMED status as our
+ # default for bugs that don't have a status specified.
+ my $default_status = first { $_->name ne 'UNCONFIRMED' } @$init_statuses;
+
+ # Use the first resolution that's not blank.
+ my $default_resolution = first { $_->name ne '' }
+ @{$self->bug_fields->{resolution}->legal_values};
+
+ # Set the values of any required drop-down fields that aren't set.
+ my @standard_drop_downs
+ = grep { !$_->custom and $_->is_select } (values %{$self->bug_fields});
+
+ # Make bug_status get set before resolution.
+ @standard_drop_downs = sort { $a->name cmp $b->name } @standard_drop_downs;
+
+ # Cache all statuses for setting the resolution.
+ my %statuses = map { lc($_->name) => $_ } Bugzilla::Status->get_all;
+
+ my $total = scalar @$bugs;
+ my $count = 1;
+ foreach my $bug (@$bugs) {
+ my $comments = delete $bug->{comments};
+ my $history = delete $bug->{history};
+ my $attachments = delete $bug->{attachments};
+
+ $self->debug($bug, 3);
+
+ foreach my $field (@standard_drop_downs) {
+ next if $field->is_abnormal;
+ my $field_name = $field->name;
+ if (!defined $bug->{$field_name}) {
+
+ # If there's a default value for this, then just let create()
+ # pick it.
+ next if grep($_->is_default, @{$field->legal_values});
+
+ # Otherwise, pick the first valid value if this is a required
+ # field.
+ if ($field_name eq 'bug_status') {
+ $bug->{bug_status} = $default_status;
}
-
- my $product = Bugzilla::Product->check($bug->{product});
-
- # If this isn't a legal starting status, or if the bug has a
- # resolution, then those will have to be set after creating the bug.
- # We make them into objects so that we can normalize their names.
- my ($set_status, $set_resolution);
- if (defined $bug->{resolution}) {
- $set_resolution = Bugzilla::Field::Choice->type('resolution')
- ->new({ name => delete $bug->{resolution} });
+ elsif ($field_name eq 'resolution') {
+ my $status = $statuses{lc($bug->{bug_status})};
+ if (!$status->is_open) {
+ $bug->{resolution} = $default_resolution;
+ }
}
- if (!$allowed_statuses{lc($bug->{bug_status})}) {
- $set_status = new Bugzilla::Status({ name => $bug->{bug_status} });
- # Set the starting status to some status that Bugzilla will
- # accept. We're going to overwrite it immediately afterward.
- $bug->{bug_status} = $default_status;
+ else {
+ $bug->{$field_name} = $field->legal_values->[0]->name;
}
+ }
+ }
- # If we're in dry-run mode, our custom fields haven't been created
- # yet, so we shouldn't try to set them on creation.
- if ($self->dry_run) {
- foreach my $field (keys %{ $self->CUSTOM_FIELDS }) {
- delete $bug->{$field};
- }
- }
+ my $product = Bugzilla::Product->check($bug->{product});
+
+ # If this isn't a legal starting status, or if the bug has a
+ # resolution, then those will have to be set after creating the bug.
+ # We make them into objects so that we can normalize their names.
+ my ($set_status, $set_resolution);
+ if (defined $bug->{resolution}) {
+ $set_resolution = Bugzilla::Field::Choice->type('resolution')
+ ->new({name => delete $bug->{resolution}});
+ }
+ if (!$allowed_statuses{lc($bug->{bug_status})}) {
+ $set_status = new Bugzilla::Status({name => $bug->{bug_status}});
+
+ # Set the starting status to some status that Bugzilla will
+ # accept. We're going to overwrite it immediately afterward.
+ $bug->{bug_status} = $default_status;
+ }
- # File the bug as the reporter.
- my $super_user = Bugzilla->user;
- my $reporter = Bugzilla::User->check($bug->{reporter});
- # Allow the user to file a bug in any product, no matter his current
- # permissions.
- $reporter->{groups} = $super_user->groups;
- Bugzilla->set_user($reporter);
- my $created = Bugzilla::Bug->create($bug);
- $self->debug('Created bug ' . $created->id);
- Bugzilla->set_user($super_user);
-
- if (defined $bug->{creation_ts}) {
- $dbh->do('UPDATE bugs SET creation_ts = ?, delta_ts = ?
+ # If we're in dry-run mode, our custom fields haven't been created
+ # yet, so we shouldn't try to set them on creation.
+ if ($self->dry_run) {
+ foreach my $field (keys %{$self->CUSTOM_FIELDS}) {
+ delete $bug->{$field};
+ }
+ }
+
+ # File the bug as the reporter.
+ my $super_user = Bugzilla->user;
+ my $reporter = Bugzilla::User->check($bug->{reporter});
+
+ # Allow the user to file a bug in any product, no matter his current
+ # permissions.
+ $reporter->{groups} = $super_user->groups;
+ Bugzilla->set_user($reporter);
+ my $created = Bugzilla::Bug->create($bug);
+ $self->debug('Created bug ' . $created->id);
+ Bugzilla->set_user($super_user);
+
+ if (defined $bug->{creation_ts}) {
+ $dbh->do(
+ 'UPDATE bugs SET creation_ts = ?, delta_ts = ?
WHERE bug_id = ?', undef, $bug->{creation_ts},
- $bug->{creation_ts}, $created->id);
- }
- if (defined $bug->{delta_ts}) {
- $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?',
- undef, $bug->{delta_ts}, $created->id);
- }
- # We don't need to send email for imported bugs.
- $dbh->do('UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?',
- undef, $created->id);
-
- # We don't use set_ and update() because that would create
- # a bugs_activity entry that we don't want.
- if ($set_status) {
- $dbh->do('UPDATE bugs SET bug_status = ? WHERE bug_id = ?',
- undef, $set_status->name, $created->id);
- }
- if ($set_resolution) {
- $dbh->do('UPDATE bugs SET resolution = ? WHERE bug_id = ?',
- undef, $set_resolution->name, $created->id);
- }
+ $bug->{creation_ts}, $created->id
+ );
+ }
+ if (defined $bug->{delta_ts}) {
+ $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?',
+ undef, $bug->{delta_ts}, $created->id);
+ }
- $self->_insert_comments($created, $comments);
- $self->_insert_history($created, $history);
- $self->_insert_attachments($created, $attachments);
+ # We don't need to send email for imported bugs.
+ $dbh->do('UPDATE bugs SET lastdiffed = delta_ts WHERE bug_id = ?',
+ undef, $created->id);
- # bugs_fulltext isn't transactional, so if we're in a dry-run we
- # need to delete anything that we put in there.
- if ($self->dry_run) {
- $dbh->do('DELETE FROM bugs_fulltext WHERE bug_id = ?',
- undef, $created->id);
- }
+ # We don't use set_ and update() because that would create
+ # a bugs_activity entry that we don't want.
+ if ($set_status) {
+ $dbh->do('UPDATE bugs SET bug_status = ? WHERE bug_id = ?',
+ undef, $set_status->name, $created->id);
+ }
+ if ($set_resolution) {
+ $dbh->do('UPDATE bugs SET resolution = ? WHERE bug_id = ?',
+ undef, $set_resolution->name, $created->id);
+ }
- if (!$self->verbose) {
- indicate_progress({ current => $count++, every => 5, total => $total });
- }
+ $self->_insert_comments($created, $comments);
+ $self->_insert_history($created, $history);
+ $self->_insert_attachments($created, $attachments);
+
+ # bugs_fulltext isn't transactional, so if we're in a dry-run we
+ # need to delete anything that we put in there.
+ if ($self->dry_run) {
+ $dbh->do('DELETE FROM bugs_fulltext WHERE bug_id = ?', undef, $created->id);
+ }
+
+ if (!$self->verbose) {
+ indicate_progress({current => $count++, every => 5, total => $total});
}
+ }
}
sub _insert_comments {
- my ($self, $bug, $comments) = @_;
- return if !$comments;
- $self->debug(' Inserting comments:', 2);
- foreach my $comment (@$comments) {
- $self->debug($comment, 3);
- my %copy = %$comment;
- # XXX In the future, if we have a Bugzilla::Comment->create, this
- # should use it.
- my $who = Bugzilla::User->check(delete $copy{who});
- $copy{who} = $who->id;
- $copy{bug_id} = $bug->id;
- $self->_do_table_insert('longdescs', \%copy);
- $self->debug(" Inserted comment from " . $who->login, 2);
- }
- $bug->_sync_fulltext( update_comments => 1 );
+ my ($self, $bug, $comments) = @_;
+ return if !$comments;
+ $self->debug(' Inserting comments:', 2);
+ foreach my $comment (@$comments) {
+ $self->debug($comment, 3);
+ my %copy = %$comment;
+
+ # XXX In the future, if we have a Bugzilla::Comment->create, this
+ # should use it.
+ my $who = Bugzilla::User->check(delete $copy{who});
+ $copy{who} = $who->id;
+ $copy{bug_id} = $bug->id;
+ $self->_do_table_insert('longdescs', \%copy);
+ $self->debug(" Inserted comment from " . $who->login, 2);
+ }
+ $bug->_sync_fulltext(update_comments => 1);
}
sub _insert_history {
- my ($self, $bug, $history) = @_;
- return if !$history;
- $self->debug(' Inserting history:', 2);
- foreach my $item (@$history) {
- $self->debug($item, 3);
- my $who = Bugzilla::User->check($item->{who});
- LogActivityEntry($bug->id, $item->{field}, $item->{removed},
- $item->{added}, $who->id, $item->{bug_when});
- $self->debug(" $item->{field} change from " . $who->login, 2);
- }
+ my ($self, $bug, $history) = @_;
+ return if !$history;
+ $self->debug(' Inserting history:', 2);
+ foreach my $item (@$history) {
+ $self->debug($item, 3);
+ my $who = Bugzilla::User->check($item->{who});
+ LogActivityEntry($bug->id, $item->{field}, $item->{removed}, $item->{added},
+ $who->id, $item->{bug_when});
+ $self->debug(" $item->{field} change from " . $who->login, 2);
+ }
}
sub _insert_attachments {
- my ($self, $bug, $attachments) = @_;
- return if !$attachments;
- $self->debug(' Inserting attachments:', 2);
- foreach my $attachment (@$attachments) {
- $self->debug($attachment, 3);
- # Make sure that our pointer is at the beginning of the file,
- # because usually it will be at the end, having just been fully
- # written to.
- if (ref $attachment->{data}) {
- $attachment->{data}->seek(0, SEEK_SET);
- }
-
- my $submitter = Bugzilla::User->check(delete $attachment->{submitter});
- my $super_user = Bugzilla->user;
- # Make sure the submitter can attach this attachment no matter what.
- $submitter->{groups} = $super_user->groups;
- Bugzilla->set_user($submitter);
- my $created =
- Bugzilla::Attachment->create({ %$attachment, bug => $bug });
- $self->debug(' Attachment ' . $created->description . ' from '
- . $submitter->login, 2);
- Bugzilla->set_user($super_user);
+ my ($self, $bug, $attachments) = @_;
+ return if !$attachments;
+ $self->debug(' Inserting attachments:', 2);
+ foreach my $attachment (@$attachments) {
+ $self->debug($attachment, 3);
+
+ # Make sure that our pointer is at the beginning of the file,
+ # because usually it will be at the end, having just been fully
+ # written to.
+ if (ref $attachment->{data}) {
+ $attachment->{data}->seek(0, SEEK_SET);
}
+
+ my $submitter = Bugzilla::User->check(delete $attachment->{submitter});
+ my $super_user = Bugzilla->user;
+
+ # Make sure the submitter can attach this attachment no matter what.
+ $submitter->{groups} = $super_user->groups;
+ Bugzilla->set_user($submitter);
+ my $created = Bugzilla::Attachment->create({%$attachment, bug => $bug});
+ $self->debug(
+ ' Attachment ' . $created->description . ' from ' . $submitter->login, 2);
+ Bugzilla->set_user($super_user);
+ }
}
sub _do_table_insert {
- my ($self, $table, $hash) = @_;
- my @fields = keys %$hash;
- my @questions = ('?') x @fields;
- my @values = map { $hash->{$_} } @fields;
- my $field_sql = join(',', @fields);
- my $question_sql = join(',', @questions);
- Bugzilla->dbh->do("INSERT INTO $table ($field_sql) VALUES ($question_sql)",
- undef, @values);
+ my ($self, $table, $hash) = @_;
+ my @fields = keys %$hash;
+ my @questions = ('?') x @fields;
+ my @values = map { $hash->{$_} } @fields;
+ my $field_sql = join(',', @fields);
+ my $question_sql = join(',', @questions);
+ Bugzilla->dbh->do("INSERT INTO $table ($field_sql) VALUES ($question_sql)",
+ undef, @values);
}
######################
@@ -877,11 +900,11 @@ sub _do_table_insert {
######################
sub _canonical_name {
- my ($module) = @_;
- $module =~ s{::}{/}g;
- $module = basename($module);
- $module =~ s/\.pm$//g;
- return $module;
+ my ($module) = @_;
+ $module =~ s{::}{/}g;
+ $module = basename($module);
+ $module =~ s/\.pm$//g;
+ return $module;
}
1;