summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Green <sgreen@redhat.com>2014-09-02 02:26:42 +0200
committerSimon Green <sgreen@redhat.com>2014-09-02 02:26:42 +0200
commite0fbbde652bd6ff8112399fc7d7bc96f19e92994 (patch)
tree3c295243c252e7675fcf9a1df53f7fc1f3ed5d5e
parent92b5ff149bb0ff364e88f14b588f160dc9866cca (diff)
downloadbugzilla-e0fbbde652bd6ff8112399fc7d7bc96f19e92994.tar.gz
bugzilla-e0fbbde652bd6ff8112399fc7d7bc96f19e92994.tar.xz
Bug 281791 - Add ability to change flags in "change several bugs at once"
r=glob, a=sgreen
-rw-r--r--Bugzilla/Flag.pm111
-rw-r--r--Bugzilla/Search.pm1
-rwxr-xr-xbuglist.cgi39
-rwxr-xr-xprocess_bug.cgi11
-rw-r--r--template/en/default/flag/list.html.tmpl16
-rw-r--r--template/en/default/list/edit-multiple.html.tmpl7
6 files changed, 184 insertions, 1 deletions
diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm
index 381cc1174..6e2096729 100644
--- a/Bugzilla/Flag.pm
+++ b/Bugzilla/Flag.pm
@@ -932,6 +932,117 @@ sub extract_flags_from_cgi {
=over
+=item C<multi_extract_flags_from_cgi($bug, $hr_vars)>
+
+Checks whether or not there are new flags to create and returns an
+array of hashes. This array is then passed to Flag::create(). This differs
+from the previous sub-routine as it is called for changing multiple bugs
+
+=back
+
+=cut
+
+sub multi_extract_flags_from_cgi {
+ my ($class, $bug, $vars, $skip) = @_;
+ my $cgi = Bugzilla->cgi;
+
+ my $match_status = Bugzilla::User::match_field({
+ '^requestee(_type)?-(\d+)$' => { 'type' => 'multi' },
+ }, undef, $skip);
+
+ $vars->{'match_field'} = 'requestee';
+ if ($match_status == USER_MATCH_FAILED) {
+ $vars->{'message'} = 'user_match_failed';
+ }
+ elsif ($match_status == USER_MATCH_MULTIPLE) {
+ $vars->{'message'} = 'user_match_multiple';
+ }
+
+ # Extract a list of flag type IDs from field names.
+ my @flagtype_ids = map(/^flag_type-(\d+)$/ ? $1 : (), $cgi->param());
+
+ my (@new_flags, @flags);
+
+ # Get a list of active flag types available for this product/component.
+ my $flag_types = Bugzilla::FlagType::match(
+ { 'product_id' => $bug->{'product_id'},
+ 'component_id' => $bug->{'component_id'},
+ 'is_active' => 1 });
+
+ foreach my $flagtype_id (@flagtype_ids) {
+ # Checks if there are unexpected flags for the product/component.
+ if (!scalar(grep { $_->id == $flagtype_id } @$flag_types)) {
+ $vars->{'message'} = 'unexpected_flag_types';
+ last;
+ }
+ }
+
+ foreach my $flag_type (@$flag_types) {
+ my $type_id = $flag_type->id;
+
+ # Bug flags are only valid for bugs
+ next unless ($flag_type->target_type eq 'bug');
+
+ # We are only interested in flags the user tries to create.
+ next unless scalar(grep { $_ == $type_id } @flagtype_ids);
+
+ # Get the flags of this type already set for this bug.
+ my $current_flags = $class->match(
+ { 'type_id' => $type_id,
+ 'target_type' => 'bug',
+ 'bug_id' => $bug->bug_id });
+
+ # We will update existing flags (instead of creating new ones)
+ # if the flag exists and the user has not chosen the 'always add'
+ # option
+ my $update = scalar(@$current_flags) && ! $cgi->param("flags_add-$type_id");
+
+ my $status = $cgi->param("flag_type-$type_id");
+ trick_taint($status);
+
+ my @logins = $cgi->param("requestee_type-$type_id");
+ if ($status eq "?" && scalar(@logins)) {
+ foreach my $login (@logins) {
+ if ($update) {
+ foreach my $current_flag (@$current_flags) {
+ push (@flags, { id => $current_flag->id,
+ status => $status,
+ requestee => $login,
+ skip_roe => $skip });
+ }
+ }
+ else {
+ push (@new_flags, { type_id => $type_id,
+ status => $status,
+ requestee => $login,
+ skip_roe => $skip });
+ }
+
+ last unless $flag_type->is_multiplicable;
+ }
+ }
+ else {
+ if ($update) {
+ foreach my $current_flag (@$current_flags) {
+ push (@flags, { id => $current_flag->id,
+ status => $status });
+ }
+ }
+ else {
+ push (@new_flags, { type_id => $type_id,
+ status => $status });
+ }
+ }
+ }
+
+ # Return the list of flags to update and/or to create.
+ return (\@flags, \@new_flags);
+}
+
+=pod
+
+=over
+
=item C<notify($flag, $old_flag, $object, $timestamp)>
Sends an email notification about a flag being created, fulfilled
diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm
index b2e8da096..2a13969cc 100644
--- a/Bugzilla/Search.pm
+++ b/Bugzilla/Search.pm
@@ -647,6 +647,7 @@ sub COLUMNS {
foreach my $col (@id_fields) {
$special_sql{$col} = "map_${col}.name";
+ $columns{"${col}_id"}{name} = "bugs.${col}_id";
}
# Do the actual column-getting from fielddefs, now.
diff --git a/buglist.cgi b/buglist.cgi
index eeea3bdf5..5106f8dc2 100755
--- a/buglist.cgi
+++ b/buglist.cgi
@@ -510,6 +510,37 @@ if (grep('relevance', @displaycolumns) && !$fulltext) {
@displaycolumns = grep($_ ne 'relevance', @displaycolumns);
}
+sub _get_common_flag_types {
+ my $component_ids = shift;
+
+ # Get all the different components in the bug list
+ my $components = Bugzilla::Component->new_from_list($component_ids);
+ my %flag_types;
+ my @flag_types_ids;
+ foreach my $component (@$components) {
+ foreach my $flag_type (@{$component->flag_types->{'bug'}}) {
+ push @flag_types_ids, $flag_type->id;
+ $flag_types{$flag_type->id} = $flag_type;
+ }
+ }
+
+ # We only want flags that appear in all components
+ my %common_flag_types;
+ foreach my $id (keys %flag_types) {
+ my $flag_type_count = scalar grep { $_ == $id } @flag_types_ids;
+ $common_flag_types{$id} = $flag_types{$id}
+ if $flag_type_count == scalar @$components;
+ }
+
+ # We only show flags that a user has request or set rights on
+ my @show_flag_types
+ = grep { $user->can_request_flag($_) || $user->can_set_flag($_) }
+ values %common_flag_types;
+ my $any_flags_requesteeble =
+ grep($_->is_requesteeble, @show_flag_types);
+
+ return(\@show_flag_types, $any_flags_requesteeble);
+}
################################################################################
# Select Column Determination
@@ -551,6 +582,7 @@ foreach my $col (@displaycolumns) {
# has for modifying the bugs.
if ($dotweak) {
push(@selectcolumns, "bug_status") if !grep($_ eq 'bug_status', @selectcolumns);
+ push(@selectcolumns, "component_id") if !grep($_ eq 'component_id', @selectcolumns);
}
if ($format->{'extension'} eq 'ics') {
@@ -753,9 +785,10 @@ my $time_info = { 'estimated_time' => 0,
'time_present' => ($estimated_time || $remaining_time ||
$actual_time || $percentage_complete),
};
-
+
my $bugowners = {};
my $bugproducts = {};
+my $bugcomponentids = {};
my $bugcomponents = {};
my $bugstatuses = {};
my @bugidlist;
@@ -789,6 +822,7 @@ foreach my $row (@$data) {
# Record the assignee, product, and status in the big hashes of those things.
$bugowners->{$bug->{'assigned_to'}} = 1 if $bug->{'assigned_to'};
$bugproducts->{$bug->{'product'}} = 1 if $bug->{'product'};
+ $bugcomponentids->{$bug->{'component_id'}} = 1 if $bug->{'component_id'};
$bugcomponents->{$bug->{'component'}} = 1 if $bug->{'component'};
$bugstatuses->{$bug->{'bug_status'}} = 1 if $bug->{'bug_status'};
@@ -956,6 +990,9 @@ if ($dotweak && scalar @bugs) {
$vars->{'severities'} = get_legal_field_values('bug_severity');
$vars->{'resolutions'} = get_legal_field_values('resolution');
+ ($vars->{'flag_types'}, $vars->{any_flags_requesteeble})
+ = _get_common_flag_types([keys %$bugcomponentids]);
+
# Convert bug statuses to their ID.
my @bug_statuses = map {$dbh->quote($_)} keys %$bugstatuses;
my $bug_status_ids =
diff --git a/process_bug.cgi b/process_bug.cgi
index 6ae5c54d6..4b35bf432 100755
--- a/process_bug.cgi
+++ b/process_bug.cgi
@@ -358,6 +358,17 @@ if (defined $cgi->param('id')) {
$first_bug->add_tag($_) foreach @$tags_added;
}
}
+else {
+ # Update flags on multiple bugs. The cgi params are slightly different
+ # than on a single bug, so we need to call a different sub. We also
+ # need to call this per bug, since we might be updating a flag in one
+ # bug, but adding it to a second bug
+ foreach my $b (@bug_objects) {
+ my ($flags, $new_flags)
+ = Bugzilla::Flag->multi_extract_flags_from_cgi($b, $vars);
+ $b->set_flags($flags, $new_flags);
+ }
+}
##############################
# Do Actual Database Updates #
diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl
index 47406d6c0..455238cfc 100644
--- a/template/en/default/flag/list.html.tmpl
+++ b/template/en/default/flag/list.html.tmpl
@@ -119,6 +119,9 @@
class="flag_select flag_type-[% type.id %]"
[% IF !can_edit_flag %] disabled="disabled"[% END %]>
[%# Only display statuses the user is allowed to set. %]
+ [% IF edit_multiple_bugs %]
+ <option value="--do_not_change--">--do_not_change--</option>
+ [% END %]
[% IF !flag || (can_edit_flag && user.can_request_flag(type)) || flag.setter_id == user.id %]
<option value="X"></option>
[% END %]
@@ -168,6 +171,19 @@
[% END %]
</td>
[% END %]
+ <td>
+ [% IF type.is_multiplicable && edit_multiple_bugs %]
+ <input type="checkbox"
+ name="flags_add-[% type.id %]"
+ id="flags_add-[% type.id %]">
+ <label for="flags_add-[% type.id %]">
+ <a class="field_help_link"
+ title="If ticked, always create a new flag. Leaving it unchecked will update existing flag(s) and add a new flag if it does not exist">
+ Always add?
+ </a>
+ </label>
+ [% END %]
+ <td>
</tr>
</tbody>
[% END %]
diff --git a/template/en/default/list/edit-multiple.html.tmpl b/template/en/default/list/edit-multiple.html.tmpl
index 144ae10c9..e581f0892 100644
--- a/template/en/default/list/edit-multiple.html.tmpl
+++ b/template/en/default/list/edit-multiple.html.tmpl
@@ -321,6 +321,13 @@
</script>
[% END %]
+[% IF flag_types %]
+<b><label for="flags">Flags:</label></b><br>
+[% PROCESS "flag/list.html.tmpl"
+ edit_multiple_bugs = 1
+ flag_no_header = 1 %]
+[% END %]
+
[% Hook.process('before_groups') %]
[% IF groups.size > 0 %]