diff options
-rw-r--r-- | Bugzilla/Bug.pm | 41 | ||||
-rw-r--r-- | Bugzilla/BugMail.pm | 11 | ||||
-rw-r--r-- | Bugzilla/Flag.pm | 3 | ||||
-rw-r--r-- | Bugzilla/User.pm | 53 | ||||
-rw-r--r-- | docs/en/xml/using.xml | 9 | ||||
-rwxr-xr-x | process_bug.cgi | 4 | ||||
-rw-r--r-- | template/en/default/account/prefs/email.html.tmpl | 43 | ||||
-rw-r--r-- | template/en/default/bug/edit.html.tmpl | 38 | ||||
-rwxr-xr-x | userprefs.cgi | 45 |
9 files changed, 219 insertions, 28 deletions
diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 45381e6e1..a873d63be 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -786,8 +786,9 @@ sub run_create_validators { sub update { my $self = shift; + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; - my $dbh = Bugzilla->dbh; # XXX This is just a temporary hack until all updating happens # inside this function. my $delta_ts = shift || $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); @@ -878,7 +879,7 @@ sub update { # Add an activity entry for the other bug. LogActivityEntry($removed_id, $other, $self->id, '', - Bugzilla->user->id, $delta_ts); + $user->id, $delta_ts); # Update delta_ts on the other bug so that we trigger mid-airs. $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?', undef, $delta_ts, $removed_id); @@ -889,7 +890,7 @@ sub update { # Add an activity entry for the other bug. LogActivityEntry($added_id, $other, '', $self->id, - Bugzilla->user->id, $delta_ts); + $user->id, $delta_ts); # Update delta_ts on the other bug so that we trigger mid-airs. $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?', undef, $delta_ts, $added_id); @@ -937,7 +938,7 @@ sub update { $comment = Bugzilla::Comment->insert_create_data($comment); if ($comment->work_time) { LogActivityEntry($self->id, "work_time", "", $comment->work_time, - Bugzilla->user->id, $delta_ts); + $user->id, $delta_ts); } } @@ -948,7 +949,7 @@ sub update { my ($from, $to) = $comment->is_private ? (0, 1) : (1, 0); LogActivityEntry($self->id, "longdescs.isprivate", $from, $to, - Bugzilla->user->id, $delta_ts, $comment->id); + $user->id, $delta_ts, $comment->id); } # Insert the values into the multiselect value tables @@ -993,8 +994,8 @@ sub update { my $change = $changes->{$field}; my $from = defined $change->[0] ? $change->[0] : ''; my $to = defined $change->[1] ? $change->[1] : ''; - LogActivityEntry($self->id, $field, $from, $to, Bugzilla->user->id, - $delta_ts); + LogActivityEntry($self->id, $field, $from, $to, + $user->id, $delta_ts); } # Check if we have to update the duplicates table and the other bug. @@ -1008,7 +1009,7 @@ sub update { $update_dup->update(); } } - + $changes->{'dup_id'} = [$old_dup || undef, $cur_dup || undef]; } @@ -1025,6 +1026,25 @@ sub update { $self->{delta_ts} = $delta_ts; } + # Update bug ignore data if user wants to ignore mail for this bug + if (exists $self->{'bug_ignored'}) { + my $bug_ignored_changed; + if ($self->{'bug_ignored'} && !$user->is_bug_ignored($self->id)) { + $dbh->do('INSERT INTO email_bug_ignore + (user_id, bug_id) VALUES (?, ?)', + undef, $user->id, $self->id); + $bug_ignored_changed = 1; + + } + elsif (!$self->{'bug_ignored'} && $user->is_bug_ignored($self->id)) { + $dbh->do('DELETE FROM email_bug_ignore + WHERE user_id = ? AND bug_id = ?', + undef, $user->id, $self->id); + $bug_ignored_changed = 1; + } + delete $user->{bugs_ignored} if $bug_ignored_changed; + } + $dbh->bz_commit_transaction(); # The only problem with this here is that update() is often called @@ -1042,7 +1062,7 @@ sub update { # Also flush the visible_bugs cache for this bug as the user's # relationship with this bug may have changed. - delete Bugzilla->user->{_visible_bugs_cache}->{$self->id}; + delete $user->{_visible_bugs_cache}->{$self->id}; return $changes; } @@ -2313,7 +2333,7 @@ sub set_all { # we have to check that the current assignee, qa, and CCs are still # valid if we've switched products, under strict_isolation. We can only # do that here, because if they *did* change the assignee, qa, or CC, - # then we don't want to check the original ones, only the new ones. + # then we don't want to check the original ones, only the new ones. $self->_check_strict_isolation() if $product_changed; } @@ -2343,6 +2363,7 @@ sub reset_assigned_to { my $comp = $self->component_obj; $self->set_assigned_to($comp->default_assignee); } +sub set_bug_ignored { $_[0]->set('bug_ignored', $_[1]); } sub set_cclist_accessible { $_[0]->set('cclist_accessible', $_[1]); } sub set_comment_is_private { my ($self, $comment_id, $isprivate) = @_; diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index fe611d55d..37421ce3e 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -263,6 +263,13 @@ sub Send { # Deleted users must be excluded. next unless $user; + # If email notifications are disabled for this account, or the bug + # is ignored, there is no need to do additional checks. + if ($user->email_disabled || $user->is_bug_ignored($id)) { + push(@excluded, $user->login); + next; + } + if ($user->can_see_bug($id)) { # Go through each role the user has and see if they want mail in # that role. @@ -279,7 +286,7 @@ sub Send { } } } - + if (scalar(%rels_which_want)) { # So the user exists, can see the bug, and wants mail in at least # one role. But do we want to send it to them? @@ -301,8 +308,6 @@ sub Send { ($user->login eq 'sync-1@bugzilla.tld' || $user->login !~ /\.tld$/)) { - # OK, OK, if we must. Email the user. - # Don't show summaries for bugs the user can't access, and # provide a hook for extensions such as SecureMail to filter # this list. diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 5f4f90b6c..d3e9b1d37 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -1021,6 +1021,9 @@ sub notify { } foreach my $to (keys %recipients) { + # Skip sending if user is ignoring the bug. + next if ($recipients{$to} && $recipients{$to}->is_bug_ignored($bug->id)); + # Add threadingmarker to allow flag notification emails to be the # threaded similar to normal bug change emails. my $thread_user_id = $recipients{$to} ? $recipients{$to}->id : 0; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 151919bf8..2c11c2381 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -432,6 +432,31 @@ sub tags { return $self->{tags}; } +sub bugs_ignored { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + if (!defined $self->{'bugs_ignored'}) { + $self->{'bugs_ignored'} = $dbh->selectall_arrayref( + 'SELECT bugs.bug_id AS id, + bugs.bug_status AS status, + bugs.short_desc AS summary + FROM bugs + INNER JOIN email_bug_ignore + ON bugs.bug_id = email_bug_ignore.bug_id + WHERE user_id = ?', + { Slice => {} }, $self->id); + # Go ahead and load these into the visible bugs cache + # to speed up can_see_bug checks later + $self->visible_bugs([ map { $_->{'id'} } @{ $self->{'bugs_ignored'} } ]); + } + return $self->{'bugs_ignored'}; +} + +sub is_bug_ignored { + my ($self, $bug_id) = @_; + return (grep {$_->{'id'} == $bug_id} @{$self->bugs_ignored}) ? 1 : 0; +} + ########################## # Saved Recent Bug Lists # ########################## @@ -2211,6 +2236,34 @@ groups. Returns a hashref with tag IDs as key, and a hashref with tag 'id', 'name' and 'bug_count' as value. +=item C<bugs_ignored> + +Returns an array of hashrefs containing information about bugs currently +being ignored by the user. + +Each hashref contains the following information: + +=over + +=item C<id> + +C<int> The id of the bug. + +=item C<status> + +C<string> The current status of the bug. + +=item C<summary> + +C<string> The current summary of the bug. + +=back + +=item C<is_bug_ignored> + +Returns true if the user does not want email notifications for the +specified bug ID, else returns false. + =back =head2 Saved Recent Bug Lists diff --git a/docs/en/xml/using.xml b/docs/en/xml/using.xml index 53766ef34..05b415021 100644 --- a/docs/en/xml/using.xml +++ b/docs/en/xml/using.xml @@ -1409,6 +1409,15 @@ their <quote>Field/recipient specific options</quote> setting. </para> + <para> + The <quote>Ignore Bugs</quote> section lets you specify a + comma-separated list of bugs from which you never want to get any + email notification of any kind. Removing a bug from this list will + re-enable email notification for this bug. This is especially useful + e.g. if you are the reporter of a very noisy bug which you are not + interested in anymore or if you are watching someone who is in such + a noisy bug. + </para> </section> <section id="savedsearches" xreflabel="Saved Searches"> diff --git a/process_bug.cgi b/process_bug.cgi index e5461e962..1f613982d 100755 --- a/process_bug.cgi +++ b/process_bug.cgi @@ -256,9 +256,9 @@ my @set_fields = qw(op_sys rep_platform priority bug_severity bug_file_loc status_whiteboard short_desc deadline remaining_time estimated_time work_time set_default_assignee set_default_qa_contact - cclist_accessible reporter_accessible + cclist_accessible reporter_accessible product confirm_product_change - bug_status resolution dup_id); + bug_status resolution dup_id bug_ignored); push(@set_fields, 'assigned_to') if !$cgi->param('set_default_assignee'); push(@set_fields, 'qa_contact') if !$cgi->param('set_default_qa_contact'); my %field_translation = ( diff --git a/template/en/default/account/prefs/email.html.tmpl b/template/en/default/account/prefs/email.html.tmpl index 32d52fd8e..ffb153785 100644 --- a/template/en/default/account/prefs/email.html.tmpl +++ b/template/en/default/account/prefs/email.html.tmpl @@ -46,7 +46,10 @@ function SetCheckboxes(setting) { for (var count = 0; count < document.userprefsform.elements.length; count++) { var theinput = document.userprefsform.elements[count]; - if (theinput.type == "checkbox" && !theinput.disabled) { + if (theinput.type == "checkbox" + && !theinput.disabled + && !theinput.name.match("remove_ignored_bug")) + { if (theinput.name.match("neg")) { theinput.checked = !setting; } @@ -286,6 +289,40 @@ You are currently not watching any users. [% END %] </p> -<hr> +<b>Ignore [% terms.Bugs %]</b> -<br> +<p> + You can specify a list of [% terms.bugs %] from which you never want to get + any email notification of any kind by adding their ID(s) as a comma-separated + list. Removing [% terms.abug %] by selecting it from the current ignored list + will re-enable email notifications for the [% terms.bug %]. +</p> +[% IF user.bugs_ignored.size %] + <p> + You are currently ignoring: + <table> + [% FOREACH bug = user.bugs_ignored %] + <tr> + <td> + <input type="checkbox" name="remove_ignored_bug_[% bug.id FILTER html %]" value="1"> + </td> + <td><a href="[% urlbase FILTER html %]show_bug.cgi?id=[% bug.id FILTER uri %]"> + [% bug.id FILTER html %]</a> + </td> + <td>[% bug.status FILTER html %]</td> + <td> + [% IF user.can_see_bug(bug.id) %] + - [% bug.summary FILTER html %] + [% ELSE %] + (private) + [% END %] + </td> + </tr> + [% END %] + </table> + </p> +[% END %] + +<p>Add [% terms.bugs %]:<br> + <input type="text" id="add_ignored_bugs" + name="add_ignored_bugs" size="60"></p> diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index d58154a01..45ea316b9 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -124,19 +124,21 @@ <table cellpadding="3" cellspacing="1"> [%# *** Reported and modified dates *** %] [% PROCESS section_dates %] - + [% PROCESS section_cclist %] - + + [% PROCESS section_bug_ignored %] + [% PROCESS section_spacer %] - + [% PROCESS section_flags %] - - [% PROCESS section_see_also %] - + + [% PROCESS section_see_also %] + [% PROCESS section_spacer %] - + [% PROCESS section_customfields %] - + [% Hook.process("after_custom_fields") %] </table> @@ -857,6 +859,26 @@ [% END %] [%############################################################################%] +[%# Block for Bug Ignored #%] +[%############################################################################%] +[% BLOCK section_bug_ignored %] + [% IF user.id %] + <tr> + <th class="field_label"> + <label for="bug_ignored" title="Ignore all email for this [% terms.bug %]"> + Ignore [% terms.Bug %] Mail: + </label> + </th> + <td> + <input type="hidden" name="defined_bug_ignored" value="1"> + <input type="checkbox" name="bug_ignored" id="bug_ignored" value="1" + [% ' checked="checked"' IF user.is_bug_ignored(bug.id) %]> + </td> + </tr> + [% END %] +[% END %] + +[%############################################################################%] [%# Block for See Also #%] [%############################################################################%] [% BLOCK section_see_also %] diff --git a/userprefs.cgi b/userprefs.cgi index f0d5a8e53..e614d8111 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -338,6 +338,47 @@ sub SaveEmail { $dbh->bz_commit_transaction(); } + + ########################################################################### + # Ignore Bugs + ########################################################################### + my %ignored_bugs = map { $_->{'id'} => 1 } @{$user->bugs_ignored}; + + # Validate the new bugs to ignore by checking that they exist and also + # if the user gave an alias + my @add_ignored = split(/[\s,]+/, $cgi->param('add_ignored_bugs')); + @add_ignored = map { Bugzilla::Bug->check($_)->id } @add_ignored; + map { $ignored_bugs{$_} = 1 } @add_ignored; + + # Remove any bug ids the user no longer wants to ignore + foreach my $key (grep(/^remove_ignored_bug_/, $cgi->param)) { + my ($bug_id) = $key =~ /(\d+)$/; + delete $ignored_bugs{$bug_id}; + } + + # Update the database with any changes made + my ($removed, $added) = diff_arrays([ map { $_->{'id'} } @{$user->bugs_ignored} ], + [ keys %ignored_bugs ]); + + if (scalar @$removed || scalar @$added) { + $dbh->bz_start_transaction(); + + if (scalar @$removed) { + $dbh->do('DELETE FROM email_bug_ignore WHERE user_id = ? AND ' . + $dbh->sql_in('bug_id', $removed), + undef, $user->id); + } + if (scalar @$added) { + my $sth = $dbh->prepare('INSERT INTO email_bug_ignore + (user_id, bug_id) VALUES (?, ?)'); + $sth->execute($user->id, $_) foreach @$added; + } + + # Reset the cache of ignored bugs if the list changed. + delete $user->{bugs_ignored}; + + $dbh->bz_commit_transaction(); + } } @@ -345,9 +386,9 @@ sub DoPermissions { my $dbh = Bugzilla->dbh; my $user = Bugzilla->user; my (@has_bits, @set_bits); - + my $groups = $dbh->selectall_arrayref( - "SELECT DISTINCT name, description FROM groups WHERE id IN (" . + "SELECT DISTINCT name, description FROM groups WHERE id IN (" . $user->groups_as_string . ") ORDER BY name"); foreach my $group (@$groups) { my ($nam, $desc) = @$group; |