This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions
@@ -0,0 +1,358 @@
# --
# 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::Debugger;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsString IsStringWithData IsHashRefWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Debugger
=head1 DESCRIPTION
GenericInterface data debugger interface.
For every communication process, one Kernel::GenericInterface::Debugger object
should be constructed and fed with data at the various stages
of the process. It will collect the data and write it into the database,
based on the configured debug level.
=head1 PUBLIC INTERFACE
=head2 new()
create an object.
use Kernel::GenericInterface::Debugger;
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => {
DebugThreshold => 'debug',
TestMode => 0, # optional, in testing mode the data will not be written to the DB
# ...
},
WebserviceID => 12,
CommunicationType => Requester, # Requester or Provider
RemoteIP => 192.168.1.1, # optional
CommunicationID => '02a381c622d5f93df868a42151db1983', # optional
);
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check DebuggerConfig - we need a hash ref with at least one entry
if ( !IsHashRefWithData( $Param{DebuggerConfig} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need DebuggerConfig!',
);
return;
}
# DebugThreshold
$Param{DebugThreshold} = $Param{DebuggerConfig}->{DebugThreshold} || 'error';
# check for mandatory values
for my $Needed (qw(WebserviceID CommunicationType DebugThreshold)) {
if ( !IsStringWithData( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
$Self->{$Needed} = $Param{$Needed};
}
# check correct DebugThreshold
if ( $Self->{DebugThreshold} !~ /^(debug|info|notice|error)/i ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'DebugThreshold is not allowed.',
);
return;
}
# check correct CommunicationType
if ( lc $Self->{CommunicationType} !~ /^(provider|requester)/i ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'CommunicationType is not allowed.',
);
return;
}
# TestMode
$Self->{TestMode} = $Param{DebuggerConfig}->{TestMode} || 0;
# remote IP optional
if ( defined $Param{RemoteIP} && !IsStringWithData( $Param{RemoteIP} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need RemoteIP address!'
);
return;
}
$Self->{RemoteIP} = $Param{RemoteIP};
# use communication ID passed in constructor
if ( $Param{CommunicationID} ) {
$Self->{CommunicationID} = $Param{CommunicationID};
}
# create new communication ID
else {
# communication ID MD5 (system time + random #)
my $CurrentTime = $Kernel::OM->Create('Kernel::System::DateTime')->ToEpoch();
my $MD5String = $Kernel::OM->Get('Kernel::System::Main')->MD5sum(
String => $CurrentTime . int( rand(1000000) ),
);
$Self->{CommunicationID} = $MD5String;
}
return $Self;
}
=head2 DebugLog()
add one piece of data to the logging of this communication process.
$DebuggerObject->DebugLog(
DebugLevel => 'debug',
Summary => 'Short summary, one line',
Data => $Data, # optional, $Data can be a string or a scalar reference
);
Available debug levels are: 'debug', 'info', 'notice' and 'error'.
Any messages with 'error' priority will also be written to Kernel::System::Log.
=cut
sub DebugLog {
my ( $Self, %Param ) = @_;
if ( !$Param{Summary} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Summary!',
);
return;
}
# if DebugLevel is not set DebugLevel from constructor is used
$Param{DebugLevel} = $Param{DebugLevel} || $Self->{DebugLevel};
# check correct DebugLevel
if ( $Param{DebugLevel} !~ /^(debug|info|notice|error)/i ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'DebugLevel is not allowed.',
);
return;
}
my %DebugLevels = (
debug => 1,
info => 2,
notice => 3,
error => 4
);
# create log message
my $DataString = '';
if ( IsHashRefWithData( $Param{Data} ) ) {
$DataString = $Kernel::OM->Get('Kernel::System::Main')->Dump( $Param{Data} );
}
elsif ( IsStringWithData( $Param{Data} ) ) {
$DataString = $Param{Data};
}
else {
$DataString = 'No data provided';
}
if ( !$Self->{TestMode} ) {
if ( $DebugLevels{ $Param{DebugLevel} } >= $DebugLevels{ $Self->{DebugThreshold} } ) {
# call AddLog function
$Kernel::OM->Get('Kernel::System::GenericInterface::DebugLog')->LogAdd(
CommunicationID => $Self->{CommunicationID},
CommunicationType => $Self->{CommunicationType},
RemoteIP => $Self->{RemoteIP},
Summary => $Param{Summary},
WebserviceID => $Self->{WebserviceID},
DebugLevel => $Param{DebugLevel},
Data => $DataString,
);
}
return 1 if $Param{DebugLevel} ne 'error';
}
my $LogMessage = <<"EOF";
DebugLog $Param{DebugLevel}:
Summary: $Param{Summary}
Data : $DataString.
EOF
if ( $Param{DebugLevel} eq 'error' ) {
$LogMessage =~ s/\n//g;
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $LogMessage,
);
return 1 if !$Self->{TestMode};
$LogMessage .= "\n";
}
print STDERR $LogMessage;
return 1;
}
=head2 Debug()
passes data to DebugLog with debug level 'debug'
$DebuggerObject->Debug(
Summary => 'Short summary, one line',
Data => $Data, # optional, $Data can be a string or a scalar reference
);
=cut
sub Debug {
my ( $Self, %Param ) = @_;
return if !$Self->DebugLog(
%Param,
DebugLevel => 'debug',
);
return 1;
}
=head2 Info()
passes data to DebugLog with debug level 'info'
$DebuggerObject->Info(
Summary => 'Short summary, one line',
Data => $Data, # optional, $Data can be a string or a scalar reference
);
=cut
sub Info {
my ( $Self, %Param ) = @_;
return if !$Self->DebugLog(
%Param,
DebugLevel => 'info',
);
return 1;
}
=head2 Notice()
passes data to DebugLog with debug level 'notice'
$DebuggerObject->Notice(
Summary => 'Short summary, one line',
Data => $Data, # optional, $Data can be a string or a scalar reference
);
=cut
sub Notice {
my ( $Self, %Param ) = @_;
return if !$Self->DebugLog(
%Param,
DebugLevel => 'notice',
);
return 1;
}
=head2 Error()
passes data to DebugLog with debug level 'error'
then returns data structure to be used as return value in calling function
$DebuggerObject->Error(
Summary => 'Short summary, one line',
Data => $Data, # optional, $Data can be a string or a scalar reference
);
=cut
sub Error {
my ( $Self, %Param ) = @_;
return if !$Self->DebugLog(
%Param,
DebugLevel => 'error',
);
return {
Success => 0,
ErrorMessage => $Param{Summary},
};
}
=begin Internal:
=cut
=head2 DESTROY()
destructor, this will write the log entries to the database.
=cut
sub DESTROY {
my $Self = shift;
#TODO: implement storing of the debug messages
return;
}
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
@@ -0,0 +1,379 @@
# --
# 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::ErrorHandling;
use strict;
use warnings;
use Kernel::GenericInterface::Debugger;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::GenericInterface::Webservice',
);
=head1 NAME
Kernel::GenericInterface::ErrorHandling - Error object to execute registered error handler modules
=head1 SYNOPSIS
=head1 PUBLIC INTERFACE
=over 4
=cut
=item new()
create an object. Do not create it directly, instead use:
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $ErrorObject = $Kernel::OM->Get('Kernel::GenericInterface::ErrorHandling');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=item HandleError()
Receives the current web service and operation or invoker data, as well as the result
of the HandleError method from the related invoker or operation.
The data will be printed via the debugger.
For every registered error handler its configuration will be checked to determine if
it should be called.
my $Result = $ErrorObject->HandleError(
WebserviceID => 1, # ID of the configured remote web service to use
WebserviceConfig => $WebserviceConfig,
CommunicationID => '02a381c622d5f93df868a42151db1983', # communication ID of current debugger instance
CommunicationType => 'Requester', # May be 'Requester' or 'Provider'
CommunicationName => 'CreateTicket', # optional, name of Invoker or Operation
ErrorStage => 'MappingIn', # stage where error occurred
Summary => $ErrorSummary,
Data => $ErrorData,
PastExecutionData => $PastExecutionDataStructure, # optional
);
$Result = {
Success => 0,
ErrorMessage => $ErrorSummary, # returns summary from call
};
=cut
sub HandleError {
my ( $Self, %Param ) = @_;
for my $Needed (qw(WebserviceID WebserviceConfig CommunicationID CommunicationType ErrorStage Summary Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Got no $Needed!",
);
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
}
# Add error message to debugger.
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => $Param{WebserviceConfig}->{Debugger},
WebserviceID => $Param{WebserviceID},
CommunicationType => $Param{CommunicationType},
CommunicationID => $Param{CommunicationID},
);
$DebuggerObject->Error(
Summary => $Param{Summary},
Data => $Param{Data},
);
my $ErrorHandlingConfig = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::ErrorHandling::Module');
if ( !IsHashRefWithData($ErrorHandlingConfig) ) {
return $Self->_LogAndReturn(
%Param,
ErrorMessage => $Param{Summary},
);
}
my $ErrorHandlingModules = $Param{WebserviceConfig}->{ $Param{CommunicationType} }->{ErrorHandling} || {};
my $ErrorHandlingPriority = $Param{WebserviceConfig}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} || {};
# Check for configured error handling modules and priority.
if ( !IsHashRefWithData($ErrorHandlingModules) || !IsArrayRefWithData($ErrorHandlingPriority) ) {
# Obviously nothing configured, just return.
return {
Success => 1,
};
}
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
# Run all registered and activated error handler modules.
my @ReturnData;
my $ReScheduleData;
my %StopAfterMatch;
my %FilterRegexStrings;
ERRORHANDLINGCONFIGKEY:
for my $ErrorHandlingConfigKey ( @{$ErrorHandlingPriority} ) {
next ERRORHANDLINGCONFIGKEY if !$ErrorHandlingConfigKey;
if ( !IsHashRefWithData( $ErrorHandlingModules->{$ErrorHandlingConfigKey} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Error handling module name not found for entry '$ErrorHandlingConfigKey' in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
my $ModuleConfig = $ErrorHandlingModules->{$ErrorHandlingConfigKey};
my $ModuleRegistration = $ErrorHandlingConfig->{ $ModuleConfig->{Type} };
next ERRORHANDLINGCONFIGKEY if $StopAfterMatch{ $ModuleConfig->{Type} };
if ( !IsHashRefWithData($ModuleRegistration) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Error handling module config registration not found for entry '$ErrorHandlingConfigKey' in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
# Check if the registration for each error handler module is valid.
if ( !$ModuleRegistration->{Name} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Module registration for error handling entry '$ErrorHandlingConfigKey' is invalid in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
my $ErrorHandlingModule = 'Kernel::GenericInterface::ErrorHandling::' . $ModuleConfig->{Type};
# Check if backend field exists.
if ( !$MainObject->Require($ErrorHandlingModule) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Can't load error handling module '$ErrorHandlingModule' in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
# Check if error handling object should be called based on filters.
my $CommunicationNameFilterName;
if ( $Param{CommunicationType} eq 'Requester' ) {
$CommunicationNameFilterName = 'InvokerFilter';
}
else {
$CommunicationNameFilterName = 'OperationFilter';
}
if ( IsArrayRefWithData( $ModuleConfig->{$CommunicationNameFilterName} ) ) {
# Only execute module for configured invoker/operations.
next ERRORHANDLINGCONFIGKEY if !IsStringWithData( $Param{CommunicationName} );
if ( !grep { $_ eq $Param{CommunicationName} } @{ $ModuleConfig->{$CommunicationNameFilterName} } ) {
next ERRORHANDLINGCONFIGKEY;
}
}
if ( IsArrayRefWithData( $ModuleConfig->{ErrorStageFilter} ) ) {
# Only execute module for configured error stages.
next ERRORHANDLINGCONFIGKEY if !grep { $_ eq $Param{ErrorStage} } @{ $ModuleConfig->{ErrorStageFilter} };
}
if ( IsStringWithData( $ModuleConfig->{ErrorMessageContentFilter} ) ) {
# OPrepare filter strings.
my @FilterParams = (qw(Summary Data));
if ( !%FilterRegexStrings ) {
for my $FilterParam (@FilterParams) {
if ( IsString( $Param{$FilterParam} ) ) {
$FilterRegexStrings{$FilterParam} = $Param{$FilterParam};
}
else {
$FilterRegexStrings{$FilterParam} = $MainObject->Dump( $Param{$FilterParam} );
}
}
}
# Only execute module if configured regex contains any matches.
my $RegexMatch;
my $FilterRegex = qr{$ModuleConfig->{ErrorMessageContentFilter}};
FILTERPARAM:
for my $FilterParam (@FilterParams) {
next FILTERPARAM if $FilterRegexStrings{$FilterParam} !~ $FilterRegex;
$RegexMatch = 1;
last FILTERPARAM;
}
next ERRORHANDLINGCONFIGKEY if !$RegexMatch;
}
my $ErrorHandlingObject = $Kernel::OM->Get($ErrorHandlingModule);
if ( !$ErrorHandlingObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Couldn't create an object of error handling module '$ModuleConfig->{Type}' in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
if ( ref $ErrorHandlingObject ne $ErrorHandlingModule ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Error handling object for module '$ModuleConfig->{Type}' was not created successfully in WebserviceID $Param{WebserviceID} ($Param{CommunicationType})!",
);
next ERRORHANDLINGCONFIGKEY;
}
my $ErrorHandlingResult = $ErrorHandlingObject->Run(
%Param,
ModuleConfig => $ModuleConfig,
);
if ( !$ErrorHandlingResult->{Success} ) {
return $Self->_LogAndReturn(
%Param,
ErrorMessage => $ErrorHandlingResult->{ErrorMessage},
);
}
push @ReturnData, {
ModuleConfig => $ModuleConfig,
ModuleData => $ErrorHandlingResult->{Data},
ModuleName => $ErrorHandlingConfigKey,
};
$DebuggerObject->Debug(
Summary => "Executed error handling module '$ErrorHandlingConfigKey'",
Data => $ReturnData[-1],
);
if ( IsHashRefWithData( $ErrorHandlingResult->{ReScheduleData} ) ) {
$ReScheduleData = $ErrorHandlingResult->{ReScheduleData};
# Print desired reschedule result.
$DebuggerObject->Info(
Summary => "Got reschedule decision from error handling '$ErrorHandlingConfigKey'",
Data => $ErrorHandlingResult->{ReScheduleData},
);
}
# Check if we should skip some/all further modules.
if ( IsStringWithData( $ModuleConfig->{StopAfterMatch} ) ) {
last ERRORHANDLINGCONFIGKEY if $ModuleConfig->{StopAfterMatch} eq 'all';
if ( $ModuleConfig->{StopAfterMatch} eq 'backend' ) {
$StopAfterMatch{ $ModuleConfig->{Type} } = 1;
}
}
next ERRORHANDLINGCONFIGKEY if !IsHashRefWithData( $ErrorHandlingResult->{Data} );
}
# Print final rescheduling info.
if (
IsHashRefWithData($ReScheduleData)
&& $ReScheduleData->{ReSchedule}
)
{
$DebuggerObject->Notice(
Summary => 'Request will be retried again at:',
Data => $ReScheduleData->{ExecutionTime},
);
}
return {
Success => 1,
Data => \@ReturnData,
ReScheduleData => $ReScheduleData,
};
}
sub _LogAndReturn {
my ( $Self, %Param ) = @_;
for my $Needed (qw(WebserviceID WebserviceConfig CommunicationID CommunicationType Summary ErrorMessage)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Got no $Needed!",
);
return {
Success => 0,
ErrorMessage => $Param{Summary} || "Got no $Needed!",
};
}
}
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => $Param{WebserviceConfig}->{Debugger},
WebserviceID => $Param{WebserviceID},
CommunicationType => $Param{CommunicationType},
CommunicationID => $Param{CommunicationID},
);
$DebuggerObject->Error(
Summary => $Param{ErrorMessage},
);
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $Param{ErrorMessage},
);
return {
Success => 0,
ErrorMessage => $Param{ErrorMessage},
};
}
1;
=back
=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
@@ -0,0 +1,324 @@
# --
# 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::ErrorHandling::RequestRetry;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
);
=head1 NAME
Kernel::GenericInterface::ErrorHandling::RequestRetry - Module do decide about rescheduling for failed requests
=head1 SYNOPSIS
=head1 PUBLIC INTERFACE
=over 4
=cut
=item new()
create an object. Do not create it directly, instead use:
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $ErrorObject = $Kernel::OM->Get('Kernel::GenericInterface::ErrorHandling');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=item Run()
Decides if a non-successful request should be retried, based on the configuration.
Relevant module configuration variables are:
- ScheduleRetry # enable or disable retry for request
- RetryIntervalStart # time in seconds for first retry after initial request
- RetryIntervalFactor # send further retries after the same interval as the first or in increasing intervals
- RetryIntervalMax # maximum allowed interval between retries
- RetryCountMax # maximum allowed number of retries
- RetryPeriodMax # maximum time allowed for retries after initial request
my $Result = $ErrorObject->Run(
PastExecutionData => $PastExecutionDataStructure, # optional
ModuleConfig => $ModuleConfig,
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # if an error occurred
Data => { ... }, # result payload
ReScheduleData => { ... }, # reschedule information
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# Module config parameter validity check.
if ( !IsHashRefWithData( $Param{ModuleConfig} ) ) {
return $Self->_LogAndReturn( ErrorMessage => 'Got no ModuleConfig!' );
}
my $ModuleConfigCheck = $Self->_ModuleConfigCheck( %{ $Param{ModuleConfig} } );
return $ModuleConfigCheck if !$ModuleConfigCheck->{Success};
# Set basic information including possibly existing past execution data.
my $RetryCount = $Param{PastExecutionData}->{RetryCount} // 0;
my $CurrentDateTime = $Kernel::OM->Create('Kernel::System::DateTime');
# Get date-time of last (=this) request. If we are not in a retry, use current date-time.
# This is used to properly calculate time based intervals.
my $CurrentRequestDateTime;
if ( IsStringWithData( $Param{PastExecutionData}->{RetryDateTime} ) ) {
$CurrentRequestDateTime = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Param{PastExecutionData}->{RetryDateTime},
},
);
return $Self->_LogAndReturn( ErrorMessage => 'RetryDateTime is invalid!' ) if !$CurrentRequestDateTime;
}
else {
$CurrentRequestDateTime = $CurrentDateTime->Clone();
}
# Get date-time of first request. If we are not in a retry, use current date-time.
my $InitialRequestDateTime;
if ( IsStringWithData( $Param{PastExecutionData}->{InitialRequestDateTime} ) ) {
$InitialRequestDateTime = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Param{PastExecutionData}->{InitialRequestDateTime},
},
);
return $Self->_LogAndReturn( ErrorMessage => 'InitialRequestDateTime is invalid!' ) if !$InitialRequestDateTime;
}
else {
$InitialRequestDateTime = $CurrentDateTime->Clone();
}
# Prepare default set of return data.
my %ReturnData = (
Success => 1,
Data => {
ReSchedule => 0,
InitialRequestDateTime => $InitialRequestDateTime->ToString(),
CurrentRequestDateTime => $CurrentRequestDateTime->ToString(),
CurrentRetryCount => $RetryCount,
MaximumRetryCountReached => 0,
MaximumRetryPeriodReached => 0,
},
ReScheduleData => {
ReSchedule => 0,
},
);
# Retries are completely disabled.
if ( !$Param{ModuleConfig}->{ScheduleRetry} ) {
return \%ReturnData;
}
# No retry if maximum retry count has been reached.
if (
$Param{ModuleConfig}->{RetryCountMax}
&& $RetryCount >= $Param{ModuleConfig}->{RetryCountMax}
)
{
$ReturnData{Data}->{MaximumRetryCountReached} = 1;
return \%ReturnData;
}
# No retry if maximum retry period has been reached.
my $DeltaInitialToCurrentRequest;
if ( $Param{ModuleConfig}->{RetryPeriodMax} ) {
$DeltaInitialToCurrentRequest = $InitialRequestDateTime->Delta( DateTimeObject => $CurrentRequestDateTime );
if ( $DeltaInitialToCurrentRequest->{AbsoluteSeconds} >= $Param{ModuleConfig}->{RetryPeriodMax} ) {
$ReturnData{Data}->{MaximumRetryPeriodReached} = 1;
return \%ReturnData;
}
}
# Calculate interval for next execution.
my $RetryInterval;
if ( IsStringWithData( $Param{PastExecutionData}->{RetryInterval} ) ) {
$RetryInterval
= int( $Param{PastExecutionData}->{RetryInterval} * $Param{ModuleConfig}->{RetryIntervalFactor} );
if (
IsStringWithData( $Param{ModuleConfig}->{RetryIntervalMax} )
&& $RetryInterval > $Param{ModuleConfig}->{RetryIntervalMax}
)
{
$RetryInterval = $Param{ModuleConfig}->{RetryIntervalMax};
}
}
else {
$RetryInterval = $Param{ModuleConfig}->{RetryIntervalStart};
}
# Calculate next execution timestamp.
my $TargetDateTime;
if ( $Param{ModuleConfig}->{RetryPeriodMax} ) {
if ( !$DeltaInitialToCurrentRequest ) {
$DeltaInitialToCurrentRequest = $InitialRequestDateTime->Delta( DateTimeObject => $CurrentRequestDateTime );
}
if (
$DeltaInitialToCurrentRequest->{AbsoluteSeconds} + $RetryInterval
>= $Param{ModuleConfig}->{RetryPeriodMax}
)
{
$TargetDateTime = $InitialRequestDateTime->Clone();
$TargetDateTime->Add( Seconds => $Param{ModuleConfig}->{RetryPeriodMax} );
}
}
if ( !$TargetDateTime ) {
$TargetDateTime = $CurrentRequestDateTime->Clone();
$TargetDateTime->Add( Seconds => $RetryInterval );
}
# Even after delayed executions, minimum wait time after requests is 1 second to prevent possible DoS.
if ( $TargetDateTime->Compare( DateTimeObject => $CurrentDateTime ) != 1 ) {
$TargetDateTime = $CurrentDateTime->Clone();
$TargetDateTime->Add( Seconds => 1 );
}
# Schedule retry and set appropriate past execution data.
$ReturnData{Data}->{ReSchedule} = 1;
return {
%ReturnData,
ReScheduleData => {
ReSchedule => 1,
ExecutionTime => $TargetDateTime->ToString(),
PastExecutionData => {
InitialRequestDateTime => $InitialRequestDateTime->ToString(),
RetryCount => ++$RetryCount,
RetryInterval => $RetryInterval,
RetryDateTime => $TargetDateTime->ToString(),
},
},
};
}
sub _ModuleConfigCheck {
my ( $Self, %Param ) = @_;
# Allowed Values:
# ScheduleRetry => [ 0, 1 ],
# RetryIntervalFactor => [ 1, 1.5, 2 ],
# RetryIntervalStart => [ 0, 1 .. 999999 ],
# RetryIntervalMax => [ undef, '', 0, 1 .. 999999 ],
# RetryCountMax => [ undef, '', 1 .. 999999 ],
# RetryPeriodMax => [ undef, '', 1 .. 999999 ],
STRINGWITHDATA:
for my $StringWithData (qw(ScheduleRetry RetryIntervalStart RetryIntervalFactor)) {
next STRINGWITHDATA if IsStringWithData( $Param{$StringWithData} );
return $Self->_LogAndReturn( ErrorMessage => "Config param '$StringWithData' is not a non-empty string!" );
}
STRING:
for my $String (qw(RetryIntervalMax RetryCountMax RetryPeriodMax)) {
# Set fall-back for optional parameters.
$Param{$String} //= '';
next STRING if IsString( $Param{$String} );
return $Self->_LogAndReturn( ErrorMessage => "Config param '$String' is not a string!" );
}
if (
$Param{ScheduleRetry} ne '0'
&& $Param{ScheduleRetry} ne '1'
)
{
return $Self->_LogAndReturn( ErrorMessage => "Config param 'ScheduleRetry' is not '0' or '1'!" );
}
if (
$Param{RetryIntervalFactor} ne '1'
&& $Param{RetryIntervalFactor} ne '1.5'
&& $Param{RetryIntervalFactor} ne '2'
)
{
return $Self->_LogAndReturn( ErrorMessage => "Config param 'RetryIntervalFactor' is not '1', '1.5' or '2'!" );
}
my %ParamToMessage = (
RetryIntervalStart => "Config param 'RetryIntervalStart' is not '0' or a positive integer!",
RetryIntervalMax => "Config param 'RetryIntervalMax' is not empty, '0' or a positive integer!",
RetryCountMax => "Config param 'RetryCountMax' is not empty or a positive integer!",
RetryPeriodMax => "Config param 'RetryPeriodMax' is not empty or a positive integer!",
);
INTEGER:
for my $Integer (qw(RetryIntervalStart RetryIntervalMax RetryCountMax RetryPeriodMax)) {
# RetryIntervalStart is not optional but string length >0 has been checked already.
next INTEGER if $Param{$Integer} eq '';
next INTEGER if IsPositiveInteger( $Param{$Integer} );
# RetryIntervalStart and RetryIntervalMax may also contain '0'.
if ( $Integer eq 'RetryIntervalStart' || $Integer eq 'RetryIntervalMax' ) {
next INTEGER if $Param{$Integer} eq '0';
}
return $Self->_LogAndReturn( ErrorMessage => $ParamToMessage{$Integer} );
}
return {
Success => 1,
};
}
sub _LogAndReturn {
my ( $Self, %Param ) = @_;
my $ErrorMessage = $Param{ErrorMessage} || 'No error message provided!';
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => $ErrorMessage,
);
return {
Success => 0,
ErrorMessage => $ErrorMessage,
};
}
1;
=back
=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
@@ -0,0 +1,999 @@
# --
# 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<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
@@ -0,0 +1,83 @@
# --
# 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::ObjectType::Appointment;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Calendar::Appointment',,
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Appointment - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my %IDs;
$IDs{AppointmentID} = $Param{Data}->{AppointmentID};
$IDs{CalendarID} = $Param{Data}->{CalendarID};
for my $Needed (qw(AppointmentID CalendarID)) {
if ( !$IDs{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::Calendar::Appointment')->AppointmentGet(%IDs);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,99 @@
# --
# 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::ObjectType::Article;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Article - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my %IDs;
$IDs{ArticleID} = $Param{Data}->{ArticleID};
$IDs{TicketID} = $Param{Data}->{TicketID};
for my $Needed (qw(ArticleID TicketID)) {
if ( !$IDs{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# Get ticket data to be able to filtering conditions at article event (see bug#13708).
my %TicketData = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $Param{Data}->{TicketID},
DynamicFields => 1,
UserID => 1,
);
my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForArticle(%IDs);
my %ObjectData = $ArticleBackendObject->ArticleGet(
%IDs,
DynamicFields => 1,
RealNames => 1,
UserID => 1,
);
return ( %TicketData, %ObjectData );
}
1;
=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
@@ -0,0 +1,81 @@
# --
# 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::ObjectType::Calendar;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Calendar',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Calendar - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{CalendarID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need CalendarID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::Calendar')->CalendarGet(
CalendarID => $ID,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,83 @@
# --
# 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::ObjectType::CustomerCompany;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::CustomerCompany',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::CustomerCompany - GenericInterface event data handler
=head1 SYNOPSIS
This event handler is a wrapper module to gather data from objects.
The term CustomerCompany is deprecated, therefor we call the Customer module.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{CustomerID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need CustomerID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::CustomerCompany')->CustomerCompanyGet(
CustomerID => $ID,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,81 @@
# --
# 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::ObjectType::CustomerUser;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::CustomerUser',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::CustomerUser - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{UserLogin};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserLogin!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $ID,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,81 @@
# --
# 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::ObjectType::DynamicField;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::DynamicField',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::DynamicField - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{NewData}->{ID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need NewData->ID!",
);
return;
}
my $ObjectData = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldGet(
ID => $ID,
);
return %{$ObjectData};
}
1;
=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
@@ -0,0 +1,82 @@
# --
# 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::ObjectType::ITSMChange;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::ITSMChange',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::ITSMChange - GenericInterface event data handler
=head1 DESCRIPTION
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{ChangeID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need ChangeID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeGet(
ChangeID => $ID,
UserID => 1,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,82 @@
# --
# 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::ObjectType::ITSMWorkOrder;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::ITSMChange::ITSMWorkOrder',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::ITSMWorkOrder - GenericInterface event data handler
=head1 DESCRIPTION
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{WorkOrderID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need WorkOrderID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder')->WorkOrderGet(
WorkOrderID => $ID,
UserID => 1,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,86 @@
# --
# 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::ObjectType::LinkObject;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::LinkObject',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::LinkObject - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
for my $Needed (qw(SourceObject SourceKey TargetObject TargetKey Type State)) {
if ( !$Param{Data}->{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need LinkObjectID!",
);
return;
}
}
my %ObjectData = (
SourceObject => $Param{Data}->{SourceObject},
SourceKey => $Param{Data}->{SourceKey},
TargetObject => $Param{Data}->{TargetObject},
TargetKey => $Param{Data}->{TargetKey},
Type => $Param{Data}->{Type},
State => $Param{Data}->{State},
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,81 @@
# --
# 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::ObjectType::Package;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Package',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Package - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
if ( !$Param{Data}->{Name} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Name!",
);
return;
}
my @List = $Kernel::OM->Get('Kernel::System::Package')->RepositoryList();
my %ListLookup = map { $_->{Name}->{Content} => $_ } @List;
my %ObjectData = %{ $ListLookup{ $Param{Data}->{Name} } };
return %ObjectData;
}
1;
=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
@@ -0,0 +1,81 @@
# --
# 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::ObjectType::Queue;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Queue',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Queue - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{Queue}->{QueueID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need QueueID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet(
ID => $ID,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,85 @@
# --
# 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::ObjectType::Ticket;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::Ticket',
);
=head1 NAME
Kernel::GenericInterface::Event::ObjectType::Ticket - GenericInterface event data handler
=head1 SYNOPSIS
This event handler gathers data from objects.
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub DataGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
my $ID = $Param{Data}->{TicketID};
if ( !$ID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need TicketID!",
);
return;
}
my %ObjectData = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $ID,
DynamicFields => 1,
UserID => 1,
Silent => 0,
Extended => 1,
);
return %ObjectData;
}
1;
=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
@@ -0,0 +1,111 @@
# --
# 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::Validation::ValidateDemo;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::Log',
);
=head1 NAME
Kernel::GenericInterface::Event::Validation::ValidateDemo - Demo for condition validation module
=head1 DESCRIPTION
All ValidateDemo functions.
=head1 PUBLIC INTERFACE
=cut
=head2 new()
create an object. Do not use it directly, instead use:
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $ValidateDemoObject = $Kernel::OM->Get('Kernel::GenericInterface::Event::Validation::ValidateDemo');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=head2 Validate()
Validate Data
my $ValidateResult = $ValidateModuleObject->Validate(
Data => {
Queue => 'Postmaster',
# ...
},
);
Returns:
$ValidateResult = 1; # or undef, only returns 1 if Queue is 'Postmaster'
);
=cut
sub Validate {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Data)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
# Check if we have Data to check against conditions
if ( !IsHashRefWithData( $Param{Data} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Data has no values!",
);
return;
}
# Check object data (e.g. ticket queue)
if ( $Param{Data}->{Queue} && $Param{Data}->{Queue} eq 'Postmaster' ) {
return 1;
}
return;
}
1;
=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
@@ -0,0 +1,239 @@
# --
# 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::Invoker;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsStringWithData);
# Prevent 'Used once' warning for Kernel::OM.
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Invoker - GenericInterface Invoker interface
=head1 DESCRIPTION
Invokers are responsible to prepare for making a remote web service
request.
For every Request, two methods are called:
=over 4
=item L</PrepareRequest()>
=item L</HandleResponse()>
=back
The first method prepares the response and can prevent it by returning
an error state. The second method must always be called if the request
was initiated to allow the Invoker to handle possible errors.
=head1 PUBLIC INTERFACE
=head2 new()
create an object.
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Invoker;
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => {
DebugThreshold => 'debug',
TestMode => 0, # optional, in testing mode the data will not be written to the DB
# ...
},
WebserviceID => 12,
CommunicationType => Requester, # Requester or Provider
RemoteIP => 192.168.1.1, # optional
);
my $InvokerObject = Kernel::GenericInterface::Invoker->new(
DebuggerObject => $DebuggerObject,
Invoker => 'TicketLock', # the name of the invoker in the web service
InvokerType => 'Nagios::TicketLock', # the Invoker backend to use
WebserviceID => 1 # the WebserviceID where the Invoker belongs
# normally this is passed by the requester
);
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
# Check needed params.
for my $Needed (qw( DebuggerObject Invoker InvokerType WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
if ( !IsStringWithData( $Param{InvokerType} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no Invoker Type as string with value!',
);
}
# Load backend module.
my $GenericModule = 'Kernel::GenericInterface::Invoker::' . $Param{InvokerType};
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
return $Self->{DebuggerObject}->Error( Summary => "Can't load invoker backend module!" );
}
$Self->{BackendObject} = $GenericModule->new( %{$Self} );
# Pass back error message from backend if backend module could not be executed.
return $Self->{BackendObject} if ref $Self->{BackendObject} ne $GenericModule;
return $Self;
}
=head2 PrepareRequest()
prepare the invocation of the configured remote web service.
my $Result = $InvokerObject->PrepareRequest(
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
$Result = {
Success => 1, # 0 or 1
StopCommunication => 1, # in case of is not needed to continue with the
# request (do nothing just exist gracefully)
};
=cut
sub PrepareRequest {
my ( $Self, %Param ) = @_;
# Check data - only accept undef or hash ref or array ref.
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' && ref $Param{Data} ne 'ARRAY' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash or array ref in Invoker handler (PrepareRequest)!'
);
}
# Start map on backend.
return $Self->{BackendObject}->PrepareRequest(%Param);
}
=head2 HandleResponse()
handle response data of the configured remote web service.
my $Result = $InvokerObject->HandleResponse(
ResponseSuccess => 1, # success status of the remote web service
ResponseErrorMessage => '', # in case of web service error
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub HandleResponse {
my ( $Self, %Param ) = @_;
# Check data - only accept undef or hash ref or array ref.
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' && ref $Param{Data} ne 'ARRAY' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash or array ref in Invoker handler (HandleResponse)!'
);
}
# Start map on backend.
return $Self->{BackendObject}->HandleResponse(%Param);
}
=head2 HandleError()
handle error data of the configured remote web service.
my $Result = $InvokerObject->HandleError(
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub HandleError {
my ( $Self, %Param ) = @_;
# Check data - only accept undef or hash ref or array ref.
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' && ref $Param{Data} ne 'ARRAY' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash or array ref in Invoker handler (HandleResponse)!'
);
}
return $Self->{BackendObject}->HandleError(%Param);
}
1;
=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
@@ -0,0 +1,180 @@
# --
# 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::Invoker::Test::Test;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsString IsStringWithData);
# prevent 'Used once' warning for Kernel::OM
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Invoker::Test::Test - GenericInterface test Invoker backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Invoker->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
if ( !$Param{DebuggerObject} ) {
return {
Success => 0,
ErrorMessage => "Got no DebuggerObject!"
};
}
$Self->{DebuggerObject} = $Param{DebuggerObject};
return $Self;
}
=head2 PrepareRequest()
prepare the invocation of the configured remote web service.
my $Result = $InvokerObject->PrepareRequest(
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub PrepareRequest {
my ( $Self, %Param ) = @_;
# we need a TicketNumber
if ( !IsStringWithData( $Param{Data}->{TicketNumber} ) ) {
return $Self->{DebuggerObject}->Error( Summary => 'Got no TicketNumber' );
}
my %ReturnData;
$ReturnData{TicketNumber} = $Param{Data}->{TicketNumber};
# check Action
if ( IsStringWithData( $Param{Data}->{Action} ) ) {
$ReturnData{Action} = $Param{Data}->{Action} . 'Test';
}
# check request for system time
if ( IsStringWithData( $Param{Data}->{GetSystemTime} ) && $Param{Data}->{GetSystemTime} ) {
$ReturnData{SystemTime} = $Kernel::OM->Create('Kernel::System::DateTime')->ToEpoch();
}
return {
Success => 1,
Data => \%ReturnData,
};
}
=head2 HandleResponse()
handle response data of the configured remote web service.
my $Result = $InvokerObject->HandleResponse(
ResponseSuccess => 1, # success status of the remote web service
ResponseErrorMessage => '', # in case of web service error
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub HandleResponse {
my ( $Self, %Param ) = @_;
# if there was an error in the response, forward it
if ( !$Param{ResponseSuccess} ) {
if ( !IsStringWithData( $Param{ResponseErrorMessage} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got response error, but no response error message!',
);
}
return {
Success => 0,
ErrorMessage => $Param{ResponseErrorMessage},
};
}
# we need a TicketNumber
if ( !IsStringWithData( $Param{Data}->{TicketNumber} ) ) {
return $Self->{DebuggerObject}->Error( Summary => 'Got no TicketNumber!' );
}
# prepare TicketNumber
my %ReturnData = (
TicketNumber => $Param{Data}->{TicketNumber},
);
# check Action
if ( IsStringWithData( $Param{Data}->{Action} ) ) {
if ( $Param{Data}->{Action} !~ m{ \A ( .*? ) Test \z }xms ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Action but it is not in required format!',
);
}
$ReturnData{Action} = $1;
}
return {
Success => 1,
Data => \%ReturnData,
};
}
1;
=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
@@ -0,0 +1,153 @@
# --
# 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::Invoker::Test::TestSimple;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsString IsStringWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Invoker::Test::TestSimple - GenericInterface test Invoker backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Invoker->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
if ( !$Param{DebuggerObject} ) {
return {
Success => 0,
ErrorMessage => "Got no DebuggerObject!"
};
}
$Self->{DebuggerObject} = $Param{DebuggerObject};
return $Self;
}
=head2 PrepareRequest()
prepare the invocation of the configured remote web service.
This will just return the data that was passed to the function.
my $Result = $InvokerObject->PrepareRequest(
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub PrepareRequest {
my ( $Self, %Param ) = @_;
return {
Success => 1,
Data => $Param{Data},
};
}
=head2 HandleResponse()
handle response data of the configured remote web service.
This will just return the data that was passed to the function.
my $Result = $InvokerObject->HandleResponse(
ResponseSuccess => 1, # success status of the remote web service
ResponseErrorMessage => '', # in case of web service error
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub HandleResponse {
my ( $Self, %Param ) = @_;
# if there was an error in the response, forward it
if ( !$Param{ResponseSuccess} ) {
return {
Success => 0,
ErrorMessage => $Param{ResponseErrorMessage},
};
}
if ( $Param{Data}->{ResponseContent} && $Param{Data}->{ResponseContent} =~ m{ReSchedule=1} ) {
# ResponseContent has URI like params, convert them into a hash
my %QueryParams = split /[&=]/, $Param{Data}->{ResponseContent};
# unscape URI strings in query parameters
for my $Param ( sort keys %QueryParams ) {
$QueryParams{$Param} = URI::Escape::uri_unescape( $QueryParams{$Param} );
}
# fix ExecutrionTime param
if ( $QueryParams{ExecutionTime} ) {
$QueryParams{ExecutionTime} =~ s{(\d+)\+(\d+)}{$1 $2};
}
return {
Success => 0,
ErrorMessage => 'Re-Scheduling...',
Data => \%QueryParams,
};
}
return {
Success => 1,
Data => $Param{Data},
};
}
1;
=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
@@ -0,0 +1,182 @@
# --
# 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::Mapping;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData IsStringWithData);
# prevent 'Used once' warning for Kernel::OM
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Mapping - GenericInterface data mapping interface
=head1 PUBLIC INTERFACE
=head2 new()
create an object.
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Mapping;
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => {
DebugThreshold => 'debug',
TestMode => 0, # optional, in testing mode the data will not be written to the DB
# ...
},
WebserviceID => 12,
CommunicationType => Requester, # Requester or Provider
RemoteIP => 192.168.1.1, # optional
);
my $MappingObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $DebuggerObject,
Invoker => 'TicketLock', # the name of the invoker in the web service
InvokerType => 'Nagios::TicketLock', # the Invoker backend to use
Operation => 'TicketCreate', # the name of the operation in the web service
OperationType => 'Ticket::TicketCreate', # the local operation backend to use
MappingConfig => {
Type => 'MappingSimple',
Config => {
# ...
},
},
);
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
for my $Needed (qw(DebuggerObject MappingConfig)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# add optional params
OPTIONAL:
for my $Optional (qw(Invoker InvokerType Operation OperationType)) {
next OPTIONAL if !$Param{$Optional};
$Self->{$Optional} = $Param{$Optional};
}
# check config - we need at least a config type
if ( !IsHashRefWithData( $Param{MappingConfig} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig as hash ref with content!',
);
}
if ( !IsStringWithData( $Param{MappingConfig}->{Type} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig with Type as string with value!',
);
}
# check config - if we have a map config, it has to be a non-empty hash ref
if (
defined $Param{MappingConfig}->{Config}
&& !IsHashRefWithData( $Param{MappingConfig}->{Config} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => 'Got MappingConfig with Data, but Data is no hash ref with content!',
);
}
# load backend module
my $GenericModule = 'Kernel::GenericInterface::Mapping::' . $Param{MappingConfig}->{Type};
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
return $Self->{DebuggerObject}->Error( Summary => "Can't load mapping backend module!" );
}
$Self->{BackendObject} = $GenericModule->new( %{$Self} );
# pass back error message from backend if backend module could not be executed
return $Self->{BackendObject} if ref $Self->{BackendObject} ne $GenericModule;
return $Self;
}
=head2 Map()
perform data mapping in backend
my $Result = $MappingObject->Map(
Data => { # data payload before mapping
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload of after mapping
...
},
};
=cut
sub Map {
my ( $Self, %Param ) = @_;
# check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Mapping handler!'
);
}
# return if data is empty
if ( !defined $Param{Data} || !%{ $Param{Data} } ) {
return {
Success => 1,
Data => {},
};
}
# start map on backend
return $Self->{BackendObject}->Map(%Param);
}
1;
=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
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func="http://exslt.org/functions"
xmlns:otrs="http://otrs.org"
extension-element-prefixes="func otrs">
<func:function name="otrs:date-xsd-to-iso">
<xsl:param name="date-time" />
<xsl:variable name="formatted">
<xsl:value-of select="substring($date-time, 1, 10)" />
<xsl:text> </xsl:text>
<xsl:value-of select="substring($date-time, 12, 8)" />
</xsl:variable>
<func:result select="string($formatted)" />
</func:function>
<func:function name="otrs:date-iso-to-xsd">
<xsl:param name="date-time" />
<xsl:variable name="formatted">
<xsl:value-of select="substring($date-time, 1, 10)" />
<xsl:text>T</xsl:text>
<xsl:value-of select="substring($date-time, 12, 8)" />
<xsl:text>Z</xsl:text>
</xsl:variable>
<func:result select="string($formatted)" />
</func:function>
</xsl:stylesheet>
@@ -0,0 +1,475 @@
# --
# 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::Mapping::Simple;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData IsString IsStringWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Mapping::Simple - GenericInterface simple data mapping backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Mapping->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
for my $Needed (qw(DebuggerObject MappingConfig)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# check mapping config
if ( !IsHashRefWithData( $Param{MappingConfig} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig as hash ref with content!',
);
}
# check config - if we have a map config, it has to be a non-empty hash ref
if (
defined $Param{MappingConfig}->{Config}
&& !IsHashRefWithData( $Param{MappingConfig}->{Config} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => 'Got MappingConfig with Data, but Data is no hash ref with content!',
);
}
# check configuration
my $ConfigCheck = $Self->_ConfigCheck( Config => $Self->{MappingConfig}->{Config} );
return $ConfigCheck if !$ConfigCheck->{Success};
return $Self;
}
=head2 Map()
provides 1:1 and regex mapping for keys and values
also the use of a default for keys and values that were not mapped is possible
we need the config to be in the following format
$Self->{MappingConfig}->{Config} = {
KeyMapExact => { # optional. key/value pairs for direct replacement
'old_value' => 'new_value',
'another_old_value' => 'another_new_value',
'maps_to_same_value => 'another_new_value',
},
KeyMapRegEx => { # optional. replace keys with value if current key matches regex
'Stat(e|us)' => 'state',
'[pP]riority' => 'prio',
},
KeyMapDefault => { # required. replace keys if the have not been replaced before
MapType => 'Keep', # possible values are
# 'Keep' (leave unchanged)
# 'Ignore' (drop key/value pair)
# 'MapTo' (use provided value as default)
MapTo => 'new_value', # only used if 'MapType' is 'MapTo'. then required
},
ValueMap => { # optional.
'new_key_name' => { # optional. Replacement for a specific key
ValueMapExact => { # optional. key/value pairs for direct replacement
'old_value' => 'new_value',
'another_old_value' => 'another_new_value',
'maps_to_same_value => 'another_new_value',
},
ValueMapRegEx => { # optional. replace keys with value if current key matches regex
'Stat(e|us)' => 'state',
'[pP]riority' => 'prio',
},
},
},
ValueMapDefault => { # required. replace keys if the have not been replaced before
MapType => 'Keep', # possible values are
# 'Keep' (leave unchanged)
# 'Ignore' (drop key/value pair)
# 'MapTo' (use provided value as default)
MapTo => 'new_value', # only used if 'MapType' is 'MapTo'. then required
},
};
my $ReturnData = $MappingObject->Map(
Data => {
'original_key' => 'original_value',
'another_key' => 'next_value',
},
);
my $ReturnData = {
'changed_key' => 'changed_value',
'original_key' => 'another_changed_value',
'another_original_key' => 'default_value',
'default_key' => 'changed_value',
};
=cut
sub Map {
my ( $Self, %Param ) = @_;
# check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Mapping Simple backend!'
);
}
# return if data is empty
if ( !defined $Param{Data} || !%{ $Param{Data} } ) {
return {
Success => 1,
Data => {},
};
}
# prepare short config variable
my $Config = $Self->{MappingConfig}->{Config};
# no config means we just return input data
if ( !$Config ) {
return {
Success => 1,
Data => $Param{Data},
};
}
# go through keys for replacement
my %ReturnData;
CONFIGKEY:
for my $OldKey ( sort keys %{ $Param{Data} } ) {
# check if key is valid
if ( !IsStringWithData($OldKey) ) {
$Self->{DebuggerObject}->Notice( Summary => 'Got an original key that is not valid!' );
next CONFIGKEY;
}
# map key
my $NewKey;
# first check in exact (1:1) map
if ( $Config->{KeyMapExact} && $Config->{KeyMapExact}->{$OldKey} ) {
$NewKey = $Config->{KeyMapExact}->{$OldKey};
}
# if we have no match from exact map, try regex map
if ( !$NewKey && $Config->{KeyMapRegEx} ) {
KEYMAPREGEX:
for my $ConfigKey ( sort keys %{ $Config->{KeyMapRegEx} } ) {
next KEYMAPREGEX if $OldKey !~ m{ \A $ConfigKey \z }xms;
if ( $ReturnData{ $Config->{KeyMapRegEx}->{$ConfigKey} } ) {
$Self->{DebuggerObject}->Notice(
Summary =>
"The data key '$Config->{KeyMapRegEx}->{$ConfigKey}' already exists!",
);
next CONFIGKEY;
}
$NewKey = $Config->{KeyMapRegEx}->{$ConfigKey};
last KEYMAPREGEX;
}
}
# if we still have no match, apply default
if ( !$NewKey ) {
# check map type options
if ( $Config->{KeyMapDefault}->{MapType} eq 'Keep' ) {
$NewKey = $OldKey;
}
elsif ( $Config->{KeyMapDefault}->{MapType} eq 'Ignore' ) {
next CONFIGKEY;
}
elsif ( $Config->{KeyMapDefault}->{MapType} eq 'MapTo' ) {
# check if we already have a key with the same name
if ( $ReturnData{ $Config->{KeyMapDefault}->{MapTo} } ) {
$Self->{DebuggerObject}->Notice(
Summary => "The data key $Config->{KeyMapDefault}->{MapTo} already exists!",
);
next CONFIGKEY;
}
$NewKey = $Config->{KeyMapDefault}->{MapTo};
}
}
# sanity check - we should have a translated key now
if ( !$NewKey ) {
return $Self->{DebuggerObject}->Error( Summary => "Could not map data key $NewKey!" );
}
# map value
my $OldValue = $Param{Data}->{$OldKey};
# if value is no string, just pass through
if ( !IsString($OldValue) ) {
$ReturnData{$NewKey} = $OldValue;
next CONFIGKEY;
}
# check if we have a value mapping for the specific key
my $ValueMap;
if ( $Config->{ValueMap} && $Config->{ValueMap}->{$NewKey} ) {
$ValueMap = $Config->{ValueMap}->{$NewKey};
}
if ($ValueMap) {
# first check in exact (1:1) map
if ( $ValueMap->{ValueMapExact} && defined $ValueMap->{ValueMapExact}->{$OldValue} ) {
$ReturnData{$NewKey} = $ValueMap->{ValueMapExact}->{$OldValue};
next CONFIGKEY;
}
# if we have no match from exact map, try regex map
if ( $ValueMap->{ValueMapRegEx} ) {
VALUEMAPREGEX:
for my $ConfigKey ( sort keys %{ $ValueMap->{ValueMapRegEx} } ) {
next VALUEMAPREGEX if $OldValue !~ m{ \A $ConfigKey \z }xms;
$ReturnData{$NewKey} = $ValueMap->{ValueMapRegEx}->{$ConfigKey};
next CONFIGKEY;
}
}
}
# if we had no mapping, apply default
# keep current value
if ( $Config->{ValueMapDefault}->{MapType} eq 'Keep' ) {
$ReturnData{$NewKey} = $OldValue;
next CONFIGKEY;
}
# map to default value
if ( $Config->{ValueMapDefault}->{MapType} eq 'MapTo' ) {
$ReturnData{$NewKey} = $Config->{ValueMapDefault}->{MapTo};
next CONFIGKEY;
}
# implicit ignore
next CONFIGKEY;
}
return {
Success => 1,
Data => \%ReturnData,
};
}
=begin Internal:
=head2 _ConfigCheck()
does checks to make sure the config is sane
my $Return = $MappingObject->_ConfigCheck(
Config => { # config as defined for Map
...
},
);
in case of an error
$Return => {
Success => 0,
ErrorMessage => 'An error occurred',
};
in case of a success
$Return = {
Success => 1,
};
=cut
sub _ConfigCheck {
my ( $Self, %Param ) = @_;
# just return success if config is undefined or empty hashref
my $Config = $Param{Config};
if ( !defined $Config ) {
return {
Success => 1,
};
}
if ( ref $Config ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Config is defined but not a hash reference!',
);
}
if ( !IsHashRefWithData($Config) ) {
return {
Success => 1,
};
}
# parse config options for validity
my %OnlyStringConfigTypes = (
KeyMapExact => 1,
KeyMapRegEx => 1,
KeyMapDefault => 1,
ValueMapDefault => 1,
);
my %RequiredConfigTypes = (
KeyMapDefault => 1,
ValueMapDefault => 1,
);
CONFIGTYPE:
for my $ConfigType (qw(KeyMapExact KeyMapRegEx KeyMapDefault ValueMap ValueMapDefault)) {
# require some types
if ( !defined $Config->{$ConfigType} ) {
next CONFIGTYPE if !$RequiredConfigTypes{$ConfigType};
return $Self->{DebuggerObject}->Error(
Summary => "Got no $ConfigType, but it is required!",
);
}
# check type definition
if ( !IsHashRefWithData( $Config->{$ConfigType} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got $ConfigType with Data, but Data is no hash ref with content!",
);
}
# check keys and values of these config types
next CONFIGTYPE if !$OnlyStringConfigTypes{$ConfigType};
for my $ConfigKey ( sort keys %{ $Config->{$ConfigType} } ) {
if ( !IsString($ConfigKey) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got key in $ConfigType which is not a string!",
);
}
if ( !IsString( $Config->{$ConfigType}->{$ConfigKey} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got value for $ConfigKey in $ConfigType which is not a string!",
);
}
}
}
# check default configuration in KeyMapDefault and ValueMapDefault
my %ValidMapTypes = (
Keep => 1,
Ignore => 1,
MapTo => 1,
);
CONFIGTYPE:
for my $ConfigType (qw(KeyMapDefault ValueMapDefault)) {
# require MapType as a string with a valid value
if (
!IsStringWithData( $Config->{$ConfigType}->{MapType} )
|| !$ValidMapTypes{ $Config->{$ConfigType}->{MapType} }
)
{
return $Self->{DebuggerObject}->Error(
Summary => "Got no valid MapType in $ConfigType!",
);
}
# check MapTo if MapType is set to 'MapTo'
if (
$Config->{$ConfigType}->{MapType} eq 'MapTo'
&& !IsStringWithData( $Config->{$ConfigType}->{MapTo} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => "Got MapType 'MapTo', but MapTo value is not valid in $ConfigType!",
);
}
}
# check ValueMap
if ( IsHashRefWithData( $Config->{ValueMap} ) ) {
for my $KeyName ( sort keys %{ $Config->{ValueMap} } ) {
# require values to be hash ref
if ( !IsHashRefWithData( $Config->{ValueMap}->{$KeyName} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got $KeyName in ValueMap, but it is no hash ref with content!",
);
}
# possible sub-values are ValueMapExact or ValueMapRegEx and need to be hash ref if defined
SUBKEY:
for my $SubKeyName (qw(ValueMapExact ValueMapRegEx)) {
my $ValueMapType = $Config->{ValueMap}->{$KeyName}->{$SubKeyName};
next SUBKEY if !defined $ValueMapType;
if ( !IsHashRefWithData($ValueMapType) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got $SubKeyName in $KeyName in ValueMap,"
. ' but it is no hash ref with content!',
);
}
# key/value pairs of ValueMapExact and ValueMapRegEx must be strings
for my $ValueMapTypeKey ( sort keys %{$ValueMapType} ) {
if ( !IsString($ValueMapTypeKey) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got key in $SubKeyName in $KeyName in ValueMap which is not a string!",
);
}
if ( !IsString( $ValueMapType->{$ValueMapTypeKey} ) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got value for $ValueMapTypeKey in $SubKeyName in $KeyName in ValueMap"
. ' which is not a string!',
);
}
}
}
}
}
# if we arrive here, all checks were OK
return {
Success => 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
@@ -0,0 +1,264 @@
# --
# 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::Mapping::Test;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData IsStringWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Mapping::Test - GenericInterface test data mapping backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Mapping->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
for my $Needed (qw(DebuggerObject MappingConfig)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# check mapping config
if ( !IsHashRefWithData( $Param{MappingConfig} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig as hash ref with content!',
);
}
# check config - if we have a map config, it has to be a non-empty hash ref
if (
defined $Param{MappingConfig}->{Config}
&& !IsHashRefWithData( $Param{MappingConfig}->{Config} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => 'Got MappingConfig with Data, but Data is no hash ref with content!',
);
}
return $Self;
}
=head2 Map()
perform data mapping
possible config options for value mapping are
- 'ToUpper', turns all characters into upper case
- 'ToLower', turns all characters into lower case
- 'Empty', sets to empty string
if no config option is provided or one that does not match the options above, the original data will be returned
my $Result = $MappingObject->Map(
Data => { # data payload before mapping
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload of after mapping
...
},
};
=cut
sub Map {
my ( $Self, %Param ) = @_;
# check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Mapping Test backend!'
);
}
# return if data is empty
if ( !defined $Param{Data} || !%{ $Param{Data} } ) {
return {
Success => 1,
Data => {},
};
}
# no config means that we just return input data
if (
!defined $Self->{MappingConfig}->{Config}
|| !defined $Self->{MappingConfig}->{Config}->{TestOption}
)
{
return {
Success => 1,
Data => $Param{Data},
};
}
# check TestOption format
if ( !IsStringWithData( $Self->{MappingConfig}->{Config}->{TestOption} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no TestOption as string with value!',
);
}
# parse data according to configuration
my $ReturnData = {};
if ( $Self->{MappingConfig}->{Config}->{TestOption} eq 'ToUpper' ) {
$ReturnData = $Self->_ToUpper( Data => $Param{Data} );
}
elsif ( $Self->{MappingConfig}->{Config}->{TestOption} eq 'ToLower' ) {
$ReturnData = $Self->_ToLower( Data => $Param{Data} );
}
elsif ( $Self->{MappingConfig}->{Config}->{TestOption} eq 'Empty' ) {
$ReturnData = $Self->_Empty( Data => $Param{Data} );
}
else {
$ReturnData = $Param{Data};
}
# return result
return {
Success => 1,
Data => $ReturnData,
};
}
=begin Internal:
=head2 _ToUpper()
change all characters in values to upper case
my $ReturnData => $MappingObject->_ToUpper(
Data => { # data payload before mapping
'abc' => 'Def,
'ghi' => 'jkl',
},
);
$ReturnData = { # data payload after mapping
'abc' => 'DEF',
'ghi' => 'JKL',
};
=cut
sub _ToUpper {
my ( $Self, %Param ) = @_;
my $ReturnData = {};
for my $Key ( sort keys %{ $Param{Data} } ) {
$ReturnData->{$Key} = uc $Param{Data}->{$Key};
}
return $ReturnData;
}
=head2 _ToLower()
change all characters in values to lower case
my $ReturnData => $MappingObject->_ToLower(
Data => { # data payload before mapping
'abc' => 'Def,
'ghi' => 'JKL',
},
);
$ReturnData = { # data payload after mapping
'abc' => 'def',
'ghi' => 'jkl',
};
=cut
sub _ToLower {
my ( $Self, %Param ) = @_;
my $ReturnData = {};
for my $Key ( sort keys %{ $Param{Data} } ) {
$ReturnData->{$Key} = lc $Param{Data}->{$Key};
}
return $ReturnData;
}
=head2 _Empty()
set all values to empty string
my $ReturnData => $MappingObject->_Empty(
Data => { # data payload before mapping
'abc' => 'Def,
'ghi' => 'JKL',
},
);
$ReturnData = { # data payload after mapping
'abc' => '',
'ghi' => '',
};
=cut
sub _Empty {
my ( $Self, %Param ) = @_;
my $ReturnData = {};
for my $Key ( sort keys %{ $Param{Data} } ) {
$ReturnData->{$Key} = '';
}
return $ReturnData;
}
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
@@ -0,0 +1,379 @@
# --
# 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::Mapping::XSLT;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
use Storable;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Mapping::XSLT - GenericInterface C<XSLT> data mapping backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Mapping->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
# Check needed params.
for my $Needed (qw(DebuggerObject MappingConfig)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# Check mapping config.
if ( !IsHashRefWithData( $Param{MappingConfig} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig as hash ref with content!',
);
}
# Check config - if we have a map config, it has to be a non-empty hash ref.
if (
defined $Param{MappingConfig}->{Config}
&& !IsHashRefWithData( $Param{MappingConfig}->{Config} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => 'Got MappingConfig with Data, but Data is no hash ref with content!',
);
}
return $Self;
}
=head2 Map()
Provides mapping based on C<XSLT> style sheets.
Additional data is provided by the function results from various stages in requester and provider.
This data can be included in the C<XSLT> mapping as 'DataInclude' structure via configuration.
my $ReturnData = $MappingObject->Map(
Data => {
'original_key' => 'original_value',
'another_key' => 'next_value',
},
DataInclude => {
RequesterRequestInput => { ... },
RequesterRequestPrepareOutput => { ... },
RequesterRequestMapOutput => { ... },
RequesterResponseInput => { ... },
RequesterResponseMapOutput => { ... },
RequesterErrorHandlingOutput => { ... },
ProviderRequestInput => { ... },
ProviderRequestMapOutput => { ... },
ProviderResponseInput => { ... },
ProviderResponseMapOutput => { ... },
ProviderErrorHandlingOutput => { ... },
},
}
);
my $ReturnData = {
'changed_key' => 'changed_value',
'original_key' => 'another_changed_value',
'another_original_key' => 'default_value',
'default_key' => 'changed_value',
};
=cut
sub Map {
my ( $Self, %Param ) = @_;
# Check data - only accept undef or hash ref or array ref.
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' && ref $Param{Data} ne 'ARRAY' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash or array ref in Mapping XSLT backend!'
);
}
# Check included data - only accept undef or hash ref.
if ( defined $Param{DataInclude} && !IsHashRefWithData( $Param{DataInclude} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got DataInclude but it is not a hash ref in Mapping XSLT backend!'
);
}
# Return if data is empty.
if ( !defined $Param{Data} || !%{ $Param{Data} } ) {
return {
Success => 1,
Data => {},
};
}
# Prepare short config variable.
my $Config = $Self->{MappingConfig}->{Config};
# No config means we just return input data.
if ( !$Config || !$Config->{Template} ) {
return {
Success => 1,
Data => $Param{Data},
};
}
# Load required libraries (XML::LibXML and XML::LibXSLT).
LIBREQUIRED:
for my $LibRequired (qw(XML::LibXML XML::LibXSLT)) {
my $LibFound = $Kernel::OM->Get('Kernel::System::Main')->Require($LibRequired);
next LIBREQUIRED if $LibFound;
return $Self->{DebuggerObject}->Error(
Summary => "Could not find required library $LibRequired",
);
}
# Prepare style sheet.
my $LibXSLT = XML::LibXSLT->new();
# Remove template line breaks and white spaces to plain text lines on the fly, see bug# 14106.
my $Template =
$Config->{Template}
=~ s{ > [ \t\n]+ (?= [^< \t\n] ) }{>}xmsgr
=~ s{ (?<! [> \t\n] ) [ \t\n]+ < }{<}xmsgr;
my ( $StyleDoc, $StyleSheet );
eval {
$StyleDoc = XML::LibXML->load_xml(
string => $Template,
no_cdata => 1,
);
};
if ( !$StyleDoc ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not load configured XSLT template',
Data => $Template,
);
}
eval {
$StyleSheet = $LibXSLT->parse_stylesheet($StyleDoc);
};
if ( !$StyleSheet ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not parse configured XSLT template',
Data => $@,
);
}
# Append the configured include data to the normal data structure.
if (
IsHashRefWithData( $Param{DataInclude} )
&& IsArrayRefWithData( $Config->{DataInclude} )
)
{
my $MergedData = Storable::dclone( $Param{Data} );
DATAINCLUDEMODULE:
for my $DataIncludeModule ( @{ $Config->{DataInclude} } ) {
next DATAINCLUDEMODULE if !$Param{DataInclude}->{$DataIncludeModule};
# Clone the data include hash to prevent circular data structure references
$MergedData->{DataInclude}->{$DataIncludeModule}
= Storable::dclone( $Param{DataInclude}->{$DataIncludeModule} );
}
$Self->{DebuggerObject}->Debug(
Summary => 'Data merged with DataInclude before mapping',
Data => $MergedData,
);
$Param{Data} = $MergedData;
}
# Note: XML::Simple was chosen over alternatives like XML::LibXML and XML::Dumper
# due to its simplicity and because we just require a straightforward conversion.
# Other modules provide more possibilities but don't allow directly exporting a complete
# and clean structure.
# Reference:
# http://www.perlmonks.org/?node_id=490846
# http://stackoverflow.com/questions/12182129/convert-string-to-hash-using-libxml-in-perl
# XSTL regex recursion.
if ( IsArrayRefWithData( $Config->{PreRegExFilter} ) ) {
$Self->_RegExRecursion(
Data => $Param{Data},
Config => $Config->{PreRegExFilter},
);
$Self->{DebuggerObject}->Debug(
Summary => 'Data before mapping after Pre RegExFilter',
Data => $Param{Data},
);
}
# Convert data to xml structure.
$Kernel::OM->Get('Kernel::System::Main')->Require('XML::Simple');
my $XMLSimple = XML::Simple->new();
my $XMLPre;
eval {
$XMLPre = $XMLSimple->XMLout(
$Param{Data},
AttrIndent => 1,
ContentKey => '-content',
NoAttr => 1,
KeyAttr => [],
RootName => 'RootElement',
);
};
if ( !$XMLPre ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not convert data from Perl to XML before mapping',
Data => $@,
);
}
# Transform xml data.
my ( $XMLSource, $Result );
eval {
$XMLSource = XML::LibXML->load_xml(
string => $XMLPre,
no_cdata => 1,
);
};
if ( !$XMLSource ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not load data after conversion from Perl to XML',
Data => $XMLPre,
);
}
eval {
$Result = $StyleSheet->transform($XMLSource);
};
if ( !$Result ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not map data',
Data => $@,
);
}
my $XMLPost = $StyleSheet->output_as_bytes($Result);
if ( !$XMLPost ) {
return $Self->{DebuggerObject}->Error(
Summary => "Could not write mapped data",
);
}
# Convert data back to perl structure.
my $ReturnData;
eval {
$ReturnData = $XMLSimple->XMLin(
$XMLPost,
ForceArray => 0,
ContentKey => '-content',
NoAttr => 1,
KeyAttr => [],
);
};
if ( !$ReturnData ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Could not convert data from XML to Perl after mapping',
Data => [ $@, $XMLPost ],
);
}
# XST regex recursion.
if ( IsArrayRefWithData( $Config->{PostRegExFilter} ) ) {
$Self->{DebuggerObject}->Debug(
Summary => 'Data after mapping before Post RegExFilter',
Data => $ReturnData,
);
$Self->_RegExRecursion(
Data => $ReturnData,
Config => $Config->{PostRegExFilter},
);
}
return {
Success => 1,
Data => $ReturnData,
};
}
sub _RegExRecursion {
my ( $Self, %Param ) = @_;
# Data must be hash ref.
return 1 if !IsHashRefWithData( $Param{Data} );
KEY:
for my $Key ( sort keys %{ $Param{Data} } ) {
# Recurse for array data in value.
if ( IsArrayRefWithData( $Param{Data}->{$Key} ) ) {
ARRAYENTRY:
for my $ArrayEntry ( @{ $Param{Data}->{$Key} } ) {
$Self->_RegExRecursion(
Data => $ArrayEntry,
Config => $Param{Config},
);
}
}
# Recurse directly otherwise (assume hash reference).
else {
$Self->_RegExRecursion(
Data => $Param{Data}->{$Key},
Config => $Param{Config},
);
}
# Apply configured RegEx to key.
REGEX:
for my $RegEx ( @{ $Param{Config} } ) {
next REGEX if $Key !~ m{$RegEx->{Search}};
# Double-eval the replacement string to allow embedded capturing group replacements,
# e.g. Search = '(foo|bar)bar', Replace = '$1foo'
# turns 'foobar' into 'foofoo' and 'barbar' into 'barfoo'.
my $NewKey = $Key =~ s{$RegEx->{Search}}{ '"' . $RegEx->{Replace} . '"' }eer;
# Skip if new key is equivalent to old key.
next KEY if $NewKey eq $Key;
# Replace matched key with new one.
$Param{Data}->{$NewKey} = delete $Param{Data}->{$Key};
}
}
return 1;
}
1;
=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
@@ -0,0 +1,176 @@
# --
# 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::Operation;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsStringWithData);
# prevent 'Used once' warning for Kernel::OM
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation - GenericInterface Operation interface
=head1 DESCRIPTION
Operations are called by web service requests from remote
systems.
=head1 PUBLIC INTERFACE
=head2 new()
create an object.
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Operation;
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => {
DebugThreshold => 'debug',
TestMode => 0, # optional, in testing mode the data will not be written to the DB
# ...
},
WebserviceID => 12,
CommunicationType => Provider, # Requester or Provider
RemoteIP => 192.168.1.1, # optional
);
my $OperationObject = Kernel::GenericInterface::Operation->new(
DebuggerObject => $DebuggerObject,
Operation => 'TicketCreate', # the name of the operation in the web service
OperationType => 'Ticket::TicketCreate', # the local operation backend to use
WebserviceID => $WebserviceID, # ID of the currently used web service
);
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# Check needed objects.
for my $Needed (qw(DebuggerObject Operation OperationType WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# Check operation.
if ( !IsStringWithData( $Param{OperationType} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no Operation with content!',
);
}
# Load backend module.
my $GenericModule = 'Kernel::GenericInterface::Operation::' . $Param{OperationType};
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Can't load operation backend module $GenericModule!"
);
}
$Self->{BackendObject} = $GenericModule->new(
%{$Self},
);
# Pass back error message from backend if backend module could not be executed.
return $Self->{BackendObject} if ref $Self->{BackendObject} ne $GenericModule;
return $Self;
}
=head2 Run()
perform the selected Operation.
my $Result = $OperationObject->Run(
Data => { # data payload before Operation
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # result data payload after Operation
...
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# Start map on backend,
return $Self->{BackendObject}->Run(%Param);
}
=head2 HandleError()
handle error data of the configured remote web service.
my $Result = $OperationObject->HandleError(
Data => { # data payload
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # data payload after Invoker
...
},
};
=cut
sub HandleError {
my ( $Self, %Param ) = @_;
# Check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Operation handler (HandleResponse)!'
);
}
return $Self->{BackendObject}->HandleError(%Param);
}
1;
=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
@@ -0,0 +1,230 @@
# --
# 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::Operation::Common;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Common - Base class for all Operations
=head1 PUBLIC INTERFACE
=head2 Auth()
performs user or customer user authorization
my ( $UserID, $UserType ) = $CommonObject->Auth(
Data => {
SessionID => 'AValidSessionIDValue' # the ID of the user session
UserLogin => 'Agent', # if no SessionID is given UserLogin or
# CustomerUserLogin is required
CustomerUserLogin => 'Customer',
Password => 'some password', # user password
},
);
returns
(
1, # the UserID from login or session data
'Agent', # || 'Customer', the UserType.
);
=cut
sub Auth {
my ( $Self, %Param ) = @_;
my $SessionID = $Param{Data}->{SessionID} || '';
# check if a valid SessionID is present
if ($SessionID) {
my $ValidSessionID;
# get session object
my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession');
if ($SessionID) {
$ValidSessionID = $SessionObject->CheckSessionID( SessionID => $SessionID );
}
return 0 if !$ValidSessionID;
# get session data
my %UserData = $SessionObject->GetSessionIDData(
SessionID => $SessionID,
);
# get UserID from SessionIDData
if ( $UserData{UserID} && $UserData{UserType} ne 'Customer' ) {
return ( $UserData{UserID}, $UserData{UserType} );
}
elsif ( $UserData{UserLogin} && $UserData{UserType} eq 'Customer' ) {
# if UserCustomerLogin
return ( $UserData{UserLogin}, $UserData{UserType} );
}
return 0;
}
if ( defined $Param{Data}->{UserLogin} && $Param{Data}->{UserLogin} ) {
my $UserID = $Self->_AuthUser(%Param);
# if UserLogin
if ($UserID) {
return ( $UserID, 'User' );
}
}
elsif ( defined $Param{Data}->{CustomerUserLogin} && $Param{Data}->{CustomerUserLogin} ) {
my $CustomerUserID = $Self->_AuthCustomerUser(%Param);
# if UserCustomerLogin
if ($CustomerUserID) {
return ( $CustomerUserID, 'Customer' );
}
}
return 0;
}
=head2 ReturnError()
helper function to return an error message.
my $Return = $CommonObject->ReturnError(
ErrorCode => Ticket.AccessDenied,
ErrorMessage => 'You don't have rights to access this ticket',
);
=cut
sub ReturnError {
my ( $Self, %Param ) = @_;
$Self->{DebuggerObject}->Error(
Summary => $Param{ErrorCode},
Data => $Param{ErrorMessage},
);
# return structure
return {
Success => 1,
ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
Data => {
Error => {
ErrorCode => $Param{ErrorCode},
ErrorMessage => $Param{ErrorMessage},
},
},
};
}
=begin Internal:
=head2 _AuthUser()
performs user authentication
my $UserID = $CommonObject->_AuthUser(
UserLogin => 'Agent',
Password => 'some password', # plain text password
);
returns
$UserID = 1; # the UserID from login or session data
=cut
sub _AuthUser {
my ( $Self, %Param ) = @_;
my $ReturnData = 0;
# get params
my $PostUser = $Param{Data}->{UserLogin} || '';
my $PostPw = $Param{Data}->{Password} || '';
# check submitted data
my $User = $Kernel::OM->Get('Kernel::System::Auth')->Auth(
User => $PostUser,
Pw => $PostPw,
);
# login is valid
if ($User) {
# get UserID
my $UserID = $Kernel::OM->Get('Kernel::System::User')->UserLookup(
UserLogin => $User,
);
$ReturnData = $UserID;
}
return $ReturnData;
}
=head2 _AuthCustomerUser()
performs customer user authentication
my $UserID = $CommonObject->_AuthCustomerUser(
UserLogin => 'Agent',
Password => 'some password', # plain text password
);
returns
$UserID = 1; # the UserID from login or session data
=cut
sub _AuthCustomerUser {
my ( $Self, %Param ) = @_;
my $ReturnData = $Param{Data}->{CustomerUserLogin} || 0;
# get params
my $PostUser = $Param{Data}->{CustomerUserLogin} || '';
my $PostPw = $Param{Data}->{Password} || '';
# check submitted data
my $User = $Kernel::OM->Get('Kernel::System::CustomerAuth')->Auth(
User => $PostUser,
Pw => $PostPw,
);
# login is invalid
if ( !$User ) {
$ReturnData = 0;
}
return $ReturnData;
}
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,707 @@
# --
# 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::Operation::ConfigItem::ConfigItemCreate;
use strict;
use warnings;
## nofilter(TidyAll::Plugin::OTRS::Migrations::OTRS6::SysConfig)
use Kernel::System::VariableCheck qw(:all);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::ConfigItem::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::ConfigItem::ConfigItemCreate - GenericInterface ConfigItem ConfigItemCreate Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
$Self->{OperationName} = 'ConfigItemCreate';
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::ConfigItemCreate');
$Self->{Config}->{DefaultValue} = 'Not Defined';
my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog');
# get a list of all config item classes
$Self->{ClassList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
if ( !IsHashRefWithData( $Self->{ClassList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get class listing of ITSM::ConfigItem::Class',
);
}
# get a list of all incistates
$Self->{InciStateList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::Core::IncidentState',
);
if ( !IsHashRefWithData( $Self->{InciStateList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get incident state listing of'
. ' ITSM::Core::IncidentState',
);
}
# get a list of all deplstates
$Self->{DeplStateList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::DeploymentState',
);
if ( !IsHashRefWithData( $Self->{DeplStateList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get incident state listing of'
. ' ITSM::ConfigItem::DeploymentState',
);
}
# also provide the classlist in reversed form for easier reverse lookups
my %ReverseClassList = reverse %{ $Self->{ClassList} };
$Self->{ReverseClassList} = \%ReverseClassList;
# also provide the incistatelist in reversed form for easier reverse lookups
my %ReverseInciStateList = reverse %{ $Self->{InciStateList} };
$Self->{ReverseInciStateList} = \%ReverseInciStateList;
# also provide the deplstatelist in reversed form for easier reverse lookups
my %ReverseDeplStateList = reverse %{ $Self->{DeplStateList} };
$Self->{ReverseDeplStateList} = \%ReverseDeplStateList;
return $Self;
}
=head2 Run()
perform ConfigItemCreate Operation. This will return the created config item number.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or SessionID is
# required
SessionID => 123,
Password => 'some password', # if UserLogin is sent then
# Password is required
ConfigItem => {
Number => '111', # optional
Class => 'Config Item Class',
Name => 'The Name',
DeplState => 'deployment state',
InciState => 'incident state',
CIXMLData => $ArrayHashRef, # it depends on the Configuration Item class and definition
Attachment => [
{
Content => 'content' # base64 encoded
ContentType => 'some content type'
Filename => 'some fine name'
},
# ...
],
#or
#Attachment => {
# Content => 'content'
# ContentType => 'some content type'
# Filename => 'some fine name'
#},
},
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # result data payload after Operation
ConfigItemID => 123, # Configuration Item ID number in OTRS::ITSM (Service desk system)
Number => 2324454323322 # Configuration Item Number in OTRS::ITSM (Service desk system)
Error => { # should not return errors
ErrorCode => 'ConfigItemCreate.ErrorCode'
ErrorMessage => 'Error Description'
},
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
$Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
# check needed stuff
if (
!$Param{Data}->{UserLogin}
&& !$Param{Data}->{SessionID}
)
{
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: UserLogin or SessionID is required!",
);
}
if ( $Param{Data}->{UserLogin} ) {
if ( !$Param{Data}->{Password} )
{
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: Password or SessionID is required!",
);
}
}
# authenticate user
my ( $UserID, $UserType ) = $Self->Auth(%Param);
if ( !$UserID ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AuthFail",
ErrorMessage => "$Self->{OperationName}: User could not be authenticated!",
);
}
# check needed hashes
for my $Needed (qw(ConfigItem)) {
if ( !IsHashRefWithData( $Param{Data}->{$Needed} ) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: $Needed parameter is missing or not valid!",
);
}
}
# isolate config item parameter
my $ConfigItem = $Param{Data}->{ConfigItem};
# remove leading and trailing spaces
for my $Attribute ( sort keys %{$ConfigItem} ) {
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
#remove leading spaces
$ConfigItem->{$Attribute} =~ s{\A\s+}{};
#remove trailing spaces
$ConfigItem->{$Attribute} =~ s{\s+\z}{};
}
}
# CIXMLData is not mandatory, but it must be HashRef if exists.
if ( defined $ConfigItem->{CIXMLData} ) {
if ( !IsHashRefWithData( $ConfigItem->{CIXMLData} ) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItem->CIXMLData is missing or invalid!",
);
}
# Remove leading and trailing spaces for CIXMLData (must be HashRef).
$Self->_CleanXMLData( XMLData => $ConfigItem->{CIXMLData} );
}
# check ConfigItem attribute values
my $ConfigItemCheck = $Self->_CheckConfigItem( ConfigItem => $ConfigItem );
if ( !$ConfigItemCheck->{Success} ) {
return $Self->ReturnError( %{$ConfigItemCheck} );
}
# check create permissions
my $Permission = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->Permission(
Scope => 'Class',
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
UserID => $UserID,
Type => $Self->{Config}->{Permission},
);
if ( !$Permission ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AccessDenied",
ErrorMessage => "$Self->{OperationName}: Can not create configuration items!",
);
}
my $Attachment;
my @AttachmentList;
if ( defined $Param{Data}->{ConfigItem}->{Attachment} ) {
# isolate Attachment parameter
$Attachment = delete $Param{Data}->{ConfigItem}->{Attachment};
# homologate imput to array
if ( IsHashRefWithData($Attachment) ) {
push @AttachmentList, $Attachment;
}
elsif ( IsArrayRefWithData($Attachment) ) {
@AttachmentList = @{$Attachment};
}
else {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Attachment parameter is invalid!",
);
}
# check Attachment internal structure
for my $AttachmentItem (@AttachmentList) {
if ( !IsHashRefWithData($AttachmentItem) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Attachment parameter is invalid!",
);
}
# remove leading and trailing spaces
for my $Attribute ( sort keys %{$AttachmentItem} ) {
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
#remove leading spaces
$AttachmentItem->{$Attribute} =~ s{\A\s+}{};
#remove trailing spaces
$AttachmentItem->{$Attribute} =~ s{\s+\z}{};
}
}
# check Attachment attribute values
my $AttachmentCheck = $Self->_CheckAttachment( Attachment => $AttachmentItem );
if ( !$AttachmentCheck->{Success} ) {
return $Self->ReturnError( %{$AttachmentCheck} );
}
}
}
return $Self->_ConfigItemCreate(
ConfigItem => $ConfigItem,
AttachmentList => \@AttachmentList,
UserID => $UserID,
);
}
=head1 INTERNAL INTERFACE
=head2 _CleanXMLData()
removed trailing and leading white spaces in the XMLData.
my $XMLDataClean = $OperationObject->_CleanXMLData(
Definition => $DefinitionArrayRef, # Config Item Definition ot just part of it
XMLData => $XMLDataHashRef,
);
returns:
$XMLDataClean = {
Success => 1, # if everything is OK
}
$XMLDataClean = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CleanXMLData {
my ( $Self, %Param ) = @_;
my $XMLData = $Param{XMLData};
KEY:
for my $Key ( sort keys %{$XMLData} ) {
if ( ref $XMLData->{$Key} eq 'ARRAY' ) {
ELEMENT:
for my $Element ( @{ $XMLData->{$Key} } ) {
if ( ref $Element eq 'HASH' ) {
# start recursion
$Self->_CleanXMLData( XMLData => $Element );
next ELEMENT;
}
elsif ( ref $Element eq '' ) {
#remove leading spaces
$Element =~ s{\A\s+}{};
#remove trailing spaces
$Element =~ s{\s+\z}{};
}
}
}
elsif ( ref $XMLData->{$Key} eq 'HASH' ) {
# start recursion
$Self->_CleanXMLData( XMLData => $XMLData->{$Key} );
next KEY;
}
elsif ( ref $XMLData->{$Key} eq '' ) {
#remove leading spaces
$XMLData->{$Key} =~ s{\A\s+}{};
#remove trailing spaces
$XMLData->{$Key} =~ s{\s+\z}{};
}
}
return 1;
}
=head2 _CheckConfigItem()
checks if the given config item parameters are valid.
my $ConfigItemCheck = $OperationObject->_CheckConfigItem(
ConfigItem => $ConfigItem, # all config item parameters
);
returns:
$ConfigItemCheck = {
Success => 1, # if everything is OK
}
$ConfigItemCheck = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CheckConfigItem {
my ( $Self, %Param ) = @_;
my $ConfigItem = $Param{ConfigItem};
# check config item internally
for my $Needed (qw(Class Name DeplState InciState)) {
if ( !$ConfigItem->{$Needed} ) {
return {
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItem->$Needed parameter is missing!",
};
}
}
# check ConfigItem->Class
if ( !$Self->ValidateClass( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Class parameter is invalid!",
};
}
# check ConfigItem->DeplState
if ( !$Self->ValidateDeplState( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->DeplState parameter is invalid!",
};
}
# check ConfigItem->DeplState
if ( !$Self->ValidateInciState( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->InciState parameter is invalid!",
};
}
# get last config item defintion
my $DefinitionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
);
my $XMLDataCheckResult = $Self->CheckXMLData(
Definition => $DefinitionData->{DefinitionRef},
XMLData => $ConfigItem->{CIXMLData},
);
if ( !$XMLDataCheckResult->{Success} ) {
return $XMLDataCheckResult;
}
# if everything is OK then return Success
return {
Success => 1,
};
}
=head2 _CheckAttachment()
checks if the given attachment parameter is valid.
my $AttachmentCheck = $OperationObject->_CheckAttachment(
Attachment => $Attachment, # all attachment parameters
);
returns:
$AttachmentCheck = {
Success => 1, # if everething is OK
}
$AttachmentCheck = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CheckAttachment {
my ( $Self, %Param ) = @_;
my $Attachment = $Param{Attachment};
# check attachment item internally
for my $Needed (qw(Content ContentType Filename)) {
if ( !$Attachment->{$Needed} ) {
return {
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: Attachment->$Needed parameter is missing!",
};
}
}
# check Article->ContentType
if ( $Attachment->{ContentType} ) {
$Attachment->{ContentType} = lc $Attachment->{ContentType};
# check Charset part
my $Charset = '';
if ( $Attachment->{ContentType} =~ /charset=/i ) {
$Charset = $Attachment->{ContentType};
$Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
$Charset =~ s/"|'//g;
$Charset =~ s/(.+?);.*/$1/g;
}
if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) )
{
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: Attachment->ContentType is invalid!",
};
}
# check MimeType part
my $MimeType = '';
if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) {
$MimeType = $1;
$MimeType =~ s/"|'//g;
}
if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: Attachment->ContentType is invalid!",
};
}
}
# if everything is OK then return Success
return {
Success => 1,
};
}
=head2 _ConfigItemCreate()
creates a configuration item with attachments if specified.
my $Response = $OperationObject->_ConfigItemCreate(
ConfigItem => $ConfigItem, # all configuration item parameters
AttachmentList => $Attachment, # a list of all attachments
UserID => 123,
);
returns:
$Response = {
Success => 1, # if everething is OK
Data => {
ConfigItemID => 123,
ConfigItemNumber => 'CN123',
}
}
$Response = {
Success => 0, # if unexpected error
ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
}
=cut
sub _ConfigItemCreate {
my ( $Self, %Param ) = @_;
my $ConfigItem = $Param{ConfigItem};
my $AttachmentList = $Param{AttachmentList};
my $DeplStateID = $Self->{ReverseDeplStateList}->{ $ConfigItem->{DeplState} };
my $InciStateID = $Self->{ReverseInciStateList}->{ $ConfigItem->{InciState} };
my $RawXMLData = $ConfigItem->{CIXMLData};
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
# get last config item defintion
my $DefinitionData = $ConfigItemObject->DefinitionGet(
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
);
# replace date, date time, customer, company and general catalog values
my $ReplacedXMLData = $Self->ReplaceXMLData(
XMLData => $RawXMLData,
Definition => $DefinitionData->{DefinitionRef},
);
# create an XMLData structure suitable for VersionAdd
my $XMLData = $Self->FormatXMLData(
XMLData => $ReplacedXMLData,
);
# create new config item
my $ConfigItemID = $ConfigItemObject->ConfigItemAdd(
Number => $ConfigItem->{Number},
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
UserID => $Param{UserID},
);
my $VersionID = $ConfigItemObject->VersionAdd(
ConfigItemID => $ConfigItemID,
Name => $ConfigItem->{Name},
DefinitionID => $DefinitionData->{DefinitionID},
DeplStateID => $DeplStateID,
InciStateID => $InciStateID,
XMLData => $XMLData,
UserID => $Param{UserID},
);
if ( !$ConfigItemID && !$VersionID ) {
return {
Success => 0,
ErrorMessage => 'Configuration Item could not be created, please contact the system'
. 'administrator',
};
}
# set attachments
if ( IsArrayRefWithData($AttachmentList) ) {
for my $Attachment ( @{$AttachmentList} ) {
my $Result = $Self->CreateAttachment(
Attachment => $Attachment,
ConfigItemID => $ConfigItemID,
UserID => $Param{UserID},
);
if ( !$Result->{Success} ) {
my $ErrorMessage =
$Result->{ErrorMessage} || "Attachment could not be created, please contact"
. " the system administrator";
return {
Success => 0,
ErrorMessage => $ErrorMessage,
};
}
}
}
# get ConfigItem data
my $ConfigItemData = $ConfigItemObject->ConfigItemGet(
ConfigItemID => $ConfigItemID,
);
if ( !IsHashRefWithData($ConfigItemData) ) {
return {
Success => 0,
ErrorMessage => 'Could not get new configuration item information, please contact the'
. ' system administrator',
};
}
return {
Success => 1,
Data => {
ConfigItemID => $ConfigItemID,
Number => $ConfigItemData->{Number},
},
};
}
1;
=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
@@ -0,0 +1,214 @@
# --
# 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::Operation::ConfigItem::ConfigItemDelete;
use strict;
use warnings;
use MIME::Base64;
use Kernel::System::VariableCheck qw(:all);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::ConfigItem::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::ConfigItem::ConfigItemDelete - GenericInterface Configuration Item Delete Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
$Self->{OperationName} = 'ConfigItemDelete';
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::ConfigItemDelete');
return $Self;
}
=head2 Run()
perform ConfigItemDelete Operation. This function is able to return
one or more ConfigItem entries in one call.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or CustomerUserLogin or SessionID is
# required
CustomerUserLogin => 'some customer login',
SessionID => 123,
Password => 'some password', # if UserLogin or customerUserLogin is sent then
# Password is required
ConfigItemID => '32,33', # required, could be coma separated IDs or an Array
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # result data payload after Operation
ConfigItemID => [123, 456], # Configuration Item IDs number in OTRS::ITSM (Service desk system)
Error => { # should not return errors
ErrorCode => 'ConfigItemDelete.ErrorCode'
ErrorMessage => 'Error Description'
},
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
$Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
my ( $UserID, $UserType ) = $Self->Auth(
%Param
);
if ( !$UserID ) {
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.AuthFail',
ErrorMessage => "$Self->{OperationName}: Authorization failing!",
);
}
# check needed stuff
for my $Needed (qw(ConfigItemID)) {
if ( !$Param{Data}->{$Needed} ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: $Needed parameter is missing!",
);
}
}
my $ErrorMessage = '';
# all needed variables
my @ConfigItemIDs;
if ( IsStringWithData( $Param{Data}->{ConfigItemID} ) ) {
@ConfigItemIDs = split /\s*,\s*/, $Param{Data}->{ConfigItemID};
}
elsif ( IsArrayRefWithData( $Param{Data}->{ConfigItemID} ) ) {
@ConfigItemIDs = @{ $Param{Data}->{ConfigItemID} };
}
else {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.WrongStructure",
ErrorMessage => "$Self->{OperationName}: Structure for ConfigItemID is not correct!",
);
}
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
my @DeletedConfigItemIDs;
# start ConfigItem loop
CONFIGITEM:
for my $ConfigItemID (@ConfigItemIDs) {
# check create permissions
my $Permission = $ConfigItemObject->Permission(
Scope => 'Item',
ItemID => $ConfigItemID,
UserID => $UserID,
Type => $Self->{Config}->{Permission},
);
if ( !$Permission ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AccessDenied",
ErrorMessage => "$Self->{OperationName}: Can not delete configuration item!",
);
}
# delete the configitem
my $DeleteSuccess = $ConfigItemObject->ConfigItemDelete(
ConfigItemID => $ConfigItemID,
UserID => $UserID,
);
if ( !$DeleteSuccess ) {
$ErrorMessage = 'Could not delete ConfigItem ID ' . $ConfigItemID
. ' in Kernel::GenericInterface::Operation::ConfigItem::ConfigItemDelete::Run()';
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.DeleteError',
ErrorMessage => "$Self->{OperationName}: $ErrorMessage",
);
}
push @DeletedConfigItemIDs, $ConfigItemID;
} # finish ConfigItem loop
if ( !IsArrayRefWithData( \@DeletedConfigItemIDs ) ) {
return {
Success => 0,
ErrorMessage => 'Could not delete ConfigItems!',
};
}
return {
Success => 1,
Data => {
ConfigItemID => \@DeletedConfigItemIDs,
},
};
}
1;
=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
@@ -0,0 +1,324 @@
# --
# 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::Operation::ConfigItem::ConfigItemGet;
use strict;
use warnings;
## nofilter(TidyAll::Plugin::OTRS::Migrations::OTRS6::SysConfig)
use MIME::Base64;
use Kernel::System::VariableCheck qw(:all);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::ConfigItem::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::ConfigItem::ConfigItemGet - GenericInterface Configuration Item Get Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
$Self->{OperationName} = 'ConfigItemGet';
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::ConfigItemGet');
return $Self;
}
=head2 Run()
perform ConfigItemGet Operation. This function is able to return
one or more ConfigItem entries in one call.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or SessionID is
SessionID => 123, # required
Password => 'some password', # if UserLogin is sent then Password is required
ConfigItemID => '32,33', # required, could be coma separated IDs or an Array
Attachments => 1, # Optional, 1 as default. If it's set with the value 1,
# attachments for articles will be included on ConfigItem data
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
ConfigItem => [
{
Number => '20101027000001',
ConfigItemID => 123,
Name => 'some name',
Class => 'some class',
VersionID => 123,
LastVersionID => 123,
DefinitionID => 123,
InciState => 'some incident state',
InciStateType => 'some incident state type',
DeplState => 'some deployment state',
DeplStateType => 'some deployment state type',
CurInciState => 'some incident state',
CurInciStateType => 'some incident state type',
CurDeplState => 'some deployment state',
CurDeplStateType => 'some deployment state type',
CreateTime => '2010-10-27 20:15:00'
CreateBy => 123,
CIXMLData => $XMLDataHashRef,
Attachment => [
{
Content => "xxxx", # actual attachment contents, base64 enconded
ContentType => "application/pdf",
Filename => "StdAttachment-Test1.pdf",
Filesize => "4.6 KBytes",
Preferences => $PreferencesHashRef,
},
{
# . . .
},
],
},
{
# . . .
},
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
$Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
my ( $UserID, $UserType ) = $Self->Auth(
%Param
);
if ( !$UserID ) {
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.AuthFail',
ErrorMessage => "$Self->{OperationName}: Authorization failing!",
);
}
# check needed stuff
for my $Needed (qw(ConfigItemID)) {
if ( !$Param{Data}->{$Needed} ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: $Needed parameter is missing!",
);
}
}
my $ErrorMessage = '';
# all needed variables
my @ConfigItemIDs;
if ( IsStringWithData( $Param{Data}->{ConfigItemID} ) ) {
@ConfigItemIDs = split( /,/, $Param{Data}->{ConfigItemID} );
}
elsif ( IsArrayRefWithData( $Param{Data}->{ConfigItemID} ) ) {
@ConfigItemIDs = @{ $Param{Data}->{ConfigItemID} };
}
else {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.WrongStructure",
ErrorMessage => "$Self->{OperationName}: Structure for ConfigItemID is not correct!",
);
}
my $Attachments = $Param{Data}->{Attachments} || 0;
my $ReturnData = {
Success => 1,
};
my @Item;
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
# start ConfigItem loop
CONFIGITEM:
for my $ConfigItemID (@ConfigItemIDs) {
# check create permissions
my $Permission = $ConfigItemObject->Permission(
Scope => 'Item',
ItemID => $ConfigItemID,
UserID => $UserID,
Type => $Self->{Config}->{Permission},
);
if ( !$Permission ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AccessDenied",
ErrorMessage => "$Self->{OperationName}: Can not get configuration item!",
);
}
# get the ConfigItem entry
my $ConfigItem = $ConfigItemObject->ConfigItemGet(
ConfigItemID => $ConfigItemID,
UserID => $UserID,
);
# get latest version
my $Version = $ConfigItemObject->VersionGet(
ConfigItemID => $ConfigItemID,
UserID => $UserID,
);
if ( !IsHashRefWithData($Version) ) {
$ErrorMessage = 'Could not get ConfigItem data'
. ' in Kernel::GenericInterface::Operation::ConfigItem::ConfigItemGet::Run()';
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.InvalidParameter',
ErrorMessage => "$Self->{OperationName}: $ErrorMessage",
);
}
# remove unneeded items
delete $Version->{ClassID};
delete $Version->{CurDeplStateID};
delete $Version->{CurInciStateID};
delete $Version->{DeplStateID};
delete $Version->{InciStateID};
delete $Version->{XMLDefinitionID};
my $Definition = delete $Version->{XMLDefinition};
my $FormatedXMLData = $Self->InvertFormatXMLData(
XMLData => $Version->{XMLData}->[1]->{Version},
);
my $ReplacedXMLData = $Self->InvertReplaceXMLData(
XMLData => $FormatedXMLData,
Definition => $Definition,
);
$Version->{XMLData} = $ReplacedXMLData;
# rename XMLData since SOAP transport complains about XML prefix on names
$Version->{CIXMLData} = delete $Version->{XMLData};
# set ConfigItem entry data
my $ConfigItemBundle = $Version;
if ($Attachments) {
my @Attachments = $ConfigItemObject->ConfigItemAttachmentList(
ConfigItemID => $ConfigItemID,
);
my @AttachmentDetails;
ATTACHMENT:
for my $Filename (@Attachments) {
next ATTACHMENT if !$Filename;
my $Attachment = $ConfigItemObject->ConfigItemAttachmentGet(
ConfigItemID => $ConfigItemID,
Filename => $Filename,
);
# next if not attachment
next ATTACHMENT if !IsHashRefWithData($Attachment);
# convert content to base64
$Attachment->{Content} = encode_base64( $Attachment->{Content} );
push @AttachmentDetails, $Attachment;
}
# set ConfigItem entry data
$ConfigItemBundle->{Attachment} = '';
if ( IsArrayRefWithData( \@AttachmentDetails ) ) {
$ConfigItemBundle->{Attachment} = \@AttachmentDetails;
}
}
# add
push @Item, $ConfigItemBundle;
} # finish ConfigItem loop
if ( !scalar @Item ) {
$ErrorMessage = 'Could not get ConfigItem data'
. ' in Kernel::GenericInterface::Operation::ConfigItem::ConfigItemGet::Run()';
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.NoConfigItemData',
ErrorMessage => "$Self->{OperationName}: $ErrorMessage",
);
}
# set ConfigItem data into return structure
$ReturnData->{Data}->{ConfigItem} = '';
if ( IsArrayRefWithData( \@Item ) ) {
$ReturnData->{Data}->{ConfigItem} = \@Item;
}
# return result
return $ReturnData;
}
1;
=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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,848 @@
# --
# 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::Operation::ConfigItem::ConfigItemUpdate;
use strict;
use warnings;
## nofilter(TidyAll::Plugin::OTRS::Migrations::OTRS6::SysConfig)
use Kernel::System::VariableCheck qw(:all);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::ConfigItem::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::ConfigItem::ConfigItemUpdate - GenericInterface ConfigItem ConfigItemUpdate Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
# define operation name
$Self->{OperationName} = 'ConfigItemUpdate';
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::ConfigItemUpdate');
$Self->{Config}->{DefaultValue} = 'Not Defined';
my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog');
# get a list of all config item classes
$Self->{ClassList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
if ( !IsHashRefWithData( $Self->{ClassList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get class listing of ITSM::ConfigItem::Class',
);
}
# get a list of all incistates
$Self->{InciStateList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::Core::IncidentState',
);
if ( !IsHashRefWithData( $Self->{InciStateList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get incident state listing of'
. ' ITSM::Core::IncidentState',
);
}
# get a list of all deplstates
$Self->{DeplStateList} = $GeneralCatalogObject->ItemList(
Class => 'ITSM::ConfigItem::DeploymentState',
);
if ( !IsHashRefWithData( $Self->{DeplStateList} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Error when trying to get incident state listing of'
. ' ITSM::ConfigItem::DeploymentState',
);
}
# also provide the classlist in reversed form for easier reverse lookups
my %ReverseClassList = reverse %{ $Self->{ClassList} };
$Self->{ReverseClassList} = \%ReverseClassList;
# also provide the incistatelist in reversed form for easier reverse lookups
my %ReverseInciStateList = reverse %{ $Self->{InciStateList} };
$Self->{ReverseInciStateList} = \%ReverseInciStateList;
# also provide the deplstatelist in reversed form for easier reverse lookups
my %ReverseDeplStateList = reverse %{ $Self->{DeplStateList} };
$Self->{ReverseDeplStateList} = \%ReverseDeplStateList;
return $Self;
}
=head2 Run()
perform ConfigItemUpdate Operation. This will return the updated config item number.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or SessionID is
SessionID => 123, # required
Password => 'some password', # if UserLogin is sent then Password is required
ReplaceExistingData => 0, # optional, 0 or 1, default 0
# this will replace the existing XML data and attachments
ConfigItemID => 123,
ConfigItem => {
Class => 'Config Item Class',
Name => 'The Name',
DeplState => 'deployment state',
InciState => 'incident state',
CIXMLData => $ArrayHashRef, # it depends on the Configuration Item class and definition
Attachment => [
{
Content => 'content' # base64 encoded
ContentType => 'some content type'
Filename => 'some fine name'
},
# ...
],
# or
#Attachment => {
# Content => 'content'
# ContentType => 'some content type'
# Filename => 'some fine name'
#},
},
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # result data payload after Operation
ConfigItemID => 123, # Configuration Item ID number in OTRS::ITSM (Service desk system)
Number => 2324454323322 # Configuration Item Number in OTRS::ITSM (Service desk system)
Error => { # should not return errors
ErrorCode => 'ConfigItemUpdate.ErrorCode'
ErrorMessage => 'Error Description'
},
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
$Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
# check needed stuff
if (
!$Param{Data}->{UserLogin}
&& !$Param{Data}->{SessionID}
)
{
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: UserLogin or SessionID is required!",
);
}
if ( $Param{Data}->{UserLogin} ) {
if ( !$Param{Data}->{Password} )
{
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: Password or SessionID is required!",
);
}
}
# authenticate user
my ( $UserID, $UserType ) = $Self->Auth(%Param);
if ( !$UserID ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AuthFail",
ErrorMessage => "$Self->{OperationName}: User could not be authenticated!",
);
}
# check needed hashes
for my $Needed (qw(ConfigItem)) {
if ( !IsHashRefWithData( $Param{Data}->{$Needed} ) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: $Needed parameter is missing or not valid!",
);
}
}
# check needed items
for my $Needed (qw(ConfigItemID)) {
if ( !IsPositiveInteger( $Param{Data}->{$Needed} ) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: $Needed parameter is missing or not valid!",
);
}
}
# check for valid ConfigItemID
my $ConfigItemID = $Param{Data}->{ConfigItemID};
# get config item object
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
# get ConfigItem data
my $ConfigItemData = $ConfigItemObject->ConfigItemGet(
ConfigItemID => $ConfigItemID,
);
if ( !IsHashRefWithData($ConfigItemData) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItemID is invalid!",
);
}
# isolate config item parameter
my $ConfigItem = $Param{Data}->{ConfigItem};
# remove leading and trailing spaces
for my $Attribute ( sort keys %{$ConfigItem} ) {
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
# remove leading spaces
$ConfigItem->{$Attribute} =~ s{\A\s+}{};
# remove trailing spaces
$ConfigItem->{$Attribute} =~ s{\s+\z}{};
}
}
# if the parameter ReplaceExistingData is set to 0 or if it is missing
# then missing, empty or only partially defined CIXMLData parameter attributes are allowed
# in this case the existing CIXMLData is used for the missing parts.
# A missing (undefined) CIXMLData attribute has the same effect
# the ReplaceExistingData parameter also influences if existing attachments should be replaced or kept
if ( !$Param{Data}->{ReplaceExistingData} || !defined $ConfigItem->{CIXMLData} ) {
# set to empty hash reference if empty or not defined
$ConfigItem->{CIXMLData} ||= {};
# CIXMLData must be a hash reference
if ( ref $ConfigItem->{CIXMLData} ne 'HASH' ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItem->CIXMLData is missing or invalid!",
);
}
# get latest version data from configitem
my $Version = $ConfigItemObject->VersionGet(
ConfigItemID => $ConfigItemID,
UserID => $UserID,
);
if ( !IsHashRefWithData($Version) ) {
my $ErrorMessage = 'Could not get ConfigItem data'
. ' in Kernel::GenericInterface::Operation::ConfigItem::ConfigItemUpdate::Run()';
return $Self->ReturnError(
ErrorCode => '$Self->{OperationName}.InvalidParameter',
ErrorMessage => "$Self->{OperationName}: $ErrorMessage",
);
}
# remove unneeded items
delete $Version->{ClassID};
delete $Version->{CurDeplStateID};
delete $Version->{CurInciStateID};
delete $Version->{DeplStateID};
delete $Version->{InciStateID};
delete $Version->{XMLDefinitionID};
my $Definition = delete $Version->{XMLDefinition};
my $FormatedXMLData = $Self->InvertFormatXMLData(
XMLData => $Version->{XMLData}->[1]->{Version},
);
my $ReplacedXMLData = $Self->InvertReplaceXMLData(
XMLData => $FormatedXMLData,
Definition => $Definition,
);
$Version->{XMLData} = $ReplacedXMLData;
# rename XMLData since SOAP transport complains about XML prefix on names
$Version->{CIXMLData} = delete $Version->{XMLData};
# merge existing data and new data from parameters
$ConfigItem->{CIXMLData} = {
%{ $Version->{CIXMLData} },
%{ $ConfigItem->{CIXMLData} },
};
}
if ( !IsHashRefWithData( $ConfigItem->{CIXMLData} ) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItem->CIXMLData is empty or invalid!",
);
}
# remove leading and trailing spaces for CIXMLData
$Self->_CleanXMLData( XMLData => $ConfigItem->{CIXMLData} );
# check ConfigItem attribute values
my $ConfigItemCheck = $Self->_CheckConfigItem( ConfigItem => $ConfigItem );
if ( !$ConfigItemCheck->{Success} ) {
return $Self->ReturnError( %{$ConfigItemCheck} );
}
# check update permissions
my $Permission = $ConfigItemObject->Permission(
Scope => 'Class',
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
UserID => $UserID,
Type => $Self->{Config}->{Permission},
);
if ( !$Permission ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.AccessDenied",
ErrorMessage => "$Self->{OperationName}: Can not update configuration items!",
);
}
# handle attachments
my $Attachment;
my @AttachmentList;
if ( defined $Param{Data}->{ConfigItem}->{Attachment} ) {
# isolate Attachment parameter
$Attachment = delete $Param{Data}->{ConfigItem}->{Attachment};
# homologate imput to array
if ( IsHashRefWithData($Attachment) ) {
push @AttachmentList, $Attachment;
}
elsif ( IsArrayRefWithData($Attachment) ) {
@AttachmentList = @{$Attachment};
}
else {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Attachment parameter is invalid!",
);
}
# check Attachment internal structure
for my $AttachmentItem (@AttachmentList) {
if ( !IsHashRefWithData($AttachmentItem) ) {
return $Self->ReturnError(
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Attachment parameter is invalid!",
);
}
# remove leading and trailing spaces
for my $Attribute ( sort keys %{$AttachmentItem} ) {
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
#remove leading spaces
$AttachmentItem->{$Attribute} =~ s{\A\s+}{};
#remove trailing spaces
$AttachmentItem->{$Attribute} =~ s{\s+\z}{};
}
}
# check Attachment attribute values
my $AttachmentCheck = $Self->_CheckAttachment( Attachment => $AttachmentItem );
if ( !$AttachmentCheck->{Success} ) {
return $Self->ReturnError( %{$AttachmentCheck} );
}
}
}
return $Self->_ConfigItemUpdate(
ConfigItem => $ConfigItem,
ConfigItemID => $ConfigItemID,
AttachmentList => \@AttachmentList,
ReplaceExistingData => $Param{Data}->{ReplaceExistingData},
UserID => $UserID,
);
}
=head1 INTERNAL INTERFACE
=head2 _CleanXMLData()
removed trailing and leading white spaces in the XMLData.
my $XMLDataClean = $OperationObject->_CleanXMLData(
Definition => $DefinitionArrayRef, # Config Item Definition ot just part of it
XMLData => $XMLDataHashRef,
);
returns:
$XMLDataClean = {
Success => 1, # if everything is OK
}
$XMLDataClean = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CleanXMLData {
my ( $Self, %Param ) = @_;
my $XMLData = $Param{XMLData};
KEY:
for my $Key ( sort keys %{$XMLData} ) {
if ( ref $XMLData->{$Key} eq 'ARRAY' ) {
ELEMENT:
for my $Element ( @{ $XMLData->{$Key} } ) {
if ( ref $Element eq 'HASH' ) {
# start recursion
$Self->_CleanXMLData( XMLData => $Element );
next ELEMENT;
}
elsif ( ref $Element eq '' ) {
#remove leading spaces
$Element =~ s{\A\s+}{};
#remove trailing spaces
$Element =~ s{\s+\z}{};
}
}
}
elsif ( ref $XMLData->{$Key} eq 'HASH' ) {
# start recursion
$Self->_CleanXMLData( XMLData => $XMLData->{$Key} );
next KEY;
}
elsif ( ref $XMLData->{$Key} eq '' ) {
# TODO: Use StringClean function!
#remove leading spaces
$XMLData->{$Key} =~ s{\A\s+}{};
#remove trailing spaces
$XMLData->{$Key} =~ s{\s+\z}{};
}
}
return 1;
}
=head2 _CheckConfigItem()
checks if the given config item parameters are valid.
my $ConfigItemCheck = $OperationObject->_CheckConfigItem(
ConfigItem => $ConfigItem, # all config item parameters
);
returns:
$ConfigItemCheck = {
Success => 1, # if everything is OK
}
$ConfigItemCheck = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CheckConfigItem {
my ( $Self, %Param ) = @_;
my $ConfigItem = $Param{ConfigItem};
# check config item internally
for my $Needed (qw(Class Name DeplState InciState CIXMLData)) {
if ( !$ConfigItem->{$Needed} ) {
return {
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage => "$Self->{OperationName}: ConfigItem->$Needed parameter is missing!",
};
}
}
# check ConfigItem->Class
if ( !$Self->ValidateClass( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->Class parameter is invalid!",
};
}
# check ConfigItem->DeplState
if ( !$Self->ValidateDeplState( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->DeplState parameter is invalid!",
};
}
# check ConfigItem->DeplState
if ( !$Self->ValidateInciState( %{$ConfigItem} ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage =>
"$Self->{OperationName}: ConfigItem->InciState parameter is invalid!",
};
}
# get last config item defintion
my $DefinitionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
);
my $XMLDataCheckResult = $Self->CheckXMLData(
Definition => $DefinitionData->{DefinitionRef},
XMLData => $ConfigItem->{CIXMLData},
);
if ( !$XMLDataCheckResult->{Success} ) {
return $XMLDataCheckResult;
}
# if everything is OK then return Success
return {
Success => 1,
};
}
=head2 _CheckAttachment()
checks if the given attachment parameter is valid.
my $AttachmentCheck = $OperationObject->_CheckAttachment(
Attachment => $Attachment, # all attachment parameters
);
returns:
$AttachmentCheck = {
Success => 1, # if everething is OK
}
$AttachmentCheck = {
ErrorCode => 'Function.Error', # if error
ErrorMessage => 'Error description',
}
=cut
sub _CheckAttachment {
my ( $Self, %Param ) = @_;
my $Attachment = $Param{Attachment};
# check attachment item internally
for my $Needed (qw(Content ContentType Filename)) {
if ( !$Attachment->{$Needed} ) {
return {
ErrorCode => "$Self->{OperationName}.MissingParameter",
ErrorMessage =>
"$Self->{OperationName}: Attachment->$Needed parameter is missing!",
};
}
}
# check Article->ContentType
if ( $Attachment->{ContentType} ) {
$Attachment->{ContentType} = lc $Attachment->{ContentType};
# check Charset part
my $Charset = '';
if ( $Attachment->{ContentType} =~ /charset=/i ) {
$Charset = $Attachment->{ContentType};
$Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
$Charset =~ s/"|'//g;
$Charset =~ s/(.+?);.*/$1/g;
}
if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) )
{
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: Attachment->ContentType is invalid!",
};
}
# check MimeType part
my $MimeType = '';
if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) {
$MimeType = $1;
$MimeType =~ s/"|'//g;
}
if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
return {
ErrorCode => "$Self->{OperationName}.InvalidParameter",
ErrorMessage => "$Self->{OperationName}: Attachment->ContentType is invalid!",
};
}
}
# if everything is OK then return Success
return {
Success => 1,
};
}
=head2 _ConfigItemUpdate()
updates a configuration item with attachments if specified.
my $Response = $OperationObject->_ConfigItemUpdate(
ConfigItemID => 123,
ConfigItem => $ConfigItem, # all configuration item parameters
AttachmentList => $Attachment, # a list of all attachments
ReplaceExistingData => 0, # if the existing xml attributes and attachments should be replaced or kept
UserID => 123,
);
returns:
$Response = {
Success => 1, # if everething is OK
Data => {
ConfigItemID => 123,
ConfigItemNumber => 'CN123',
}
}
$Response = {
Success => 0, # if unexpected error
ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
}
=cut
sub _ConfigItemUpdate {
my ( $Self, %Param ) = @_;
my $ConfigItemID = $Param{ConfigItemID};
my $ConfigItem = $Param{ConfigItem};
my $AttachmentList = $Param{AttachmentList};
my $DeplStateID = $Self->{ReverseDeplStateList}->{ $ConfigItem->{DeplState} };
my $InciStateID = $Self->{ReverseInciStateList}->{ $ConfigItem->{InciState} };
my $RawXMLData = $ConfigItem->{CIXMLData};
# get config item object
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
# get last config item defintion
my $DefinitionData = $ConfigItemObject->DefinitionGet(
ClassID => $Self->{ReverseClassList}->{ $ConfigItem->{Class} },
);
# replace date, date time, customer, company and general catalog values
my $ReplacedXMLData = $Self->ReplaceXMLData(
XMLData => $RawXMLData,
Definition => $DefinitionData->{DefinitionRef},
);
# create an XMLData structure suitable for VersionAdd
my $XMLData = $Self->FormatXMLData(
XMLData => $ReplacedXMLData,
);
# get the current config item version data
my $CurrentVersion = $ConfigItemObject->VersionGet(
ConfigItemID => $ConfigItemID,
UserID => $Param{UserID},
);
my $VersionID = $ConfigItemObject->VersionAdd(
ConfigItemID => $ConfigItemID,
Name => $ConfigItem->{Name},
DefinitionID => $DefinitionData->{DefinitionID},
DeplStateID => $DeplStateID,
InciStateID => $InciStateID,
XMLData => $XMLData,
UserID => $Param{UserID},
);
if ( !$VersionID ) {
return {
Success => 0,
ErrorMessage => 'Configuration Item could not be updated, please contact the system'
. 'administrator'
};
}
# get the version ID of the config item before the update
my $CurrentVersionID = $CurrentVersion->{VersionID} || '';
# compare old version and new version IDs
if ( $CurrentVersionID eq $VersionID ) {
$Self->{DebuggerObject}->Notice(
Summary => "$Self->{OperationName}: No change in configuration item version",
Data => 'The internal structure of the configuration item was indentical to the last'
. ' one, no update was performed',
);
}
# the ReplaceExistingData flag is set
if ( $Param{ReplaceExistingData} ) {
# get a list of all attachments
my @ExistingAttachments = $ConfigItemObject->ConfigItemAttachmentList(
ConfigItemID => $ConfigItemID,
);
# delete all attachments of this config item
FILENAME:
for my $Filename (@ExistingAttachments) {
# delete the attachment
my $DeletionSuccess = $ConfigItemObject->ConfigItemAttachmentDelete(
ConfigItemID => $ConfigItemID,
Filename => $Filename,
UserID => $Param{UserID},
);
if ( !$DeletionSuccess ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Unknown problem when deleting attachment $Filename of ConfigItem "
. "$ConfigItemID. Please check the VirtualFS backend for stale files!",
);
}
}
}
# set attachments
if ( IsArrayRefWithData($AttachmentList) ) {
for my $Attachment ( @{$AttachmentList} ) {
my $Result = $Self->CreateAttachment(
Attachment => $Attachment,
ConfigItemID => $ConfigItemID,
UserID => $Param{UserID}
);
if ( !$Result->{Success} ) {
my $ErrorMessage = $Result->{ErrorMessage}
|| "Attachment could not be created, please contact the system administrator";
return {
Success => 0,
ErrorMessage => $ErrorMessage,
};
}
}
}
# get ConfigItem data
my $ConfigItemData = $ConfigItemObject->ConfigItemGet(
ConfigItemID => $ConfigItemID,
);
if ( !IsHashRefWithData($ConfigItemData) ) {
return {
Success => 0,
ErrorMessage => 'Could not get new configuration item information, please contact the system administrator',
};
}
return {
Success => 1,
Data => {
ConfigItemID => $ConfigItemID,
Number => $ConfigItemData->{Number},
},
};
}
1;
=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
@@ -0,0 +1,130 @@
# --
# 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::Operation::FAQ::LanguageList;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::FAQ::LanguageList - GenericInterface FAQ LanguageList Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform LanguageList Operation. This will return the current FAQ Languages.
my $Result = $OperationObject->Run(
Data => {},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => { # result data payload after Operation
Language => [
{
ID => 1,
Name> 'en',
},
{
ID => 2,
Name> 'OneMoreLanguage',
},
# ...
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# Set UserID to root because in public interface there is no user.
my %Languages = $Kernel::OM->Get('Kernel::System::FAQ')->LanguageList(
UserID => 1,
);
if ( !IsHashRefWithData( \%Languages ) ) {
my $ErrorMessage = 'Could not get language data'
. ' in Kernel::GenericInterface::Operation::FAQ::LanguageList::Run()';
return $Self->ReturnError(
ErrorCode => 'TicketList.NotLanguageData',
ErrorMessage => "TicketList: $ErrorMessage",
);
}
my @LanguageList;
for my $Key ( sort keys %Languages ) {
my %Language = (
ID => $Key,
Name => $Languages{$Key},
);
push @LanguageList, {%Language};
}
return {
Success => 1,
Data => {
Language => \@LanguageList,
},
};
}
1;
=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
@@ -0,0 +1,140 @@
# --
# 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::Operation::FAQ::PublicCategoryList;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::FAQ::PublicCategoryList - GenericInterface FAQ PublicCategoryList Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform PublicCategoryList Operation. This will return the current FAQ Categories.
my $Result = $OperationObject->Run(
Data => {},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => { # result data payload after Operation
Category => [
{
ID => 1,
Name> 'Misc',
},
{
ID => 2,
Name> 'OneMoreCategory',
},
# ...
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# Set UserID to root because in public interface there is no user.
my $CategoryTree = $Kernel::OM->Get('Kernel::System::FAQ')->GetPublicCategoriesLongNames(
Valid => 1,
Type => 'rw',
UserID => 1,
);
if ( !IsHashRefWithData($CategoryTree) ) {
my $ErrorMessage = 'Could not get category data'
. ' in Kernel::GenericInterface::Operation::FAQ::PublicCategoryList::Run()';
return $Self->ReturnError(
ErrorCode => 'PublicCategoryList.NotCategoryData',
ErrorMessage => "PublicCategoryList: $ErrorMessage",
);
}
my @PublicCategoryList;
for my $Key ( sort( keys %{$CategoryTree} ) ) {
my %Category = (
ID => $Key,
Name => $CategoryTree->{$Key},
);
push @PublicCategoryList, {%Category};
}
# Prepare return data.
my $ReturnData = {
Success => 1,
Data => {},
};
if ( scalar @PublicCategoryList > 1 ) {
$ReturnData->{Data}->{Category} = \@PublicCategoryList;
}
else {
$ReturnData->{Data}->{Category} = $PublicCategoryList[0];
}
return $ReturnData;
}
1;
=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
@@ -0,0 +1,305 @@
# --
# 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::Operation::FAQ::PublicFAQGet;
use strict;
use warnings;
use MIME::Base64;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::FAQ::PublicFAQGet - GenericInterface FAQ PublicFAQGet Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform PublicFAQGet Operation. This will return a Public FAQ entry.
my $Result = $OperationObject->Run(
Data => {
ItemID = '32,33',
GetAttachmentContents = 1, # 0|1, defaults to 1
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => { # result data payload after Operation
ItemID => [
{
ID => 32,
ItemID => 32,
FAQID => 32,
Number => 100032,
CategoryID => '2',
CategoryName => 'CategoryA::CategoryB',
CategoryShortName => 'CategoryB',
LanguageID => 1,
Language => 'en',
Title => 'Article Title',
Field1 => 'The Symptoms',
Field2 => 'The Problem',
Field3 => 'The Solution',
Field4 => undef, # Not active by default
Field5 => undef, # Not active by default
Field6 => 'Comments',
Approved => 1, # or 0
Keywords => 'KeyWord1 KeyWord2',
Votes => 0, # number of votes
VoteResult => '0.00', # a number between 0.00 and 100.00
StateID => 1,
State => 'internal (agent)', # or 'external (customer)' or
# 'public (all)'
StateTypeID => 1,
StateTypeName => 'internal', # or 'external' or 'public'
CreatedBy => 1,
Changed => '2011-01-05 21:53:50',
ChangedBy => '1',
Created => '2011-01-05 21:53:50',
Name => '1294286030-31.1697297104732', # FAQ Article name or
# systemtime + '-' + random number
ContentType => 'text/html',
Attachment => {
{
Filesize => '540286', # file size in bytes
ContentType => 'image/jpeg',
Filename => 'Error.jpg',
Content => '...', # base64 content
Inline => 0, # specify if is an inline attachment
FileID => 34 # FileID for relation with rich text content
},
{
Filesize => '540286', # file size in bytes
ContentType => 'image/jpeg',
Filename => 'Pencil.jpg',
Content => '...', # base64 content
Inline => 1, # specify if is an inline attachment
FileID => 35 # FileID for relation with rich text content
},
},
},
{
ID => 33,
ItemID => 33,
FAQID => 33,
Number => 100033,
CategoryID => '3',
CategoryName => 'CategoryD::CategoryE',
CategoryShortName => 'CategoryE',
LanguageID => 1,
Language => 'en',
Title => 'Article Title',
Field1 => 'The Symptoms',
Field2 => 'The Problem',
Field3 => 'The Solution',
Field4 => undef, # Not active by default
Field5 => undef, # Not active by default
Field6 => 'Comments',
Approved => 1, # or 0
Keywords => 'KeyWord1 KeyWord2',
Votes => 0, # number of votes
VoteResult => '0.00', # a number between 0.00 and 100.00
StateID => 1,
State => 'internal (agent)', # or 'external (customer)' or
# 'public (all)'
StateTypeID => 1,
StateTypeName => 'internal', # or 'external' or 'public'
CreatedBy => 1,
Changed => '2011-01-05 21:53:50',
ChangedBy => '1',
Created => '2011-01-05 21:53:50',
Name => '1294286030-31.1697297104732', # FAQ Article name or
# systemtime + '-' + random number
},
# ...
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
if ( !$Param{Data}->{ItemID} ) {
return $Self->ReturnError(
ErrorCode => 'PublicFAQGet.MissingParameter',
ErrorMessage => "PublicFAQGet: Got no ItemID!",
);
}
if ( !defined( $Param{Data}->{GetAttachmentContents} ) ) {
$Param{Data}->{GetAttachmentContents} = 1;
}
my $ErrorMessage = '';
my $ReturnData = {
Success => 1,
};
my @ItemIDs = split( /,/, $Param{Data}->{ItemID} );
my @Item;
# Set UserID to root because in public interface there is no user.
my $UserID = 1;
my $FAQObject = $Kernel::OM->Get('Kernel::System::FAQ');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# Get public state types.
my $InterfaceStates = $FAQObject->StateTypeList(
Types => $ConfigObject->Get('FAQ::Public::StateTypes'),
UserID => $UserID,
);
for my $ItemID (@ItemIDs) {
my %FAQEntry = $FAQObject->FAQGet(
ItemID => $ItemID,
ItemFields => 1,
UserID => $UserID,
);
if ( !IsHashRefWithData( \%FAQEntry ) ) {
$ErrorMessage = 'Could not get FAQ data'
. ' in Kernel::GenericInterface::Operation::FAQ::PublicFAQGet::Run()';
return $Self->ReturnError(
ErrorCode => 'PublicFAQGet.NotValidFAQID',
ErrorMessage => "PublicFAQGet: $ErrorMessage",
);
}
# Check permissions.
my $ApprovalSuccess = 1;
if ( $ConfigObject->Get('FAQ::ApprovalRequired') ) {
$ApprovalSuccess = $FAQEntry{Approved};
}
if ( !$ApprovalSuccess || !$InterfaceStates->{ $FAQEntry{StateTypeID} } ) {
$ErrorMessage = 'Could not get FAQ data'
. ' in Kernel::GenericInterface::Operation::FAQ::PublicFAQGet::Run()';
return $Self->ReturnError(
ErrorCode => 'PublicFAQGet.AccessDenied',
ErrorMessage => "PublicFAQGet: $ErrorMessage",
);
}
my @Index = $FAQObject->AttachmentIndex(
ItemID => $ItemID,
ShowInline => 1, # ( 0|1, default 1)
UserID => $UserID,
);
my %File;
if ( IsArrayRefWithData( \@Index ) ) {
my @Attachments;
for my $Attachment (@Index) {
if ( $Param{Data}->{GetAttachmentContents} ) {
%File = $FAQObject->AttachmentGet(
ItemID => $ItemID,
FileID => $Attachment->{FileID},
UserID => $UserID,
);
# Convert content to base64.
$File{Content} = encode_base64( $File{Content} );
$File{Inline} = $Attachment->{Inline};
$File{FileID} = $Attachment->{FileID};
}
else {
%File = (
Filename => $Attachment->{Filename},
ContentType => $Attachment->{ContentType},
Filesize => $Attachment->{Filesize},
Content => '',
Inline => $Attachment->{Inline},
FileID => $Attachment->{FileID}
);
}
push @Attachments, {%File};
}
# Set FAQ entry data.
$FAQEntry{Attachment} = \@Attachments;
}
push @Item, \%FAQEntry;
}
if ( !scalar @Item ) {
$ErrorMessage = 'Could not get FAQ data'
. ' in Kernel::GenericInterface::Operation::FAQ::PublicFAQGet::Run()';
return $Self->ReturnError(
ErrorCode => 'PublicFAQGet.NoFAQData',
ErrorMessage => "PublicFAQGet: $ErrorMessage",
);
}
$ReturnData->{Data}->{FAQItem} = \@Item;
return $ReturnData;
}
1;
=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
@@ -0,0 +1,216 @@
# --
# 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::Operation::FAQ::PublicFAQSearch;
use strict;
use warnings;
use MIME::Base64;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::FAQ::PublicFAQSearch - GenericInterface FAQ PublicFAQSearch Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject WebserviceID )) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform PublicFAQSearch Operation. This will return a list of public FAQ entries.
my @IDs = $OperationObject->Run(
Data => {
Number => '*134*', # (optional)
Title => '*some title*', # (optional)
# is searching in Number, Title, Keyword and Field1-6
What => '*some text*', # (optional)
Keyword => '*webserver*', # (optional)
LanguageIDs => [ 4, 5, 6 ], # (optional)
CategoryIDs => [ 7, 8, 9 ], # (optional)
OrderBy => [ 'FAQID', 'Title' ], # (optional)
# Additional information for OrderBy:
# The OrderByDirection can be specified for each OrderBy attribute.
# The pairing is made by the array indexes.
OrderByDirection => 'Down', # (Down | Up) # (optional)
# default: 'Down'
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => { # result data payload after Operation
ID => [
32,
13,
12,
9,
6,
5,
4,
1,
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# Set SearchLimit on 0 because we need to get all entries.
my $SearchLimit = 0;
my $Config = $Kernel::OM->Get('Kernel::Config')->Get("FAQ::Frontend::PublicFAQSearch");
my $SortBy = $Param{Data}->{OrderBy}
|| $Config->{'SortBy::Default'}
|| 'FAQID';
# The SortBy param could be an ARRAY an SCALAR or an empty value.
if ( !IsArrayRefWithData($SortBy) && $SortBy ne '' ) {
$SortBy = [$SortBy];
}
my $OrderBy = $Param{Data}->{OrderByDirection}
|| $Config->{'Order::Default'}
|| 'Down';
my $CategoryIDs;
# The CategoryID param could be an ARRAY an SCALAR or an empty value.
$Param{Data}->{CategoryIDs} = $Param{Data}->{CategoryIDs} || '';
if ( !IsArrayRefWithData( $Param{Data}->{CategoryIDs} ) && $Param{Data}->{CategoryIDs} ne '' ) {
$CategoryIDs = [ $Param{Data}->{CategoryIDs} ];
}
elsif ( $Param{Data}->{CategoryIDs} ne '' ) {
$CategoryIDs = $Param{Data}->{CategoryIDs};
}
my $LanguageIDs;
# The LanguageID param could be an ARRAY an SCALAR or an empty value.
$Param{Data}->{LanguageIDs} = $Param{Data}->{LanguageIDs} || '';
if ( !IsArrayRefWithData( $Param{Data}->{LanguageIDs} ) && $Param{Data}->{LanguageIDs} ne '' ) {
$LanguageIDs = [ $Param{Data}->{LanguageIDs} ];
}
elsif ( $Param{Data}->{LanguageIDs} ne '' ) {
$LanguageIDs = $Param{Data}->{LanguageIDs};
}
my $FAQObject = $Kernel::OM->Get('Kernel::System::FAQ');
# Set UserID to root because in public interface there is no user.
my $UserID = 1;
# Set default interface settings.
my $Interface = $FAQObject->StateTypeGet(
Name => 'public',
UserID => $UserID,
);
my $InterfaceStates = $FAQObject->StateTypeList(
Types => $Kernel::OM->Get('Kernel::Config')->Get('FAQ::Public::StateTypes'),
UserID => $UserID,
);
# Perform FAQ search.
my @ViewableItemIDs = $FAQObject->FAQSearch(
Number => $Param{Data}->{Number} || '',
Title => $Param{Data}->{Title} || '',
What => $Param{Data}->{What} || '',
Keyword => $Param{Data}->{Keyword} || '',
LanguageIDs => $LanguageIDs,
CategoryIDs => $CategoryIDs,
OrderBy => $SortBy,
OrderByDirection => [$OrderBy],
Limit => $SearchLimit,
UserID => $UserID,
States => $InterfaceStates,
Interface => $Interface,
);
if ( !IsArrayRefWithData( \@ViewableItemIDs ) ) {
my $ErrorMessage = 'Could not get FAQ data'
. ' in Kernel::GenericInterface::Operation::FAQ::PublicFAQSearch::Run()';
return $Self->ReturnError(
ErrorCode => 'PublicFAQSearch.NotFAQData',
ErrorMessage => "PublicFAQSearch: $ErrorMessage",
);
}
# Prepare return data.
my $ReturnData = {
Data => {},
Success => 1,
};
# Set FAQ entry data.
if ( scalar @ViewableItemIDs > 1 ) {
$ReturnData->{Data}->{ID} = \@ViewableItemIDs;
}
else {
$ReturnData->{Data}->{ID} = $ViewableItemIDs[0];
}
return $ReturnData;
}
1;
=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
@@ -0,0 +1,112 @@
# --
# 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::Operation::Session::Common;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Session::Common - Base class for Session Operations
=head1 PUBLIC INTERFACE
=head2 CreateSessionID()
performs user authentication and return a new SessionID value
my $SessionID = $CommonObject->CreateSessionID(
Data {
UserLogin => 'Agent1',
CustomerUserLogin => 'Customer1', # optional, provide UserLogin or
# CustomerUserLogin
Password => 'some password', # plain text password
}
);
Returns undef on failure or
$SessionID = 'AValidSessionIDValue'; # the new session id value
=cut
sub CreateSessionID {
my ( $Self, %Param ) = @_;
my $User;
my %UserData;
my $UserType;
# get params
my $PostPw = $Param{Data}->{Password} || '';
if ( defined $Param{Data}->{UserLogin} && $Param{Data}->{UserLogin} ) {
# if UserLogin
my $PostUser = $Param{Data}->{UserLogin} || '';
# check submitted data
$User = $Kernel::OM->Get('Kernel::System::Auth')->Auth(
User => $PostUser,
Pw => $PostPw,
);
%UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
User => $User,
Valid => 1,
);
$UserType = 'User';
}
elsif ( defined $Param{Data}->{CustomerUserLogin} && $Param{Data}->{CustomerUserLogin} ) {
# if UserCustomerLogin
my $PostUser = $Param{Data}->{CustomerUserLogin} || '';
# check submitted data
$User = $Kernel::OM->Get('Kernel::System::CustomerAuth')->Auth(
User => $PostUser,
Pw => $PostPw,
);
%UserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $PostUser,
Valid => 1,
);
$UserType = 'Customer';
}
# login is invalid
return if !$User;
# create new session id
my $NewSessionID = $Kernel::OM->Get('Kernel::System::AuthSession')->CreateSessionID(
%UserData,
UserLastRequest => $Kernel::OM->Create('Kernel::System::DateTime')->ToEpoch(),
UserType => $UserType,
SessionSource => 'GenericInterface',
);
return $NewSessionID if ($NewSessionID);
return;
}
1;
=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
@@ -0,0 +1,135 @@
# --
# 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::Operation::Session::SessionCreate;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsStringWithData IsHashRefWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::Session::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Session::SessionCreate - GenericInterface Session Create Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (
qw(DebuggerObject WebserviceID)
)
{
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
Retrieve a new session id value.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'Agent1',
CustomerUserLogin => 'Customer1', # optional, provide UserLogin or CustomerUserLogin
Password => 'some password', # plain text password
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
SessionID => $SessionID,
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !IsHashRefWithData( $Param{Data} ) ) {
return $Self->ReturnError(
ErrorCode => 'SessionCreate.MissingParameter',
ErrorMessage => "SessionCreate: The request is empty!",
);
}
for my $Needed (qw( Password )) {
if ( !$Param{Data}->{$Needed} ) {
return $Self->ReturnError(
ErrorCode => 'SessionCreate.MissingParameter',
ErrorMessage => "SessionCreate: $Needed parameter is missing!",
);
}
}
my $SessionID = $Self->CreateSessionID(
%Param,
);
if ( !$SessionID ) {
return $Self->ReturnError(
ErrorCode => 'SessionCreate.AuthFail',
ErrorMessage => "SessionCreate: Authorization failing!",
);
}
return {
Success => 1,
Data => {
SessionID => $SessionID,
},
};
}
1;
=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
@@ -0,0 +1,165 @@
# --
# 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::Operation::Session::SessionGet;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsStringWithData IsHashRefWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::Session::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Session::SessionGet - GenericInterface Session Get Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (
qw(DebuggerObject WebserviceID)
)
{
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
Get session information.
my $Result = $OperationObject->Run(
Data => {
SessionID => '1234567890123456',
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
UserSessionStart => '1293801801',
UserRemoteAddr => '127.0.0.1',
UserRemoteUserAgent => 'Some User Agent x.x',
UserLastname => 'SomeLastName',
UserFirstname => 'SomeFirstname',
# and other preferences values
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
if ( !IsHashRefWithData( $Param{Data} ) ) {
return $Self->ReturnError(
ErrorCode => 'SessionGet.MissingParameter',
ErrorMessage => "SessionGet: The request is empty!",
);
}
if ( !$Param{Data}->{SessionID} ) {
return $Self->ReturnError(
ErrorCode => 'SessionGet.MissingParameter',
ErrorMessage => "SessionGet: SessionID is missing!",
);
}
my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession');
# Honor SessionCheckRemoteIP, SessionMaxIdleTime, etc.
my $Valid = $SessionObject->CheckSessionID(
SessionID => $Param{Data}->{SessionID},
);
if ( !$Valid ) {
return $Self->ReturnError(
ErrorCode => 'SessionGet.SessionInvalid',
ErrorMessage => 'SessionGet: SessionID is Invalid!',
);
}
my %SessionDataRaw = $SessionObject->GetSessionIDData(
SessionID => $Param{Data}->{SessionID},
);
# Filter out some sensitive values
delete $SessionDataRaw{UserPw};
delete $SessionDataRaw{UserChallengeToken};
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
my @SessionData;
for my $DataKey ( sort keys %SessionDataRaw ) {
my $Value = $SessionDataRaw{$DataKey};
my %Data = (
Key => $DataKey,
Value => $Value,
);
if ( ref $Value ) {
$Data{Value} = $JSONObject->Encode(
Data => $Value,
SortKeys => 1,
);
$Data{Serialized} = 1;
}
push @SessionData, \%Data;
}
return {
Success => 1,
Data => {
SessionData => \@SessionData,
},
};
}
1;
=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
@@ -0,0 +1,144 @@
# --
# 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::Operation::Test::Test;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Test::Test - GenericInterface Operation Test backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform the selected test Operation. This will return the data that
was handed to the function or return a variable data if 'TestError' and
'ErrorData' params are sent.
my $Result = $OperationObject->Run(
Data => { # data payload before Operation
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => { # result data payload after Operation
...
},
};
my $Result = $OperationObject->Run(
Data => { # data payload before Operation
TestError => 1,
ErrorData => {
...
},
},
);
$Result = {
Success => 0, # it always return 0
ErrorMessage => 'Error message for error code: 1', # including the 'TestError' param
Data => {
ErrorData => { # same data was sent as
# 'ErrorData' param
},
...
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
# check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Operation Test backend)!'
);
}
if ( defined $Param{Data} && $Param{Data}->{TestError} ) {
return {
Success => 0,
ErrorMessage => "Error message for error code: $Param{Data}->{TestError}",
Data => {
ErrorData => $Param{Data}->{ErrorData},
},
};
}
# copy data
my $ReturnData;
if ( ref $Param{Data} eq 'HASH' ) {
$ReturnData = \%{ $Param{Data} };
}
else {
$ReturnData = undef;
}
# return result
return {
Success => 1,
Data => $ReturnData,
};
}
1;
=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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,568 @@
# --
# 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::Operation::Ticket::TicketGet;
use strict;
use warnings;
use MIME::Base64;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::Ticket::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Ticket::TicketGet - GenericInterface Ticket Get Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform TicketGet Operation. This function is able to return
one or more ticket entries in one call.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or CustomerUserLogin or SessionID is
# required
CustomerUserLogin => 'some customer login',
SessionID => 123,
Password => 'some password', # if UserLogin or customerUserLogin is sent then
# Password is required
TicketID => '32,33', # required, could be coma separated IDs or an Array
DynamicFields => 0, # Optional, 0 as default. Indicate if Dynamic Fields
# should be included or not on the ticket content.
Extended => 1, # Optional, 0 as default
AllArticles => 1, # Optional, 0 as default. Set as 1 will include articles
# for tickets.
ArticleSenderType => [ $ArticleSenderType1, $ArticleSenderType2 ], # Optional, only requested article sender types
ArticleOrder => 'DESC', # Optional, DESC,ASC - default is ASC
ArticleLimit => 5, # Optional
Attachments => 1, # Optional, 0 as default. If it's set with the value 1,
# attachments for articles will be included on ticket data
GetAttachmentContents = 1 # Optional, 1 as default. 0|1,
HTMLBodyAsAttachment => 1 # Optional, If enabled the HTML body version of each article
# is added to the attachments list
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
Ticket => [
{
TicketNumber => '20101027000001',
Title => 'some title',
TicketID => 123,
State => 'some state',
StateID => 123,
StateType => 'some state type',
Priority => 'some priority',
PriorityID => 123,
Lock => 'lock',
LockID => 123,
Queue => 'some queue',
QueueID => 123,
CustomerID => 'customer_id_123',
CustomerUserID => 'customer_user_id_123',
Owner => 'some_owner_login',
OwnerID => 123,
Type => 'some ticket type',
TypeID => 123,
SLA => 'some sla',
SLAID => 123,
Service => 'some service',
ServiceID => 123,
Responsible => 'some_responsible_login',
ResponsibleID => 123,
Age => 3456,
Created => '2010-10-27 20:15:00'
CreateBy => 123,
Changed => '2010-10-27 20:15:15',
ChangeBy => 123,
ArchiveFlag => 'y',
# If DynamicFields => 1 was passed, you'll get an entry like this for each dynamic field:
DynamicField => [
{
Name => 'some name',
Value => 'some value',
},
],
# (time stamps of expected escalations)
EscalationResponseTime (unix time stamp of response time escalation)
EscalationUpdateTime (unix time stamp of update time escalation)
EscalationSolutionTime (unix time stamp of solution time escalation)
# (general escalation info of nearest escalation type)
EscalationDestinationIn (escalation in e. g. 1h 4m)
EscalationDestinationTime (date of escalation in unix time, e. g. 72193292)
EscalationDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
EscalationTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
EscalationTime (seconds total till escalation of nearest escalation time type - response, update or solution time, e. g. "3600")
# (detailed escalation info about first response, update and solution time)
FirstResponseTimeEscalation (if true, ticket is escalated)
FirstResponseTimeNotification (if true, notify - x% of escalation has reached)
FirstResponseTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
FirstResponseTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
FirstResponseTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
FirstResponseTime (seconds total till escalation, e. g. "3600")
UpdateTimeEscalation (if true, ticket is escalated)
UpdateTimeNotification (if true, notify - x% of escalation has reached)
UpdateTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
UpdateTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
UpdateTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
UpdateTime (seconds total till escalation, e. g. "3600")
SolutionTimeEscalation (if true, ticket is escalated)
SolutionTimeNotification (if true, notify - x% of escalation has reached)
SolutionTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
SolutionTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
SolutionTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
SolutionTime (seconds total till escalation, e. g. "3600")
# if you use param Extended to get extended ticket attributes
FirstResponse (timestamp of first response, first contact with customer)
FirstResponseInMin (minutes till first response)
FirstResponseDiffInMin (minutes till or over first response)
SolutionInMin (minutes till solution time)
SolutionDiffInMin (minutes till or over solution time)
FirstLock (timestamp of first lock)
Article => [
{
ArticleID
From
To
Cc
Subject
Body
ReplyTo
MessageID
InReplyTo
References
SenderType
SenderTypeID
IsVisibleForCustomer
ContentType
Charset
MimeType
IncomingTime
# If DynamicFields => 1 was passed, you'll get an entry like this for each dynamic field:
DynamicField => [
{
Name => 'some name',
Value => 'some value',
},
],
Attachment => [
{
Content => "xxxx", # actual attachment contents, base64 enconded
ContentAlternative => "",
ContentID => "",
ContentType => "application/pdf",
FileID => 34,
Filename => "StdAttachment-Test1.pdf",
FilesizeRaw => 4722,
},
{
# . . .
},
]
},
{
#. . .
},
],
},
{
#. . .
},
]
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
return $Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
my ( $UserID, $UserType ) = $Self->Auth(
%Param,
);
return $Self->ReturnError(
ErrorCode => 'TicketGet.AuthFail',
ErrorMessage => "TicketGet: Authorization failing!",
) if !$UserID;
# check needed stuff
for my $Needed (qw(TicketID)) {
if ( !$Param{Data}->{$Needed} ) {
return $Self->ReturnError(
ErrorCode => 'TicketGet.MissingParameter',
ErrorMessage => "TicketGet: $Needed parameter is missing!",
);
}
}
my $ErrorMessage = '';
# all needed variables
my @TicketIDs;
if ( IsStringWithData( $Param{Data}->{TicketID} ) ) {
@TicketIDs = split( /,/, $Param{Data}->{TicketID} );
}
elsif ( IsArrayRefWithData( $Param{Data}->{TicketID} ) ) {
@TicketIDs = @{ $Param{Data}->{TicketID} };
}
else {
return $Self->ReturnError(
ErrorCode => 'TicketGet.WrongStructure',
ErrorMessage => "TicketGet: Structure for TicketID is not correct!",
);
}
# Get the list of article dynamic fields
my $ArticleDynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldList(
ObjectType => 'Article',
ResultType => 'HASH',
);
# Crate a lookup list for easy search
my %ArticleDynamicFieldLookup = reverse %{$ArticleDynamicFieldList};
TICKET:
for my $TicketID (@TicketIDs) {
my $Access = $Self->CheckAccessPermissions(
TicketID => $TicketID,
UserID => $UserID,
UserType => $UserType,
);
next TICKET if $Access;
return $Self->ReturnError(
ErrorCode => 'TicketGet.AccessDenied',
ErrorMessage => 'TicketGet: User does not have access to the ticket!',
);
}
my $DynamicFields = $Param{Data}->{DynamicFields} || 0;
my $Extended = $Param{Data}->{Extended} || 0;
my $AllArticles = $Param{Data}->{AllArticles} || 0;
my $ArticleOrder = $Param{Data}->{ArticleOrder} || 'ASC';
my $ArticleLimit = $Param{Data}->{ArticleLimit} || 0;
my $Attachments = $Param{Data}->{Attachments} || 0;
my $GetAttachmentContents = $Param{Data}->{GetAttachmentContents} // 1;
my $ReturnData = {
Success => 1,
};
my @Item;
my $ArticleSenderType = '';
if ( IsArrayRefWithData( $Param{Data}->{ArticleSenderType} ) ) {
$ArticleSenderType = $Param{Data}->{ArticleSenderType};
}
elsif ( IsStringWithData( $Param{Data}->{ArticleSenderType} ) ) {
$ArticleSenderType = [ $Param{Data}->{ArticleSenderType} ];
}
# By default, do not include HTML body as attachment, unless it is explicitly requested.
my %ExcludeAttachments = (
ExcludePlainText => 1,
ExcludeHTMLBody => $Param{Data}->{HTMLBodyAsAttachment} ? 0 : 1,
);
# start ticket loop
TICKET:
for my $TicketID (@TicketIDs) {
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# get the Ticket entry
my %TicketEntryRaw = $TicketObject->TicketGet(
TicketID => $TicketID,
DynamicFields => $DynamicFields,
Extended => $Extended,
UserID => $UserID,
);
if ( !IsHashRefWithData( \%TicketEntryRaw ) ) {
$ErrorMessage = 'Could not get Ticket data'
. ' in Kernel::GenericInterface::Operation::Ticket::TicketGet::Run()';
return $Self->ReturnError(
ErrorCode => 'TicketGet.NotValidTicketID',
ErrorMessage => "TicketGet: $ErrorMessage",
);
}
my %TicketEntry;
my @DynamicFields;
# remove all dynamic fields from main ticket hash and set them into an array.
ATTRIBUTE:
for my $Attribute ( sort keys %TicketEntryRaw ) {
if ( $Attribute =~ m{\A DynamicField_(.*) \z}msx ) {
push @DynamicFields, {
Name => $1,
Value => $TicketEntryRaw{$Attribute},
};
next ATTRIBUTE;
}
$TicketEntry{$Attribute} = $TicketEntryRaw{$Attribute};
}
$TicketEntry{TimeUnit} = $TicketObject->TicketAccountedTimeGet(
TicketID => $TicketID,
);
# add dynamic fields array into 'DynamicField' hash key if any
if (@DynamicFields) {
$TicketEntry{DynamicField} = \@DynamicFields;
}
# set Ticket entry data
my $TicketBundle = {
%TicketEntry,
};
if ( !$AllArticles ) {
push @Item, $TicketBundle;
next TICKET;
}
my %ArticleListFilters;
if ( $UserType eq 'Customer' ) {
%ArticleListFilters = (
IsVisibleForCustomer => 1,
);
}
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my @Articles;
if ($ArticleSenderType) {
for my $SenderType ( @{ $ArticleSenderType || [] } ) {
my @ArticlesFiltered = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => $SenderType,
%ArticleListFilters,
);
push @Articles, @ArticlesFiltered;
}
}
else {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
%ArticleListFilters,
);
}
# Modify ArticleLimit if it is greater then number of articles (see bug#14585).
if ( $ArticleLimit > scalar @Articles ) {
$ArticleLimit = scalar @Articles;
}
# Set number of articles by ArticleLimit and ArticleOrder parameters.
if ( IsArrayRefWithData( \@Articles ) && $ArticleLimit ) {
if ( $ArticleOrder eq 'DESC' ) {
@Articles = reverse @Articles;
}
@Articles = @Articles[ 0 .. ( $ArticleLimit - 1 ) ];
}
# start article loop
ARTICLE:
for my $Article (@Articles) {
my $ArticleBackendObject = $ArticleObject->BackendForArticle( %{$Article} );
my %ArticleData = $ArticleBackendObject->ArticleGet(
TicketID => $TicketID,
ArticleID => $Article->{ArticleID},
DynamicFields => $DynamicFields,
);
$Article = \%ArticleData;
next ARTICLE if !$Attachments;
# get attachment index (without attachments)
my %AtmIndex = $ArticleBackendObject->ArticleAttachmentIndex(
ArticleID => $Article->{ArticleID},
%ExcludeAttachments,
);
next ARTICLE if !IsHashRefWithData( \%AtmIndex );
my @Attachments;
ATTACHMENT:
for my $FileID ( sort keys %AtmIndex ) {
next ATTACHMENT if !$FileID;
my %Attachment = $ArticleBackendObject->ArticleAttachment(
ArticleID => $Article->{ArticleID},
FileID => $FileID, # as returned by ArticleAttachmentIndex
);
next ATTACHMENT if !IsHashRefWithData( \%Attachment );
$Attachment{FileID} = $FileID;
if ($GetAttachmentContents)
{
# convert content to base64, but prevent 76 chars brake, see bug#14500.
$Attachment{Content} = encode_base64( $Attachment{Content}, '' );
}
else {
# unset content
$Attachment{Content} = '';
$Attachment{ContentAlternative} = '';
}
push @Attachments, {%Attachment};
}
# set Attachments data
$Article->{Attachment} = \@Attachments;
} # finish article loop
# set Ticket entry data
if (@Articles) {
my @ArticleBox;
for my $ArticleRaw (@Articles) {
my %Article;
my @ArticleDynamicFields;
# remove all dynamic fields from main article hash and set them into an array.
ATTRIBUTE:
for my $Attribute ( sort keys %{$ArticleRaw} ) {
if ( $Attribute =~ m{\A DynamicField_(.*) \z}msx ) {
# skip dynamic fields that are not article related
# this is needed because ArticleGet() also returns ticket dynamic fields
next ATTRIBUTE if ( !$ArticleDynamicFieldLookup{$1} );
push @ArticleDynamicFields, {
Name => $1,
Value => $ArticleRaw->{$Attribute},
};
next ATTRIBUTE;
}
$Article{$Attribute} = $ArticleRaw->{$Attribute};
}
$Article{TimeUnit} = $ArticleObject->ArticleAccountedTimeGet(
ArticleID => $ArticleRaw->{ArticleID}
);
# add dynamic fields array into 'DynamicField' hash key if any
if (@ArticleDynamicFields) {
$Article{DynamicField} = \@ArticleDynamicFields;
}
push @ArticleBox, \%Article;
}
$TicketBundle->{Article} = \@ArticleBox;
}
# add
push @Item, $TicketBundle;
} # finish ticket loop
if ( !scalar @Item ) {
$ErrorMessage = 'Could not get Ticket data'
. ' in Kernel::GenericInterface::Operation::Ticket::TicketGet::Run()';
return $Self->ReturnError(
ErrorCode => 'TicketGet.NotTicketData',
ErrorMessage => "TicketGet: $ErrorMessage",
);
}
# set ticket data into return structure
$ReturnData->{Data}->{Ticket} = \@Item;
# return result
return $ReturnData;
}
1;
=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
@@ -0,0 +1,240 @@
# --
# 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::Operation::Ticket::TicketHistoryGet;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsStringWithData);
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::Ticket::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Ticket::TicketHistoryGet - GenericInterface Ticket History Get Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
return $Self;
}
=head2 Run()
perform TicketHistoryGet Operation. This function is able to return
one or more ticket entries in one call.
my $Result = $OperationObject->Run(
Data => {
UserLogin => 'some agent login', # UserLogin or SessionID is required
SessionID => 123,
Password => 'some password', # if UserLogin is sent then Password is required
TicketID => '32,33', # required, could be coma separated IDs or an Array
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
TicketHistory => [
{
TicketID => 123,
History => [
# ...
],
},
],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
return $Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
my ( $UserID, $UserType ) = $Self->Auth(
%Param,
);
if ( !$UserID ) {
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.AuthFail',
ErrorMessage => "TicketHistoryGet: Authorization failing!",
);
}
if ( $UserType ne 'User' ) {
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.AuthFail',
ErrorMessage => "TicketHistoryGet: User needs to an Agent!",
);
}
for my $Needed (qw(TicketID)) {
if ( !$Param{Data}->{$Needed} ) {
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.MissingParameter',
ErrorMessage => "TicketHistoryGet: $Needed parameter is missing!",
);
}
}
my $ErrorMessage = '';
# All needed variables.
my @TicketIDs;
if ( IsStringWithData( $Param{Data}->{TicketID} ) ) {
@TicketIDs = split( /,/, $Param{Data}->{TicketID} );
}
elsif ( IsArrayRefWithData( $Param{Data}->{TicketID} ) ) {
@TicketIDs = @{ $Param{Data}->{TicketID} };
}
else {
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.WrongStructure',
ErrorMessage => "TicketHistoryGet: Structure for TicketID is not correct!",
);
}
TICKET:
for my $TicketID (@TicketIDs) {
my $Access = $Self->CheckAccessPermissions(
TicketID => $TicketID,
UserID => $UserID,
UserType => $UserType,
);
next TICKET if $Access;
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.AccessDenied',
ErrorMessage => "TicketHistoryGet: User does not have access to the ticket $TicketID!",
);
}
my $ReturnData = {
Success => 1,
};
my @Histories;
my %SafeAttributes = (
TicketID => 1,
ArticleID => 1,
Name => 1,
CreateBy => 1,
CreateTime => 1,
HistoryType => 1,
QueueID => 1,
OwnerID => 1,
PriorityID => 1,
StateID => 1,
HistoryTypeID => 1,
TypeID => 1,
);
# start ticket loop
TICKET:
for my $TicketID (@TicketIDs) {
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my @LinesRaw = $TicketObject->HistoryGet(
TicketID => $TicketID,
UserID => $UserID,
);
my @Lines;
for my $Line (@LinesRaw) {
my %Attributes;
ATTRIBUTE:
for my $Attribute ( sort keys %{$Line} ) {
next ATTRIBUTE if !$SafeAttributes{$Attribute};
$Attributes{$Attribute} = $Line->{$Attribute};
}
push @Lines, \%Attributes;
}
push @Histories, {
TicketID => $TicketID,
History => \@Lines,
};
}
if ( !scalar @Histories ) {
$ErrorMessage = 'Could not get Ticket history data'
. ' in Kernel::GenericInterface::Operation::Ticket::TicketHistoryGet::Run()';
return $Self->ReturnError(
ErrorCode => 'TicketHistoryGet.NotTicketData',
ErrorMessage => "TicketHistoryGet: $ErrorMessage",
);
}
# set ticket data into return structure
$ReturnData->{Data}->{TicketHistory} = \@Histories;
# return result
return $ReturnData;
}
1;
=head1 TERMS AND CONDITIONS
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
@@ -0,0 +1,848 @@
# --
# 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::Operation::Ticket::TicketSearch;
use strict;
use warnings;
use Kernel::System::VariableCheck qw( :all );
use parent qw(
Kernel::GenericInterface::Operation::Common
Kernel::GenericInterface::Operation::Ticket::Common
);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Operation::Ticket::TicketSearch - GenericInterface Ticket Search Operation backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (qw(DebuggerObject WebserviceID)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
$Self->{$Needed} = $Param{$Needed};
}
# get config for this screen
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::TicketSearch');
return $Self;
}
=head2 Run()
perform TicketSearch Operation. This will return a Ticket ID list.
my $Result = $OperationObject->Run(
# ticket number (optional) as STRING or as ARRAYREF
TicketNumber => '%123546%',
TicketNumber => ['%123546%', '%123666%'],
# ticket title (optional) as STRING or as ARRAYREF
Title => '%SomeText%',
Title => ['%SomeTest1%', '%SomeTest2%'],
Queues => ['system queue', 'other queue'],
QueueIDs => [1, 42, 512],
# use also sub queues of Queue|Queues in search
UseSubQueues => 0,
# You can use types like normal, ...
Types => ['normal', 'change', 'incident'],
TypeIDs => [3, 4],
# You can use states like new, open, pending reminder, ...
States => ['new', 'open'],
StateIDs => [3, 4],
# (Open|Closed) tickets for all closed or open tickets.
StateType => 'Open',
# You also can use real state types like new, open, closed,
# pending reminder, pending auto, removed and merged.
StateType => ['open', 'new'],
StateTypeIDs => [1, 2, 3],
Priorities => ['1 very low', '2 low', '3 normal'],
PriorityIDs => [1, 2, 3],
Services => ['Service A', 'Service B'],
ServiceIDs => [1, 2, 3],
SLAs => ['SLA A', 'SLA B'],
SLAIDs => [1, 2, 3],
Locks => ['unlock'],
LockIDs => [1, 2, 3],
OwnerIDs => [1, 12, 455, 32]
ResponsibleIDs => [1, 12, 455, 32]
WatchUserIDs => [1, 12, 455, 32]
# CustomerID (optional) as STRING or as ARRAYREF
CustomerID => '123',
CustomerID => ['123', 'ABC'],
# CustomerUserLogin (optional) as STRING as ARRAYREF
CustomerUserLogin => 'uid123',
CustomerUserLogin => ['uid123', 'uid777'],
# create ticket properties (optional)
CreatedUserIDs => [1, 12, 455, 32]
CreatedTypes => ['normal', 'change', 'incident'],
CreatedTypeIDs => [1, 2, 3],
CreatedPriorities => ['1 very low', '2 low', '3 normal'],
CreatedPriorityIDs => [1, 2, 3],
CreatedStates => ['new', 'open'],
CreatedStateIDs => [3, 4],
CreatedQueues => ['system queue', 'other queue'],
CreatedQueueIDs => [1, 42, 512],
# DynamicFields
# At least one operator must be specified. Operators will be connected with AND,
# values in an operator with OR.
# You can also pass more than one argument to an operator: ['value1', 'value2']
DynamicField_FieldNameX => {
Empty => 1, # will return dynamic fields without a value
# set to 0 to search fields with a value present.
Equals => 123,
Like => 'value*', # "equals" operator with wildcard support
GreaterThan => '2001-01-01 01:01:01',
GreaterThanEquals => '2001-01-01 01:01:01',
SmallerThan => '2002-02-02 02:02:02',
SmallerThanEquals => '2002-02-02 02:02:02',
},
# article stuff (optional)
MIMEBase_From => '%spam@example.com%',
MIMEBase_To => '%service@example.com%',
MIMEBase_Cc => '%client@example.com%',
MIMEBase_Subject => '%VIRUS 32%',
MIMEBase_Body => '%VIRUS 32%',
# attachment stuff (optional, applies only for ArticleStorageDB)
AttachmentName => '%anyfile.txt%',
# use full article text index if configured (optional, default off)
FullTextIndex => 1,
# article content search (AND or OR for From, To, Cc, Subject and Body) (optional)
ContentSearch => 'AND',
# content conditions for From,To,Cc,Subject,Body
# Title,CustomerID and CustomerUserLogin (all optional)
ConditionInline => 1,
# articles created more than 60 minutes ago (article older than 60 minutes) (optional)
ArticleCreateTimeOlderMinutes => 60,
# articles created less than 120 minutes ago (article newer than 60 minutes) (optional)
ArticleCreateTimeNewerMinutes => 120,
# articles with create time after ... (article newer than this date) (optional)
ArticleCreateTimeNewerDate => '2006-01-09 00:00:01',
# articles with created time before ... (article older than this date) (optional)
ArticleCreateTimeOlderDate => '2006-01-19 23:59:59',
# tickets created more than 60 minutes ago (ticket older than 60 minutes) (optional)
TicketCreateTimeOlderMinutes => 60,
# tickets created less than 120 minutes ago (ticket newer than 120 minutes) (optional)
TicketCreateTimeNewerMinutes => 120,
# tickets with create time after ... (ticket newer than this date) (optional)
TicketCreateTimeNewerDate => '2006-01-09 00:00:01',
# tickets with created time before ... (ticket older than this date) (optional)
TicketCreateTimeOlderDate => '2006-01-19 23:59:59',
# ticket history entries that created more than 60 minutes ago (optional)
TicketChangeTimeOlderMinutes => 60,
# ticket history entries that created less than 120 minutes ago (optional)
TicketChangeTimeNewerMinutes => 120,
# tickets changed more than 60 minutes ago (optional)
TicketLastChangeTimeOlderMinutes => 60,
# tickets changed less than 120 minutes ago (optional)
TicketLastChangeTimeNewerMinutes => 120,
# tickets with changed time after ... (ticket changed newer than this date) (optional)
TicketLastChangeTimeNewerDate => '2006-01-09 00:00:01',
# tickets with changed time before ... (ticket changed older than this date) (optional)
TicketLastChangeTimeOlderDate => '2006-01-19 23:59:59',
# ticket history entry create time after ... (ticket history entries newer than this date) (optional)
TicketChangeTimeNewerDate => '2006-01-09 00:00:01',
# ticket history entry create time before ... (ticket history entries older than this date) (optional)
TicketChangeTimeOlderDate => '2006-01-19 23:59:59',
# tickets closed more than 60 minutes ago (optional)
TicketCloseTimeOlderMinutes => 60,
# tickets closed less than 120 minutes ago (optional)
TicketCloseTimeNewerMinutes => 120,
# tickets with closed time after ... (ticket closed newer than this date) (optional)
TicketCloseTimeNewerDate => '2006-01-09 00:00:01',
# tickets with closed time before ... (ticket closed older than this date) (optional)
TicketCloseTimeOlderDate => '2006-01-19 23:59:59',
# tickets with pending time of more than 60 minutes ago (optional)
TicketPendingTimeOlderMinutes => 60,
# tickets with pending time of less than 120 minutes ago (optional)
TicketPendingTimeNewerMinutes => 120,
# tickets with pending time after ... (optional)
TicketPendingTimeNewerDate => '2006-01-09 00:00:01',
# tickets with pending time before ... (optional)
TicketPendingTimeOlderDate => '2006-01-19 23:59:59',
# you can use all following escalation options with this four different ways of escalations
# TicketEscalationTime...
# TicketEscalationUpdateTime...
# TicketEscalationResponseTime...
# TicketEscalationSolutionTime...
# ticket escalation time of more than 60 minutes ago (optional)
TicketEscalationTimeOlderMinutes => -60,
# ticket escalation time of less than 120 minutes ago (optional)
TicketEscalationTimeNewerMinutes => -120,
# tickets with escalation time after ... (optional)
TicketEscalationTimeNewerDate => '2006-01-09 00:00:01',
# tickets with escalation time before ... (optional)
TicketEscalationTimeOlderDate => '2006-01-09 23:59:59',
# search in archive (optional, default is not to search in archived tickets)
SearchInArchive => 'AllTickets', # 'AllTickets' (normal and archived) or 'ArchivedTickets' (only archived)
# OrderBy and SortBy (optional)
OrderBy => 'Down', # Down|Up
SortBy => 'Age', # Owner|Responsible|CustomerID|State|TicketNumber|Queue|Priority|Age|Type|Lock
# Changed|Title|Service|SLA|PendingTime|EscalationTime
# EscalationUpdateTime|EscalationResponseTime|EscalationSolutionTime
# DynamicField_FieldNameX
# TicketFreeTime1-6|TicketFreeKey1-16|TicketFreeText1-16
# OrderBy and SortBy as ARRAY for sub sorting (optional)
OrderBy => ['Down', 'Up'],
SortBy => ['Priority', 'Age'],
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
TicketID => [ 1, 2, 3, 4 ],
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
my $Result = $Self->Init(
WebserviceID => $Self->{WebserviceID},
);
if ( !$Result->{Success} ) {
$Self->ReturnError(
ErrorCode => 'Webservice.InvalidConfiguration',
ErrorMessage => $Result->{ErrorMessage},
);
}
my ( $UserID, $UserType ) = $Self->Auth(
%Param,
);
return $Self->ReturnError(
ErrorCode => 'TicketSearch.AuthFail',
ErrorMessage => "TicketSearch: Authorization failing!",
) if !$UserID;
# all needed variables
$Self->{SearchLimit} = $Param{Data}->{Limit}
|| $Self->{Config}->{SearchLimit}
|| 500;
$Self->{SortBy} = $Param{Data}->{SortBy}
|| $Self->{Config}->{'SortBy::Default'}
|| 'Age';
$Self->{OrderBy} = $Param{Data}->{OrderBy}
|| $Self->{Config}->{'Order::Default'}
|| 'Down';
$Self->{FullTextIndex} = $Param{Data}->{FullTextIndex} || 0;
# get parameter from data
my %GetParam = $Self->_GetParams( %{ $Param{Data} } );
# create time settings
%GetParam = $Self->_CreateTimeSettings(%GetParam);
# get dynamic fields
my %DynamicFieldSearchParameters = $Self->_GetDynamicFields( %{ $Param{Data} } );
# perform ticket search
$UserType = ( $UserType eq 'Customer' ) ? 'CustomerUserID' : 'UserID';
my @TicketIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
%GetParam,
%DynamicFieldSearchParameters,
Result => 'ARRAY',
SortBy => $Self->{SortBy},
OrderBy => $Self->{OrderBy},
Limit => $Self->{SearchLimit},
$UserType => $UserID,
ConditionInline => $Self->{Config}->{ExtendedSearchCondition},
ContentSearchPrefix => '*',
ContentSearchSuffix => '*',
FullTextIndex => $Self->{FullTextIndex},
);
if (@TicketIDs) {
return {
Success => 1,
Data => {
TicketID => \@TicketIDs,
},
};
}
# return result
return {
Success => 1,
Data => {},
};
}
=begin Internal:
=head2 _GetParams()
get search parameters.
my %GetParam = _GetParams(
%Params, # all ticket parameters
);
returns:
%GetParam = {
AllowedParams => 'WithContent', # return not empty parameters for search
}
=cut
sub _GetParams {
my ( $Self, %Param ) = @_;
# get single params
my %GetParam;
my %SearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList();
for my $Item (
sort keys %SearchableFields,
qw(
Agent ResultForm TimeSearchType ChangeTimeSearchType LastChangeTimeSearchType CloseTimeSearchType UseSubQueues
ArticleTimeSearchType SearchInArchive
Fulltext ContentSearch ShownAttributes
)
)
{
# get search string params (get submitted params)
if ( IsStringWithData( $Param{$Item} ) ) {
$GetParam{$Item} = $Param{$Item};
# remove white space on the start and end
$GetParam{$Item} =~ s/\s+$//g;
$GetParam{$Item} =~ s/^\s+//g;
}
}
# get array params
for my $Item (
qw(TicketNumber TicketID Title
StateIDs StateTypeIDs QueueIDs PriorityIDs OwnerIDs
CreatedUserIDs WatchUserIDs ResponsibleIDs
TypeIDs ServiceIDs SLAIDs LockIDs Queues Types States
Priorities Services SLAs Locks
CreatedTypes CreatedTypeIDs CreatedPriorities
CreatedPriorityIDs CreatedStates CreatedStateIDs
CreatedQueues CreatedQueueIDs StateType CustomerID
CustomerUserLogin )
)
{
# get search array params
my @Values;
if ( IsArrayRefWithData( $Param{$Item} ) ) {
@Values = @{ $Param{$Item} };
}
elsif ( IsStringWithData( $Param{$Item} ) ) {
@Values = ( $Param{$Item} );
}
$GetParam{$Item} = \@Values if scalar @Values;
}
# get escalation times
my %EscalationTimes = (
1 => '',
2 => 'Update',
3 => 'Response',
4 => 'Solution',
);
for my $Index ( sort keys %EscalationTimes ) {
for my $PostFix (qw( OlderMinutes NewerMinutes NewerDate OlderDate )) {
my $Item = 'TicketEscalation' . $EscalationTimes{$Index} . 'Time' . $PostFix;
# get search string params (get submitted params)
if ( IsStringWithData( $Param{$Item} ) ) {
$GetParam{$Item} = $Param{$Item};
# remove white space on the start and end
$GetParam{$Item} =~ s/\s+$//g;
$GetParam{$Item} =~ s/^\s+//g;
}
}
}
my @Prefixes = (
'TicketCreateTime',
'TicketChangeTime',
'TicketLastChangeTime',
'TicketCloseTime',
'TicketPendingTime',
'ArticleCreateTime',
);
my @Postfixes = (
'Point',
'PointFormat',
'PointStart',
'Start',
'StartDay',
'StartMonth',
'StartYear',
'Stop',
'StopDay',
'StopMonth',
'StopYear',
'OlderMinutes',
'NewerMinutes',
'OlderDate',
'NewerDate',
);
for my $Prefix (@Prefixes) {
# get search string params (get submitted params)
if ( IsStringWithData( $Param{$Prefix} ) ) {
$GetParam{$Prefix} = $Param{$Prefix};
# remove white space on the start and end
$GetParam{$Prefix} =~ s/\s+$//g;
$GetParam{$Prefix} =~ s/^\s+//g;
}
for my $Postfix (@Postfixes) {
my $Item = $Prefix . $Postfix;
# get search string params (get submitted params)
if ( IsStringWithData( $Param{$Item} ) ) {
$GetParam{$Item} = $Param{$Item};
# remove white space on the start and end
$GetParam{$Item} =~ s/\s+$//g;
$GetParam{$Item} =~ s/^\s+//g;
}
}
}
return %GetParam;
}
=head2 _GetDynamicFields()
get search parameters.
my %DynamicFieldSearchParameters = _GetDynamicFields(
%Params, # all ticket parameters
);
returns:
%DynamicFieldSearchParameters = {
'AllAllowedDF' => 'WithData', # return not empty parameters for search
}
=cut
sub _GetDynamicFields {
my ( $Self, %Param ) = @_;
# dynamic fields search parameters for ticket search
my %DynamicFieldSearchParameters;
# get single params
my %AttributeLookup;
# get the dynamic fields for ticket object
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
);
my %DynamicFieldsRaw;
if ( $Param{DynamicField} ) {
my %SearchParams;
if ( IsHashRefWithData( $Param{DynamicField} ) ) {
$DynamicFieldsRaw{ $Param{DynamicField}->{Name} } = $Param{DynamicField};
}
elsif ( IsArrayRefWithData( $Param{DynamicField} ) ) {
%DynamicFieldsRaw = map { $_->{Name} => $_ } @{ $Param{DynamicField} };
}
else {
return %DynamicFieldSearchParameters;
}
}
else {
# Compatibility with older versions of the web service.
for my $ParameterName ( sort keys %Param ) {
if ( $ParameterName =~ m{\A DynamicField_ ( [a-zA-Z\d]+ ) \z}xms ) {
$DynamicFieldsRaw{$1} = $Param{$ParameterName};
}
}
}
# loop over the dynamic fields configured
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
# skip all fields that does not match with current field name
next DYNAMICFIELD if !$DynamicFieldsRaw{ $DynamicFieldConfig->{Name} };
next DYNAMICFIELD if !IsHashRefWithData( $DynamicFieldsRaw{ $DynamicFieldConfig->{Name} } );
my %SearchOperators = %{ $DynamicFieldsRaw{ $DynamicFieldConfig->{Name} } };
delete $SearchOperators{Name};
# set search parameter
$DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = \%SearchOperators;
}
# allow free fields
return %DynamicFieldSearchParameters;
}
=head2 _CreateTimeSettings()
get search parameters.
my %GetParam = _CreateTimeSettings(
%Params, # all ticket parameters
);
returns:
%GetParam = {
AllowedTimeSettings => 'WithData', # return not empty parameters for search
}
=cut
sub _CreateTimeSettings {
my ( $Self, %Param ) = @_;
# get single params
my %GetParam = %Param;
# get change time settings
if ( !$GetParam{ChangeTimeSearchType} ) {
# do nothing on time stuff
}
elsif ( $GetParam{ChangeTimeSearchType} eq 'TimeSlot' ) {
for (qw(Month Day)) {
$GetParam{"TicketChangeTimeStart$_"} = sprintf( "%02d", $GetParam{"TicketChangeTimeStart$_"} );
}
for (qw(Month Day)) {
$GetParam{"TicketChangeTimeStop$_"} = sprintf( "%02d", $GetParam{"TicketChangeTimeStop$_"} );
}
if (
$GetParam{TicketChangeTimeStartDay}
&& $GetParam{TicketChangeTimeStartMonth}
&& $GetParam{TicketChangeTimeStartYear}
)
{
$GetParam{TicketChangeTimeNewerDate} = $GetParam{TicketChangeTimeStartYear} . '-'
. $GetParam{TicketChangeTimeStartMonth} . '-'
. $GetParam{TicketChangeTimeStartDay}
. ' 00:00:00';
}
if (
$GetParam{TicketChangeTimeStopDay}
&& $GetParam{TicketChangeTimeStopMonth}
&& $GetParam{TicketChangeTimeStopYear}
)
{
$GetParam{TicketChangeTimeOlderDate} = $GetParam{TicketChangeTimeStopYear} . '-'
. $GetParam{TicketChangeTimeStopMonth} . '-'
. $GetParam{TicketChangeTimeStopDay}
. ' 23:59:59';
}
}
elsif ( $GetParam{ChangeTimeSearchType} eq 'TimePoint' ) {
if (
$GetParam{TicketChangeTimePoint}
&& $GetParam{TicketChangeTimePointStart}
&& $GetParam{TicketChangeTimePointFormat}
)
{
my $Time = 0;
if ( $GetParam{TicketChangeTimePointFormat} eq 'minute' ) {
$Time = $GetParam{TicketChangeTimePoint};
}
elsif ( $GetParam{TicketChangeTimePointFormat} eq 'hour' ) {
$Time = $GetParam{TicketChangeTimePoint} * 60;
}
elsif ( $GetParam{TicketChangeTimePointFormat} eq 'day' ) {
$Time = $GetParam{TicketChangeTimePoint} * 60 * 24;
}
elsif ( $GetParam{TicketChangeTimePointFormat} eq 'week' ) {
$Time = $GetParam{TicketChangeTimePoint} * 60 * 24 * 7;
}
elsif ( $GetParam{TicketChangeTimePointFormat} eq 'month' ) {
$Time = $GetParam{TicketChangeTimePoint} * 60 * 24 * 30;
}
elsif ( $GetParam{TicketChangeTimePointFormat} eq 'year' ) {
$Time = $GetParam{TicketChangeTimePoint} * 60 * 24 * 365;
}
if ( $GetParam{TicketChangeTimePointStart} eq 'Before' ) {
$GetParam{TicketChangeTimeOlderMinutes} = $Time;
}
else {
$GetParam{TicketChangeTimeNewerMinutes} = $Time;
}
}
}
# get last change time settings
if ( !$GetParam{LastChangeTimeSearchType} ) {
# do nothing on time stuff
}
elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimeSlot' ) {
for (qw(Month Day)) {
$GetParam{"TicketLastChangeTimeStart$_"} = sprintf( "%02d", $GetParam{"TicketLastChangeTimeStart$_"} );
}
for (qw(Month Day)) {
$GetParam{"TicketLastChangeTimeStop$_"} = sprintf( "%02d", $GetParam{"TicketLastChangeTimeStop$_"} );
}
if (
$GetParam{TicketLastChangeTimeStartDay}
&& $GetParam{TicketLastChangeTimeStartMonth}
&& $GetParam{TicketLastChangeTimeStartYear}
)
{
$GetParam{TicketLastChangeTimeNewerDate} = $GetParam{TicketLastChangeTimeStartYear} . '-'
. $GetParam{TicketLastChangeTimeStartMonth} . '-'
. $GetParam{TicketLastChangeTimeStartDay}
. ' 00:00:00';
}
if (
$GetParam{TicketLastChangeTimeStopDay}
&& $GetParam{TicketLastChangeTimeStopMonth}
&& $GetParam{TicketLastChangeTimeStopYear}
)
{
$GetParam{TicketLastChangeTimeOlderDate} = $GetParam{TicketLastChangeTimeStopYear} . '-'
. $GetParam{TicketLastChangeTimeStopMonth} . '-'
. $GetParam{TicketLastChangeTimeStopDay}
. ' 23:59:59';
}
}
elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimePoint' ) {
if (
$GetParam{TicketLastChangeTimePoint}
&& $GetParam{TicketLastChangeTimePointStart}
&& $GetParam{TicketLastChangeTimePointFormat}
)
{
my $Time = 0;
if ( $GetParam{TicketLastChangeTimePointFormat} eq 'minute' ) {
$Time = $GetParam{TicketLastChangeTimePoint};
}
elsif ( $GetParam{TicketLastChangeTimePointFormat} eq 'hour' ) {
$Time = $GetParam{TicketLastChangeTimePoint} * 60;
}
elsif ( $GetParam{TicketLastChangeTimePointFormat} eq 'day' ) {
$Time = $GetParam{TicketLastChangeTimePoint} * 60 * 24;
}
elsif ( $GetParam{TicketLastChangeTimePointFormat} eq 'week' ) {
$Time = $GetParam{TicketLastChangeTimePoint} * 60 * 24 * 7;
}
elsif ( $GetParam{TicketLastChangeTimePointFormat} eq 'month' ) {
$Time = $GetParam{TicketLastChangeTimePoint} * 60 * 24 * 30;
}
elsif ( $GetParam{TicketLastChangeTimePointFormat} eq 'year' ) {
$Time = $GetParam{TicketLastChangeTimePoint} * 60 * 24 * 365;
}
if ( $GetParam{TicketLastChangeTimePointStart} eq 'Before' ) {
$GetParam{TicketLastChangeTimeOlderMinutes} = $Time;
}
else {
$GetParam{TicketLastChangeTimeNewerMinutes} = $Time;
}
}
}
# get close time settings
if ( !$GetParam{CloseTimeSearchType} ) {
# do nothing on time stuff
}
elsif ( $GetParam{CloseTimeSearchType} eq 'TimeSlot' ) {
for (qw(Month Day)) {
$GetParam{"TicketCloseTimeStart$_"} = sprintf( "%02d", $GetParam{"TicketCloseTimeStart$_"} );
}
for (qw(Month Day)) {
$GetParam{"TicketCloseTimeStop$_"} = sprintf( "%02d", $GetParam{"TicketCloseTimeStop$_"} );
}
if (
$GetParam{TicketCloseTimeStartDay}
&& $GetParam{TicketCloseTimeStartMonth}
&& $GetParam{TicketCloseTimeStartYear}
)
{
$GetParam{TicketCloseTimeNewerDate} = $GetParam{TicketCloseTimeStartYear} . '-'
. $GetParam{TicketCloseTimeStartMonth} . '-'
. $GetParam{TicketCloseTimeStartDay}
. ' 00:00:00';
}
if (
$GetParam{TicketCloseTimeStopDay}
&& $GetParam{TicketCloseTimeStopMonth}
&& $GetParam{TicketCloseTimeStopYear}
)
{
$GetParam{TicketCloseTimeOlderDate} = $GetParam{TicketCloseTimeStopYear} . '-'
. $GetParam{TicketCloseTimeStopMonth} . '-'
. $GetParam{TicketCloseTimeStopDay}
. ' 23:59:59';
}
}
elsif ( $GetParam{CloseTimeSearchType} eq 'TimePoint' ) {
if (
$GetParam{TicketCloseTimePoint}
&& $GetParam{TicketCloseTimePointStart}
&& $GetParam{TicketCloseTimePointFormat}
)
{
my $Time = 0;
if ( $GetParam{TicketCloseTimePointFormat} eq 'minute' ) {
$Time = $GetParam{TicketCloseTimePoint};
}
elsif ( $GetParam{TicketCloseTimePointFormat} eq 'hour' ) {
$Time = $GetParam{TicketCloseTimePoint} * 60;
}
elsif ( $GetParam{TicketCloseTimePointFormat} eq 'day' ) {
$Time = $GetParam{TicketCloseTimePoint} * 60 * 24;
}
elsif ( $GetParam{TicketCloseTimePointFormat} eq 'week' ) {
$Time = $GetParam{TicketCloseTimePoint} * 60 * 24 * 7;
}
elsif ( $GetParam{TicketCloseTimePointFormat} eq 'month' ) {
$Time = $GetParam{TicketCloseTimePoint} * 60 * 24 * 30;
}
elsif ( $GetParam{TicketCloseTimePointFormat} eq 'year' ) {
$Time = $GetParam{TicketCloseTimePoint} * 60 * 24 * 365;
}
if ( $GetParam{TicketCloseTimePointStart} eq 'Before' ) {
$GetParam{TicketCloseTimeOlderMinutes} = $Time;
}
else {
$GetParam{TicketCloseTimeNewerMinutes} = $Time;
}
}
}
# prepare full text search
if ( $GetParam{Fulltext} ) {
$GetParam{ContentSearch} = 'OR';
for (qw(From To Cc Subject Body)) {
$GetParam{$_} = $GetParam{Fulltext};
}
}
# prepare archive flag
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ArchiveSystem') ) {
$GetParam{SearchInArchive} ||= '';
if ( $GetParam{SearchInArchive} eq 'AllTickets' ) {
$GetParam{ArchiveFlags} = [ 'y', 'n' ];
}
elsif ( $GetParam{SearchInArchive} eq 'ArchivedTickets' ) {
$GetParam{ArchiveFlags} = ['y'];
}
else {
$GetParam{ArchiveFlags} = ['n'];
}
}
return %GetParam;
}
=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
1;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,591 @@
# --
# 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::Provider;
use strict;
use warnings;
use URI::Escape;
use Storable;
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Transport;
use Kernel::GenericInterface::Mapping;
use Kernel::GenericInterface::Operation;
use Kernel::System::GenericInterface::Webservice;
use Kernel::System::VariableCheck qw(IsHashRefWithData);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::GenericInterface::Webservice',
'Kernel::GenericInterface::ErrorHandling',
);
=head1 NAME
Kernel::GenericInterface::Provider - handler for incoming web service requests.
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $ProviderObject = $Kernel::OM->Get('Kernel::GenericInterface::Provider');
=cut
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=head2 Run()
Receives the current incoming web service request, handles it,
and returns an appropriate answer based on the requested web service.
# put this in the handler script
$ProviderObject->Run();
=cut
sub Run {
my ( $Self, %Param ) = @_;
# On Microsoft IIS 7.0, $ENV{REQUEST_URI} is not set. See bug#9172.
my $RequestURI = $ENV{REQUEST_URI} || $ENV{PATH_INFO};
#
# Locate and verify the desired web service based on the request URI and load its configuration data.
#
# Check RequestURI for a web service by id or name.
my %WebserviceGetData;
if (
$RequestURI
&& $RequestURI
=~ m{ nph-genericinterface[.]pl/ (?: WebserviceID/ (?<ID> \d+ ) | Webservice/ (?<Name> [^/?]+ ) ) }smx
)
{
%WebserviceGetData = (
ID => $+{ID},
Name => $+{Name} ? URI::Escape::uri_unescape( $+{Name} ) : undef,
);
}
# URI is empty or invalid.
if ( !%WebserviceGetData ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Could not determine WebserviceID or Webservice from query string '$RequestURI'",
);
return; # bail out without Transport, Apache will generate 500 Error
}
# Check if requested web service exists and is valid.
my $WebserviceObject = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice');
my $WebserviceList = $WebserviceObject->WebserviceList();
my %WebserviceListReverse = reverse %{$WebserviceList};
if (
$WebserviceGetData{Name} && !$WebserviceListReverse{ $WebserviceGetData{Name} }
|| $WebserviceGetData{ID} && !$WebserviceList->{ $WebserviceGetData{ID} }
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Could not find valid web service for query string '$RequestURI'",
);
return; # bail out without Transport, Apache will generate 500 Error
}
my $Webservice = $WebserviceObject->WebserviceGet(%WebserviceGetData);
if ( !IsHashRefWithData($Webservice) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Could not load web service configuration for query string '$RequestURI'",
);
return; # bail out without Transport, Apache will generate 500 Error
}
# Create a debugger instance which will log the details of this communication entry.
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => $Webservice->{Config}->{Debugger},
WebserviceID => $Webservice->{ID},
CommunicationType => 'Provider',
RemoteIP => $ENV{REMOTE_ADDR},
);
if ( ref $DebuggerObject ne 'Kernel::GenericInterface::Debugger' ) {
return; # bail out without Transport, Apache will generate 500 Error
}
$DebuggerObject->Debug(
Summary => 'Communication sequence started',
Data => \%ENV,
);
#
# Create the network transport backend and read the network request.
#
my $ProviderConfig = $Webservice->{Config}->{Provider};
$Self->{TransportObject} = Kernel::GenericInterface::Transport->new(
DebuggerObject => $DebuggerObject,
TransportConfig => $ProviderConfig->{Transport},
);
# Bail out if transport initialization failed.
if ( ref $Self->{TransportObject} ne 'Kernel::GenericInterface::Transport' ) {
return $DebuggerObject->Error(
Summary => 'TransportObject could not be initialized',
Data => $Self->{TransportObject},
);
}
# Combine all data for error handler we got so far.
my %HandleErrorData = (
DebuggerObject => $DebuggerObject,
WebserviceID => $Webservice->{ID},
WebserviceConfig => $Webservice->{Config},
);
# Read request content.
my $FunctionResult = $Self->{TransportObject}->ProviderProcessRequest();
# If the request was not processed correctly, send error to client.
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'TransportObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => {},
ErrorStage => 'ProviderRequestReceive',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# prepare the data include configuration and payload
my %DataInclude = (
ProviderRequestInput => $FunctionResult->{Data},
);
my $Operation = $FunctionResult->{Operation};
$DebuggerObject->Debug(
Summary => "Detected operation '$Operation'",
);
#
# Map the incoming data based on the configured mapping.
#
my $DataIn = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Incoming data before mapping",
Data => $DataIn,
);
# Decide if mapping needs to be used or not.
if (
IsHashRefWithData( $ProviderConfig->{Operation}->{$Operation}->{MappingInbound} )
)
{
my $MappingInObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $DebuggerObject,
Operation => $Operation,
OperationType => $ProviderConfig->{Operation}->{$Operation}->{Type},
MappingConfig =>
$ProviderConfig->{Operation}->{$Operation}->{MappingInbound},
);
# If mapping initialization failed, bail out.
if ( ref $MappingInObject ne 'Kernel::GenericInterface::Mapping' ) {
$DebuggerObject->Error(
Summary => 'MappingIn could not be initialized',
Data => $MappingInObject,
);
return $Self->_GenerateErrorResponse(
DebuggerObject => $DebuggerObject,
ErrorMessage => $FunctionResult->{ErrorMessage},
);
}
# add operation to data for error handler
$HandleErrorData{Operation} = $Operation;
$FunctionResult = $MappingInObject->Map(
Data => $DataIn,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'MappingInObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'ProviderRequestMap',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# extend the data include payload
$DataInclude{ProviderRequestMapOutput} = $FunctionResult->{Data};
$DataIn = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Incoming data after mapping",
Data => $DataIn,
);
}
#
# Execute actual operation.
#
my $OperationObject = Kernel::GenericInterface::Operation->new(
DebuggerObject => $DebuggerObject,
Operation => $Operation,
OperationType => $ProviderConfig->{Operation}->{$Operation}->{Type},
WebserviceID => $Webservice->{ID},
);
# If operation initialization failed, bail out.
if ( ref $OperationObject ne 'Kernel::GenericInterface::Operation' ) {
$DebuggerObject->Error(
Summary => 'Operation could not be initialized',
Data => $OperationObject,
);
# Set default error message.
my $ErrorMessage = 'Unknown error in Operation initialization';
# Check if we got an error message from the operation and overwrite it.
if ( IsHashRefWithData($OperationObject) && $OperationObject->{ErrorMessage} ) {
$ErrorMessage = $OperationObject->{ErrorMessage};
}
return $Self->_GenerateErrorResponse(
DebuggerObject => $DebuggerObject,
ErrorMessage => $ErrorMessage,
);
}
# add operation object to data for error handler
$HandleErrorData{OperationObject} = $OperationObject;
$FunctionResult = $OperationObject->Run(
Data => $DataIn,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'OperationObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'ProviderRequestProcess',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# extend the data include payload
$DataInclude{ProviderResponseInput} = $FunctionResult->{Data};
#
# Map the outgoing data based on configured mapping.
#
my $DataOut = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Outgoing data before mapping",
Data => $DataOut,
);
# Decide if mapping needs to be used or not.
if (
IsHashRefWithData(
$ProviderConfig->{Operation}->{$Operation}->{MappingOutbound}
)
)
{
my $MappingOutObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $DebuggerObject,
Operation => $Operation,
OperationType => $ProviderConfig->{Operation}->{$Operation}->{Type},
MappingConfig =>
$ProviderConfig->{Operation}->{$Operation}->{MappingOutbound},
);
# If mapping initialization failed, bail out
if ( ref $MappingOutObject ne 'Kernel::GenericInterface::Mapping' ) {
$DebuggerObject->Error(
Summary => 'MappingOut could not be initialized',
Data => $MappingOutObject,
);
return $Self->_GenerateErrorResponse(
DebuggerObject => $DebuggerObject,
ErrorMessage => $FunctionResult->{ErrorMessage},
);
}
$FunctionResult = $MappingOutObject->Map(
Data => $DataOut,
DataInclude => \%DataInclude,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'MappingOutObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'ProviderResponseMap',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# extend the data include payload
$DataInclude{ProviderResponseMapOutput} = $FunctionResult->{Data};
$DataOut = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Outgoing data after mapping",
Data => $DataOut,
);
}
#
# Generate the actual response.
#
$FunctionResult = $Self->{TransportObject}->ProviderGenerateResponse(
Success => 1,
Data => $DataOut,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'TransportObject returned an error, cancelling Request';
$Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'ProviderResponseTransmit',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
return;
}
=begin Internal:
=head2 _GenerateErrorResponse()
returns an error message to the client.
$ProviderObject->_GenerateErrorResponse(
ErrorMessage => $ErrorMessage,
);
=cut
sub _GenerateErrorResponse {
my ( $Self, %Param ) = @_;
my $FunctionResult = $Self->{TransportObject}->ProviderGenerateResponse(
Success => 0,
ErrorMessage => $Param{ErrorMessage},
);
if ( !$FunctionResult->{Success} ) {
$Param{DebuggerObject}->Error(
Summary => 'Error response could not be sent',
Data => $FunctionResult->{ErrorMessage},
);
}
return;
}
=head2 _HandleError()
handles errors by
- informing operation about it (if supported)
- calling an error handling layer
my $ReturnData = $RequesterObject->_HandleError(
DebuggerObject => $DebuggerObject,
WebserviceID => 1,
WebserviceConfig => $WebserviceConfig,
DataInclude => $DataIncludeStructure,
ErrorStage => 'PrepareRequest', # at what point did the error occur?
Summary => 'an error occurred',
Data => $ErrorDataStructure,
OperationObject => $OperationObject, # optional
Operation => 'OperationName', # optional
);
my $ReturnData = {
Success => 0,
ErrorMessage => $Param{Summary},
};
=cut
sub _HandleError {
my ( $Self, %Param ) = @_;
NEEDED:
for my $Needed (qw(DebuggerObject WebserviceID WebserviceConfig DataInclude ErrorStage Summary Data)) {
next NEEDED if $Param{$Needed};
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Got no $Needed!",
);
return $Self->_GenerateErrorResponse(
DebuggerObject => $Param{DebuggerObject},
ErrorMessage => "Got no $Needed!",
);
}
my $ErrorHandlingResult = $Kernel::OM->Get('Kernel::GenericInterface::ErrorHandling')->HandleError(
WebserviceID => $Param{WebserviceID},
WebserviceConfig => $Param{WebserviceConfig},
CommunicationID => $Param{DebuggerObject}->{CommunicationID},
CommunicationType => 'Provider',
CommunicationName => $Param{Operation},
ErrorStage => $Param{ErrorStage},
Summary => $Param{Summary},
Data => $Param{Data},
);
if (
!$Param{Operation}
|| !$Param{OperationObject}
|| !$Param{OperationObject}->{BackendObject}->can('HandleError')
)
{
return $Self->_GenerateErrorResponse(
DebuggerObject => $Param{DebuggerObject},
ErrorMessage => $Param{Summary},
);
}
my $HandleErrorData;
if ( !defined $Param{Data} || IsString( $Param{Data} ) ) {
$HandleErrorData = $Param{Data} // '';
}
else {
$HandleErrorData = Storable::dclone( $Param{Data} );
}
$Param{DebuggerObject}->Debug(
Summary => 'Error data before mapping',
Data => $HandleErrorData,
);
my $OperationConfig = $Param{WebserviceConfig}->{Provider}->{Operation}->{ $Param{Operation} };
# TODO: use separate mapping config for errors.
if ( IsHashRefWithData( $OperationConfig->{MappingInbound} ) ) {
my $MappingErrorObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $Param{DebuggerObject},
Operation => $Param{Operation},
OperationType => $OperationConfig->{Type},
MappingConfig => $OperationConfig->{MappingInbound},
);
# If mapping init failed, bail out.
if ( ref $MappingErrorObject ne 'Kernel::GenericInterface::Mapping' ) {
$Param{DebuggerObject}->Error(
Summary => 'MappingErr could not be initialized',
Data => $MappingErrorObject,
);
return $Self->_GenerateErrorResponse(
DebuggerObject => $Param{DebuggerObject},
ErrorMessage => 'MappingErr could not be initialized',
);
}
# Map error data.
my $MappingErrorResult = $MappingErrorObject->Map(
Data => {
Fault => $HandleErrorData,
},
DataInclude => {
%{ $Param{DataInclude} },
ProviderErrorHandlingOutput => $ErrorHandlingResult->{Data},
},
);
if ( !$MappingErrorResult->{Success} ) {
return $Self->_GenerateErrorResponse(
DebuggerObject => $Param{DebuggerObject},
ErrorMessage => $MappingErrorResult->{ErrorMessage},
);
}
$HandleErrorData = $MappingErrorResult->{Data};
$Param{DebuggerObject}->Debug(
Summary => 'Error data after mapping',
Data => $HandleErrorData,
);
}
my $OperationHandleErrorOutput = $Param{OperationObject}->HandleError(
Data => $HandleErrorData,
);
if ( !$OperationHandleErrorOutput->{Success} ) {
$Param{DebuggerObject}->Error(
Summary => 'Error handling error data in Operation',
Data => $OperationHandleErrorOutput->{ErrorMessage},
);
}
return $Self->_GenerateErrorResponse(
DebuggerObject => $Param{DebuggerObject},
ErrorMessage => $Param{Summary},
);
}
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
@@ -0,0 +1,626 @@
# --
# 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::Requester;
use strict;
use warnings;
use Storable;
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Invoker;
use Kernel::GenericInterface::Mapping;
use Kernel::GenericInterface::Transport;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::GenericInterface::Webservice',
'Kernel::System::Log',
'Kernel::GenericInterface::ErrorHandling',
);
=head1 NAME
Kernel::GenericInterface::Requester - GenericInterface handler for sending web service requests to remote providers
=head1 PUBLIC INTERFACE
=head2 new()
create an object. Do not create it directly, instead use:
my $RequesterObject = $Kernel::OM->Get('Kernel::GenericInterface::Requester');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
=head2 Run()
receives the current incoming web service request, handles it,
and returns an appropriate answer based on the configured requested
web service.
my $Result = $RequesterObject->Run(
WebserviceID => 1, # ID of the configured remote web service to use OR
Invoker => 'some_operation', # Name of the Invoker to be used for sending the request
Asynchronous => 1, # Optional, 1 or 0, defaults to 0
Data => { # Data payload for the Invoker request (remote web service)
#...
},
PastExecutionData => { # Meta data containing information about previous request attempts, optional
#...
}
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # if an error occurred
Data => { # Data payload of Invoker result (web service response)
#...
},
};
in case of an error if the request has been made asynchronously it can be re-schedule in future if
the invoker returns the appropriate information
$Result = {
Success => 0, # 0 or 1
ErrorMessage => 'some error message',
Data => {
ReSchedule => 1,
ExecutionTime => '2015-01-01 00:00:00', # optional
},
};
=cut
sub Run {
my ( $Self, %Param ) = @_;
for my $Needed (qw(WebserviceID Invoker Data)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Got no $Needed!",
);
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
}
#
# Locate desired web service and load its configuration data.
#
my $WebserviceID = $Param{WebserviceID};
my $Webservice = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet(
ID => $WebserviceID,
);
if ( !IsHashRefWithData($Webservice) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Could not load web service configuration for web service $Param{WebserviceID}",
);
return {
Success => 0,
ErrorMessage =>
"Could not load web service configuration for web service $Param{WebserviceID}",
};
}
my $RequesterConfig = $Webservice->{Config}->{Requester};
#
# Create a debugger instance which will log the details of this
# communication entry.
#
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => $Webservice->{Config}->{Debugger},
WebserviceID => $WebserviceID,
CommunicationType => 'Requester',
);
if ( ref $DebuggerObject ne 'Kernel::GenericInterface::Debugger' ) {
return {
Success => 0,
ErrorMessage => "Could not initialize debugger",
};
}
$DebuggerObject->Debug(
Summary => 'Communication sequence started',
Data => $Param{Data},
);
#
# Create Invoker object and prepare the request on it.
#
$DebuggerObject->Debug(
Summary => "Using invoker '$Param{Invoker}'",
);
my $InvokerObject = Kernel::GenericInterface::Invoker->new(
DebuggerObject => $DebuggerObject,
Invoker => $Param{Invoker},
InvokerType => $RequesterConfig->{Invoker}->{ $Param{Invoker} }->{Type},
WebserviceID => $WebserviceID,
);
# Bail out if invoker initialization failed.
if ( ref $InvokerObject ne 'Kernel::GenericInterface::Invoker' ) {
return $DebuggerObject->Error(
Summary => 'InvokerObject could not be initialized',
Data => $InvokerObject,
);
}
# Prepare the data include configuration and payload.
my %DataInclude = (
RequesterRequestInput => $Param{Data},
);
# Combine all data for error handler we got so far.
my %HandleErrorData = (
InvokerObject => $InvokerObject,
Invoker => $Param{Invoker},
DebuggerObject => $DebuggerObject,
WebserviceID => $WebserviceID,
WebserviceConfig => $Webservice->{Config},
PastExecutionData => $Param{PastExecutionData},
);
my $FunctionResult = $InvokerObject->PrepareRequest(
Data => $Param{Data},
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'InvokerObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'RequesterRequestPrepare',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# Not always a success on the invoker prepare request means that invoker need to do something
# there are cases in which the requester does not need to do anything, for this cases
# StopCommunication can be sent. in this cases the request will be successful with out sending
# the request actually.
elsif ( $FunctionResult->{StopCommunication} && $FunctionResult->{StopCommunication} eq 1 ) {
return {
Success => 1,
};
}
# Extend the data include payload/
$DataInclude{RequesterRequestPrepareOutput} = $FunctionResult->{Data};
#
# Map the outgoing data.
#
my $DataOut = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Outgoing data before mapping",
Data => $DataOut,
);
# Decide if mapping needs to be used or not.
if (
IsHashRefWithData(
$RequesterConfig->{Invoker}->{ $Param{Invoker} }->{MappingOutbound}
)
)
{
my $MappingOutObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $DebuggerObject,
Invoker => $Param{Invoker},
InvokerType => $RequesterConfig->{Invoker}->{ $Param{Invoker} }->{Type},
MappingConfig =>
$RequesterConfig->{Invoker}->{ $Param{Invoker} }->{MappingOutbound},
);
# If mapping initialization failed, bail out.
if ( ref $MappingOutObject ne 'Kernel::GenericInterface::Mapping' ) {
$DebuggerObject->Error(
Summary => 'MappingOut could not be initialized',
Data => $MappingOutObject,
);
return $DebuggerObject->Error(
Summary => $FunctionResult->{ErrorMessage},
);
}
$FunctionResult = $MappingOutObject->Map(
Data => $DataOut,
DataInclude => \%DataInclude,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'MappingOutObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'RequesterRequestMap',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# Extend the data include payload.
$DataInclude{RequesterRequestMapOutput} = $FunctionResult->{Data};
$DataOut = $FunctionResult->{Data};
$DebuggerObject->Debug(
Summary => "Outgoing data after mapping",
Data => $DataOut,
);
}
my $TransportObject = Kernel::GenericInterface::Transport->new(
DebuggerObject => $DebuggerObject,
TransportConfig => $RequesterConfig->{Transport},
);
# Bail out if transport initialization failed.
if ( ref $TransportObject ne 'Kernel::GenericInterface::Transport' ) {
return $DebuggerObject->Error(
Summary => 'TransportObject could not be initialized',
Data => $TransportObject,
);
}
# Read request content.
$FunctionResult = $TransportObject->RequesterPerformRequest(
Operation => $Param{Invoker},
Data => $DataOut,
);
my $IsAsynchronousCall = $Param{Asynchronous} ? 1 : 0;
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'TransportObject returned an error, cancelling Request';
my $ErrorReturn = $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'RequesterRequestPerform',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
# Send error to Invoker.
my $Response = $InvokerObject->HandleResponse(
ResponseSuccess => 0,
ResponseErrorMessage => $FunctionResult->{ErrorMessage},
);
if ($IsAsynchronousCall) {
RESPONSEKEY:
for my $ResponseKey ( sort keys %{$Response} ) {
# Skip Success and ErrorMessage as they are set already.
next RESPONSEKEY if $ResponseKey eq 'Success';
next RESPONSEKEY if $ResponseKey eq 'ErrorMessage';
# Add any other key from the invoker HandleResponse() in Data.
$ErrorReturn->{$ResponseKey} = $Response->{$ResponseKey};
}
}
return $ErrorReturn;
}
# Extend the data include payload.
$DataInclude{RequesterResponseInput} = $FunctionResult->{Data};
my $DataIn = $FunctionResult->{Data};
my $SizeExeeded = $FunctionResult->{SizeExeeded} || 0;
if ($SizeExeeded) {
$DebuggerObject->Debug(
Summary => "Incoming data before mapping was too large for logging",
Data => 'See SysConfig option GenericInterface::Operation::ResponseLoggingMaxSize to change the maximum.',
);
}
else {
$DebuggerObject->Debug(
Summary => "Incoming data before mapping",
Data => $DataIn,
);
}
# Decide if mapping needs to be used or not.
if (
IsHashRefWithData(
$RequesterConfig->{Invoker}->{ $Param{Invoker} }->{MappingInbound}
)
)
{
my $MappingInObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $DebuggerObject,
Invoker => $Param{Invoker},
InvokerType => $RequesterConfig->{Invoker}->{ $Param{Invoker} }->{Type},
MappingConfig =>
$RequesterConfig->{Invoker}->{ $Param{Invoker} }->{MappingInbound},
);
# If mapping initialization failed, bail out.
if ( ref $MappingInObject ne 'Kernel::GenericInterface::Mapping' ) {
$DebuggerObject->Error(
Summary => 'MappingOut could not be initialized',
Data => $MappingInObject,
);
return $DebuggerObject->Error(
Summary => $FunctionResult->{ErrorMessage},
);
}
$FunctionResult = $MappingInObject->Map(
Data => $DataIn,
DataInclude => \%DataInclude,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'MappingInObject returned an error, cancelling Request';
return $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'RequesterResponseMap',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
}
# Extend the data include payload.
$DataInclude{RequesterResponseMapOutput} = $FunctionResult->{Data};
$DataIn = $FunctionResult->{Data};
if ($SizeExeeded) {
$DebuggerObject->Debug(
Summary => "Incoming data after mapping was too large for logging",
Data =>
'See SysConfig option GenericInterface::Operation::ResponseLoggingMaxSize to change the maximum.',
);
}
else {
$DebuggerObject->Debug(
Summary => "Incoming data after mapping",
Data => $DataIn,
);
}
}
#
# Handle response data in Invoker.
#
$FunctionResult = $InvokerObject->HandleResponse(
ResponseSuccess => 1,
Data => $DataIn,
);
if ( !$FunctionResult->{Success} ) {
my $Summary = $FunctionResult->{ErrorMessage} // 'InvokerObject returned an error, cancelling Request';
my $ErrorReturn = $Self->_HandleError(
%HandleErrorData,
DataInclude => \%DataInclude,
ErrorStage => 'RequesterResponseProcess',
Summary => $Summary,
Data => $FunctionResult->{Data} // $Summary,
);
if ($IsAsynchronousCall) {
RESPONSEKEY:
for my $ResponseKey ( sort keys %{$FunctionResult} ) {
# Skip Success and ErrorMessage as they are set already.
next RESPONSEKEY if $ResponseKey eq 'Success';
next RESPONSEKEY if $ResponseKey eq 'ErrorMessage';
# Add any other key from the invoker HandleResponse() in Data.
$ErrorReturn->{$ResponseKey} = $FunctionResult->{$ResponseKey};
}
}
return $ErrorReturn;
}
$DataIn = $FunctionResult->{Data};
return {
Success => 1,
Data => $DataIn,
};
}
=head2 _HandleError()
handles errors by
- informing invoker about it (if supported)
- calling an error handling layer
my $ReturnData = $RequesterObject->_HandleError(
InvokerObject => $InvokerObject,
Invoker => 'InvokerName',
DebuggerObject => $DebuggerObject,
WebserviceID => 1,
WebserviceConfig => $WebserviceConfig,
DataInclude => $DataIncludeStructure,
ErrorStage => 'PrepareRequest', # at what point did the error occur?
Summary => 'an error occurred',
Data => $ErrorDataStructure,
PastExecutionData => $PastExecutionDataStructure, # optional
);
my $ReturnData = {
Success => 0,
ErrorMessage => $Param{Summary},
};
=cut
sub _HandleError {
my ( $Self, %Param ) = @_;
NEEDED:
for my $Needed (
qw(InvokerObject Invoker DebuggerObject WebserviceID WebserviceConfig DataInclude ErrorStage Summary Data)
)
{
next NEEDED if $Param{$Needed};
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Got no $Needed!",
);
return {
Success => 0,
ErrorMessage => "Got no $Needed!",
};
}
my $ErrorHandlingResult = $Kernel::OM->Get('Kernel::GenericInterface::ErrorHandling')->HandleError(
WebserviceID => $Param{WebserviceID},
WebserviceConfig => $Param{WebserviceConfig},
CommunicationID => $Param{DebuggerObject}->{CommunicationID},
CommunicationType => 'Requester',
CommunicationName => $Param{Invoker},
ErrorStage => $Param{ErrorStage},
Summary => $Param{Summary},
Data => $Param{Data},
PastExecutionData => $Param{PastExecutionData},
);
my $ReturnData = {
Success => 0,
ErrorMessage => $ErrorHandlingResult->{ErrorMessage} || $Param{Summary},
Data => $ErrorHandlingResult->{ReScheduleData},
};
return $ReturnData if !$Param{InvokerObject}->{BackendObject}->can('HandleError');
my $HandleErrorData;
if ( !defined $Param{Data} || IsString( $Param{Data} ) ) {
$HandleErrorData = $Param{Data} // '';
}
else {
$HandleErrorData = Storable::dclone( $Param{Data} );
}
$Param{DebuggerObject}->Debug(
Summary => 'Error data before mapping',
Data => $HandleErrorData,
);
# TODO: Use separate mapping config for errors.
my $InvokerConfig = $Param{WebserviceConfig}->{Requester}->{Invoker}->{ $Param{Invoker} };
if ( IsHashRefWithData( $InvokerConfig->{MappingInbound} ) ) {
my $MappingErrorObject = Kernel::GenericInterface::Mapping->new(
DebuggerObject => $Param{DebuggerObject},
Invoker => $Param{Invoker},
InvokerType => $InvokerConfig->{Type},
MappingConfig => $InvokerConfig->{MappingInbound},
);
# If mapping init failed, bail out.
if ( ref $MappingErrorObject ne 'Kernel::GenericInterface::Mapping' ) {
$Param{DebuggerObject}->Error(
Summary => 'MappingErr could not be initialized',
Data => $MappingErrorObject,
);
return $ReturnData;
}
# Map error data.
my $MappingErrorResult = $MappingErrorObject->Map(
Data => {
Fault => $HandleErrorData,
},
DataInclude => {
%{ $Param{DataInclude} },
RequesterErrorHandlingOutput => $ErrorHandlingResult->{Data},
},
);
if ( !$MappingErrorResult->{Success} ) {
$Param{DebuggerObject}->Error(
Summary => $MappingErrorResult->{ErrorMessage},
);
return $ReturnData;
}
$HandleErrorData = $MappingErrorResult->{Data};
$Param{DebuggerObject}->Debug(
Summary => 'Error data after mapping',
Data => $HandleErrorData,
);
}
my $InvokerHandleErrorOutput = $Param{InvokerObject}->HandleError(
Data => $HandleErrorData,
);
if ( !$InvokerHandleErrorOutput->{Success} ) {
$Param{DebuggerObject}->Error(
Summary => 'Error handling error data in Invoker',
Data => $InvokerHandleErrorOutput->{ErrorMessage},
);
}
return $ReturnData;
}
1;
=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
@@ -0,0 +1,206 @@
# --
# 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::Transport;
use strict;
use warnings;
# prevent 'Used once' warning for Kernel::OM
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Transport - GenericInterface network transport interface
=head1 PUBLIC INTERFACE
=head2 new()
create an object.
use Kernel::GenericInterface::Debugger;
use Kernel::GenericInterface::Transport;
my $DebuggerObject = Kernel::GenericInterface::Debugger->new(
DebuggerConfig => {
DebugThreshold => 'debug',
TestMode => 0, # optional, in testing mode the data will not be written to the DB
# ...
},
WebserviceID => 12,
CommunicationType => Requester, # Requester or Provider
RemoteIP => 192.168.1.1, # optional
);
my $TransportObject = Kernel::GenericInterface::Transport->new(
DebuggerObject => $DebuggerObject,
TransportConfig => {
Type => 'HTTP::SOAP',
Config => {
...
},
},
);
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject TransportConfig)) {
$Self->{$Needed} = $Param{$Needed} || return {
Success => 0,
Summary => "Got no $Needed!",
};
}
# select and instantiate the backend
my $Backend = 'Kernel::GenericInterface::Transport::' . $Self->{TransportConfig}->{Type};
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($Backend) ) {
return $Self->{DebuggerObject}->Error( Summary => "Backend $Backend not found." );
}
$Self->{BackendObject} = $Backend->new( %{$Self} );
# if the backend constructor failed, it returns an error hash, pass it on in this case
return $Self->{BackendObject} if ref $Self->{BackendObject} ne $Backend;
return $Self;
}
=head2 ProviderProcessRequest()
process an incoming web service request. This function has to read the request data
from the web server process.
my $Result = $TransportObject->ProviderProcessRequest();
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Operation => 'DesiredOperation', # name of the operation to perform
Data => { # data payload of request
...
},
};
=cut
sub ProviderProcessRequest {
my ( $Self, %Param ) = @_;
my $Result = $Self->{BackendObject}->ProviderProcessRequest(%Param);
# make sure an operation is provided in success case
if ( $Result->{Success} && !$Result->{Operation} ) {
return $Self->{DebuggerObject}->Error(
Summary => 'TransportObject backend did not return an operation',
);
}
return $Result;
}
=head2 ProviderGenerateResponse()
generate response for an incoming web service request.
my $Result = $TransportObject->ProviderGenerateResponse(
Success => 1, # 1 or 0
ErrorMessage => '', # in case of an error, optional
Data => { # data payload for response, optional
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
};
=cut
sub ProviderGenerateResponse {
my ( $Self, %Param ) = @_;
if ( !defined $Param{Success} ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Missing parameter Success.',
);
}
if ( $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Data is not a hash reference.',
);
}
return $Self->{BackendObject}->ProviderGenerateResponse(%Param);
}
=head2 RequesterPerformRequest()
generate an outgoing web service request, receive the response and return its data..
my $Result = $TransportObject->RequesterPerformRequest(
Operation => 'remote_op', # name of remote operation to perform
Data => { # data payload for request
...
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # in case of error
Data => {
...
},
};
=cut
sub RequesterPerformRequest {
my ( $Self, %Param ) = @_;
if ( !$Param{Operation} ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Missing parameter Operation.',
);
}
if ( $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Data is not a hash reference.',
);
}
return $Self->{BackendObject}->RequesterPerformRequest(%Param);
}
1;
=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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,273 @@
# --
# 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::Transport::HTTP::Test;
use strict;
use warnings;
use HTTP::Request::Common;
use LWP::UserAgent;
use LWP::Protocol;
# prevent 'Used once' warning for Kernel::OM
use Kernel::System::ObjectManager;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Transport::HTTP::Test - GenericInterface network transport interface for testing purposes
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Transport->new();
use Kernel::GenericInterface::Transport;
my $TransportObject = Kernel::GenericInterface::Transport->new(
TransportConfig => {
Type => 'HTTP::Test',
Config => {
Fail => 0, # 0 or 1
},
},
);
In the config parameter 'Fail' you can tell the transport to simulate
failed network requests. If 'Fail' is set to 0, the transport will return
the query string of the requests as return data (see L<RequesterPerformRequest>
for an example);
=cut
sub new {
my ( $Type, %Param ) = @_;
my $Self = {};
bless( $Self, $Type );
for my $Needed (qw( DebuggerObject TransportConfig)) {
$Self->{$Needed} = $Param{$Needed} || return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
return $Self;
}
=head2 ProviderProcessRequest()
this will read the incoming HTTP request via CGI and
return the HTTP parameters in the data hash.
=cut
sub ProviderProcessRequest {
my ( $Self, %Param ) = @_;
if ( $Self->{TransportConfig}->{Config}->{Fail} ) {
return {
Success => 0,
ErrorMessage => "HTTP status code: 500",
Data => {},
};
}
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my %Result;
for my $ParamName ( $ParamObject->GetParamNames() ) {
$Result{$ParamName} = $ParamObject->GetParam( Param => $ParamName );
}
# special handling for empty post request
if ( scalar keys %Result == 1 && exists $Result{POSTDATA} && !$Result{POSTDATA} ) {
%Result = ();
}
if ( !%Result ) {
return $Self->{DebuggerObject}->Error(
Summary => 'No request data found.',
);
}
return {
Success => 1,
Data => \%Result,
Operation => 'test_operation',
};
}
=head2 ProviderGenerateResponse()
this will generate a query string from the passed data hash
and generate an HTTP response with this string as the body.
This response will be printed so that the web server will
send it to the client.
=cut
sub ProviderGenerateResponse {
my ( $Self, %Param ) = @_;
if ( $Self->{TransportConfig}->{Config}->{Fail} ) {
return {
Success => 0,
ErrorMessage => 'Test response generation failed',
};
}
my $Response;
if ( !$Param{Success} ) {
$Response = HTTP::Response->new( 500 => ( $Param{ErrorMessage} || 'Internal Server Error' ) );
$Response->protocol('HTTP/1.0');
$Response->content_type("text/plain; charset=UTF-8");
$Response->date(time);
}
else {
# generate a request string from the data
my $Request = HTTP::Request::Common::POST( 'http://testhost.local/', Content => $Param{Data} );
$Response = HTTP::Response->new( 200 => "OK" );
$Response->protocol('HTTP/1.0');
$Response->content_type("text/plain; charset=UTF-8");
$Response->add_content_utf8( $Request->content() );
$Response->date(time);
}
$Self->{DebuggerObject}->Debug(
Summary => 'Sending HTTP response',
Data => $Response->as_string(),
);
# now send response to client
print STDOUT $Response->as_string();
return {
Success => 1,
};
}
=head2 RequesterPerformRequest()
in Fail mode, returns error status. Otherwise, returns the
query string generated out of the data for the HTTP response.
my $Result = $TransportObject->RequesterPerformRequest(
Data => {
A => 'A',
b => 'b',
},
);
Returns
$Result = {
Success => 1,
Data => {
ResponseData => 'A=A&b=b',
},
};
=cut
sub RequesterPerformRequest {
my ( $Self, %Param ) = @_;
if ( $Self->{TransportConfig}->{Config}->{Fail} ) {
return {
Success => 0,
ErrorMessage => "HTTP status code: 500",
Data => {},
};
}
# use custom protocol handler to avoid sending out real network requests
LWP::Protocol::implementor(
testhttp => 'Kernel::GenericInterface::Transport::HTTP::Test::CustomHTTPProtocol'
);
my $UserAgent = LWP::UserAgent->new();
my $Response = $UserAgent->post( 'testhttp://localhost.local/', Content => $Param{Data} );
return {
Success => 1,
Data => {
ResponseContent => $Response->content(),
},
};
}
=begin Internal:
=cut
=head1 NAME
Kernel::GenericInterface::Transport::HTTP::Test::CustomHTTPProtocol
=head1 DESCRIPTION
This package is used to handle the custom HTTP requests of
Kernel::GenericInterface::Transport::HTTP::Test.
The requests are immediately answered with a response, without
sending them out to the network.
=cut
package Kernel::GenericInterface::Transport::HTTP::Test::CustomHTTPProtocol; ## no critic
use parent qw(LWP::Protocol);
sub new {
my $Class = shift;
return $Class->SUPER::new(@_);
}
sub request { ## no critic
my $Self = shift;
my ( $Request, $Proxy, $Arg, $Size, $Timeout ) = @_;
my $Response = HTTP::Response->new( 200 => "OK" );
$Response->protocol('HTTP/1.0');
$Response->content_type("text/plain; charset=UTF-8");
$Response->add_content_utf8( $Request->content() );
$Response->date(time);
#print $Request->as_string();
#print $Response->as_string();
return $Response;
}
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