diff options
Diffstat (limited to 'extensions/TrackingFlags/bin/migrate_tracking_flags.pl')
-rwxr-xr-x | extensions/TrackingFlags/bin/migrate_tracking_flags.pl | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/extensions/TrackingFlags/bin/migrate_tracking_flags.pl b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl new file mode 100755 index 000000000..06b3596c4 --- /dev/null +++ b/extensions/TrackingFlags/bin/migrate_tracking_flags.pl @@ -0,0 +1,316 @@ +#!/usr/bin/perl -w +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +# Migrate old custom field based tracking flags to the new +# table based tracking flags + +use strict; +use warnings; + +use FindBin '$RealBin'; +use lib "$RealBin/../../.."; +use lib "$RealBin/../../../lib"; +use lib "$RealBin/../lib"; + +BEGIN { + use Bugzilla; + Bugzilla->extensions; +} + +use Bugzilla::Constants; +use Bugzilla::Field; +use Bugzilla::Product; +use Bugzilla::Component; +use Bugzilla::Extension::BMO::Data; +use Bugzilla::Install::Util qw(indicate_progress); + +use Bugzilla::Extension::TrackingFlags::Constants; +use Bugzilla::Extension::TrackingFlags::Flag; +use Bugzilla::Extension::TrackingFlags::Flag::Bug; +use Bugzilla::Extension::TrackingFlags::Flag::Value; +use Bugzilla::Extension::TrackingFlags::Flag::Visibility; + +use Getopt::Long; +use Data::Dumper; + +Bugzilla->usage_mode(USAGE_MODE_CMDLINE); + +my ($dry_run, $trace) = (0, 0); +GetOptions( + "dry-run" => \$dry_run, + "trace" => \$trace, +) or exit; + +my $dbh = Bugzilla->dbh; + +$dbh->{TraceLevel} = 1 if $trace; + +my %product_cache; +my %component_cache; + +sub migrate_flag_visibility { + my ($new_flag, $products) = @_; + + # Create product/component visibility + foreach my $prod_name (keys %$products) { + $product_cache{$prod_name} ||= Bugzilla::Product->new({ name => $prod_name }); + if (!$product_cache{$prod_name}) { + warn "No such product $prod_name\n"; + next; + } + + # If no components specified then we do Product/__any__ + # otherwise, we enter an entry for each Product/Component + my $components = $products->{$prod_name}; + if (!@$components) { + Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ + tracking_flag_id => $new_flag->flag_id, + product_id => $product_cache{$prod_name}->id, + component_id => undef + }); + } + else { + foreach my $comp_name (@$components) { + my $comp_matches = []; + # If the component is a regexp, we need to find all components + # matching the regex and insert each individually + if (ref $comp_name eq 'Regexp') { + my $comp_re = $comp_name; + $comp_re =~ s/\?\-xism://; + $comp_re =~ s/\(//; + $comp_re =~ s/\)//; + $comp_matches = $dbh->selectcol_arrayref( + 'SELECT components.name FROM components + WHERE components.product_id = ? + AND ' . $dbh->sql_regexp('components.name', $dbh->quote($comp_re)) . ' + ORDER BY components.name', + undef, + $product_cache{$prod_name}->id); + } + else { + $comp_matches = [ $comp_name ]; + } + + foreach my $comp_match (@$comp_matches) { + $component_cache{"${prod_name}:${comp_match}"} + ||= Bugzilla::Component->new({ name => $comp_match, + product => $product_cache{$prod_name} }); + if (!$component_cache{"${prod_name}:${comp_match}"}) { + warn "No such product $prod_name and component $comp_match\n"; + next; + } + + Bugzilla::Extension::TrackingFlags::Flag::Visibility->create({ + tracking_flag_id => $new_flag->flag_id, + product_id => $product_cache{$prod_name}->id, + component_id => $component_cache{"${prod_name}:${comp_match}"}->id, + }); + } + } + } + } +} + +sub migrate_flag_values { + my ($new_flag, $field) = @_; + + print "Migrating flag values..."; + + my %blocking_trusted_requesters + = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_requesters}; + my %blocking_trusted_setters + = %{$Bugzilla::Extension::BMO::Data::blocking_trusted_setters}; + my %status_trusted_wanters + = %{$Bugzilla::Extension::BMO::Data::status_trusted_wanters}; + my %status_trusted_setters + = %{$Bugzilla::Extension::BMO::Data::status_trusted_setters}; + + my %group_cache; + foreach my $value (@{ $field->legal_values }) { + my $group_name = 'everyone'; + + if ($field->name =~ /^cf_(blocking|tracking)_/) { + if ($value->name ne '---' && $value->name !~ '\?$') { + $group_name = get_setter_group($field->name, \%blocking_trusted_setters); + } + if ($value->name eq '?') { + $group_name = get_setter_group($field->name, \%blocking_trusted_requesters); + } + } elsif ($field->name =~ /^cf_status_/) { + if ($value->name eq 'wanted') { + $group_name = get_setter_group($field->name, \%status_trusted_wanters); + } elsif ($value->name ne '---' && $value->name ne '?') { + $group_name = get_setter_group($field->name, \%status_trusted_setters); + } + } + + $group_cache{$group_name} ||= Bugzilla::Group->new({ name => $group_name }); + $group_cache{$group_name} || die "Setter group '$group_name' does not exist"; + + Bugzilla::Extension::TrackingFlags::Flag::Value->create({ + tracking_flag_id => $new_flag->flag_id, + value => $value->name, + setter_group_id => $group_cache{$group_name}->id, + sortkey => $value->sortkey, + is_active => $value->is_active + }); + } + + print "done.\n"; +} + +sub get_setter_group { + my ($field, $trusted) = @_; + my $setter_group = $trusted->{'_default'} || ""; + foreach my $dfield (keys %$trusted) { + if ($field =~ $dfield) { + $setter_group = $trusted->{$dfield}; + } + } + return $setter_group; +} + +sub migrate_flag_bugs { + my ($new_flag, $field) = @_; + + print "Migrating bug values..."; + + my $bugs = $dbh->selectall_arrayref("SELECT bug_id, " . $field->name . " + FROM bugs + WHERE " . $field->name . " != '---' + ORDER BY bug_id"); + local $| = 1; + my $count = 1; + my $total = scalar @$bugs; + foreach my $row (@$bugs) { + my ($id, $value) = @$row; + indicate_progress({ current => $count++, total => $total, every => 25 }); + Bugzilla::Extension::TrackingFlags::Flag::Bug->create({ + tracking_flag_id => $new_flag->flag_id, + bug_id => $id, + value => $value, + + }); + } + + print "done.\n"; +} + +sub migrate_flag_activity { + my ($new_flag, $field) = @_; + + print "Migating flag activity..."; + + my $new_field = Bugzilla::Field->new({ name => $new_flag->name }); + $dbh->do("UPDATE bugs_activity SET fieldid = ? WHERE fieldid = ?", + undef, $new_field->id, $field->id); + + print "done.\n"; +} + +sub do_migration { + my $bmo_tracking_flags = $Bugzilla::Extension::BMO::Data::cf_visible_in_products; + my $bmo_project_flags = $Bugzilla::Extension::BMO::Data::cf_project_flags; + my $bmo_disabled_flags = $Bugzilla::Extension::BMO::Data::cf_disabled_flags; + + my $fields = Bugzilla::Field->match({ custom => 1, + type => FIELD_TYPE_SINGLE_SELECT }); + + my @drop_columns; + foreach my $field (@$fields) { + next if $field->name !~ /^cf_(blocking|tracking|status)_/; + + foreach my $field_re (keys %$bmo_tracking_flags) { + next if $field->name !~ $field_re; + + # Create the new tracking flag if not exists + my $new_flag + = Bugzilla::Extension::TrackingFlags::Flag->new({ name => $field->name }); + + next if $new_flag; + + print "----------------------------------\n" . + "Migrating custom tracking field " . $field->name . "...\n"; + + my $new_flag_name = $field->name . "_new"; # Temporary name til we delete the old + + my $type = grep($field->name =~ $_, @$bmo_project_flags) + ? 'project' + : 'tracking'; + + my $is_active = grep($_ eq $field->name, @$bmo_disabled_flags) ? 0 : 1; + + $new_flag = Bugzilla::Extension::TrackingFlags::Flag->create({ + name => $new_flag_name, + description => $field->description, + type => $type, + sortkey => $field->sortkey, + is_active => $is_active, + enter_bug => $field->enter_bug, + }); + + migrate_flag_visibility($new_flag, $bmo_tracking_flags->{$field_re}); + + migrate_flag_values($new_flag, $field); + + migrate_flag_bugs($new_flag, $field); + + migrate_flag_activity($new_flag, $field); + + push(@drop_columns, $field->name); + + # Remove the old flag entry from fielddefs + $dbh->do("DELETE FROM fielddefs WHERE name = ?", + undef, $field->name); + + # Rename the new flag + $dbh->do("UPDATE fielddefs SET name = ? WHERE name = ?", + undef, $field->name, $new_flag_name); + + $new_flag->set_name($field->name); + $new_flag->update; + + # more than one regex could possibly match but we only want the first one + last; + } + } + + # Drop each custom flag's value table and the column from the bz schema object + if (!$dry_run && @drop_columns) { + print "Dropping value tables and updating bz schema object...\n"; + + foreach my $column (@drop_columns) { + # Drop the values table + $dbh->bz_drop_table($column); + + # Drop the bugs table column from the bz schema object + $dbh->_bz_real_schema->delete_column('bugs', $column); + $dbh->_bz_store_real_schema; + } + + # Do the one alter table to drop all columns at once + $dbh->do("ALTER TABLE bugs DROP COLUMN " . join(", DROP COLUMN ", @drop_columns)); + } +} + +# Start Main + +eval { + if ($dry_run) { + print "** dry run : no changes to the database will be made **\n"; + $dbh->bz_start_transaction(); + } + print "Starting migration...\n"; + do_migration(); + $dbh->bz_rollback_transaction() if $dry_run; + print "All done!\n"; +}; +if ($@) { + $dbh->bz_rollback_transaction() if $dry_run; + die "$@" if $@; +} |