parent
a021d9ad90
commit
a9520867da
2
.gitrev
2
.gitrev
|
@ -1 +1 @@
|
|||
36f56bd9112c2e930fb5bdbee3b5c89334de5247
|
||||
eab44b1aad3f243dd69b1d30519b73a1e537fda2
|
|
@ -28,6 +28,7 @@ use Bugzilla::Group;
|
|||
use Bugzilla::Status;
|
||||
use Bugzilla::Comment;
|
||||
use Bugzilla::BugUrl;
|
||||
use Bugzilla::BugUserLastVisit;
|
||||
|
||||
use List::MoreUtils qw(firstidx uniq part);
|
||||
use List::Util qw(min max first);
|
||||
|
@ -4081,6 +4082,23 @@ sub LogActivityEntry {
|
|||
}
|
||||
}
|
||||
|
||||
# Update bug_user_last_visit table
|
||||
sub update_user_last_visit {
|
||||
my ($self, $user, $last_visit_ts) = @_;
|
||||
my $lv = Bugzilla::BugUserLastVisit->match({ bug_id => $self->id,
|
||||
user_id => $user->id })->[0];
|
||||
|
||||
if ($lv) {
|
||||
$lv->set(last_visit_ts => $last_visit_ts);
|
||||
$lv->update;
|
||||
}
|
||||
else {
|
||||
Bugzilla::BugUserLastVisit->create({ bug_id => $self->id,
|
||||
user_id => $user->id,
|
||||
last_visit_ts => $last_visit_ts });
|
||||
}
|
||||
}
|
||||
|
||||
# Convert WebService API and email_in.pl field names to internal DB field
|
||||
# names.
|
||||
sub map_fields {
|
||||
|
@ -4407,6 +4425,7 @@ sub _multi_select_accessor {
|
|||
|
||||
1;
|
||||
|
||||
__END__
|
||||
=head1 B<Methods>
|
||||
|
||||
=over
|
||||
|
@ -4415,6 +4434,11 @@ sub _multi_select_accessor {
|
|||
|
||||
Ensures the accessors for custom fields are always created.
|
||||
|
||||
=item C<update_user_last_visit($user, $last_visit)>
|
||||
|
||||
Creates or updates a L<Bugzilla::BugUserLastVisit> for this bug and the supplied
|
||||
$user, the timestamp given as $last_visit.
|
||||
|
||||
=back
|
||||
|
||||
=head1 B<Methods in need of POD>
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package Bugzilla::BugUserLastVisit;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use parent qw(Bugzilla::Object);
|
||||
|
||||
#####################################################################
|
||||
# Overriden Constants that are used as methods
|
||||
#####################################################################
|
||||
|
||||
use constant DB_TABLE => 'bug_user_last_visit';
|
||||
use constant DB_COLUMNS => qw( id user_id bug_id last_visit_ts );
|
||||
use constant UPDATE_COLUMNS => qw( last_visit_ts );
|
||||
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 last_visit_ts { return $_[0]->{last_visit_ts} }
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::BugUserLastVisit - Model for BugUserLastVisit bug search data
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Bugzilla::BugUserLastVisit;
|
||||
|
||||
my $lv = Bugzilla::BugUserLastVisit->new($id);
|
||||
|
||||
# Class Functions
|
||||
$user = Bugzilla::BugUserLastVisit->create({
|
||||
bug_id => $bug_id,
|
||||
user_id => $user_id,
|
||||
last_visit_ts => $last_visit_ts
|
||||
});
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package handles Bugzilla BugUserLastVisit.
|
||||
|
||||
C<Bugzilla::BugUserLastVisit> is an implementation of L<Bugzilla::Object>, and
|
||||
thus provides all the methods of L<Bugzilla::Object> in addition to the methods
|
||||
listed below.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 Accessor Methods
|
||||
|
||||
=over
|
||||
|
||||
=item C<id>
|
||||
|
||||
=item C<bug_id>
|
||||
|
||||
=item C<user_id>
|
||||
|
||||
=item C<last_visit_ts>
|
||||
|
||||
=back
|
|
@ -33,6 +33,13 @@ sub get_param_list {
|
|||
name => 'allowuserdeletion',
|
||||
type => 'b',
|
||||
default => 0
|
||||
},
|
||||
|
||||
{
|
||||
name => 'last_visit_keep_days',
|
||||
type => 't',
|
||||
default => 10,
|
||||
checker => \&check_numeric
|
||||
});
|
||||
return @param_list;
|
||||
}
|
||||
|
|
|
@ -1713,6 +1713,25 @@ use constant ABSTRACT_SCHEMA => {
|
|||
],
|
||||
},
|
||||
|
||||
bug_user_last_visit => {
|
||||
FIELDS => [
|
||||
id => {TYPE => 'INTSERIAL', NOTNULL => 1,
|
||||
PRIMARYKEY => 1},
|
||||
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'}},
|
||||
last_visit_ts => {TYPE => 'DATETIME', NOTNULL => 1},
|
||||
],
|
||||
INDEXES => [
|
||||
bug_user_last_visit_idx => {FIELDS => ['user_id', 'bug_id'],
|
||||
TYPE => 'UNIQUE'}
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
# Foreign Keys are added in Bugzilla::DB::bz_add_field_tables
|
||||
|
|
|
@ -258,6 +258,8 @@ use constant DEFAULT_FIELDS => (
|
|||
type => FIELD_TYPE_BUG_URLS},
|
||||
{name => 'tag', desc => 'Personal Tags', buglist => 1,
|
||||
type => FIELD_TYPE_KEYWORDS},
|
||||
{name => 'last_visit_ts', desc => 'Last Visit', buglist => 1,
|
||||
type => FIELD_TYPE_DATETIME},
|
||||
{name => 'comment_tag', desc => 'Comment Tag'},
|
||||
);
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ sub FILESYSTEM {
|
|||
'jobqueue.pl' => { perms => OWNER_EXECUTE },
|
||||
'migrate.pl' => { perms => OWNER_EXECUTE },
|
||||
'install-module.pl' => { perms => OWNER_EXECUTE },
|
||||
'clean-bug-user-last-visit.pl' => { perms => WS_EXECUTE },
|
||||
|
||||
'Bugzilla.pm' => { perms => CGI_READ },
|
||||
"$localconfig*" => { perms => CGI_READ },
|
||||
|
|
|
@ -320,6 +320,10 @@ use constant OPERATOR_FIELD_OVERRIDE => {
|
|||
changedafter => \&_work_time_changedbefore_after,
|
||||
_default => \&_work_time,
|
||||
},
|
||||
last_visit_ts => {
|
||||
_non_changed => \&_last_visit_ts,
|
||||
_default => \&_last_visit_ts_invalid_operator,
|
||||
},
|
||||
|
||||
# Custom Fields
|
||||
FIELD_TYPE_FREETEXT, { _non_changed => \&_nullable },
|
||||
|
@ -349,6 +353,10 @@ sub SPECIAL_PARSING {
|
|||
creation_ts => \&_datetime_translate,
|
||||
deadline => \&_date_translate,
|
||||
delta_ts => \&_datetime_translate,
|
||||
|
||||
# last_visit field that accept both a 1d, 1w, 1m, 1y format and the
|
||||
# %last_changed% pronoun.
|
||||
last_visit_ts => \&_last_visit_datetime,
|
||||
};
|
||||
foreach my $field (Bugzilla->active_custom_fields) {
|
||||
if ($field->type == FIELD_TYPE_DATETIME) {
|
||||
|
@ -514,7 +522,14 @@ sub COLUMN_JOINS {
|
|||
from => 'map_bug_tag.tag_id',
|
||||
to => 'id',
|
||||
},
|
||||
}
|
||||
},
|
||||
last_visit_ts => {
|
||||
as => 'bug_user_last_visit',
|
||||
table => 'bug_user_last_visit',
|
||||
extra => ['bug_user_last_visit.user_id = ' . $user->id],
|
||||
from => 'bug_id',
|
||||
to => 'bug_id',
|
||||
},
|
||||
};
|
||||
return $joins;
|
||||
};
|
||||
|
@ -587,6 +602,7 @@ sub COLUMNS {
|
|||
'longdescs.count' => 'COUNT(DISTINCT map_longdescs_count.comment_id)',
|
||||
|
||||
tag => $dbh->sql_group_concat('DISTINCT map_tag.name'),
|
||||
last_visit_ts => 'bug_user_last_visit.last_visit_ts',
|
||||
);
|
||||
|
||||
# Backward-compatibility for old field names. Goes new_name => old_name.
|
||||
|
@ -2141,6 +2157,21 @@ sub _datetime_translate {
|
|||
return shift->_timestamp_translate(0, @_);
|
||||
}
|
||||
|
||||
sub _last_visit_datetime {
|
||||
my ($self, $args) = @_;
|
||||
my $value = $args->{value};
|
||||
|
||||
$self->_datetime_translate($args);
|
||||
if ($value eq $args->{value}) {
|
||||
# Failed to translate a datetime. let's try the pronoun expando.
|
||||
if ($value eq '%last_changed%') {
|
||||
$self->_add_extra_column('changeddate');
|
||||
$args->{value} = $args->{quoted} = 'bugs.delta_ts';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub _date_translate {
|
||||
return shift->_timestamp_translate(1, @_);
|
||||
}
|
||||
|
@ -2622,6 +2653,21 @@ sub _percentage_complete {
|
|||
$self->_add_extra_column('actual_time');
|
||||
}
|
||||
|
||||
sub _last_visit_ts {
|
||||
my ($self, $args) = @_;
|
||||
|
||||
$args->{full_field} = $self->COLUMNS->{last_visit_ts}->{name};
|
||||
$self->_add_extra_column('last_visit_ts');
|
||||
}
|
||||
|
||||
sub _last_visit_ts_invalid_operator {
|
||||
my ($self, $args) = @_;
|
||||
|
||||
ThrowUserError('search_field_operator_invalid',
|
||||
{ field => $args->{field},
|
||||
operator => $args->{operator} });
|
||||
}
|
||||
|
||||
sub _days_elapsed {
|
||||
my ($self, $args) = @_;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
|
|
@ -19,9 +19,11 @@ use Bugzilla::Product;
|
|||
use Bugzilla::Classification;
|
||||
use Bugzilla::Field;
|
||||
use Bugzilla::Group;
|
||||
use Bugzilla::BugUserLastVisit;
|
||||
|
||||
use DateTime::TimeZone;
|
||||
use List::Util qw(max);
|
||||
use List::MoreUtils qw(any);
|
||||
use Scalar::Util qw(blessed);
|
||||
use URI;
|
||||
use URI::QueryParam;
|
||||
|
@ -729,6 +731,28 @@ sub groups {
|
|||
return $self->{groups};
|
||||
}
|
||||
|
||||
sub last_visited {
|
||||
my ($self) = @_;
|
||||
|
||||
return Bugzilla::BugUserLastVisit->match({ user_id => $self->id });
|
||||
}
|
||||
|
||||
sub is_involved_in_bug {
|
||||
my ($self, $bug) = @_;
|
||||
my $user_id = $self->id;
|
||||
my $user_login = $self->login;
|
||||
|
||||
return unless $user_id;
|
||||
return 1 if $user_id == $bug->assigned_to->id;
|
||||
return 1 if $user_id == $bug->reporter->id;
|
||||
|
||||
if (Bugzilla->params->{'useqacontact'} and $bug->qa_contact) {
|
||||
return 1 if $user_id == $bug->qa_contact->id;
|
||||
}
|
||||
|
||||
return any { $user_login eq $_ } @{ $bug->cc };
|
||||
}
|
||||
|
||||
# It turns out that calling ->id on objects a few hundred thousand
|
||||
# times is pretty slow. (It showed up as a significant time contributor
|
||||
# when profiling xt/search.t.) So we cache the group ids separately from
|
||||
|
@ -2767,6 +2791,35 @@ Returns true if the user can attach tags to comments.
|
|||
i.e. if the 'comment_taggers_group' parameter is set and the user belongs to
|
||||
this group.
|
||||
|
||||
=item C<last_visited>
|
||||
|
||||
Returns an arrayref L<Bugzilla::BugUserLastVisit> objects.
|
||||
|
||||
=item C<is_involved_in_bug($bug)>
|
||||
|
||||
Returns true if any of the following conditions are met, false otherwise.
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
|
||||
User is the assignee of the bug
|
||||
|
||||
=item *
|
||||
|
||||
User is the reporter of the bug
|
||||
|
||||
=item *
|
||||
|
||||
User is the QA contact of the bug (if Bugzilla is configured to use a QA
|
||||
contact)
|
||||
|
||||
=item *
|
||||
|
||||
User is in the cc list for the bug.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head1 CLASS FUNCTIONS
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
# 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::WebService::BugUserLastVisit;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use parent qw(Bugzilla::WebService);
|
||||
|
||||
use Bugzilla::Bug;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::WebService::Util qw( validate filter );
|
||||
use Bugzilla::Constants;
|
||||
|
||||
sub update {
|
||||
my ($self, $params) = validate(@_, 'ids');
|
||||
my $user = Bugzilla->user;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
$user->login(LOGIN_REQUIRED);
|
||||
|
||||
my $ids = $params->{ids} // [];
|
||||
ThrowCodeError('param_required', { param => 'ids' }) unless @$ids;
|
||||
|
||||
# Cache permissions for bugs. This highly reduces the number of calls to the
|
||||
# DB. visible_bugs() is only able to handle bug IDs, so we have to skip
|
||||
# aliases.
|
||||
$user->visible_bugs([grep /^[0-9]$/, @$ids]);
|
||||
|
||||
$dbh->bz_start_transaction();
|
||||
my @results;
|
||||
my $last_visit_ts = $dbh->selectrow_array('SELECT NOW()');
|
||||
foreach my $bug_id (@$ids) {
|
||||
my $bug = Bugzilla::Bug->check({ id => $bug_id, cache => 1 });
|
||||
|
||||
ThrowUserError('user_not_involved', { bug_id => $bug->id })
|
||||
unless $user->is_involved_in_bug($bug);
|
||||
|
||||
$bug->update_user_last_visit($user, $last_visit_ts);
|
||||
|
||||
push(
|
||||
@results,
|
||||
$self->_bug_user_last_visit_to_hash(
|
||||
$bug, $last_visit_ts, $params
|
||||
));
|
||||
}
|
||||
$dbh->bz_commit_transaction();
|
||||
|
||||
return \@results;
|
||||
}
|
||||
|
||||
sub get {
|
||||
my ($self, $params) = validate(@_, 'ids');
|
||||
my $user = Bugzilla->user;
|
||||
my $ids = $params->{ids};
|
||||
|
||||
$user->login(LOGIN_REQUIRED);
|
||||
|
||||
if ($ids) {
|
||||
# Cache permissions for bugs. This highly reduces the number of calls to
|
||||
# the DB. visible_bugs() is only able to handle bug IDs, so we have to
|
||||
# skip aliases.
|
||||
$user->visible_bugs([grep /^[0-9]$/, @$ids]);
|
||||
}
|
||||
|
||||
my @last_visits = @{ $user->last_visits };
|
||||
|
||||
if ($ids) {
|
||||
# remove bugs that we arn't interested in if ids is passed in.
|
||||
my %id_set = map { ($_ => 1) } @$ids;
|
||||
@last_visits = grep { $id_set{ $_->bug_id } } @last_visits;
|
||||
}
|
||||
|
||||
return [
|
||||
map {
|
||||
$self->_bug_user_last_visit_to_hash($_->bug_id, $_->last_visit_ts,
|
||||
$params)
|
||||
} @last_visits
|
||||
];
|
||||
}
|
||||
|
||||
sub _bug_user_last_visit_to_hash {
|
||||
my ($self, $bug_id, $last_visit_ts, $params) = @_;
|
||||
|
||||
my %result = (id => $self->type('int', $bug_id),
|
||||
last_visit_ts => $self->type('dateTime', $last_visit_ts));
|
||||
|
||||
return filter($params, \%result);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::WebService::BugUserLastVisit - Find and Store the last time a user
|
||||
visited a bug.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head2 update
|
||||
|
||||
B<EXPERIMENTAL>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
Update the last visit time for the specified bug and current user.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To add a single bug id:
|
||||
|
||||
POST /rest/bug_user_last_visit/<bug-id>
|
||||
|
||||
Tp add one or more bug ids at once:
|
||||
|
||||
POST /rest/bug_user_last_visit
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<ids> (array) - One or more bug ids to add.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
|
||||
=over
|
||||
|
||||
=item C<array> - An array of hashes containing the following:
|
||||
|
||||
=over
|
||||
|
||||
=item C<id> - (int) The bug id.
|
||||
|
||||
=item C<last_visit_ts> - (string) The timestamp the user last visited the bug.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 get
|
||||
|
||||
B<EXPERIMENTAL>
|
||||
|
||||
=over
|
||||
|
||||
=item B<Description>
|
||||
|
||||
Get the last visited timestamp for one or more specified bug ids or get a
|
||||
list of the last 20 visited bugs and their timestamps.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To return the last visited timestamp for a single bug id:
|
||||
|
||||
GET /rest/bug_visit/<bug-id>
|
||||
|
||||
To return more than one bug timestamp or the last 20:
|
||||
|
||||
GET /rest/bug_visit
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
||||
=item C<ids> (integer) - One or more optional bug ids to get.
|
||||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
|
||||
=over
|
||||
|
||||
=item C<array> - An array of hashes containing the following:
|
||||
|
||||
=over
|
||||
|
||||
=item C<id> - (int) The bug id.
|
||||
|
||||
=item C<last_visit_ts> - (string) The timestamp the user last visited the bug.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=back
|
|
@ -266,12 +266,13 @@ sub WS_DISPATCH {
|
|||
Bugzilla::Hook::process('webservice', { dispatch => \%hook_dispatch });
|
||||
|
||||
my $dispatch = {
|
||||
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
|
||||
'Bug' => 'Bugzilla::WebService::Bug',
|
||||
'Classification' => 'Bugzilla::WebService::Classification',
|
||||
'Group' => 'Bugzilla::WebService::Group',
|
||||
'Product' => 'Bugzilla::WebService::Product',
|
||||
'User' => 'Bugzilla::WebService::User',
|
||||
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
|
||||
'Bug' => 'Bugzilla::WebService::Bug',
|
||||
'Classification' => 'Bugzilla::WebService::Classification',
|
||||
'Group' => 'Bugzilla::WebService::Group',
|
||||
'Product' => 'Bugzilla::WebService::Product',
|
||||
'User' => 'Bugzilla::WebService::User',
|
||||
'BugUserLastVisit' => 'Bugzilla::WebService::BugUserLastVisit',
|
||||
%hook_dispatch
|
||||
};
|
||||
return $dispatch;
|
||||
|
|
|
@ -27,6 +27,7 @@ use Bugzilla::WebService::Server::REST::Resources::Classification;
|
|||
use Bugzilla::WebService::Server::REST::Resources::Group;
|
||||
use Bugzilla::WebService::Server::REST::Resources::Product;
|
||||
use Bugzilla::WebService::Server::REST::Resources::User;
|
||||
use Bugzilla::WebService::Server::REST::Resources::BugUserLastVisit;;
|
||||
|
||||
use Scalar::Util qw(blessed reftype);
|
||||
use MIME::Base64 qw(decode_base64);
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# 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::WebService::Server::REST::Resources::BugUserLastVisit;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::BugUserLastVisit::rest_resources = \&_rest_resources;
|
||||
}
|
||||
|
||||
sub _rest_resources {
|
||||
return [
|
||||
# bug-id
|
||||
qr{^/bug_user_last_visit/(\d+)$}, {
|
||||
GET => {
|
||||
method => 'get',
|
||||
params => sub {
|
||||
return { ids => $_[0] };
|
||||
},
|
||||
},
|
||||
POST => {
|
||||
method => 'update',
|
||||
params => sub {
|
||||
return { ids => $_[0] };
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::BugUserLastVisit - The
|
||||
BugUserLastVisit REST API
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to lookup and update the last time
|
||||
a user visited a bug.
|
||||
|
||||
See L<Bugzilla::WebService::BugUserLastVisit> for more details on how to use
|
||||
this part of the REST API.
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/perl -wT
|
||||
# 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.
|
||||
|
||||
=head1 NAME
|
||||
|
||||
clean-bug-user-last-visit.pl
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This utility script cleans out entries from the bug_user_last_visit table that
|
||||
are older than (a configurable) number of days.
|
||||
|
||||
It takes no arguments and produces no output except in the case of errors.
|
||||
|
||||
=cut
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
use warnings;
|
||||
use lib qw(. lib);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
Bugzilla->usage_mode(USAGE_MODE_CMDLINE);
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $sql = 'DELETE FROM bug_user_last_visit WHERE last_visit_ts < '
|
||||
. $dbh->sql_date_math('NOW()',
|
||||
'-',
|
||||
Bugzilla->params->{last_visit_keep_days},
|
||||
'DAY');
|
||||
$dbh->do($sql);
|
46
js/bug.js
46
js/bug.js
|
@ -189,3 +189,49 @@ function set_assign_to(use_qa_contact) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
var JSON = YAHOO.lang.JSON;
|
||||
|
||||
YAHOO.bugzilla.bugUserLastVisit = {
|
||||
update: function(bug_id) {
|
||||
var args = JSON.stringify({
|
||||
version: "1.1",
|
||||
method: 'BugUserLastVisit.update',
|
||||
params: { ids: bug_id },
|
||||
});
|
||||
var callbacks = {
|
||||
failure: function(res) {
|
||||
if (console)
|
||||
console.log("failed to update last visited: "
|
||||
+ res.responseText);
|
||||
},
|
||||
};
|
||||
|
||||
YAHOO.util.Connect.setDefaultPostHeader('application/json', true);
|
||||
YAHOO.util.Connect.asyncRequest('POST', 'jsonrpc.cgi', callbacks,
|
||||
args)
|
||||
},
|
||||
|
||||
get: function(done) {
|
||||
var args = JSON.stringify({
|
||||
version: "1.1",
|
||||
method: 'BugUserLastVisit.get',
|
||||
params: { },
|
||||
});
|
||||
var callbacks = {
|
||||
success: function(res) { done(JSON.parse(res.responseText)) },
|
||||
failure: function(res) {
|
||||
if (console)
|
||||
console.log("failed to get last visited: "
|
||||
+ res.responseText);
|
||||
},
|
||||
};
|
||||
|
||||
YAHOO.util.Connect.setDefaultPostHeader('application/json', true);
|
||||
YAHOO.util.Connect.asyncRequest('POST', 'jsonrpc.cgi', callbacks,
|
||||
args)
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -24,5 +24,8 @@
|
|||
"Bugzilla will issue a warning in case you'd run into inconsistencies " _
|
||||
"when you're about to do so, but such deletions remain kinda scary. " _
|
||||
"So, you have to turn on this option before any such deletions " _
|
||||
"will ever happen." }
|
||||
"will ever happen."
|
||||
|
||||
last_visit_keep_days => "This option controls how many days Bugzilla will " _
|
||||
"remember when users visit specific bugs."}
|
||||
%]
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
[% yui = ['autocomplete', 'calendar'] %]
|
||||
[% yui.push('container') IF user.can_tag_comments %]
|
||||
[% javascript_urls = [ "js/util.js", "js/field.js" ] %]
|
||||
[% javascript_urls.push("js/bug.js") IF user.id %]
|
||||
[% javascript_urls.push('js/comment-tagging.js')
|
||||
IF user.id && Param('comment_taggers_group') %]
|
||||
[% IF bug.defined %]
|
||||
|
@ -52,6 +53,10 @@
|
|||
}
|
||||
YAHOO.util.Event.onDOMReady(function() {
|
||||
initDirtyFieldTracking();
|
||||
|
||||
[% IF user.id AND user.is_involved_in_bug(bug) %]
|
||||
YAHOO.bugzilla.bugUserLastVisit.update([% bug.bug_id FILTER none %]);
|
||||
[% END %]
|
||||
});
|
||||
[% javascript FILTER none %]
|
||||
[% END %]
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
"everconfirmed" => "Ever confirmed",
|
||||
"flagtypes.name" => "Flags",
|
||||
"keywords" => "Keywords",
|
||||
"last_visit_ts" => "Last Visit",
|
||||
"longdesc" => "Comment",
|
||||
"longdescs.count" => "Number of Comments",
|
||||
"longdescs.isprivate" => "Comment is private",
|
||||
|
|
|
@ -1871,6 +1871,11 @@
|
|||
Sorry, but you are not allowed to (un)mark comments or attachments
|
||||
as private.
|
||||
|
||||
[% ELSIF error == "user_not_involved" %]
|
||||
[% title = "User Not Involved with $terms.Bug" %]
|
||||
Sorry, but you are not involved with [% terms.Bug %] [%+
|
||||
bug_id FILTER bug_link(bug_id) FILTER none %].
|
||||
|
||||
[% ELSIF error == "webdot_too_large" %]
|
||||
[% title = "Dependency Graph Too Large" %]
|
||||
The dependency graph contains too many [% terms.bugs %] to display (more
|
||||
|
|
Loading…
Reference in New Issue