diff options
-rw-r--r-- | Bugzilla/DB/Schema.pm | 19 | ||||
-rw-r--r-- | Bugzilla/Memcached.pm | 20 | ||||
-rw-r--r-- | Bugzilla/User.pm | 116 | ||||
-rwxr-xr-x | editusers.cgi | 1 | ||||
-rw-r--r-- | extensions/BMO/lib/Reports/UserActivity.pm | 29 | ||||
-rw-r--r-- | extensions/BMO/template/en/default/pages/user_activity.html.tmpl | 4 |
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 %] |