diff options
Diffstat (limited to 'Bugzilla')
-rwxr-xr-x | Bugzilla/Bug.pm | 113 | ||||
-rw-r--r-- | Bugzilla/Product.pm | 70 |
2 files changed, 182 insertions, 1 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index bfd110ad0..e9274466d 100755 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -498,7 +498,29 @@ sub update { my $old_bug = $self->new($self->id); my $changes = $self->SUPER::update(@_); - + + my %old_groups = map {$_->id => $_} @{$old_bug->groups_in}; + my %new_groups = map {$_->id => $_} @{$self->groups_in}; + my ($removed_gr, $added_gr) = diff_arrays([keys %old_groups], + [keys %new_groups]); + if (scalar @$removed_gr || scalar @$added_gr) { + if (@$removed_gr) { + my $qmarks = join(',', ('?') x @$removed_gr); + $dbh->do("DELETE FROM bug_group_map + WHERE bug_id = ? AND group_id IN ($qmarks)", undef, + $self->id, @$removed_gr); + } + my $sth_insert = $dbh->prepare( + 'INSERT INTO bug_group_map (bug_id, group_id) VALUES (?,?)'); + foreach my $gid (@$added_gr) { + $sth_insert->execute($self->id, $gid); + } + my @removed_names = map { $old_groups{$_}->name } @$removed_gr; + my @added_names = map { $new_groups{$_}->name } @$added_gr; + $changes->{'bug_group'} = [join(', ', @removed_names), + join(', ', @added_names)]; + } + foreach my $comment (@{$self->{added_comments} || []}) { my $columns = join(',', keys %$comment); my @values = values %$comment; @@ -1599,6 +1621,26 @@ sub set_product { $self->set_target_milestone($tm_name); } + if ($product_changed) { + # Remove groups that aren't valid in the new product. This will also + # have the side effect of removing the bug from groups that aren't + # active anymore. + # + # We copy this array because the original array is modified while we're + # working, and that confuses "foreach". + my @current_groups = @{$self->groups_in}; + foreach my $group (@current_groups) { + if (!grep($group->id == $_->id, @{$product->groups_valid})) { + $self->remove_group($group); + } + } + + # Make sure the bug is in all the mandatory groups for the new product. + foreach my $group (@{$product->groups_mandatory_for(Bugzilla->user)}) { + $self->add_group($group); + } + } + # XXX This is temporary until all of process_bug uses update(); return $product_changed; } @@ -1756,6 +1798,75 @@ sub modify_keywords { return $any_changes; } +sub add_group { + my ($self, $group) = @_; + # Invalid ids are silently ignored. (We can't tell people whether + # or not a group exists.) + $group = new Bugzilla::Group($group) unless ref $group; + return unless $group; + + # Make sure that bugs in this product can actually be restricted + # to this group. + grep($group->id == $_->id, @{$self->product_obj->groups_valid}) + || ThrowUserError('group_invalid_restriction', + { product => $self->product, group_id => $group->id }); + + # OtherControl people can add groups only during a product change, + # and only when the group is not NA for them. + if (!Bugzilla->user->in_group($group->name)) { + my $controls = $self->product_obj->group_controls->{$group->id}; + if (!$self->{_old_product_name} + || $controls->{othercontrol} == CONTROLMAPNA) + { + ThrowUserError('group_change_denied', + { bug => $self, group_id => $group->id }); + } + } + + my $current_groups = $self->groups_in; + if (!grep($group->id == $_->id, @$current_groups)) { + push(@$current_groups, $group); + } +} + +sub remove_group { + my ($self, $group) = @_; + $group = new Bugzilla::Group($group) unless ref $group; + return unless $group; + + # First, check if this is a valid group for this product. + # You can *always* remove a group that is not valid for this product, so + # we don't do any other checks if that's the case. (set_product does this.) + # + # This particularly happens when isbuggroup is no longer 1, and we're + # moving a bug to a new product. + if (grep($_->id == $group->id, @{$self->product_obj->groups_valid})) { + my $controls = $self->product_obj->group_controls->{$group->id}; + + # Nobody can ever remove a Mandatory group. + if ($controls->{membercontrol} == CONTROLMAPMANDATORY) { + ThrowUserError('group_invalid_removal', + { product => $self->product, group_id => $group->id, + bug => $self }); + } + + # OtherControl people can remove groups only during a product change, + # and only when they are non-Mandatory and non-NA. + if (!Bugzilla->user->in_group($group->name)) { + if (!$self->{_old_product_name} + || $controls->{othercontrol} == CONTROLMAPMANDATORY + || $controls->{othercontrol} == CONTROLMAPNA) + { + ThrowUserError('group_change_denied', + { bug => $self, group_id => $group->id }); + } + } + } + + my $current_groups = $self->groups_in; + @$current_groups = grep { $_->id != $group->id } @$current_groups; +} + ##################################################################### # Instance Accessors ##################################################################### diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm index 728bd0652..45c489973 100644 --- a/Bugzilla/Product.pm +++ b/Bugzilla/Product.pm @@ -21,6 +21,7 @@ package Bugzilla::Product; use Bugzilla::Version; use Bugzilla::Milestone; +use Bugzilla::Constants; use Bugzilla::Util; use Bugzilla::Group; use Bugzilla::Error; @@ -101,6 +102,39 @@ sub group_controls { return $self->{group_controls}; } +sub groups_mandatory_for { + my ($self, $user) = @_; + my $groups = $user->groups_as_string; + my $mandatory = CONTROLMAPMANDATORY; + # For membercontrol we don't check group_id IN, because if membercontrol + # is Mandatory, the group is Mandatory for everybody, regardless of their + # group membership. + my $ids = Bugzilla->dbh->selectcol_arrayref( + "SELECT group_id FROM group_control_map + WHERE product_id = ? + AND (membercontrol = $mandatory + OR (othercontrol = $mandatory + AND group_id NOT IN ($groups)))", + undef, $self->id); + return Bugzilla::Group->new_from_list($ids); +} + +sub groups_valid { + my ($self) = @_; + return $self->{groups_valid} if defined $self->{groups_valid}; + + # Note that we don't check OtherControl below, because there is no + # valid NA/* combination. + my $ids = Bugzilla->dbh->selectcol_arrayref( + "SELECT DISTINCT group_id + FROM group_control_map AS gcm + INNER JOIN groups ON gcm.group_id = groups.id + WHERE product_id = ? AND isbuggroup = 1 + AND membercontrol != " . CONTROLMAPNA, undef, $self->id); + $self->{groups_valid} = Bugzilla::Group->new_from_list($ids); + return $self->{groups_valid}; +} + sub versions { my $self = shift; my $dbh = Bugzilla->dbh; @@ -285,6 +319,42 @@ below. Returns: A hash with group id as key and hash containing a Bugzilla::Group object and the properties of group relative to the product. + +=item C<groups_mandatory_for> + +=over + +=item B<Description> + +Tells you what groups are mandatory for bugs in this product. + +=item B<Params> + +C<$user> - The user who you want to check. + +=item B<Returns> An arrayref of C<Bugzilla::Group> objects. + +=back + +=item C<groups_valid> + +=over + +=item B<Description> + +Returns an arrayref of L<Bugzilla::Group> objects, representing groups +that bugs could validly be restricted to within this product. Used mostly +by L<Bugzilla::Bug> to assure that you're adding valid groups to a bug. + +B<Note>: This doesn't check whether or not the current user can add/remove +bugs to/from these groups. It just tells you that bugs I<could be in> these +groups, in this product. + +=item B<Params> (none) + +=item B<Returns> An arrayref of L<Bugzilla::Group> objects. + +=back =item C<versions()> |