summaryrefslogtreecommitdiffstats
path: root/Bugzilla
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla')
-rw-r--r--Bugzilla/Bug.pm67
-rw-r--r--Bugzilla/Product.pm107
-rw-r--r--Bugzilla/User.pm26
-rw-r--r--Bugzilla/WebService/Bug.pm13
4 files changed, 157 insertions, 56 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm
index ad272af22..4a90c1ce7 100644
--- a/Bugzilla/Bug.pm
+++ b/Bugzilla/Bug.pm
@@ -497,8 +497,8 @@ sub create {
# Add the group restrictions
my $sth_group = $dbh->prepare(
'INSERT INTO bug_group_map (bug_id, group_id) VALUES (?, ?)');
- foreach my $group_id (@$groups) {
- $sth_group->execute($bug->bug_id, $group_id);
+ foreach my $group (@$groups) {
+ $sth_group->execute($bug->bug_id, $group->id);
}
$dbh->do('UPDATE bugs SET creation_ts = ? WHERE bug_id = ?', undef,
@@ -597,8 +597,7 @@ sub run_create_validators {
$params->{keywords} = $class->_check_keywords($params->{keywords}, $product);
- $params->{groups} = $class->_check_groups($product,
- $params->{groups});
+ $params->{groups} = $class->_check_groups($params->{groups}, $product);
my $component = $class->_check_component($params->{component}, $product);
$params->{component_id} = $component->id;
@@ -1388,48 +1387,38 @@ sub _check_estimated_time {
}
sub _check_groups {
- my ($invocant, $product, $group_ids) = @_;
-
- my $user = Bugzilla->user;
+ my ($invocant, $group_names, $product) = @_;
my %add_groups;
- my $controls = $product->group_controls;
-
- foreach my $id (@$group_ids) {
- my $group = new Bugzilla::Group($id)
- || ThrowUserError("invalid_group_ID");
-
- # This can only happen if somebody hacked the enter_bug form.
- ThrowCodeError("inactive_group", { name => $group->name })
- unless $group->is_active;
-
- my $membercontrol = $controls->{$id}
- && $controls->{$id}->{membercontrol};
- my $othercontrol = $controls->{$id}
- && $controls->{$id}->{othercontrol};
-
- my $permit = ($membercontrol && $user->in_group($group->name))
- || $othercontrol;
- $add_groups{$id} = 1 if $permit;
+ # In email or WebServices, when the "groups" item actually
+ # isn't specified, then just add the default groups.
+ if (!defined $group_names) {
+ my $available = $product->groups_available;
+ foreach my $group (@$available) {
+ $add_groups{$group->id} = $group if $group->{is_default};
+ }
}
+ else {
+ # Allow a comma-separated list, for email_in.pl.
+ $group_names = [map { trim($_) } split(',', $group_names)]
+ if !ref $group_names;
- foreach my $id (keys %$controls) {
- next unless $controls->{$id}->{'group'}->is_active;
- my $membercontrol = $controls->{$id}->{membercontrol} || 0;
- my $othercontrol = $controls->{$id}->{othercontrol} || 0;
-
- # Add groups required
- if ($membercontrol == CONTROLMAPMANDATORY
- || ($othercontrol == CONTROLMAPMANDATORY
- && !$user->in_group_id($id)))
- {
- # User had no option, bug needs to be in this group.
- $add_groups{$id} = 1;
+ # First check all the groups they chose to set.
+ foreach my $name (@$group_names) {
+ # We don't want to expose the existence or non-existence of groups,
+ # so instead of doing check(), we just do "next" on an invalid
+ # group.
+ my $group = new Bugzilla::Group({ name => $name }) or next;
+ next if !$product->group_is_valid($group);
+ $add_groups{$group->id} = $group;
}
}
- my @add_groups = keys %add_groups;
+ # Now enforce mandatory groups.
+ $add_groups{$_->id} = $_ foreach @{ $product->groups_mandatory };
+
+ my @add_groups = values %add_groups;
return \@add_groups;
}
@@ -2099,7 +2088,7 @@ sub set_product {
}
# Make sure the bug is in all the mandatory groups for the new product.
- foreach my $group (@{$product->groups_mandatory_for(Bugzilla->user)}) {
+ foreach my $group (@{$product->groups_mandatory}) {
$self->add_group($group);
}
}
diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm
index 46e91aa65..5c4057595 100644
--- a/Bugzilla/Product.pm
+++ b/Bugzilla/Product.pm
@@ -15,9 +15,9 @@
# Contributor(s): Tiago R. Mello <timello@async.com.br>
# Frédéric Buclin <LpSolit@gmail.com>
-use strict;
-
package Bugzilla::Product;
+use strict;
+use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
use Bugzilla::Constants;
use Bugzilla::Util;
@@ -32,7 +32,7 @@ use Bugzilla::Mailer;
use Bugzilla::Series;
use Bugzilla::Hook;
-use base qw(Bugzilla::Field::ChoiceInterface Bugzilla::Object);
+use Scalar::Util qw(blessed);
use constant DEFAULT_CLASSIFICATION_ID => 1;
@@ -256,6 +256,9 @@ sub update {
}
}
}
+
+ delete $self->{groups_available};
+ delete $self->{groups_mandatory};
}
$dbh->bz_commit_transaction();
# Changes have been committed.
@@ -611,9 +614,53 @@ sub group_controls {
return $self->{group_controls};
}
-sub groups_mandatory_for {
- my ($self, $user) = @_;
- my $groups = $user->groups_as_string;
+sub groups_available {
+ my ($self) = @_;
+ return $self->{groups_available} if defined $self->{groups_available};
+ my $dbh = Bugzilla->dbh;
+ my $shown = CONTROLMAPSHOWN;
+ my $default = CONTROLMAPDEFAULT;
+ my %member_groups = @{ $dbh->selectcol_arrayref(
+ "SELECT group_id, membercontrol
+ FROM group_control_map
+ INNER JOIN groups ON group_control_map.group_id = groups.id
+ WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ?
+ AND (membercontrol = $shown OR membercontrol = $default)
+ AND " . Bugzilla->user->groups_in_sql(),
+ {Columns=>[1,2]}, $self->id) };
+ # We don't need to check the group membership here, because we only
+ # add these groups to the list below if the group isn't already listed
+ # for membercontrol.
+ my %other_groups = @{ $dbh->selectcol_arrayref(
+ "SELECT group_id, othercontrol
+ FROM group_control_map
+ INNER JOIN groups ON group_control_map.group_id = groups.id
+ WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ?
+ AND (othercontrol = $shown OR othercontrol = $default)",
+ {Columns=>[1,2]}, $self->id) };
+
+ # If the user is a member, then we use the membercontrol value.
+ # Otherwise, we use the othercontrol value.
+ my %all_groups = %member_groups;
+ foreach my $id (keys %other_groups) {
+ if (!defined $all_groups{$id}) {
+ $all_groups{$id} = $other_groups{$id};
+ }
+ }
+
+ my $available = Bugzilla::Group->new_from_list([keys %all_groups]);
+ foreach my $group (@$available) {
+ $group->{is_default} = 1 if $all_groups{$group->id} == $default;
+ }
+
+ $self->{groups_available} = $available;
+ return $self->{groups_available};
+}
+
+sub groups_mandatory {
+ my ($self) = @_;
+ return $self->{groups_mandatory} if $self->{groups_mandatory};
+ my $groups = Bugzilla->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
@@ -625,7 +672,20 @@ sub groups_mandatory_for {
OR (othercontrol = $mandatory
AND group_id NOT IN ($groups)))",
undef, $self->id);
- return Bugzilla::Group->new_from_list($ids);
+ $self->{groups_mandatory} = Bugzilla::Group->new_from_list($ids);
+ return $self->{groups_mandatory};
+}
+
+# We don't just check groups_valid, because we want to know specifically
+# if this group is valid for the currently-logged-in user.
+sub group_is_valid {
+ my ($self, $group) = @_;
+ my $group_id = blessed($group) ? $group->id : $group;
+ my $is_mandatory = grep { $group_id == $_->id }
+ @{ $self->groups_mandatory };
+ my $is_available = grep { $group_id == $_->id }
+ @{ $self->groups_available };
+ return ($is_mandatory or $is_available) ? 1 : 0;
}
sub groups_valid {
@@ -837,22 +897,47 @@ below.
a Bugzilla::Group object and the properties of group
relative to the product.
-=item C<groups_mandatory_for>
+=item C<groups_available>
+
+Tells you what groups are set to Default or Shown for the
+currently-logged-in user (taking into account both OtherControl and
+MemberControl). Returns an arrayref of L<Bugzilla::Group> objects with
+an extra hash keys set, C<is_default>, which is true if the group
+is set to Default for the currently-logged-in user.
+
+=item C<groups_mandatory>
+
+Tells you what groups are mandatory for bugs in this product, for the
+currently-logged-in user. Returns an arrayref of C<Bugzilla::Group> objects.
+
+=item C<group_is_valid>
=over
=item B<Description>
-Tells you what groups are mandatory for bugs in this product.
+Tells you whether or not the currently-logged-in user can set a group
+on a bug (whether or not they match the MemberControl/OtherControl
+settings for a group in this product). Groups that are C<Mandatory> for
+the currently-loggeed-in user are also acceptable since from Bugzilla's
+perspective, there's no problem with "setting" a Mandatory group on
+a bug. (In fact, the user I<must> set the Mandatory group on the bug.)
=item B<Params>
-C<$user> - The user who you want to check.
+=over
-=item B<Returns> An arrayref of C<Bugzilla::Group> objects.
+=item C<$group> - Either a numeric group id or a L<Bugzilla::Group> object.
=back
+=item B<Returns>
+
+C<1> if the group is valid in this product, C<0> otherwise.
+
+=back
+
+
=item C<groups_valid>
=over
diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm
index 7dd86f301..5c6209811 100644
--- a/Bugzilla/User.pm
+++ b/Bugzilla/User.pm
@@ -334,7 +334,7 @@ sub queries_subscribed {
ON ngm.namedquery_id = lif.namedquery_id
WHERE lif.user_id = ?
AND lif.namedquery_id NOT IN ($query_id_string)
- AND ngm.group_id IN (" . $self->groups_as_string . ")",
+ AND " . $self->groups_in_sql,
undef, $self->id);
require Bugzilla::Search::Saved;
$self->{queries_subscribed} =
@@ -353,7 +353,7 @@ sub queries_available {
my $avail_query_ids = Bugzilla->dbh->selectcol_arrayref(
'SELECT namedquery_id FROM namedquery_group_map
- WHERE group_id IN (' . $self->groups_as_string . ")
+ WHERE ' . $self->groups_in_sql . "
AND namedquery_id NOT IN ($query_id_string)");
require Bugzilla::Search::Saved;
$self->{queries_available} =
@@ -464,6 +464,14 @@ sub groups_as_string {
return scalar(@ids) ? join(',', @ids) : '-1';
}
+sub groups_in_sql {
+ my ($self, $field) = @_;
+ $field ||= 'group_id';
+ my @ids = map { $_->id } @{ $self->groups };
+ @ids = (-1) if !scalar @ids;
+ return Bugzilla->dbh->sql_in($field, \@ids);
+}
+
sub bless_groups {
my $self = shift;
@@ -524,7 +532,7 @@ sub in_group {
FROM group_control_map
WHERE product_id = ?
AND $group != 0
- AND group_id IN (" . $self->groups_as_string . ") " .
+ AND " . $self->groups_in_sql . ' ' .
$dbh->sql_limit(1),
undef, $product_id);
@@ -550,7 +558,7 @@ sub get_products_by_permission {
"SELECT DISTINCT product_id
FROM group_control_map
WHERE $group != 0
- AND group_id IN(" . $self->groups_as_string . ")");
+ AND " . $self->groups_in_sql);
# No need to go further if the user has no "special" privs.
return [] unless scalar(@$product_ids);
@@ -898,10 +906,9 @@ sub visible_groups_direct {
my $sth;
if (Bugzilla->params->{'usevisibilitygroups'}) {
- my $glist = $self->groups_as_string;
$sth = $dbh->prepare("SELECT DISTINCT grantor_id
FROM group_group_map
- WHERE member_id IN($glist)
+ WHERE " . $self->groups_in_sql('member_id') . "
AND grant_type=" . GROUP_VISIBLE);
}
else {
@@ -1957,6 +1964,13 @@ Returns a string containing a comma-separated list of numeric group ids. If
the user is not a member of any groups, returns "-1". This is most often used
within an SQL IN() function.
+=item C<groups_in_sql>
+
+This returns an C<IN> clause for SQL, containing either all of the groups
+the user is in, or C<-1> if the user is in no groups. This takes one
+argument--the name of the SQL field that should be on the left-hand-side
+of the C<IN> statement, which defaults to C<group_id> if not specified.
+
=item C<in_group($group_name, $product_id)>
Determines whether or not a user is in the given group by name.
diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm
index 1d7b9f7d9..b38168602 100644
--- a/Bugzilla/WebService/Bug.pm
+++ b/Bugzilla/WebService/Bug.pm
@@ -1802,6 +1802,15 @@ don't want it to be assigned to the component owner.
=item C<cc> (array) - An array of usernames to CC on this bug.
+=item C<groups> (array) - An array of group names to put this
+bug into. You can see valid group names on the Permissions
+tab of the Preferences screen, or, if you are an administrator,
+in the Groups control panel. Note that invalid group names or
+groups that the bug can't be restricted to are silently ignored. If
+you don't specify this argument, then a bug will be added into
+all the groups that are set as being "Default" for this product. (If
+you want to avoid that, you should specify C<groups> as an empty array.)
+
=item C<qa_contact> (username) - If this installation has QA Contacts
enabled, you can set the QA Contact here if you don't want to use
the component's default QA Contact.
@@ -1867,6 +1876,10 @@ in them. The error message will have more details.
=item Before B<3.0.4>, parameters marked as B<Defaulted> were actually
B<Required>, due to a bug in Bugzilla.
+=item The C<groups> argument was added in Bugzilla B<3.8>. Before
+Bugzilla 3.8, bugs were only added into Mandatory groups by this
+method.
+
=back
=back