init III
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user