# --
# 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::ITSMConfigItem::Definition;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::System::ITSMConfigItem::Definition - sub module of Kernel::System::ITSMConfigItem
=head1 PUBLIC INTERFACE
=head1 DESCRIPTION
All definition functions.
=head2 DefinitionList()
return a config item definition list as array-hash reference
my $DefinitionListRef = $ConfigItemObject->DefinitionList(
ClassID => 123,
);
returns
my $DefinitionListRef = [
{
'Version' => '1',
'CreateTime' => '2012-06-12 14:09:43',
'DefinitionID' => '1',
'CreateBy' => '123',
'Definition' => '---
- Key: Vendor
Name: Vendor
Searchable: 1
Input:
- Type: Text
Size: 50
MaxLength: 50
- Key: Description
Name: Description
Searchable: 1
Input:
- Type: TextArea
- Key: Type
Name: Type
Searchable: 1
Input:
- Type: TextArea
- Type: GeneralCatalog\
Class: ITSM::ConfigItem::Computer::Type
Translation: 1
# ... etc ...
',
},
];
=cut
sub DefinitionList {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ClassID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ClassID!',
);
return;
}
# ask database
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id, configitem_definition, version, create_time, create_by '
. 'FROM configitem_definition WHERE class_id = ? ORDER BY version',
Bind => [ \$Param{ClassID} ],
);
my @DefinitionList;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
my %Definition;
$Definition{DefinitionID} = $Row[0];
$Definition{Definition} = $Row[1] || "--- []";
$Definition{Version} = $Row[2];
$Definition{CreateTime} = $Row[3];
$Definition{CreateBy} = $Row[4];
# Check if definition code is not a YAML string.
if ( substr( $Definition{Definition}, 0, 3 ) ne '---' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "DefinitionID: $Definition{DefinitionID}"
. " ClassID: $Param{ClassID}"
. " found in legacy Perl code format, can not continue",
);
$Definition{Definition} = "--- []";
}
push @DefinitionList, \%Definition;
}
return \@DefinitionList;
}
=head2 DefinitionGet()
return a config item definition as hash reference
Return
$Definition->{DefinitionID}
$Definition->{ClassID}
$Definition->{Class}
$Definition->{Definition}
$Definition->{DefinitionRef}
$Definition->{Version}
$Definition->{CreateTime}
$Definition->{CreateBy}
my $DefinitionRef = $ConfigItemObject->DefinitionGet(
DefinitionID => 123,
);
or
my $DefinitionRef = $ConfigItemObject->DefinitionGet(
ClassID => 123,
);
=cut
sub DefinitionGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{DefinitionID} && !$Param{ClassID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need DefinitionID or ClassID!',
);
return;
}
if ( $Param{DefinitionID} ) {
# check if result is already cached
return $Self->{Cache}->{DefinitionGet}->{ $Param{DefinitionID} }
if $Self->{Cache}->{DefinitionGet}->{ $Param{DefinitionID} };
# ask database
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id, class_id, configitem_definition, version, create_time, create_by '
. 'FROM configitem_definition WHERE id = ?',
Bind => [ \$Param{DefinitionID} ],
Limit => 1,
);
}
else {
# ask database
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id, class_id, configitem_definition, version, create_time, create_by '
. 'FROM configitem_definition '
. 'WHERE class_id = ? ORDER BY version DESC',
Bind => [ \$Param{ClassID} ],
Limit => 1,
);
}
# fetch the result
my %Definition;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$Definition{DefinitionID} = $Row[0];
$Definition{ClassID} = $Row[1];
$Definition{Definition} = $Row[2] || "--- []";
$Definition{Version} = $Row[3];
$Definition{CreateTime} = $Row[4];
$Definition{CreateBy} = $Row[5];
# Check if definition code is not a YAML string.
if ( substr( $Definition{Definition}, 0, 3 ) ne '---' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "DefinitionID: $Definition{DefinitionID}"
. " ClassID: $Definition{ClassID}"
. " found in legacy Perl code format, can not continue",
);
$Definition{Definition} = "--- []";
}
$Definition{DefinitionRef} = $Kernel::OM->Get('Kernel::System::YAML')->Load(
Data => $Definition{Definition},
);
}
return {} if !$Definition{DefinitionID};
# prepare definition
if ( $Definition{DefinitionRef} && ref $Definition{DefinitionRef} eq 'ARRAY' ) {
$Self->_DefinitionPrepare(
DefinitionRef => $Definition{DefinitionRef},
);
}
else {
$Definition{DefinitionRef} = '';
}
# get class list
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# add class
$Definition{Class} = $ClassList->{ $Definition{ClassID} };
# cache the result
$Self->{Cache}->{DefinitionGet}->{ $Definition{DefinitionID} } = \%Definition;
return \%Definition;
}
=head2 DefinitionAdd()
add a new definition
my $DefinitionID = $ConfigItemObject->DefinitionAdd(
ClassID => 123,
Definition => 'the definition code',
UserID => 1,
);
=cut
sub DefinitionAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ClassID Definition UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check definition
my $Check = $Self->DefinitionCheck(
Definition => $Param{Definition},
);
return if !$Check;
# get last definition
my $LastDefinition = $Self->DefinitionGet(
ClassID => $Param{ClassID},
);
# stop add, if definition was not changed
if ( $LastDefinition->{DefinitionID} && $LastDefinition->{Definition} eq $Param{Definition} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't add new definition! The definition was not changed.",
);
return;
}
# set version
my $Version = 1;
if ( $LastDefinition->{Version} ) {
$Version = $LastDefinition->{Version};
$Version++;
}
# insert new definition
my $Success = $Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'INSERT INTO configitem_definition '
. '(class_id, configitem_definition, version, create_time, create_by) VALUES '
. '(?, ?, ?, current_timestamp, ?)',
Bind => [ \$Param{ClassID}, \$Param{Definition}, \$Version, \$Param{UserID} ],
);
return if !$Success;
# get id of new definition
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM configitem_definition WHERE '
. 'class_id = ? AND version = ? '
. 'ORDER BY version DESC',
Bind => [ \$Param{ClassID}, \$Version ],
Limit => 1,
);
# fetch the result
my $DefinitionID;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$DefinitionID = $Row[0];
}
# trigger DefinitionCreate event
$Self->EventHandler(
Event => 'DefinitionCreate',
Data => {
Comment => $DefinitionID,
},
UserID => $Param{UserID},
);
return $DefinitionID;
}
=head2 DefinitionCheck()
check the syntax of a new definition
my $True = $ConfigItemObject->DefinitionCheck(
Definition => 'the definition code',
CheckSubElement => 1, # (optional, default 0, to check sub elements recursively)
);
=cut
sub DefinitionCheck {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Definition} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Definition!',
);
return;
}
# if check sub elements is enabled, we must not YAML load it
# because this has been done in an earlier recursion step already
my $Definition;
if ( $Param{CheckSubElement} ) {
$Definition = $Param{Definition};
}
else {
if ( substr( $Param{Definition}, 0, 3 ) ne '---' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Definition must be a YAML string",
);
return;
}
$Definition = $Kernel::OM->Get('Kernel::System::YAML')->Load(
Data => $Param{Definition},
);
}
# check if definition exists at all
if ( !$Definition ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Invalid Definition! You have an syntax error in the definition.',
);
return;
}
# definition must be an array
if ( ref $Definition ne 'ARRAY' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Invalid Definition! Definition is not an array reference.',
);
return;
}
# check each definition attribute
for my $Attribute ( @{$Definition} ) {
# each definition attribute must be a hash reference with data
if ( !$Attribute || ref $Attribute ne 'HASH' || !%{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Invalid Definition! At least one definition attribute is not a hash reference.',
);
return;
}
# check if the key contains no spaces
if ( $Attribute->{Key} && $Attribute->{Key} =~ m{ \s }xms ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid Definition! Key '$Attribute->{Key}' must not contain whitespace!",
);
return;
}
# check if the key contains non-ascii characters
if ( $Attribute->{Key} && $Attribute->{Key} =~ m{ ([^\x{00}-\x{7f}]) }xms ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid Definition! Key '$Attribute->{Key}' must not contain non ASCII characters '$1'!",
);
return;
}
# recursion check for Sub-Elements
for my $Key ( sort keys %{$Attribute} ) {
my $Value = $Attribute->{$Key};
if ( $Key eq 'Sub' && ref $Value eq 'ARRAY' ) {
# check the sub array
my $Check = $Self->DefinitionCheck(
Definition => $Value,
CheckSubElement => 1,
);
if ( !$Check ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Invalid Sub-Definition of element with the key '$Attribute->{Key}'.",
);
return;
}
}
}
}
return 1;
}
=head2 DefinitionAttributeInfo()
Return attribute information from the definition for a given attribute path name
my $AttributeInfo = $ConfigItemObject->DefinitionAttributeInfo(
AttributePath => 'HardDisk::Capacity',
Definition => '
[
# ...
{
Key => 'HardDisk',
Name => 'Hard Disk',
Input => {
Type => 'Text',
Size => 50,
MaxLength => 100,
},
CountMax => 10,
Sub => [
{
Key => 'Capacity',
Name => 'Capacity',
Input => {
Type => 'Text',
Size => 20,
MaxLength => 10,
},
},
],
},
# ...
];
',
);
Returns:
my $AttributeInfo = {
CountDefault => 1,
CountMax => 1,
Input => {
Size => 20,
Type => 'Text',
MaxLength => 10
},
Name => 'Capacity',
CountMin => 1,
Key => 'Capacity'
};
=cut
sub DefinitionAttributeInfo {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(Definition AttributePath)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
my $Subtree = $Param{Definition};
my $Info;
PART:
for my $Part ( split /::/, $Param{AttributePath} ) {
my ($Found) = grep { $_->{Key} eq $Part } @{$Subtree};
last PART if !$Found;
$Subtree = $Found->{Sub};
$Info = $Found;
}
return $Info;
}
=begin Internal:
=head2 _DefinitionPrepare()
Prepare the syntax of a new definition
my $True = $ConfigItemObject->_DefinitionPrepare(
DefinitionRef => $ArrayRef,
);
=cut
sub _DefinitionPrepare {
my ( $Self, %Param ) = @_;
# check definition
if ( !$Param{DefinitionRef} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need DefinitionRef!',
);
return;
}
for my $Item ( @{ $Param{DefinitionRef} } ) {
# set CountMin
if ( !defined $Item->{CountMin} ) {
$Item->{CountMin} = 1;
}
# set CountMax
$Item->{CountMax} ||= 1;
# set CountMin
if ( $Item->{CountMin} > $Item->{CountMax} ) {
$Item->{CountMin} = $Item->{CountMax};
}
# set CountDefault
if ( !defined $Item->{CountDefault} ) {
$Item->{CountDefault} = 1;
}
if ( $Item->{CountDefault} < $Item->{CountMin} ) {
$Item->{CountDefault} = $Item->{CountMin};
}
if ( $Item->{CountDefault} > $Item->{CountMax} ) {
$Item->{CountDefault} = $Item->{CountMax};
}
# start recursion, if "Sub" is defined.
if ( $Item->{Sub} && ref $Item->{Sub} eq 'ARRAY' ) {
$Self->_DefinitionPrepare(
DefinitionRef => $Item->{Sub},
);
}
else {
delete $Item->{Sub};
}
}
return 1;
}
1;
=end Internal:
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L.
=cut