Files
scripts/Perl OTRS/Kernel/System/GenericAgent.pm
2024-10-14 00:08:40 +02:00

1554 lines
50 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::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<https://otrs.org/>).
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<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut