summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorDavid Lawrence [:dkl] <dkl@mozilla.com>2014-06-17 09:52:16 +0200
committerByron Jones <glob@mozilla.com>2014-06-17 09:52:16 +0200
commit464be059d311db4a1bc15933ba007f45f76c11a0 (patch)
treef70d867f0b74b24b9a69d875cd6d44db937302df /extensions
parentaaba5db2aed1ac418d6e4421c1c2760d9c739b70 (diff)
downloadbugzilla-464be059d311db4a1bc15933ba007f45f76c11a0.tar.gz
bugzilla-464be059d311db4a1bc15933ba007f45f76c11a0.tar.xz
Bug 649691: Add a "mentor" and "mentored bug type" field to b.m.o
Diffstat (limited to 'extensions')
-rw-r--r--extensions/Review/Extension.pm229
-rw-r--r--extensions/Review/template/en/default/hook/bug/create/create-after_custom_fields.html.tmpl19
-rw-r--r--extensions/Review/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl50
-rw-r--r--extensions/Review/template/en/default/hook/bug/show-bug_end.xml.tmpl12
4 files changed, 244 insertions, 66 deletions
diff --git a/extensions/Review/Extension.pm b/extensions/Review/Extension.pm
index 6509218f3..ac7cb7233 100644
--- a/extensions/Review/Extension.pm
+++ b/extensions/Review/Extension.pm
@@ -18,8 +18,9 @@ use Bugzilla::Error;
use Bugzilla::Extension::Review::FlagStateActivity;
use Bugzilla::Extension::Review::Util;
use Bugzilla::Install::Filesystem;
+use Bugzilla::Search;
use Bugzilla::User;
-use Bugzilla::Util qw(clean_text);
+use Bugzilla::Util qw(clean_text diff_arrays);
use constant UNAVAILABLE_RE => qr/\b(?:unavailable|pto|away)\b/i;
@@ -77,42 +78,19 @@ sub _reviewers_objs {
sub _bug_mentors {
my ($self) = @_;
- # extract the mentors from the status_whiteboard
- # when the mentors gets its own field, this will be easier
- if (!exists $self->{mentors}) {
- my @mentors;
- my $whiteboard = $self->status_whiteboard;
- my $logout = 0;
- while ($whiteboard =~ /\[mentor=([^\]]+)\]/g) {
- my $mentor_string = $1;
- my $user;
- if ($mentor_string =~ /\@/) {
- # assume it's a full username if it contains an @
- $user = Bugzilla::User->new({ name => $mentor_string });
- } else {
- # otherwise assume it's a : prefixed nick. only works if a
- # single user matches.
-
- # we need to be logged in to do user searching
- if (!Bugzilla->user->id) {
- Bugzilla->set_user(Bugzilla::User->check({ name => 'nobody@mozilla.org' }));
- $logout = 1;
- }
-
- foreach my $query ("*:$mentor_string*", "*$mentor_string*") {
- my $matches = Bugzilla::User::match($query, 2);
- if ($matches && scalar(@$matches) == 1) {
- $user = $matches->[0];
- last;
- }
- }
- }
- push @mentors, $user if $user;
+ my $dbh = Bugzilla->dbh;
+ if (!$self->{bug_mentors}) {
+ my $mentor_ids = $dbh->selectcol_arrayref("
+ SELECT user_id FROM bug_mentors WHERE bug_id = ?",
+ undef,
+ $self->id);
+ $self->{bug_mentors} = [];
+ foreach my $mentor_id (@$mentor_ids) {
+ push(@{ $self->{bug_mentors} },
+ Bugzilla::User->new({ id => $mentor_id, cache => 1 }));
}
- Bugzilla->logout_request() if $logout;
- $self->{mentors} = \@mentors;
}
- return $self->{mentors};
+ return [ sort { $a->login cmp $b->login } @{ $self->{bug_mentors} } ];
}
sub _bug_is_mentor {
@@ -191,8 +169,8 @@ sub object_end_of_create {
my ($object, $params) = @$args{qw(object params)};
if ($object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component')) {
- my ($new, $new_users) = _new_reviewers_from_input();
- _update_reviewers($object, [], $new_users);
+ my ($new, $new_users) = _new_users_from_input('reviewers');
+ _update_users($object, [], $new_users);
}
elsif (_is_countable_flag($object) && $object->requestee_id && $object->status eq '?') {
_adjust_request_count($object, +1);
@@ -200,6 +178,10 @@ sub object_end_of_create {
if (_is_countable_flag($object)) {
$self->_log_flag_state_activity($object, $object->status);
}
+ elsif ($object->isa('Bugzilla::Bug')) {
+ my ($new, $new_users) = _new_users_from_input('bug_mentors');
+ _update_users($object, [], $new_users);
+ }
}
sub object_end_of_update {
@@ -207,10 +189,10 @@ sub object_end_of_update {
my ($object, $old_object, $changes) = @$args{qw(object old_object changes)};
if ($object->isa('Bugzilla::Product') || $object->isa('Bugzilla::Component')) {
- my ($new, $new_users) = _new_reviewers_from_input();
+ my ($new, $new_users) = _new_users_from_input('reviewers');
my $old = $old_object->reviewers(1);
if ($old ne $new) {
- _update_reviewers($object, $old_object->reviewers_objs(1), $new_users);
+ _update_users($object, $old_object->reviewers_objs(1), $new_users);
$changes->{reviewers} = [ $old ? $old : undef, $new ? $new : undef ];
}
}
@@ -240,6 +222,19 @@ sub object_end_of_update {
_adjust_request_count($object, +1);
}
}
+ elsif ($object->isa('Bugzilla::Bug')) {
+ my ($new, $new_mentors) = _new_users_from_input('bug_mentors');
+ my $old = join(", ", map { $_->login } @{ $old_object->mentors });
+ if ($old ne $new) {
+ _update_users($object, $old_object->mentors, $new_mentors);
+
+ my @old_names = map { $_->login } @{ $old_object->mentors };
+ my @new_names = map { $_->login } @{ $new_mentors };
+ my ($removed, $added) = diff_arrays(\@old_names, \@new_names);
+ $changes->{bug_mentor} = [ @$removed ? join(", ", @$removed) : undef,
+ @$added ? join(", ", @$added) : undef ];
+ }
+ }
}
sub flag_updated {
@@ -310,12 +305,13 @@ sub _adjust_request_count {
Bugzilla->memcached->clear({ table => 'profiles', id => $requestee_id });
}
-sub _new_reviewers_from_input {
- if (!Bugzilla->input_params->{reviewers}) {
+sub _new_users_from_input {
+ my $field = shift;
+ if (!Bugzilla->input_params->{$field}) {
return ('', []);
}
- Bugzilla::User::match_field({ 'reviewers' => {'type' => 'multi'} });
- my $new = Bugzilla->input_params->{reviewers};
+ Bugzilla::User::match_field({ $field => {'type' => 'multi'} });
+ my $new = Bugzilla->input_params->{$field};
$new = [ $new ] unless ref($new);
my $new_users = [];
foreach my $login (@$new) {
@@ -325,39 +321,64 @@ sub _new_reviewers_from_input {
return ($new, $new_users);
}
-sub _update_reviewers {
+sub _update_users {
my ($object, $old_users, $new_users) = @_;
my $dbh = Bugzilla->dbh;
- my $type = $object->isa('Bugzilla::Product') ? 'product' : 'component';
- # remove deleted users
- foreach my $old_user (@$old_users) {
- if (!grep { $_->id == $old_user->id } @$new_users) {
- $dbh->do(
- "DELETE FROM ${type}_reviewers WHERE ${type}_id=? AND user_id=?",
- undef,
- $object->id, $old_user->id,
- );
+ if ($object->isa('Bugzilla::Bug')) {
+ # remove deleted users
+ foreach my $old_user (@$old_users) {
+ if (!grep { $_->id == $old_user->id } @$new_users) {
+ $dbh->do(
+ "DELETE FROM bug_mentors WHERE bug_id = ? AND user_id = ?",
+ undef,
+ $object->id, $old_user->id,
+ );
+ }
+ }
+ # add new users
+ foreach my $new_user (@$new_users) {
+ if (!grep { $_->id == $new_user->id } @$old_users) {
+ $dbh->do(
+ "INSERT INTO bug_mentors (bug_id,user_id) VALUES (?, ?)",
+ undef,
+ $object->id, $new_user->id,
+ );
+ }
}
}
- # add new users
- foreach my $new_user (@$new_users) {
- if (!grep { $_->id == $new_user->id } @$old_users) {
+ else {
+ my $type = $object->isa('Bugzilla::Product') ? 'product' : 'component';
+
+ # remove deleted users
+ foreach my $old_user (@$old_users) {
+ if (!grep { $_->id == $old_user->id } @$new_users) {
+ $dbh->do(
+ "DELETE FROM ${type}_reviewers WHERE ${type}_id=? AND user_id=?",
+ undef,
+ $object->id, $old_user->id,
+ );
+ }
+ }
+ # add new users
+ foreach my $new_user (@$new_users) {
+ if (!grep { $_->id == $new_user->id } @$old_users) {
+ $dbh->do(
+ "INSERT INTO ${type}_reviewers(${type}_id,user_id) VALUES (?,?)",
+ undef,
+ $object->id, $new_user->id,
+ );
+ }
+ }
+ # and update the sortkey for all users
+ for (my $i = 0; $i < scalar(@$new_users); $i++) {
$dbh->do(
- "INSERT INTO ${type}_reviewers(${type}_id,user_id) VALUES (?,?)",
+ "UPDATE ${type}_reviewers SET sortkey=? WHERE ${type}_id=? AND user_id=?",
undef,
- $object->id, $new_user->id,
+ ($i + 1) * 10, $object->id, $new_users->[$i]->id,
);
}
}
- # and update the sortkey for all users
- for (my $i = 0; $i < scalar(@$new_users); $i++) {
- $dbh->do(
- "UPDATE ${type}_reviewers SET sortkey=? WHERE ${type}_id=? AND user_id=?",
- undef,
- ($i + 1) * 10, $object->id, $new_users->[$i]->id,
- );
- }
}
# bugzilla's handling of requestee matching when creating bugs is "if it's
@@ -439,6 +460,51 @@ sub flag_end_of_update {
}
#
+# search
+#
+
+sub buglist_columns {
+ my ($self, $args) = @_;
+ my $dbh = Bugzilla->dbh;
+ my $columns = $args->{columns};
+ $columns->{bug_mentor} = { title => 'Mentor' };
+ if (Bugzilla->user->id) {
+ $columns->{bug_mentor}->{name}
+ = $dbh->sql_group_concat('map_mentors_names.login_name');
+ }
+ else {
+ $columns->{bug_mentor}->{name}
+ = $dbh->sql_group_concat('map_mentors_names.realname');
+
+ }
+}
+
+sub buglist_column_joins {
+ my ($self, $args) = @_;
+ my $column_joins = $args->{column_joins};
+ $column_joins->{bug_mentor} = {
+ as => 'map_mentors',
+ table => 'bug_mentors',
+ then_to => {
+ as => 'map_mentors_names',
+ table => 'profiles',
+ from => 'map_mentors.user_id',
+ to => 'userid',
+ },
+ },
+}
+
+sub search_operator_field_override {
+ my ($self, $args) = @_;
+ my $operators = $args->{operators};
+ $operators->{bug_mentor} = {
+ _non_changed => sub {
+ Bugzilla::Search::_user_nonchanged(@_)
+ }
+ };
+}
+
+#
# web service / pages
#
@@ -700,6 +766,36 @@ sub db_schema_abstract_schema {
bug_mentors_bug_id_idx => [ 'bug_id' ],
],
};
+
+ $args->{'schema'}->{'bug_mentors'} = {
+ FIELDS => [
+ bug_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {
+ TABLE => 'bugs',
+ COLUMN => 'bug_id',
+ DELETE => 'CASCADE',
+ },
+ },
+ user_id => {
+ TYPE => 'INT3',
+ NOTNULL => 1,
+ REFERENCES => {
+ TABLE => 'profiles',
+ COLUMN => 'userid',
+ DELETE => 'CASCADE',
+ }
+ },
+ ],
+ INDEXES => [
+ bug_mentors_idx => {
+ FIELDS => [ 'bug_id', 'user_id' ],
+ TYPE => 'UNIQUE',
+ },
+ bug_mentors_bug_id_idx => [ 'bug_id' ],
+ ],
+ };
}
sub install_update_db {
@@ -720,6 +816,7 @@ sub install_update_db {
'profiles',
'needinfo_request_count', { TYPE => 'INT2', NOTNULL => 1, DEFAULT => 0 }
);
+
my $field = Bugzilla::Field->new({ name => 'bug_mentor' });
if (!$field) {
Bugzilla::Field->create({
diff --git a/extensions/Review/template/en/default/hook/bug/create/create-after_custom_fields.html.tmpl b/extensions/Review/template/en/default/hook/bug/create/create-after_custom_fields.html.tmpl
new file mode 100644
index 000000000..6d890d975
--- /dev/null
+++ b/extensions/Review/template/en/default/hook/bug/create/create-after_custom_fields.html.tmpl
@@ -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.
+ #%]
+
+<tr>
+ <th class="field_label">Mentors:</th>
+ <td colspan="3" class="field_value">
+ [% INCLUDE global/userselect.html.tmpl
+ id = "bug_mentors"
+ name = "bug_mentors"
+ size = 30
+ multiple = 5
+ %]
+ </td>
+</tr>
diff --git a/extensions/Review/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl b/extensions/Review/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl
new file mode 100644
index 000000000..352dfa6c7
--- /dev/null
+++ b/extensions/Review/template/en/default/hook/bug/edit-after_custom_fields.html.tmpl
@@ -0,0 +1,50 @@
+[%# 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.
+ #%]
+
+[% mentor_logins = [] %]
+[% FOREACH mentor = bug.mentors %]
+ [% mentor_logins.push(mentor.login) %]
+[% END %]
+<tr>
+ <th class="field_label">Mentors:</th>
+ <td>
+ [% IF bug.check_can_change_field("bug_mentors", 0, 1) %]
+ <div id="bz_bug_mentors_edit_container" class="bz_default_hidden">
+ <span>
+ [% FOREACH mentor = bug.mentors %]
+ [% INCLUDE global/user.html.tmpl who = mentor %]<br>
+ [% END %]
+ (<a href="#" id="bz_bug_mentors_edit_action">edit</a>)
+ </span>
+ </div>
+ <div id="bz_bug_mentors_input">
+ [% INCLUDE global/userselect.html.tmpl
+ id = "bug_mentors"
+ name = "bug_mentors"
+ value = mentor_logins.join(", ")
+ classes = ["bz_userfield"]
+ size = 30
+ multiple = 5
+ %]
+ <br>
+ </div>
+ <script type="text/javascript">
+ hideEditableField('bz_bug_mentors_edit_container',
+ 'bz_bug_mentors_input',
+ 'bz_bug_mentors_edit_action',
+ 'bug_mentors',
+ '[% mentor_logins.join(", ") FILTER js %]' );
+ </script>
+ [% ELSE %]
+ [% FOREACH mentor = bug.mentors %]
+ [% INCLUDE global/user.html.tmpl who = mentor %]<br>
+ [% END %]
+ [% END %]
+ </td>
+</tr>
+
diff --git a/extensions/Review/template/en/default/hook/bug/show-bug_end.xml.tmpl b/extensions/Review/template/en/default/hook/bug/show-bug_end.xml.tmpl
new file mode 100644
index 000000000..9ad650b2f
--- /dev/null
+++ b/extensions/Review/template/en/default/hook/bug/show-bug_end.xml.tmpl
@@ -0,0 +1,12 @@
+[%# 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.
+ #%]
+
+[% FOREACH mentor = bug.mentors %]
+ <mentor name="[% mentor.name FILTER xml %]">
+ [% mentor.login FILTER email FILTER xml %]</mentor>
+[% END %]