summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Green <sgreen@redhat.com>2014-05-22 01:17:46 +0200
committerSimon Green <sgreen@redhat.com>2014-05-22 01:17:46 +0200
commit9bd3f08dce23acc052819d97d1f082666c354b20 (patch)
tree6fee7c37bb9bff84a681593eff2e565e7bdb5f3e
parentabf6ec5cff89b9507aa978d5345559239886c014 (diff)
downloadbugzilla-9bd3f08dce23acc052819d97d1f082666c354b20.tar.gz
bugzilla-9bd3f08dce23acc052819d97d1f082666c354b20.tar.xz
Bug 1008764: Add a web service to create and update Flag types
r=glob, a=justdave
-rw-r--r--Bugzilla/FlagType.pm16
-rw-r--r--Bugzilla/WebService.pm2
-rw-r--r--Bugzilla/WebService/Constants.pm13
-rw-r--r--Bugzilla/WebService/FlagType.pm647
-rw-r--r--Bugzilla/WebService/Server/REST/Resources/FlagType.pm56
-rwxr-xr-xbuglist.cgi47
-rw-r--r--template/en/default/list/edit-multiple.html.tmpl7
7 files changed, 777 insertions, 11 deletions
diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm
index 773996b2e..34973684a 100644
--- a/Bugzilla/FlagType.pm
+++ b/Bugzilla/FlagType.pm
@@ -644,9 +644,19 @@ sub sqlify_criteria {
my @criteria = ("1=1");
if ($criteria->{name}) {
- my $name = $dbh->quote($criteria->{name});
- trick_taint($name); # Detaint data as we have quoted it.
- push(@criteria, "flagtypes.name = $name");
+ if (ref($criteria->{name}) eq 'ARRAY') {
+ my @names = map { $dbh->quote($_) } @{$criteria->{name}};
+ # Detaint data as we have quoted it.
+ foreach my $name (@names) {
+ trick_taint($name);
+ }
+ push @criteria, $dbh->sql_in('flagtypes.name', \@names);
+ }
+ else {
+ my $name = $dbh->quote($criteria->{name});
+ trick_taint($name); # Detaint data as we have quoted it.
+ push(@criteria, "flagtypes.name = $name");
+ }
}
if ($criteria->{target_type}) {
# The target type is stored in the database as a one-character string
diff --git a/Bugzilla/WebService.pm b/Bugzilla/WebService.pm
index ebad7930a..1dc04c1f6 100644
--- a/Bugzilla/WebService.pm
+++ b/Bugzilla/WebService.pm
@@ -365,6 +365,8 @@ objects.
=item L<Bugzilla::WebService::Classification>
+=item L<Bugzilla::WebService::FlagType>
+
=item L<Bugzilla::WebService::Group>
=item L<Bugzilla::WebService::Product>
diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm
index e18e2b8ec..5164ec0c1 100644
--- a/Bugzilla/WebService/Constants.pm
+++ b/Bugzilla/WebService/Constants.pm
@@ -81,8 +81,9 @@ use constant WS_ERROR_CODE => {
illegal_field => 104,
freetext_too_long => 104,
# Component errors
- require_component => 105,
- component_name_too_long => 105,
+ require_component => 105,
+ component_name_too_long => 105,
+ product_unknown_component => 105,
# Invalid Product
no_products => 106,
entry_access_denied => 106,
@@ -191,6 +192,13 @@ use constant WS_ERROR_CODE => {
# Search errors are 1000-1100
buglist_parameters_required => 1000,
+ # Flag type errors are 1100-1200
+ flag_type_name_invalid => 1101,
+ flag_type_description_invalid => 1102,
+ flag_type_cc_list_invalid => 1103,
+ flag_type_sortkey_invalid => 1104,
+ flag_type_not_editable => 1105,
+
# Errors thrown by the WebService itself. The ones that are negative
# conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
@@ -269,6 +277,7 @@ sub WS_DISPATCH {
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
'Bug' => 'Bugzilla::WebService::Bug',
'Classification' => 'Bugzilla::WebService::Classification',
+ 'FlagType' => 'Bugzilla::WebService::FlagType',
'Group' => 'Bugzilla::WebService::Group',
'Product' => 'Bugzilla::WebService::Product',
'User' => 'Bugzilla::WebService::User',
diff --git a/Bugzilla/WebService/FlagType.pm b/Bugzilla/WebService/FlagType.pm
new file mode 100644
index 000000000..d755b8885
--- /dev/null
+++ b/Bugzilla/WebService/FlagType.pm
@@ -0,0 +1,647 @@
+# 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.
+
+package Bugzilla::WebService::FlagType;
+
+use 5.10.1;
+use strict;
+
+use parent qw(Bugzilla::WebService);
+use Bugzilla::Component;
+use Bugzilla::Constants;
+use Bugzilla::Error;
+use Bugzilla::FlagType;
+use Bugzilla::Product;
+use Bugzilla::Util qw(trim);
+
+use List::MoreUtils qw(uniq);
+
+sub create {
+ my ($self, $params) = @_;
+
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+
+ Bugzilla->user->in_group('editcomponents')
+ || scalar(@{$user->get_products_by_permission('editcomponents')})
+ || ThrowUserError("auth_failure", { group => "editcomponents",
+ action => "add",
+ object => "flagtypes" });
+
+ $params->{name} || ThrowCodeError('param_required', { param => 'name' });
+ $params->{description} || ThrowCodeError('param_required', { param => 'description' });
+
+ my %args = (
+ sortkey => 1,
+ name => undef,
+ inclusions => ['0:0'], # Default to __ALL__:__ALL__
+ cc_list => '',
+ description => undef,
+ is_requestable => 'on',
+ exclusions => [],
+ is_multiplicable => 'on',
+ request_group => '',
+ is_active => 'on',
+ is_specifically_requestable => 'on',
+ target_type => 'bug',
+ grant_group => '',
+ );
+
+ foreach my $key (keys %args) {
+ $args{$key} = $params->{$key} if defined($params->{$key});
+ }
+
+ $args{name} = trim($params->{name});
+ $args{description} = trim($params->{description});
+
+ # Is specifically requestable is actually is_requesteeable
+ if (exists $args{is_specifically_requestable}) {
+ $args{is_requesteeble} = delete $args{is_specifically_requestable};
+ }
+
+ # Default is on for the tickbox flags.
+ # If the user has set them to 'off' then undefine them so the flags are not ticked
+ foreach my $arg_name (qw(is_requestable is_multiplicable is_active is_requesteeble)) {
+ if (defined($args{$arg_name}) && ($args{$arg_name} eq '0')) {
+ $args{$arg_name} = undef;
+ }
+ }
+
+ # Process group inclusions and exclusions
+ $args{inclusions} = _process_lists($params->{inclusions}) if defined $params->{inclusions};
+ $args{exclusions} = _process_lists($params->{exclusions}) if defined $params->{exclusions};
+
+ my $flagtype = Bugzilla::FlagType->create(\%args);
+
+ return { id => $self->type('int', $flagtype->id) };
+}
+
+sub update {
+ my ($self, $params) = @_;
+
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+
+ Bugzilla->login(LOGIN_REQUIRED);
+ $user->in_group('editcomponents')
+ || scalar(@{$user->get_products_by_permission('editcomponents')})
+ || ThrowUserError("auth_failure", { group => "editcomponents",
+ action => "edit",
+ object => "flagtypes" });
+
+ defined($params->{names}) || defined($params->{ids})
+ || ThrowCodeError('params_required',
+ { function => 'FlagType.update', params => ['ids', 'names'] });
+
+ # Get the list of unique flag type ids we are updating
+ my @flag_type_ids = defined($params->{ids}) ? @{$params->{ids}} : ();
+ if (defined $params->{names}) {
+ push @flag_type_ids, map { $_->id }
+ @{ Bugzilla::FlagType::match({ name => $params->{names} }) };
+ }
+ @flag_type_ids = uniq @flag_type_ids;
+
+ # We delete names and ids to keep only new values to set.
+ delete $params->{names};
+ delete $params->{ids};
+
+ # Process group inclusions and exclusions
+ # We removed them from $params because these are handled differently
+ my $inclusions = _process_lists(delete $params->{inclusions}) if defined $params->{inclusions};
+ my $exclusions = _process_lists(delete $params->{exclusions}) if defined $params->{exclusions};
+
+ $dbh->bz_start_transaction();
+ my %changes = ();
+
+ foreach my $flag_type_id (@flag_type_ids) {
+ my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_type_id);
+
+ if ($can_fully_edit) {
+ $flagtype->set_all($params);
+ }
+ elsif (scalar keys %$params) {
+ ThrowUserError('flag_type_not_editable', { flagtype => $flagtype });
+ }
+
+ # Process the clusions
+ foreach my $type ('inclusions', 'exclusions') {
+ my $clusions = $type eq 'inclusions' ? $inclusions : $exclusions;
+ next if not defined $clusions;
+
+ my @extra_clusions = ();
+ if (!$user->in_group('editcomponents')) {
+ my $products = $user->get_products_by_permission('editcomponents');
+ # Bring back the products the user cannot edit.
+ foreach my $item (values %{$flagtype->$type}) {
+ my ($prod_id, $comp_id) = split(':', $item);
+ push(@extra_clusions, $item) unless grep { $_->id == $prod_id } @$products;
+ }
+ }
+
+ $flagtype->set_clusions({
+ $type => [@$clusions, @extra_clusions],
+ });
+ }
+
+ my $returned_changes = $flagtype->update();
+ $changes{$flagtype->id} = {
+ name => $flagtype->name,
+ changes => $returned_changes,
+ };
+ }
+ $dbh->bz_commit_transaction();
+
+ my @result;
+ foreach my $flag_type_id (keys %changes) {
+ my %hash = (
+ id => $self->type('int', $flag_type_id),
+ name => $self->type('string', $changes{$flag_type_id}{name}),
+ changes => {},
+ );
+
+ foreach my $field (keys %{ $changes{$flag_type_id}{changes} }) {
+ my $change = $changes{$flag_type_id}{changes}{$field};
+ $hash{changes}{$field} = {
+ removed => $self->type('string', $change->[0]),
+ added => $self->type('string', $change->[1])
+ };
+ }
+
+ push(@result, \%hash);
+ }
+
+ return { flagtypes => \@result };
+}
+
+sub _process_lists {
+ my $list = shift;
+ my $user = Bugzilla->user;
+
+ my @products;
+ if ($user->in_group('editcomponents')) {
+ @products = Bugzilla::Product->get_all;
+ }
+ else {
+ @products = @{$user->get_products_by_permission('editcomponents')};
+ }
+
+ my @component_list;
+
+ foreach my $item (@$list) {
+ # A hash with products as the key and component names as the values
+ if(ref($item) eq 'HASH') {
+ while (my ($product_name, $component_names) = each %$item) {
+ my $product = Bugzilla::Product->check({name => $product_name});
+ unless (grep { $product->name eq $_->name } @products) {
+ ThrowUserError('product_access_denied', { name => $product_name });
+ }
+ my @component_ids;
+
+ foreach my $comp_name (@$component_names) {
+ my $component = Bugzilla::Component->check({product => $product, name => $comp_name});
+ ThrowCodeError('param_invalid', { param => $comp_name}) unless defined $component;
+ push @component_list, $product->id . ':' . $component->id;
+ }
+ }
+ }
+ elsif(!ref($item)) {
+ # These are whole products
+ my $product = Bugzilla::Product->check({name => $item});
+ unless (grep { $product->name eq $_->name } @products) {
+ ThrowUserError('product_access_denied', { name => $item });
+ }
+ push @component_list, $product->id . ':0';
+ }
+ else {
+ # The user has passed something invalid
+ ThrowCodeError('param_invalid', { param => $item });
+ }
+ }
+
+ return \@component_list;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::WebService::FlagType - API for creating flags.
+
+=head1 DESCRIPTION
+
+This part of the Bugzilla API allows you to create new flags
+
+=head1 METHODS
+
+See L<Bugzilla::WebService> for a description of what B<STABLE>, B<UNSTABLE>,
+and B<EXPERIMENTAL> mean, and for more description about error codes.
+
+=head2 Create Flag
+
+=over
+
+=item C<create> B<UNSTABLE>
+
+=item B<Description>
+
+Creates a new FlagType
+
+=item B<REST>
+
+POST /rest/flagtype
+
+The params to include in the POST body as well as the returned data format,
+are the same as below.
+
+=item B<Params>
+
+At a minimum the following two arguments must be supplied:
+
+=over
+
+=item C<name> (string) - The name of the new Flag Type.
+
+=item C<description> (string) - A description for the Flag Type object.
+
+=back
+
+=item B<Returns>
+
+C<int> flag_id
+
+The ID of the new FlagType object is returned.
+
+=item B<Params>
+
+=over
+
+=item name B<required>
+
+C<string> A short name identifying this type.
+
+=item description B<required>
+
+C<string> A comprehensive description of this type.
+
+=item inclusions B<optional>
+
+An array of strings or a hash containing product names, and optionally
+component names. If you provide a string, the flag type will be shown on
+all bugs in that product. If you provide a hash, the key represents the
+product name, and the value is the components of the product to be included.
+
+For example:
+
+ [ 'FooProduct',
+ {
+ BarProduct => [ 'C1', 'C3' ],
+ BazProduct => [ 'C7' ]
+ }
+ ]
+
+This flag will be added to B<All> components of I<FooProduct>,
+components C1 and C3 of I<BarProduct>, and C7 of I<BazProduct>.
+
+=item exclusions B<optional>
+
+An array of strings or hashes containing product names. This uses the same
+fromat as inclusions.
+
+This will exclude the flag from all products and components specified.
+
+=item sortkey B<optional>
+
+C<int> A number between 1 and 32767 by which this type will be sorted when
+displayed to users in a list; ignore if you don't care what order the types
+appear in or if you want them to appear in alphabetical order.
+
+=item is_active B<optional>
+
+C<boolean> Flag of this type appear in the UI and can be set. Default is B<true>.
+
+=item is_requestable B<optional>
+
+C<boolean> Users can ask for flags of this type to be set. Default is B<true>.
+
+=item cc_list B<optional>
+
+C<array> An array of strings. If the flag type is requestable, who should
+receive e-mail notification of requests. This is an array of e-mail addresses
+which do not need to be Bugzilla logins.
+
+=item is_specifically_requestable B<optional>
+
+C<boolean> Users can ask specific other users to set flags of this type as
+opposed to just asking the wind. Default is B<true>.
+
+=item is_multiplicable B<optional>
+
+C<boolean> Multiple flags of this type can be set on the same bug. Default is B<true>.
+
+=item grant_group B<optional>
+
+C<string> The group allowed to grant/deny flags of this type (to allow all
+users to grant/deny these flags, select no group). Default is B<no group>.
+
+=item request_group B<optional>
+
+C<string> If flags of this type are requestable, the group allowed to request
+them (to allow all users to request these flags, select no group). Note that
+the request group alone has no effect if the grant group is not defined!
+Default is B<no group>.
+
+=back
+
+=item B<Errors>
+
+=over
+
+=item 51 (Group Does Not Exist)
+
+The group name you entered does not exist, or you do not have access to it.
+
+=item 105 (Unknown component)
+
+The component does not exist for this product.
+
+=item 106 (Product Access Denied)
+
+Either the product does not exist or you don't have editcomponents privileges
+to it.
+
+=item 501 (Illegal Email Address)
+
+One of the e-mail address in the CC list is invalid. An e-mail in the CC
+list does NOT need to be a valid Bugzilla user.
+
+=item 1101 (Flag Type Name invalid)
+
+You must specify a non-blank name for this flag type. It must
+no contain spaces or commas, and must be 50 characters or less.
+
+=item 1102 (Flag type must have description)
+
+You must specify a description for this flag type.
+
+=item 1103 (Flag type CC list is invalid
+
+The CC list must be 200 characters or less.
+
+=item 1104 (Flag Type Sort Key Not Valid)
+
+The sort key is not a valid number.
+
+=item 1105 (Flag Type Not Editable)
+
+This flag type is not available for the products you can administer. Therefore
+you can not edit attributes of the flag type, other than the inclusion and
+exclusion list.
+
+=back
+
+=item B<History>
+
+=over
+
+=item Added in Bugzilla B<5.0>.
+
+=back
+
+=back
+
+=head2 update
+
+B<EXPERIMENTAL>
+
+=over
+
+=item B<Description>
+
+This allows you to update a flag type in Bugzilla.
+
+=item B<REST>
+
+PUT /rest/flagtype/<product_id_or_name>
+
+The params to include in the PUT body as well as the returned data format,
+are the same as below. The C<ids> and C<names> params will be overridden as
+it is pulled from the URL path.
+
+=item B<Params>
+
+B<Note:> The following parameters specify which products you are updating.
+You must set one or both of these parameters.
+
+=over
+
+=item C<ids>
+
+C<array> of C<int>s. Numeric ids of the flag types that you wish to update.
+
+=item C<names>
+
+C<array> of C<string>s. Names of the flag types that you wish to update. If
+many flag types have the same name, this will change ALL of them.
+
+=back
+
+B<Note:> The following parameters specify the new values you want to set for
+the products you are updating.
+
+=over
+
+=item name
+
+C<string> A short name identifying this type.
+
+=item description
+
+C<string> A comprehensive description of this type.
+
+=item inclusions B<optional>
+
+An array of strings or a hash containing product names, and optionally
+component names. If you provide a string, the flag type will be shown on
+all bugs in that product. If you provide a hash, the key represents the
+product name, and the value is the components of the product to be included.
+
+for example
+
+ [ 'FooProduct',
+ {
+ BarProduct => [ 'C1', 'C3' ],
+ BazProduct => [ 'C7' ]
+ }
+ ]
+
+This flag will be added to B<All> components of I<FooProduct>,
+components C1 and C3 of I<BarProduct>, and C7 of I<BazProduct>.
+
+=item exclusions B<optional>
+
+An array of strings or hashes containing product names.
+This uses the same fromat as inclusions.
+
+This will exclude the flag from all products and components specified.
+
+=item sortkey
+
+C<int> A number between 1 and 32767 by which this type will be sorted when
+displayed to users in a list; ignore if you don't care what order the types
+appear in or if you want them to appear in alphabetical order.
+
+=item is_active
+
+C<boolean> Flag of this type appear in the UI and can be set.
+
+=item is_requestable
+
+C<boolean> Users can ask for flags of this type to be set.
+
+=item cc_list
+
+C<array> An array of strings. If the flag type is requestable, who should
+receive e-mail notification of requests. This is an array of e-mail addresses
+which do not need to be Bugzilla logins.
+
+=item is_specifically_requestable
+
+C<boolean> Users can ask specific other users to set flags of this type as
+opposed to just asking the wind.
+
+=item is_multiplicable
+
+C<boolean> Multiple flags of this type can be set on the same bug.
+
+=item grant_group
+
+C<string> The group allowed to grant/deny flags of this type (to allow all
+users to grant/deny these flags, select no group).
+
+=item request_group
+
+C<string> If flags of this type are requestable, the group allowed to request
+them (to allow all users to request these flags, select no group). Note that
+the request group alone has no effect if the grant group is not defined!
+
+=back
+
+=item B<Returns>
+
+A C<hash> with a single field "flagtypes". This points to an array of hashes
+with the following fields:
+
+=over
+
+=item C<id>
+
+C<int> The id of the product that was updated.
+
+=item C<name>
+
+C<string> The name of the product that was updated.
+
+=item C<changes>
+
+C<hash> The changes that were actually done on this product. The keys are
+the names of the fields that were changed, and the values are a hash
+with two keys:
+
+=over
+
+=item C<added>
+
+C<string> The value that this field was changed to.
+
+=item C<removed>
+
+C<string> The value that was previously set in this field.
+
+=back
+
+Note that booleans will be represented with the strings '1' and '0'.
+
+Here's an example of what a return value might look like:
+
+ {
+ products => [
+ {
+ id => 123,
+ changes => {
+ name => {
+ removed => 'FooFlagType',
+ added => 'BarFlagType'
+ },
+ is_requestable => {
+ removed => '1',
+ added => '0',
+ }
+ }
+ }
+ ]
+ }
+
+=back
+
+=item B<Errors>
+
+=over
+
+=item 51 (Group Does Not Exist)
+
+The group name you entered does not exist, or you do not have access to it.
+
+=item 105 (Unknown component)
+
+The component does not exist for this product.
+
+=item 106 (Product Access Denied)
+
+Either the product does not exist or you don't have editcomponents privileges
+to it.
+
+=item 501 (Illegal Email Address)
+
+One of the e-mail address in the CC list is invalid. An e-mail in the CC
+list does NOT need to be a valid Bugzilla user.
+
+=item 1101 (Flag Type Name invalid)
+
+You must specify a non-blank name for this flag type. It must
+no contain spaces or commas, and must be 50 characters or less.
+
+=item 1102 (Flag type must have description)
+
+You must specify a description for this flag type.
+
+=item 1103 (Flag type CC list is invalid
+
+The CC list must be 200 characters or less.
+
+=item 1104 (Flag Type Sort Key Not Valid)
+
+The sort key is not a valid number.
+
+=item 1105 (Flag Type Not Editable)
+
+This flag type is not available for the products you can administer. Therefore
+you can not edit attributes of the flag type, other than the inclusion and
+exclusion list.
+
+=back
+
+=item B<History>
+
+=over
+
+=item Added in Bugzilla B<5.0>.
+
+=back
+
+=back
diff --git a/Bugzilla/WebService/Server/REST/Resources/FlagType.pm b/Bugzilla/WebService/Server/REST/Resources/FlagType.pm
new file mode 100644
index 000000000..745785838
--- /dev/null
+++ b/Bugzilla/WebService/Server/REST/Resources/FlagType.pm
@@ -0,0 +1,56 @@
+# 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.
+
+package Bugzilla::WebService::Server::REST::Resources::FlagType;
+
+use 5.10.1;
+use strict;
+
+use Bugzilla::WebService::Constants;
+use Bugzilla::WebService::FlagType;
+
+use Bugzilla::Error;
+
+BEGIN {
+ *Bugzilla::WebService::FlagType::rest_resources = \&_rest_resources;
+};
+
+sub _rest_resources {
+ my $rest_resources = [
+ qr{^/flagtype$}, {
+ POST => {
+ method => 'create',
+ success_code => STATUS_CREATED
+ }
+ },
+ qr{^/flagtype/([^/]+)$}, {
+ PUT => {
+ method => 'update',
+ params => sub {
+ my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
+ return { $param => [ $_[0] ] };
+ }
+ }
+ },
+ ];
+ return $rest_resources;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Webservice::Server::REST::Resources::FlagType - The Flag Type REST API
+
+=head1 DESCRIPTION
+
+This part of the Bugzilla REST API allows you to create and update Flag types.
+
+See L<Bugzilla::WebService::FlagType> for more details on how to use this
+part of the REST API.
diff --git a/buglist.cgi b/buglist.cgi
index e0a4a6aaa..d88939171 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -259,7 +259,7 @@ sub GetGroups {
my %legal_groups;
foreach my $product_name (@$product_names) {
- my $product = new Bugzilla::Product({name => $product_name});
+ my $product = Bugzilla::Product->new({name => $product_name, cache => 1});
foreach my $gid (keys %{$product->group_controls}) {
# The user can only edit groups they belong to.
@@ -874,7 +874,7 @@ $vars->{'time_info'} = $time_info;
if (!$user->in_group('editbugs')) {
foreach my $product (keys %$bugproducts) {
- my $prod = new Bugzilla::Product({name => $product});
+ my $prod = Bugzilla::Product->new({name => $product, cache => 1});
if (!$user->in_group('editbugs', $prod->id)) {
$vars->{'caneditbugs'} = 0;
last;
@@ -905,12 +905,12 @@ $vars->{'currenttime'} = localtime(time());
my @products = keys %$bugproducts;
my $one_product;
if (scalar(@products) == 1) {
- $one_product = new Bugzilla::Product({ name => $products[0] });
+ $one_product = Bugzilla::Product->new({ name => $products[0], cache => 1 });
}
# This is used in the "Zarroo Boogs" case.
elsif (my @product_input = $cgi->param('product')) {
if (scalar(@product_input) == 1 and $product_input[0] ne '') {
- $one_product = new Bugzilla::Product({ name => $cgi->param('product') });
+ $one_product = Bugzilla::Product->new({ name => $cgi->param('product'), cache => 1 });
}
}
# We only want the template to use it if the user can actually
@@ -993,10 +993,47 @@ if ($dotweak && scalar @bugs) {
$vars->{'versions'} = [map($_->name, grep($_->is_active, @{ $one_product->versions }))];
$vars->{'components'} = [map($_->name, grep($_->is_active, @{ $one_product->components }))];
if (Bugzilla->params->{'usetargetmilestone'}) {
- $vars->{'targetmilestones'} = [map($_->name, grep($_->is_active,
+ $vars->{'milestones'} = [map($_->name, grep($_->is_active,
@{ $one_product->milestones }))];
}
}
+ else {
+ # We will only show the values at are active in all products.
+ my %values = ();
+ my @fields = ('components', 'versions');
+ if (Bugzilla->params->{'usetargetmilestone'}) {
+ push @fields, 'milestones';
+ }
+
+ # Go through each product and count the number of times each field
+ # is used
+ foreach my $product_name (@products) {
+ my $product = Bugzilla::Product->new({name => $product_name, cache => 1});
+ foreach my $field (@fields) {
+ my $list = $product->$field;
+ foreach my $item (@$list) {
+ ++$values{$field}{$item->name} if $item->is_active;
+ }
+ }
+ }
+
+ # Now we get the list of each field and see which values have
+ # $product_count (i.e. appears in every product)
+ my $product_count = scalar(@products);
+ foreach my $field (@fields) {
+ my @values = grep { $values{$field}{$_} == $product_count } keys %{$values{$field}};
+ if (scalar @values) {
+ @{$vars->{$field}} = $field eq 'version'
+ ? sort { vers_cmp(lc($a), lc($b)) } @values
+ : sort { lc($a) cmp lc($b) } @values
+ }
+
+ # Do we need to show a warning about limited visiblity?
+ if (@values != scalar keys %{$values{$field}}) {
+ $vars->{excluded_values} = 1;
+ }
+ }
+ }
}
# If we're editing a stored query, use the existing query name as default for
diff --git a/template/en/default/list/edit-multiple.html.tmpl b/template/en/default/list/edit-multiple.html.tmpl
index 5adc47a59..144ae10c9 100644
--- a/template/en/default/list/edit-multiple.html.tmpl
+++ b/template/en/default/list/edit-multiple.html.tmpl
@@ -44,6 +44,11 @@
</ol>
</div>
+[% IF excluded_values %]
+ <p class="extra_info">Only values that are available for all products of the above
+ [%+ terms.bugs %] are shown.</p>
+[% END %]
+
<table id="form">
<tr>
@@ -124,7 +129,7 @@
<th><label for="target_milestone">Target Milestone:</label></th>
<td>
[% PROCESS selectmenu menuname = "target_milestone"
- menuitems = targetmilestones %]
+ menuitems = milestones %]
</td>
[% END %]
</tr>