# --
# 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::ITSMChange::ITSMCondition::Action;
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::System::ITSMChange::ITSMCondition::Action - condition action lib
=head1 PUBLIC INTERFACE
=head2 ActionAdd()
Add a new condition action.
my $ActionID = $ConditionObject->ActionAdd(
ConditionID => 123,
ActionNumber => 5,
ObjectID => 234,
AttributeID => 345,
OperatorID => 456,
Selector => 1234,
ActionValue => 'rejected',
UserID => 1,
);
=cut
sub ActionAdd {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ConditionID ObjectID AttributeID OperatorID Selector UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# handle 'ActionValue' in a special way
if ( !exists $Param{ActionValue} || !defined $Param{ActionValue} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ActionValue!',
);
return;
}
# get condition for event handler
my $Condition = $Self->ConditionGet(
ConditionID => $Param{ConditionID},
UserID => $Param{UserID},
);
# check condition
return if !$Condition;
# trigger ActionAddPre-Event
$Self->EventHandler(
Event => 'ActionAddPre',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
},
UserID => $Param{UserID},
);
# get default action number if not given
my $ActionNumber = delete $Param{ActionNumber};
if ( !$ActionNumber ) {
$ActionNumber = $Self->_CreateNewActionNumber(%Param);
}
# add new action name to database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'INSERT INTO condition_action '
. '(condition_id, action_number, object_id, '
. 'attribute_id, operator_id, selector, '
. ' action_value) '
. 'VALUES (?, ?, ?, ?, ?, ?, ?)',
Bind => [
\$Param{ConditionID}, \$ActionNumber, \$Param{ObjectID},
\$Param{AttributeID}, \$Param{OperatorID}, \$Param{Selector},
\$Param{ActionValue},
],
);
# prepare SQL statement
my $ActionID;
# this is important for oracle for which an empty string and NULL is the same!
if ( $Self->{DBType} eq 'oracle' && $Param{ActionValue} eq '' ) {
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM condition_action '
. 'WHERE condition_id = ? AND action_number = ? AND object_id = ? '
. 'AND attribute_id = ? AND operator_id = ? AND selector = ? '
. 'AND action_value IS NULL',
Bind => [
\$Param{ConditionID}, \$ActionNumber, \$Param{ObjectID},
\$Param{AttributeID}, \$Param{OperatorID}, \$Param{Selector},
],
Limit => 1,
);
}
# for all other databases AND for oracle IF the action value is NOT an empty string
else {
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM condition_action '
. 'WHERE condition_id = ? AND action_number = ? AND object_id = ? '
. 'AND attribute_id = ? AND operator_id = ? AND selector = ? '
. 'AND action_value = ?',
Bind => [
\$Param{ConditionID}, \$ActionNumber, \$Param{ObjectID},
\$Param{AttributeID}, \$Param{OperatorID}, \$Param{Selector},
\$Param{ActionValue},
],
Limit => 1,
);
}
# get id of created action
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$ActionID = $Row[0];
}
# check if action could be added
if ( !$ActionID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "ActionAdd() failed!",
);
return;
}
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => 'ActionList::ConditionID::' . $Param{ConditionID},
);
# trigger ActionAddPost-Event
$Self->EventHandler(
Event => 'ActionAddPost',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
ActionID => $ActionID,
},
UserID => $Param{UserID},
);
return $ActionID;
}
=head2 ActionUpdate()
Update a condition action.
my $Success = $ConditionObject->ActionUpdate(
ActionID => 1234,
ActionNumber => 1, # (optional)
ObjectID => 234, # (optional)
AttributeID => 345, # (optional)
OperatorID => 456, # (optional)
Selector => 1234', # (optional)
ActionValue => 'rejected', # (optional)
UserID => 1,
);
=cut
sub ActionUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ActionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# get action
my $Action = $Self->ActionGet(
ActionID => $Param{ActionID},
UserID => $Param{UserID},
);
# check action
return if !$Action;
# get condition for event handler
my $Condition = $Self->ConditionGet(
ConditionID => $Action->{ConditionID},
UserID => $Param{UserID},
);
# check condition
return if !$Condition;
# trigger ActionUpdatePre-Event
$Self->EventHandler(
Event => 'ActionUpdatePre',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
},
UserID => $Param{UserID},
);
# map update attributes to column names
my %Attribute = (
ActionNumber => 'action_number',
ObjectID => 'object_id',
AttributeID => 'attribute_id',
OperatorID => 'operator_id',
Selector => 'selector',
ActionValue => 'action_value',
);
# build SQL to update action
my $SQL = 'UPDATE condition_action SET ';
my @Bind;
ATTRIBUTE:
for my $Attribute ( sort keys %Attribute ) {
# preserve the old value, when the column isn't in function parameters
next ATTRIBUTE if !exists $Param{$Attribute};
next ATTRIBUTE if !defined $Param{$Attribute};
# param checking has already been done, so this is safe
$SQL .= "$Attribute{$Attribute} = ?, ";
push @Bind, \$Param{$Attribute};
}
# set condition ID to allow trailing comma of previous loop
$SQL .= ' condition_id = condition_id ';
# set matching of SQL statement
$SQL .= 'WHERE id = ?';
push @Bind, \$Param{ActionID};
# update action
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => $SQL,
Bind => \@Bind,
);
# delete cache
for my $Key (
'ActionList::ConditionID::' . $Action->{ConditionID},
'ActionGet::' . $Param{ActionID},
)
{
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => $Key,
);
}
# trigger ActionUpdatePost-Event
$Self->EventHandler(
Event => 'ActionUpdatePost',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
OldActionData => $Action,
},
UserID => $Param{UserID},
);
return 1;
}
=head2 ActionGet()
Get a condition action for a given action id.
Returns a hash reference of the action data.
my $ConditionActionRef = $ConditionObject->ActionGet(
ActionID => 1234,
UserID => 1,
);
The returned hash reference contains following elements:
$ConditionAction{ActionID}
$ConditionAction{ConditionID}
$ConditionAction{ActionNumber}
$ConditionAction{ObjectID}
$ConditionAction{AttributeID}
$ConditionAction{OperatorID}
$ConditionAction{Selector}
$ConditionAction{ActionValue}
=cut
sub ActionGet {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ActionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check cache
my $CacheKey = 'ActionGet::' . $Param{ActionID};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return $Cache if $Cache;
# prepare SQL statement
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id, condition_id, action_number, object_id, '
. 'attribute_id, operator_id, selector, action_value '
. 'FROM condition_action WHERE id = ?',
Bind => [ \$Param{ActionID} ],
Limit => 1,
);
# fetch the result
my %ActionData;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$ActionData{ActionID} = $Row[0];
$ActionData{ConditionID} = $Row[1];
$ActionData{ActionNumber} = $Row[2];
$ActionData{ObjectID} = $Row[3];
$ActionData{AttributeID} = $Row[4];
$ActionData{OperatorID} = $Row[5];
$ActionData{Selector} = $Row[6];
# this is important for oracle for which an empty string and NULL is the same!
$ActionData{ActionValue} = $Row[7] // '';
}
# check error
if ( !%ActionData ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "ActionID $Param{ActionID} does not exist!",
);
return;
}
# set cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => $CacheKey,
Value => \%ActionData,
TTL => $Self->{CacheTTL},
);
return \%ActionData;
}
=head2 ActionList()
Returns a sorted list of all condition action
ids for a given ConditionID as array reference.
my $ConditionActionIDsRef = $ConditionObject->ActionList(
ConditionID => 1234,
UserID => 1,
);
=cut
sub ActionList {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ConditionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check cache
my $CacheKey = 'ActionList::ConditionID::' . $Param{ConditionID};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return $Cache if $Cache;
# prepare SQL statement
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM condition_action '
. 'WHERE condition_id = ? '
. 'ORDER BY action_number ASC',
Bind => [ \$Param{ConditionID} ],
);
# fetch the result
my @ActionList;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @ActionList, $Row[0];
}
# set cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => $CacheKey,
Value => \@ActionList,
TTL => $Self->{CacheTTL},
);
return \@ActionList;
}
=head2 ActionDelete()
Deletes a condition action.
my $Success = $ConditionObject->ActionDelete(
ActionID => 123,
UserID => 1,
);
=cut
sub ActionDelete {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ActionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# get action
my $Action = $Self->ActionGet(
ActionID => $Param{ActionID},
UserID => $Param{UserID},
);
# check action
return if !$Action;
# get condition for event handler
my $Condition = $Self->ConditionGet(
ConditionID => $Action->{ConditionID},
UserID => $Param{UserID},
);
# check condition
return if !$Condition;
# trigger ActionDeletePre-Event
$Self->EventHandler(
Event => 'ActionDeletePre',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
},
UserID => $Param{UserID},
);
# delete condition action from database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM condition_action '
. 'WHERE id = ?',
Bind => [ \$Param{ActionID} ],
);
# delete cache
for my $Key (
'ActionList::ConditionID::' . $Action->{ConditionID},
'ActionGet::' . $Param{ActionID},
)
{
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => $Key,
);
}
# trigger ActionDeletePost-Event
$Self->EventHandler(
Event => 'ActionDeletePost',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
},
UserID => $Param{UserID},
);
return 1;
}
=head2 ActionDeleteAll()
Deletes all condition actions for a given condition id.
my $Success = $ConditionObject->ActionDeleteAll(
ConditionID => 123,
UserID => 1,
);
=cut
sub ActionDeleteAll {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ConditionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# get condition for event handler
my $Condition = $Self->ConditionGet(
ConditionID => $Param{ConditionID},
UserID => $Param{UserID},
);
# check condition
return if !$Condition;
# get all actions for the given condition id
my $ActionIDsRef = $Self->ActionList(
ConditionID => $Param{ConditionID},
UserID => $Param{UserID},
);
# trigger ActionDeleteAllPre-Event
$Self->EventHandler(
Event => 'ActionDeleteAllPre',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
ConditionID => $Param{ConditionID},
},
UserID => $Param{UserID},
);
# delete condition actions from database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM condition_action '
. 'WHERE condition_id = ?',
Bind => [ \$Param{ConditionID} ],
);
# delete cache
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => 'ActionList::ConditionID::' . $Param{ConditionID},
);
# delete cache
if ( $ActionIDsRef && @{$ActionIDsRef} ) {
for my $ActionID ( @{$ActionIDsRef} ) {
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
Type => $Self->{CacheType},
Key => 'ActionGet::' . $ActionID,
);
}
}
# trigger ActionDeleteAllPost-Event
$Self->EventHandler(
Event => 'ActionDeleteAllPost',
Data => {
%Param,
ChangeID => $Condition->{ChangeID},
ConditionID => $Param{ConditionID},
},
UserID => $Param{UserID},
);
return 1;
}
=head2 ActionExecute()
Returns the success value of the execution of an action.
my $Success = $ConditionObject->ActionExecute(
ActionID => 123,
UserID => 1,
);
=cut
sub ActionExecute {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Argument (qw(ActionID UserID)) {
if ( !$Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# get action content
my $Action = $Self->ActionGet(
ActionID => $Param{ActionID},
UserID => $Param{UserID},
);
# check action content
return if !$Action;
# get condition for event handler
my $Condition = $Self->ConditionGet(
ConditionID => $Action->{ConditionID},
UserID => $Param{UserID},
);
# check condition
return if !$Condition;
# get action attributes
my $ActionData = $Self->_ActionExecuteInit(
Action => $Action,
UserID => $Param{UserID},
);
# check action attributes
return if !$ActionData;
# do not execute 'lock' actions, they are passive!
my @OmitActions = qw( lock );
return 0 if grep { $ActionData->{Operator}->{Name} eq $_ } @OmitActions;
# trigger ActionExecutePre-Event
$Self->EventHandler(
Event => 'ActionExecutePre',
Data => {
%Param,
%{$Condition},
ConditionName => $Condition->{Name},
ChangeID => $Condition->{ChangeID},
},
UserID => $Param{UserID},
);
# get object name
my $ObjectName = $ActionData->{Object}->{Name};
# get object data
my $ActionObjectData = $Self->ObjectDataGet(
ConditionID => $Action->{ConditionID},
ObjectName => $ObjectName,
Selector => $Action->{Selector},
UserID => $Param{UserID},
);
# check for action object data
# no need to execute operator if it is an empty array ref
if (
!$ActionObjectData
|| ref $ActionObjectData ne 'ARRAY'
|| ref $ActionObjectData eq 'ARRAY' && !@{$ActionObjectData}
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No object data for $ObjectName ($Action->{Selector}) found!",
);
return;
}
# get attribute type
my $AttributeType = $ActionData->{Attribute}->{Name};
# check attribute type
if ( !$AttributeType ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No attribute $ObjectName ($Action->{Selector}) found!",
);
return;
}
# check for object attribute
for my $ActionObject ( @{$ActionObjectData} ) {
if ( !exists $ActionObject->{$AttributeType} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No object attribute for $ObjectName ($AttributeType) found!",
);
return;
}
}
# define operator values
my %OperatorExecute = (
OperatorName => $ActionData->{Operator}->{Name},
ObjectData => $ActionObjectData,
ObjectName => $ObjectName,
Selector => $Action->{Selector},
Attribute => $AttributeType,
ActionValue => $Action->{ActionValue},
ActionID => $Action->{ActionID},
);
# return result of the actions execution
my $Result = $Self->OperatorExecute(
%OperatorExecute,
UserID => $Param{UserID},
);
# get nice action result
my $ActionResult = ($Result) ? 'successfully' : 'unsuccessfully';
# trigger ActionExecutePost-Event
$Self->EventHandler(
Event => 'ActionExecutePost',
Data => {
%Param,
%{$Condition},
ConditionName => $Condition->{Name},
%OperatorExecute,
ChangeID => $Condition->{ChangeID},
ActionResult => $ActionResult,
},
UserID => $Param{UserID},
);
# return result of the actions execution
return $Result;
}
=head1 PRIVATE INTERFACE
=head2 _ActionExecuteInit()
Returns object, attribute and operator of a given action.
my $ActionData = $ConditionObject->_ActionExecuteInit(
Action => $ActionRef,
UserID => 1,
);
=cut
sub _ActionExecuteInit {
my ( $Self, %Param ) = @_;
# extract action
my $Action = $Param{Action};
# declare action data
my %ActionData;
# get object data
$ActionData{Object} = $Self->ObjectGet(
ObjectID => $Action->{ObjectID},
UserID => $Param{UserID},
);
# check for object data
if ( !$ActionData{Object} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No value for 'Object' with ID '$Action->{ObjectID}'!",
);
return;
}
# get attribute data
$ActionData{Attribute} = $Self->AttributeGet(
AttributeID => $Action->{AttributeID},
UserID => $Param{UserID},
);
# check for attribute data
if ( !$ActionData{Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No value for 'Attribute' with ID '$Action->{AttributeID}'!",
);
return;
}
# get operator data
$ActionData{Operator} = $Self->OperatorGet(
OperatorID => $Action->{OperatorID},
UserID => $Param{UserID},
);
# check for operator data
if ( !$ActionData{Operator} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No value for 'Operator' with ID '$Action->{OperatorID}'!",
);
return;
}
return \%ActionData;
}
=head2 _CreateNewActionNumber()
Create a new unused action number for the given condition.
The highest current action number for the given condition is
looked up and incremented by one.
my $ActionNumber = $ConditionObject->_CreateNewActionNumber(
ConditionID => 123,
);
=cut
sub _CreateNewActionNumber {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ConditionID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ConditionID!',
);
return;
}
# get the largest action number
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT MAX(action_number) '
. 'FROM condition_action '
. 'WHERE condition_id = ?',
Bind => [ \$Param{ConditionID} ],
Limit => 1,
);
# fetch the result, default to 0 when there are no actions yet
my $ActionNumber;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$ActionNumber = $Row[0];
}
$ActionNumber ||= 0;
# increment number to get a non-existent action number
$ActionNumber++;
return $ActionNumber;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L.
=cut