453 lines
15 KiB
Perl
453 lines
15 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::ITSMChange::Event::Notification;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
our @ObjectDependencies = (
|
|
'Kernel::System::Group',
|
|
'Kernel::System::ITSMChange',
|
|
'Kernel::System::ITSMChange::History',
|
|
'Kernel::System::ITSMChange::ITSMWorkOrder',
|
|
'Kernel::System::ITSMChange::Notification',
|
|
'Kernel::System::LinkObject',
|
|
'Kernel::System::Log',
|
|
);
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
# allocate new hash for object
|
|
my $Self = {};
|
|
bless( $Self, $Type );
|
|
|
|
return $Self;
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(Data Event Config UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# do not modify the original event, because we need this unmodified in later event modules
|
|
my $Event = $Param{Event};
|
|
|
|
# in history event handling we use Event name without the trailing 'Post'
|
|
$Event =~ s{ Post \z }{}xms;
|
|
|
|
# distinguish between Change and WorkOrder events, based on naming convention
|
|
my $Type;
|
|
if ( $Event =~ m{ \A (Change|ActionExecute) }xms ) {
|
|
$Type = 'Change';
|
|
}
|
|
elsif ( $Event =~ m{ \A WorkOrder }xms ) {
|
|
$Type = 'WorkOrder';
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Could not determine the object type for the event '$Event'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get the event id, for looking up the list of relevant rules
|
|
my $EventID = $Kernel::OM->Get('Kernel::System::ITSMChange::History')->HistoryTypeLookup(
|
|
HistoryType => $Event,
|
|
);
|
|
if ( !$EventID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Encountered unknown event '$Event'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
my $NotificationRuleIDs = $Kernel::OM->Get('Kernel::System::ITSMChange::Notification')->NotificationRuleSearch(
|
|
EventID => $EventID,
|
|
);
|
|
if ( !$NotificationRuleIDs ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Could not get notification rules for the event '$Event'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# in case of an update, we have the old data for comparison
|
|
my $OldData = $Param{Data}->{"Old${Type}Data"};
|
|
|
|
# The notification rules are based on names, while the ChangeUpdate-Function
|
|
# primarily cares about IDs. So there needs to be a mapping.
|
|
my %Name2ID = (
|
|
ChangeState => 'ChangeStateID',
|
|
WorkOrderState => 'WorkOrderStateID',
|
|
);
|
|
|
|
# loop over the notification rules and check the condition
|
|
RULEID:
|
|
for my $RuleID ( @{$NotificationRuleIDs} ) {
|
|
my $Rule = $Kernel::OM->Get('Kernel::System::ITSMChange::Notification')->NotificationRuleGet(
|
|
ID => $RuleID,
|
|
);
|
|
|
|
my $Attribute = $Rule->{Attribute} || '';
|
|
if ( $Name2ID{$Attribute} ) {
|
|
$Attribute = $Name2ID{$Attribute};
|
|
}
|
|
|
|
# no notification if the attribute is not relevant
|
|
if ( $Attribute && !exists $Param{Data}->{$Attribute} ) {
|
|
next RULEID;
|
|
}
|
|
|
|
# in case of an update, check whether the attribute has changed
|
|
if (
|
|
$Attribute
|
|
&& ( $Event eq 'ChangeUpdate' || $Event eq 'WorkOrderUpdate' )
|
|
)
|
|
{
|
|
my $HasChanged = $Self->_HasFieldChanged(
|
|
New => $Param{Data}->{$Attribute},
|
|
Old => $OldData->{$Attribute},
|
|
);
|
|
|
|
next RULEID if !$HasChanged;
|
|
}
|
|
|
|
# get the string to match against
|
|
# TODO: support other combinations, maybe use GeneralCatalog directly
|
|
my $NewFieldContent = $Attribute ? $Param{Data}->{$Attribute} : '';
|
|
|
|
if ( $Attribute eq 'ChangeStateID' ) {
|
|
$NewFieldContent = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeStateLookup(
|
|
ChangeStateID => $NewFieldContent,
|
|
);
|
|
}
|
|
elsif ( $Attribute eq 'WorkOrderStateID' ) {
|
|
$NewFieldContent = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder')->WorkOrderStateLookup(
|
|
WorkOrderStateID => $NewFieldContent,
|
|
);
|
|
}
|
|
|
|
# should the notification be sent ?
|
|
# the x-modifier is harmful here, as $Rule->{Rule} can contain spaces
|
|
if (
|
|
defined $Rule->{Rule}
|
|
&& defined $NewFieldContent
|
|
&& $NewFieldContent !~ m/^$Rule->{Rule}$/
|
|
)
|
|
{
|
|
next RULEID;
|
|
}
|
|
|
|
# determine list of agents and customers
|
|
my $AgentAndCustomerIDs = $Self->_AgentAndCustomerIDsGet(
|
|
Recipients => $Rule->{Recipients},
|
|
Type => $Type,
|
|
Event => $Event,
|
|
ChangeID => $Param{Data}->{ChangeID},
|
|
WorkOrderID => $Param{Data}->{WorkOrderID},
|
|
OldData => $OldData,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
next RULEID if !$AgentAndCustomerIDs;
|
|
|
|
$Kernel::OM->Get('Kernel::System::ITSMChange::Notification')->NotificationSend(
|
|
%{$AgentAndCustomerIDs},
|
|
Type => $Type,
|
|
Event => $Event,
|
|
UserID => $Param{UserID},
|
|
Data => {
|
|
%{ $Param{Data} }, # do not pass as reference, as it would influence later events!
|
|
},
|
|
|
|
Message => $Rule->{Message},
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=begin Internal:
|
|
|
|
=head2 _HasFieldChanged()
|
|
|
|
This method checks whether a field was changed or not. It returns 1 when field
|
|
was changed, 0 otherwise
|
|
|
|
my $FieldHasChanged = $NotificationEventObject->_HasFieldChanged(
|
|
Old => 'old value', # can be array reference or hash reference as well
|
|
New => 'new value', # can be array reference or hash reference as well
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _HasFieldChanged {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# field has changed when either 'new' or 'old is not set
|
|
return 1 if !( $Param{New} && $Param{Old} ) && ( $Param{New} || $Param{Old} );
|
|
|
|
# field has not changed when both values are empty
|
|
return if !$Param{New} && !$Param{Old};
|
|
|
|
# return result of 'eq' when both params are scalars
|
|
return $Param{New} ne $Param{Old} if !ref( $Param{New} ) && !ref( $Param{Old} );
|
|
|
|
# a field has changed when 'ref' is different
|
|
return 1 if ref( $Param{New} ) ne ref( $Param{Old} );
|
|
|
|
# check hashes
|
|
if ( ref $Param{New} eq 'HASH' ) {
|
|
|
|
# field has changed when number of keys are different
|
|
return 1 if scalar keys %{ $Param{New} } != scalar keys %{ $Param{Old} };
|
|
|
|
# check the values for each key
|
|
for my $Key ( sort keys %{ $Param{New} } ) {
|
|
return 1 if $Param{New}->{$Key} ne $Param{Old}->{$Key};
|
|
}
|
|
}
|
|
|
|
# check arrays
|
|
if ( ref $Param{New} eq 'ARRAY' ) {
|
|
|
|
# changed when number of elements differ
|
|
return 1 if scalar @{ $Param{New} } != scalar @{ $Param{Old} };
|
|
|
|
# check each element
|
|
for my $Index ( 0 .. $#{ $Param{New} } ) {
|
|
return 1 if $Param{New}->[$Index] ne $Param{Old}->[$Index];
|
|
}
|
|
}
|
|
|
|
# field has not been changed
|
|
return 0;
|
|
}
|
|
|
|
=head2 _AgentAndCustomerIDsGet()
|
|
|
|
Get the agent and customer IDs from the recipient list.
|
|
|
|
my $AgentAndCustomerIDs = $HistoryObject->_AgentAndCustomerIDsGet(
|
|
Recipients => ['ChangeBuilder', 'ChangeManager'],
|
|
);
|
|
|
|
returns
|
|
|
|
$AgentAndCustomerIDs = {
|
|
AgentIDs => [ 2, 4 ],
|
|
CustomerIDs => [],
|
|
};
|
|
|
|
=cut
|
|
|
|
sub _AgentAndCustomerIDsGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $WorkOrderAgentID;
|
|
if ( $Param{Type} eq 'WorkOrder' ) {
|
|
|
|
# check WorkOrderID
|
|
if ( !$Param{WorkOrderID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The param 'WorkOrderID' is required for WorkOrder events!",
|
|
);
|
|
}
|
|
elsif ( $Param{Event} eq 'WorkOrderDelete' ) {
|
|
|
|
# the workorder is already deleted, so we look at the OldData
|
|
$Param{ChangeID} = $Param{OldData}->{ChangeID};
|
|
$WorkOrderAgentID = $Param{OldData}->{WorkOrderAgentID};
|
|
}
|
|
else {
|
|
|
|
# get ChangeID and WorkOrderAgentID from the WorkOrder,
|
|
# the WorkOrderAgent might have been recently updated
|
|
my $WorkOrder = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder')->WorkOrderGet(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
|
|
);
|
|
$Param{ChangeID} = $WorkOrder->{ChangeID};
|
|
$WorkOrderAgentID = $WorkOrder->{WorkOrderAgentID};
|
|
}
|
|
}
|
|
|
|
# these arrays will be returned
|
|
my ( @AgentIDs, @CustomerIDs );
|
|
|
|
# needed for determining the actual recipients
|
|
my $Change = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeGet(
|
|
ChangeID => $Param{ChangeID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return if !$Change;
|
|
return if ref $Change ne 'HASH';
|
|
return if !%{$Change};
|
|
|
|
for my $Recipient ( @{ $Param{Recipients} } ) {
|
|
|
|
if ( $Recipient eq 'ChangeBuilder' || $Recipient eq 'ChangeManager' ) {
|
|
|
|
# take the builder or manager from the current change data
|
|
push @AgentIDs, $Change->{ $Recipient . 'ID' };
|
|
}
|
|
elsif ( $Recipient eq 'OldChangeBuilder' || $Recipient eq 'OldChangeManager' ) {
|
|
if ( $Param{Event} ne 'ChangeUpdate' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Recipient $Recipient is only valid for ChangeUpdate events, "
|
|
. "but the event was a '$Param{Event}' event!",
|
|
);
|
|
}
|
|
else {
|
|
|
|
# take the builder or manager from the original change data
|
|
$Recipient =~ s{ \A Old }{}xms;
|
|
push @AgentIDs, $Param{OldData}->{ $Recipient . 'ID' };
|
|
}
|
|
}
|
|
elsif ( $Recipient eq 'CABCustomers' ) {
|
|
push @CustomerIDs, @{ $Change->{CABCustomers} };
|
|
}
|
|
elsif ( $Recipient eq 'CABAgents' ) {
|
|
push @AgentIDs, @{ $Change->{CABAgents} };
|
|
}
|
|
elsif ( $Recipient eq 'WorkOrderAgent' ) {
|
|
if ( $Param{Type} ne 'WorkOrder' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Recipient $Recipient is only valid for workorder events "
|
|
. "but the event was a '$Param{Event}' event!",
|
|
);
|
|
}
|
|
else {
|
|
push @AgentIDs, $WorkOrderAgentID;
|
|
}
|
|
}
|
|
elsif ( $Recipient eq 'OldWorkOrderAgent' ) {
|
|
if ( $Param{Event} ne 'WorkOrderUpdate' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Recipient $Recipient is only valid for WorkOrderUpdate events "
|
|
. "but the event was a '$Param{Event}' event!",
|
|
);
|
|
}
|
|
else {
|
|
|
|
# take the workorder agent from the original workorder data
|
|
$Recipient =~ s{ \A Old }{}xms;
|
|
push @AgentIDs, $Param{OldData}->{ $Recipient . 'ID' };
|
|
}
|
|
}
|
|
elsif ( $Recipient eq 'WorkOrderAgents' ) {
|
|
|
|
# loop over the workorders of a change and get their workorder agents
|
|
for my $WorkOrderID ( @{ $Change->{WorkOrderIDs} } ) {
|
|
my $WorkOrder = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder')->WorkOrderGet(
|
|
WorkOrderID => $WorkOrderID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
push @AgentIDs, $WorkOrder->{WorkOrderAgentID};
|
|
}
|
|
}
|
|
elsif ( $Recipient eq 'ChangeInitiators' ) {
|
|
|
|
# get linked objects which are directly linked with this change object
|
|
my $LinkListWithData = $Kernel::OM->Get('Kernel::System::LinkObject')->LinkListWithData(
|
|
Object => 'ITSMChange',
|
|
Key => $Param{ChangeID},
|
|
State => 'Valid',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# get change initiators (customer users of linked tickets)
|
|
# This should be the same list a displayed in ChangeZoom.
|
|
my $LinkList = $LinkListWithData->{Ticket} || {};
|
|
for my $LinkType ( sort keys %{$LinkList} ) {
|
|
|
|
# the linked tickets are always the 'Source'.
|
|
for my $TicketData ( values %{ $LinkList->{$LinkType}->{Source} } ) {
|
|
|
|
# The data for the linked ticket can have a customer id.
|
|
# If it doesn't, fall back to the owner.
|
|
if ( $TicketData->{CustomerUserID} ) {
|
|
push @CustomerIDs, $TicketData->{CustomerUserID};
|
|
}
|
|
else {
|
|
push @AgentIDs, $TicketData->{OwnerID};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elsif ( $Recipient =~ m{ \A GroupITSMChange(|Builder|Manager) \z }xms ) {
|
|
|
|
my %Recipient2Group = (
|
|
GroupITSMChange => 'itsm-change',
|
|
GroupITSMChangeBuilder => 'itsm-change-builder',
|
|
GroupITSMChangeManager => 'itsm-change-manager',
|
|
);
|
|
|
|
# get group id
|
|
my $GroupID = $Kernel::OM->Get('Kernel::System::Group')->GroupLookup(
|
|
Group => $Recipient2Group{$Recipient}
|
|
);
|
|
|
|
# get members of group
|
|
my %Users = $Kernel::OM->Get('Kernel::System::Group')->GroupMemberList(
|
|
GroupID => $GroupID,
|
|
Type => 'ro',
|
|
Result => 'HASH',
|
|
Cached => 1,
|
|
);
|
|
|
|
push @AgentIDs, keys %Users;
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Unknown recipient '$Recipient'!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# no need to eliminate duplicates, NotificationSend() takes care of that
|
|
# remove empty IDs
|
|
@AgentIDs = grep {$_} @AgentIDs;
|
|
@CustomerIDs = grep {$_} @CustomerIDs;
|
|
|
|
my %AgentAndCustomerIDs = (
|
|
AgentIDs => \@AgentIDs,
|
|
CustomerIDs => \@CustomerIDs,
|
|
);
|
|
|
|
return \%AgentAndCustomerIDs;
|
|
}
|
|
|
|
1;
|
|
|
|
=end Internal:
|