From ccea670dcba24ff2ac0233437aa549b22edb390c Mon Sep 17 00:00:00 2001 From: Dylan William Hardison Date: Fri, 10 Oct 2014 13:58:41 -0400 Subject: Bug 1074586 - New Feature: Bugs of Interest r=dkl --- extensions/MyDashboard/Extension.pm | 112 +++++++++++++++++++++ extensions/MyDashboard/lib/BugInterest.pm | 70 +++++++++++++ extensions/MyDashboard/lib/Queries.pm | 17 ++++ extensions/MyDashboard/lib/WebService.pm | 27 +++++ .../en/default/pages/mydashboard.html.tmpl | 4 +- extensions/MyDashboard/web/js/query.js | 25 +++-- extensions/MyDashboard/web/styles/mydashboard.css | 3 +- 7 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 extensions/MyDashboard/lib/BugInterest.pm (limited to 'extensions/MyDashboard') diff --git a/extensions/MyDashboard/Extension.pm b/extensions/MyDashboard/Extension.pm index 082f1c562..51e774b5b 100644 --- a/extensions/MyDashboard/Extension.pm +++ b/extensions/MyDashboard/Extension.pm @@ -12,10 +12,13 @@ use strict; use base qw(Bugzilla::Extension); use Bugzilla; +use Bugzilla::Status 'is_open_state'; + use Bugzilla::Constants; use Bugzilla::Search::Saved; use Bugzilla::Extension::MyDashboard::Queries qw(QUERY_DEFS); +use Bugzilla::Extension::MyDashboard::BugInterest; our $VERSION = BUGZILLA_VERSION; @@ -45,6 +48,33 @@ sub db_schema_abstract_schema { mydashboard_user_id_idx => ['user_id'], ], }; + + $schema->{'bug_interest'} = { + FIELDS => [ + id => { TYPE => 'MEDIUMSERIAL', + NOTNULL => 1, + PRIMARYKEY => 1 }, + + bug_id => { TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => { TABLE => 'bugs', + COLUMN => 'bug_id', + DELETE => 'CASCADE' } }, + + user_id => { TYPE => 'INT3', + NOTNOLL => 1, + REFERENCES => { TABLE => 'profiles', + COLUMN => 'userid' } }, + + modification_time => { TYPE => 'DATETIME', + NOTNULL => 1 } + ], + INDEXES => [ + bug_interest_idx => { FIELDS => [qw(bug_id user_id)], + TYPE => 'UNIQUE' }, + bug_interest_user_id_idx => ['user_id'] + ], + }; } ########### @@ -53,6 +83,7 @@ sub db_schema_abstract_schema { BEGIN { *Bugzilla::Search::Saved::in_mydashboard = \&_in_mydashboard; + *Bugzilla::Component::watcher_ids = \&_component_watcher_ids; } sub _in_mydashboard { @@ -65,6 +96,22 @@ sub _in_mydashboard { return $self->{'in_mydashboard'}; } +sub _component_watcher_ids { + my ($self) = @_; + my $dbh = Bugzilla->dbh; + + my $query = "SELECT user_id FROM component_watch + WHERE product_id = ? + AND (component_id = ? + OR component_id IS NULL + OR ? LIKE CONCAT(component_prefix, '%'))"; + + $self->{watcher_ids} ||= $dbh->selectcol_arrayref($query, undef, + $self->product_id, $self->id, $self->name); + + return $self->{watcher_ids}; +} + ############# # Templates # ############# @@ -123,4 +170,69 @@ sub webservice { $dispatch->{MyDashboard} = "Bugzilla::Extension::MyDashboard::WebService"; } +sub bug_end_of_create { + my ($self, $args) = @_; + my ($bug, $params, $timestamp) = @$args{qw(bug params timestamp)}; + my $user = Bugzilla->user; + + # Anyone added to the CC list of a bug is now interested in that bug. + foreach my $cc_user (@{ $bug->cc_users }) { + next $user->id == $cc_user->id; + Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id, $timestamp); + } + + # Anyone that is watching a component is interested when a bug is filed into the component. + foreach my $watcher_id (@{ $bug->component_obj->watcher_ids }) { + Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id, $timestamp); + } +} + +sub bug_end_of_update { + my ($self, $args) = @_; + my ($bug, $old_bug, $changes, $timestamp) = @$args{qw(bug old_bug changes timestamp)}; + my $user = Bugzilla->user; + + # Anyone added to the CC list of a bug is now interested in that bug. + my %old_cc = map { $_->id => $_ } grep { defined } @{ $old_bug->cc_users }; + my @added = grep { not $old_cc{ $_->id } } grep { defined } @{ $bug->cc_users }; + foreach my $cc_user (@added) { + next $user->id == $cc_user->id; + Bugzilla::Extension::MyDashboard::BugInterest->mark($cc_user->id, $bug->id, $timestamp); + } + + # Anyone that is watching a component is interested when a bug is filed into the component. + if ($changes->{product} or $changes->{component}) { + # All of the watchers would be interested in this bug update + foreach my $watcher_id (@{ $bug->component_obj->watcher_ids }) { + Bugzilla::Extension::MyDashboard::BugInterest->mark($watcher_id, $bug->id, $timestamp); + } + } + + if ($changes->{bug_status}) { + my ($old_status, $new_status) = @{ $changes->{bug_status} }; + if (is_open_state($old_status) && !is_open_state($new_status)) { + my @related_bugs = (@{ $bug->blocks_obj }, @{ $bug->depends_on_obj }); + my %involved; + + foreach my $related_bug (@related_bugs) { + my @users = grep { defined } $related_bug->assigned_to, + $related_bug->reporter, + $related_bug->qa_contact, + @{ $related_bug->cc_users }; + + foreach my $involved_user (@users) { + $involved{ $involved_user->id }{ $related_bug->id } = 1; + } + } + foreach my $involved_user_id (keys %involved) { + foreach my $related_bug_id (keys %{$involved{$involved_user_id}}) { + Bugzilla::Extension::MyDashboard::BugInterest->mark($involved_user_id, + $related_bug_id, + $timestamp); + } + } + } + } +} + __PACKAGE__->NAME; diff --git a/extensions/MyDashboard/lib/BugInterest.pm b/extensions/MyDashboard/lib/BugInterest.pm new file mode 100644 index 000000000..4f14eb4fd --- /dev/null +++ b/extensions/MyDashboard/lib/BugInterest.pm @@ -0,0 +1,70 @@ +# 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::MyDashboard::BugInterest; + +use 5.10.1; +use strict; + +use parent qw(Bugzilla::Object); + +##################################################################### +# Overriden Constants that are used as methods +##################################################################### + +use constant DB_TABLE => 'bug_interest'; +use constant DB_COLUMNS => qw( id bug_id user_id modification_time ); +use constant UPDATE_COLUMNS => qw( modification_time ); +use constant VALIDATORS => {}; +use constant LIST_ORDER => 'id'; +use constant NAME_FIELD => 'id'; + +# turn off auditing and exclude these objects from memcached +use constant { AUDIT_CREATES => 0, + AUDIT_UPDATES => 0, + AUDIT_REMOVES => 0, + USE_MEMCACHED => 0 }; + +##################################################################### +# Provide accessors for our columns +##################################################################### + +sub id { return $_[0]->{id} } +sub bug_id { return $_[0]->{bug_id} } +sub user_id { return $_[0]->{user_id} } +sub modification_time { return $_[0]->{modification_time} } + +sub mark { + my ($class, $user_id, $bug_id, $timestamp) = @_; + + my ($interest) = @{ $class->match({ user_id => $user_id, + bug_id => $bug_id }) }; + if ($interest) { + $interest->set(modification_time => $timestamp); + $interest->update(); + return $interest; + } + else { + return $class->create({ + user_id => $user_id, + bug_id => $bug_id, + modification_time => $timestamp, + }); + } +} + +sub unmark { + my ($class, $user_id, $bug_id) = @_; + + my ($interest) = @{ $class->match({ user_id => $user_id, + bug_id => $bug_id }) }; + if ($interest) { + $interest->remove_from_db(); + } +} + +1; diff --git a/extensions/MyDashboard/lib/Queries.pm b/extensions/MyDashboard/lib/Queries.pm index 9dff5abe4..34c63e07b 100644 --- a/extensions/MyDashboard/lib/Queries.pm +++ b/extensions/MyDashboard/lib/Queries.pm @@ -106,16 +106,33 @@ sub QUERY_DEFS { name => 'lastvisitedbugs', heading => 'Updated Since Last Visit', description => 'Bugs updated since last visited', + mark_read => 'Mark Visited', params => { o1 => 'lessthan', v1 => '%last_changed%', f1 => 'last_visit_ts', }, }, + { + name => 'interestingbugs', + heading => 'Interesting Bugs', + description => 'Bugs that you may find interesting', + mark_read => 'Remove Interest', + params => { + j_top => 'OR', + f1 => 'bug_interest_ts', + o1 => 'isnotempty', + + f2 => 'last_visit_ts', + o2 => 'lessthan', + v2 => '%last_changed%', + } + }, { name => 'nevervisitbugs', heading => 'Involved with and Never Visited', description => "Bugs you've never visited, but are involved with", + mark_read => 'Mark Visited', params => { query_format => "advanced", bug_status => ['__open__'],, diff --git a/extensions/MyDashboard/lib/WebService.pm b/extensions/MyDashboard/lib/WebService.pm index 87061eabe..9e9de42be 100644 --- a/extensions/MyDashboard/lib/WebService.pm +++ b/extensions/MyDashboard/lib/WebService.pm @@ -17,6 +17,7 @@ use Bugzilla::Util qw(detaint_natural trick_taint template_var datetime_from); use Bugzilla::WebService::Util qw(validate); use Bugzilla::Extension::MyDashboard::Queries qw(QUERY_DEFS query_bugs query_flags); +use Bugzilla::Extension::MyDashboard::BugInterest; use constant READ_ONLY => qw( run_bug_query @@ -127,6 +128,32 @@ sub run_flag_query { return { result => { $type => $results }}; } +sub bug_interest_unmark { + my ($self, $params) = @_; + my $user = Bugzilla->login(LOGIN_REQUIRED); + + ThrowCodeError('param_required', { function => 'MyDashboard.bug_interest_unmark', param => 'bug_ids' }) + unless $params->{bug_ids}; + + my @bug_ids = ref($params->{bug_ids}) ? @{$params->{bug_ids}} : ( $params->{bug_ids} ); + + Bugzilla->dbh->bz_start_transaction(); + foreach my $bug_id (@bug_ids) { + Bugzilla::Extension::MyDashboard::BugInterest->unmark($user->id, $bug_id); + } + Bugzilla->dbh->bz_commit_transaction(); +} + +sub rest_resources { + return [ + qr{^/bug_interest_unmark$}, { + PUT => { + method => 'bug_interest_unmark' + } + } + ]; +} + 1; __END__ diff --git a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl index 16f363f49..350c8d0aa 100644 --- a/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl +++ b/extensions/MyDashboard/template/en/default/pages/mydashboard.html.tmpl @@ -107,8 +107,8 @@ 0 [% terms.bugs %] found | Refresh - | Mark Visited - Mark Visited + | none + none | Buglist
diff --git a/extensions/MyDashboard/web/js/query.js b/extensions/MyDashboard/web/js/query.js index 4a6b64157..42dfca561 100644 --- a/extensions/MyDashboard/web/js/query.js +++ b/extensions/MyDashboard/web/js/query.js @@ -57,7 +57,8 @@ YUI({ metaFields: { description: "result.result.description", heading: "result.result.heading", - buffer: "result.result.buffer" + buffer: "result.result.buffer", + mark_read: "result.result.mark_read" } } }); @@ -82,6 +83,19 @@ YUI({ '' + e.response.results.length + ' bugs found'); bugQueryTable.set('data', e.response.results); + + var mark_read = e.response.meta.mark_read; + if (mark_read) { + Y.one('#query_markread').setHTML( mark_read ); + Y.one('#bar_markread').removeClass('bz_default_hidden'); + Y.one('#query_markread_text').setHTML( mark_read ); + Y.one('#query_markread').removeClass('bz_default_hidden'); + } + else { + Y.one('#bar_markread').addClass('bz_default_hidden'); + Y.one('#query_markread').addClass('bz_default_hidden'); + } + Y.one('#query_markread_text').addClass('bz_default_hidden'); } }, failure: function(o) { @@ -99,8 +113,6 @@ YUI({ counter = counter + 1; lastChangesCache = {}; - Y.one('#query_markvisited').removeClass('bz_default_hidden'); - Y.one('#query_markvisited_text').addClass('bz_default_hidden'); Y.one('#query_count_refresh').addClass('bz_default_hidden'); bugQueryTable.set('data', []); bugQueryTable.render("#query_table"); @@ -240,17 +252,18 @@ YUI({ updateQueryTable(selected_value); }); - Y.one('#query_markvisited').on('click', function(e) { + Y.one('#query_markread').on('click', function(e) { var data = bugQueryTable.data; var bug_ids = []; - Y.one('#query_markvisited').addClass('bz_default_hidden'); - Y.one('#query_markvisited_text').removeClass('bz_default_hidden'); + Y.one('#query_markread').addClass('bz_default_hidden'); + Y.one('#query_markread_text').removeClass('bz_default_hidden'); for (var i = 0, l = data.size(); i < l; i++) { bug_ids.push(data.item(i).get('bug_id')); } YAHOO.bugzilla.bugUserLastVisit.update(bug_ids); + YAHOO.bugzilla.bugInterest.unmark(bug_ids); }); Y.one('#query_buglist').on('click', function(e) { diff --git a/extensions/MyDashboard/web/styles/mydashboard.css b/extensions/MyDashboard/web/styles/mydashboard.css index 2ce19d96b..d7deadcad 100644 --- a/extensions/MyDashboard/web/styles/mydashboard.css +++ b/extensions/MyDashboard/web/styles/mydashboard.css @@ -47,8 +47,7 @@ width: 40%; } -.items_found, .refresh, .buglist, .markvisited { - +.items_found, .refresh, .buglist, .markread { font-size: 80%; } -- cgit v1.2.3-24-g4f1b