summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron Jones <bjones@mozilla.com>2013-10-31 06:44:10 +0100
committerByron Jones <bjones@mozilla.com>2013-10-31 06:44:10 +0100
commit2c925582f8bd4f6e062dbddabbd424ddfd69debb (patch)
treedac01fccbcbeaa0ff346ab9bd9b2b9114d1503a0
parenta37157083da30af1714b1fe0f70daf1348c994d1 (diff)
downloadbugzilla-2c925582f8bd4f6e062dbddabbd424ddfd69debb.tar.gz
bugzilla-2c925582f8bd4f6e062dbddabbd424ddfd69debb.tar.xz
Bug 920026: show reminder of pending review, feedback, and needinfo requests in the header
-rw-r--r--Bugzilla/Flag.pm4
-rw-r--r--extensions/Review/Extension.pm139
-rwxr-xr-xextensions/Review/bin/review_requests_rebuild.pl29
-rw-r--r--extensions/Review/lib/Util.pm63
-rw-r--r--extensions/Review/template/en/default/hook/global/header-message.html.tmpl23
-rw-r--r--extensions/Review/template/en/default/hook/global/header-start.html.tmpl7
-rw-r--r--extensions/Review/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl11
-rw-r--r--extensions/Review/template/en/default/pages/review_requests_rebuild.html.tmpl23
-rw-r--r--extensions/Review/web/styles/badge.css19
-rw-r--r--template/en/default/global/header.html.tmpl5
10 files changed, 308 insertions, 15 deletions
diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm
index d84a1b2b8..089ac6c9f 100644
--- a/Bugzilla/Flag.pm
+++ b/Bugzilla/Flag.pm
@@ -196,8 +196,8 @@ is an attachment flag, else undefined.
sub type {
my $self = shift;
- $self->{'type'} ||= new Bugzilla::FlagType($self->{'type_id'});
- return $self->{'type'};
+ return $self->{'type'}
+ ||= new Bugzilla::FlagType($self->{'type_id'}, cache => 1 );
}
sub setter {
diff --git a/extensions/Review/Extension.pm b/extensions/Review/Extension.pm
index cc0f0f3de..b00fc4e69 100644
--- a/extensions/Review/Extension.pm
+++ b/extensions/Review/Extension.pm
@@ -15,6 +15,8 @@ our $VERSION = '1';
use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Error;
+use Bugzilla::Extension::Review::Util;
+use Bugzilla::Install::Filesystem;
use Bugzilla::User;
use Bugzilla::Util qw(clean_text);
@@ -35,7 +37,7 @@ BEGIN {
}
#
-# reviewers
+# monkey-patched methods
#
sub _product_reviewers { _reviewers($_[0], 'product', $_[1]) }
@@ -124,6 +126,9 @@ sub object_columns {
if ($class->isa('Bugzilla::Product')) {
push @$columns, 'reviewer_required';
}
+ elsif ($class->isa('Bugzilla::User')) {
+ push @$columns, qw(review_request_count feedback_request_count needinfo_request_count);
+ }
}
sub object_update_columns {
@@ -132,6 +137,9 @@ sub object_update_columns {
if ($object->isa('Bugzilla::Product')) {
push @$columns, 'reviewer_required';
}
+ elsif ($object->isa('Bugzilla::User')) {
+ push @$columns, qw(review_request_count feedback_request_count needinfo_request_count);
+ }
}
#
@@ -157,23 +165,91 @@ sub object_end_of_set_all {
sub object_end_of_create {
my ($self, $args) = @_;
my ($object, $params) = @$args{qw(object params)};
- return unless $object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component');;
- my ($new, $new_users) = _new_reviewers_from_input();
- _update_reviewers($object, [], $new_users);
+ if ($object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component')) {
+ my ($new, $new_users) = _new_reviewers_from_input();
+ _update_reviewers($object, [], $new_users);
+ }
+ elsif (_is_countable_flag($object) && $object->requestee_id && $object->status eq '?') {
+ _adjust_request_count($object, +1);
+ }
}
sub object_end_of_update {
my ($self, $args) = @_;
my ($object, $old_object, $changes) = @$args{qw(object old_object changes)};
- return unless $object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component');;
- my ($new, $new_users) = _new_reviewers_from_input();
- my $old = $old_object->reviewers(1);
- if ($old ne $new) {
- _update_reviewers($object, $old_object->reviewers_objs(1), $new_users);
- $changes->{reviewers} = [ $old ? $old : undef, $new ? $new : undef ];
+ if ($object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component')) {
+ my ($new, $new_users) = _new_reviewers_from_input();
+ my $old = $old_object->reviewers(1);
+ if ($old ne $new) {
+ _update_reviewers($object, $old_object->reviewers_objs(1), $new_users);
+ $changes->{reviewers} = [ $old ? $old : undef, $new ? $new : undef ];
+ }
+ }
+ elsif (_is_countable_flag($object)) {
+ my ($old_status, $new_status) = ($old_object->status, $object->status);
+ if ($old_status ne '?' && $new_status eq '?') {
+ # setting flag to ?
+ _adjust_request_count($object, +1);
+ }
+ elsif ($old_status eq '?' && $new_status ne '?') {
+ # setting flag from ?
+ _adjust_request_count($old_object, -1);
+ }
+ elsif ($old_object->requestee_id && !$object->requestee_id) {
+ # removing requestee
+ _adjust_request_count($old_object, -1);
+ }
+ elsif (!$old_object->requestee_id && $object->requestee_id) {
+ # setting requestee
+ _adjust_request_count($object, +1);
+ }
+ elsif ($old_object->requestee_id && $object->requestee_id
+ && $old_object->requestee_id != $object->requestee_id)
+ {
+ # changing requestee
+ _adjust_request_count($old_object, -1);
+ _adjust_request_count($object, +1);
+ }
+ }
+}
+
+sub object_before_delete {
+ my ($self, $args) = @_;
+ my $object = $args->{object};
+
+ if (_is_countable_flag($object) && $object->requestee_id && $object->status eq '?') {
+ _adjust_request_count($object, -1);
+ }
+}
+
+sub _is_countable_flag {
+ my ($object) = @_;
+ return unless $object->isa('Bugzilla::Flag');
+ my $type_name = $object->type->name;
+ return $type_name eq 'review' || $type_name eq 'feedback' || $type_name eq 'needinfo';
+}
+
+sub _adjust_request_count {
+ my ($flag, $add) = @_;
+ return unless my $requestee_id = $flag->requestee_id;
+ my $field = $flag->type->name . '_request_count';
+
+ # update the current user's object so things are display correctly on the
+ # post-processing page
+ my $user = Bugzilla->user;
+ if ($requestee_id == $user->id) {
+ $user->{$field} += $add;
}
+
+ # update database directly to avoid creating audit_log entries
+ $add = $add == -1 ? ' - 1' : ' + 1';
+ Bugzilla->dbh->do(
+ "UPDATE profiles SET $field = $field $add WHERE userid = ?",
+ undef,
+ $requestee_id
+ );
}
sub _new_reviewers_from_input {
@@ -295,7 +371,7 @@ sub flag_end_of_update {
}
#
-# web service / reports
+# web service / pages
#
sub webservice {
@@ -306,7 +382,18 @@ sub webservice {
sub page_before_template {
my ($self, $args) = @_;
- return unless $args->{page_id} eq 'review_suggestions.html';
+
+ if ($args->{page_id} eq 'review_suggestions.html') {
+ $self->review_suggestions_report($args);
+ }
+ elsif ($args->{page_id} eq 'review_requests_rebuild.html') {
+ $self->review_requests_rebuild($args);
+ }
+}
+
+sub review_suggestions_report {
+ my ($self, $args) = @_;
+
my $user = Bugzilla->login(LOGIN_REQUIRED);
my $products = [];
my @products = sort { lc($a->name) cmp lc($b->name) }
@@ -338,6 +425,24 @@ sub page_before_template {
$args->{vars}->{products} = $products;
}
+sub review_requests_rebuild {
+ my ($self, $args) = @_;
+
+ Bugzilla->user->in_group('admin')
+ || ThrowUserError('auth_failure', { group => 'admin',
+ action => 'run',
+ object => 'review_requests_rebuild' });
+ if (Bugzilla->cgi->param('rebuild')) {
+ my $processed_users = 0;
+ rebuild_review_counters(sub {
+ my ($count, $total) = @_;
+ $processed_users = $total;
+ });
+ $args->{vars}->{rebuild} = 1;
+ $args->{vars}->{total} = $processed_users;
+ }
+}
+
#
# installation
#
@@ -449,4 +554,14 @@ sub install_update_db {
);
}
+sub install_filesystem {
+ my ($self, $args) = @_;
+ my $files = $args->{files};
+ my $extensions_dir = bz_locations()->{extensionsdir};
+ $files->{"$extensions_dir/Review/bin/review_requests_rebuild.pl"} = {
+ perms => Bugzilla::Install::Filesystem::OWNER_EXECUTE
+ };
+}
+
+
__PACKAGE__->NAME;
diff --git a/extensions/Review/bin/review_requests_rebuild.pl b/extensions/Review/bin/review_requests_rebuild.pl
new file mode 100755
index 000000000..04f8b1042
--- /dev/null
+++ b/extensions/Review/bin/review_requests_rebuild.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+# 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.
+
+use strict;
+use warnings;
+$| = 1;
+
+use FindBin qw($Bin);
+use lib "$Bin/../../..";
+
+use Bugzilla;
+BEGIN { Bugzilla->extensions() }
+
+use Bugzilla::Constants;
+use Bugzilla::Install::Util qw(indicate_progress);
+use Bugzilla::Extension::Review::Util;
+
+Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
+
+rebuild_review_counters(sub{
+ my ($count, $total) = @_;
+ indicate_progress({ current => $count, total => $total, every => 5 });
+});
diff --git a/extensions/Review/lib/Util.pm b/extensions/Review/lib/Util.pm
new file mode 100644
index 000000000..7304f9ba6
--- /dev/null
+++ b/extensions/Review/lib/Util.pm
@@ -0,0 +1,63 @@
+# 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::Extension::Review::Util;
+use strict;
+use warnings;
+
+use base qw(Exporter);
+use Bugzilla;
+
+our @EXPORT = qw( rebuild_review_counters );
+
+sub rebuild_review_counters {
+ my ($callback) = @_;
+ my $dbh = Bugzilla->dbh;
+
+ $dbh->bz_start_transaction;
+ my $rows = $dbh->selectall_arrayref("
+ SELECT flags.requestee_id AS user_id,
+ flagtypes.name AS flagtype,
+ COUNT(*) as count
+ FROM flags
+ INNER JOIN profiles ON profiles.userid = flags.requestee_id
+ INNER JOIN flagtypes ON flagtypes.id = flags.type_id
+ WHERE flags.status = '?'
+ AND flagtypes.name IN ('review', 'feedback', 'needinfo')
+ GROUP BY flags.requestee_id, flagtypes.name
+ ", { Slice => {} });
+
+ my ($count, $total, $current) = (1, scalar(@$rows), { id => 0 });
+ foreach my $row (@$rows) {
+ $callback->($count++, $total) if $callback;
+ if ($row->{user_id} != $current->{id}) {
+ _update_profile($dbh, $current) if $current->{id};
+ $current = { id => $row->{user_id} };
+ }
+ $current->{$row->{flagtype}} = $row->{count};
+ }
+ _update_profile($dbh, $current) if $current->{id};
+ $dbh->bz_commit_transaction;
+}
+
+sub _update_profile {
+ my ($dbh, $data) = @_;
+ $dbh->do("
+ UPDATE profiles
+ SET review_request_count = ?,
+ feedback_request_count = ?,
+ needinfo_request_count = ?
+ WHERE userid = ?",
+ undef,
+ $data->{review} || 0,
+ $data->{feedback} || 0,
+ $data->{needinfo} || 0,
+ $data->{id}
+ );
+}
+
+1;
diff --git a/extensions/Review/template/en/default/hook/global/header-message.html.tmpl b/extensions/Review/template/en/default/hook/global/header-message.html.tmpl
new file mode 100644
index 000000000..33e899492
--- /dev/null
+++ b/extensions/Review/template/en/default/hook/global/header-message.html.tmpl
@@ -0,0 +1,23 @@
+[%# 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.
+ #%]
+
+[% RETURN UNLESS
+ user.review_request_count
+ || user.feedback_request_count
+ || user.needinfo_request_count
+%]
+
+<span id="badge" title="Flags requested of you:
+ [%- " review (" _ user.review_request_count _ ")" IF user.review_request_count -%]
+ [%- " feedback (" _ user.feedback_request_count _ ")" IF user.feedback_request_count -%]
+ [%- " needinfo (" _ user.needinfo_request_count _ ")" IF user.needinfo_request_count -%]
+">
+ <a href="request.cgi?action=queue&amp;requestee=[% user.login FILTER uri %]&amp;group=type">
+ [%- user.review_request_count + user.feedback_request_count + user.needinfo_request_count ~%]
+ </a>
+</span>
diff --git a/extensions/Review/template/en/default/hook/global/header-start.html.tmpl b/extensions/Review/template/en/default/hook/global/header-start.html.tmpl
index e086e9b55..585041e99 100644
--- a/extensions/Review/template/en/default/hook/global/header-start.html.tmpl
+++ b/extensions/Review/template/en/default/hook/global/header-start.html.tmpl
@@ -6,6 +6,13 @@
# defined by the Mozilla Public License, v. 2.0.
#%]
+[% IF user.review_request_count
+ || user.feedback_request_count
+ || user.needinfo_request_count
+%]
+ [% style_urls.push('extensions/Review/web/styles/badge.css') %]
+[% END %]
+
[% RETURN UNLESS template.name == 'attachment/edit.html.tmpl'
|| template.name == 'attachment/create.html.tmpl'
|| template.name == 'attachment/diff-header.html.tmpl'
diff --git a/extensions/Review/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl b/extensions/Review/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl
new file mode 100644
index 000000000..156f0aa93
--- /dev/null
+++ b/extensions/Review/template/en/default/hook/global/user-error-auth_failure_object.html.tmpl
@@ -0,0 +1,11 @@
+[%# 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.
+ #%]
+
+[% IF object == 'review_requests_rebuild' %]
+ rebuild review request counters
+[% END %]
diff --git a/extensions/Review/template/en/default/pages/review_requests_rebuild.html.tmpl b/extensions/Review/template/en/default/pages/review_requests_rebuild.html.tmpl
new file mode 100644
index 000000000..5ec811126
--- /dev/null
+++ b/extensions/Review/template/en/default/pages/review_requests_rebuild.html.tmpl
@@ -0,0 +1,23 @@
+[%# 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.
+ #%]
+
+[% INCLUDE global/header.html.tmpl
+ title = "Review Requests Rebuild"
+%]
+
+[% IF rebuild %]
+ Counters rebuilt for [% total FILTER html %] users.
+[% ELSE %]
+ <form method="post">
+ <input type="hidden" name="id" value="review_requests_rebuild.html">
+ <input type="hidden" name="rebuild" value="1">
+ <input type="submit" value="Rebuild Review Request Counters">
+ </form>
+[% END %]
+
+[% INCLUDE global/footer.html.tmpl %]
diff --git a/extensions/Review/web/styles/badge.css b/extensions/Review/web/styles/badge.css
new file mode 100644
index 000000000..8b0791f12
--- /dev/null
+++ b/extensions/Review/web/styles/badge.css
@@ -0,0 +1,19 @@
+/* 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. */
+
+#badge {
+ background-color: #c00;
+ font-size: small;
+ font-weight: bold;
+ padding: 0 5px;
+ border-radius: 10px;
+ margin: 0 5px;
+}
+
+#badge a {
+ color: #fff !important;
+}
diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl
index 87ce891f9..9336d5cd4 100644
--- a/template/en/default/global/header.html.tmpl
+++ b/template/en/default/global/header.html.tmpl
@@ -252,7 +252,9 @@
<td id="title">
<a href="./" title="Home">[% terms.BugzillaTitle %]</a>
</td>
- <td id="information"></td>
+ <td>
+ [% Hook.process("message") %]
+ </td>
<td id="moz_login">
[% IF user.id %]
<ul class="links">
@@ -321,6 +323,7 @@
<tr>
<td id="title">
<p>[% terms.BugzillaTitle %]
+ [% Hook.process("message") %]
[% " &ndash; $header" IF header %]</p>
</td>