284 lines
8.9 KiB
Perl
284 lines
8.9 KiB
Perl
# --
|
|
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
|
|
# --
|
|
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
|
|
# the enclosed file COPYING for license information (GPL). If you
|
|
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
|
|
# --
|
|
|
|
package Kernel::System::Console::Command::Maint::Ticket::InvalidUserCleanup;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use parent qw(Kernel::System::Console::BaseCommand);
|
|
|
|
our @ObjectDependencies = (
|
|
'Kernel::Config',
|
|
'Kernel::System::DB',
|
|
'Kernel::System::DateTime',
|
|
'Kernel::System::Ticket',
|
|
'Kernel::System::User',
|
|
'Kernel::System::Ticket::Article',
|
|
);
|
|
|
|
sub Configure {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
$Self->Description(
|
|
'Delete ticket/article seen flags and ticket watcher entries of users which have been invalid for more than a month, and unlocks tickets by invalid agents immedately.'
|
|
);
|
|
$Self->AddOption(
|
|
Name => 'micro-sleep',
|
|
Description => "Specify microseconds to sleep after every ticket to reduce system load (e.g. 1000).",
|
|
Required => 0,
|
|
HasValue => 1,
|
|
ValueRegex => qr/^\d+$/smx,
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
$Self->Print("<yellow>Starting invalid user cleanup...</yellow>\n");
|
|
|
|
my $InvalidID = 2;
|
|
|
|
# Users must be invalid for at least one month
|
|
my $InvalidBeforeDTObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
|
$InvalidBeforeDTObject->Subtract( Seconds => 60 * 60 * 24 * 31 );
|
|
|
|
# get user object
|
|
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
|
my $MicroSleep = $Self->GetOption('micro-sleep');
|
|
|
|
# First, find all invalid users which are invalid for more than one month
|
|
my %AllUsers = $UserObject->UserList( Valid => 0 );
|
|
my @CleanupInvalidUsers;
|
|
my @CleanupInvalidUsersImmediately;
|
|
USERID:
|
|
for my $UserID ( sort keys %AllUsers ) {
|
|
|
|
my %User = $UserObject->GetUserData(
|
|
UserID => $UserID,
|
|
);
|
|
|
|
# Only take invalid users
|
|
next USERID if ( $User{ValidID} != $InvalidID );
|
|
|
|
# Only take users which are invalid for more than one month
|
|
my $InvalidTimeDTObject = $Kernel::OM->Create(
|
|
'Kernel::System::DateTime',
|
|
ObjectParams => {
|
|
String => $User{ChangeTime},
|
|
},
|
|
);
|
|
|
|
push @CleanupInvalidUsersImmediately, \%User;
|
|
|
|
next USERID if ( $InvalidTimeDTObject >= $InvalidBeforeDTObject );
|
|
|
|
push @CleanupInvalidUsers, \%User;
|
|
}
|
|
|
|
if ( !@CleanupInvalidUsersImmediately ) {
|
|
$Self->Print("<green>No cleanup for invalid users is needed.</green>\n");
|
|
return $Self->ExitCodeOk();
|
|
}
|
|
|
|
$Self->_CleanupLocks(
|
|
InvalidUsers => \@CleanupInvalidUsersImmediately,
|
|
MicroSleep => $MicroSleep,
|
|
);
|
|
|
|
if (@CleanupInvalidUsers) {
|
|
$Self->_CleanupFlags(
|
|
CleanupInvalidUsers => \@CleanupInvalidUsers,
|
|
MicroSleep => $MicroSleep,
|
|
);
|
|
}
|
|
|
|
$Self->Print("<green>Done.</green>\n");
|
|
return $Self->ExitCodeOk();
|
|
}
|
|
|
|
sub _CleanupLocks {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my @Users = @{ $Param{InvalidUsers} };
|
|
|
|
$Self->Print( " Lock Cleanup for " . ( scalar @Users ) . " users starting...\n" );
|
|
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
my $StateMap = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::InvalidOwner::StateChange') // {};
|
|
|
|
my @TicketIDs = $TicketObject->TicketSearch(
|
|
Result => 'ARRAY',
|
|
Limit => 1_000_000,
|
|
OwnerIDs => [ map { $_->{UserID} } @Users ],
|
|
Locks => ['lock'],
|
|
UserID => 1,
|
|
);
|
|
|
|
my $StateCount = 0;
|
|
for my $TicketID (@TicketIDs) {
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
UserID => 1,
|
|
);
|
|
$TicketObject->TicketLockSet(
|
|
Lock => 'unlock',
|
|
TicketID => $TicketID,
|
|
UserID => $Ticket{OwnerID},
|
|
SendNoNotification => 1,
|
|
);
|
|
if ( my $NewState = $StateMap->{ $Ticket{StateType} } ) {
|
|
my $StateSet = $TicketObject->TicketStateSet(
|
|
TicketID => $TicketID,
|
|
State => $NewState,
|
|
UserID => 1,
|
|
);
|
|
$StateCount++ if $StateSet;
|
|
}
|
|
Time::HiRes::usleep( $Param{MicroSleep} ) if $Param{MicroSleep};
|
|
}
|
|
$Self->Print(
|
|
" <green>Done</green> (unlocked <yellow>"
|
|
. @TicketIDs
|
|
. "</yellow> and changed state of <yellow>$StateCount</yellow> tickets).\n"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
sub _CleanupFlags {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my @CleanupInvalidUsers = @{ $Param{CleanupInvalidUsers} };
|
|
$Self->Print( " Flag Cleanup for " . ( scalar @CleanupInvalidUsers ) . " users starting...\n" );
|
|
|
|
# get needed objects
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
|
|
|
for my $User (@CleanupInvalidUsers) {
|
|
|
|
$Self->Print(" Checking for tickets with seen flags for user <yellow>$User->{UserLogin}</yellow>...\n");
|
|
|
|
return if !$DBObject->Prepare(
|
|
SQL => "
|
|
SELECT DISTINCT(ticket.id)
|
|
FROM ticket
|
|
INNER JOIN ticket_flag ON ticket.id = ticket_flag.ticket_id
|
|
WHERE ticket_flag.create_by = $User->{UserID}
|
|
AND ticket_flag.ticket_key = 'Seen'",
|
|
Limit => 1_000_000,
|
|
);
|
|
|
|
my @TicketIDs;
|
|
|
|
while ( my @Row = $DBObject->FetchrowArray() ) {
|
|
|
|
push @TicketIDs, $Row[0];
|
|
}
|
|
|
|
my $Count = 0;
|
|
for my $TicketID (@TicketIDs) {
|
|
my $Delete = $TicketObject->TicketFlagDelete(
|
|
TicketID => $TicketID,
|
|
Key => 'Seen',
|
|
UserID => $User->{UserID},
|
|
);
|
|
$Count++ if $Delete;
|
|
Time::HiRes::usleep( $Param{MicroSleep} ) if $Param{MicroSleep};
|
|
}
|
|
|
|
$Self->Print(
|
|
" <green>Done</green> (changed <yellow>$Count</yellow> tickets for user <yellow>$User->{UserLogin}</yellow>).\n"
|
|
);
|
|
$Self->Print(" Checking for articles with seen flags for user <yellow>$User->{UserLogin}</yellow>...\n");
|
|
|
|
return if !$DBObject->Prepare(
|
|
SQL => "
|
|
SELECT DISTINCT(article.id), article.ticket_id
|
|
FROM article
|
|
INNER JOIN ticket ON ticket.id = article.ticket_id
|
|
INNER JOIN article_flag ON article.id = article_flag.article_id
|
|
WHERE article_flag.create_by = $User->{UserID}
|
|
AND article_flag.article_key = 'Seen'",
|
|
Limit => 1_000_000,
|
|
);
|
|
|
|
my @IDs;
|
|
while ( my @Row = $DBObject->FetchrowArray() ) {
|
|
push @IDs,
|
|
{
|
|
ArticleID => $Row[0],
|
|
TicketID => $Row[1],
|
|
};
|
|
}
|
|
|
|
$Count = 0;
|
|
for my $ID (@IDs) {
|
|
my $Delete = $ArticleObject->ArticleFlagDelete(
|
|
ArticleID => $ID->{ArticleID},
|
|
TicketID => $ID->{TicketID},
|
|
Key => 'Seen',
|
|
UserID => $User->{UserID},
|
|
);
|
|
$Count++ if $Delete;
|
|
Time::HiRes::usleep( $Param{MicroSleep} ) if $Param{MicroSleep};
|
|
}
|
|
|
|
$Self->Print(
|
|
" <green>Done</green> (changed <yellow>$Count</yellow> articles for user <yellow>$User->{UserLogin}</yellow>).\n"
|
|
);
|
|
|
|
if ( $ConfigObject->Get('Ticket::Watcher') ) {
|
|
|
|
$Self->Print(
|
|
" Checking for tickets with ticket watcher entries for user <yellow>$User->{UserLogin}</yellow>...\n"
|
|
);
|
|
|
|
return if !$DBObject->Prepare(
|
|
SQL => "
|
|
SELECT DISTINCT(ticket.id)
|
|
FROM ticket
|
|
INNER JOIN ticket_watcher ON ticket.id = ticket_watcher.ticket_id",
|
|
Limit => 1_000_000,
|
|
);
|
|
|
|
@TicketIDs = ();
|
|
while ( my @Row = $DBObject->FetchrowArray() ) {
|
|
push @TicketIDs, $Row[0];
|
|
}
|
|
|
|
my $Count = 0;
|
|
for my $TicketID (@TicketIDs) {
|
|
|
|
my $Unsubscribe = $TicketObject->TicketWatchUnsubscribe(
|
|
TicketID => $TicketID,
|
|
WatchUserID => $User->{UserID},
|
|
UserID => 1,
|
|
);
|
|
$Count++ if $$Unsubscribe;
|
|
Time::HiRes::usleep( $Param{MicroSleep} ) if $Param{MicroSleep};
|
|
}
|
|
|
|
$Self->Print(
|
|
" <green>Done</green> (changed <yellow>$Count</yellow> tickets for user <yellow>$User->{UserLogin}</yellow>).\n"
|
|
);
|
|
}
|
|
}
|
|
$Self->Print(" <green>Done.</green>\n");
|
|
|
|
return;
|
|
}
|
|
|
|
1;
|