]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 148564 - Ability to ignore specific bugs (not get email from them, even as the...
authorDave Lawrence <dlawrence@mozilla.com>
Mon, 8 Apr 2013 20:03:09 +0000 (16:03 -0400)
committerDave Lawrence <dlawrence@mozilla.com>
Mon, 8 Apr 2013 20:03:09 +0000 (16:03 -0400)
r=glob,r/a=LpSolit

12 files changed:
Bugzilla.pm
Bugzilla/Bug.pm
Bugzilla/BugMail.pm
Bugzilla/DB/Schema.pm
Bugzilla/Flag.pm
Bugzilla/Mailer.pm
Bugzilla/User.pm
docs/en/xml/using.xml
process_bug.cgi
template/en/default/account/prefs/email.html.tmpl
template/en/default/bug/edit.html.tmpl
userprefs.cgi

index 0681b83c75ca4e887ddff588e3cad396daa9ec2b..8f235da21758bfd6e9ef4bc7a3a0b3c7ac5e2332 100644 (file)
@@ -68,7 +68,7 @@ use constant SHUTDOWNHTML_RETRY_AFTER => 3600;
 # 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.
 sub init_page {
index 703ef1bb635de1a35f9bd6cd178fcfb9cded9f05..fef7127d7e873a01834b99f2dab7ef6ed42ed9fc 100644 (file)
@@ -788,8 +788,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)');
@@ -880,7 +881,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);
@@ -891,7 +892,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);
@@ -939,7 +940,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);
         }
     }
 
@@ -950,7 +951,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
@@ -995,8 +996,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.
@@ -1010,7 +1011,7 @@ sub update {
                 $update_dup->update();
             }
         }
-        
+
         $changes->{'dup_id'} = [$old_dup || undef, $cur_dup || undef];
     }
 
@@ -1027,6 +1028,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
@@ -1044,7 +1064,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;
 }
@@ -2303,7 +2323,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;
 }
 
@@ -2333,6 +2353,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) = @_;
@@ -4475,6 +4496,8 @@ sub _multi_select_accessor {
 
 =item set_cclist_accessible
 
+=item set_bug_ignored
+
 =item product
 
 =item VALIDATORS
index 089d3013e5802a99414021591305380cd7c1e512..12914ca734d0ba3ea34ab1bf7ffc34e14e1ea433 100644 (file)
@@ -212,6 +212,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.
@@ -228,7 +235,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?
@@ -241,9 +248,8 @@ sub Send {
                 $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.
-            if ($user->email_enabled && $dep_ok) {
-                # OK, OK, if we must. Email the user.
+            # Email the user if the dep check passed.
+            if ($dep_ok) {
                 $sent_mail = sendMail(
                     { to       => $user, 
                       bug      => $bug,
index f92d0aeb788d5d8dcdc54c4caab7caedffd75153..0d8396df5734e83afd41a6b325ab5e8f1b2636ab 100644 (file)
@@ -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 => {
         FIELDS => [
             watcher => {TYPE => 'INT3', NOTNULL => 1,
index 98029a1b14c31fea9aae611016e4fea1ebb8d813..0ecc545fdcb477036a53bb6fefd487cf9cf70ed5 100644 (file)
@@ -997,6 +997,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;
index 6602c6cef087d5d195442a0db429cb73cec78251..b60ddb72e5f71bd8d14ddc8ab3b32f138ef33f5f 100644 (file)
@@ -136,6 +136,8 @@ sub MessageToMTA {
     Bugzilla::Hook::process('mailer_before_send', 
                             { email => $email, mailer_args => \@args });
 
+    return if $email->header('to') eq '';
+
     $email->walk_parts(sub {
         my ($part) = @_;
         return if $part->parts > 1; # Top-level
index 5e39eb49a8223363e4c41d5ab0e025e071f0eaba..6e08ccf0e2eec5188a2f763130e7edde4eef6ecb 100644 (file)
@@ -429,6 +429,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 #
 ##########################
@@ -2220,6 +2245,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
index 88a87f0fcb5920ac3960e191981d6139d6f49463..42f1e4dd9288d58cb342df9978c5eb7bff8afa3d 100644 (file)
         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">
index cf2f31b6ff7880d62cd6f9dfee405ca90b641f3b..21113c2e9d18c4786a0207059d7dfd8ea1574729 100755 (executable)
@@ -212,9 +212,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 = (
index 3fcb8617696510338219a2625e8b3704defd452f..ef8d0ae298bd43c8c87559ddf7dd94691491b6e8 100644 (file)
 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;
       }
@@ -285,6 +288,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>
index c48b17c0845c5c6494798285cfaaee9586e10bdd..4c39b7cc394b5cf22b1d23340fd501ec9618513d 100644 (file)
         <table cellpadding="3" cellspacing="1">
         [%# *** Reported and modified dates *** %]
          [% PROCESS section_dates %]
-         
+
          [% PROCESS section_cclist %]
-         
+
+         [% PROCESS section_bug_ignored %]
+
          [% PROCESS section_spacer %]
 
          [% PROCESS section_see_also %] 
-         
+
          [% PROCESS section_customfields %]
-         
+
          [% PROCESS section_spacer %]
-         
+
          [% Hook.process("after_custom_fields") %]
-                
+
          [% PROCESS section_flags %]
 
         </table>
     </tr>
 [% 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                                                       #%]
 [%############################################################################%]
index a80184d51f9a6a3e02bb172f136f7981a1ff2468..225a6247e7a892c7feda8ef65b64041193f930a3 100755 (executable)
@@ -318,6 +318,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->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 {
     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;