Files
scripts/Perl OTRS/Kernel/System/ProcessManagement/DB/Process.pm
2024-10-14 00:08:40 +02:00

2198 lines
69 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::ProcessManagement::DB::Process;
use strict;
use warnings;
use Kernel::System::ProcessManagement::DB::Entity;
use Kernel::System::ProcessManagement::DB::Activity;
use Kernel::System::ProcessManagement::DB::ActivityDialog;
use Kernel::System::ProcessManagement::DB::Process::State;
use Kernel::System::ProcessManagement::DB::Transition;
use Kernel::System::ProcessManagement::DB::TransitionAction;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::Language',
'Kernel::System::Cache',
'Kernel::System::DB',
'Kernel::System::DynamicField',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::User',
'Kernel::System::YAML',
);
=head1 NAME
Kernel::System::ProcessManagement::DB::Process
=head1 DESCRIPTION
Process Management DB Process backend
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{EntityObject} = Kernel::System::ProcessManagement::DB::Entity->new();
$Self->{ActivityDialogObject} = Kernel::System::ProcessManagement::DB::ActivityDialog->new();
$Self->{ActivityObject} = Kernel::System::ProcessManagement::DB::Activity->new();
$Self->{StateObject} = Kernel::System::ProcessManagement::DB::Process::State->new();
$Self->{TransitionObject} = Kernel::System::ProcessManagement::DB::Transition->new();
$Self->{TransitionActionObject} = Kernel::System::ProcessManagement::DB::TransitionAction->new();
# get the cache TTL (in seconds)
$Self->{CacheTTL} = int( $Kernel::OM->Get('Kernel::Config')->Get('Process::CacheTTL') || 3600 );
# set lower if database is case sensitive
$Self->{Lower} = '';
if ( $Kernel::OM->Get('Kernel::System::DB')->GetDatabaseFunction('CaseSensitive') ) {
$Self->{Lower} = 'LOWER';
}
return $Self;
}
=head2 ProcessAdd()
add new Process
returns the id of the created process if success or undef otherwise
my $ID = $ProcessObject->ProcessAdd(
EntityID => 'P1' # mandatory, exportable unique identifier
Name => 'NameOfProcess', # mandatory
StateEntityID => 'S1',
Layout => $LayoutHashRef, # mandatory, diagram objects positions to be stored in
# YAML format
Config => $ConfigHashRef, # mandatory, process configuration to be stored in YAML
# format
UserID => 123, # mandatory
);
Returns:
$ID = 567;
=cut
sub ProcessAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(EntityID Name StateEntityID Layout Config UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!",
);
return;
}
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# check if EntityID already exists
return if !$DBObject->Prepare(
SQL => "
SELECT id
FROM pm_process
WHERE $Self->{Lower}(entity_id) = $Self->{Lower}(?)",
Bind => [ \$Param{EntityID} ],
Limit => 1,
);
my $EntityExists;
while ( my @Data = $DBObject->FetchrowArray() ) {
$EntityExists = 1;
}
if ($EntityExists) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "The EntityID:$Param{EntityID} already exists for a process!"
);
return;
}
# check config valid format (at least it must contain the description)
if ( !IsHashRefWithData( $Param{Config} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Config needs to be a valid Hash reference!",
);
return;
}
if ( !$Param{Config}->{Description} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Description in Config!",
);
return;
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
# dump layout and config as string
my $Layout = $YAMLObject->Dump( Data => $Param{Layout} );
my $Config = $YAMLObject->Dump( Data => $Param{Config} );
# Make sure the resulting string has the UTF-8 flag. YAML only sets it if
# part of the data already had it.
utf8::upgrade($Layout);
utf8::upgrade($Config);
# SQL
return if !$DBObject->Do(
SQL => '
INSERT INTO pm_process ( entity_id, name, state_entity_id, layout, config, create_time,
create_by, change_time, change_by )
VALUES (?, ?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
Bind => [
\$Param{EntityID}, \$Param{Name}, \$Param{StateEntityID}, \$Layout, \$Config,
\$Param{UserID}, \$Param{UserID},
],
);
return if !$DBObject->Prepare(
SQL => 'SELECT id FROM pm_process WHERE entity_id = ?',
Bind => [ \$Param{EntityID} ],
);
my $ID;
while ( my @Row = $DBObject->FetchrowArray() ) {
$ID = $Row[0];
}
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ProcessManagement_Process',
);
return if !$ID;
return $ID;
}
=head2 ProcessDelete()
delete a Process
returns 1 if success or undef otherwise
my $Success = $ProcessObject->ProcessDelete(
ID => 123,
UserID => 123,
);
=cut
sub ProcessDelete {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(ID UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!"
);
return;
}
}
# check if exists
my $Process = $Self->ProcessGet(
ID => $Param{ID},
UserID => 1,
);
return if !IsHashRefWithData($Process);
# delete process
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM pm_process WHERE id = ?',
Bind => [ \$Param{ID} ],
);
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ProcessManagement_Process',
);
return 1;
}
=head2 ProcessGet()
get Process attributes
my $Process = $ProcessObject->ProcessGet(
ID => 123, # ID or EntityID is needed
EntityID => 'P1',
ActivityNames => 1, # default 0, 1 || 0, if 0 returns an Activities array
# with the activity entity IDs, if 1 returns an
# Activities hash with the activity entity IDs as
# keys and Activity Names as values
TransitionNames => 1, # default 0, 1 || 0, if 0 returns an Transitions array
# with the transition entity IDs, if 1 returns an
# Transitions hash with the transition entity IDs as
# keys and Transition Names as values
TransitionActionNames => 1, # default 0, 1 || 0, if 0 returns an TransitionActions array
# with the TransitionAction entity IDs, if 1 returns an
# TransitionAction hash with the TransitionAction entity IDs as
# keys and TransitionAction Names as values
UserID => 123, # mandatory
);
Returns:
$Process = {
ID => 123,
EntityID => 'P1',
Name => 'some name',
StateEntityID => 'S1',
State => 'Active',
Layout => $LayoutHashRef,
Config => $ConfigHashRef,
Activities => ['A1','A2','A3'],
Activities => ['T1','T2','T3'],
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
};
$Process = {
ID => 123,
EntityID => 'P1',
Name => 'some name',
StateEntityID => 'S1',
State => 'Active',
Layout => $LayoutHashRef,
Config => $ConfigHashRef,
Activities => {
'A1' => 'Activity1',
'A2' => 'Activity2',
'A3' => 'Activity3',
};
Transitions => {
'T1' => 'Transition1',
'T2' => 'Transition2',
'T3' => 'Transition3',
};
TransitionActions => {
'TA1' => 'TransitionAction1',
'TA2' => 'TransitionAction2',
'TA3' => 'TransitionAction3',
};
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
};
=cut
sub ProcessGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ID} && !$Param{EntityID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ID or EntityID!'
);
return;
}
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
my $ActivityNames = 0;
if ( defined $Param{ActivityNames} && $Param{ActivityNames} == 1 ) {
$ActivityNames = 1;
}
my $TransitionNames = 0;
if ( defined $Param{TransitionNames} && $Param{TransitionNames} == 1 ) {
$TransitionNames = 1;
}
my $TransitionActionNames = 0;
if ( defined $Param{TransitionActionNames} && $Param{TransitionActionNames} == 1 ) {
$TransitionActionNames = 1;
}
# check cache
my $CacheKey;
if ( $Param{ID} ) {
$CacheKey = 'ProcessGet::ID::' . $Param{ID} . '::ActivityNames::'
. $ActivityNames
. '::TransitionNames::'
. $TransitionNames
. '::TransitionActionNames::'
. $TransitionActionNames;
}
else {
$CacheKey = 'ProcessGet::EntityID::' . $Param{EntityID} . '::ActivityNames::'
. $ActivityNames
. '::TransitionNames::'
. $TransitionNames
. '::TransitionActionNames::'
. $TransitionActionNames;
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
my $Cache = $CacheObject->Get(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
);
return $Cache if $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# SQL
if ( $Param{ID} ) {
return if !$DBObject->Prepare(
SQL => '
SELECT id, entity_id, name, state_entity_id, layout, config, create_time,
change_time
FROM pm_process
WHERE id = ?',
Bind => [ \$Param{ID} ],
Limit => 1,
);
}
else {
return if !$DBObject->Prepare(
SQL => '
SELECT id, entity_id, name, state_entity_id, layout, config, create_time,
change_time
FROM pm_process
WHERE entity_id = ?',
Bind => [ \$Param{EntityID} ],
Limit => 1,
);
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
my %Data;
while ( my @Data = $DBObject->FetchrowArray() ) {
my $Layout = $YAMLObject->Load( Data => $Data[4] );
my $Config = $YAMLObject->Load( Data => $Data[5] );
%Data = (
ID => $Data[0],
EntityID => $Data[1],
Name => $Data[2],
StateEntityID => $Data[3],
Layout => $Layout,
Config => $Config,
CreateTime => $Data[6],
ChangeTime => $Data[7],
);
}
return if !$Data{ID};
# create the ActivityList
if ($ActivityNames) {
my %Activities;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
my $ActivityList = $Self->{ActivityObject}->ActivityList(
UseEntities => 1,
UserID => 1,
);
for my $ActivityEntityID ( sort keys %{ $Data{Config}->{Path} } ) {
$Activities{$ActivityEntityID} = $ActivityList->{$ActivityEntityID};
}
}
$Data{Activities} = \%Activities;
}
else {
my @Activities;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
# get path keys (ActivityEntityIDs) and map them into an array
@Activities = map {$_} sort keys %{ $Data{Config}->{Path} };
}
$Data{Activities} = \@Activities;
}
# create the transition list
if ($TransitionNames) {
my %Transitions;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
my $TransitionList = $Self->{TransitionObject}->TransitionList(
UseEntities => 1,
UserID => 1,
);
for my $ActivityEntityID ( sort keys %{ $Data{Config}->{Path} } ) {
for my $TransitionEntityID (
sort keys %{ $Data{Config}->{Path}->{$ActivityEntityID} }
)
{
$Transitions{$TransitionEntityID} = $TransitionList->{$TransitionEntityID};
}
}
}
$Data{Transitions} = \%Transitions;
}
else {
my @Transitions;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
for my $ActivityEntityID ( sort keys %{ $Data{Config}->{Path} } ) {
for my $TransitionEntityID (
sort keys %{ $Data{Config}->{Path}->{$ActivityEntityID} }
)
{
push @Transitions, $TransitionEntityID;
}
}
}
$Data{Transitions} = \@Transitions;
}
# create the transition action list
if ($TransitionActionNames) {
my %TransitionActions;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
my $TransitionActionList = $Self->{TransitionActionObject}->TransitionActionList(
UseEntities => 1,
UserID => 1,
);
for my $ActivityEntityID ( sort keys %{ $Data{Config}->{Path} } ) {
my $TransitionPath = $Data{Config}->{Path}->{$ActivityEntityID};
for my $TransitionEntityID ( sort keys %{$TransitionPath} ) {
my $TransitionActionPath = $Data{Config}->{Path}->{$ActivityEntityID}->{$TransitionEntityID}
->{Action};
if ( $TransitionActionPath && @{$TransitionActionPath} ) {
for my $TransitionActionEntityID ( sort @{$TransitionActionPath} ) {
$TransitionActions{$TransitionActionEntityID}
= $TransitionActionList->{$TransitionEntityID};
}
}
}
}
}
$Data{TransitionActions} = \%TransitionActions;
}
else {
my @TransitionActions;
if ( IsHashRefWithData( $Data{Config}->{Path} ) ) {
for my $ActivityEntityID ( sort keys %{ $Data{Config}->{Path} } ) {
my $TransitionPath = $Data{Config}->{Path}->{$ActivityEntityID};
for my $TransitionEntityID ( sort keys %{$TransitionPath} ) {
my $TransitionActionPath = $TransitionPath->{$TransitionEntityID}->{TransitionAction};
if ( $TransitionActionPath && @{$TransitionActionPath} ) {
for my $TransitionActionEntityID ( sort @{$TransitionActionPath} ) {
push @TransitionActions, $TransitionActionEntityID;
}
}
}
}
}
$Data{TransitionActions} = \@TransitionActions;
}
$Data{State} = $Self->{StateObject}->StateLookup(
EntityID => $Data{StateEntityID},
UserID => 1,
);
# set cache
$CacheObject->Set(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
Value => \%Data,
TTL => $Self->{CacheTTL},
);
return \%Data;
}
=head2 ProcessUpdate()
update Process attributes
returns 1 if success or undef otherwise
my $Success = $ProcessObject->ProcessUpdate(
ID => 123, # mandatory
EntityID => 'P1' # mandatory, exportable unique identifier
Name => 'NameOfProcess', # mandatory
StateentityID => 'S1',
Layout => $LayoutHashRef, # mandatory, diagram objects positions to be stored in
# YAML format
Config => $ConfigHashRef, # mandatory, process configuration to be stored in YAML
# format
UserID => 123, # mandatory
);
=cut
sub ProcessUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Key (qw(ID EntityID Name StateEntityID Layout Config UserID)) {
if ( !$Param{$Key} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Key!"
);
return;
}
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# check if EntityID already exists
return if !$DBObject->Prepare(
SQL => "
SELECT id FROM pm_process
WHERE $Self->{Lower}(entity_id) = $Self->{Lower}(?)
AND id != ?",
Bind => [ \$Param{EntityID}, \$Param{ID} ],
LIMIT => 1,
);
my $EntityExists;
while ( my @Data = $DBObject->FetchrowArray() ) {
$EntityExists = 1;
}
if ($EntityExists) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "The EntityID:$Param{Name} already exists for a process!",
);
return;
}
# check config valid format (at least it must contain the description)
if ( !IsHashRefWithData( $Param{Config} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Config needs to be a valid Hash reference!",
);
return;
}
if ( !$Param{Config}->{Description} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Description in Config!",
);
return;
}
# get yaml object
my $YAMLObject = $Kernel::OM->Get('Kernel::System::YAML');
# dump layout and config as string
my $Layout = $YAMLObject->Dump( Data => $Param{Layout} );
my $Config = $YAMLObject->Dump( Data => $Param{Config} );
# Make sure the resulting string has the UTF-8 flag. YAML only sets it if
# part of the data already had it.
utf8::upgrade($Layout);
utf8::upgrade($Config);
# check if need to update db
return if !$DBObject->Prepare(
SQL => '
SELECT entity_id, name, state_entity_id, layout, config
FROM pm_process
WHERE id = ?',
Bind => [ \$Param{ID} ],
Limit => 1,
);
my $CurrentEntityID;
my $CurrentName;
my $CurrentStateEntityID;
my $CurrentLayout;
my $CurrentConfig;
while ( my @Data = $DBObject->FetchrowArray() ) {
$CurrentEntityID = $Data[0];
$CurrentName = $Data[1];
$CurrentStateEntityID = $Data[2];
$CurrentLayout = $Data[3];
$CurrentConfig = $Data[4];
}
if ($CurrentEntityID) {
return 1 if $CurrentEntityID eq $Param{EntityID}
&& $CurrentName eq $Param{Name}
&& $CurrentStateEntityID eq $Param{StateEntityID}
&& $CurrentLayout eq $Layout
&& $CurrentConfig eq $Config;
}
# SQL
return if !$DBObject->Do(
SQL => '
UPDATE pm_process
SET entity_id = ?, name = ?, state_entity_id = ?, layout = ?, config = ?,
change_time = current_timestamp, change_by = ?
WHERE id = ?',
Bind => [
\$Param{EntityID}, \$Param{Name}, \$Param{StateEntityID}, \$Layout, \$Config,
\$Param{UserID}, \$Param{ID},
],
);
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ProcessManagement_Process',
);
return 1;
}
=head2 ProcessList()
get a Process list
my $List = $ProcessObject->ProcessList(
UseEntities => 0, # default 0, 1 || 0. if 0 the return hash keys are
# the process IDs otherwise keys are the
# process entity IDs
StateEntityIDs => ['S1','S2'], # optional, to filter processes that match listed
# state entity IDs
UserID => 1,
);
Returns:
$List = {
1 => 'NameOfProcess',
}
or
$List = {
'P1' => 'NameOfProcess',
}
=cut
sub ProcessList {
my ( $Self, %Param ) = @_;
# check needed
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID!"
);
return;
}
my $StateEntityIDsStrg;
if ( !IsArrayRefWithData( $Param{StateEntityIDs} ) ) {
$StateEntityIDsStrg = 'ALL';
}
else {
$StateEntityIDsStrg = join ',', @{ $Param{StateEntityIDs} };
}
# check cache
my $UseEntities = 0;
if ( defined $Param{UseEntities} && $Param{UseEntities} ) {
$UseEntities = 1;
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
my $CacheKey = 'ProcessList::UseEntities::' . $UseEntities . '::StateEntityIDs::'
. $StateEntityIDsStrg;
my $Cache = $CacheObject->Get(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
);
return $Cache if ref $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $SQL = '
SELECT id, entity_id, name
FROM pm_process ';
if ( $StateEntityIDsStrg ne 'ALL' ) {
my $StateEntityIDsStrgDB =
join ',', map { "'" . $DBObject->Quote($_) . "'" } @{ $Param{StateEntityIDs} };
$SQL .= "WHERE state_entity_id IN ($StateEntityIDsStrgDB)";
}
return if !$DBObject->Prepare( SQL => $SQL );
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
if ( !$UseEntities ) {
$Data{ $Row[0] } = $Row[2];
}
else {
$Data{ $Row[1] } = $Row[2];
}
}
# set cache
$CacheObject->Set(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
Value => \%Data,
TTL => $Self->{CacheTTL},
);
return \%Data;
}
=head2 ProcessListGet()
get a Process list with all process details
my $List = $ProcessObject->ProcessListGet(
UserID => 1,
);
Returns:
$List = [
{
ID => 123,
EntityID => 'P1',
Name => 'some name',
StateEntityID => 'S1',
State => 'Active',
Layout => $LayoutHashRef,
Config => $ConfigHashRef,
Activities => ['A1','A2','A3'],
CreateTime => '2012-07-04 15:08:00',
ChangeTime => '2012-07-04 15:08:00',
},
{
ID => 456,
EntityID => 'P2',
Name => 'some name',
StateEntityID => 'S1',
State => 'Active',
Layout => $LayoutHashRef,
Config => $ConfigHashRef,
Activities => ['A3','A4','A5'],
CreateTime => '2012-07-04 15:10:00',
ChangeTime => '2012-07-04 15:10:00',
},
];
=cut
sub ProcessListGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
# get cache object
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
# check cache
my $CacheKey = 'ProcessListGet';
my $Cache = $CacheObject->Get(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
);
return $Cache if $Cache;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# SQL
return if !$DBObject->Prepare(
SQL => '
SELECT id, entity_id
FROM pm_process
ORDER BY id',
);
my @ProcessIDs;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @ProcessIDs, $Row[0];
}
my @Data;
for my $ItemID (@ProcessIDs) {
my $ProcessData = $Self->ProcessGet(
ID => $ItemID,
UserID => 1,
);
push @Data, $ProcessData;
}
# set cache
$CacheObject->Set(
Type => 'ProcessManagement_Process',
Key => $CacheKey,
Value => \@Data,
TTL => $Self->{CacheTTL},
);
return \@Data;
}
=head2 ProcessSearch()
search processes by process name
my $ProcessEntityIDs = $ProcessObject->ProcessSearch(
ProcessName => 'SomeText', # e. g. "SomeText*", "Some*ext" or ['*SomeTest1*', '*SomeTest2*']
);
Returns:
$ProcessEntityIDs = [ 'Process-e11e2e9aa83344a235279d4f6babc6ec', 'Process-f8194a25ab0ccddefeb4240c281c1f56' ];
=cut
sub ProcessSearch {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ProcessName} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ProcessName!',
);
return;
}
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
my $SQL = 'SELECT DISTINCT entity_id
FROM pm_process ';
# if it's no ref, put it to array ref
if ( ref $Param{ProcessName} eq '' ) {
$Param{ProcessName} = [ $Param{ProcessName} ];
}
if ( IsArrayRefWithData( $Param{ProcessName} ) ) {
$SQL .= ' WHERE' if IsArrayRefWithData( $Param{ProcessName} );
}
my @QuotedSearch;
my $SQLOR = 0;
VALUE:
for my $Value ( @{ $Param{ProcessName} } ) {
next VALUE if !defined $Value || !length $Value;
$Value = '%' . $DBObject->Quote( $Value, 'Like' ) . '%';
$Value =~ s/\*/%/g;
$Value =~ s/%%/%/gi;
if ($SQLOR) {
$SQL .= ' OR';
}
$SQL .= ' name LIKE ? ';
push @QuotedSearch, $Value;
$SQLOR = 1;
}
if ( IsArrayRefWithData( $Param{ProcessName} ) ) {
$SQL .= $DBObject->GetDatabaseFunction('LikeEscapeString');
}
$SQL .= ' ORDER BY entity_id';
return if !$DBObject->Prepare(
SQL => $SQL,
Bind => [ \(@QuotedSearch) ]
);
my @Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
push @Data, $Row[0];
}
return \@Data;
}
=head2 ProcessDump()
gets a complete processes information dump from the DB including: Process State, Activities,
ActivityDialogs, Transitions and TransitionActions
my $ProcessDump = $ProcessObject->ProcessDump(
ResultType => 'SCALAR' # 'SCALAR' || 'HASH' || 'FILE'
Location => '/opt/otrs/var/myfile.txt' # mandatory for ResultType = 'FILE'
UserID => 1,
);
Returns:
$ProcessDump = '
$Self->{'Process'} = {
'P1' => {
'Name' => 'Process 1',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'Path' => {
'A1' => {
'T1' => {
'Action' => [
'TA1',
],
}
},
'StartActivity' => 'A1',
'StartActivityDialog' => 'AD1',
'State' => 'S1'
},
# ...
};
$Self->{'Process::State'} = {
'S1' => 'Active',
'S2' => 'Inactive',
'S3' => 'FadeAway'
};
$Self->{'Process::Activity'} = {
'A1' => {
'Name' => 'Activity 1'
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'ActivityDialog' => {
'1' => 'AD1',
}
},
},
# ...
};
$Self->{'Process::ActivityDialog'} = {
'AD1' => {
'Name' => 'Activity Dialog 1',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'FieldOrder' => [
'StateID',
'DynamicField_Marke',
],
'Fields' => {
'StateID' => {
'DefaultValue' => '1',
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'Display' => '0'
},
'DynamicField_Marke' => {
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'Display' => '2'
},
},
#...
};
$Self->{'Process::Transition'} = {
'T1' => {
'Name' => 'Transition 1'
'ChangeTime' => '2012-07-21 08:11:33',
'CreateTime' => '2012-07-21 08:11:33',
'Condition' => {
'Type' => 'and',
'Cond1' => {
'Fields' => {
'DynamicField_Marke' => {
'Match' => 'Teststring',
'Type' => 'String',
},
},
'Type' => 'and',
},
},
},
# ...
};
$Self->{'Process::Action'} = {
'TA1' => {
'Name' => 'Queue Move',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'Module' => 'Kernel::System::Process::Transition::Action::QueueMove',
'Config' => {
'NewOwner' => 'root@localhost',
'TargetQueue' => 'Raw',
},
},
# ...
};
';
my $ProcessDump = $ProcessObject->ProcessDump(
ResultType => 'HASH' # 'SCALAR' || 'HASH' || 'FILE'
Location => '/opt/otrs/var/myfile.txt' # mandatory for ResultType = 'FILE'
UserID => 1,
);
Returns:
$ProcessDump = {
Process => {
'P1' => {
'Name' => 'Process 1',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'Path' => {
'A1' => {
'T1' => {
'Action' => [
'TA1',
],
}
},
'StartActivity' => 'A1',
'StartActivityDialog' => 'AD1',
'State' => 'S1'
},
# ...
};
State => {
'S1' => 'Active',
'S2' => 'Inactive',
'S3' => 'FadeAway'
};
Activity => {
'A1' => {
'Name' => 'Activity 1'
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'ActivityDialog' => {
'1' => 'AD1',
}
},
},
# ...
};
ActivityDialog => {
'AD1' => {
'Name' => 'Activity Dialog 1',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'FieldOrder' => [
'StateID',
'DynamicField_Marke',
],
'Fields' => {
'StateID' => {
'DefaultValue' => '1',
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'Display' => '0'
},
'DynamicField_Marke' => {
'DescriptionLong' => 'Longer description',
'DescriptionShort' => 'Short description',
'Display' => '2'
},
},
#...
};
Transition => {
'T1' => {
'Name' => 'Transition 1'
'ChangeTime' => '2012-07-21 08:11:33',
'CreateTime' => '2012-07-21 08:11:33',
'Condition' => {
'Type' => 'and',
'Cond1' => {
'Fields' => {
'DynamicField_Marke' => {
'Match' => 'Teststring',
'Type' => 'String',
},
},
'Type' => 'and',
},
},
},
# ...
};
TransitionAction => {
'TA1' => {
'Name' => 'Queue Move',
'CreateTime' => '2012-07-21 08:11:33',
'ChangeTime' => '2012-07-21 08:11:33',
'Module' => 'Kernel::System::Process::Transition::Action::QueueMove',
'Config' => {
'NewOwner' => 'root@localhost',
'TargetQueue' => 'Raw',
},
},
# ...
};
}
my $ProcessDump = $ProcessObject->ProcessDump(
ResultType => 'Location' # 'SCALAR' || 'HASH' || 'FILE'
Location => '/opt/otrs/var/myfile.txt' # mandatory for ResultType = 'FILE'
UserID => 1,
);
Returns:
$ProcessDump = '/opt/otrs/var/myfile.txt'; # or undef if can't write the file
=cut
sub ProcessDump {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!',
);
return;
}
if ( !defined $Param{ResultType} )
{
$Param{ResultType} = 'SCALAR';
}
if ( $Param{ResultType} eq 'FILE' ) {
if ( !$Param{Location} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Location for ResultType \'FILE\'!',
);
}
}
# get States
my %StateDump = %{ $Self->{StateObject}->StateList( UserID => 1 ) };
# get Processes
my $ProcessList = $Self->ProcessListGet( UserID => 1 );
my %ProcessDump;
PROCESS:
for my $ProcessData ( @{$ProcessList} ) {
next PROCESS if !IsHashRefWithData($ProcessData);
$ProcessDump{ $ProcessData->{EntityID} } = {
Name => $ProcessData->{Name},
CreateTime => $ProcessData->{CreateTime},
ChangeTime => $ProcessData->{ChangeTime},
StateEntityID => $ProcessData->{StateEntityID},
State => $ProcessData->{State},
StartActivity => $ProcessData->{Config}->{StartActivity} || '',
StartActivityDialog => $ProcessData->{Config}->{StartActivityDialog} || '',
Path => $ProcessData->{Config}->{Path} || {},
};
}
# get Activities
my $ActivitiesList = $Self->{ActivityObject}->ActivityListGet( UserID => 1 );
my %ActivityDump;
ACTIVITY:
for my $ActivityData ( @{$ActivitiesList} ) {
next ACTIVITY if !IsHashRefWithData($ActivityData);
$ActivityDump{ $ActivityData->{EntityID} } = {
ID => $ActivityData->{ID},
Name => $ActivityData->{Name},
CreateTime => $ActivityData->{CreateTime},
ChangeTime => $ActivityData->{ChangeTime},
ActivityDialog => $ActivityData->{Config}->{ActivityDialog} || '',
};
}
# get ActivityDialogs
my $ActivityDialogsList = $Self->{ActivityDialogObject}->ActivityDialogListGet( UserID => 1 );
my %ActivityDialogDump;
ACTIVITYDIALOG:
for my $ActivityDialogData ( @{$ActivityDialogsList} ) {
next ACTIVITY if !IsHashRefWithData($ActivityDialogData);
$ActivityDialogDump{ $ActivityDialogData->{EntityID} } = {
Name => $ActivityDialogData->{Name},
CreateTime => $ActivityDialogData->{CreateTime},
ChangeTime => $ActivityDialogData->{ChangeTime},
Interface => $ActivityDialogData->{Config}->{Interface} || '',
DescriptionShort => $ActivityDialogData->{Config}->{DescriptionShort} || '',
DescriptionLong => $ActivityDialogData->{Config}->{DescriptionLong} || '',
Fields => $ActivityDialogData->{Config}->{Fields} || {},
FieldOrder => $ActivityDialogData->{Config}->{FieldOrder} || [],
Permission => $ActivityDialogData->{Config}->{Permission} || '',
RequiredLock => $ActivityDialogData->{Config}->{RequiredLock} || '',
SubmitAdviceText => $ActivityDialogData->{Config}->{SubmitAdviceText} || '',
SubmitButtonText => $ActivityDialogData->{Config}->{SubmitButtonText} || '',
};
}
# get Transitions
my $TransitionsList = $Self->{TransitionObject}->TransitionListGet( UserID => 1 );
my %TransitionDump;
TRANSITION:
for my $TransitionData ( @{$TransitionsList} ) {
next TRANSITION if !IsHashRefWithData($TransitionData);
$TransitionDump{ $TransitionData->{EntityID} } = {
Name => $TransitionData->{Name},
CreateTime => $TransitionData->{CreateTime},
ChangeTime => $TransitionData->{ChangeTime},
Condition => $TransitionData->{Config}->{Condition} || {},
ConditionLinking => $TransitionData->{Config}->{ConditionLinking} || '',
};
}
# get TransitionActions
my $TransitionActionsList = $Self->{TransitionActionObject}->TransitionActionListGet( UserID => 1 );
my %TransitionActionDump;
TRANSITIONACTION:
for my $TransitionActionData ( @{$TransitionActionsList} ) {
next TRANSITIONACTION if !IsHashRefWithData($TransitionActionData);
$TransitionActionDump{ $TransitionActionData->{EntityID} } = {
Name => $TransitionActionData->{Name},
CreateTime => $TransitionActionData->{CreateTime},
ChangeTime => $TransitionActionData->{ChangeTime},
Module => $TransitionActionData->{Config}->{Module} || '',
Config => $TransitionActionData->{Config}->{Config} || {},
};
}
# delete cache (this will also delete the cache for display or hide AgentTicketProcess menu
# item)
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => 'ProcessManagement_Process',
);
# return Hash useful for JSON
if ( $Param{ResultType} eq 'HASH' ) {
return {
'Process' => \%ProcessDump,
'State' => \%StateDump,
'Activity' => \%ActivityDump,
'ActivityDialog' => \%ActivityDialogDump,
'Transition' => \%TransitionDump,
'TransitionAction' => \%TransitionActionDump,
};
}
else {
# create output
my $Output = $Self->_ProcessItemOutput(
Key => "Process",
Value => \%ProcessDump,
);
$Output .= $Self->_ProcessItemOutput(
Key => 'Process::State',
Value => \%StateDump,
);
$Output .= $Self->_ProcessItemOutput(
Key => 'Process::Activity',
Value => \%ActivityDump,
);
$Output .= $Self->_ProcessItemOutput(
Key => 'Process::ActivityDialog',
Value => \%ActivityDialogDump,
);
$Output .= $Self->_ProcessItemOutput(
Key => 'Process::Transition',
Value => \%TransitionDump,
);
$Output .= $Self->_ProcessItemOutput(
Key => 'Process::TransitionAction',
Value => \%TransitionActionDump,
);
# return a scalar variable with all config as test
if ( $Param{ResultType} ne 'FILE' ) {
return $Output;
}
# return a file location
else {
# get user data of the current user to use for the file comment
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $Param{UserID},
);
# remove home from location path to show in file comment
my $Home = $Kernel::OM->Get('Kernel::Config')->Get('Home');
my $Location = $Param{Location};
$Location =~ s{$Home\/}{}xmsg;
# build comment (therefore we need to trick out the filter)
my $FileStart = <<'EOF';
# OTRS config file (automatically generated)
# VERSION:1.1
package Kernel::Config::Files::ZZZProcessManagement;
use strict;
use warnings;
no warnings 'redefine'; ## no critic
use utf8;
sub Load {
my ($File, $Self) = @_;
EOF
my $FileEnd = <<'EOF';
return;
}
1;
EOF
$Output = $FileStart . $Output . $FileEnd;
my $FileLocation = $Kernel::OM->Get('Kernel::System::Main')->FileWrite(
Location => $Param{Location},
Content => \$Output,
Mode => 'utf8',
Type => 'Local',
);
return $FileLocation;
}
}
}
=head2 ProcessImport()
import a process YAML file/content
my %ProcessImport = $ProcessObject->ProcessImport(
Content => $YAMLContent, # mandatory, YAML format
OverwriteExistingEntities => 0, # 0 || 1
UserID => 1, # mandatory
);
Returns:
%ProcessImport = (
Message => 'The Message to show.', # error or success message
Comment => 'Any comment', # optional
Success => 1, # 1 if success or undef otherwise
);
=cut
sub ProcessImport {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Content UserID)) {
# check needed stuff
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $ProcessData = $Kernel::OM->Get('Kernel::System::YAML')->Load( Data => $Param{Content} );
if ( ref $ProcessData ne 'HASH' ) {
return (
Message =>
"Couldn't read process configuration file. Please make sure you file is valid.",
);
}
# collect all used fields and make sure they're present
my @UsedDynamicFields;
for my $ActivityDialog ( sort keys %{ $ProcessData->{ActivityDialogs} } ) {
for my $FieldName (
sort
keys %{ $ProcessData->{ActivityDialogs}->{$ActivityDialog}->{Config}->{Fields} }
)
{
if ( $FieldName =~ s{DynamicField_(\w+)}{$1}xms ) {
push @UsedDynamicFields, $FieldName;
}
}
}
# get dynamic field object
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
# get all present dynamic fields and check if the fields used in the config are beyond them
my $DynamicFieldList = $DynamicFieldObject->DynamicFieldList(
ResultType => 'HASH',
);
my @PresentDynamicFieldNames = values %{$DynamicFieldList};
my @MissingDynamicFieldNames;
for my $UsedDynamicFieldName (@UsedDynamicFields) {
if ( !grep { $_ eq $UsedDynamicFieldName } @PresentDynamicFieldNames ) {
push @MissingDynamicFieldNames, $UsedDynamicFieldName;
}
}
if ( $#MissingDynamicFieldNames > -1 ) {
my $MissingDynamicFields = join( ', ', @MissingDynamicFieldNames );
return (
Message => "The following dynamic fields are missing: $MissingDynamicFields. "
. "Import has been stopped.",
);
}
# make sure all activities and dialogs are present
my @UsedActivityDialogs;
for my $ActivityEntityID ( @{ $ProcessData->{Process}->{Activities} } ) {
if ( ref $ProcessData->{Activities}->{$ActivityEntityID} ne 'HASH' ) {
return (
Message => "Missing data for Activity $ActivityEntityID.",
);
}
else {
for my $UsedActivityDialog (
@{ $ProcessData->{Activities}->{$ActivityEntityID}->{ActivityDialogs} }
)
{
push @UsedActivityDialogs, $UsedActivityDialog;
}
}
}
for my $ActivityDialogEntityID (@UsedActivityDialogs) {
if ( ref $ProcessData->{ActivityDialogs}->{$ActivityDialogEntityID} ne 'HASH' ) {
return (
Message => "Missing data for ActivityDialog $ActivityDialogEntityID.",
);
}
}
# make sure all transitions are present
for my $TransitionEntityID ( @{ $ProcessData->{Process}->{Transitions} } ) {
if ( ref $ProcessData->{Transitions}->{$TransitionEntityID} ne 'HASH' ) {
return (
Message => "Missing data for Transition $TransitionEntityID.",
);
}
}
# make sure all transition actions are present
for my $TransitionActionEntityID ( @{ $ProcessData->{Process}->{TransitionActions} } ) {
if ( ref $ProcessData->{TransitionActions}->{$TransitionActionEntityID} ne 'HASH' ) {
return (
Message => "Missing data for TransitionAction $TransitionActionEntityID.",
);
}
}
my %EntityMapping;
my %PartNameMap = (
Activity => 'Activities',
ActivityDialog => 'ActivityDialogs',
Transition => 'Transitions',
TransitionAction => 'TransitionActions'
);
# if OverwriteExistingEntities no new entities must be added to the DB unless the entity does
# not not exists
if ( $Param{OverwriteExistingEntities} ) {
my $EntityID = $ProcessData->{Process}->{EntityID};
my $NewEntityID = $EntityID;
# check if EntityID matched the format (it could be that a process from 3.3.x is been
# imported)
if ( $NewEntityID !~ m{\A Process - [0-9a-f]{32} \z}msx ) {
# generate new EntityIDs
$NewEntityID = $Self->{EntityObject}->EntityIDGenerate(
EntityType => 'Process',
UserID => $Param{UserID},
);
}
$EntityMapping{Process}->{$EntityID} = $NewEntityID;
for my $PartName (qw(Activity ActivityDialog Transition TransitionAction)) {
for my $PartEntityID ( sort keys %{ $ProcessData->{ $PartNameMap{$PartName} } } ) {
$NewEntityID = $PartEntityID;
# check if EntityID matched the format (it could be that a process from 3.3.x is been
# imported)
if ( $NewEntityID !~ m{\A $PartName - [0-9a-f]{32} \z}msx ) {
# generate new EntityIDs
$NewEntityID = $Self->{EntityObject}->EntityIDGenerate(
EntityType => $PartName,
UserID => $Param{UserID},
);
}
$EntityMapping{ $PartNameMap{$PartName} }->{$PartEntityID} = $NewEntityID;
}
# make sure that all entity mapping parts are defined as hash references
$EntityMapping{ $PartNameMap{$PartName} } //= {};
}
}
else {
my $EntityID = $ProcessData->{Process}->{EntityID};
# generate new EntityIDs
my $NewEntityID = $Self->{EntityObject}->EntityIDGenerate(
EntityType => 'Process',
UserID => $Param{UserID},
);
$EntityMapping{Process}->{$EntityID} = $NewEntityID;
for my $PartName (qw(Activity ActivityDialog Transition TransitionAction)) {
for my $PartEntityID ( sort keys %{ $ProcessData->{ $PartNameMap{$PartName} } } ) {
$NewEntityID = $Self->{EntityObject}->EntityIDGenerate(
EntityType => $PartName,
UserID => $Param{UserID},
);
$EntityMapping{ $PartNameMap{$PartName} }->{$PartEntityID} = $NewEntityID;
}
# make sure that all entity mapping parts are defined as hash references
$EntityMapping{ $PartNameMap{$PartName} } //= {};
}
# set EntityIDs
my $UpdateResult = $Self->_ImportedEntitiesUpdate(
ProcessData => $ProcessData,
EntityMapping => \%EntityMapping,
);
if ( !$UpdateResult->{Success} ) {
return %{$UpdateResult};
}
# overwrite process data with the temporary entities
$ProcessData = $UpdateResult->{ProcessData};
}
# invert the entity mappings, this is needed as we need to check if the new entities exists:
# for non overwriting processes they must not exists and new records must be generated,
# for overwriting processes it might happens that one record does not exists and it needs
# to be created before it is updated
# if new entities are to be created they will be using minimal data and updated with real data
# later, this way overwriting and non overwriting processes will share the same logic
%{ $EntityMapping{Process} } = reverse %{ $EntityMapping{Process} };
%{ $EntityMapping{Activities} } = reverse %{ $EntityMapping{Activities} };
%{ $EntityMapping{ActivityDialogs} } = reverse %{ $EntityMapping{ActivityDialogs} };
%{ $EntityMapping{Transitions} } = reverse %{ $EntityMapping{Transitions} };
%{ $EntityMapping{TransitionActions} } = reverse %{ $EntityMapping{TransitionActions} };
my %AddedEntityIDs;
# get all processes
my $ProcessList = $Self->ProcessList(
UseEntities => 1,
UserID => $Param{UserID},
);
# check if processes exists otherwise create them
for my $ProcessEntityID ( sort keys %{ $EntityMapping{Process} } ) {
if ( !$ProcessList->{$ProcessEntityID} ) {
# create an empty process
my $ProcessID = $Self->ProcessAdd(
EntityID => $ProcessEntityID,
Name => 'NewProcess',
StateEntityID => 'S1',
Layout => {},
Config => {
Path => {},
Description => 'NewProcess',
},
UserID => $Param{UserID},
);
if ( !$ProcessID ) {
return $Self->_ProcessImportRollBack(
AddedEntityIDs => \%AddedEntityIDs,
UserID => $Param{UserID},
Message => 'Process '
. $ProcessData->{Process}->{Name}
. ' could not be added. Stopping import!',
);
}
# remember added entity
$AddedEntityIDs{Process}->{$ProcessEntityID} = $ProcessID;
}
}
my %PartConfigMap = (
Activity => {},
ActivityDialog => {
DescriptionShort => 'NewActivityDialog',
Fields => {},
FieldOrder => [],
},
Transition => {
Condition => {},
},
TransitionAction => {
Module => 'NewTransitionAction',
Config => {},
},
);
# create missing process parts
for my $PartName (qw(Activity ActivityDialog Transition TransitionAction)) {
my $PartListFunction = $PartName . 'List';
my $PartAddFunction = $PartName . 'Add';
my $PartObject = $PartName . 'Object';
# get all part items
my $PartsList = $Self->{$PartObject}->$PartListFunction(
UseEntities => 1,
UserID => $Param{UserID},
);
# check if part exists otherwise create them
for my $PartEntityID ( sort keys %{ $EntityMapping{ $PartNameMap{$PartName} } } ) {
if ( !$PartsList->{$PartEntityID} ) {
# create an empty part
my $PartID = $Self->{$PartObject}->$PartAddFunction(
EntityID => $PartEntityID,
Name => "New$PartName",
Config => $PartConfigMap{$PartName},
UserID => $Param{UserID},
);
if ( !$PartID ) {
return $Self->_ProcessImportRollBack(
AddedEntityIDs => \%AddedEntityIDs,
UserID => $Param{UserID},
Message => "$PartName "
. $ProcessData->{ $PartNameMap{$PartName} }->{$PartEntityID}->{Name}
. ' could not be added. Stopping import!',
);
}
# remember added entity
$AddedEntityIDs{ $PartNameMap{$PartName} }->{$PartEntityID} = $PartID;
}
}
}
# update all entities with real data
# update process
for my $ProcessEntityID ( sort keys %{ $EntityMapping{Process} } ) {
my $Process = $Self->ProcessGet(
EntityID => $ProcessEntityID,
UserID => $Param{UserID},
);
my $Success = $Self->ProcessUpdate(
%{ $ProcessData->{Process} },
ID => $Process->{ID},
UserID => $Param{UserID},
);
if ( !$Success ) {
return $Self->_ProcessImportRollBack(
AddedEntityIDs => \%AddedEntityIDs,
UserID => $Param{UserID},
Message => "Process: $ProcessEntityID could not be updated. "
. "Stopping import!",
);
}
}
# update all other process parts
for my $PartName (qw(Activity ActivityDialog Transition TransitionAction)) {
my $PartGetFunction = $PartName . 'Get';
my $PartUpdateFunction = $PartName . 'Update';
my $PartObject = $PartName . 'Object';
for my $PartEntityID ( sort keys %{ $EntityMapping{ $PartNameMap{$PartName} } } ) {
my $Part = $Self->{$PartObject}->$PartGetFunction(
EntityID => $PartEntityID,
UserID => $Param{UserID}
);
my $Success = $Self->{$PartObject}->$PartUpdateFunction(
%{ $ProcessData->{ $PartNameMap{$PartName} }->{$PartEntityID} },
ID => $Part->{ID},
UserID => $Param{UserID},
);
if ( !$Success ) {
return $Self->_ProcessImportRollBack(
AddedEntityIDs => \%AddedEntityIDs,
UserID => $Param{UserID},
Message => "$PartName: $PartEntityID could not be updated. "
. " Stopping import!",
);
}
}
}
return (
Message => $Kernel::OM->Get('Kernel::Language')->Translate(
'The process "%s" and all of its data has been imported successfully.',
$ProcessData->{Process}->{Name}
),
Success => 1,
);
}
sub _ProcessItemOutput {
my ( $Self, %Param ) = @_;
my $Output = $Kernel::OM->Get('Kernel::System::Main')->Dump(
$Param{Value},
);
my $Key = "\$Self->{'$Param{Key}'}";
$Output =~ s{\A \$VAR1}{$Key}mxs;
return $Output . "\n";
}
sub _ImportedEntitiesUpdate {
my ( $Self, %Param ) = @_;
my %EntityMapping = %{ $Param{EntityMapping} };
# re-write process with entity mapping information
my $Process = $Param{ProcessData}->{Process};
my $NewProcess;
# set non changing root attributes
for my $Attribute (qw(Name StateEntityID)) {
$NewProcess->{$Attribute} = $Process->{$Attribute};
}
# set new process main entity
my $NewProcessEntityID = $EntityMapping{Process}->{ $Process->{EntityID} };
if ( !$NewProcessEntityID ) {
my $Message = "Could not find a entity mapping for Process: $Process->{EntityID}";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewProcess->{EntityID} = $NewProcessEntityID;
# set the process layout
$NewProcess->{Layout} = {};
for my $ActivityEntityID ( sort keys %{ $Process->{Layout} } ) {
my $NewActivityEntityID = $EntityMapping{Activities}->{$ActivityEntityID};
if ( !$NewActivityEntityID ) {
my $Message = "Could not find a entity mapping for Activity: $ActivityEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewProcess->{Layout}->{$NewActivityEntityID} = $Process->{Layout}->{$ActivityEntityID};
}
# set process config non changing attributes
$NewProcess->{Config}->{Description} = $Process->{Config}->{Description};
# set process config start activity and start activity dialog EntityID
my %AttributeMap = (
Activity => 'Activities',
ActivityDialog => 'ActivityDialogs',
);
ATTRIBUTE:
for my $Attribute (qw(Activity ActivityDialog)) {
$NewProcess->{Config}->{"Start$Attribute"} = '';
next ATTRIBUTE if !$Process->{Config}->{"Start$Attribute"};
my $NewAttributeEntityID = $EntityMapping{ $AttributeMap{$Attribute} }
->{ $Process->{Config}->{"Start$Attribute"} };
if ( !$NewAttributeEntityID ) {
my $Message = "Could not find a entity mapping for $Attribute: "
. "$Process->{Config}->{Start$Attribute}";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewProcess->{Config}->{"Start$Attribute"} = $NewAttributeEntityID;
}
# set process path
$NewProcess->{Config}->{Path} = {};
for my $ActivityEntityID ( sort keys %{ $Process->{Config}->{Path} } ) {
# set new activity EntityID in process path
my $NewActivityEntityID = $EntityMapping{Activities}->{$ActivityEntityID};
if ( !$NewActivityEntityID ) {
my $Message = "Could not find a entity mapping for Activity: $ActivityEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewProcess->{Config}->{Path}->{$NewActivityEntityID} = {};
# check if original action has configuration (e.g. last activity might be empty)
my $Activity = $Process->{Config}->{Path}->{$ActivityEntityID};
if ( IsHashRefWithData($Activity) ) {
for my $TransitionEntityID ( sort keys %{$Activity} ) {
my $Transition = $Activity->{$TransitionEntityID};
my $NewTransition;
for my $TransitionActionEntityID ( @{ $Transition->{TransitionAction} } ) {
# set new transition action EntityID from process path activity transition
my $NewTransitionActionEntityID = $EntityMapping{TransitionActions}->{$TransitionActionEntityID};
if ( !$NewTransitionActionEntityID ) {
my $Message = "Could not find a entity mapping for Transition Action: "
. "$TransitionActionEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
push @{ $NewTransition->{TransitionAction} }, $NewTransitionActionEntityID;
}
# set new activity EntityID stored in the transition
my $NewDestinationActivityEntityID = $EntityMapping{Activities}->{ $Transition->{ActivityEntityID} };
if ( !$NewDestinationActivityEntityID ) {
my $Message = "Could not find a entity mapping for Activity: "
. "$Transition->{ActivityEntityID}";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewTransition->{ActivityEntityID} = $NewDestinationActivityEntityID;
# set new transition EntityID
my $NewTransitionEntityID = $EntityMapping{Transitions}->{$TransitionEntityID};
if ( !$NewTransitionEntityID ) {
my $Message = "Could not find a entity mapping for Transition: $TransitionEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
# set new transition to its entity hash key
$NewProcess->{Config}->{Path}->{$NewActivityEntityID}->{$NewTransitionEntityID} = $NewTransition;
}
}
}
# re-write activities with entity mapping information
my $Activities = $Param{ProcessData}->{Activities};
my $NewActivities;
for my $ActivityEntityID ( sort keys %{$Activities} ) {
# get current old activity
my $CurrentActivity = $Activities->{$ActivityEntityID};
# set new activity EntityID
my $NewActivityEntityID = $EntityMapping{Activities}->{$ActivityEntityID};
if ( !$NewActivityEntityID ) {
my $Message = "Could not find a entity mapping for Activity: $ActivityEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewActivities->{$NewActivityEntityID}->{EntityID} = $NewActivityEntityID;
# set non changing attributes
$NewActivities->{$NewActivityEntityID}->{Name} = $CurrentActivity->{Name};
# set an empty configuration
$NewActivities->{$NewActivityEntityID}->{Config}->{ActivityDialog} = {};
# set new entities for the configured activity dialogs
my $CurrentActivityDialogs = $CurrentActivity->{Config}->{ActivityDialog};
for my $OrderKey ( sort keys %{$CurrentActivityDialogs} ) {
# get old activity dialog EntityID
my $ActivityDialogEntityID = $CurrentActivityDialogs->{$OrderKey};
# set new activity dialog EntityID
my $NewActivityDialogEntityID = $EntityMapping{ActivityDialogs}->{$ActivityDialogEntityID};
if ( !$NewActivityDialogEntityID ) {
my $Message = "Could not find a entity mapping for Activity Dialog: "
. "$ActivityDialogEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewActivities->{$NewActivityEntityID}->{Config}->{ActivityDialog}->{$OrderKey}
= $NewActivityDialogEntityID;
}
}
# re-write all other parts
my %PartNameMap = (
ActivityDialog => 'ActivityDialogs',
Transition => 'Transitions',
TransitionAction => 'TransitionActions'
);
my %NewParts;
for my $PartName (qw(ActivityDialog Transition TransitionAction)) {
my $CurrentParts = $Param{ProcessData}->{ $PartNameMap{$PartName} };
for my $CurrentEntityID ( sort keys %{$CurrentParts} ) {
# get current old process part
my $CurrentPart = $CurrentParts->{$CurrentEntityID};
# set new part EntityID
my $NewEntityID = $EntityMapping{ $PartNameMap{$PartName} }->{$CurrentEntityID};
if ( !$NewEntityID ) {
my $Message = "Could not find a entity mapping for $PartName: $CurrentEntityID";
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'Error',
Message => $Message,
);
return {
Success => 0,
Message => $Message,
};
}
$NewParts{ $PartNameMap{$PartName} }->{$NewEntityID}->{EntityID} = $NewEntityID;
# set non changing attributes
for my $Attribute (qw(Name Config)) {
$NewParts{ $PartNameMap{$PartName} }->{$NewEntityID}->{$Attribute} = $CurrentPart->{$Attribute};
}
}
}
return {
Success => 1,
ProcessData => {
%NewParts,
Process => $NewProcess,
Activities => $NewActivities,
},
};
}
sub _ProcessImportRollBack {
my ( $Self, %Param ) = @_;
my %AddedEntityIDs = %{ $Param{AddedEntityIDs} };
my $Error;
# delete added processes
for my $ProcessEntityID ( sort keys %{ $AddedEntityIDs{Process} } ) {
my $ProcessID = $AddedEntityIDs{Process}->{$ProcessEntityID};
my $Success = $Self->ProcessDelete(
ID => $ProcessID,
UserID => $Param{UserID},
);
if ( !$Success ) {
$Error = 1;
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Process: $ProcessEntityID could not be deleted",
);
}
}
my %PartNameMap = (
Activity => 'Activities',
ActivityDialog => 'ActivityDialogs',
Transition => 'Transitions',
TransitionAction => 'TransitionActions'
);
# delete added process parts
for my $Part (qw(Activity ActivityDialog Transition TransitionAction)) {
for my $PartEntityID ( sort keys %{ $AddedEntityIDs{ $PartNameMap{$Part} } } ) {
my $PartID = $AddedEntityIDs{ $PartNameMap{$Part} }->{$PartEntityID};
my $DeleteFunction = $Part . 'Delete';
my $Object = $Part . 'Object';
my $Success = $Self->{$Object}->$DeleteFunction(
ID => $PartID,
UserID => $Param{UserID},
);
if ( !$Success ) {
$Error = 1;
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Part: $PartEntityID could not be deleted",
);
}
}
}
my $Comment = 'Process could not be imported. All changes have been rolled back.';
if ($Error) {
$Comment = ' There was an error rolling back the partially imported process, please'
. ' check the error log for details.';
}
return (
Success => 0,
Message => $Param{Message},
Comment => $Comment,
);
}
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