diff options
Diffstat (limited to 'extensions/TrackingFlags')
8 files changed, 408 insertions, 230 deletions
diff --git a/extensions/TrackingFlags/Extension.pm b/extensions/TrackingFlags/Extension.pm index b79faef61..be9cb9309 100644 --- a/extensions/TrackingFlags/Extension.pm +++ b/extensions/TrackingFlags/Extension.pm @@ -65,7 +65,9 @@ sub template_before_process { $vars->{'tracking_flag_types'} = FLAG_TYPES; } - elsif ($file eq 'bug/edit.html.tmpl'|| $file eq 'bug/show.xml.tmpl') { + elsif ($file eq 'bug/edit.html.tmpl'|| $file eq 'bug/show.xml.tmpl' + || $file eq 'email/bugmail.html.tmpl' || $file eq 'email/bugmail.txt.tmpl') + { # note: bug/edit.html.tmpl doesn't support multiple bugs my $bug = exists $vars->{'bugs'} ? $vars->{'bugs'}[0] : $vars->{'bug'}; @@ -306,8 +308,9 @@ sub active_custom_fields { my @tracking_flags; if ($product) { - my $params = { product_id => $product->id }; + $params->{'product_id'} = $product->id; $params->{'component_id'} = $component->id if $component; + $params->{'is_active'} = 1; @tracking_flags = @{ Bugzilla::Extension::TrackingFlags::Flag->match($params) }; } else { @@ -546,4 +549,33 @@ sub mailer_before_send { } } +# Purpose: generically handle generating pretty blocking/status "flags" from +# custom field names. +sub quicksearch_map { + my ($self, $args) = @_; + my $map = $args->{'map'}; + + foreach my $name (keys %$map) { + if ($name =~ /^cf_(blocking|tracking|status)_([a-z]+)?(\d+)?$/) { + my $type = $1; + my $product = $2; + my $version = $3; + + if ($version) { + $version = join('.', split(//, $version)); + } + + my $pretty_name = $type; + if ($product) { + $pretty_name .= "-" . $product; + } + if ($version) { + $pretty_name .= $version; + } + + $map->{$pretty_name} = $name; + } + } +} + __PACKAGE__->NAME; 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 $@; +} diff --git a/extensions/TrackingFlags/lib/Constants.pm b/extensions/TrackingFlags/lib/Constants.pm index 57b2873e3..b6813c3c2 100644 --- a/extensions/TrackingFlags/lib/Constants.pm +++ b/extensions/TrackingFlags/lib/Constants.pm @@ -14,27 +14,34 @@ our @EXPORT = qw( FLAG_TYPES ); -use constant FLAG_TYPES => [ - { - name => 'tracking', - description => 'Tracking Flags', - collapsed => 1, - }, - { - name => 'project', - description => 'Project Flags', - collapsed => 0, - }, - { - name => 'blocking', - description => 'Blocking Flags', - collapsed => 1, - }, - { - name => 'b2g', - description => 'B2G Flags', - collapsed => 1, - } -]; +sub FLAG_TYPES { + my @flag_types = ( + { + name => 'project', + description => 'Project Flags', + collapsed => 0, + sortkey => 0 + }, + { + name => 'tracking', + description => 'Tracking Flags', + collapsed => 1, + sortkey => 1 + }, + { + name => 'blocking', + description => 'Blocking Flags', + collapsed => 1, + sortkey => 2 + }, + { + name => 'b2g', + description => 'B2G Flags', + collapsed => 1, + sortkey => 3 + }, + ); + return [ sort { $a->{'sortkey'} <=> $b->{'sortkey'} } @flag_types ]; +} 1; diff --git a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags.html.tmpl b/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags.html.tmpl new file mode 100644 index 000000000..b41e1619f --- /dev/null +++ b/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags.html.tmpl @@ -0,0 +1,29 @@ +[%# 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. + #%] + +[% RETURN IF NOT tracking_flags.size %] +<td> + <table class="tracking_flags"> + [% FOREACH type = tracking_flag_types %] + [% flag_list = [] %] + [% FOREACH flag = tracking_flags %] + [% flag_list.push(flag) IF flag.flag_type == type.name %] + [% END %] + [% IF flag_list.size %] + <tr> + <th style="text-align:right"> + [% type.description FILTER html %]: + </th> + </tr> + [% INCLUDE bug/tracking_flags.html.tmpl + flag_list = flag_list + new_bug = 1 %] + [% END %] + [% END %] + </table> +</td> diff --git a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags_end.html.tmpl b/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags_end.html.tmpl deleted file mode 100644 index 2a90cbfe3..000000000 --- a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-bug_flags_end.html.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -[%# 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. - #%] - -[% RETURN IF NOT tracking_flags.size %] - -[% FOREACH type = tracking_flag_types %] - [% NEXT IF type.name == 'tracking' || type.name == 'project' %] - [% flag_list = [] %] - [% FOREACH flag = tracking_flags %] - [% flag_list.push(flag) IF flag.flag_type == type.name %] - [% END %] - [% IF flag_list.size %] - <tr> - <td> - <table class="tracking_flags"> - <tr> - <th> - [% type.description FILTER html %]: - </th> - </tr> - [% INCLUDE bug/tracking_flags.html.tmpl - flag_list = flag_list - new_bug = 1 %] - </table> - </td> - </tr> - [% END %] -[% END %] diff --git a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-project_flags_end.html.tmpl b/extensions/TrackingFlags/template/en/default/hook/bug/create/create-project_flags_end.html.tmpl deleted file mode 100644 index 662bc26ee..000000000 --- a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-project_flags_end.html.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -[%# 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. - #%] - -[% RETURN IF NOT tracking_flags.size %] - -[% flag_list = [] %] -[% FOREACH flag = tracking_flags %] - [% NEXT IF flag.flag_type != 'project' %] - [% flag_list.push(flag) %] -[% END %] -[% INCLUDE bug/tracking_flags.html.tmpl - flag_list = flag_list - new_bug = 1 %] diff --git a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-tracking_flags_end.html.tmpl b/extensions/TrackingFlags/template/en/default/hook/bug/create/create-tracking_flags_end.html.tmpl deleted file mode 100644 index 69827a87a..000000000 --- a/extensions/TrackingFlags/template/en/default/hook/bug/create/create-tracking_flags_end.html.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -[%# 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. - #%] - -[% RETURN IF NOT tracking_flags.size %] - -[% flag_list = [] %] -[% FOREACH flag = tracking_flags %] - [% NEXT IF flag.flag_type != 'tracking' %] - [% flag_list.push(flag) %] -[% END %] -[% INCLUDE bug/tracking_flags.html.tmpl - flag_list = flag_list - new_bug = 1 %] diff --git a/extensions/TrackingFlags/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl b/extensions/TrackingFlags/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl index 697db75ce..e0411b512 100644 --- a/extensions/TrackingFlags/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl +++ b/extensions/TrackingFlags/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl @@ -6,145 +6,8 @@ # defined by the Mozilla Public License, v. 2.0. #%] -[%# Old style custom field based tracking flags %] -[% old_tracking_flags = [] %] -[% old_project_flags = [] %] -[% FOREACH field = Bugzilla.active_custom_fields(product=>bug.product_obj,component=>bug.component_obj,type=>2) %] - [% NEXT IF field.type == constants.FIELD_TYPE_EXTENSION %] - [% NEXT IF NOT user.id AND bug.${field.name} == "---" %] - [% NEXT IF cf_flag_disabled(field.name, bug) %] - [% IF cf_is_project_flag(field.name) %] - [% old_project_flags.push(field) %] - [% ELSE %] - [% old_tracking_flags.push(field) %] - [% END %] -[% END %] - -[%# Add in the new tracking flags that are type tracking or project %] -[% new_tracking_flags = [] %] -[% new_project_flags = [] %] -[% IF tracking_flags.size %] - [% FOREACH flag = tracking_flags %] - [% IF flag.flag_type == 'tracking' %] - [% new_tracking_flags.push(flag) %] - [% END %] - [% IF flag.flag_type == 'project' %] - [% new_project_flags.push(flag) %] - [% END %] - [% END %] -[% END %] - -[% IF old_project_flags.size || new_project_flags.size %] - <tr> - <td class="field_label"> - <label>Project Flags:</label> - </td> - <td> - [% IF bug.check_can_change_field('flagtypes.name', 0, 1) %] - <table class="tracking_flags"> - [% FOREACH field = old_project_flags %] - [% NEXT IF NOT user.id AND field.value == "---" %] - <tr id="row_[% field.name FILTER js %]"> - <td> - <label for="[% field.name FILTER html %]"> - [% field_descs.${field.name} FILTER html %]: - </label> - </td> - <td> - [% PROCESS bug/field.html.tmpl value = bug.${field.name} - editable = user.id - no_tds = 1 %] - [% IF user.id %] - <span id="ro_[% field.name FILTER html %]" class="bz_default_hidden"> - [% bug.${field.name} FILTER html %] - </span> - [% END %] - </td> - </tr> - [% END %] - [% INCLUDE bug/tracking_flags.html.tmpl - flag_list = new_project_flags %] - </table> - [% ELSE %] - [% FOREACH field = old_project_flags %] - [% NEXT IF bug.${field.name} == "---" %] - [% field_descs.${field.name} FILTER html %]: [% bug.${field.name} FILTER html %]<br> - [% END %] - [% FOREACH flag = project_flags %] - [% NEXT IF flag.bug_flag.value == '---' %] - [% flag.description FILTER html %]: [% flag.bug_flag.value FILTER html %]<br> - [% END %] - [% END %] - </td> - </tr> -[% END %] - -[% IF old_tracking_flags.size || new_tracking_flags.size %] - <tr> - <td class="field_label"> - <label>Tracking Flags:</label> - </td> - <td> - [% IF bug.check_can_change_field('flagtypes.name', 0, 1) %] - [% IF user.id %] - <span id="edit_tracking_flags_action"> - (<a href="#" name="tracking" class="edit_tracking_flags_link">edit</a>) - </span> - [% END %] - <table class="tracking_flags"> - [% FOREACH field = old_tracking_flags %] - [% NEXT IF NOT user.id AND field.value == "---" %] - <tr id="row_[% field.name FILTER js %]"> - <td> - <label for="[% field.name FILTER html %]"> - [% field_descs.${field.name} FILTER html %]: - </label> - </td> - <td> - [% PROCESS bug/field.html.tmpl - value = bug.${field.name} - editable = user.id - no_tds = 1 - %] - [% IF user.id %] - <span id="ro_[% field.name FILTER html %]" class="bz_default_hidden"> - [% bug.${field.name} FILTER html %] - </span> - [% END %] - </td> - </tr> - [% END %] - [% INCLUDE bug/tracking_flags.html.tmpl - flag_list = new_tracking_flags %] - </table> - [% ELSE %] - [% FOREACH field = old_tracking_flags %] - [% NEXT IF bug.${field.name} == "---" %] - [% field_descs.${field.name} FILTER html %]: [% bug.${field.name} FILTER html %]<br> - [% END %] - [% FOREACH flag = new_tracking_flags %] - [% NEXT IF flag.status == '---' %] - [% flag.description FILTER html %]: [% flag.bug_flag.value FILTER html %]<br> - [% END %] - [% END %] - </td> - </tr> - <script type="text/javascript"> - TrackingFlags.flags['tracking'] = {}; - [% FOREACH field = old_tracking_flags %] - TrackingFlags.flags['tracking']['[% field.name FILTER js %]'] = '[% bug.${field.name} FILTER js %]'; - [% END %] - [% FOREACH flag = new_tracking_flags %] - TrackingFlags.flags['tracking']['[% flag.name FILTER js %]'] = '[% flag.bug_flag.value FILTER js %]'; - [% END %] - TrackingFlags.types.push('tracking'); - </script> -[% END %] - -[%# Last, display any new style flags that are not type tracking or project %] [% IF tracking_flags.size %] [% FOREACH type = tracking_flag_types %] - [% NEXT IF type.name == 'tracking' || type.name == 'project' %] [% flag_list = [] %] [% FOREACH flag = tracking_flags %] [% flag_list.push(flag) IF flag.flag_type == type.name %] |