# -- # 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("Starting invalid user cleanup...\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("No cleanup for invalid users is needed.\n"); return $Self->ExitCodeOk(); } $Self->_CleanupLocks( InvalidUsers => \@CleanupInvalidUsersImmediately, MicroSleep => $MicroSleep, ); if (@CleanupInvalidUsers) { $Self->_CleanupFlags( CleanupInvalidUsers => \@CleanupInvalidUsers, MicroSleep => $MicroSleep, ); } $Self->Print("Done.\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( " Done (unlocked " . @TicketIDs . " and changed state of $StateCount 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 $User->{UserLogin}...\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( " Done (changed $Count tickets for user $User->{UserLogin}).\n" ); $Self->Print(" Checking for articles with seen flags for user $User->{UserLogin}...\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( " Done (changed $Count articles for user $User->{UserLogin}).\n" ); if ( $ConfigObject->Get('Ticket::Watcher') ) { $Self->Print( " Checking for tickets with ticket watcher entries for user $User->{UserLogin}...\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( " Done (changed $Count tickets for user $User->{UserLogin}).\n" ); } } $Self->Print(" Done.\n"); return; } 1;