# 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; use 5.10.1; use strict; use warnings; 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; ################ # Installation # ################ sub db_schema_abstract_schema { my ($self, $args) = @_; my $schema = $args->{schema}; $schema->{'mydashboard'} = { FIELDS => [ namedquery_id => { TYPE => 'INT3', NOTNULL => 1, REFERENCES => {TABLE => 'namedqueries', COLUMN => 'id', DELETE => 'CASCADE'} }, user_id => { TYPE => 'INT3', NOTNULL => 1, REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'} }, ], INDEXES => [ mydashboard_namedquery_id_idx => {FIELDS => [qw(namedquery_id user_id)], TYPE => 'UNIQUE'}, 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'] ], }; } ########### # Objects # ########### BEGIN { *Bugzilla::Search::Saved::in_mydashboard = \&_in_mydashboard; *Bugzilla::Component::watcher_ids = \&_component_watcher_ids; } sub _in_mydashboard { my ($self) = @_; my $dbh = Bugzilla->dbh; return $self->{'in_mydashboard'} if exists $self->{'in_mydashboard'}; $self->{'in_mydashboard'} = $dbh->selectrow_array(" SELECT 1 FROM mydashboard WHERE namedquery_id = ? AND user_id = ?", undef, $self->id, Bugzilla->user->id); 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 @{[$dbh->sql_string_concat('component_prefix', q{'%'})]})"; $self->{watcher_ids} ||= $dbh->selectcol_arrayref($query, undef, $self->product_id, $self->id, $self->name); return $self->{watcher_ids}; } ############# # Templates # ############# sub page_before_template { my ($self, $args) = @_; my $page = $args->{'page_id'}; my $vars = $args->{'vars'}; return if $page ne 'mydashboard.html'; # require user to be logged in for this page Bugzilla->login(LOGIN_REQUIRED); $vars->{queries} = [QUERY_DEFS]; } ######### # Hooks # ######### sub user_preferences { my ($self, $args) = @_; my $tab = $args->{'current_tab'}; return unless $tab eq 'saved-searches'; my $save = $args->{'save_changes'}; my $handled = $args->{'handled'}; my $vars = $args->{'vars'}; my $dbh = Bugzilla->dbh; my $user = Bugzilla->user; my $params = Bugzilla->input_params; if ($save) { my $sth_insert_fp = $dbh->prepare( 'INSERT INTO mydashboard (namedquery_id, user_id) VALUES (?, ?)' ); my $sth_delete_fp = $dbh->prepare( 'DELETE FROM mydashboard WHERE namedquery_id = ? AND user_id = ?' ); foreach my $q (@{$user->queries}) { if (defined $params->{'in_mydashboard_' . $q->id}) { $sth_insert_fp->execute($q->id, $user->id) if !$q->in_mydashboard; } else { $sth_delete_fp->execute($q->id, $user->id) if $q->in_mydashboard; } } } } sub webservice { my ($self, $args) = @_; my $dispatch = $args->{dispatch}; $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 if $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 if $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); } } } } } sub merge_users_before { my ($self, $args) = @_; my $old_id = $args->{old_id}; my $dbh = Bugzilla->dbh; # If the bug_interest table has both the source user # and destination user, then we remove the old user entry. $dbh->do("DELETE FROM bug_interest WHERE user_id = ?", undef, $old_id); } __PACKAGE__->NAME;