Files
scripts/Perl OTRS/Kernel/System/Calendar/Event/Notification.pm
2024-10-14 00:08:40 +02:00

696 lines
23 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::Calendar::Event::Notification;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Calendar',
'Kernel::System::Calendar::Appointment',
'Kernel::System::DateTime',
'Kernel::System::Group',
'Kernel::System::JSON',
'Kernel::System::Log',
'Kernel::System::NotificationEvent',
'Kernel::System::CalendarTemplateGenerator',
'Kernel::System::Ticket',
'Kernel::System::User',
);
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 $Needed (qw(Event Data Config UserID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
if ( !$Param{Data}->{AppointmentID} && !$Param{Data}->{CalendarID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need either CalendarID or AppointmentID in Data!',
);
return;
}
# get notification event object
my $NotificationEventObject = $Kernel::OM->Get('Kernel::System::NotificationEvent');
# check if event is affected
my @IDs = $NotificationEventObject->NotificationEventCheck(
Event => $Param{Event},
);
# return if no notification for event exists
if ( !IsArrayRefWithData( \@IDs ) ) {
# update the future tasks
$Self->_FutureTaskUpdate();
return 1;
}
my $AppointmentObject = $Kernel::OM->Get('Kernel::System::Calendar::Appointment');
my $CalendarObject = $Kernel::OM->Get('Kernel::System::Calendar');
my %Calendar;
my %Appointment;
if ( $Param{Data}->{AppointmentID} ) {
%Appointment = $AppointmentObject->AppointmentGet(
AppointmentID => $Param{Data}->{AppointmentID},
);
%Calendar = $CalendarObject->CalendarGet(
CalendarID => $Appointment{CalendarID} || $Param{Data}->{CalendarID},
);
}
elsif ( $Param{Data}->{CalendarID} ) {
%Calendar = $CalendarObject->CalendarGet(
CalendarID => $Param{Data}->{CalendarID},
);
}
NOTIFICATIONID:
for my $NotificationID (@IDs) {
my %Notification = $NotificationEventObject->NotificationGet(
ID => $NotificationID,
);
# verify appointment conditions
my $PassFilter = $Self->_NotificationFilter(
%Param,
Appointment => \%Appointment,
Calendar => \%Calendar,
Notification => \%Notification,
);
next NOTIFICATIONID if !$PassFilter;
# get recipients
my @RecipientUsers = $Self->_RecipientsGet(
%Param,
Appointment => \%Appointment,
Calendar => \%Calendar,
Notification => \%Notification,
);
my @NotificationBundle;
# get template generator object;
my $TemplateGeneratorObject = $Kernel::OM->Get('Kernel::System::CalendarTemplateGenerator');
# parse all notification tags for each user
for my $Recipient (@RecipientUsers) {
my %ReplacedNotification = $TemplateGeneratorObject->NotificationEvent(
AppointmentID => $Param{Data}->{AppointmentID},
CalendarID => $Param{Data}->{CalendarID},
Recipient => $Recipient,
Notification => \%Notification,
UserID => $Param{UserID},
);
my $UserNotificationTransport = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
Data => $Recipient->{AppointmentNotificationTransport},
);
push @NotificationBundle, {
Recipient => $Recipient,
Notification => \%ReplacedNotification,
RecipientNotificationTransport => $UserNotificationTransport,
};
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get notification transport config
my %TransportConfig = %{ $ConfigObject->Get('AppointmentNotification::Transport') || {} };
# remember already sent agent notifications
my %AlreadySent;
# loop over transports for each notification
TRANSPORT:
for my $Transport ( sort keys %TransportConfig ) {
# only configured transports for this notification
if ( !grep { $_ eq $Transport } @{ $Notification{Data}->{Transports} } ) {
next TRANSPORT;
}
next TRANSPORT if !IsHashRefWithData( $TransportConfig{$Transport} );
next TRANSPORT if !$TransportConfig{$Transport}->{Module};
# get transport object
my $TransportObject;
eval {
$TransportObject = $Kernel::OM->Get( $TransportConfig{$Transport}->{Module} );
};
if ( !$TransportObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Could not create a new $TransportConfig{$Transport}->{Module} object!",
);
next TRANSPORT;
}
if ( ref $TransportObject ne $TransportConfig{$Transport}->{Module} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$TransportConfig{$Transport}->{Module} object is invalid",
);
next TRANSPORT;
}
# check if transport is usable
next TRANSPORT if !$TransportObject->IsUsable();
BUNDLE:
for my $Bundle (@NotificationBundle) {
my $UserPreference = "Notification-$Notification{ID}-$Transport";
# check if agent should get the notification
my $AgentSendNotification = 0;
if ( defined $Bundle->{RecipientNotificationTransport}->{$UserPreference} ) {
$AgentSendNotification = $Bundle->{RecipientNotificationTransport}->{$UserPreference};
}
elsif ( grep { $_ eq $Transport } @{ $Notification{Data}->{AgentEnabledByDefault} } ) {
$AgentSendNotification = 1;
}
elsif (
!IsArrayRefWithData( $Notification{Data}->{VisibleForAgent} )
|| (
defined $Notification{Data}->{VisibleForAgent}->[0]
&& !$Notification{Data}->{VisibleForAgent}->[0]
)
)
{
$AgentSendNotification = 1;
}
# skip sending the notification if the agent has disable it in its preferences
if (
IsArrayRefWithData( $Notification{Data}->{VisibleForAgent} )
&& $Notification{Data}->{VisibleForAgent}->[0]
&& $Bundle->{Recipient}->{Type} eq 'Agent'
&& !$AgentSendNotification
)
{
next BUNDLE;
}
my $Success = $Self->_SendRecipientNotification(
AppointmentID => $Appointment{AppointmentID} || '',
CalendarID => $Calendar{CalendarID} || $Appointment{CalendarID} || '',
Notification => $Bundle->{Notification},
CustomerMessageParams => $Param{Data}->{CustomerMessageParams} || {},
Recipient => $Bundle->{Recipient},
Event => $Param{Event},
Transport => $Transport,
TransportObject => $TransportObject,
UserID => $Param{UserID},
);
# remember to have sent
$AlreadySent{ $Bundle->{Recipient}->{UserID} } = 1;
}
# get special recipients specific for each transport
my @TransportRecipients = $TransportObject->GetTransportRecipients(
Notification => \%Notification,
);
next TRANSPORT if !@TransportRecipients;
RECIPIENT:
for my $Recipient (@TransportRecipients) {
# replace all notification tags for each special recipient
my %ReplacedNotification = $TemplateGeneratorObject->NotificationEvent(
AppointmentID => $Param{Data}->{AppointmentID},
Recipient => $Recipient,
Notification => \%Notification,
CustomerMessageParams => $Param{Data}->{CustomerMessageParams} || {},
UserID => $Param{UserID},
);
my $Success = $Self->_SendRecipientNotification(
AppointmentID => $Appointment{AppointmentID} || '',
CalendarID => $Calendar{CalendarID} || $Appointment{CalendarID} || '',
Notification => \%ReplacedNotification,
CustomerMessageParams => $Param{Data}->{CustomerMessageParams} || {},
Recipient => $Recipient,
Event => $Param{Event},
Transport => $Transport,
TransportObject => $TransportObject,
UserID => $Param{UserID},
);
}
}
}
# update appointment future tasks
$Self->_FutureTaskUpdate();
return 1;
}
sub _NotificationFilter {
my ( $Self, %Param ) = @_;
# check needed params
for my $Needed (qw(Appointment Calendar Notification)) {
return if !$Param{$Needed};
}
# set local values
my %Notification = %{ $Param{Notification} };
return if !IsHashRefWithData( $Notification{Data} );
KEY:
for my $Key ( sort keys %{ $Notification{Data} } ) {
# ignore not appointment related attributes
next KEY if $Key eq 'Recipients';
next KEY if $Key eq 'SkipRecipients';
next KEY if $Key eq 'RecipientAgents';
next KEY if $Key eq 'RecipientGroups';
next KEY if $Key eq 'RecipientRoles';
next KEY if $Key eq 'TransportEmailTemplate';
next KEY if $Key eq 'Events';
next KEY if $Key eq 'ArticleTypeID';
next KEY if $Key eq 'ArticleSenderTypeID';
next KEY if $Key eq 'ArticleSubjectMatch';
next KEY if $Key eq 'ArticleBodyMatch';
next KEY if $Key eq 'ArticleAttachmentInclude';
next KEY if $Key eq 'NotificationArticleTypeID';
next KEY if $Key eq 'Transports';
next KEY if $Key eq 'OncePerDay';
next KEY if $Key eq 'VisibleForAgent';
next KEY if $Key eq 'VisibleForAgentTooltip';
next KEY if $Key eq 'LanguageID';
next KEY if $Key eq 'SendOnOutOfOffice';
next KEY if $Key eq 'AgentEnabledByDefault';
next KEY if $Key eq 'NotificationType';
# check recipient fields from transport methods
if ( $Key =~ m{\A Recipient}xms ) {
next KEY;
}
# check appointment attributes
next KEY if !$Notification{Data}->{$Key};
next KEY if !@{ $Notification{Data}->{$Key} };
next KEY if !$Notification{Data}->{$Key}->[0];
my $Match = 0;
VALUE:
for my $Value ( @{ $Notification{Data}->{$Key} } ) {
next VALUE if !$Value;
if (
$Key eq 'TeamID'
|| $Key eq 'ResourceID'
)
{
# check for existing object ids in appointment
next KEY if !IsArrayRefWithData( $Param{Appointment}->{$Key} );
OBJECTID:
for my $ObjectID ( @{ $Param{Appointment}->{$Key} } ) {
next OBJECTID if !$ObjectID;
if ( $Value eq $ObjectID ) {
$Match = 1;
last VALUE;
}
}
}
elsif ( $Key eq 'Title' || $Key eq 'Location' ) {
if ( defined $Param{Appointment}->{$Key} && $Param{Appointment}->{$Key} =~ m/$Value/i ) {
$Match = 1;
last VALUE;
}
}
else {
if ( defined $Param{Appointment}->{$Key} && $Value eq $Param{Appointment}->{$Key} ) {
$Match = 1;
last VALUE;
}
}
}
return if !$Match;
}
return 1;
}
sub _RecipientsGet {
my ( $Self, %Param ) = @_;
# check needed params
for my $Needed (qw(Appointment Calendar Notification)) {
return if !$Param{$Needed};
}
# set local values
my %Notification = %{ $Param{Notification} };
my %Appointment = %{ $Param{Appointment} };
# get needed objects
my $AppointmentObject = $Kernel::OM->Get('Kernel::System::Calendar::Appointment');
my $CalendarObject = $Kernel::OM->Get('Kernel::System::Calendar');
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my @RecipientUserIDs;
my @RecipientUsers;
# add pre-calculated recipient
if ( IsArrayRefWithData( $Param{Data}->{Recipients} ) ) {
push @RecipientUserIDs, @{ $Param{Data}->{Recipients} };
}
# remember pre-calculated user recipients for later comparisons
my %PrecalculatedUserIDs = map { $_ => 1 } @RecipientUserIDs;
# get recipients by Recipients
if ( $Notification{Data}->{Recipients} ) {
RECIPIENT:
for my $Recipient ( @{ $Notification{Data}->{Recipients} } ) {
if (
$Recipient
=~ /^Appointment(Agents|AgentReadPermissions|AgentWritePermissions)$/
)
{
if ( $Recipient eq 'AppointmentAgents' ) {
RESOURCEID:
for my $ResourceID ( @{ $Appointment{ResourceID} } ) {
next RESOURCEID if !$ResourceID;
push @{ $Notification{Data}->{RecipientAgents} }, $ResourceID;
}
}
elsif ( $Recipient eq 'AppointmentAgentReadPermissions' ) {
# get calendar information
my %Calendar = $CalendarObject->CalendarGet(
CalendarID => $Appointment{CalendarID} || $Param{Calendar}->{CalendarID},
UserID => 1,
);
# get a list of read access users for the related calendar
my %Users = $GroupObject->PermissionGroupGet(
GroupID => $Calendar{GroupID},
Type => 'ro',
);
USERID:
for my $UserID ( sort keys %Users ) {
next USERID if !$UserID;
push @{ $Notification{Data}->{RecipientAgents} }, $UserID;
}
}
elsif ( $Recipient eq 'AppointmentAgentWritePermissions' ) {
# get calendar information
my %Calendar = $CalendarObject->CalendarGet(
CalendarID => $Appointment{CalendarID} || $Param{Calendar}->{CalendarID},
UserID => 1,
);
# get a list of read access users for the related calendar
my %Users = $GroupObject->PermissionGroupGet(
GroupID => $Calendar{GroupID},
Type => 'rw',
);
USERID:
for my $UserID ( sort keys %Users ) {
next USERID if !$UserID;
push @{ $Notification{Data}->{RecipientAgents} }, $UserID;
}
}
}
}
}
# add recipient agents
if ( IsArrayRefWithData( $Notification{Data}->{RecipientAgents} ) ) {
push @RecipientUserIDs, @{ $Notification{Data}->{RecipientAgents} };
}
# hash to keep track which agents are already receiving this notification
my %AgentUsed = map { $_ => 1 } @RecipientUserIDs;
# get recipients by RecipientGroups
if ( $Notification{Data}->{RecipientGroups} ) {
RECIPIENT:
for my $GroupID ( @{ $Notification{Data}->{RecipientGroups} } ) {
my %GroupMemberList = $GroupObject->PermissionGroupUserGet(
GroupID => $GroupID,
Type => 'ro',
);
GROUPMEMBER:
for my $UserID ( sort keys %GroupMemberList ) {
next GROUPMEMBER if $UserID == 1;
next GROUPMEMBER if $AgentUsed{$UserID};
$AgentUsed{$UserID} = 1;
push @RecipientUserIDs, $UserID;
}
}
}
# get recipients by RecipientRoles
if ( $Notification{Data}->{RecipientRoles} ) {
RECIPIENT:
for my $RoleID ( @{ $Notification{Data}->{RecipientRoles} } ) {
my %RoleMemberList = $GroupObject->PermissionRoleUserGet(
RoleID => $RoleID,
);
ROLEMEMBER:
for my $UserID ( sort keys %RoleMemberList ) {
next ROLEMEMBER if $UserID == 1;
next ROLEMEMBER if $AgentUsed{$UserID};
$AgentUsed{$UserID} = 1;
push @RecipientUserIDs, $UserID;
}
}
}
# get needed objects
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
my %SkipRecipients;
if ( IsArrayRefWithData( $Param{Data}->{SkipRecipients} ) ) {
%SkipRecipients = map { $_ => 1 } @{ $Param{Data}->{SkipRecipients} };
}
# agent 1 should not receive notifications
$SkipRecipients{'1'} = 1;
# remove recipients should not receive a notification
@RecipientUserIDs = grep { !$SkipRecipients{$_} } @RecipientUserIDs;
# get valid users list
my %ValidUsersList = $UserObject->UserList(
Type => 'Short',
Valid => 1,
NoOutOfOffice => 0,
);
# remove invalid users
@RecipientUserIDs = grep { $ValidUsersList{$_} } @RecipientUserIDs;
# remove duplicated
my %TempRecipientUserIDs = map { $_ => 1 } @RecipientUserIDs;
@RecipientUserIDs = sort keys %TempRecipientUserIDs;
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
# get all data for recipients as they should be needed by all notification transports
RECIPIENT:
for my $UserID (@RecipientUserIDs) {
my %User = $UserObject->GetUserData(
UserID => $UserID,
Valid => 1,
);
next RECIPIENT if !%User;
# skip user that triggers the event (it should not be notified) but only if it is not
# a pre-calculated recipient
if (
!$ConfigObject->Get('AgentSelfNotifyOnAction')
&& $User{UserID} == $Param{UserID}
&& !$PrecalculatedUserIDs{ $Param{UserID} }
)
{
next RECIPIENT;
}
# skip users out of the office if configured
if ( !$Notification{Data}->{SendOnOutOfOffice} && $User{OutOfOffice} ) {
my $Start = sprintf(
"%04d-%02d-%02d 00:00:00",
$User{OutOfOfficeStartYear}, $User{OutOfOfficeStartMonth},
$User{OutOfOfficeStartDay}
);
my $StartTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Start,
},
);
my $End = sprintf(
"%04d-%02d-%02d 23:59:59",
$User{OutOfOfficeEndYear}, $User{OutOfOfficeEndMonth},
$User{OutOfOfficeEndDay}
);
my $EndTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $End,
},
);
next RECIPIENT if $StartTimeObject < $DateTimeObject && $EndTimeObject > $DateTimeObject;
}
# skip PostMasterUserID
my $PostmasterUserID = $ConfigObject->Get('PostmasterUserID') || 1;
next RECIPIENT if $User{UserID} == $PostmasterUserID;
$User{Type} = 'Agent';
push @RecipientUsers, \%User;
}
return @RecipientUsers;
}
sub _SendRecipientNotification {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(UserID Notification Recipient Event Transport TransportObject)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
}
}
my $TransportObject = $Param{TransportObject};
# send notification to each recipient
my $Success = $TransportObject->SendNotification(
AppointmentID => $Param{AppointmentID},
CalendarID => $Param{CalendarID},
UserID => $Param{UserID},
Notification => $Param{Notification},
CustomerMessageParams => $Param{CustomerMessageParams},
Recipient => $Param{Recipient},
Event => $Param{Event},
Attachments => $Param{Attachments},
);
return if !$Success;
my %EventData = %{ $TransportObject->GetTransportEventData() };
return 1 if !%EventData;
if ( !$EventData{Event} || !$EventData{Data} || !$EventData{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Could not trigger notification post send event",
);
return;
}
return 1;
}
sub _FutureTaskUpdate {
my ( $Self, %Param ) = @_;
# Get the next upcoming appointment.
my $Success = $Kernel::OM->Get('Kernel::System::Calendar::Appointment')->AppointmentFutureTasksUpdate();
if ( !$Success ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Could not update upcoming appointment data!',
);
return;
}
return 1;
}
1;