1654 lines
50 KiB
Perl
1654 lines
50 KiB
Perl
# --
|
|
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
|
|
# --
|
|
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
|
|
# the enclosed file COPYING for license information (GPL). If you
|
|
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
|
|
# --
|
|
|
|
package Kernel::System::ImportExport::ObjectBackend::ITSMConfigItem;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use List::Util qw(min);
|
|
use Kernel::Language qw(Translatable);
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
|
|
our @ObjectDependencies = (
|
|
'Kernel::Config',
|
|
'Kernel::System::GeneralCatalog',
|
|
'Kernel::System::ITSMConfigItem',
|
|
'Kernel::System::ImportExport',
|
|
'Kernel::System::Log',
|
|
);
|
|
|
|
=head1 NAME
|
|
|
|
Kernel::System::ImportExport::ObjectBackend::ITSMConfigItem - import/export backend for ITSMConfigItem
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
All functions to import and export ITSM config items.
|
|
|
|
=head1 PUBLIC INTERFACE
|
|
|
|
=head2 new()
|
|
|
|
create an object
|
|
|
|
use Kernel::System::ObjectManager;
|
|
local $Kernel::OM = Kernel::System::ObjectManager->new();
|
|
my $BackendObject = $Kernel::OM->Get('Kernel::System::ImportExport::ObjectBackend::ITSMConfigItem');
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
# allocate new hash for object
|
|
my $Self = {};
|
|
bless( $Self, $Type );
|
|
|
|
return $Self;
|
|
}
|
|
|
|
=head2 ObjectAttributesGet()
|
|
|
|
get the object attributes of an object as a ref to an array of hash references
|
|
|
|
my $Attributes = $ObjectBackend->ObjectAttributesGet(
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub ObjectAttributesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed object
|
|
if ( !$Param{UserID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need UserID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get class list
|
|
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::Class',
|
|
) || {};
|
|
|
|
my $Attributes = [
|
|
{
|
|
Key => 'ClassID',
|
|
Name => Translatable('Class'),
|
|
Input => {
|
|
Type => 'Selection',
|
|
Data => $ClassList,
|
|
Required => 1,
|
|
Translation => 0,
|
|
PossibleNone => 1,
|
|
},
|
|
},
|
|
{
|
|
Key => 'CountMax',
|
|
Name => Translatable('Maximum number of one element'),
|
|
Input => {
|
|
Type => 'Text',
|
|
ValueDefault => '10',
|
|
Required => 1,
|
|
Regex => qr{ \A \d+ \z }xms,
|
|
Translation => 0,
|
|
Size => 5,
|
|
MaxLength => 5,
|
|
DataType => 'IntegerBiggerThanZero',
|
|
},
|
|
},
|
|
{
|
|
Key => 'EmptyFieldsLeaveTheOldValues',
|
|
Name => Translatable('Empty fields indicate that the current values are kept'),
|
|
Input => {
|
|
Type => 'Checkbox',
|
|
},
|
|
},
|
|
];
|
|
|
|
return $Attributes;
|
|
}
|
|
|
|
=head2 MappingObjectAttributesGet()
|
|
|
|
get the mapping attributes of an object as array/hash reference
|
|
|
|
my $Attributes = $ObjectBackend->MappingObjectAttributesGet(
|
|
TemplateID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub MappingObjectAttributesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(TemplateID UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get object data
|
|
my $ObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->ObjectDataGet(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return [] if !$ObjectData;
|
|
return [] if ref $ObjectData ne 'HASH';
|
|
return [] if !$ObjectData->{ClassID};
|
|
|
|
# get definition
|
|
my $XMLDefinition = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
|
|
ClassID => $ObjectData->{ClassID},
|
|
);
|
|
|
|
return [] if !$XMLDefinition;
|
|
return [] if ref $XMLDefinition ne 'HASH';
|
|
return [] if !$XMLDefinition->{DefinitionRef};
|
|
return [] if ref $XMLDefinition->{DefinitionRef} ne 'ARRAY';
|
|
|
|
my $ElementList = [
|
|
{
|
|
Key => 'Number',
|
|
Value => Translatable('Number'),
|
|
},
|
|
{
|
|
Key => 'Name',
|
|
Value => Translatable('Name'),
|
|
},
|
|
{
|
|
Key => 'DeplState',
|
|
Value => Translatable('Deployment State'),
|
|
},
|
|
{
|
|
Key => 'InciState',
|
|
Value => Translatable('Incident State'),
|
|
},
|
|
];
|
|
|
|
# add xml elements
|
|
$Self->_MappingObjectAttributesGet(
|
|
XMLDefinition => $XMLDefinition->{DefinitionRef},
|
|
ElementList => $ElementList,
|
|
CountMaxLimit => $ObjectData->{CountMax} || 10,
|
|
);
|
|
|
|
my $Attributes = [
|
|
{
|
|
Key => 'Key',
|
|
Name => Translatable('Key'),
|
|
Input => {
|
|
Type => 'Selection',
|
|
Data => $ElementList,
|
|
Required => 1,
|
|
Translation => 0,
|
|
PossibleNone => 1,
|
|
},
|
|
},
|
|
{
|
|
Key => 'Identifier',
|
|
Name => Translatable('Identifier'),
|
|
Input => {
|
|
Type => 'Checkbox',
|
|
},
|
|
},
|
|
];
|
|
|
|
return $Attributes;
|
|
}
|
|
|
|
=head2 SearchAttributesGet()
|
|
|
|
get the search object attributes of an object as array/hash reference
|
|
|
|
my $AttributeList = $ObjectBackend->SearchAttributesGet(
|
|
TemplateID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub SearchAttributesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(TemplateID UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get object data
|
|
my $ObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->ObjectDataGet(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return [] if !$ObjectData;
|
|
return [] if ref $ObjectData ne 'HASH';
|
|
return [] if !$ObjectData->{ClassID};
|
|
|
|
# get definition
|
|
my $XMLDefinition = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
|
|
ClassID => $ObjectData->{ClassID},
|
|
);
|
|
|
|
return [] if !$XMLDefinition;
|
|
return [] if ref $XMLDefinition ne 'HASH';
|
|
return [] if !$XMLDefinition->{DefinitionRef};
|
|
return [] if ref $XMLDefinition->{DefinitionRef} ne 'ARRAY';
|
|
|
|
# get deployment state list
|
|
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::DeploymentState',
|
|
) || {};
|
|
|
|
# get incident state list
|
|
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::Core::IncidentState',
|
|
) || {};
|
|
|
|
my $AttributeList = [
|
|
{
|
|
Key => 'Number',
|
|
Name => Translatable('Number'),
|
|
Input => {
|
|
Type => 'Text',
|
|
Size => 80,
|
|
MaxLength => 255,
|
|
},
|
|
},
|
|
{
|
|
Key => 'Name',
|
|
Name => Translatable('Name'),
|
|
Input => {
|
|
Type => 'Text',
|
|
Size => 80,
|
|
MaxLength => 255,
|
|
},
|
|
},
|
|
{
|
|
Key => 'DeplStateIDs',
|
|
Name => Translatable('Deployment State'),
|
|
Input => {
|
|
Type => 'Selection',
|
|
Data => $DeplStateList,
|
|
Translation => 1,
|
|
Size => 5,
|
|
Multiple => 1,
|
|
},
|
|
},
|
|
{
|
|
Key => 'InciStateIDs',
|
|
Name => Translatable('Incident State'),
|
|
Input => {
|
|
Type => 'Selection',
|
|
Data => $InciStateList,
|
|
Translation => 1,
|
|
Size => 5,
|
|
Multiple => 1,
|
|
},
|
|
},
|
|
];
|
|
|
|
# add xml attributes
|
|
$Self->_SearchAttributesGet(
|
|
XMLDefinition => $XMLDefinition->{DefinitionRef},
|
|
AttributeList => $AttributeList,
|
|
);
|
|
|
|
return $AttributeList;
|
|
}
|
|
|
|
=head2 ExportDataGet()
|
|
|
|
get export data as C<2D-array-hash> reference
|
|
|
|
my $ExportData = $ObjectBackend->ExportDataGet(
|
|
TemplateID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub ExportDataGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(TemplateID UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get object data
|
|
my $ObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->ObjectDataGet(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check object data
|
|
if ( !$ObjectData || ref $ObjectData ne 'HASH' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "No object data found for the template id $Param{TemplateID}",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get class list
|
|
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::Class',
|
|
);
|
|
|
|
return if !$ClassList || ref $ClassList ne 'HASH';
|
|
|
|
# check the class id
|
|
if ( !$ObjectData->{ClassID} || !$ClassList->{ $ObjectData->{ClassID} } ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "No valid class id found for the template id $Param{TemplateID}",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get the mapping list
|
|
my $MappingList = $Kernel::OM->Get('Kernel::System::ImportExport')->MappingList(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check the mapping list
|
|
if ( !$MappingList || ref $MappingList ne 'ARRAY' || !@{$MappingList} ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "No valid mapping list found for the template id $Param{TemplateID}",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# create the mapping object list
|
|
my @MappingObjectList;
|
|
for my $MappingID ( @{$MappingList} ) {
|
|
|
|
# get mapping object data
|
|
my $MappingObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->MappingObjectDataGet(
|
|
MappingID => $MappingID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check mapping object data
|
|
if ( !$MappingObjectData || ref $MappingObjectData ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "No valid mapping list found for the template id $Param{TemplateID}",
|
|
);
|
|
return;
|
|
}
|
|
|
|
push @MappingObjectList, $MappingObjectData;
|
|
}
|
|
|
|
# get search data
|
|
my $SearchData = $Kernel::OM->Get('Kernel::System::ImportExport')->SearchDataGet(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return if !$SearchData || ref $SearchData ne 'HASH';
|
|
|
|
# get deployment state list
|
|
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::DeploymentState',
|
|
);
|
|
|
|
# check deployment state list
|
|
if ( !$DeplStateList || ref $DeplStateList ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Can't get the general catalog list ITSM::ConfigItem::DeploymentState!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get incident state list
|
|
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::Core::IncidentState',
|
|
);
|
|
|
|
# check incident state list
|
|
if ( !$InciStateList || ref $InciStateList ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Can't get the general catalog list ITSM::Core::IncidentState!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get current definition of this class
|
|
my $DefinitionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
|
|
ClassID => $ObjectData->{ClassID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
my %SearchParams;
|
|
|
|
# add number to the search params
|
|
if ( $SearchData->{Number} ) {
|
|
$SearchParams{Number} = delete $SearchData->{Number};
|
|
}
|
|
|
|
# add name to the search params
|
|
if ( $SearchData->{Name} ) {
|
|
$SearchParams{Name} = delete $SearchData->{Name};
|
|
}
|
|
|
|
# add deployment state to the search params
|
|
if ( $SearchData->{DeplStateIDs} ) {
|
|
my @DeplStateIDs = split '#####', $SearchData->{DeplStateIDs};
|
|
$SearchParams{DeplStateIDs} = \@DeplStateIDs;
|
|
delete $SearchData->{DeplStateIDs};
|
|
}
|
|
|
|
# add incident state to the search params
|
|
if ( $SearchData->{InciStateIDs} ) {
|
|
my @InciStateIDs = split '#####', $SearchData->{InciStateIDs};
|
|
$SearchParams{InciStateIDs} = \@InciStateIDs;
|
|
delete $SearchData->{InciStateIDs};
|
|
}
|
|
|
|
# add all XML data to the search params
|
|
my @SearchParamsWhat;
|
|
$Self->_ExportXMLSearchDataPrepare(
|
|
XMLDefinition => $DefinitionData->{DefinitionRef},
|
|
What => \@SearchParamsWhat,
|
|
SearchData => $SearchData,
|
|
);
|
|
|
|
# add XML search params to the search hash
|
|
if (@SearchParamsWhat) {
|
|
$SearchParams{What} = \@SearchParamsWhat;
|
|
}
|
|
|
|
# search the config items
|
|
my $ConfigItemList = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->ConfigItemSearchExtended(
|
|
%SearchParams,
|
|
ClassIDs => [ $ObjectData->{ClassID} ],
|
|
PreviousVersionSearch => 0,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
my @ExportData;
|
|
CONFIGITEMID:
|
|
for my $ConfigItemID ( @{$ConfigItemList} ) {
|
|
|
|
# get last version
|
|
my $VersionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->VersionGet(
|
|
ConfigItemID => $ConfigItemID,
|
|
);
|
|
|
|
next CONFIGITEMID if !$VersionData;
|
|
next CONFIGITEMID if ref $VersionData ne 'HASH';
|
|
|
|
# translate xmldata to a 2d hash
|
|
my %XMLData2D;
|
|
$Self->_ExportXMLDataPrepare(
|
|
XMLDefinition => $DefinitionData->{DefinitionRef},
|
|
XMLData => $VersionData->{XMLData}->[1]->{Version}->[1],
|
|
XMLData2D => \%XMLData2D,
|
|
);
|
|
|
|
# add data to the export data array
|
|
my @Item;
|
|
MAPPINGOBJECT:
|
|
for my $MappingObject (@MappingObjectList) {
|
|
|
|
# extract key
|
|
my $Key = $MappingObject->{Key};
|
|
|
|
# handle empty key
|
|
if ( !$Key ) {
|
|
push @Item, '';
|
|
next MAPPINGOBJECT;
|
|
}
|
|
|
|
# handle config item number
|
|
if ( $Key eq 'Number' ) {
|
|
push @Item, $VersionData->{Number};
|
|
next MAPPINGOBJECT;
|
|
}
|
|
|
|
# handle current config item name
|
|
if ( $Key eq 'Name' ) {
|
|
push @Item, $VersionData->{Name};
|
|
next MAPPINGOBJECT;
|
|
}
|
|
|
|
# handle deployment state
|
|
if ( $Key eq 'DeplState' ) {
|
|
$VersionData->{DeplStateID} ||= 'DUMMY';
|
|
push @Item, $DeplStateList->{ $VersionData->{DeplStateID} };
|
|
next MAPPINGOBJECT;
|
|
}
|
|
|
|
# handle incident state
|
|
if ( $Key eq 'InciState' ) {
|
|
$VersionData->{InciStateID} ||= 'DUMMY';
|
|
push @Item, $InciStateList->{ $VersionData->{InciStateID} };
|
|
next MAPPINGOBJECT;
|
|
}
|
|
|
|
# handle all XML data elements
|
|
push @Item, $XMLData2D{$Key};
|
|
}
|
|
|
|
push @ExportData, \@Item;
|
|
}
|
|
|
|
return \@ExportData;
|
|
}
|
|
|
|
=head2 ImportDataSave()
|
|
|
|
imports a single entity of the import data. The C<TemplateID> points to the definition
|
|
of the current import. C<ImportDataRow> holds the data. C<Counter> is only used in
|
|
error messages, for indicating which item was not imported successfully.
|
|
|
|
The current version of the config item will never be deleted. When there are no
|
|
changes in the data, the import will be skipped. When there is new or changed data,
|
|
then a new config item or a new version is created.
|
|
|
|
In the case of changed data, the new version of the config item will contain the
|
|
attributes of the C<ImportDataRow> plus the old attributes that are
|
|
not part of the import definition.
|
|
Thus ImportDataSave() guarantees to not overwrite undeclared attributes.
|
|
|
|
The behavior when imported attributes are empty depends on the setting in the object data.
|
|
When C<EmptyFieldsLeaveTheOldValues> is not set, then empty values will wipe out
|
|
the old data. This is the default behavior. When C<EmptyFieldsLeaveTheOldValues> is set,
|
|
then empty values will leave the old values.
|
|
|
|
The decision what constitute an empty value is a bit hairy.
|
|
Here are the rules.
|
|
Fields that are not even mentioned in the Import definition are empty. These are the 'not defined' fields.
|
|
Empty strings and undefined values constitute empty fields.
|
|
Fields with with only one or more whitespace characters are not empty.
|
|
Fields with the digit '0' are not empty.
|
|
|
|
my ( $ConfigItemID, $RetCode ) = $ObjectBackend->ImportDataSave(
|
|
TemplateID => 123,
|
|
ImportDataRow => $ArrayRef,
|
|
Counter => 367,
|
|
UserID => 1,
|
|
);
|
|
|
|
An empty C<ConfigItemID> indicates failure. Otherwise it indicates the
|
|
location of the imported data.
|
|
C<RetCode> is either 'Created', 'Updated' or 'Skipped'. 'Created' means that a new
|
|
config item has been created. 'Updated' means that a new version has been added to
|
|
an existing config item. 'Skipped' means that no new version has been created,
|
|
as the new data is identical to the latest version of an existing config item.
|
|
|
|
No codes have yet been defined for the failure case.
|
|
|
|
=cut
|
|
|
|
sub ImportDataSave {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(TemplateID ImportDataRow Counter UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check import data row
|
|
if ( ref $Param{ImportDataRow} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "ImportDataRow must be an array reference",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get object data
|
|
my $ObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->ObjectDataGet(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check object data
|
|
if ( !$ObjectData || ref $ObjectData ne 'HASH' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "No object data found for the template id '$Param{TemplateID}'",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# just for convenience
|
|
my $EmptyFieldsLeaveTheOldValues = $ObjectData->{EmptyFieldsLeaveTheOldValues};
|
|
|
|
# get class list
|
|
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::Class',
|
|
);
|
|
|
|
# check class list
|
|
if ( !$ClassList || ref $ClassList ne 'HASH' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Can't get the general catalog list ITSM::ConfigItem::Class",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check the class id
|
|
if ( !$ObjectData->{ClassID} || !$ClassList->{ $ObjectData->{ClassID} } ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "No class found for the template id '$Param{TemplateID}'",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get the mapping list
|
|
my $MappingList = $Kernel::OM->Get('Kernel::System::ImportExport')->MappingList(
|
|
TemplateID => $Param{TemplateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check the mapping list
|
|
if ( !$MappingList || ref $MappingList ne 'ARRAY' || !@{$MappingList} ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "No valid mapping list found for the template id '$Param{TemplateID}'",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# create the mapping object list
|
|
my @MappingObjectList;
|
|
for my $MappingID ( @{$MappingList} ) {
|
|
|
|
# get mapping object data
|
|
my $MappingObjectData = $Kernel::OM->Get('Kernel::System::ImportExport')->MappingObjectDataGet(
|
|
MappingID => $MappingID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check mapping object data
|
|
if ( !$MappingObjectData || ref $MappingObjectData ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "No mapping object data found for the mapping id '$MappingID'",
|
|
);
|
|
return;
|
|
}
|
|
|
|
push @MappingObjectList, $MappingObjectData;
|
|
}
|
|
|
|
# check and remember the Identifiers
|
|
# the Identifiers identify the config item that should be updated
|
|
my %Identifier;
|
|
my $RowIndex = 0;
|
|
MAPPINGOBJECTDATA:
|
|
for my $MappingObjectData (@MappingObjectList) {
|
|
|
|
next MAPPINGOBJECTDATA if !$MappingObjectData->{Identifier};
|
|
|
|
# check if identifier already exists
|
|
if ( $Identifier{ $MappingObjectData->{Key} } ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "'$MappingObjectData->{Key}' has been used multiple times as an identifier",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set identifier value
|
|
$Identifier{ $MappingObjectData->{Key} } = $Param{ImportDataRow}->[$RowIndex];
|
|
|
|
next MAPPINGOBJECTDATA if $MappingObjectData->{Key} && $Param{ImportDataRow}->[$RowIndex];
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Identifier field is empty",
|
|
);
|
|
|
|
return;
|
|
}
|
|
continue {
|
|
$RowIndex++;
|
|
}
|
|
|
|
# get deployment state list
|
|
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::DeploymentState',
|
|
);
|
|
|
|
# check deployment state list
|
|
if ( !$DeplStateList || ref $DeplStateList ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Can't get the general catalog list ITSM::ConfigItem::DeploymentState!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# reverse the deployment state list
|
|
my %DeplStateListReverse = reverse %{$DeplStateList};
|
|
|
|
# get incident state list
|
|
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::Core::IncidentState',
|
|
);
|
|
|
|
# check incident state list
|
|
if ( !$InciStateList || ref $InciStateList ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Can't get the general catalog list ITSM::Core::IncidentState",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# reverse the incident state list
|
|
my %InciStateListReverse = reverse %{$InciStateList};
|
|
|
|
# get current definition of this class
|
|
my $DefinitionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
|
|
ClassID => $ObjectData->{ClassID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check definition data
|
|
if ( !$DefinitionData || ref $DefinitionData ne 'HASH' ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Can't get the definition of class id $ObjectData->{ClassID}",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# try to get config item ids, when there are identifiers
|
|
my $ConfigItemID;
|
|
if (%Identifier) {
|
|
|
|
my %SearchParams;
|
|
|
|
# add number to the search params
|
|
if ( $Identifier{Number} ) {
|
|
$SearchParams{Number} = delete $Identifier{Number};
|
|
}
|
|
|
|
# add name to the search params
|
|
if ( $Identifier{Name} ) {
|
|
$SearchParams{Name} = delete $Identifier{Name};
|
|
}
|
|
|
|
# add deployment state to the search params
|
|
if ( $Identifier{DeplState} ) {
|
|
|
|
# extract deployment state id
|
|
my $DeplStateID = $DeplStateListReverse{ $Identifier{DeplState} } || '';
|
|
|
|
if ( !$DeplStateID ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "The deployment state '$Identifier{DeplState}' is invalid",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$SearchParams{DeplStateIDs} = [$DeplStateID];
|
|
delete $Identifier{DeplState};
|
|
}
|
|
|
|
# add incident state to the search params
|
|
if ( $Identifier{InciState} ) {
|
|
|
|
# extract incident state id
|
|
my $InciStateID = $InciStateListReverse{ $Identifier{InciState} } || '';
|
|
|
|
if ( !$InciStateID ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "The incident state '$Identifier{InciState}' is invalid",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$SearchParams{InciStateIDs} = [$InciStateID];
|
|
delete $Identifier{InciState};
|
|
}
|
|
|
|
# add all XML data to the search params
|
|
my @SearchParamsWhat;
|
|
$Self->_ImportXMLSearchDataPrepare(
|
|
XMLDefinition => $DefinitionData->{DefinitionRef},
|
|
What => \@SearchParamsWhat,
|
|
Identifier => \%Identifier,
|
|
);
|
|
|
|
# add XML search params to the search hash
|
|
if (@SearchParamsWhat) {
|
|
$SearchParams{What} = \@SearchParamsWhat;
|
|
}
|
|
|
|
# search existing config item with the same identifiers
|
|
my $ConfigItemList = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->ConfigItemSearchExtended(
|
|
%SearchParams,
|
|
ClassIDs => [ $ObjectData->{ClassID} ],
|
|
PreviousVersionSearch => 0,
|
|
UsingWildcards => 0,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
if ( scalar @{$ConfigItemList} > 1 ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Identifier fields NOT unique!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$ConfigItemID = $ConfigItemList->[0];
|
|
}
|
|
|
|
# get version data of the config item
|
|
my $VersionData = {};
|
|
if ($ConfigItemID) {
|
|
|
|
# get latest version
|
|
$VersionData = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->VersionGet(
|
|
ConfigItemID => $ConfigItemID,
|
|
);
|
|
|
|
# remove empty xml data
|
|
if (
|
|
!$VersionData->{XMLData}
|
|
|| ref $VersionData->{XMLData} ne 'ARRAY'
|
|
|| !@{ $VersionData->{XMLData} }
|
|
)
|
|
{
|
|
delete $VersionData->{XMLData};
|
|
}
|
|
}
|
|
|
|
# set up fields in VersionData and in the XML attributes
|
|
my %XMLData2D;
|
|
$RowIndex = 0;
|
|
for my $MappingObjectData (@MappingObjectList) {
|
|
|
|
# just for convenience
|
|
my $Key = $MappingObjectData->{Key};
|
|
my $Value = $Param{ImportDataRow}->[ $RowIndex++ ];
|
|
|
|
if ( $Key eq 'Number' ) {
|
|
|
|
# do nothing
|
|
# Import does not override the config item number
|
|
}
|
|
elsif ( $Key eq 'Name' ) {
|
|
|
|
if ( $EmptyFieldsLeaveTheOldValues && ( !defined $Value || $Value eq '' ) ) {
|
|
|
|
# do nothing, keep the old value
|
|
}
|
|
else {
|
|
if ( !$Value ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "The name '$Value' is invalid!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$VersionData->{$Key} = $Value;
|
|
}
|
|
}
|
|
elsif ( $Key eq 'DeplState' ) {
|
|
|
|
if ( $EmptyFieldsLeaveTheOldValues && ( !defined $Value || $Value eq '' ) ) {
|
|
|
|
# do nothing, keep the old value
|
|
}
|
|
else {
|
|
|
|
# extract deployment state id
|
|
my $DeplStateID = $DeplStateListReverse{$Value} || '';
|
|
if ( !$DeplStateID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "The deployment state '$Value' is invalid!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$VersionData->{DeplStateID} = $DeplStateID;
|
|
}
|
|
}
|
|
elsif ( $Key eq 'InciState' ) {
|
|
|
|
if ( $EmptyFieldsLeaveTheOldValues && ( !defined $Value || $Value eq '' ) ) {
|
|
|
|
# do nothing, keep the old value
|
|
}
|
|
else {
|
|
|
|
# extract the deployment state id
|
|
my $InciStateID = $InciStateListReverse{$Value} || '';
|
|
if ( !$InciStateID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "The incident state '$Value' is invalid!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$VersionData->{InciStateID} = $InciStateID;
|
|
}
|
|
}
|
|
else {
|
|
|
|
# handle xml data
|
|
$XMLData2D{$Key} = $Value;
|
|
}
|
|
}
|
|
|
|
# set up empty container, in case there is no previous data
|
|
$VersionData->{XMLData}->[1]->{Version}->[1] ||= {};
|
|
|
|
# Edit XMLDataPrev, so that the values in XMLData2D take precedence.
|
|
my $MergeOk = $Self->_ImportXMLDataMerge(
|
|
XMLDefinition => $DefinitionData->{DefinitionRef},
|
|
XMLDataPrev => $VersionData->{XMLData}->[1]->{Version}->[1],
|
|
XMLData2D => \%XMLData2D,
|
|
EmptyFieldsLeaveTheOldValues => $EmptyFieldsLeaveTheOldValues,
|
|
);
|
|
|
|
# bail out, when the was a problem in _ImportXMLDataMerge()
|
|
if ( !$MergeOk ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Could not prepare the input!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $RetCode = $ConfigItemID ? Translatable('Changed') : Translatable('Created');
|
|
|
|
# check if the feature to check for a unique name is enabled
|
|
if (
|
|
IsStringWithData( $VersionData->{Name} )
|
|
&& $Kernel::OM->Get('Kernel::Config')->Get('UniqueCIName::EnableUniquenessCheck')
|
|
)
|
|
{
|
|
|
|
if ( $Kernel::OM->Get('Kernel::Config')->{Debug} > 0 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'debug',
|
|
Message => "Checking for duplicate names (ClassID: $ObjectData->{ClassID}, "
|
|
. "Name: $VersionData->{Name}, ConfigItemID: " . $ConfigItemID || 'NEW' . ')',
|
|
);
|
|
}
|
|
|
|
my $NameDuplicates = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->UniqueNameCheck(
|
|
ConfigItemID => $ConfigItemID || 'NEW',
|
|
ClassID => $ObjectData->{ClassID},
|
|
Name => $VersionData->{Name},
|
|
);
|
|
|
|
# stop processing if the name is not unique
|
|
if ( IsArrayRefWithData($NameDuplicates) ) {
|
|
|
|
# build a string of all duplicate IDs
|
|
my $NameDuplicatesString = join ', ', @{$NameDuplicates};
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"The name $VersionData->{Name} is already in use by the ConfigItemID(s): "
|
|
. $NameDuplicatesString,
|
|
);
|
|
|
|
# set the return code to also include the duplicate name
|
|
$RetCode = "DuplicateName '$VersionData->{Name}'";
|
|
|
|
# return undef for the config item id so it will be counted as 'Failed'
|
|
return undef, $RetCode; ## no critic
|
|
}
|
|
}
|
|
|
|
my $LatestVersionID = 0;
|
|
if ($ConfigItemID) {
|
|
|
|
# the specified config item already exists
|
|
# get id of the latest version, for checking later whether a version was created
|
|
my $VersionList = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->VersionList(
|
|
ConfigItemID => $ConfigItemID,
|
|
) || [];
|
|
if ( scalar @{$VersionList} ) {
|
|
$LatestVersionID = $VersionList->[-1];
|
|
}
|
|
}
|
|
else {
|
|
|
|
# no config item was found, so add new config item
|
|
$ConfigItemID = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->ConfigItemAdd(
|
|
ClassID => $ObjectData->{ClassID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check the new config item id
|
|
if ( !$ConfigItemID ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Error when adding the new config item.",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# add new version
|
|
my $VersionID = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->VersionAdd(
|
|
ConfigItemID => $ConfigItemID,
|
|
Name => $VersionData->{Name},
|
|
DefinitionID => $DefinitionData->{DefinitionID},
|
|
DeplStateID => $VersionData->{DeplStateID},
|
|
InciStateID => $VersionData->{InciStateID},
|
|
XMLData => $VersionData->{XMLData},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# the import was successful, when we get a version id
|
|
if ($VersionID) {
|
|
|
|
# When VersionAdd() returns the previous latest version ID, we know that
|
|
# no new version has been added.
|
|
# The import of this config item has been skipped.
|
|
if ( $LatestVersionID && $VersionID == $LatestVersionID ) {
|
|
$RetCode = Translatable('Skipped');
|
|
}
|
|
|
|
return $ConfigItemID, $RetCode;
|
|
}
|
|
|
|
if ( $RetCode eq 'Created' ) {
|
|
|
|
# delete the new config item
|
|
$Kernel::OM->Get('Kernel::System::ITSMConfigItem')->ConfigItemDelete(
|
|
ConfigItemID => $ConfigItemID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Can't import entity $Param{Counter}: "
|
|
. "Error when adding the new config item version.",
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
=head1 INTERNAL INTERFACE
|
|
|
|
=head2 _MappingObjectAttributesGet()
|
|
|
|
recursion function for MappingObjectAttributesGet().
|
|
Definitions for object attributes are passed in C<XMLDefinition>.
|
|
The new object attributes are appended to C<ElementList>.
|
|
C<CountMaxLimit> limits the max length of importable arrays.
|
|
|
|
$ObjectBackend->_MappingObjectAttributesGet(
|
|
XMLDefinition => $ArrayRef,
|
|
ElementList => $ArrayRef,
|
|
CountMaxLimit => 10,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _MappingObjectAttributesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
return if !$Param{CountMaxLimit};
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{ElementList};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY';
|
|
return if ref $Param{ElementList} ne 'ARRAY';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
|
|
# limit the length of importable arrays, even if more elements can be set via the GUI
|
|
my $CountMax = min( $Item->{CountMax}, $Param{CountMaxLimit} );
|
|
|
|
COUNT:
|
|
for my $Count ( 1 .. $CountMax ) {
|
|
|
|
# create key string
|
|
my $Key = $Item->{Key} . '::' . $Count;
|
|
|
|
# add prefix to key
|
|
if ( $Param{KeyPrefix} ) {
|
|
$Key = $Param{KeyPrefix} . '::' . $Key;
|
|
}
|
|
|
|
# create value string
|
|
my $Value = $Item->{Key};
|
|
|
|
# add count if required
|
|
if ( $CountMax > 1 || $Item->{Sub} ) {
|
|
$Value .= '::' . $Count;
|
|
}
|
|
|
|
# add prefix to key
|
|
if ( $Param{ValuePrefix} ) {
|
|
$Value = $Param{ValuePrefix} . '::' . $Value;
|
|
}
|
|
|
|
# add row
|
|
my %Row = (
|
|
Key => $Key,
|
|
Value => $Value,
|
|
);
|
|
push @{ $Param{ElementList} }, \%Row;
|
|
|
|
next COUNT if !$Item->{Sub};
|
|
|
|
# start recursion
|
|
$Self->_MappingObjectAttributesGet(
|
|
XMLDefinition => $Item->{Sub},
|
|
ElementList => $Param{ElementList},
|
|
KeyPrefix => $Key,
|
|
ValuePrefix => $Value,
|
|
CountMaxLimit => $Param{CountMaxLimit} || '10',
|
|
);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _SearchAttributesGet()
|
|
|
|
recursion function for MappingObjectAttributesGet()
|
|
|
|
$ObjectBackend->_SearchAttributesGet(
|
|
XMLDefinition => $ArrayRef,
|
|
AttributeList => $ArrayRef,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _SearchAttributesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{AttributeList};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY';
|
|
return if ref $Param{AttributeList} ne 'ARRAY';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
|
|
# set prefix
|
|
my $Key = $Item->{Key};
|
|
my $Name = $Item->{Name};
|
|
|
|
if ( $Param{KeyPrefix} ) {
|
|
$Key = $Param{KeyPrefix} . '::' . $Key;
|
|
}
|
|
|
|
if ( $Param{NamePrefix} ) {
|
|
$Name = $Param{NamePrefix} . '::' . $Name;
|
|
}
|
|
|
|
# add attribute, if marked as searchable
|
|
if ( $Item->{Searchable} ) {
|
|
|
|
if ( $Item->{Input}->{Type} eq 'Text' || $Item->{Input}->{Type} eq 'TextArea' ) {
|
|
|
|
my %Row = (
|
|
Key => $Key,
|
|
Name => $Name,
|
|
Input => {
|
|
Type => 'Text',
|
|
Translation => $Item->{Input}->{Input}->{Translation},
|
|
Size => $Item->{Input}->{Input}->{Size} || 60,
|
|
MaxLength => $Item->{Input}->{Input}->{MaxLength},
|
|
},
|
|
);
|
|
|
|
push @{ $Param{AttributeList} }, \%Row;
|
|
}
|
|
elsif ( $Item->{Input}->{Type} eq 'GeneralCatalog' ) {
|
|
|
|
# get general catalog list
|
|
my $GeneralCatalogList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => $Item->{Input}->{Class},
|
|
) || {};
|
|
|
|
my %Row = (
|
|
Key => $Key,
|
|
Name => $Name,
|
|
Input => {
|
|
Type => 'Selection',
|
|
Data => $GeneralCatalogList,
|
|
Translation => $Item->{Input}->{Input}->{Translation},
|
|
Size => 5,
|
|
Multiple => 1,
|
|
},
|
|
);
|
|
|
|
push @{ $Param{AttributeList} }, \%Row;
|
|
}
|
|
}
|
|
|
|
next ITEM if !$Item->{Sub};
|
|
|
|
# start recursion, if "Sub" was found
|
|
$Self->_SearchAttributesGet(
|
|
XMLDefinition => $Item->{Sub},
|
|
AttributeList => $Param{AttributeList},
|
|
KeyPrefix => $Key,
|
|
NamePrefix => $Name,
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _ExportXMLSearchDataPrepare()
|
|
|
|
recursion function to prepare the export XML search params
|
|
|
|
$ObjectBackend->_ExportXMLSearchDataPrepare(
|
|
XMLDefinition => $ArrayRef,
|
|
What => $ArrayRef,
|
|
SearchData => $HashRef,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _ExportXMLSearchDataPrepare {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{What};
|
|
return if !$Param{SearchData};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY';
|
|
return if ref $Param{What} ne 'ARRAY';
|
|
return if ref $Param{SearchData} ne 'HASH';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
|
|
# create key
|
|
my $Key = $Param{Prefix} ? $Param{Prefix} . '::' . $Item->{Key} : $Item->{Key};
|
|
|
|
# prepare value
|
|
my $Values = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLExportSearchValuePrepare(
|
|
Item => $Item,
|
|
Value => $Param{SearchData}->{$Key},
|
|
);
|
|
|
|
if ($Values) {
|
|
|
|
# create search key
|
|
my $SearchKey = $Key;
|
|
$SearchKey =~ s{ :: }{\'\}[%]\{\'}xmsg;
|
|
|
|
# create search hash
|
|
my $SearchHash = {
|
|
'[1]{\'Version\'}[1]{\'' . $SearchKey . '\'}[%]{\'Content\'}' => $Values,
|
|
};
|
|
|
|
push @{ $Param{What} }, $SearchHash;
|
|
}
|
|
|
|
next ITEM if !$Item->{Sub};
|
|
|
|
# start recursion, if "Sub" was found
|
|
$Self->_ExportXMLSearchDataPrepare(
|
|
XMLDefinition => $Item->{Sub},
|
|
What => $Param{What},
|
|
SearchData => $Param{SearchData},
|
|
Prefix => $Key,
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _ExportXMLDataPrepare()
|
|
|
|
recursion function to prepare the export XML data
|
|
|
|
$ObjectBackend->_ExportXMLDataPrepare(
|
|
XMLDefinition => $ArrayRef,
|
|
XMLData => $HashRef,
|
|
XMLData2D => $HashRef,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _ExportXMLDataPrepare {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{XMLData};
|
|
return if !$Param{XMLData2D};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY';
|
|
return if ref $Param{XMLData} ne 'HASH';
|
|
return if ref $Param{XMLData2D} ne 'HASH';
|
|
|
|
if ( $Param{Prefix} ) {
|
|
$Param{Prefix} .= '::';
|
|
}
|
|
$Param{Prefix} ||= '';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
COUNTER:
|
|
for my $Counter ( 1 .. $Item->{CountMax} ) {
|
|
|
|
# stop loop, if no content was given
|
|
last COUNTER if !defined $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content};
|
|
|
|
# create key
|
|
my $Key = $Param{Prefix} . $Item->{Key} . '::' . $Counter;
|
|
|
|
# prepare value
|
|
$Param{XMLData2D}->{$Key} = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLExportValuePrepare(
|
|
Item => $Item,
|
|
Value => $Param{XMLData}->{ $Item->{Key} }->[$Counter]->{Content},
|
|
);
|
|
|
|
next COUNTER if !$Item->{Sub};
|
|
|
|
# start recursion, if "Sub" was found
|
|
$Self->_ExportXMLDataPrepare(
|
|
XMLDefinition => $Item->{Sub},
|
|
XMLData => $Param{XMLData}->{ $Item->{Key} }->[$Counter],
|
|
XMLData2D => $Param{XMLData2D},
|
|
Prefix => $Key,
|
|
);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _ImportXMLSearchDataPrepare()
|
|
|
|
recursion function to prepare the import XML search params
|
|
|
|
$ObjectBackend->_ImportXMLSearchDataPrepare(
|
|
XMLDefinition => $ArrayRef,
|
|
What => $ArrayRef,
|
|
Identifier => $HashRef,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _ImportXMLSearchDataPrepare {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{What};
|
|
return if !$Param{Identifier};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY';
|
|
return if ref $Param{What} ne 'ARRAY';
|
|
return if ref $Param{Identifier} ne 'HASH';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
|
|
# create key
|
|
my $Key = $Param{Prefix} ? $Param{Prefix} . '::\d+::' . $Item->{Key} : $Item->{Key};
|
|
$Key .= '::\d+';
|
|
|
|
my $IdentifierKey;
|
|
IDENTIFIERKEY:
|
|
for my $IdentKey ( sort keys %{ $Param{Identifier} } ) {
|
|
|
|
next IDENTIFIERKEY if $IdentKey !~ m{ \A $Key \z }xms;
|
|
|
|
$IdentifierKey = $IdentKey;
|
|
}
|
|
|
|
if ($IdentifierKey) {
|
|
|
|
# prepare value
|
|
my $Value = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLImportSearchValuePrepare(
|
|
Item => $Item,
|
|
Value => $Param{Identifier}->{$IdentifierKey},
|
|
);
|
|
|
|
if ($Value) {
|
|
|
|
# prepare key
|
|
my $Counter = 0;
|
|
while ( $IdentifierKey =~ m{ :: }xms ) {
|
|
|
|
if ( $Counter % 2 ) {
|
|
$IdentifierKey =~ s{ :: }{]\{'}xms;
|
|
}
|
|
else {
|
|
$IdentifierKey =~ s{ :: }{'\}[}xms;
|
|
}
|
|
|
|
$Counter++;
|
|
}
|
|
|
|
# create search hash
|
|
my $SearchHash = {
|
|
'[1]{\'Version\'}[1]{\'' . $IdentifierKey . ']{\'Content\'}' => $Value,
|
|
};
|
|
|
|
push @{ $Param{What} }, $SearchHash;
|
|
}
|
|
}
|
|
|
|
next ITEM if !$Item->{Sub};
|
|
|
|
# start recursion, if "Sub" was found
|
|
$Self->_ImportXMLSearchDataPrepare(
|
|
XMLDefinition => $Item->{Sub},
|
|
What => $Param{What},
|
|
Identifier => $Param{Identifier},
|
|
Prefix => $Key,
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _ImportXMLDataMerge()
|
|
|
|
recursive function to inplace edit the import XML data.
|
|
|
|
my $MergeOk = $ObjectBackend->_ImportXMLDataMerge(
|
|
XMLDefinition => $ArrayRef,
|
|
XMLDataPrev => $HashRef,
|
|
XMLData2D => $HashRef,
|
|
);
|
|
|
|
The return value indicates whether the merge was successful.
|
|
A merge fails when for example a general catalog item name can't be mapped to an id.
|
|
|
|
=cut
|
|
|
|
sub _ImportXMLDataMerge {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
return if !$Param{XMLDefinition};
|
|
return if !$Param{XMLData2D};
|
|
return if !$Param{XMLDataPrev};
|
|
return if ref $Param{XMLDefinition} ne 'ARRAY'; # the attributes of the config item class
|
|
return if ref $Param{XMLData2D} ne 'HASH'; # hash with values that should be imported
|
|
return if ref $Param{XMLDataPrev} ne 'HASH'; # hash with current values of the config item
|
|
|
|
my $XMLData = $Param{XMLDataPrev};
|
|
|
|
# default value for prefix
|
|
$Param{Prefix} ||= '';
|
|
|
|
ITEM:
|
|
for my $Item ( @{ $Param{XMLDefinition} } ) {
|
|
|
|
COUNTER:
|
|
for my $Counter ( 1 .. $Item->{CountMax} ) {
|
|
|
|
# create inputkey
|
|
my $Key = $Param{Prefix} . $Item->{Key} . '::' . $Counter;
|
|
|
|
# start recursion, if "Sub" was found
|
|
if ( $Item->{Sub} ) {
|
|
$XMLData->{ $Item->{Key} }->[$Counter]
|
|
||= {}; # empty container, in case there is no previous data
|
|
my $MergeOk = $Self->_ImportXMLDataMerge(
|
|
XMLDefinition => $Item->{Sub},
|
|
XMLData2D => $Param{XMLData2D},
|
|
XMLDataPrev => $XMLData->{ $Item->{Key} }->[$Counter],
|
|
Prefix => $Key . '::',
|
|
EmptyFieldsLeaveTheOldValues => $Param{EmptyFieldsLeaveTheOldValues},
|
|
);
|
|
|
|
return if !$MergeOk;
|
|
}
|
|
|
|
# When the data point is not part of the input definition,
|
|
# then do not overwrite the previous setting.
|
|
# False values are OK.
|
|
next COUNTER if !exists $Param{XMLData2D}->{$Key};
|
|
|
|
if ( $Param{EmptyFieldsLeaveTheOldValues} ) {
|
|
|
|
# do not override old value with an empty field is imported
|
|
next COUNTER if !defined $Param{XMLData2D}->{$Key};
|
|
next COUNTER if $Param{XMLData2D}->{$Key} eq '';
|
|
}
|
|
|
|
# prepare value
|
|
my $Value = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLImportValuePrepare(
|
|
Item => $Item,
|
|
Value => $Param{XMLData2D}->{$Key},
|
|
);
|
|
|
|
# let merge fail, when a value cannot be prepared
|
|
return if !defined $Value;
|
|
|
|
# save the prepared value
|
|
$XMLData->{ $Item->{Key} }->[$Counter]->{Content} = $Value;
|
|
}
|
|
}
|
|
|
|
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
|