diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index b2250d411f..29db402402 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -4447,25 +4447,59 @@ sub _migrate_profiles_modification_ts { $dbh->bz_add_column('profiles', 'modification_ts', {TYPE => 'DATETIME'}); + # A fresh DB will not have this column yet as it is added later by an extension + # so we will need to ignore it later in this case. + my $has_creation_ts = $dbh->bz_column_info('profiles', 'creation_ts'); + my $sth = $dbh->prepare( 'UPDATE profiles SET modification_ts = FROM_UNIXTIME(?) WHERE userid = ?'); + # Todays timestamp + my $now_when + = $dbh->selectrow_array('SELECT UNIX_TIMESTAMP(LOCALTIMESTAMP(0))'); + my $user_ids - = $dbh->selectall_arrayref('SELECT userid FROM profiles ORDER BY userid'); + = $dbh->selectcol_arrayref('SELECT userid FROM profiles ORDER BY userid'); + + my $count = 1; + my $total = scalar @{$user_ids}; + foreach my $user_id (@{$user_ids}) { - my ($audit_log_when) = $dbh->selectrow_array( + indicate_progress({total => $total, current => $count++, every => 25}); + + my $audit_log_when = $dbh->selectrow_array( 'SELECT UNIX_TIMESTAMP(at_time) FROM audit_log WHERE class = \'Bugzilla::User\' AND object_id = ? ORDER BY at_time DESC ' . $dbh->sql_limit(1), undef, $user_id ); - my ($profiles_act_when) = $dbh->selectrow_array( + my $profiles_act_when = $dbh->selectrow_array( 'SELECT UNIX_TIMESTAMP(profiles_when) FROM profiles_activity WHERE userid = ? ORDER BY profiles_when DESC ' . $dbh->sql_limit(1), undef, $user_id ); - # We use unix timestamps to make value comparison easier - my $modification_ts = max($audit_log_when, $profiles_act_when); + $audit_log_when ||= 0; + $profiles_act_when ||= 0; + + my $creation_when = 0; + if ($has_creation_ts) { + $creation_when + = $dbh->selectrow_array( + 'SELECT UNIX_TIMESTAMP(creation_ts) FROM profiles WHERE userid = ?', + undef, $user_id); + $creation_when ||= 0; + } + + my $modification_ts = 0; + # IF we could not find anything then use todays date + if (!$audit_log_when && !$profiles_act_when && !$creation_when) { + $modification_ts = $now_when; + } + # We used unix timestamps to make value comparison easier without using DateTime instance of each. + else { + $modification_ts = max($audit_log_when, $profiles_act_when, $creation_when); + } + $sth->execute($modification_ts, $user_id); } diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 347f4c1567..fb57db004a 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -85,7 +85,8 @@ sub DB_COLUMNS { 'profiles.mfa', 'profiles.mfa_required_date', 'profiles.nickname', - 'profiles.bounce_count' + 'profiles.bounce_count', + $dbh->sql_date_format('modification_ts', '%Y-%m-%d %H:%i:%s') . ' AS modification_ts', ), ; } @@ -107,6 +108,7 @@ use constant VALIDATORS => { password_change_reason => \&_check_password_change_reason, mfa => \&_check_mfa, bounce_count => \&_check_numeric, + modification_ts => \&_check_timestamp, }; sub UPDATE_COLUMNS { @@ -124,6 +126,7 @@ sub UPDATE_COLUMNS { mfa_required_date nickname bounce_count + modification_ts ); push(@cols, 'cryptpassword') if exists $self->{cryptpassword}; return @cols; @@ -337,6 +340,11 @@ sub update { || exists $changes->{cryptpassword}) ); + # Update modification_ts if any changes were made + if (keys %{$changes}) { + $dbh->do('UPDATE profiles set modification_ts = NOW() WHERE userid = ?', undef, $self->id) + } + # XXX Can update profiles_activity here as soon as it understands # field names like login_name. @@ -448,6 +456,10 @@ sub _check_numeric { return $value; } +sub _check_timestamp { + return Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); +} + ################################################################################ # Mutators ################################################################################ @@ -669,6 +681,7 @@ sub showmybugslink { $_[0]->{showmybugslink}; } sub email_disabled { $_[0]->{disable_mail} || !$_[0]->{is_enabled}; } sub email_enabled { !$_[0]->email_disabled; } sub last_seen_date { $_[0]->{last_seen_date}; } +sub modification_ts { $_[0]->{modification_ts}; } sub password_change_required { $_[0]->{password_change_required}; } sub password_change_reason { $_[0]->{password_change_reason}; } diff --git a/scripts/bug1926081.pl b/scripts/bug1926081.pl new file mode 100644 index 0000000000..7377626f33 --- /dev/null +++ b/scripts/bug1926081.pl @@ -0,0 +1,78 @@ +#!/usr/bin/env perl +# 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. + +use 5.10.1; +use strict; +use warnings; +use lib qw(. lib local/lib/perl5); + +use Bugzilla; +use Bugzilla::Constants; +use Bugzilla::Install::Util qw(indicate_progress); + +use List::Util qw(max); + +Bugzilla->usage_mode(USAGE_MODE_CMDLINE); + +my $dbh = Bugzilla->dbh; + +my $sth = $dbh->prepare( + 'UPDATE profiles SET modification_ts = FROM_UNIXTIME(?) WHERE userid = ?'); + +# Todays timestamp +my $now_when + = $dbh->selectrow_array('SELECT UNIX_TIMESTAMP(LOCALTIMESTAMP(0))'); + +my $user_ids + = $dbh->selectcol_arrayref('SELECT userid FROM profiles ORDER BY userid'); + +my $count = 1; +my $total = scalar @{$user_ids}; + +foreach my $user_id (@{$user_ids}) { + indicate_progress({total => $total, current => $count++, every => 25}); + + my $audit_log_when = $dbh->selectrow_array( + 'SELECT UNIX_TIMESTAMP(at_time) FROM audit_log + WHERE class = \'Bugzilla::User\' AND object_id = ? ORDER BY at_time DESC ' + . $dbh->sql_limit(1), undef, $user_id + ); + my $profiles_act_when = $dbh->selectrow_array( + 'SELECT UNIX_TIMESTAMP(profiles_when) FROM profiles_activity + WHERE userid = ? ORDER BY profiles_when DESC ' + . $dbh->sql_limit(1), undef, $user_id + ); + + my $creation_when + = $dbh->selectrow_array( + 'SELECT UNIX_TIMESTAMP(creation_ts) FROM profiles WHERE userid = ?', + undef, $user_id); + + $creation_when ||= 0; + $audit_log_when ||= 0; + $profiles_act_when ||= 0; + + my $modification_ts = 0; + + # IF we could not find anything then use todays date + if (!$audit_log_when && !$profiles_act_when && !$creation_when) { + $modification_ts = $now_when; + } + +# We used unix timestamps to make value comparison easier without using DateTime instance of each. + else { + $modification_ts = max($audit_log_when, $profiles_act_when, $creation_when); + } + + $sth->execute($modification_ts, $user_id); +} + +$dbh->bz_alter_column('profiles', 'modification_ts', + {TYPE => 'DATETIME', NOTNULL => 1}); + +1; diff --git a/template/en/default/admin/users/edit.html.tmpl b/template/en/default/admin/users/edit.html.tmpl index 689e870e65..4571ea1f44 100644 --- a/template/en/default/admin/users/edit.html.tmpl +++ b/template/en/default/admin/users/edit.html.tmpl @@ -153,7 +153,12 @@ $(function() { [% END %] - +