diff options
-rwxr-xr-x | Bugzilla/Bug.pm | 2 | ||||
-rw-r--r-- | Bugzilla/Flag.pm | 88 |
2 files changed, 87 insertions, 3 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 97e6042be..65606e3f6 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -1748,7 +1748,7 @@ sub _validate_attribute { my @valid_attributes = ( # Miscellaneous properties and methods. - qw(error groups + qw(error groups product_id component_id longdescs milestoneurl attachments isopened isunconfirmed flag_types num_attachment_flag_types diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 8b09102d1..7888640f9 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -520,7 +520,10 @@ sub process { AND i.type_id IS NULL", undef, $bug_id); - foreach my $flag_id (@$flag_ids) { clear($flag_id, $bug, $attachment) } + foreach my $flag_id (@$flag_ids) { + my $is_retargetted = retarget($flag_id, $bug, $attachment); + clear($flag_id, $bug, $attachment) unless $is_retargetted; + } $flag_ids = $dbh->selectcol_arrayref( "SELECT DISTINCT flags.id @@ -532,7 +535,10 @@ sub process { AND (bugs.component_id = e.component_id OR e.component_id IS NULL)", undef, $bug_id); - foreach my $flag_id (@$flag_ids) { clear($flag_id, $bug, $attachment) } + foreach my $flag_id (@$flag_ids) { + my $is_retargetted = retarget($flag_id, $bug, $attachment); + clear($flag_id, $bug, $attachment) unless $is_retargetted; + } # Take a snapshot of flags after changes. my @new_summaries = snapshot($bug_id, $attach_id); @@ -759,6 +765,84 @@ sub modify { =over +=item C<retarget($flag_id, $bug, $attachment)> + +Change the type of the flag, if possible. The new flag type must have +the same name as the current flag type, must exist in the product and +attachment the bug is in, and the current settings of the flag must pass +validation. If no such flag type can be found, the type remains unchanged. + +Retargetting flags is a good way to keep flags when moving bugs from one +product where a flag type is available to another product where the flag +type is unavailable, but another flag type having the same name exists. +Most of the time, if they have the same name, this means that they have +the same meaning, but with different settings. + +=back + +=cut + +sub retarget { + my ($flag_id, $bug, $attachment) = @_; + my $dbh = Bugzilla->dbh; + + my $flag = new Bugzilla::Flag($flag_id); + # We are looking for flagtypes having the same name as the flagtype + # to which the current flag belongs, and being in the new product and + # component of the bug. + my $flagtypes = Bugzilla::FlagType::match( + {'name' => $flag->name, + 'target_type' => $attachment ? 'attachment' : 'bug', + 'is_active' => 1, + 'product_id' => $bug->product_id, + 'component_id' => $bug->component_id}); + + # If we found no flagtype, the flag will be deleted. + return 0 unless scalar(@$flagtypes); + + # If we found at least one, change the type of the flag, + # assuming the setter/requester is allowed to set/request flags + # belonging to this flagtype. + my $is_retargetted = 0; + foreach my $flagtype (@$flagtypes) { + # Get the number of flags of this type already set for this target. + my $has_flags = count( + { 'type_id' => $flag->type->id, + 'target_type' => $attachment ? 'attachment' : 'bug', + 'bug_id' => $bug->bug_id, + 'attach_id' => $attachment ? $attachment->id : undef }); + + # Do not create a new flag of this type if this flag type is + # not multiplicable and already has a flag set. + next if (!$flag->type->is_multiplicable && $has_flags); + + # Check user privileges. + my $error_mode_cache = Bugzilla->error_mode; + Bugzilla->error_mode(ERROR_MODE_DIE); + eval { + my $requestee = $flag->requestee ? [$flag->requestee->login] : []; + my $is_private = $attachment ? $attachment->isprivate : 0; + _validate($flag, $flag->type, $flag->status, $flag->setter, + $requestee, $is_private); + }; + Bugzilla->error_mode($error_mode_cache); + # If the validation failed, then we cannot use this flagtype. + next if ($@); + + # Checks are successful, we can retarget the flag to this flagtype. + $dbh->do('UPDATE flags SET type_id = ? WHERE id = ?', + undef, ($flagtype->id, $flag->id)); + + $is_retargetted = 1; + last; + } + return $is_retargetted; +} + +=pod + +=over + =item C<clear($id, $bug, $attachment)> Remove a flag from the DB. |