Commit 72780239 authored by Dave Lawrence's avatar Dave Lawrence

Bug 148564 - Ability to ignore specific bugs (not get email from them, even as the reporter)

r=glob,r/a=LpSolit
parent bae33962
...@@ -68,7 +68,7 @@ use constant SHUTDOWNHTML_RETRY_AFTER => 3600; ...@@ -68,7 +68,7 @@ use constant SHUTDOWNHTML_RETRY_AFTER => 3600;
# Global Code # Global Code
##################################################################### #####################################################################
# $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess; $::SIG{__DIE__} = i_am_cgi() ? \&CGI::Carp::confess : \&Carp::confess;
# Note that this is a raw subroutine, not a method, so $class isn't available. # Note that this is a raw subroutine, not a method, so $class isn't available.
sub init_page { sub init_page {
......
...@@ -788,8 +788,9 @@ sub run_create_validators { ...@@ -788,8 +788,9 @@ sub run_create_validators {
sub update { sub update {
my $self = shift; 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 # XXX This is just a temporary hack until all updating happens
# inside this function. # inside this function.
my $delta_ts = shift || $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); my $delta_ts = shift || $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
...@@ -880,7 +881,7 @@ sub update { ...@@ -880,7 +881,7 @@ sub update {
# Add an activity entry for the other bug. # Add an activity entry for the other bug.
LogActivityEntry($removed_id, $other, $self->id, '', 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. # Update delta_ts on the other bug so that we trigger mid-airs.
$dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?', $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?',
undef, $delta_ts, $removed_id); undef, $delta_ts, $removed_id);
...@@ -891,7 +892,7 @@ sub update { ...@@ -891,7 +892,7 @@ sub update {
# Add an activity entry for the other bug. # Add an activity entry for the other bug.
LogActivityEntry($added_id, $other, '', $self->id, 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. # Update delta_ts on the other bug so that we trigger mid-airs.
$dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?', $dbh->do('UPDATE bugs SET delta_ts = ? WHERE bug_id = ?',
undef, $delta_ts, $added_id); undef, $delta_ts, $added_id);
...@@ -939,7 +940,7 @@ sub update { ...@@ -939,7 +940,7 @@ sub update {
$comment = Bugzilla::Comment->insert_create_data($comment); $comment = Bugzilla::Comment->insert_create_data($comment);
if ($comment->work_time) { if ($comment->work_time) {
LogActivityEntry($self->id, "work_time", "", $comment->work_time, LogActivityEntry($self->id, "work_time", "", $comment->work_time,
Bugzilla->user->id, $delta_ts); $user->id, $delta_ts);
} }
} }
...@@ -950,7 +951,7 @@ sub update { ...@@ -950,7 +951,7 @@ sub update {
my ($from, $to) my ($from, $to)
= $comment->is_private ? (0, 1) : (1, 0); = $comment->is_private ? (0, 1) : (1, 0);
LogActivityEntry($self->id, "longdescs.isprivate", $from, $to, 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 # Insert the values into the multiselect value tables
...@@ -995,8 +996,8 @@ sub update { ...@@ -995,8 +996,8 @@ sub update {
my $change = $changes->{$field}; my $change = $changes->{$field};
my $from = defined $change->[0] ? $change->[0] : ''; my $from = defined $change->[0] ? $change->[0] : '';
my $to = defined $change->[1] ? $change->[1] : ''; my $to = defined $change->[1] ? $change->[1] : '';
LogActivityEntry($self->id, $field, $from, $to, Bugzilla->user->id, LogActivityEntry($self->id, $field, $from, $to,
$delta_ts); $user->id, $delta_ts);
} }
# Check if we have to update the duplicates table and the other bug. # Check if we have to update the duplicates table and the other bug.
...@@ -1010,7 +1011,7 @@ sub update { ...@@ -1010,7 +1011,7 @@ sub update {
$update_dup->update(); $update_dup->update();
} }
} }
$changes->{'dup_id'} = [$old_dup || undef, $cur_dup || undef]; $changes->{'dup_id'} = [$old_dup || undef, $cur_dup || undef];
} }
...@@ -1027,6 +1028,25 @@ sub update { ...@@ -1027,6 +1028,25 @@ sub update {
$self->{delta_ts} = $delta_ts; $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(); $dbh->bz_commit_transaction();
# The only problem with this here is that update() is often called # The only problem with this here is that update() is often called
...@@ -1044,7 +1064,7 @@ sub update { ...@@ -1044,7 +1064,7 @@ sub update {
# Also flush the visible_bugs cache for this bug as the user's # Also flush the visible_bugs cache for this bug as the user's
# relationship with this bug may have changed. # relationship with this bug may have changed.
delete Bugzilla->user->{_visible_bugs_cache}->{$self->id}; delete $user->{_visible_bugs_cache}->{$self->id};
return $changes; return $changes;
} }
...@@ -2303,7 +2323,7 @@ sub set_all { ...@@ -2303,7 +2323,7 @@ sub set_all {
# we have to check that the current assignee, qa, and CCs are still # 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 # 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, # 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; $self->_check_strict_isolation() if $product_changed;
} }
...@@ -2333,6 +2353,7 @@ sub reset_assigned_to { ...@@ -2333,6 +2353,7 @@ sub reset_assigned_to {
my $comp = $self->component_obj; my $comp = $self->component_obj;
$self->set_assigned_to($comp->default_assignee); $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_cclist_accessible { $_[0]->set('cclist_accessible', $_[1]); }
sub set_comment_is_private { sub set_comment_is_private {
my ($self, $comment_id, $isprivate) = @_; my ($self, $comment_id, $isprivate) = @_;
...@@ -4475,6 +4496,8 @@ sub _multi_select_accessor { ...@@ -4475,6 +4496,8 @@ sub _multi_select_accessor {
=item set_cclist_accessible =item set_cclist_accessible
=item set_bug_ignored
=item product =item product
=item VALIDATORS =item VALIDATORS
......
...@@ -212,6 +212,13 @@ sub Send { ...@@ -212,6 +212,13 @@ sub Send {
# Deleted users must be excluded. # Deleted users must be excluded.
next unless $user; 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)) { if ($user->can_see_bug($id)) {
# Go through each role the user has and see if they want mail in # Go through each role the user has and see if they want mail in
# that role. # that role.
...@@ -228,7 +235,7 @@ sub Send { ...@@ -228,7 +235,7 @@ sub Send {
} }
} }
} }
if (scalar(%rels_which_want)) { if (scalar(%rels_which_want)) {
# So the user exists, can see the bug, and wants mail in at least # 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? # one role. But do we want to send it to them?
...@@ -241,9 +248,8 @@ sub Send { ...@@ -241,9 +248,8 @@ sub Send {
$dep_ok = $user->can_see_bug($params->{blocker}->id) ? 1 : 0; $dep_ok = $user->can_see_bug($params->{blocker}->id) ? 1 : 0;
} }
# Make sure the user isn't in the nomail list, and the dep check passed. # Email the user if the dep check passed.
if ($user->email_enabled && $dep_ok) { if ($dep_ok) {
# OK, OK, if we must. Email the user.
$sent_mail = sendMail( $sent_mail = sendMail(
{ to => $user, { to => $user,
bug => $bug, bug => $bug,
......
...@@ -945,6 +945,23 @@ use constant ABSTRACT_SCHEMA => { ...@@ -945,6 +945,23 @@ use constant ABSTRACT_SCHEMA => {
], ],
}, },
email_bug_ignore => {
FIELDS => [
user_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'profiles',
COLUMN => 'userid',
DELETE => 'CASCADE'}},
bug_id => {TYPE => 'INT3', NOTNULL => 1,
REFERENCES => {TABLE => 'bugs',
COLUMN => 'bug_id',
DELETE => 'CASCADE'}},
],
INDEXES => [
email_bug_ignore_user_id_idx => {FIELDS => [qw(user_id bug_id)],
TYPE => 'UNIQUE'},
],
},
watch => { watch => {
FIELDS => [ FIELDS => [
watcher => {TYPE => 'INT3', NOTNULL => 1, watcher => {TYPE => 'INT3', NOTNULL => 1,
......
...@@ -997,6 +997,9 @@ sub notify { ...@@ -997,6 +997,9 @@ sub notify {
} }
foreach my $to (keys %recipients) { 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 # Add threadingmarker to allow flag notification emails to be the
# threaded similar to normal bug change emails. # threaded similar to normal bug change emails.
my $thread_user_id = $recipients{$to} ? $recipients{$to}->id : 0; my $thread_user_id = $recipients{$to} ? $recipients{$to}->id : 0;
......
...@@ -136,6 +136,8 @@ sub MessageToMTA { ...@@ -136,6 +136,8 @@ sub MessageToMTA {
Bugzilla::Hook::process('mailer_before_send', Bugzilla::Hook::process('mailer_before_send',
{ email => $email, mailer_args => \@args }); { email => $email, mailer_args => \@args });
return if $email->header('to') eq '';
$email->walk_parts(sub { $email->walk_parts(sub {
my ($part) = @_; my ($part) = @_;
return if $part->parts > 1; # Top-level return if $part->parts > 1; # Top-level
......
...@@ -429,6 +429,31 @@ sub tags { ...@@ -429,6 +429,31 @@ sub tags {
return $self->{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 # # Saved Recent Bug Lists #
########################## ##########################
...@@ -2220,6 +2245,34 @@ groups. ...@@ -2220,6 +2245,34 @@ groups.
Returns a hashref with tag IDs as key, and a hashref with tag 'id', Returns a hashref with tag IDs as key, and a hashref with tag 'id',
'name' and 'bug_count' as value. '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 =back
=head2 Saved Recent Bug Lists =head2 Saved Recent Bug Lists
......
...@@ -1420,6 +1420,15 @@ ...@@ -1420,6 +1420,15 @@
their <quote>Field/recipient specific options</quote> setting. their <quote>Field/recipient specific options</quote> setting.
</para> </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>
<section id="savedsearches" xreflabel="Saved Searches"> <section id="savedsearches" xreflabel="Saved Searches">
......
...@@ -212,9 +212,9 @@ my @set_fields = qw(op_sys rep_platform priority bug_severity ...@@ -212,9 +212,9 @@ my @set_fields = qw(op_sys rep_platform priority bug_severity
bug_file_loc status_whiteboard short_desc bug_file_loc status_whiteboard short_desc
deadline remaining_time estimated_time deadline remaining_time estimated_time
work_time set_default_assignee set_default_qa_contact work_time set_default_assignee set_default_qa_contact
cclist_accessible reporter_accessible cclist_accessible reporter_accessible
product confirm_product_change 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, 'assigned_to') if !$cgi->param('set_default_assignee');
push(@set_fields, 'qa_contact') if !$cgi->param('set_default_qa_contact'); push(@set_fields, 'qa_contact') if !$cgi->param('set_default_qa_contact');
my %field_translation = ( my %field_translation = (
......
...@@ -45,7 +45,10 @@ ...@@ -45,7 +45,10 @@
function SetCheckboxes(setting) { function SetCheckboxes(setting) {
for (var count = 0; count < document.userprefsform.elements.length; count++) { for (var count = 0; count < document.userprefsform.elements.length; count++) {
var theinput = document.userprefsform.elements[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")) { if (theinput.name.match("neg")) {
theinput.checked = !setting; theinput.checked = !setting;
} }
...@@ -285,6 +288,40 @@ You are currently not watching any users. ...@@ -285,6 +288,40 @@ You are currently not watching any users.
[% END %] [% END %]
</p> </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>
...@@ -92,19 +92,21 @@ ...@@ -92,19 +92,21 @@
<table cellpadding="3" cellspacing="1"> <table cellpadding="3" cellspacing="1">
[%# *** Reported and modified dates *** %] [%# *** Reported and modified dates *** %]
[% PROCESS section_dates %] [% PROCESS section_dates %]
[% PROCESS section_cclist %] [% PROCESS section_cclist %]
[% PROCESS section_bug_ignored %]
[% PROCESS section_spacer %] [% PROCESS section_spacer %]
[% PROCESS section_see_also %] [% PROCESS section_see_also %]
[% PROCESS section_customfields %] [% PROCESS section_customfields %]
[% PROCESS section_spacer %] [% PROCESS section_spacer %]
[% Hook.process("after_custom_fields") %] [% Hook.process("after_custom_fields") %]
[% PROCESS section_flags %] [% PROCESS section_flags %]
</table> </table>
...@@ -819,6 +821,26 @@ ...@@ -819,6 +821,26 @@
[% END %] [% 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 for See Also #%]
[%############################################################################%] [%############################################################################%]
[% BLOCK section_see_also %] [% BLOCK section_see_also %]
......
...@@ -318,6 +318,47 @@ sub SaveEmail { ...@@ -318,6 +318,47 @@ sub SaveEmail {
$dbh->bz_commit_transaction(); $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->params)) {
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();
}
} }
...@@ -325,9 +366,9 @@ sub DoPermissions { ...@@ -325,9 +366,9 @@ sub DoPermissions {
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $user = Bugzilla->user; my $user = Bugzilla->user;
my (@has_bits, @set_bits); my (@has_bits, @set_bits);
my $groups = $dbh->selectall_arrayref( 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"); $user->groups_as_string . ") ORDER BY name");
foreach my $group (@$groups) { foreach my $group (@$groups) {
my ($nam, $desc) = @$group; my ($nam, $desc) = @$group;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment