summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/DB/Schema.pm19
-rw-r--r--Bugzilla/Memcached.pm20
-rw-r--r--Bugzilla/User.pm116
-rwxr-xr-xeditusers.cgi1
-rw-r--r--extensions/BMO/lib/Reports/UserActivity.pm29
-rw-r--r--extensions/BMO/template/en/default/pages/user_activity.html.tmpl4
6 files changed, 132 insertions, 57 deletions
diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm
index d8f3e175a..cbcb5b26c 100644
--- a/Bugzilla/DB/Schema.pm
+++ b/Bugzilla/DB/Schema.pm
@@ -1714,6 +1714,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
diff --git a/Bugzilla/Memcached.pm b/Bugzilla/Memcached.pm
index 7a9734375..28934e4f5 100644
--- a/Bugzilla/Memcached.pm
+++ b/Bugzilla/Memcached.pm
@@ -106,7 +106,7 @@ sub set_config {
return unless $self->{memcached};
if (exists $args->{key}) {
- return $self->_set($self->_config_prefix . ':' . $args->{key}, $args->{data});
+ return $self->_set($self->_config_prefix . '.' . $args->{key}, $args->{data});
}
else {
ThrowCodeError('params_required', { function => "Bugzilla::Memcached::set_config",
@@ -119,7 +119,7 @@ sub get_config {
return unless $self->{memcached};
if (exists $args->{key}) {
- return $self->_get($self->_config_prefix . ':' . $args->{key});
+ return $self->_get($self->_config_prefix . '.' . $args->{key});
}
else {
ThrowCodeError('params_required', { function => "Bugzilla::Memcached::get_config",
@@ -167,9 +167,14 @@ sub clear_all {
}
sub clear_config {
- my ($self) = @_;
+ my ($self, $args) = @_;
return unless $self->{memcached};
- $self->_inc_prefix("config");
+ if ($args && exists $args->{key}) {
+ $self->_delete($self->_config_prefix . '.' . $args->{key});
+ }
+ else {
+ $self->_inc_prefix("config");
+ }
}
# in order to clear all our keys, we add a prefix to all our keys. when we
@@ -221,7 +226,7 @@ sub _config_prefix {
sub _encode_key {
my ($self, $key) = @_;
- $key = $self->_global_prefix . ':' . uri_escape_utf8($key);
+ $key = $self->_global_prefix . '.' . uri_escape_utf8($key);
return length($self->{namespace} . $key) > MAX_KEY_LENGTH
? undef
: $key;
@@ -426,6 +431,11 @@ corresponding C<table> and C<name> entry.
Removes C<value> with the specified C<table> and C<name>, as well as the
corresponding C<table> and C<id> entry.
+=item C<clear_config({ key =E<gt> $key })>
+
+Remove C<value> with the specified C<key> from the configuration cache. See
+C<set_config> for more information.
+
=item C<clear_config>
Removes all configuration related values from the cache. See C<set_config> for
diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm
index 4c0b18250..d9c3756b0 100644
--- a/Bugzilla/User.pm
+++ b/Bugzilla/User.pm
@@ -677,63 +677,75 @@ sub groups {
return $self->{groups} if defined $self->{groups};
return [] unless $self->id;
- my $dbh = Bugzilla->dbh;
- my $groups_to_check = $dbh->selectcol_arrayref(
- q{SELECT DISTINCT group_id
- FROM user_group_map
- WHERE user_id = ? AND isbless = 0}, undef, $self->id);
-
- my $cache_key = 'group_grant_type_' . GROUP_MEMBERSHIP;
- my $membership_rows = Bugzilla->memcached->get_config({
- key => $cache_key,
+ my $user_groups_key = "user_groups." . $self->id;
+ my $groups = Bugzilla->memcached->get_config({
+ key => $user_groups_key
});
- if (!$membership_rows) {
- $membership_rows = $dbh->selectall_arrayref(
- "SELECT DISTINCT grantor_id, member_id
- FROM group_group_map
- WHERE grant_type = " . GROUP_MEMBERSHIP);
- Bugzilla->memcached->set_config({
- key => $cache_key,
- data => $membership_rows,
+
+ if (!$groups) {
+ my $dbh = Bugzilla->dbh;
+ my $groups_to_check = $dbh->selectcol_arrayref(
+ "SELECT DISTINCT group_id
+ FROM user_group_map
+ WHERE user_id = ? AND isbless = 0", undef, $self->id);
+
+ my $grant_type_key = 'group_grant_type_' . GROUP_MEMBERSHIP;
+ my $membership_rows = Bugzilla->memcached->get_config({
+ key => $grant_type_key,
});
- }
+ if (!$membership_rows) {
+ $membership_rows = $dbh->selectall_arrayref(
+ "SELECT DISTINCT grantor_id, member_id
+ FROM group_group_map
+ WHERE grant_type = " . GROUP_MEMBERSHIP);
+ Bugzilla->memcached->set_config({
+ key => $grant_type_key,
+ data => $membership_rows,
+ });
+ }
- my %group_membership;
- foreach my $row (@$membership_rows) {
- my ($grantor_id, $member_id) = @$row;
- push (@{ $group_membership{$member_id} }, $grantor_id);
- }
-
- # Let's walk the groups hierarchy tree (using FIFO)
- # On the first iteration it's pre-filled with direct groups
- # membership. Later on, each group can add its own members into the
- # FIFO. Circular dependencies are eliminated by checking
- # $checked_groups{$member_id} hash values.
- # As a result, %groups will have all the groups we are the member of.
- my %checked_groups;
- my %groups;
- while (scalar(@$groups_to_check) > 0) {
- # Pop the head group from FIFO
- my $member_id = shift @$groups_to_check;
-
- # Skip the group if we have already checked it
- if (!$checked_groups{$member_id}) {
- # Mark group as checked
- $checked_groups{$member_id} = 1;
-
- # Add all its members to the FIFO check list
- # %group_membership contains arrays of group members
- # for all groups. Accessible by group number.
- my $members = $group_membership{$member_id};
- my @new_to_check = grep(!$checked_groups{$_}, @$members);
- push(@$groups_to_check, @new_to_check);
-
- $groups{$member_id} = 1;
+ my %group_membership;
+ foreach my $row (@$membership_rows) {
+ my ($grantor_id, $member_id) = @$row;
+ push (@{ $group_membership{$member_id} }, $grantor_id);
}
- }
- $self->{groups} = Bugzilla::Group->new_from_list([keys %groups]);
+ # Let's walk the groups hierarchy tree (using FIFO)
+ # On the first iteration it's pre-filled with direct groups
+ # membership. Later on, each group can add its own members into the
+ # FIFO. Circular dependencies are eliminated by checking
+ # $checked_groups{$member_id} hash values.
+ # As a result, %groups will have all the groups we are the member of.
+ my %checked_groups;
+ my %groups;
+ while (scalar(@$groups_to_check) > 0) {
+ # Pop the head group from FIFO
+ my $member_id = shift @$groups_to_check;
+
+ # Skip the group if we have already checked it
+ if (!$checked_groups{$member_id}) {
+ # Mark group as checked
+ $checked_groups{$member_id} = 1;
+
+ # Add all its members to the FIFO check list
+ # %group_membership contains arrays of group members
+ # for all groups. Accessible by group number.
+ my $members = $group_membership{$member_id};
+ my @new_to_check = grep(!$checked_groups{$_}, @$members);
+ push(@$groups_to_check, @new_to_check);
+
+ $groups{$member_id} = 1;
+ }
+ }
+ $groups = [ keys %groups ];
+
+ Bugzilla->memcached->set_config({
+ key => $user_groups_key,
+ data => $groups,
+ });
+ }
+ $self->{groups} = Bugzilla::Group->new_from_list($groups);
return $self->{groups};
}
@@ -1343,6 +1355,8 @@ sub derive_regexp_groups {
$group_delete->execute($id, $group, GRANT_REGEXP) if $present;
}
}
+
+ Bugzilla->memcached->clear_config({ key => "user_groups.$id" });
}
sub product_responsibilities {
diff --git a/editusers.cgi b/editusers.cgi
index b92ace507..84b95856b 100755
--- a/editusers.cgi
+++ b/editusers.cgi
@@ -342,6 +342,7 @@ if ($action eq 'search') {
($otherUserID, $userid,
get_field_id('bug_group'),
join(', ', @groupsRemovedFrom), join(', ', @groupsAddedTo)));
+ Bugzilla->memcached->clear_config({ key => "user_groups.$otherUserID" })
}
# XXX: should create profiles_activity entries for blesser changes.
diff --git a/extensions/BMO/lib/Reports/UserActivity.pm b/extensions/BMO/lib/Reports/UserActivity.pm
index dc0ee26d3..a961096a6 100644
--- a/extensions/BMO/lib/Reports/UserActivity.pm
+++ b/extensions/BMO/lib/Reports/UserActivity.pm
@@ -54,6 +54,7 @@ sub report {
my ($activity_joins, $activity_where) = ('', '');
my ($attachments_joins, $attachments_where) = ('', '');
+ my ($tags_activity_joins, $tags_activity_where) = ('', '');
if (Bugzilla->params->{"insidergroup"}
&& !Bugzilla->user->in_group(Bugzilla->params->{'insidergroup'}))
{
@@ -61,6 +62,10 @@ sub report {
ON attachments.attach_id = bugs_activity.attach_id";
$activity_where = "AND COALESCE(attachments.isprivate, 0) = 0";
$attachments_where = $activity_where;
+
+ $tags_activity_joins = 'LEFT JOIN longdescs
+ ON longdescs_tags_activity.comment_id = longdescs.comment_id';
+ $tags_activity_where = 'AND COALESCE(longdescs.isprivate, 0) = 0';
}
my @who_bits;
@@ -92,7 +97,7 @@ sub report {
$from_dt = $from_dt->ymd() . ' 00:00:00';
$to_dt = $to_dt->ymd() . ' 23:59:59';
my @params;
- for (1..4) {
+ for (1..5) {
push @params, @who;
push @params, ($from_dt, $to_dt);
}
@@ -129,6 +134,28 @@ sub report {
UNION ALL
SELECT
+ 'comment_tag' AS name,
+ longdescs_tags_activity.bug_id,
+ NULL as attach_id,
+ ".$dbh->sql_date_format('longdescs_tags_activity.bug_when',
+ '%Y.%m.%d %H:%i:%s') . " AS bug_when,
+ longdescs_tags_activity.removed,
+ longdescs_tags_activity.added,
+ profiles.login_name,
+ longdescs_tags_activity.comment_id,
+ longdescs_tags_activity.bug_when
+ FROM longdescs_tags_activity
+ $tags_activity_joins
+ INNER JOIN profiles
+ ON profiles.userid = longdescs_tags_activity.who
+ WHERE profiles.login_name IN ($who_bits)
+ AND longdescs_tags_activity.bug_when >= ?
+ AND longdescs_tags_activity.bug_when <= ?
+ $tags_activity_where
+
+ UNION ALL
+
+ SELECT
'bug_id' AS name,
bugs.bug_id,
NULL AS attach_id,
diff --git a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
index f299b862b..ef2115bcb 100644
--- a/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
+++ b/extensions/BMO/template/en/default/pages/user_activity.html.tmpl
@@ -160,6 +160,10 @@
[% "Comment $change.comment.count"
FILTER bug_link(operation.bug, comment_num => change.comment.count)
FILTER none %]
+ [% ELSIF change.comment.defined && change.fieldname == 'comment_tag' %]
+ [% "Comment $change.comment.count Tagged"
+ FILTER bug_link(operation.bug, comment_num => change.comment.count)
+ FILTER none %]
[% ELSE %]
[%+ field_descs.${change.fieldname} FILTER html %]
[% END %]