# --
# 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::GenericInterface::Event::Handler;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
use Storable;
our @ObjectDependencies = (
'Kernel::GenericInterface::Requester',
'Kernel::System::Scheduler',
'Kernel::System::GenericInterface::Webservice',
'Kernel::System::Log',
'Kernel::System::Event',
'Kernel::System::Main',
'Kernel::Config',
'Kernel::System::Daemon::SchedulerDB',
'Kernel::System::DateTime',
);
=head1 NAME
Kernel::GenericInterface::Event::Handler - GenericInterface event handler
=head1 DESCRIPTION
This event handler intercepts all system events and fires connected GenericInterface
invokers.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
for my $Needed (qw(Data Event Config)) {
if ( !$Param{$Needed} ) {
$LogObject->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $WebserviceObject = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice');
my $SchedulerObject = $Kernel::OM->Get('Kernel::System::Scheduler');
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my $RequesterObject = $Kernel::OM->Get('Kernel::GenericInterface::Requester');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my %WebserviceList = %{ $WebserviceObject->WebserviceList( Valid => 1 ) };
my %RegisteredEvents = $Kernel::OM->Get('Kernel::System::Event')->EventList();
# Loop over web services.
WEBSERVICEID:
for my $WebserviceID ( sort keys %WebserviceList ) {
my $WebserviceData = $WebserviceObject->WebserviceGet(
ID => $WebserviceID,
);
next WEBSERVICEID if !IsHashRefWithData( $WebserviceData->{Config} );
next WEBSERVICEID if !IsHashRefWithData( $WebserviceData->{Config}->{Requester} );
next WEBSERVICEID if !IsHashRefWithData( $WebserviceData->{Config}->{Requester}->{Invoker} );
# Check invokers of the web service, to see if some might be connected to this event.
INVOKER:
for my $Invoker ( sort keys %{ $WebserviceData->{Config}->{Requester}->{Invoker} } ) {
my $InvokerConfig = $WebserviceData->{Config}->{Requester}->{Invoker}->{$Invoker};
next INVOKER if ref $InvokerConfig->{Events} ne 'ARRAY';
INVOKEREVENT:
for my $InvokerEvent ( @{ $InvokerConfig->{Events} } ) {
# Check if the invoker is connected to this event.
next INVOKEREVENT if !IsHashRefWithData($InvokerEvent);
next INVOKEREVENT if !IsStringWithData( $InvokerEvent->{Event} );
next INVOKEREVENT if $InvokerEvent->{Event} ne $Param{Event};
# Prepare event type.
my $EventType;
# Set the event type (event object like Article or Ticket) and event condition
EVENTTYPE:
for my $Type ( sort keys %RegisteredEvents ) {
my $EventFound = grep { $_ eq $InvokerEvent->{Event} } @{ $RegisteredEvents{$Type} || [] };
next EVENTTYPE if !$EventFound;
$EventType = $Type;
last EVENTTYPE;
}
if (
$EventType
&& IsHashRefWithData( $InvokerEvent->{Condition} )
&& IsHashRefWithData( $InvokerEvent->{Condition}->{Condition} )
)
{
my $BackendObject = $Self->{EventTypeBackendObject}->{$EventType};
if ( !$BackendObject ) {
my $ObjectClass = "Kernel::GenericInterface::Event::ObjectType::$EventType";
my $Loaded = $MainObject->Require(
$ObjectClass,
);
if ( !$Loaded ) {
$LogObject->Log(
Priority => 'error',
Message =>
"Could not load $ObjectClass, skipping condition checks for event $InvokerEvent->{Event}!",
);
next INVOKEREVENT;
}
$BackendObject = $Kernel::OM->Get($ObjectClass);
$Self->{EventTypeBackendObject}->{$EventType} = $BackendObject;
}
# Get object data
my %EventData = $BackendObject->DataGet(
Data => $Param{Data},
);
if ( IsHashRefWithData( \%EventData ) ) {
my %ObjectData;
$Self->_SerializeConfig(
Data => \%EventData,
SHash => \%ObjectData,
);
# Check if the event condition matches.
my $ConditionCheckResult = $Self->_ConditionCheck(
%{ $InvokerEvent->{Condition} },
Data => \%ObjectData,
);
next INVOKEREVENT if !$ConditionCheckResult;
}
}
# create scheduler task for asynchronous tasks
if ( $InvokerEvent->{Asynchronous} ) {
my $Success = $SchedulerObject->TaskAdd(
Type => 'GenericInterface',
Name => 'Invoker-' . $Invoker,
Attempts => 10,
Data => {
WebserviceID => $WebserviceID,
Invoker => $Invoker,
Data => $Param{Data},
},
);
if ( !$Success ) {
$LogObject->Log(
Priority => 'error',
Message => 'Could not schedule task for Invoker-' . $Invoker,
);
}
next INVOKEREVENT;
}
# execute synchronous tasks directly
my $Result = $RequesterObject->Run(
WebserviceID => $WebserviceID,
Invoker => $Invoker,
Data => Storable::dclone( $Param{Data} ),
);
next INVOKEREVENT if $Result->{Success};
# check if rescheduling is requested on errors
next INVOKEREVENT if !IsHashRefWithData( $Result->{Data} );
next INVOKEREVENT if !$Result->{Data}->{ReSchedule};
# Use the execution time from the return data
my $ExecutionTime = $Result->{Data}->{ExecutionTime};
my $ExecutionDateTime;
# Check if execution time is valid.
if ( IsStringWithData($ExecutionTime) ) {
$ExecutionDateTime = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $ExecutionTime,
},
);
if ( !$ExecutionDateTime ) {
my $WebServiceName = $WebserviceData->{Name} // 'N/A';
$LogObject->Log(
Priority => 'error',
Message =>
"WebService $WebServiceName, Invoker $Invoker returned invalid execution time $ExecutionTime. Falling back to default!",
);
}
}
# Set default execution time.
if ( !$ExecutionTime || !$ExecutionDateTime ) {
# Get default time difference from config.
my $FutureTaskTimeDiff
= int( $ConfigObject->Get('Daemon::SchedulerGenericInterfaceTaskManager::FutureTaskTimeDiff') )
|| 300;
$ExecutionDateTime = $Kernel::OM->Create('Kernel::System::DateTime');
$ExecutionDateTime->Add( Seconds => $FutureTaskTimeDiff );
}
# Create a new task that will be executed in the future.
my $Success = $SchedulerObject->TaskAdd(
ExecutionTime => $ExecutionDateTime->ToString(),
Type => 'GenericInterface',
Name => 'Invoker-' . $Invoker,
Attempts => 10,
Data => {
Data => $Param{Data},
PastExecutionData => $Result->{Data}->{PastExecutionData},
WebserviceID => $WebserviceID,
Invoker => $Invoker,
},
);
if ( !$Success ) {
$LogObject->Log(
Priority => 'error',
Message => 'Could not re-schedule a task in future for Invoker ' . $Invoker,
);
}
}
}
}
return 1;
}
=head2 _SerializeConfig()
returns a serialized hash/array of a given hash/array
my $ConditionCheck = $Self->_SerializeConfig(
Data => \%OldHash,
SHash => \%NewHash,
);
Modifies NewHash (SHash):
my %OldHash = (
Config => {
A => 1,
B => 2,
C => 3,
},
Config2 => 1
);
my %NewHash = (
Config_A => 1,
Config_B => 1,
Config_C => 1,
Config2 => 1,
);
=cut
sub _SerializeConfig {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data SHash)) {
if ( !$Param{$Needed} ) {
print "Got no $Needed!\n";
return;
}
}
my @ConfigContainer;
my $DataType = 'Hash';
if ( IsHashRefWithData( $Param{Data} ) ) {
@ConfigContainer = sort keys %{ $Param{Data} };
}
else {
@ConfigContainer = @{ $Param{Data} };
$DataType = 'Array';
}
# Prepare prefix.
my $Prefix = $Param{Prefix} || '';
my $ArrayCount = 0;
CONFIGITEM:
for my $ConfigItem (@ConfigContainer) {
next CONFIGITEM if !$ConfigItem;
# Check if param data is a hash or an array ref.
if ( $DataType eq 'Hash' ) {
# We got a hash ref.
if (
IsHashRefWithData( $Param{Data}->{$ConfigItem} )
|| IsArrayRefWithData( $Param{Data}->{$ConfigItem} )
)
{
$Self->_SerializeConfig(
Data => $Param{Data}->{$ConfigItem},
SHash => $Param{SHash},
Prefix => $Prefix . $ConfigItem . '_',
);
}
else {
$Prefix = $Prefix . $ConfigItem;
$Param{SHash}->{$Prefix} = $Param{Data}->{$ConfigItem};
$Prefix = $Param{Prefix} || '';
}
}
# We got an array ref
else {
if ( IsHashRefWithData($ConfigItem) || IsArrayRefWithData($ConfigItem) ) {
$Self->_SerializeConfig(
Data => $ConfigItem,
SHash => $Param{SHash},
Prefix => $Prefix . $ConfigItem . '_',
);
}
else {
$Prefix = $Prefix . $ArrayCount;
$Param{SHash}->{$Prefix} = $ConfigItem;
$Prefix = $Param{Prefix} || '';
}
$ArrayCount++;
}
}
return 1;
}
=head2 _ConditionCheck()
Checks if one or more conditions are true
my $ConditionCheck = $Self->_ConditionCheck(
ConditionLinking => 'and',
Condition => {
1 => {
Type => 'and',
Fields => {
DynamicField_Make => [ '2' ],
DynamicField_VWModel => {
Type => 'String',
Match => 'Golf',
},
DynamicField_A => {
Type => 'Hash',
Match => {
Value => 1,
},
},
DynamicField_B => {
Type => 'Regexp',
Match => qr{ [\n\r\f] }xms
},
DynamicField_C => {
Type => 'Module',
Match =>
'Kernel::GenericInterface::Event::Validation::MyModule',
},
Queue => {
Type => 'Array',
Match => [ 'Raw' ],
},
# ...
},
},
# ...
},
Data => {
Queue => 'Raw',
DynamicField1 => 'Value',
Subject => 'Testsubject',
# ...
},
);
Returns:
$CheckResult = 1; # 1 = process with Scheduler or Requester
# 0 = stop processing
=cut
sub _ConditionCheck {
my ( $Self, %Param ) = @_;
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
for my $Needed (qw(Condition Data)) {
if ( !defined $Param{$Needed} ) {
$LogObject->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# Check if we have Data to check against Condition.
if ( !IsHashRefWithData( $Param{Data} ) ) {
$LogObject->Log(
Priority => 'error',
Message => "Data has no values!",
);
return;
}
# Check if we have Condition to check against Data.
if ( !IsHashRefWithData( $Param{Condition} ) ) {
$LogObject->Log(
Priority => 'error',
Message => "Condition has no values!",
);
return;
}
my $ConditionLinking = $Param{ConditionLinking} || 'and';
# If there is something else than 'and', 'or', 'xor' log defect condition configuration
if (
$ConditionLinking ne 'and'
&& $ConditionLinking ne 'or'
&& $ConditionLinking ne 'xor'
)
{
$LogObject->Log(
Priority => 'error',
Message => "Invalid ConditionLinking!",
);
return;
}
my ( $ConditionSuccess, $ConditionFail ) = ( 0, 0 );
# Loop through all submitted conditions
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
CONDITIONNAME:
for my $ConditionName ( sort { $a cmp $b } keys %{ $Param{Condition} } ) {
next CONDITIONNAME if $ConditionName eq 'ConditionLinking';
# Get the condition data.
my $ActualCondition = $Param{Condition}->{$ConditionName};
# Check if we have Fields in our Condition
if ( !IsHashRefWithData( $ActualCondition->{Fields} ) )
{
$LogObject->Log(
Priority => 'error',
Message => "No Fields in Condition->$ConditionName found!",
);
return;
}
# If we don't have a Condition->$Condition->Type, set it to 'and' by default
my $CondType = $ActualCondition->{Type} || 'and';
# If there is something else than 'and', 'or', 'xor' log defect condition configuration
if ( $CondType ne 'and' && $CondType ne 'or' && $CondType ne 'xor' ) {
$LogObject->Log(
Priority => 'error',
Message => "Invalid Condition->$ConditionName->Type!",
);
return;
}
my ( $FieldSuccess, $FieldFail ) = ( 0, 0 );
FIELDLNAME:
for my $FieldName ( sort keys %{ $ActualCondition->{Fields} } ) {
# If we have just a String transform it into string check condition.
if ( ref $ActualCondition->{Fields}->{$FieldName} eq '' ) {
$ActualCondition->{Fields}->{$FieldName} = {
Type => 'String',
Match => $ActualCondition->{Fields}->{$FieldName},
};
}
# If we have an Array ref in "Fields" we deal with just values
# -> transform it into a { Type => 'Array', Match => [1,2,3,4] } structure
# to unify testing later on.
if ( ref $ActualCondition->{Fields}->{$FieldName} eq 'ARRAY' ) {
$ActualCondition->{Fields}->{$FieldName} = {
Type => 'Array',
Match => $ActualCondition->{Fields}->{$FieldName},
};
}
# If we don't have a Condition->$ConditionName->Fields->Field->Type
# set it to 'String' by default.
my $FieldType = $ActualCondition->{Fields}->{$FieldName}->{Type} || 'String';
# If there is something else than 'String', 'Regexp', 'Hash', 'Array', 'Module' log
# defect config.
if (
$FieldType ne 'String'
&& $FieldType ne 'Hash'
&& $FieldType ne 'Array'
&& $FieldType ne 'Regexp'
&& $FieldType ne 'Module'
)
{
$LogObject->Log(
Priority => 'error',
Message => "Invalid Condition->Type!",
);
return;
}
if ( $ActualCondition->{Fields}->{$FieldName}->{Type} eq 'String' ) {
# If our Check contains anything else than a string we can't check,
# Special Condition: if Match contains '0' we can check
if (
(
!$ActualCondition->{Fields}->{$FieldName}->{Match}
&& $ActualCondition->{Fields}->{$FieldName}->{Match} ne '0'
)
|| ref $ActualCondition->{Fields}->{$FieldName}->{Match}
)
{
$LogObject->Log(
Priority => 'error',
Message =>
"Condition->$ConditionName->Fields->$FieldName Match must"
. " be a String if Type is set to String!",
);
return;
}
# Make sure the data string is here and it isn't a ref (array or whatsoever)
# then compare it to our Condition configuration.
if (
defined $Param{Data}->{$FieldName}
&& defined $ActualCondition->{Fields}->{$FieldName}->{Match}
&& ( $Param{Data}->{$FieldName} || $Param{Data}->{$FieldName} eq '0' )
)
{
my $Match;
# Check if field data is a string and compare directly.
if (
ref $Param{Data}->{$FieldName} eq ''
&& $ActualCondition->{Fields}->{$FieldName}->{Match} eq $Param{Data}->{$FieldName}
)
{
$Match = 1;
}
# Otherwise check if field data is and array and compare each element until
# one match.
elsif ( ref $Param{Data}->{$FieldName} eq 'ARRAY' ) {
ITEM:
for my $Item ( @{ $Param{Data}->{$FieldName} } ) {
if ( $ActualCondition->{Fields}->{$FieldName}->{Match} eq $Item ) {
$Match = 1;
last ITEM;
}
}
}
if ($Match) {
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
}
else {
$FieldFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true.
next CONDITIONNAME if $CondType eq 'and';
}
next FIELDLNAME;
}
my @ArrayFields = grep { $_ =~ m{ \A \Q$FieldName\E _ \d+ \z }xms } keys %{ $Param{Data} };
if ( @ArrayFields && defined $ActualCondition->{Fields}->{$FieldName}->{Match} ) {
ARRAYFIELD:
for my $ArrayField (@ArrayFields) {
next ARRAYFIELD if ref $Param{Data}->{$ArrayField} ne '';
if ( $Param{Data}->{$ArrayField} ne $ActualCondition->{Fields}->{$FieldName}->{Match} ) {
next ARRAYFIELD;
}
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
next FIELDLNAME;
}
}
# No match = fail.
$FieldFail++;
# Failed check if we have all 'and' conditions
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true
next CONDITIONNAME if $CondType eq 'and';
next FIELDLNAME;
}
elsif ( $ActualCondition->{Fields}->{$FieldName}->{Type} eq 'Array' ) {
# 1. Go through each Condition->$ConditionName->Fields->$Field->Value (map).
# 2. Assign the value to $CheckValue.
# 3. Grep through $Data->{$Field} to find the "toCheck" value inside the Data->{$Field} Array
# 4. Assign all found Values to @CheckResults.
my $CheckValue;
my @CheckResults =
map {
$CheckValue = $_;
grep { $CheckValue eq $_ } @{ $Param{Data}->{$FieldName} }
}
@{ $ActualCondition->{Fields}->{$FieldName}->{Match} };
# If the found amount is the same as the "toCheck" amount we succeeded
if (
scalar @CheckResults
== scalar @{ $ActualCondition->{Fields}->{$FieldName}->{Match} }
)
{
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
}
else {
$FieldFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true.
next CONDITIONNAME if $CondType eq 'and';
}
next FIELDLNAME;
}
elsif ( $ActualCondition->{Fields}->{$FieldName}->{Type} eq 'Hash' ) {
# if our Check doesn't contain a hash.
if ( ref $ActualCondition->{Fields}->{$FieldName}->{Match} ne 'HASH' ) {
$LogObject->Log(
Priority => 'error',
Message =>
"Condition->$ConditionName->Fields->$FieldName Match must"
. " be a Hash!",
);
return;
}
# If we have no data or Data isn't a hash, test failed.
if (
!$Param{Data}->{$FieldName}
|| ref $Param{Data}->{$FieldName} ne 'HASH'
)
{
$FieldFail++;
next FIELDLNAME;
}
# Find all Data Hash values that equal to the Condition Match Values.
my @CheckResults =
grep {
$Param{Data}->{$FieldName}->{$_} eq
$ActualCondition->{Fields}->{$FieldName}->{Match}->{$_}
}
keys %{ $ActualCondition->{Fields}->{$FieldName}->{Match} };
# If the amount of Results equals the amount of Keys in our hash this part matched.
if (
scalar @CheckResults
== scalar keys %{ $ActualCondition->{Fields}->{$FieldName}->{Match} }
)
{
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
}
else {
$FieldFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true.
next CONDITIONNAME if $CondType eq 'and';
}
next FIELDLNAME;
}
elsif ( $ActualCondition->{Fields}->{$FieldName}->{Type} eq 'Regexp' )
{
# If our Check contains anything else then a string we can't check.
if (
!$ActualCondition->{Fields}->{$FieldName}->{Match}
||
(
ref $ActualCondition->{Fields}->{$FieldName}->{Match} ne 'Regexp'
&& ref $ActualCondition->{Fields}->{$FieldName}->{Match} ne ''
)
)
{
$LogObject->Log(
Priority => 'error',
Message =>
"Condition->$ConditionName->Fields->$FieldName Match must"
. " be a Regular expression if Type is set to Regexp!",
);
return;
}
# Precompile Regexp if is a string.
if ( ref $ActualCondition->{Fields}->{$FieldName}->{Match} eq '' ) {
my $Match = $ActualCondition->{Fields}->{$FieldName}->{Match};
eval {
$ActualCondition->{Fields}->{$FieldName}->{Match} = qr{$Match};
};
if ($@) {
$LogObject->Log(
Priority => 'error',
Message => $@,
);
return;
}
}
# Make sure there is data to compare.
if ( $Param{Data}->{$FieldName} ) {
my $Match;
# Check if field data is a string and compare directly.
if (
ref $Param{Data}->{$FieldName} eq ''
&& $Param{Data}->{$FieldName} =~ $ActualCondition->{Fields}->{$FieldName}->{Match}
)
{
$Match = 1;
}
# Otherwise check if field data is and array and compare each element until one match.
elsif ( ref $Param{Data}->{$FieldName} eq 'ARRAY' ) {
ITEM:
for my $Item ( @{ $Param{Data}->{$FieldName} } ) {
if ( $Item =~ $ActualCondition->{Fields}->{$FieldName}->{Match} ) {
$Match = 1;
last ITEM;
}
}
}
if ($Match) {
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Transition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
}
else {
$FieldFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true.
next CONDITIONNAME if $CondType eq 'and';
}
next FIELDLNAME;
}
my @ArrayFields = grep { $_ =~ m{ \A \Q$FieldName\E _ \d+ \z }xms } keys %{ $Param{Data} };
if ( @ArrayFields && defined $ActualCondition->{Fields}->{$FieldName}->{Match} ) {
ARRAYFIELD:
for my $ArrayField (@ArrayFields) {
next ARRAYFIELD if ref $Param{Data}->{$ArrayField} ne '';
if ( $Param{Data}->{$ArrayField} !~ $ActualCondition->{Fields}->{$FieldName}->{Match} ) {
next ARRAYFIELD;
}
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
next FIELDLNAME;
}
}
# No match = fail.
$FieldFail++;
# Failed check if we have all 'and' conditions
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true
next CONDITIONNAME if $CondType eq 'and';
next FIELDLNAME;
}
elsif ( $ActualCondition->{Fields}->{$FieldName}->{Type} eq 'Module' ) {
# Load Validation Modules. Default location for validation modules:
# Kernel/GenericInterface/Event/Validation/
if (
!$MainObject->Require(
$ActualCondition->{Fields}->{$FieldName}->{Match}
)
)
{
$LogObject->Log(
Priority => 'error',
Message => "Can't load "
. $ActualCondition->{Fields}->{$FieldName}->{Type}
. "Module for Condition->$ConditionName->Fields->$FieldName validation!",
);
return;
}
# Create new ValidateModuleObject.
my $ValidateModuleObject = $Kernel::OM->Get(
$ActualCondition->{Fields}->{$FieldName}->{Match}
);
# Handle "Data" Param to ValidateModule's "Validate" subroutine.
if ( $ValidateModuleObject->Validate( Data => $Param{Data} ) ) {
$FieldSuccess++;
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or' && $CondType eq 'or';
next CONDITIONNAME if $ConditionLinking ne 'or' && $CondType eq 'or';
}
else {
$FieldFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and' && $CondType eq 'and';
# Try next Condition if all Condition Fields have to be true.
next CONDITIONNAME if $CondType eq 'and';
}
next FIELDLNAME;
}
}
# FIELDLNAME end.
if ( $CondType eq 'and' ) {
# If we had no failing check this condition matched.
if ( !$FieldFail ) {
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or';
$ConditionSuccess++;
}
else {
$ConditionFail++;
# Failed check if we have all 'and' condition.s
return if $ConditionLinking eq 'and';
}
}
elsif ( $CondType eq 'or' )
{
# If we had at least one successful check, this condition matched.
if ( $FieldSuccess > 0 ) {
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or';
$ConditionSuccess++;
}
else {
$ConditionFail++;
# Failed check if we have all 'and' conditions.
return if $ConditionLinking eq 'and';
}
}
elsif ( $CondType eq 'xor' )
{
# If we had exactly one successful check, this condition matched.
if ( $FieldSuccess == 1 ) {
# Successful check if we just need one matching Condition to make this Condition valid.
return 1 if $ConditionLinking eq 'or';
$ConditionSuccess++;
}
else {
$ConditionFail++;
}
}
}
# CONDITIONNAME end.
if ( $ConditionLinking eq 'and' ) {
# If we had no failing conditions this Condition matched.
return 1 if !$ConditionFail;
}
elsif ( $ConditionLinking eq 'or' )
{
# If we had at least one successful condition, this condition matched.
return 1 if $ConditionSuccess > 0;
}
elsif ( $ConditionLinking eq 'xor' )
{
# If we had exactly one successful condition, this condition matched.
return 1 if $ConditionSuccess == 1;
}
# If no condition matched till here, we failed.
return;
}
1;
=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