# -- # 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::GenericAgent; use strict; use warnings; use Time::HiRes qw(usleep); use Kernel::System::VariableCheck qw(:all); our @ObjectDependencies = ( 'Kernel::Config', 'Kernel::System::Cache', 'Kernel::System::DateTime', 'Kernel::System::DB', 'Kernel::System::DynamicField', 'Kernel::System::DynamicField::Backend', 'Kernel::System::Log', 'Kernel::System::Main', 'Kernel::System::Queue', 'Kernel::System::State', 'Kernel::System::Ticket', 'Kernel::System::Ticket::Article', 'Kernel::System::TemplateGenerator', 'Kernel::System::CustomerUser', ); =head1 NAME Kernel::System::GenericAgent - to manage the generic agent jobs =head1 DESCRIPTION All functions to manage the generic agent and the generic agent jobs. =head1 PUBLIC INTERFACE =head2 new() Don't use the constructor directly, use the ObjectManager instead: my $GenericAgentObject = $Kernel::OM->Get('Kernel::System::GenericAgent'); =cut sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {}; bless( $Self, $Type ); # get dynamic field objects my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # get the dynamic fields for ticket object $Self->{DynamicField} = $DynamicFieldObject->DynamicFieldListGet( Valid => 1, ObjectType => ['Ticket'], ); # debug $Self->{Debug} = $Param{Debug} || 0; # notice on STDOUT $Self->{NoticeSTDOUT} = $Param{NoticeSTDOUT} || 0; my %Map = ( TicketNumber => 'SCALAR', Title => 'SCALAR', MIMEBase_From => 'SCALAR', MIMEBase_To => 'SCALAR', MIMEBase_Cc => 'SCALAR', MIMEBase_Subject => 'SCALAR', MIMEBase_Body => 'SCALAR', TimeUnit => 'SCALAR', CustomerID => 'SCALAR', CustomerUserLogin => 'SCALAR', Agent => 'SCALAR', StateIDs => 'ARRAY', StateTypeIDs => 'ARRAY', QueueIDs => 'ARRAY', PriorityIDs => 'ARRAY', OwnerIDs => 'ARRAY', LockIDs => 'ARRAY', TypeIDs => 'ARRAY', ResponsibleIDs => 'ARRAY', ServiceIDs => 'ARRAY', SLAIDs => 'ARRAY', NewTitle => 'SCALAR', NewCustomerID => 'SCALAR', NewCustomerUserLogin => 'SCALAR', NewStateID => 'SCALAR', NewQueueID => 'SCALAR', NewPriorityID => 'SCALAR', NewOwnerID => 'SCALAR', NewLockID => 'SCALAR', NewTypeID => 'SCALAR', NewResponsibleID => 'SCALAR', NewServiceID => 'SCALAR', NewSLAID => 'SCALAR', ScheduleLastRun => 'SCALAR', ScheduleLastRunUnixTime => 'SCALAR', Valid => 'SCALAR', ScheduleDays => 'ARRAY', ScheduleMinutes => 'ARRAY', ScheduleHours => 'ARRAY', EventValues => 'ARRAY', ); # add time attributes for my $Type ( qw(Time ChangeTime CloseTime TimePending EscalationTime EscalationResponseTime EscalationUpdateTime EscalationSolutionTime) ) { my $Key = $Type . 'SearchType'; $Map{$Key} = 'SCALAR'; } for my $Type ( qw(TicketCreate TicketChange TicketClose TicketLastChange TicketPending TicketEscalation TicketEscalationResponse TicketEscalationUpdate TicketEscalationSolution) ) { for my $Attribute ( qw(PointFormat Point PointStart Start StartDay StartMonth StartYear Stop StopDay StopMonth StopYear) ) { my $Key = $Type . 'Time' . $Attribute; $Map{$Key} = 'SCALAR'; } } # Add Dynamic Fields attributes DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # get the field type of the dynamic fields for edit and search my $FieldValueType = $DynamicFieldBackendObject->TemplateValueTypeGet( DynamicFieldConfig => $DynamicFieldConfig, FieldType => 'All', ); # Add field type to Map if ( IsHashRefWithData($FieldValueType) ) { for my $FieldName ( sort keys %{$FieldValueType} ) { $Map{$FieldName} = $FieldValueType->{$FieldName}; } } } $Self->{Map} = \%Map; return $Self; } =head2 JobRun() run a generic agent job $GenericAgentObject->JobRun( Job => 'JobName', OnlyTicketID => 123, # (optional) for event based Job execution SleepTime => 100_000 # (optional) sleeptime per ticket in microseconds UserID => 1, ); =cut sub JobRun { my ( $Self, %Param ) = @_; # check needed stuff for (qw(Job UserID)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } if ( $Self->{NoticeSTDOUT} ) { print "Job: '$Param{Job}'\n"; } # get job from param my %Job; my %DynamicFieldSearchTemplate; if ( $Param{Config} ) { %Job = %{ $Param{Config} }; # log event $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Run GenericAgent Job '$Param{Job}' from config file.", ); } # get db job else { # log event $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Run GenericAgent Job '$Param{Job}' from db.", ); # get job data my %DBJobRaw = $Self->JobGet( Name => $Param{Job} ); # updated last run time $Self->_JobUpdateRunTime( Name => $Param{Job}, UserID => $Param{UserID} ); # rework for my $Key ( sort keys %DBJobRaw ) { if ( $Key =~ /^New/ ) { my $NewKey = $Key; $NewKey =~ s/^New//; $Job{New}->{$NewKey} = $DBJobRaw{$Key}; } else { # skip dynamic fields if ( $Key !~ m{ DynamicField_ }xms ) { $Job{$Key} = $DBJobRaw{$Key}; } } # convert dynamic fields if ( $Key =~ m{ \A DynamicField_ }xms ) { $Job{New}->{$Key} = $DBJobRaw{$Key}; } elsif ( $Key =~ m{ \A Search_DynamicField_ }xms ) { $DynamicFieldSearchTemplate{$Key} = $DBJobRaw{$Key}; } } # Pass module parameters directly to the module in %Param, # but don't overwrite existing keys for my $Counter ( 1 .. 6 ) { if ( $Job{New}->{"ParamKey$Counter"} ) { $Job{New}->{ $Job{New}->{"ParamKey$Counter"} } //= $Job{New}->{"ParamValue$Counter"}; } } if ( exists $Job{SearchInArchive} && $Job{SearchInArchive} eq 'ArchivedTickets' ) { $Job{ArchiveFlags} = ['y']; } if ( exists $Job{SearchInArchive} && $Job{SearchInArchive} eq 'AllTickets' ) { $Job{ArchiveFlags} = [ 'y', 'n' ]; } } # get dynamic field backend objects my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # set dynamic fields search parameters my %DynamicFieldSearchParameters; DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # get search field preferences my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( DynamicFieldConfig => $DynamicFieldConfig, ); next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); PREFERENCE: for my $Preference ( @{$SearchFieldPreferences} ) { my $DynamicFieldTemp = $DynamicFieldSearchTemplate{ 'Search_DynamicField_' . $DynamicFieldConfig->{Name} . $Preference->{Type} }; next PREFERENCE if !defined $DynamicFieldTemp; # extract the dynamic field value from the profile my $SearchParameter = $DynamicFieldBackendObject->SearchFieldParameterBuild( DynamicFieldConfig => $DynamicFieldConfig, Profile => \%DynamicFieldSearchTemplate, Type => $Preference->{Type}, ); # set search parameter if ( defined $SearchParameter ) { $DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $SearchParameter->{Parameter}; } } } if ( $Param{OnlyTicketID} ) { $Job{TicketID} = $Param{OnlyTicketID}; } # get needed objects my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # escalation tickets my %Tickets; # get ticket limit on job run my $RunLimit = $ConfigObject->Get('Ticket::GenericAgentRunLimit'); if ( $Job{Escalation} ) { # Find all tickets which will escalate within the next five days. # The notification module will determine if a notification must be sent out or not. my @Tickets = $TicketObject->TicketSearch( %Job, Result => 'ARRAY', Limit => $Job{Limit} || $Param{Limit} || 100, TicketEscalationTimeOlderMinutes => $Job{TicketEscalationTimeOlderMinutes} || -( 5 * 24 * 60 ), Permission => 'rw', UserID => $Param{UserID} || 1, ); for (@Tickets) { if ( !$Job{Queue} ) { $Tickets{$_} = $TicketObject->TicketNumberLookup( TicketID => $_ ); } else { my %Ticket = $TicketObject->TicketGet( TicketID => $_, DynamicFields => 0, ); if ( $Ticket{Queue} eq $Job{Queue} ) { $Tickets{$_} = $Ticket{TicketNumber}; } } } } # pending tickets elsif ( $Job{PendingReminder} || $Job{PendingAuto} ) { my $Type = ''; if ( $Job{PendingReminder} ) { $Type = 'PendingReminder'; } else { $Type = 'PendingAuto'; } if ( !$Job{Queue} ) { %Tickets = ( $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => 1, StateType => $Type, Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ), %Tickets ); } elsif ( ref $Job{Queue} eq 'ARRAY' ) { for ( @{ $Job{Queue} } ) { if ( $Self->{NoticeSTDOUT} ) { print " For Queue: $_\n"; } %Tickets = ( $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => 1, Queues => [$_], StateType => $Type, Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ), %Tickets ); } } else { %Tickets = ( $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => 1, StateType => $Type, Queues => [ $Job{Queue} ], Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ), %Tickets ); } for ( sort keys %Tickets ) { my %Ticket = $TicketObject->TicketGet( TicketID => $_, DynamicFields => 0, ); if ( $Ticket{UntilTime} > 1 ) { delete $Tickets{$_}; } } } # get regular tickets else { if ( !$Job{Queue} ) { # check min. one search arg my $Count = 0; for ( sort keys %Job ) { if ( $_ !~ /^(New|Name|Valid|Schedule|Event)/ && $Job{$_} ) { $Count++; } } # also search in Dynamic fields search attributes for my $DynamicFieldName ( sort keys %DynamicFieldSearchParameters ) { $Count++; } # log no search attribute if ( !$Count ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Attention: Can't run GenericAgent Job '$Param{Job}' because no " . "search attributes are used!.", ); return; } # search tickets if ( $Self->{NoticeSTDOUT} ) { print " For all Queues: \n"; } my $GenericAgentTicketSearch = $ConfigObject->Get("Ticket::GenericAgentTicketSearch") || {}; %Tickets = $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => $GenericAgentTicketSearch->{ExtendedSearchCondition}, Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ); } elsif ( ref $Job{Queue} eq 'ARRAY' ) { for ( @{ $Job{Queue} } ) { if ( $Self->{NoticeSTDOUT} ) { print " For Queue: $_\n"; } %Tickets = ( $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => 1, Queues => [$_], Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ), %Tickets ); } } else { %Tickets = $TicketObject->TicketSearch( %Job, %DynamicFieldSearchParameters, ConditionInline => 1, Queues => [ $Job{Queue} ], Limit => $Param{Limit} || $RunLimit, UserID => $Param{UserID}, ); } } # process each ticket TICKETID: for my $TicketID ( sort keys %Tickets ) { $Self->_JobRunTicket( Config => \%Job, Job => $Param{Job}, TicketID => $TicketID, TicketNumber => $Tickets{$TicketID}, UserID => $Param{UserID}, ); next TICKETID if !$Param{SleepTime}; Time::HiRes::usleep( $Param{SleepTime} ); } return 1; } =head2 JobList() returns a hash of jobs my %List = $GenericAgentObject->JobList(); =cut sub JobList { my ( $Self, %Param ) = @_; # get cache object my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); # check cache my $CacheKey = "JobList"; my $Cache = $CacheObject->Get( Type => 'GenericAgent', Key => $CacheKey, ); return %{$Cache} if ref $Cache; # get database object my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); return if !$DBObject->Prepare( SQL => 'SELECT DISTINCT(job_name) FROM generic_agent_jobs', ); my %Data; while ( my @Row = $DBObject->FetchrowArray() ) { $Data{ $Row[0] } = $Row[0]; } $CacheObject->Set( Type => 'GenericAgent', Key => $CacheKey, Value => \%Data, TTL => 24 * 60 * 60, ); return %Data; } =head2 JobGet() returns a hash of the job data my %Job = $GenericAgentObject->JobGet(Name => 'JobName'); =cut sub JobGet { my ( $Self, %Param ) = @_; # check needed stuff for (qw(Name)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } # get cache object my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); # check cache my $CacheKey = 'JobGet::' . $Param{Name}; my $Cache = $CacheObject->Get( Type => 'GenericAgent', Key => $CacheKey, ); return %{$Cache} if ref $Cache; # get database object my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); return if !$DBObject->Prepare( SQL => ' SELECT job_key, job_value FROM generic_agent_jobs WHERE job_name = ?', Bind => [ \$Param{Name} ], ); my %Data; while ( my @Row = $DBObject->FetchrowArray() ) { if ( $Self->{Map}->{ $Row[0] } && $Self->{Map}->{ $Row[0] } eq 'ARRAY' ) { push @{ $Data{ $Row[0] } }, $Row[1]; } else { $Data{ $Row[0] } = $Row[1]; } } # get time settings my %Map = ( TicketCreate => 'Time', TicketChange => 'ChangeTime', TicketClose => 'CloseTime', TicketLastChange => 'LastChangeTime', TicketPending => 'TimePending', TicketEscalation => 'EscalationTime', TicketEscalationResponse => 'EscalationResponseTime', TicketEscalationUpdate => 'EscalationUpdateTime', TicketEscalationSolution => 'EscalationSolutionTime', ); for my $Type ( qw(TicketCreate TicketChange TicketClose TicketLastChange TicketPending TicketEscalation TicketEscalationResponse TicketEscalationUpdate TicketEscalationSolution) ) { my $SearchType = $Map{$Type} . 'SearchType'; if ( !$Data{$SearchType} || $Data{$SearchType} eq 'None' ) { # do nothing on time stuff for ( qw(TimeStartMonth TimeStopMonth TimeStopDay TimeStartDay TimeStopYear TimePoint TimeStartYear TimePointFormat TimePointStart) ) { delete $Data{ $Type . $_ }; } } elsif ( $Data{$SearchType} && $Data{$SearchType} eq 'TimeSlot' ) { for (qw(TimePoint TimePointFormat TimePointStart)) { delete $Data{ $Type . $_ }; } for (qw(Month Day)) { $Data{ $Type . "TimeStart$_" } = sprintf( '%02d', $Data{ $Type . "TimeStart$_" } ); $Data{ $Type . "TimeStop$_" } = sprintf( '%02d', $Data{ $Type . "TimeStop$_" } ); } if ( $Data{ $Type . 'TimeStartDay' } && $Data{ $Type . 'TimeStartMonth' } && $Data{ $Type . 'TimeStartYear' } ) { $Data{ $Type . 'TimeNewerDate' } = $Data{ $Type . 'TimeStartYear' } . '-' . $Data{ $Type . 'TimeStartMonth' } . '-' . $Data{ $Type . 'TimeStartDay' } . ' 00:00:01'; } if ( $Data{ $Type . 'TimeStopDay' } && $Data{ $Type . 'TimeStopMonth' } && $Data{ $Type . 'TimeStopYear' } ) { $Data{ $Type . 'TimeOlderDate' } = $Data{ $Type . 'TimeStopYear' } . '-' . $Data{ $Type . 'TimeStopMonth' } . '-' . $Data{ $Type . 'TimeStopDay' } . ' 23:59:59'; } } elsif ( $Data{$SearchType} && $Data{$SearchType} eq 'TimePoint' ) { for ( qw(TimeStartMonth TimeStopMonth TimeStopDay TimeStartDay TimeStopYear TimeStartYear) ) { delete $Data{ $Type . $_ }; } if ( $Data{ $Type . 'TimePoint' } && $Data{ $Type . 'TimePointStart' } && $Data{ $Type . 'TimePointFormat' } ) { my $Time = 0; if ( $Data{ $Type . 'TimePointFormat' } eq 'minute' ) { $Time = $Data{ $Type . 'TimePoint' }; } elsif ( $Data{ $Type . 'TimePointFormat' } eq 'hour' ) { $Time = $Data{ $Type . 'TimePoint' } * 60; } elsif ( $Data{ $Type . 'TimePointFormat' } eq 'day' ) { $Time = $Data{ $Type . 'TimePoint' } * 60 * 24; } elsif ( $Data{ $Type . 'TimePointFormat' } eq 'week' ) { $Time = $Data{ $Type . 'TimePoint' } * 60 * 24 * 7; } elsif ( $Data{ $Type . 'TimePointFormat' } eq 'month' ) { $Time = $Data{ $Type . 'TimePoint' } * 60 * 24 * 30; } elsif ( $Data{ $Type . 'TimePointFormat' } eq 'year' ) { $Time = $Data{ $Type . 'TimePoint' } * 60 * 24 * 365; } if ( $Data{ $Type . 'TimePointStart' } eq 'Before' ) { # more than ... ago $Data{ $Type . 'TimeOlderMinutes' } = $Time; } elsif ( $Data{ $Type . 'TimePointStart' } eq 'Next' ) { # within the next ... $Data{ $Type . 'TimeNewerMinutes' } = 0; $Data{ $Type . 'TimeOlderMinutes' } = -$Time; } else { # within the last ... $Data{ $Type . 'TimeOlderMinutes' } = 0; $Data{ $Type . 'TimeNewerMinutes' } = $Time; } } } } # check valid if ( %Data && !defined $Data{Valid} ) { $Data{Valid} = 1; } if (%Data) { $Data{Name} = $Param{Name}; } $CacheObject->Set( Type => 'GenericAgent', Key => $CacheKey, Value => \%Data, TTL => 24 * 60 * 60, ); return %Data; } =head2 JobAdd() adds a new job to the database $GenericAgentObject->JobAdd( Name => 'JobName', Data => { Queue => 'SomeQueue', ... Valid => 1, }, UserID => 123, ); =cut sub JobAdd { my ( $Self, %Param ) = @_; # check needed stuff for (qw(Name Data UserID)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } # check if job name already exists my %Check = $Self->JobGet( Name => $Param{Name} ); if (%Check) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "A job with the name '$Param{Name}' already exists.", ); return; } # get database object my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # insert data into db for my $Key ( sort keys %{ $Param{Data} } ) { if ( ref $Param{Data}->{$Key} eq 'ARRAY' ) { for my $Item ( @{ $Param{Data}->{$Key} } ) { if ( defined $Item ) { $DBObject->Do( SQL => 'INSERT INTO generic_agent_jobs ' . '(job_name, job_key, job_value) VALUES (?, ?, ?)', Bind => [ \$Param{Name}, \$Key, \$Item ], ); } } } else { if ( defined $Param{Data}->{$Key} ) { $DBObject->Do( SQL => 'INSERT INTO generic_agent_jobs ' . '(job_name, job_key, job_value) VALUES (?, ?, ?)', Bind => [ \$Param{Name}, \$Key, \$Param{Data}->{$Key} ], ); } } } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "New GenericAgent job '$Param{Name}' added (UserID=$Param{UserID}).", ); $Kernel::OM->Get('Kernel::System::Cache')->CleanUp( Type => 'GenericAgent', ); return 1; } =head2 JobDelete() deletes a job from the database my $Success = $GenericAgentObject->JobDelete( Name => 'JobName', UserID => 123, ); returns: $Success = 1; # or false in case of a failure =cut sub JobDelete { my ( $Self, %Param ) = @_; # check needed stuff for (qw(Name UserID)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } # delete job $Kernel::OM->Get('Kernel::System::DB')->Do( SQL => 'DELETE FROM generic_agent_jobs WHERE job_name = ?', Bind => [ \$Param{Name} ], ); $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "GenericAgent job '$Param{Name}' deleted (UserID=$Param{UserID}).", ); $Kernel::OM->Get('Kernel::System::Cache')->CleanUp( Type => 'GenericAgent', ); return 1; } =head2 JobEventList() returns a hash of events for each job my %List = $GenericAgentObject->JobEventList(); =cut sub JobEventList { my ( $Self, %Param ) = @_; # get cache object my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache'); # check cache my $CacheKey = "JobEventList"; my $Cache = $CacheObject->Get( Type => 'GenericAgent', Key => $CacheKey, ); return %{$Cache} if ref $Cache; my %JobList = $Self->JobList(); my %Data; JOB_NAME: for my $JobName ( sort keys %JobList ) { my %Job = $Self->JobGet( Name => $JobName ); next JOB_NAME if !$Job{Valid}; $Data{$JobName} = $Job{EventValues}; } $CacheObject->Set( Type => 'GenericAgent', Key => $CacheKey, Value => \%Data, TTL => 24 * 60 * 60, ); return %Data; } =begin Internal: =cut =head2 _JobRunTicket() run a generic agent job on a ticket $GenericAgentObject->_JobRunTicket( TicketID => 123, TicketNumber => '2004081400001', Config => { %Job, }, UserID => 1, ); =cut sub _JobRunTicket { my ( $Self, %Param ) = @_; # check needed stuff for (qw(TicketID TicketNumber Config UserID)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } # get ticket object my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $Ticket = "($Param{TicketNumber}/$Param{TicketID})"; # disable sending emails $TicketObject->{SendNoNotification} = $Param{Config}->{New}->{SendNoNotification} ? 1 : 0; # move ticket if ( $Param{Config}->{New}->{Queue} ) { if ( $Self->{NoticeSTDOUT} ) { print " - Move Ticket $Ticket to Queue '$Param{Config}->{New}->{Queue}'\n"; } $TicketObject->TicketQueueSet( QueueID => $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup( Queue => $Param{Config}->{New}->{Queue}, Cache => 1, ), UserID => $Param{UserID}, TicketID => $Param{TicketID}, ); } if ( $Param{Config}->{New}->{QueueID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - Move Ticket $Ticket to QueueID '$Param{Config}->{New}->{QueueID}'\n"; } $TicketObject->TicketQueueSet( QueueID => $Param{Config}->{New}->{QueueID}, UserID => $Param{UserID}, TicketID => $Param{TicketID}, ); } my $ContentType = 'text/plain'; # add note if wanted if ( $Param{Config}->{New}->{Note}->{Body} || $Param{Config}->{New}->{NoteBody} ) { if ( $Self->{NoticeSTDOUT} ) { print " - Add note to Ticket $Ticket\n"; } my %Ticket = $TicketObject->TicketGet( TicketID => $Param{TicketID}, DynamicFields => 0, ); my %CustomerUserData; # We can only do OTRS Tag replacement if we have a CustomerUserID (langauge settings...) if ( IsHashRefWithData( \%Ticket ) && IsStringWithData( $Ticket{CustomerUserID} ) ) { my %CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet( User => $Ticket{CustomerUserID}, ); my %Notification = ( Subject => $Param{Config}->{New}->{NoteSubject}, Body => $Param{Config}->{New}->{NoteBody}, ContentType => 'text/plain', ); my %GenericAgentArticle = $Kernel::OM->Get('Kernel::System::TemplateGenerator')->GenericAgentArticle( TicketID => $Param{TicketID}, Recipient => \%CustomerUserData, # Agent or Customer data get result Notification => \%Notification, UserID => $Param{UserID}, ); if ( IsStringWithData( $GenericAgentArticle{Body} ) || IsHashRefWithData( $GenericAgentArticle{Subject} ) ) { $Param{Config}->{New}->{Note}->{Subject} = $GenericAgentArticle{Subject} || ''; $Param{Config}->{New}->{Note}->{Body} = $GenericAgentArticle{Body} || ''; $ContentType = $GenericAgentArticle{ContentType}; } } my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel( ChannelName => 'Internal', ); my $ArticleID = $ArticleBackendObject->ArticleCreate( TicketID => $Param{TicketID}, SenderType => 'agent', IsVisibleForCustomer => $Param{Config}->{New}->{Note}->{IsVisibleForCustomer} // $Param{Config}->{New}->{NoteIsVisibleForCustomer} // 0, From => $Param{Config}->{New}->{Note}->{From} || $Param{Config}->{New}->{NoteFrom} || 'GenericAgent', Subject => $Param{Config}->{New}->{Note}->{Subject} || $Param{Config}->{New}->{NoteSubject} || 'Note', Body => $Param{Config}->{New}->{Note}->{Body} || $Param{Config}->{New}->{NoteBody}, MimeType => $ContentType, Charset => 'utf-8', UserID => $Param{UserID}, HistoryType => 'AddNote', HistoryComment => 'Generic Agent note added.', NoAgentNotify => $Param{Config}->{New}->{SendNoNotification} || 0, ); my $TimeUnits = $Param{Config}->{New}->{Note}->{TimeUnits} || $Param{Config}->{New}->{NoteTimeUnits}; if ( $ArticleID && $TimeUnits ) { $TicketObject->TicketAccountTime( TicketID => $Param{TicketID}, ArticleID => $ArticleID, TimeUnit => $TimeUnits, UserID => $Param{UserID}, ); } } my %PendingStates = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType( StateType => [ 'pending auto', 'pending reminder' ], Result => 'HASH', ); $Self->{PendingStateList} = \%PendingStates || {}; # set new state my $IsPendingState; if ( $Param{Config}->{New}->{State} ) { if ( $Self->{NoticeSTDOUT} ) { print " - changed state of Ticket $Ticket to '$Param{Config}->{New}->{State}'\n"; } $TicketObject->TicketStateSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, State => $Param{Config}->{New}->{State}, ); $IsPendingState = grep { $_ eq $Param{Config}->{New}->{State} } values %{ $Self->{PendingStateList} }; } if ( $Param{Config}->{New}->{StateID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - changed state id of ticket $Ticket to '$Param{Config}->{New}->{StateID}'\n"; } $TicketObject->TicketStateSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, StateID => $Param{Config}->{New}->{StateID}, ); $IsPendingState = grep { $_ == $Param{Config}->{New}->{StateID} } keys %{ $Self->{PendingStateList} }; } if ( $Param{Config}->{New}->{PendingTime} && !$Param{Config}->{New}->{State} && !$Param{Config}->{New}->{StateID} ) { # if pending time is provided, but there is no new ticket state provided, # check if ticket is already in pending state my %Ticket = $TicketObject->TicketGet( TicketID => $Param{TicketID}, DynamicFields => 0, ); $IsPendingState = grep { $_ eq $Ticket{State} } values %{ $Self->{PendingStateList} }; } # set pending time, if new state is pending state if ( $IsPendingState && $Param{Config}->{New}->{PendingTime} ) { # pending time my $PendingTime = $Param{Config}->{New}->{PendingTime}; # calculate pending time based on hours, minutes, years... if ( $Param{Config}->{New}->{PendingTimeType} ) { $PendingTime *= $Param{Config}->{New}->{PendingTimeType}; } # add systemtime my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); $DateTimeObject->Add( Seconds => $PendingTime ); # set pending time $TicketObject->TicketPendingTimeSet( Year => $DateTimeObject->Format( Format => '%Y' ), Month => $DateTimeObject->Format( Format => '%m' ), Day => $DateTimeObject->Format( Format => '%d' ), Hour => $DateTimeObject->Format( Format => '%H' ), Minute => $DateTimeObject->Format( Format => '%M' ), TicketID => $Param{TicketID}, UserID => $Param{UserID}, ); } # set customer id and customer user if ( $Param{Config}->{New}->{CustomerID} || $Param{Config}->{New}->{CustomerUserLogin} ) { if ( $Param{Config}->{New}->{CustomerID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set customer id of Ticket $Ticket to '$Param{Config}->{New}->{CustomerID}'\n"; } } if ( $Param{Config}->{New}->{CustomerUserLogin} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set customer user id of Ticket $Ticket to '$Param{Config}->{New}->{CustomerUserLogin}'\n"; } } $TicketObject->TicketCustomerSet( TicketID => $Param{TicketID}, No => $Param{Config}->{New}->{CustomerID} || '', User => $Param{Config}->{New}->{CustomerUserLogin} || '', UserID => $Param{UserID}, ); } # set new title if ( $Param{Config}->{New}->{Title} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set title of Ticket $Ticket to '$Param{Config}->{New}->{Title}'\n"; } $TicketObject->TicketTitleUpdate( Title => $Param{Config}->{New}->{Title}, TicketID => $Param{TicketID}, UserID => $Param{UserID}, ); } # set new type if ( $Param{Config}->{New}->{Type} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set type of Ticket $Ticket to '$Param{Config}->{New}->{Type}'\n"; } $TicketObject->TicketTypeSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, Type => $Param{Config}->{New}->{Type}, ); } if ( $Param{Config}->{New}->{TypeID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set type id of Ticket $Ticket to '$Param{Config}->{New}->{TypeID}'\n"; } $TicketObject->TicketTypeSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, TypeID => $Param{Config}->{New}->{TypeID}, ); } # set new service if ( $Param{Config}->{New}->{Service} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set service of Ticket $Ticket to '$Param{Config}->{New}->{Service}'\n"; } $TicketObject->TicketServiceSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, Service => $Param{Config}->{New}->{Service}, ); } if ( $Param{Config}->{New}->{ServiceID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set service id of Ticket $Ticket to '$Param{Config}->{New}->{ServiceID}'\n"; } $TicketObject->TicketServiceSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, ServiceID => $Param{Config}->{New}->{ServiceID}, ); } # set new sla if ( $Param{Config}->{New}->{SLA} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set sla of Ticket $Ticket to '$Param{Config}->{New}->{SLA}'\n"; } $TicketObject->TicketSLASet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, SLA => $Param{Config}->{New}->{SLA}, ); } if ( $Param{Config}->{New}->{SLAID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set sla id of Ticket $Ticket to '$Param{Config}->{New}->{SLAID}'\n"; } $TicketObject->TicketSLASet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, SLAID => $Param{Config}->{New}->{SLAID}, ); } # set new priority if ( $Param{Config}->{New}->{Priority} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set priority of Ticket $Ticket to '$Param{Config}->{New}->{Priority}'\n"; } $TicketObject->TicketPrioritySet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, Priority => $Param{Config}->{New}->{Priority}, ); } if ( $Param{Config}->{New}->{PriorityID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set priority id of Ticket $Ticket to '$Param{Config}->{New}->{PriorityID}'\n"; } $TicketObject->TicketPrioritySet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, PriorityID => $Param{Config}->{New}->{PriorityID}, ); } # set new owner if ( $Param{Config}->{New}->{Owner} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set owner of Ticket $Ticket to '$Param{Config}->{New}->{Owner}'\n"; } $TicketObject->TicketOwnerSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, NewUser => $Param{Config}->{New}->{Owner}, ); } if ( $Param{Config}->{New}->{OwnerID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set owner id of Ticket $Ticket to '$Param{Config}->{New}->{OwnerID}'\n"; } $TicketObject->TicketOwnerSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, NewUserID => $Param{Config}->{New}->{OwnerID}, ); } # set new responsible if ( $Param{Config}->{New}->{Responsible} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set responsible of Ticket $Ticket to '$Param{Config}->{New}->{Responsible}'\n"; } $TicketObject->TicketResponsibleSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, NewUser => $Param{Config}->{New}->{Responsible}, ); } if ( $Param{Config}->{New}->{ResponsibleID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set responsible id of Ticket $Ticket to '$Param{Config}->{New}->{ResponsibleID}'\n"; } $TicketObject->TicketResponsibleSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, NewUserID => $Param{Config}->{New}->{ResponsibleID}, ); } # set new lock if ( $Param{Config}->{New}->{Lock} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set lock of Ticket $Ticket to '$Param{Config}->{New}->{Lock}'\n"; } $TicketObject->TicketLockSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, Lock => $Param{Config}->{New}->{Lock}, ); } if ( $Param{Config}->{New}->{LockID} ) { if ( $Self->{NoticeSTDOUT} ) { print " - set lock id of Ticket $Ticket to '$Param{Config}->{New}->{LockID}'\n"; } $TicketObject->TicketLockSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, LockID => $Param{Config}->{New}->{LockID}, ); } # get dynamic field backend objects my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # set new dynamic fields options # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # extract the dynamic field value from the web request my $Value = $DynamicFieldBackendObject->EditFieldValueGet( DynamicFieldConfig => $DynamicFieldConfig, Template => $Param{Config}->{New}, TransformDates => 0, ); # check if we got a value or an empty value if # an empty value is configured as valid (PossibleNone) # for the current dynamic field if ( defined $Value && ( $DynamicFieldConfig->{Config}->{PossibleNone} || $Value ne '' ) ) { my $Success = $DynamicFieldBackendObject->ValueSet( DynamicFieldConfig => $DynamicFieldConfig, ObjectID => $Param{TicketID}, Value => $Value, UserID => 1, ); if ($Success) { if ( $Self->{NoticeSTDOUT} ) { my $ValueStrg = $DynamicFieldBackendObject->ReadableValueRender( DynamicFieldConfig => $DynamicFieldConfig, Value => $Value, ); print " - set ticket dynamic field $DynamicFieldConfig->{Name} " . "of Ticket $Ticket to $ValueStrg->{Title} '\n"; } } else { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Could not set dynamic field $DynamicFieldConfig->{Name} " . "for Ticket $Ticket.", ); } } } # run module my $AllowCustomModuleExecution = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::GenericAgentAllowCustomModuleExecution') || 0; if ( $Param{Config}->{New}->{Module} && $AllowCustomModuleExecution ) { if ( $Self->{NoticeSTDOUT} ) { print " - Use module ($Param{Config}->{New}->{Module}) for Ticket $Ticket.\n"; } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Use module ($Param{Config}->{New}->{Module}) for Ticket $Ticket.", ); if ( $Self->{Debug} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => "Try to load module: $Param{Config}->{New}->{Module}!", ); } if ( $Kernel::OM->Get('Kernel::System::Main')->Require( $Param{Config}->{New}->{Module} ) ) { # protect parent process eval { my $Object = $Param{Config}->{New}->{Module}->new( Debug => $Self->{Debug}, ); if ($Object) { $Object->Run( %{ $Param{Config} }, TicketID => $Param{TicketID}, ); } }; if ($@) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => $@ ); } } } elsif ( $Param{Config}->{New}->{Module} && !$AllowCustomModuleExecution ) { if ( $Self->{NoticeSTDOUT} ) { print " - Use module ($Param{Config}->{New}->{Module}) is not allowed by the system configuration.\n"; } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Use module ($Param{Config}->{New}->{Module}) is not allowed by the system configuration.", ); } # set new archive flag if ( $Param{Config}->{New}->{ArchiveFlag} && $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ArchiveSystem') ) { if ( $Self->{NoticeSTDOUT} ) { print " - set archive flag of Ticket $Ticket to '$Param{Config}->{New}->{ArchiveFlag}'\n"; } $TicketObject->TicketArchiveFlagSet( TicketID => $Param{TicketID}, UserID => $Param{UserID}, ArchiveFlag => $Param{Config}->{New}->{ArchiveFlag}, ); } # cmd my $AllowCustomScriptExecution = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::GenericAgentAllowCustomScriptExecution') || 0; if ( $Param{Config}->{New}->{CMD} && $AllowCustomScriptExecution ) { if ( $Self->{NoticeSTDOUT} ) { print " - Execute '$Param{Config}->{New}->{CMD}' for Ticket $Ticket.\n"; } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Execute '$Param{Config}->{New}->{CMD}' for Ticket $Ticket.", ); system("$Param{Config}->{New}->{CMD} $Param{TicketNumber} $Param{TicketID} "); if ( $? ne 0 ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Command returned a nonzero return code: rc=$?, err=$!", ); } } elsif ( $Param{Config}->{New}->{CMD} && !$AllowCustomScriptExecution ) { if ( $Self->{NoticeSTDOUT} ) { print " - Execute '$Param{Config}->{New}->{CMD}' is not allowed by the system configuration..\n"; } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Execute '$Param{Config}->{New}->{CMD}' is not allowed by the system configuration..", ); } # delete ticket if ( $Param{Config}->{New}->{Delete} ) { if ( $Self->{NoticeSTDOUT} ) { print " - Delete Ticket $Ticket.\n"; } $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'notice', Message => "Delete Ticket $Ticket.", ); $TicketObject->TicketDelete( UserID => $Param{UserID}, TicketID => $Param{TicketID}, ); } return 1; } sub _JobUpdateRunTime { my ( $Self, %Param ) = @_; # check needed stuff for (qw(Name UserID)) { if ( !$Param{$_} ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => "Need $_!" ); return; } } # get database object my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); # delete old run times return if !$DBObject->Do( SQL => 'DELETE FROM generic_agent_jobs WHERE job_name = ? AND job_key IN (?, ?)', Bind => [ \$Param{Name}, \'ScheduleLastRun', \'ScheduleLastRunUnixTime' ], ); # get time object my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); # update new run time my %Insert = ( ScheduleLastRun => $DateTimeObject->ToString(), ScheduleLastRunUnixTime => $DateTimeObject->ToEpoch(), ); for my $Key ( sort keys %Insert ) { $DBObject->Do( SQL => 'INSERT INTO generic_agent_jobs (job_name,job_key, job_value) VALUES (?, ?, ?)', Bind => [ \$Param{Name}, \$Key, \$Insert{$Key} ], ); } $Kernel::OM->Get('Kernel::System::Cache')->Delete( Key => 'JobGet::' . $Param{Name}, Type => 'GenericAgent', ); return 1; } 1; =end Internal: =head1 TERMS AND CONDITIONS This software is part of the OTRS project (L). 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 L. =cut