1833 lines
54 KiB
Perl
1833 lines
54 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::ITSMConfigItem::Version;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
## nofilter(TidyAll::Plugin::OTRS::Migrations::OTRS6::SysConfig)
|
|
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
|
|
use Storable;
|
|
|
|
our $ObjectManagerDisabled = 1;
|
|
|
|
=head1 NAME
|
|
|
|
Kernel::System::ITSMConfigItem::Version - sub module of Kernel::System::ITSMConfigItem
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
All version functions.
|
|
|
|
=head1 PUBLIC INTERFACE
|
|
|
|
=head2 VersionZoomList()
|
|
|
|
return a config item version list as array-hash reference
|
|
|
|
my $VersionListRef = $ConfigItemObject->VersionZoomList(
|
|
ConfigItemID => 123,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionZoomList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{ConfigItemID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need ConfigItemID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get config item
|
|
my $ConfigItem = $Self->ConfigItemGet(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
|
|
# get version zoom list
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, name, depl_state_id, inci_state_id, create_time, create_by '
|
|
. 'FROM configitem_version WHERE configitem_id = ? ORDER BY id',
|
|
Bind => [ \$Param{ConfigItemID} ],
|
|
);
|
|
|
|
# fetch the result
|
|
my @VersionList;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
my %Version;
|
|
$Version{VersionID} = $Row[0];
|
|
$Version{Name} = $Row[1];
|
|
$Version{DeplStateID} = $Row[2];
|
|
$Version{InciStateID} = $Row[3];
|
|
$Version{CreateTime} = $Row[4];
|
|
$Version{CreateBy} = $Row[5];
|
|
|
|
push @VersionList, \%Version;
|
|
}
|
|
|
|
for my $Version (@VersionList) {
|
|
|
|
# get deployment state functionality
|
|
my $DeplState = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
ItemID => $Version->{DeplStateID},
|
|
);
|
|
|
|
$Version->{DeplState} = $DeplState->{Name};
|
|
$Version->{DeplStateType} = $DeplState->{Functionality};
|
|
|
|
# get incident state functionality
|
|
my $InciState = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
ItemID => $Version->{InciStateID},
|
|
);
|
|
|
|
$Version->{InciState} = $InciState->{Name};
|
|
$Version->{InciStateType} = $InciState->{Functionality};
|
|
|
|
# add config item data
|
|
$Version->{ClassID} = $ConfigItem->{ClassID};
|
|
$Version->{Class} = $ConfigItem->{Class};
|
|
$Version->{Number} = $ConfigItem->{Number};
|
|
$Version->{CurDeplStateID} = $ConfigItem->{CurDeplStateID};
|
|
$Version->{CurDeplState} = $ConfigItem->{CurDeplState};
|
|
$Version->{CurDeplStateType} = $ConfigItem->{CurDeplStateType};
|
|
$Version->{CurInciStateID} = $ConfigItem->{CurInciStateID};
|
|
$Version->{CurInciState} = $ConfigItem->{CurInciState};
|
|
$Version->{CurInciStateType} = $ConfigItem->{CurInciStateType};
|
|
}
|
|
|
|
return \@VersionList;
|
|
}
|
|
|
|
=head2 VersionListAll()
|
|
|
|
Returns a two-dimensional hash reference with the config item ids as keys of the first level
|
|
and the corresponding version ids as keys of the second level,
|
|
then followed by the version data as hash reference.
|
|
|
|
my $VersionListRef = $ConfigItemObject->VersionListAll(
|
|
ConfigItemIDs => [ 1, 2, 3, 4, ...], # optional
|
|
|
|
OlderDate => '2014-12-22 23:59:59', # optional
|
|
# finds versions older than the given date
|
|
# Format MUST be
|
|
# YYYY-MM-DD HH:MM:SS
|
|
# fill missing values with 0 first
|
|
# Example: 2014-04-01 07:03:04
|
|
# otherwise it won't be taken as
|
|
# valid search parameter
|
|
|
|
Limit => 1000000, # optional
|
|
);
|
|
|
|
Returns:
|
|
|
|
$VersionListRef = {
|
|
|
|
# ConfigItemID
|
|
1 => {
|
|
|
|
# VersionID
|
|
100 => {
|
|
VersionID => 100,
|
|
ConfigItemID => 1,
|
|
Name => 'ConfigItem1',
|
|
DefinitionID => 5,
|
|
DeplStateID => 3,
|
|
InciStateID => 2,
|
|
CreateTime => '2016-03-22 17:58:00',
|
|
CreateBy => 1,
|
|
},
|
|
|
|
# VersionID
|
|
101 => {
|
|
VersionID => 101,
|
|
ConfigItemID => 1,
|
|
Name => 'ConfigItem2',
|
|
DefinitionID => 5,
|
|
DeplStateID => 3,
|
|
InciStateID => 2,
|
|
CreateTime => '2016-03-22 17:58:00',
|
|
CreateBy => 1,
|
|
},
|
|
},
|
|
|
|
# ConfigItemID
|
|
2 => {
|
|
|
|
# VersionID
|
|
150 => {
|
|
VersionID => 150,
|
|
ConfigItemID => 2,
|
|
Name => 'ConfigItem1',
|
|
DefinitionID => 5,
|
|
DeplStateID => 3,
|
|
InciStateID => 2,
|
|
CreateTime => '2016-03-22 17:58:00',
|
|
CreateBy => 1,
|
|
},
|
|
|
|
# VersionID
|
|
151 => {
|
|
VersionID => 151,
|
|
ConfigItemID => 2,
|
|
Name => 'ConfigItem1',
|
|
DefinitionID => 5,
|
|
DeplStateID => 3,
|
|
InciStateID => 2,
|
|
CreateTime => '2016-03-22 17:58:00',
|
|
CreateBy => 1,
|
|
},
|
|
},
|
|
};
|
|
|
|
=cut
|
|
|
|
sub VersionListAll {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# build sql
|
|
my $SQL = 'SELECT id, configitem_id, name, definition_id,
|
|
depl_state_id, inci_state_id, create_time, create_by
|
|
FROM configitem_version WHERE 1=1';
|
|
|
|
# if we got ConfigItemIDs make sure we just have numeric ids,
|
|
# extract those and use it for the query
|
|
if ( IsArrayRefWithData( $Param{ConfigItemIDs} ) ) {
|
|
my @ConfigItemIDs = grep { $_ =~ /^\d+$/ } @{ $Param{ConfigItemIDs} };
|
|
$SQL .= ' AND configitem_id IN (' . join ', ', @ConfigItemIDs . ')';
|
|
}
|
|
|
|
my @BindParameter;
|
|
if ( $Param{OlderDate} && $Param{OlderDate} =~ /^\d{4}\-\d{2}\-\d{2}\ \d{2}\:\d{2}:\d{2}$/ ) {
|
|
$SQL .= ' AND create_time < ?';
|
|
push @BindParameter, \$Param{OlderDate};
|
|
}
|
|
|
|
# set limit
|
|
if ( $Param{Limit} ) {
|
|
$Param{Limit} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{Limit}, 'Integer' );
|
|
}
|
|
|
|
if (@BindParameter) {
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Bind => \@BindParameter,
|
|
Limit => $Param{Limit},
|
|
);
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Limit => $Param{Limit},
|
|
);
|
|
}
|
|
|
|
my %Results;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$Results{ $Row[1] }->{ $Row[0] } = {
|
|
VersionID => $Row[0],
|
|
ConfigItemID => $Row[1],
|
|
Name => $Row[2] || '',
|
|
DefinitionID => $Row[3] || '',
|
|
DeplStateID => $Row[4] || '',
|
|
InciStateID => $Row[5] || '',
|
|
CreateTime => $Row[6] || '',
|
|
CreateBy => $Row[7] || '',
|
|
};
|
|
}
|
|
|
|
return \%Results;
|
|
}
|
|
|
|
=head2 VersionList()
|
|
|
|
return a config item version list as array reference
|
|
|
|
my $VersionListRef = $ConfigItemObject->VersionList(
|
|
ConfigItemID => 123,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{ConfigItemID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need ConfigItemID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get version list
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id FROM configitem_version WHERE configitem_id = ? ORDER BY id',
|
|
Bind => [ \$Param{ConfigItemID} ],
|
|
);
|
|
|
|
# fetch the result
|
|
my @VersionList;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
push @VersionList, $Row[0];
|
|
}
|
|
|
|
return \@VersionList;
|
|
}
|
|
|
|
=head2 VersionGet()
|
|
|
|
returns a version of a config item as hash reference.
|
|
The returned hash contains following attributes.
|
|
|
|
$Version{VersionID}
|
|
$Version{ConfigItemID}
|
|
$Version{Number}
|
|
$Version{ClassID}
|
|
$Version{Class}
|
|
$Version{LastVersionID}
|
|
$Version{Name}
|
|
$Version{DefinitionID}
|
|
$Version{DeplStateID}
|
|
$Version{DeplState}
|
|
$Version{DeplStateType}
|
|
$Version{CurDeplStateID}
|
|
$Version{CurDeplState}
|
|
$Version{CurDeplStateType}
|
|
$Version{InciStateID}
|
|
$Version{InciState}
|
|
$Version{InciStateType}
|
|
$Version{CurInciStateID}
|
|
$Version{CurInciState}
|
|
$Version{CurInciStateType}
|
|
$Version{XMLDefinition}
|
|
$Version{XMLData}
|
|
$Version{CreateTime}
|
|
$Version{CreateBy}
|
|
|
|
my $VersionRef = $ConfigItemObject->VersionGet(
|
|
VersionID => 123,
|
|
XMLDataGet => 1, # (optional) default 1 (0|1)
|
|
);
|
|
|
|
or
|
|
|
|
my $VersionRef = $ConfigItemObject->VersionGet(
|
|
ConfigItemID => 123,
|
|
);
|
|
|
|
When the data from the XML storage is not needed then fetching the XML data can be
|
|
explicitly turned off by passing XMLDataGet => 0.
|
|
|
|
my $VersionRef = $ConfigItemObject->VersionGet(
|
|
ConfigItemID => 123,
|
|
XMLDataGet => 0,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{VersionID} && !$Param{ConfigItemID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need VersionID or ConfigItemID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ( !defined $Param{XMLDataGet} ) {
|
|
$Param{XMLDataGet} = 1;
|
|
}
|
|
|
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
|
|
|
if ( $Param{VersionID} ) {
|
|
|
|
# check if result is already cached
|
|
my $CacheKey = 'VersionGet::VersionID::' . $Param{VersionID} . '::XMLData::' . $Param{XMLDataGet};
|
|
my $Cache = $CacheObject->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
return Storable::dclone($Cache) if $Cache;
|
|
|
|
# get version
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, configitem_id, name, definition_id, '
|
|
. 'depl_state_id, inci_state_id, create_time, create_by '
|
|
. 'FROM configitem_version WHERE id = ?',
|
|
Bind => [ \$Param{VersionID} ],
|
|
Limit => 1,
|
|
);
|
|
}
|
|
else {
|
|
|
|
# check if result is already cached
|
|
my $CacheKey = 'VersionGet::ConfigItemID::' . $Param{ConfigItemID} . '::XMLData::' . $Param{XMLDataGet};
|
|
my $Cache = $CacheObject->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
return Storable::dclone($Cache) if $Cache;
|
|
|
|
# get version
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, configitem_id, name, definition_id, '
|
|
. 'depl_state_id, inci_state_id, create_time, create_by '
|
|
. 'FROM configitem_version '
|
|
. 'WHERE configitem_id = ? ORDER BY id DESC',
|
|
Bind => [ \$Param{ConfigItemID} ],
|
|
Limit => 1,
|
|
);
|
|
}
|
|
|
|
# fetch the result
|
|
my %Version;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$Version{VersionID} = $Row[0];
|
|
$Version{ConfigItemID} = $Row[1];
|
|
$Version{Name} = $Row[2];
|
|
$Version{DefinitionID} = $Row[3];
|
|
$Version{DeplStateID} = $Row[4];
|
|
$Version{InciStateID} = $Row[5];
|
|
$Version{CreateTime} = $Row[6];
|
|
$Version{CreateBy} = $Row[7];
|
|
}
|
|
|
|
# check version
|
|
if ( !$Version{VersionID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'No such config item version!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get deployment state functionality
|
|
my $DeplState = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
ItemID => $Version{DeplStateID},
|
|
);
|
|
|
|
$Version{DeplState} = $DeplState->{Name};
|
|
$Version{DeplStateType} = $DeplState->{Functionality};
|
|
|
|
# get incident state functionality
|
|
my $InciState = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
ItemID => $Version{InciStateID},
|
|
);
|
|
|
|
$Version{InciState} = $InciState->{Name};
|
|
$Version{InciStateType} = $InciState->{Functionality};
|
|
|
|
# get config item
|
|
my $ConfigItem = $Self->ConfigItemGet(
|
|
ConfigItemID => $Version{ConfigItemID},
|
|
Cache => 0,
|
|
);
|
|
|
|
# check config item data
|
|
if ( !$ConfigItem || ref $ConfigItem ne 'HASH' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Can't get config item $Version{ConfigItemID}!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$Version{ClassID} = $ConfigItem->{ClassID};
|
|
$Version{Class} = $ConfigItem->{Class};
|
|
$Version{LastVersionID} = $ConfigItem->{LastVersionID};
|
|
$Version{Number} = $ConfigItem->{Number};
|
|
$Version{CurDeplStateID} = $ConfigItem->{CurDeplStateID};
|
|
$Version{CurDeplState} = $ConfigItem->{CurDeplState};
|
|
$Version{CurDeplStateType} = $ConfigItem->{CurDeplStateType};
|
|
$Version{CurInciStateID} = $ConfigItem->{CurInciStateID};
|
|
$Version{CurInciState} = $ConfigItem->{CurInciState};
|
|
$Version{CurInciStateType} = $ConfigItem->{CurInciStateType};
|
|
|
|
# set cache for VersionID without xml data (always)
|
|
my $CacheKey = 'VersionGet::VersionID::' . $Version{VersionID} . '::XMLData::0';
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => Storable::dclone( \%Version ),
|
|
);
|
|
|
|
# set cache for ConfigItemID without xml data (only if called with ConfigItemID)
|
|
if ( $Param{ConfigItemID} ) {
|
|
$CacheKey = 'VersionGet::ConfigItemID::' . $Version{ConfigItemID} . '::XMLData::0';
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => Storable::dclone( \%Version ),
|
|
);
|
|
}
|
|
|
|
# done if we don't need xml data
|
|
return \%Version if !$Param{XMLDataGet};
|
|
|
|
# get xml definition
|
|
my $Definition = $Self->DefinitionGet(
|
|
DefinitionID => $Version{DefinitionID},
|
|
);
|
|
$Version{XMLDefinition} = $Definition->{DefinitionRef};
|
|
|
|
# get xml data
|
|
$Version{XMLData} = $Self->_XMLVersionGet(
|
|
ClassID => $ConfigItem->{ClassID},
|
|
VersionID => $Version{VersionID},
|
|
);
|
|
|
|
# set cache for VersionID (always)
|
|
$CacheKey = 'VersionGet::VersionID::' . $Version{VersionID} . '::XMLData::1';
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => Storable::dclone( \%Version ),
|
|
);
|
|
|
|
# set cache for ConfigItemID (only if called with ConfigItemID)
|
|
if ( $Param{ConfigItemID} ) {
|
|
$CacheKey = 'VersionGet::ConfigItemID::' . $Version{ConfigItemID} . '::XMLData::1';
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => Storable::dclone( \%Version ),
|
|
);
|
|
}
|
|
|
|
return \%Version;
|
|
}
|
|
|
|
=head2 VersionNameGet()
|
|
|
|
returns the name of a version of a config item.
|
|
|
|
my $VersionName = $ConfigItemObject->VersionNameGet(
|
|
VersionID => 123,
|
|
);
|
|
|
|
or
|
|
|
|
my $VersionName = $ConfigItemObject->VersionNameGet(
|
|
ConfigItemID => 123,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionNameGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{VersionID} && !$Param{ConfigItemID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need VersionID or ConfigItemID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
|
|
|
if ( $Param{VersionID} ) {
|
|
|
|
# check if result is already cached
|
|
my $CacheKey = 'VersionNameGet::VersionID::' . $Param{VersionID};
|
|
my $Cache = $CacheObject->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
return ${$Cache} if $Cache;
|
|
|
|
# get version
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, name '
|
|
. 'FROM configitem_version WHERE id = ?',
|
|
Bind => [ \$Param{VersionID} ],
|
|
Limit => 1,
|
|
);
|
|
}
|
|
else {
|
|
|
|
# check if result is already cached
|
|
my $CacheKey = 'VersionNameGet::ConfigItemID::' . $Param{ConfigItemID};
|
|
my $Cache = $CacheObject->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
return ${$Cache} if $Cache;
|
|
|
|
# get version
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, name '
|
|
. 'FROM configitem_version '
|
|
. 'WHERE configitem_id = ? ORDER BY id DESC',
|
|
Bind => [ \$Param{ConfigItemID} ],
|
|
Limit => 1,
|
|
);
|
|
}
|
|
|
|
# fetch the result
|
|
my %Version;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$Version{VersionID} = $Row[0];
|
|
$Version{Name} = $Row[1];
|
|
}
|
|
|
|
# check version
|
|
if ( !$Version{VersionID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'No such config item version!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set cache for VersionID (always)
|
|
my $CacheKey = 'VersionNameGet::VersionID::' . $Version{VersionID};
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => \$Version{Name},
|
|
);
|
|
|
|
# set cache for ConfigItemID (only if called with ConfigItemID)
|
|
if ( $Param{ConfigItemID} ) {
|
|
$CacheKey = 'VersionNameGet::ConfigItemID::' . $Param{ConfigItemID};
|
|
$CacheObject->Set(
|
|
Type => $Self->{CacheType},
|
|
TTL => $Self->{CacheTTL},
|
|
Key => $CacheKey,
|
|
Value => \$Version{Name},
|
|
);
|
|
}
|
|
|
|
return $Version{Name};
|
|
}
|
|
|
|
=head2 VersionConfigItemIDGet()
|
|
|
|
return the config item id of a version
|
|
|
|
my $ConfigItemID = $ConfigItemObject->VersionConfigItemIDGet(
|
|
VersionID => 123,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionConfigItemIDGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{VersionID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need VersionID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check if result is already cached
|
|
return $Self->{Cache}->{VersionConfigItemIDGet}->{ $Param{VersionID} }
|
|
if $Self->{Cache}->{VersionConfigItemIDGet}->{ $Param{VersionID} };
|
|
|
|
# get config item id
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT configitem_id FROM configitem_version WHERE id = ?',
|
|
Bind => [ \$Param{VersionID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
my $ConfigItemID;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$ConfigItemID = $Row[0];
|
|
}
|
|
|
|
# cache the result
|
|
$Self->{Cache}->{VersionConfigItemIDGet}->{ $Param{VersionID} } = $ConfigItemID;
|
|
|
|
return $ConfigItemID;
|
|
}
|
|
|
|
=head2 VersionAdd()
|
|
|
|
add a new version
|
|
|
|
my $VersionID = $ConfigItemObject->VersionAdd(
|
|
ConfigItemID => 123,
|
|
Name => 'The Name',
|
|
DefinitionID => 1212,
|
|
DeplStateID => 8,
|
|
InciStateID => 4,
|
|
XMLData => $ArrayHashRef, # (optional)
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionAdd {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(ConfigItemID Name DefinitionID DeplStateID InciStateID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
|
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
|
|
|
# get deployment state list
|
|
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ConfigItem::DeploymentState',
|
|
);
|
|
|
|
return if !$DeplStateList;
|
|
return if ref $DeplStateList ne 'HASH';
|
|
|
|
# check the deployment state id
|
|
if ( !$DeplStateList->{ $Param{DeplStateID} } ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'No valid deployment state id given!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get incident state list
|
|
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::Core::IncidentState',
|
|
);
|
|
|
|
return if !$InciStateList;
|
|
return if ref $InciStateList ne 'HASH';
|
|
|
|
# check the incident state id
|
|
if ( !$InciStateList->{ $Param{InciStateID} } ) {
|
|
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'No valid incident state id given!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get VersionList
|
|
my $VersionList = $Self->VersionList(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
|
|
my $ConfigItemInfo = {};
|
|
|
|
if ( @{$VersionList} ) {
|
|
|
|
# get old version info for comparisons with current version
|
|
# this is needed to trigger some events
|
|
$ConfigItemInfo = $Self->VersionGet(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
XMLDataGet => 0,
|
|
);
|
|
}
|
|
else {
|
|
|
|
# get config item
|
|
$ConfigItemInfo = $Self->ConfigItemGet(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
}
|
|
|
|
return if !$ConfigItemInfo;
|
|
return if ref $ConfigItemInfo ne 'HASH';
|
|
|
|
# check, whether the feature to check for a unique name is enabled
|
|
if ( $Kernel::OM->Get('Kernel::Config')->Get('UniqueCIName::EnableUniquenessCheck') ) {
|
|
|
|
my $NameDuplicates = $Self->UniqueNameCheck(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
ClassID => $ConfigItemInfo->{ClassID},
|
|
Name => $Param{Name},
|
|
);
|
|
|
|
# stop processing if the name is not unique
|
|
if ( IsArrayRefWithData($NameDuplicates) ) {
|
|
|
|
# build a string of all duplicate IDs
|
|
my $Duplicates = join ', ', @{$NameDuplicates};
|
|
|
|
# write an error log message containing all the duplicate IDs
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The name $Param{Name} is already in use (ConfigItemIDs: $Duplicates)!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my $Events = $Self->_GetEvents(
|
|
Param => \%Param,
|
|
ConfigItemInfo => $ConfigItemInfo,
|
|
);
|
|
|
|
my $ReturnVersionID = scalar @{$VersionList} ? $VersionList->[-1] : 0;
|
|
return $ReturnVersionID if !( $Events && keys %{$Events} );
|
|
|
|
# Special case that only XML attributes have been changed that should not create a new version
|
|
if ( $Events->{UpdateLastVersion} && $Events->{UpdateXMLData} && ref $Events->{UpdateXMLData} eq 'ARRAY' ) {
|
|
|
|
my %ValueHash2D = $Kernel::OM->Get('Kernel::System::XML')->XMLHash2D(
|
|
XMLHash => $Events->{UpdateXMLData},
|
|
);
|
|
|
|
UPDATEVALUE:
|
|
for my $UpdateValue ( sort keys %{ $Events->{ValueUpdate} } ) {
|
|
|
|
my $ContentString = $UpdateValue . '{\'Content\'}';
|
|
|
|
# Check if the key exists.
|
|
next UPDATEVALUE if !exists $ValueHash2D{$ContentString};
|
|
|
|
my $Type = 'ITSM::ConfigItem::' . $ConfigItemInfo->{ClassID};
|
|
|
|
# Delete old entries from the database.
|
|
return if !$DBObject->Do(
|
|
SQL => '
|
|
DELETE FROM xml_storage
|
|
WHERE xml_type = ?
|
|
AND xml_key = ?
|
|
AND xml_content_key = ?
|
|
',
|
|
Bind => [
|
|
\$Type,
|
|
\$ReturnVersionID,
|
|
\$ContentString,
|
|
],
|
|
);
|
|
|
|
# Add updated entry to the database.
|
|
return if !$DBObject->Do(
|
|
SQL => '
|
|
INSERT INTO xml_storage
|
|
(xml_type, xml_key, xml_content_key, xml_content_value)
|
|
VALUES (?, ?, ?, ?)
|
|
',
|
|
Bind => [
|
|
\$Type,
|
|
\$ReturnVersionID,
|
|
\$ContentString,
|
|
\$ValueHash2D{$ContentString},
|
|
],
|
|
);
|
|
|
|
# Delete cache.
|
|
$CacheObject->Delete(
|
|
Type => 'XML',
|
|
Key => "$Type-$ReturnVersionID",
|
|
);
|
|
}
|
|
|
|
# delete affected caches
|
|
my $CacheKey = 'VersionGet::ConfigItemID::' . $Param{ConfigItemID} . '::XMLData::';
|
|
for my $XMLData (qw(0 1)) {
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey . $XMLData,
|
|
);
|
|
}
|
|
|
|
# delete affected caches
|
|
$CacheKey = 'VersionGet::VersionID::' . $ReturnVersionID . '::XMLData::';
|
|
for my $XMLData (qw(0 1)) {
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey . $XMLData,
|
|
);
|
|
}
|
|
|
|
# update change_time and change_by of the config item
|
|
return if !$DBObject->Do(
|
|
SQL => '
|
|
UPDATE configitem
|
|
SET change_time = current_timestamp,
|
|
change_by = ?
|
|
WHERE id = ?',
|
|
Bind => [
|
|
\$Param{UserID},
|
|
\$Param{ConfigItemID},
|
|
],
|
|
);
|
|
|
|
# delete config item cache
|
|
$CacheKey = 'ConfigItemGet::ConfigItemID::' . $Param{ConfigItemID};
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
# Trigger ValueUpdate event.
|
|
$Self->_EventHandlerForChangedXMLValues(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
UpdateValues => $Events->{ValueUpdate},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return $ReturnVersionID;
|
|
}
|
|
|
|
# insert new version
|
|
my $Success = $DBObject->Do(
|
|
SQL => 'INSERT INTO configitem_version '
|
|
. '(configitem_id, name, definition_id, '
|
|
. 'depl_state_id, inci_state_id, create_time, create_by) VALUES '
|
|
. '(?, ?, ?, ?, ?, current_timestamp, ?)',
|
|
Bind => [
|
|
\$Param{ConfigItemID},
|
|
\$Param{Name},
|
|
\$Param{DefinitionID},
|
|
\$Param{DeplStateID},
|
|
\$Param{InciStateID},
|
|
\$Param{UserID},
|
|
],
|
|
);
|
|
|
|
return if !$Success;
|
|
|
|
# delete affected caches
|
|
my $CacheKey = 'VersionGet::ConfigItemID::' . $Param{ConfigItemID} . '::XMLData::';
|
|
for my $XMLData (qw(0 1)) {
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey . $XMLData,
|
|
);
|
|
}
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => 'VersionNameGet::ConfigItemID::' . $Param{ConfigItemID},
|
|
);
|
|
|
|
# get id of new version
|
|
$DBObject->Prepare(
|
|
SQL => 'SELECT id, create_time FROM configitem_version WHERE '
|
|
. 'configitem_id = ? ORDER BY id DESC',
|
|
Bind => [ \$Param{ConfigItemID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
my $VersionID;
|
|
my $CreateTime;
|
|
while ( my @Row = $DBObject->FetchrowArray() ) {
|
|
$VersionID = $Row[0];
|
|
$CreateTime = $Row[1];
|
|
}
|
|
|
|
# check version id
|
|
if ( !$VersionID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Can't get the new version id!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# add xml data
|
|
if ( $Param{XMLData} && ref $Param{XMLData} eq 'ARRAY' ) {
|
|
$Self->_XMLVersionAdd(
|
|
ClassID => $ConfigItemInfo->{ClassID},
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
VersionID => $VersionID,
|
|
XMLData => $Param{XMLData},
|
|
);
|
|
}
|
|
|
|
# update last_version_id, cur_depl_state_id, cur_inci_state_id, change_time and change_by
|
|
$DBObject->Do(
|
|
SQL => 'UPDATE configitem SET last_version_id = ?, '
|
|
. 'cur_depl_state_id = ?, cur_inci_state_id = ?, '
|
|
. 'change_time = ?, change_by = ? '
|
|
. 'WHERE id = ?',
|
|
Bind => [
|
|
\$VersionID,
|
|
\$Param{DeplStateID},
|
|
\$Param{InciStateID},
|
|
\$CreateTime,
|
|
\$Param{UserID},
|
|
\$Param{ConfigItemID},
|
|
],
|
|
);
|
|
|
|
# delete config item cache
|
|
$CacheKey = 'ConfigItemGet::ConfigItemID::' . $Param{ConfigItemID};
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
# trigger VersionCreate event
|
|
$Self->EventHandler(
|
|
Event => 'VersionCreate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $VersionID,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# compare current and old values
|
|
if ( $Events->{ValueUpdate} ) {
|
|
$Self->_EventHandlerForChangedXMLValues(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
UpdateValues => $Events->{ValueUpdate},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# trigger definition update event
|
|
if ( $Events->{DefinitionUpdate} ) {
|
|
$Self->EventHandler(
|
|
Event => 'DefinitionUpdate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $Events->{DefinitionUpdate},
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# check old and new name
|
|
if ( $Events->{NameUpdate} ) {
|
|
$Self->EventHandler(
|
|
Event => 'NameUpdate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $Events->{NameUpdate},
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# trigger incident state update event
|
|
if ( $Events->{IncidentStateUpdate} ) {
|
|
$Self->EventHandler(
|
|
Event => 'IncidentStateUpdate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $Events->{IncidentStateUpdate},
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# trigger deployment state update event
|
|
if ( $Events->{DeploymentStateUpdate} ) {
|
|
$Self->EventHandler(
|
|
Event => 'DeploymentStateUpdate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $Events->{DeploymentStateUpdate},
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# recalculate the current incident state of all linked config items
|
|
$Self->CurInciStateRecalc(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
|
|
return $VersionID;
|
|
}
|
|
|
|
=head2 VersionDelete()
|
|
|
|
delete an existing version or versions
|
|
|
|
my $True = $ConfigItemObject->VersionDelete(
|
|
VersionID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
or
|
|
|
|
my $True = $ConfigItemObject->VersionDelete(
|
|
ConfigItemID => 321,
|
|
UserID => 1,
|
|
);
|
|
|
|
my $True = $ConfigItemObject->VersionDelete(
|
|
VersionIDs => [ 1, 2, 3, 4 ],
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionDelete {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{UserID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need UserID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ( !$Param{VersionID} && !$Param{ConfigItemID} && !IsArrayRefWithData( $Param{VersionIDs} ) ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need VersionID, ConfigItemID or VersionIDs!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $VersionList = [];
|
|
if ( $Param{VersionID} ) {
|
|
push @{$VersionList}, $Param{VersionID};
|
|
}
|
|
elsif ( $Param{VersionIDs} ) {
|
|
push @{$VersionList}, @{ $Param{VersionIDs} };
|
|
}
|
|
else {
|
|
# get version list
|
|
$VersionList = $Self->VersionList(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
}
|
|
|
|
return 1 if !scalar @{$VersionList};
|
|
|
|
# get config item id for version (needed for event handling)
|
|
my $ConfigItemID = $Param{ConfigItemID};
|
|
|
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
|
|
|
# to store unique config item ids if there are versions from different config items
|
|
my %ConfigItemIDs;
|
|
|
|
my $Success;
|
|
for my $VersionID ( @{$VersionList} ) {
|
|
|
|
# get configitem id if neccessary
|
|
if ( $Param{VersionID} || $Param{VersionIDs} ) {
|
|
$ConfigItemID = $Self->VersionConfigItemIDGet(
|
|
VersionID => $VersionID,
|
|
);
|
|
}
|
|
|
|
# remember the unique config item ids
|
|
$ConfigItemIDs{$ConfigItemID} = 1;
|
|
|
|
# delete the xml version data
|
|
$Self->_XMLVersionDelete(
|
|
VersionID => $VersionID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# delete version
|
|
$Success = $Kernel::OM->Get('Kernel::System::DB')->Do(
|
|
SQL => "DELETE FROM configitem_version WHERE id = ?",
|
|
Bind => [ \$VersionID ],
|
|
);
|
|
|
|
# trigger VersionDelete event when deletion was successful
|
|
if ($Success) {
|
|
|
|
$Self->EventHandler(
|
|
Event => 'VersionDelete',
|
|
Data => {
|
|
ConfigItemID => $ConfigItemID,
|
|
Comment => $VersionID,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# delete affected caches
|
|
my $CacheKey = 'VersionGet::VersionID::' . $VersionID . '::XMLData::';
|
|
for my $XMLData (qw(0 1)) {
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey . $XMLData,
|
|
);
|
|
}
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => 'VersionNameGet::VersionID::' . $VersionID,
|
|
);
|
|
|
|
delete $Self->{Cache}->{VersionConfigItemIDGet}->{$VersionID};
|
|
}
|
|
}
|
|
|
|
for my $ConfigItemID ( sort keys %ConfigItemIDs ) {
|
|
|
|
# delete affected caches for ConfigItemID (most recent version might have been removed)
|
|
my $CacheKey = 'VersionGet::ConfigItemID::' . $ConfigItemID . '::XMLData::';
|
|
for my $XMLData (qw(0 1)) {
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey . $XMLData,
|
|
);
|
|
}
|
|
$CacheObject->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => 'VersionNameGet::ConfigItemID::' . $ConfigItemID,
|
|
);
|
|
}
|
|
|
|
return $Success;
|
|
}
|
|
|
|
=head2 VersionSearch()
|
|
|
|
return a config item list as an array reference
|
|
|
|
my $ConfigItemIDs = $ConfigItemObject->VersionSearch(
|
|
Name => 'The Name', # (optional)
|
|
ClassIDs => [ 9, 8, 7, 6 ], # (optional)
|
|
DeplStateIDs => [ 321, 123 ], # (optional)
|
|
InciStateIDs => [ 321, 123 ], # (optional)
|
|
|
|
PreviousVersionSearch => 1, # (optional) default 0 (0|1)
|
|
|
|
OrderBy => [ 'ConfigItemID', 'Number' ], # (optional)
|
|
# default: [ 'ConfigItemID' ]
|
|
# (ConfigItemID, Name, Number, ClassID, DeplStateID, InciStateID
|
|
# CreateTime, CreateBy, ChangeTime, ChangeBy)
|
|
|
|
# Additional information for OrderBy:
|
|
# The OrderByDirection can be specified for each OrderBy attribute.
|
|
# The pairing is made by the array indices.
|
|
|
|
OrderByDirection => [ 'Up', 'Down' ], # (optional)
|
|
# default: [ 'Up' ]
|
|
# (Down | Up)
|
|
|
|
Limit => 122, # (optional)
|
|
UsingWildcards => 0, # (optional) default 1
|
|
);
|
|
|
|
=cut
|
|
|
|
sub VersionSearch {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# set default values
|
|
if ( !defined $Param{UsingWildcards} ) {
|
|
$Param{UsingWildcards} = 1;
|
|
}
|
|
|
|
# verify that all passed array parameters contain an arrayref
|
|
ARGUMENT:
|
|
for my $Argument (
|
|
qw(
|
|
OrderBy
|
|
OrderByDirection
|
|
)
|
|
)
|
|
{
|
|
if ( !defined $Param{$Argument} ) {
|
|
$Param{$Argument} ||= [];
|
|
|
|
next ARGUMENT;
|
|
}
|
|
|
|
if ( ref $Param{$Argument} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "$Argument must be an array reference!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# set default order and order direction
|
|
if ( !@{ $Param{OrderBy} } ) {
|
|
$Param{OrderBy} = ['ConfigItemID'];
|
|
}
|
|
if ( !@{ $Param{OrderByDirection} } ) {
|
|
$Param{OrderByDirection} = ['Up'];
|
|
}
|
|
|
|
# define order table
|
|
my %OrderByTable = (
|
|
ConfigItemID => 'vr.configitem_id',
|
|
Name => 'vr.name',
|
|
Number => 'ci.configitem_number',
|
|
ClassID => 'ci.class_id',
|
|
DeplStateID => 'vr.depl_state_id',
|
|
InciStateID => 'vr.inci_state_id',
|
|
CreateTime => 'ci.create_time',
|
|
CreateBy => 'ci.create_by',
|
|
|
|
# the change time of the CI is the same as the create time of the version!
|
|
ChangeTime => 'vr.create_time',
|
|
|
|
ChangeBy => 'ci.change_by',
|
|
);
|
|
|
|
# check if OrderBy contains only unique valid values
|
|
my %OrderBySeen;
|
|
for my $OrderBy ( @{ $Param{OrderBy} } ) {
|
|
|
|
if ( !$OrderBy || !$OrderByTable{$OrderBy} || $OrderBySeen{$OrderBy} ) {
|
|
|
|
# found an error
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "OrderBy contains invalid value '$OrderBy' "
|
|
. 'or the value is used more than once!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# remember the value to check if it appears more than once
|
|
$OrderBySeen{$OrderBy} = 1;
|
|
}
|
|
|
|
# check if OrderByDirection array contains only 'Up' or 'Down'
|
|
DIRECTION:
|
|
for my $Direction ( @{ $Param{OrderByDirection} } ) {
|
|
|
|
# only 'Up' or 'Down' allowed
|
|
next DIRECTION if $Direction eq 'Up';
|
|
next DIRECTION if $Direction eq 'Down';
|
|
|
|
# found an error
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "OrderByDirection can only contain 'Up' or 'Down'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# assemble the ORDER BY clause
|
|
my @SQLOrderBy;
|
|
my $Count = 0;
|
|
my @OrderBySelectColumns;
|
|
for my $OrderBy ( @{ $Param{OrderBy} } ) {
|
|
|
|
# set the default order direction
|
|
my $Direction = 'DESC';
|
|
|
|
# add the given order direction
|
|
if ( $Param{OrderByDirection}->[$Count] ) {
|
|
if ( $Param{OrderByDirection}->[$Count] eq 'Up' ) {
|
|
$Direction = 'ASC';
|
|
}
|
|
elsif ( $Param{OrderByDirection}->[$Count] eq 'Down' ) {
|
|
$Direction = 'DESC';
|
|
}
|
|
}
|
|
|
|
# add SQL
|
|
push @SQLOrderBy, "$OrderByTable{$OrderBy} $Direction";
|
|
push @OrderBySelectColumns, $OrderByTable{$OrderBy};
|
|
|
|
}
|
|
continue {
|
|
$Count++;
|
|
}
|
|
|
|
# get like escape string needed for some databases (e.g. oracle)
|
|
my $LikeEscapeString = $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('LikeEscapeString');
|
|
|
|
# add name to sql where array
|
|
my @SQLWhere;
|
|
if ( defined $Param{Name} && $Param{Name} ne '' ) {
|
|
|
|
# duplicate the name
|
|
my $Name = $Param{Name};
|
|
|
|
# quote
|
|
$Name = $Kernel::OM->Get('Kernel::System::DB')->Quote($Name);
|
|
|
|
if ( $Param{UsingWildcards} ) {
|
|
|
|
# prepare like string
|
|
$Self->_PrepareLikeString( \$Name );
|
|
|
|
push @SQLWhere, "LOWER(vr.name) LIKE LOWER('$Name') $LikeEscapeString";
|
|
}
|
|
else {
|
|
push @SQLWhere, "LOWER(vr.name) = LOWER('$Name')";
|
|
}
|
|
}
|
|
|
|
# set array params
|
|
my %ArrayParams = (
|
|
ClassIDs => 'ci.id = vr.configitem_id AND ci.class_id',
|
|
DeplStateIDs => 'vr.depl_state_id',
|
|
InciStateIDs => 'vr.inci_state_id',
|
|
);
|
|
|
|
ARRAYPARAM:
|
|
for my $ArrayParam ( sort keys %ArrayParams ) {
|
|
|
|
next ARRAYPARAM if !$Param{$ArrayParam};
|
|
|
|
if ( ref $Param{$ArrayParam} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "$ArrayParam must be an array reference!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
next ARRAYPARAM if !@{ $Param{$ArrayParam} };
|
|
|
|
# quote as integer
|
|
for my $OneParam ( @{ $Param{$ArrayParam} } ) {
|
|
$OneParam = $Kernel::OM->Get('Kernel::System::DB')->Quote( $OneParam, 'Integer' );
|
|
}
|
|
|
|
# create string
|
|
my $InString = join q{, }, @{ $Param{$ArrayParam} };
|
|
|
|
push @SQLWhere, "$ArrayParams{ $ArrayParam } IN ($InString)";
|
|
}
|
|
|
|
# add previous version param
|
|
if ( !$Param{PreviousVersionSearch} ) {
|
|
push @SQLWhere, 'ci.last_version_id = vr.id';
|
|
}
|
|
|
|
# create where string
|
|
my $WhereString = @SQLWhere ? ' WHERE ' . join q{ AND }, @SQLWhere : '';
|
|
|
|
# set limit, quote as integer
|
|
if ( $Param{Limit} ) {
|
|
$Param{Limit} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{Limit}, 'Integer' );
|
|
}
|
|
|
|
# add the order by columns also to the selected columns
|
|
my $OrderBySelectString = '';
|
|
if (@OrderBySelectColumns) {
|
|
$OrderBySelectString = join ', ', @OrderBySelectColumns;
|
|
$OrderBySelectString = ', ' . $OrderBySelectString;
|
|
}
|
|
|
|
# build SQL
|
|
my $SQL = "SELECT DISTINCT vr.configitem_id $OrderBySelectString "
|
|
. 'FROM configitem ci, configitem_version vr '
|
|
. $WhereString;
|
|
|
|
# add the ORDER BY clause
|
|
if (@SQLOrderBy) {
|
|
$SQL .= ' ORDER BY ';
|
|
$SQL .= join ', ', @SQLOrderBy;
|
|
$SQL .= ' ';
|
|
}
|
|
|
|
# ask the database
|
|
$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Limit => $Param{Limit},
|
|
);
|
|
|
|
# fetch the result
|
|
my @ConfigItemList;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
push @ConfigItemList, $Row[0];
|
|
}
|
|
|
|
return \@ConfigItemList;
|
|
}
|
|
|
|
=head1 INTERNAL INTERFACE
|
|
|
|
=head2 _GetEvents()
|
|
|
|
This method checks what values were changed and what events have to be triggered.
|
|
It returns a hash reference with all event names as keys that should be triggered.
|
|
|
|
my $Events = $CIObject->_GetEvents(
|
|
Param => {
|
|
DeplStateID => 123,
|
|
},
|
|
ConfigItemInfo => {
|
|
DeplStateID => 234,
|
|
},
|
|
);
|
|
|
|
print keys %{$Events}; # prints "DeploymentStateUpdate"
|
|
|
|
=cut
|
|
|
|
sub _GetEvents {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Needed (qw(ConfigItemInfo Param)) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my $Events = {};
|
|
|
|
# check old and new name
|
|
my $OldName = $Param{ConfigItemInfo}->{Name} || '';
|
|
my $NewName = $Param{Param}->{Name} || '';
|
|
|
|
if ( $OldName ne $NewName ) {
|
|
$Events->{NameUpdate} = $NewName . '%%' . $OldName;
|
|
}
|
|
|
|
# if depl_state is updated
|
|
my $LastDeplStateID = $Param{ConfigItemInfo}->{DeplStateID} || '';
|
|
my $CurDeplStateID = $Param{Param}->{DeplStateID} || '';
|
|
|
|
if ( $LastDeplStateID ne $CurDeplStateID ) {
|
|
$Events->{DeploymentStateUpdate} = $CurDeplStateID . '%%' . $LastDeplStateID;
|
|
}
|
|
|
|
# if incistate is updated
|
|
my $LastInciStateID = $Param{ConfigItemInfo}->{InciStateID} || '';
|
|
my $CurInciStateID = $Param{Param}->{InciStateID} || '';
|
|
|
|
if ( $LastInciStateID ne $CurInciStateID ) {
|
|
$Events->{IncidentStateUpdate} = $CurInciStateID . '%%' . $LastInciStateID;
|
|
}
|
|
|
|
# check old and new definition_id
|
|
my $OldDefinitionID = $Param{ConfigItemInfo}->{DefinitionID} || '';
|
|
my $NewDefinitionID = $Param{Param}->{DefinitionID} || '';
|
|
|
|
if ( $OldDefinitionID ne $NewDefinitionID ) {
|
|
$Events->{DefinitionUpdate} = $NewDefinitionID;
|
|
}
|
|
|
|
# store if anything like Name, DeploymentState, IncidentState, Definition has been changed.
|
|
my $NothingElseChanged;
|
|
if ( !%{$Events} ) {
|
|
$NothingElseChanged = 1;
|
|
}
|
|
|
|
# check for changes in XML data
|
|
if ( $Param{Param}->{XMLData} && ref $Param{Param}->{XMLData} eq 'ARRAY' ) {
|
|
my %UpdateValues = $Self->_FindChangedXMLValues(
|
|
ConfigItemID => $Param{Param}->{ConfigItemID},
|
|
NewXMLData => $Param{Param}->{XMLData},
|
|
DefinitionID => $NewDefinitionID,
|
|
NothingElseChanged => $NothingElseChanged,
|
|
);
|
|
|
|
if (%UpdateValues) {
|
|
|
|
# if only attributes have changed that should not create a new version
|
|
if ( $UpdateValues{UpdateLastVersion} && $NothingElseChanged ) {
|
|
|
|
$Events->{UpdateLastVersion} = 1;
|
|
|
|
# Clone the data structure, so we can delete the origial.
|
|
$Events->{UpdateXMLData} = $Kernel::OM->Get('Kernel::System::Storable')->Clone(
|
|
Data => $UpdateValues{UpdateXMLData},
|
|
);
|
|
|
|
# We need to delete the flag and data again, to not cause any problems later
|
|
# when processing the values.
|
|
delete $UpdateValues{UpdateLastVersion};
|
|
delete $UpdateValues{UpdateXMLData};
|
|
}
|
|
|
|
$Events->{ValueUpdate} = \%UpdateValues;
|
|
}
|
|
}
|
|
|
|
return $Events;
|
|
}
|
|
|
|
=head2 _EventHandlerForChangedXMLValues()
|
|
|
|
This method calls the event handler for each changed value of the config item.
|
|
The changed values are passed in C<UpdateValues> as an hashref with tag-keys as keys.
|
|
Please note that this only handles values inside the XML structure, not general
|
|
attributes like C<CurInciState>.
|
|
|
|
my $Success = $ConfigItemObject->_EventHandlerForChangedXMLValues(
|
|
ConfigItemID => 123,
|
|
UpdateValues =>
|
|
{
|
|
"[1]{'Version'}[1]{'Vendor'}[1]" => 'OldVendor%%NewVendor',
|
|
"[1]{'Version'}[1]{'Type'}[1]" => '127%%128',
|
|
}
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _EventHandlerForChangedXMLValues {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Needed (qw(UpdateValues ConfigItemID UserID)) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# trigger ValueUpdate event for each changed value
|
|
for my $Key ( sort keys %{ $Param{UpdateValues} } ) {
|
|
$Self->EventHandler(
|
|
Event => 'ValueUpdate',
|
|
Data => {
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
Comment => $Key . '%%' . $Param{UpdateValues}->{$Key},
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _FindChangedXMLValues()
|
|
|
|
compares the new xml data C<NewXMLData> with the xml data of the latest version
|
|
of the config item C<ConfigItemID>. Note that the new XML data does not contain tag keys.
|
|
All values of the two data sets are compared.
|
|
When a changed value is encountered, the tag key and the old and the new value are stored in a hash.
|
|
The hash with the updated values is returned.
|
|
|
|
my %UpdateValues = $ConfigItemObject->_FindChangedXMLValues(
|
|
ConfigItemID => 123,
|
|
NewXMLData =>
|
|
[
|
|
undef,
|
|
{
|
|
'Version' =>
|
|
[
|
|
undef,
|
|
{
|
|
'Owner' =>
|
|
[
|
|
undef,
|
|
{
|
|
'Content' => ''
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
);
|
|
|
|
The returned hash looks like:
|
|
|
|
%UpdateValues = (
|
|
"[1]{'Version'}[1]{'Vendor'}[1]" => 'OldVendor%%NewVendor',
|
|
"[1]{'Version'}[1]{'Type'}[1]" => '127%%128',
|
|
);
|
|
|
|
The key is a tag key. The values contains the old and the new XML value.
|
|
|
|
=cut
|
|
|
|
sub _FindChangedXMLValues {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check for needed stuff
|
|
for my $Needed (qw(ConfigItemID NewXMLData DefinitionID)) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get a list with all versionnumbers that exist for the
|
|
# given config item
|
|
my $VersionList = $Self->VersionList(
|
|
ConfigItemID => $Param{ConfigItemID},
|
|
);
|
|
|
|
# skip the check if this is the first version of the item
|
|
return if !@{$VersionList};
|
|
|
|
# get old version
|
|
my $OldVersion = $Self->VersionGet(
|
|
VersionID => $VersionList->[-1],
|
|
);
|
|
|
|
# The short names for new and old xml data are used in the 'eval' below,
|
|
# and we do not want to modify the original parameter data NewXMLData
|
|
# due to side effects of Perls autovivification feature used later here
|
|
# in another eval statement.
|
|
my $NewXMLData = Storable::dclone( $Param{NewXMLData} );
|
|
my $OldXMLData = $OldVersion->{XMLData};
|
|
|
|
# get xml definition
|
|
my $Definition = $Self->DefinitionGet(
|
|
DefinitionID => $Param{DefinitionID},
|
|
);
|
|
|
|
# get all tagkeys in new and old XML data
|
|
# use a side effect of XMLHash2D(), which adds the tag keys to the passed in data structure
|
|
$Kernel::OM->Get('Kernel::System::XML')->XMLHash2D( XMLHash => $NewXMLData );
|
|
my @TagKeys = $Self->_GrabTagKeys( Data => [ $OldXMLData, $NewXMLData ] );
|
|
|
|
# get an unique list of all tag keys
|
|
my %UniqueTagKeys = map { $_ => 1 } @TagKeys;
|
|
|
|
my %UpdateValues;
|
|
my %SuppressVersionAdd;
|
|
|
|
for my $TagKey ( sort keys %UniqueTagKeys ) {
|
|
my $NewContent = eval '$NewXMLData->' . $TagKey . '->{Content}' || ''; ## no critic
|
|
my $OldContent = eval '$OldXMLData->' . $TagKey . '->{Content}' || ''; ## no critic
|
|
|
|
if ( $NewContent ne $OldContent ) {
|
|
|
|
# extract attribute path name
|
|
my $AttributePath = $TagKey;
|
|
$AttributePath =~ s{ \A \[.*?\] \{'Version'\} \[.*?\] \{' }{}xms;
|
|
$AttributePath =~ s{ '\} \[.*?\] \{' }{::}xmsg;
|
|
$AttributePath =~ s{ '\} \[.*?\] \z }{}xms;
|
|
|
|
# get definition info about attribute
|
|
my $AttributeInfo = $Self->DefinitionAttributeInfo(
|
|
Definition => $Definition->{DefinitionRef},
|
|
AttributePath => $AttributePath,
|
|
);
|
|
|
|
# check if this attribute should be suppressed and nothing else of
|
|
# Name, DeploymentState, IncidentState, Definition has been changed.
|
|
if ( $AttributeInfo->{SuppressVersionAdd} && $Param{NothingElseChanged} ) {
|
|
|
|
if ( $AttributeInfo->{SuppressVersionAdd} eq 'UpdateLastVersion' ) {
|
|
$SuppressVersionAdd{UpdateLastVersion}->{$TagKey} = join '%%', $OldContent, $NewContent;
|
|
|
|
# Build a new data structire only with the update values.
|
|
eval '$SuppressVersionAdd{UpdateXMLData}->' . $TagKey . '->{Content} = $NewContent'; ## no critic
|
|
}
|
|
elsif ( $AttributeInfo->{SuppressVersionAdd} eq 'Ignore' ) {
|
|
$SuppressVersionAdd{Ignore}->{$TagKey} = join '%%', $OldContent, $NewContent;
|
|
}
|
|
}
|
|
|
|
# change was found
|
|
$UpdateValues{$TagKey} = join '%%', $OldContent, $NewContent;
|
|
}
|
|
}
|
|
|
|
# count how many of each SuppressVersionAdd types we have.
|
|
my $UpdateValuesCount = scalar keys %UpdateValues;
|
|
my $IgnoreCount = scalar keys %{ $SuppressVersionAdd{Ignore} };
|
|
my $UpdateLastVersionCount = scalar keys %{ $SuppressVersionAdd{UpdateLastVersion} };
|
|
|
|
if (%UpdateValues) {
|
|
|
|
# Update values contains only values that should be ignored.
|
|
if ( $UpdateValuesCount == $IgnoreCount ) {
|
|
%UpdateValues = ();
|
|
}
|
|
|
|
# Update values contains only values that should update the last version or should be ignored,
|
|
elsif (
|
|
( $UpdateValuesCount == $UpdateLastVersionCount )
|
|
|| ( $UpdateValuesCount == ( $UpdateLastVersionCount + $IgnoreCount ) )
|
|
)
|
|
{
|
|
%UpdateValues = %{ $SuppressVersionAdd{UpdateLastVersion} };
|
|
$UpdateValues{UpdateXMLData} = $SuppressVersionAdd{UpdateXMLData};
|
|
$UpdateValues{UpdateLastVersion} = 1;
|
|
}
|
|
}
|
|
|
|
return %UpdateValues;
|
|
}
|
|
|
|
=head2 _GrabTagKeys()
|
|
|
|
recursively scans a perl data structure for the hash key 'TagKey' and returns a
|
|
list of all the values for that key.
|
|
|
|
my @TagKeys = $ConfigItemObject->_GrabTagKeys(
|
|
Data => $XMLHashReferenz,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _GrabTagKeys {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
return () if !$Param{Data};
|
|
|
|
my @TagKeys;
|
|
if ( ref $Param{Data} eq 'ARRAY' ) {
|
|
|
|
ELEM:
|
|
for my $Elem ( @{ $Param{Data} } ) {
|
|
|
|
next ELEM if !$Elem;
|
|
next ELEM if !ref $Elem;
|
|
|
|
push @TagKeys, $Self->_GrabTagKeys( Data => $Elem );
|
|
}
|
|
}
|
|
elsif ( ref $Param{Data} eq 'HASH' ) {
|
|
|
|
for my $Key ( sort keys %{ $Param{Data} } ) {
|
|
|
|
if ( $Key eq 'TagKey' ) {
|
|
push @TagKeys, $Param{Data}->{$Key};
|
|
}
|
|
elsif ( ref $Param{Data}->{$Key} ) {
|
|
push @TagKeys, $Self->_GrabTagKeys( Data => $Param{Data}->{$Key} );
|
|
}
|
|
}
|
|
}
|
|
|
|
return @TagKeys;
|
|
}
|
|
|
|
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
|