summaryrefslogtreecommitdiffstats
path: root/extensions/TrackingFlags/bin/migrate_tracking_flags.pl
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/TrackingFlags/bin/migrate_tracking_flags.pl')
-rwxr-xr-xextensions/TrackingFlags/bin/migrate_tracking_flags.pl316
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 $@;
+}