diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 477c65f92c..a2bbd3224a 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -732,6 +732,44 @@ use constant ABSTRACT_SCHEMA => { ], }, + flag_activity => { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1}, + flag_when => {TYPE => 'DATETIME', NOTNULL => 1}, + type_id => { + TYPE => 'INT2', + NOTNULL => 1, + REFERENCES => {TABLE => 'flagtypes', COLUMN => 'id', DELETE => 'CASCADE'}, + }, + flag_id => {TYPE => 'INT3', NOTNULL => 1}, + setter_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}, + }, + requestee_id => { + TYPE => 'INT3', + REFERENCES => {TABLE => 'profiles', COLUMN => 'userid'}, + }, + bug_id => { + TYPE => 'INT3', + NOTNULL => 1, + REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'}, + }, + attachment_id => { + TYPE => 'INT5', + REFERENCES => {TABLE => 'attachments', COLUMN => 'attach_id', DELETE => 'CASCADE'}, + }, + status => {TYPE => 'CHAR(1)', NOTNULL => 1}, + ], + INDEXES => [ + flag_activity_flag_id_idx => ['flag_id'], + flag_activity_type_id_idx => ['type_id'], + flag_activity_bug_id_idx => ['bug_id'], + flag_activity_status_when_idx => ['status', 'flag_when'], + ], + }, + # "flagtypes" defines the types of flags that can be set. flagtypes => { FIELDS => [ diff --git a/Bugzilla/Flag.pm b/Bugzilla/Flag.pm index 3255918f12..17fe8fc0e5 100644 --- a/Bugzilla/Flag.pm +++ b/Bugzilla/Flag.pm @@ -42,6 +42,7 @@ whose names start with _ or a re specifically noted as being private. use Scalar::Util qw(blessed); use Storable qw(dclone); +use Bugzilla::FlagActivity; use Bugzilla::FlagType; use Bugzilla::Hook; use Bugzilla::User; @@ -489,6 +490,18 @@ sub create { $params->{creation_date} = $params->{modification_date} = $timestamp; $flag = $class->SUPER::create($params); + + Bugzilla::FlagActivity->create({ + flag_when => $timestamp, + setter_id => $flag->setter_id, + status => $flag->status, + type_id => $flag->type_id, + flag_id => $flag->id, + requestee_id => $flag->requestee_id, + bug_id => $flag->bug_id, + attachment_id => $flag->attach_id, + }); + return $flag; } @@ -505,6 +518,17 @@ sub update { $self->{'modification_date'} = format_time($timestamp, '%Y-%m-%d %T', Bugzilla->local_timezone); Bugzilla->memcached->clear({table => 'flags', id => $self->id}); + + Bugzilla::FlagActivity->create({ + flag_when => $timestamp, + setter_id => $self->setter_id, + status => $self->status, + type_id => $self->type_id, + flag_id => $self->id, + requestee_id => $self->requestee_id, + bug_id => $self->bug_id, + attachment_id => $self->attach_id, + }); } # BMO - provide a hook which passes the flag object @@ -575,6 +599,18 @@ sub update_flags { # because that isn't passed to remove_from_db(). Bugzilla::Hook::process('flag_deleted', {flag => $old_flag, timestamp => $timestamp}); + + Bugzilla::FlagActivity->create({ + flag_when => $timestamp, + setter_id => Bugzilla->user->id, + status => 'X', + type_id => $old_flag->type_id, + flag_id => $old_flag->id, + requestee_id => $old_flag->requestee_id, + bug_id => $old_flag->bug_id, + attachment_id => $old_flag->attach_id, + }); + $old_flag->remove_from_db(); } @@ -685,6 +721,18 @@ sub force_retarget { my ($timestamp) = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); Bugzilla::Hook::process('flag_deleted', {flag => $flag, timestamp => $timestamp}); + + Bugzilla::FlagActivity->create({ + flag_when => $timestamp, + setter_id => Bugzilla->user->id, + status => 'X', + type_id => $flag->type_id, + flag_id => $flag->id, + requestee_id => $flag->requestee_id, + bug_id => $flag->bug_id, + attachment_id => $flag->attach_id, + }); + $flag->remove_from_db(); } } diff --git a/Bugzilla/FlagActivity.pm b/Bugzilla/FlagActivity.pm new file mode 100644 index 0000000000..da41a79980 --- /dev/null +++ b/Bugzilla/FlagActivity.pm @@ -0,0 +1,115 @@ +# 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::FlagActivity; + +use 5.10.1; +use strict; +use warnings; + +use Bugzilla::Error; +use Bugzilla::Util qw(trim datetime_from); +use List::MoreUtils qw(none); + +use base qw( Bugzilla::Object ); + +use constant DB_TABLE => 'flag_activity'; +use constant LIST_ORDER => 'id'; +use constant AUDIT_CREATES => 0; +use constant AUDIT_UPDATES => 0; +use constant AUDIT_REMOVES => 0; + +use constant DB_COLUMNS => qw( + id + flag_when + type_id + flag_id + setter_id + requestee_id + bug_id + attachment_id + status +); + +sub _check_param_required { + my ($param) = @_; + + return sub { + my ($invocant, $value) = @_; + $value = trim($value) or ThrowCodeError('param_required', {param => $param}); + return $value; + },; +} + +sub _check_date { + my ($invocant, $date) = @_; + + $date = trim($date); + datetime_from($date) + or ThrowUserError('illegal_date', + {date => $date, format => 'YYYY-MM-DD HH24:MI:SS'}); + return $date; +} + +sub _check_status { + my ($self, $status) = @_; + + if (none { $status eq $_ } qw( X + - ? )) { + ThrowUserError('flag_status_invalid', {id => $self->id, status => $status}); + } + return $status; +} + +use constant VALIDATORS => { + flag_when => \&_check_date, + type_id => _check_param_required('type_id'), + flag_id => _check_param_required('flag_id'), + setter_id => _check_param_required('setter_id'), + bug_id => _check_param_required('bug_id'), + status => \&_check_status, +}; + +sub flag_when { return $_[0]->{flag_when} } +sub type_id { return $_[0]->{type_id} } +sub flag_id { return $_[0]->{flag_id} } +sub setter_id { return $_[0]->{setter_id} } +sub bug_id { return $_[0]->{bug_id} } +sub requestee_id { return $_[0]->{requestee_id} } +sub attachment_id { return $_[0]->{attachment_id} } +sub status { return $_[0]->{status} } + +sub type { + my ($self) = @_; + return $self->{type} + //= Bugzilla::FlagType->new({id => $self->type_id, cache => 1}); +} + +sub setter { + my ($self) = @_; + return $self->{setter} + //= Bugzilla::User->new({id => $self->setter_id, cache => 1}); +} + +sub requestee { + my ($self) = @_; + return undef unless defined $self->requestee_id; + return $self->{requestee} + //= Bugzilla::User->new({id => $self->requestee_id, cache => 1}); +} + +sub bug { + my ($self) = @_; + return $self->{bug} //= Bugzilla::Bug->new({id => $self->bug_id, cache => 1}); +} + +sub attachment { + my ($self) = @_; + return $self->{attachment} + //= Bugzilla::Attachment->new({id => $self->attachment_id, cache => 1}); +} + +1; diff --git a/extensions/BMO/bin/bug_1022707.pl b/extensions/BMO/bin/bug_1022707.pl index 4c336cbff7..01ae0b73f3 100755 --- a/extensions/BMO/bin/bug_1022707.pl +++ b/extensions/BMO/bin/bug_1022707.pl @@ -37,7 +37,7 @@ print "Press to start, or ^C to cancel...\n"; readline; -my $update_fsa_sql = "UPDATE flag_state_activity SET type_id = 4 WHERE " +my $update_fsa_sql = "UPDATE flag_activity SET type_id = 4 WHERE " . $dbh->sql_in('flag_id', $flag_ids); my $update_flags_sql = "UPDATE flags SET type_id = 4 WHERE " . $dbh->sql_in('id', $flag_ids); diff --git a/extensions/BMO/bin/export_bmo_etl.pl b/extensions/BMO/bin/export_bmo_etl.pl index cb9633ebdb..79de938741 100644 --- a/extensions/BMO/bin/export_bmo_etl.pl +++ b/extensions/BMO/bin/export_bmo_etl.pl @@ -19,7 +19,7 @@ use Bugzilla::Group; use Bugzilla::Logging; use Bugzilla::User; -use Bugzilla::Extension::Review::FlagStateActivity; +use Bugzilla::FlagActivity; use HTTP::Headers; use HTTP::Request; @@ -102,7 +102,7 @@ process_bugs(); process_attachments(); process_flags(); -process_flag_state_activity(); +process_flag_activity(); process_tracking_flags(); process_keywords(); process_see_also(); @@ -407,10 +407,10 @@ sub process_flags { } } -# Process flags that were removed today using the flag_state_activity table +# Process flags that were removed today using the flag_activity table # These entries will also go into the flags table in BigQuery. -sub process_flag_state_activity { - my $table_name = 'flag_state_activity'; +sub process_flag_activity { + my $table_name = 'flag_activity'; my $total_count = 0; my $last_offset = 0; @@ -418,7 +418,7 @@ sub process_flag_state_activity { my $sth = $dbh->prepare( - 'SELECT id, flag_when FROM flag_state_activity WHERE status = \'X\' AND flag_when LIKE \'' + 'SELECT id, flag_when FROM flag_activity WHERE status = \'X\' AND flag_when LIKE \'' . $snapshot_date . ' %\' ORDER BY id LIMIT ? OFFSET ?'); @@ -439,7 +439,7 @@ sub process_flag_state_activity { logger("$table_name id $id with time $mod_time not found in cache.", DEBUG_OUTPUT); - my $obj = Bugzilla::Extension::Review::FlagStateActivity->new($id); + my $obj = Bugzilla::FlagActivity->new($id); if (!$obj) { logger("Object $id not loaded from database or no longer exists"); diff --git a/extensions/Review/Extension.pm b/extensions/Review/Extension.pm index 53103ab502..ee173d0988 100644 --- a/extensions/Review/Extension.pm +++ b/extensions/Review/Extension.pm @@ -18,7 +18,7 @@ use Bugzilla; use Bugzilla::Config::Common qw(check_numeric); use Bugzilla::Constants; use Bugzilla::Error; -use Bugzilla::Extension::Review::FlagStateActivity; +use Bugzilla::FlagActivity; use Bugzilla::Extension::Review::Util; use Bugzilla::Install::Filesystem; use Bugzilla::Search; @@ -369,12 +369,9 @@ sub object_end_of_create { _check_requestee($object); _adjust_request_count($object, +1); } - if (_is_countable_flag($object)) { - $self->_log_flag_state_activity($object, $object->status, - $object->modification_date); - } } + sub object_end_of_update { my ($self, $args) = @_; my ($object, $old_object, $changes) = @$args{qw(object old_object changes)}; @@ -461,9 +458,6 @@ sub flag_updated { my $changes = $args->{changes}; return unless scalar(keys %$changes); - if (_is_countable_flag($flag)) { - $self->_log_flag_state_activity($flag, $flag->status, $timestamp); - } } sub flag_deleted { @@ -475,9 +469,6 @@ sub flag_deleted { _adjust_request_count($flag, -1); } - if (_is_countable_flag($flag)) { - $self->_log_flag_state_activity($flag, 'X', $timestamp, Bugzilla->user->id); - } } sub _is_countable_flag { @@ -503,22 +494,6 @@ sub _check_requestee { } } -sub _log_flag_state_activity { - my ($self, $flag, $status, $timestamp, $setter_id) = @_; - - $setter_id //= $flag->setter_id; - - Bugzilla::Extension::Review::FlagStateActivity->create({ - flag_when => $timestamp, - setter_id => $setter_id, - status => $status, - type_id => $flag->type_id, - flag_id => $flag->id, - requestee_id => $flag->requestee_id, - bug_id => $flag->bug_id, - attachment_id => $flag->attach_id, - }); -} sub _adjust_request_count { my ($flag, $add) = @_; @@ -850,45 +825,6 @@ sub db_schema_abstract_schema { ], }; - $args->{'schema'}->{'flag_state_activity'} = { - FIELDS => [ - id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1,}, - - flag_when => {TYPE => 'DATETIME', NOTNULL => 1,}, - - type_id => { - TYPE => 'INT2', - NOTNULL => 1, - REFERENCES => {TABLE => 'flagtypes', COLUMN => 'id', DELETE => 'CASCADE'} - }, - - flag_id => {TYPE => 'INT3', NOTNULL => 1,}, - - setter_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => {TABLE => 'profiles', COLUMN => 'userid',}, - }, - - requestee_id => - {TYPE => 'INT3', REFERENCES => {TABLE => 'profiles', COLUMN => 'userid',},}, - - bug_id => { - TYPE => 'INT3', - NOTNULL => 1, - REFERENCES => {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE'} - }, - - attachment_id => { - TYPE => 'INT5', - REFERENCES => - {TABLE => 'attachments', COLUMN => 'attach_id', DELETE => 'CASCADE'} - }, - - status => {TYPE => 'CHAR(1)', NOTNULL => 1,}, - ], - }; - $args->{'schema'}->{'bug_mentors'} = { FIELDS => [ bug_id => { @@ -930,6 +866,22 @@ sub db_schema_abstract_schema { sub install_update_db { my $dbh = Bugzilla->dbh; + + # Bug 1806896 - migrate flag_state_activity to flag_activity (now in core schema) + if ($dbh->bz_table_info('flag_state_activity')) { + if ($dbh->bz_table_info('flag_activity')) { + my ($new_count) = $dbh->selectrow_array('SELECT COUNT(*) FROM flag_activity'); + $new_count ||= 0; + if (!$new_count) { + $dbh->bz_drop_table('flag_activity'); + $dbh->bz_rename_table('flag_state_activity', 'flag_activity'); + } + } + else { + $dbh->bz_rename_table('flag_state_activity', 'flag_activity'); + } + } + $dbh->bz_add_column('products', 'reviewer_required', {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}); $dbh->bz_add_column('profiles', 'review_request_count', @@ -954,7 +906,8 @@ sub install_update_db { } # Bug 1588221 - dkl@mozilla.com - $dbh->bz_alter_column('flag_state_activity', 'attachment_id', {TYPE => 'INT5'}); + # Bug 1806896 - table renamed from flag_state_activity to flag_activity + $dbh->bz_alter_column('flag_activity', 'attachment_id', {TYPE => 'INT5'}); } sub install_filesystem { diff --git a/extensions/Review/lib/FlagStateActivity.pm b/extensions/Review/lib/FlagStateActivity.pm index 92efb6c02f..efdcfe3748 100644 --- a/extensions/Review/lib/FlagStateActivity.pm +++ b/extensions/Review/lib/FlagStateActivity.pm @@ -5,116 +5,12 @@ # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. +# Bug 1806896 - class moved to core as Bugzilla::FlagActivity package Bugzilla::Extension::Review::FlagStateActivity; use 5.10.1; use strict; use warnings; -use Bugzilla::Error qw(ThrowUserError); -use Bugzilla::Util qw(trim datetime_from); -use List::MoreUtils qw(none); - -use base qw( Bugzilla::Object ); - -use constant DB_TABLE => 'flag_state_activity'; -use constant LIST_ORDER => 'id'; -use constant AUDIT_CREATES => 0; -use constant AUDIT_UPDATES => 0; -use constant AUDIT_REMOVES => 0; - -use constant DB_COLUMNS => qw( - id - flag_when - type_id - flag_id - setter_id - requestee_id - bug_id - attachment_id - status -); - - -sub _check_param_required { - my ($param) = @_; - - return sub { - my ($invocant, $value) = @_; - $value = trim($value) or ThrowCodeError('param_required', {param => $param}); - return $value; - },; -} - -sub _check_date { - my ($invocant, $date) = @_; - - $date = trim($date); - datetime_from($date) - or ThrowUserError('illegal_date', - {date => $date, format => 'YYYY-MM-DD HH24:MI:SS'}); - return $date; -} - -sub _check_status { - my ($self, $status) = @_; - - # - Make sure the status is valid. - # - Make sure the user didn't request the flag unless it's requestable. - # If the flag existed and was requested before it became unrequestable, - # leave it as is. - if (none { $status eq $_ } qw( X + - ? )) { - ThrowUserError('flag_status_invalid', {id => $self->id, status => $status}); - } - return $status; -} - -use constant VALIDATORS => { - flag_when => \&_check_date, - type_id => _check_param_required('type_id'), - flag_id => _check_param_required('flag_id'), - setter_id => _check_param_required('setter_id'), - bug_id => _check_param_required('bug_id'), - status => \&_check_status, -}; - -sub flag_when { return $_[0]->{flag_when} } -sub type_id { return $_[0]->{type_id} } -sub flag_id { return $_[0]->{flag_id} } -sub setter_id { return $_[0]->{setter_id} } -sub bug_id { return $_[0]->{bug_id} } -sub requestee_id { return $_[0]->{requestee_id} } -sub attachment_id { return $_[0]->{attachment_id} } -sub status { return $_[0]->{status} } - -sub type { - my ($self) = @_; - return $self->{type} - //= Bugzilla::FlagType->new({id => $self->type_id, cache => 1}); -} - -sub setter { - my ($self) = @_; - return $self->{setter} - //= Bugzilla::User->new({id => $self->setter_id, cache => 1}); -} - -sub requestee { - my ($self) = @_; - return undef unless defined $self->requestee_id; - return $self->{requestee} - //= Bugzilla::User->new({id => $self->requestee_id, cache => 1}); -} - -sub bug { - my ($self) = @_; - return $self->{bug} //= Bugzilla::Bug->new({id => $self->bug_id, cache => 1}); -} - -sub attachment { - my ($self) = @_; - return $self->{attachment} - //= Bugzilla::Attachment->new({id => $self->attachment_id, cache => 1}); -} - +use parent 'Bugzilla::FlagActivity'; 1; diff --git a/extensions/Review/lib/WebService.pm b/extensions/Review/lib/WebService.pm index 6f3183c9dd..71c530133d 100644 --- a/extensions/Review/lib/WebService.pm +++ b/extensions/Review/lib/WebService.pm @@ -172,21 +172,21 @@ sub flag_activity { } my $matches - = Bugzilla::Extension::Review::FlagStateActivity->match(\%match_criteria); + = Bugzilla::FlagActivity->match(\%match_criteria); my $user = Bugzilla->user; $user->visible_bugs([map { $_->bug_id } @$matches]); my @results - = map { $self->_flag_state_activity_to_hash($_, $params) } + = map { $self->_flag_activity_to_hash($_, $params) } grep { $user->can_see_bug($_->bug_id) && _can_see_attachment($user, $_) } @$matches; return \@results; } sub _can_see_attachment { - my ($user, $flag_state_activity) = @_; + my ($user, $flag_activity) = @_; - return 1 if !$flag_state_activity->attachment_id; - return 0 if $flag_state_activity->attachment->isprivate && !$user->is_insider; + return 1 if !$flag_activity->attachment_id; + return 0 if $flag_activity->attachment->isprivate && !$user->is_insider; return 1; } @@ -266,21 +266,21 @@ sub rest_resources { ]; } -sub _flag_state_activity_to_hash { - my ($self, $fsa, $params) = @_; +sub _flag_activity_to_hash { + my ($self, $fa, $params) = @_; my %flag = ( - id => $self->type('int', $fsa->id), - creation_time => $self->type('string', $fsa->flag_when), - type => $self->_flagtype_to_hash($fsa->type), - setter => $self->_user_to_hash($fsa->setter), - bug_id => $self->type('int', $fsa->bug_id), - attachment_id => $self->type('int', $fsa->attachment_id), - status => $self->type('string', $fsa->status), + id => $self->type('int', $fa->id), + creation_time => $self->type('string', $fa->flag_when), + type => $self->_flagtype_to_hash($fa->type), + setter => $self->_user_to_hash($fa->setter), + bug_id => $self->type('int', $fa->bug_id), + attachment_id => $self->type('int', $fa->attachment_id), + status => $self->type('string', $fa->status), ); - $flag{requestee} = $self->_user_to_hash($fsa->requestee) if $fsa->requestee; - $flag{flag_id} = $self->type('int', $fsa->flag_id) unless $params->{flag_id}; + $flag{requestee} = $self->_user_to_hash($fa->requestee) if $fa->requestee; + $flag{flag_id} = $self->type('int', $fa->flag_id) unless $params->{flag_id}; return filter($params, \%flag); } diff --git a/scripts/remove-non-public-data.pl b/scripts/remove-non-public-data.pl index a764a168e2..c4cffc202a 100755 --- a/scripts/remove-non-public-data.pl +++ b/scripts/remove-non-public-data.pl @@ -92,7 +92,7 @@ BEGIN id name type custom description obsolete ) ], - flag_state_activity => [ + flag_activity => [ qw( id flag_when type_id flag_id setter_id requestee_id bug_id attachment_id status @@ -267,8 +267,8 @@ BEGIN AND (SELECT COUNT(*) FROM attachments WHERE attachments.submitter_id = profiles.userid) = 0 AND (SELECT COUNT(*) FROM flags WHERE flags.setter_id = profiles.userid) = 0 AND (SELECT COUNT(*) FROM flags WHERE flags.requestee_id = profiles.userid) = 0 - AND (SELECT COUNT(*) FROM flag_state_activity WHERE flag_state_activity.setter_id = profiles.userid) = 0 - AND (SELECT COUNT(*) FROM flag_state_activity WHERE flag_state_activity.requestee_id = profiles.userid) = 0 + AND (SELECT COUNT(*) FROM flag_activity WHERE flag_activity.setter_id = profiles.userid) = 0 + AND (SELECT COUNT(*) FROM flag_activity WHERE flag_activity.requestee_id = profiles.userid) = 0 "); sub drop_referencing {