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

1969 lines
60 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::TemplateGenerator;
## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject)
use strict;
use warnings;
use Kernel::Language;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::AutoResponse',
'Kernel::System::CommunicationChannel',
'Kernel::System::CustomerUser',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Encode',
'Kernel::System::HTMLUtils',
'Kernel::System::Log',
'Kernel::System::Queue',
'Kernel::System::Salutation',
'Kernel::System::Signature',
'Kernel::System::StandardTemplate',
'Kernel::System::SystemAddress',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
'Kernel::System::User',
'Kernel::Output::HTML::Layout',
'Kernel::System::DateTime',
);
=head1 NAME
Kernel::System::TemplateGenerator - signature lib
=head1 DESCRIPTION
All signature functions.
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $TemplateGeneratorObject = $Kernel::OM->Get('Kernel::System::TemplateGenerator');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{RichText} = $Kernel::OM->Get('Kernel::Config')->Get('Frontend::RichText');
return $Self;
}
=head2 Salutation()
generate salutation
my $Salutation = $TemplateGeneratorObject->Salutation(
TicketID => 123,
UserID => 123,
Data => $ArticleHashRef,
);
returns
Text
ContentType
=cut
sub Salutation {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID Data UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# Get ticket.
my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
);
# Get queue.
my %Queue = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet(
ID => $Ticket{QueueID},
);
# Get salutation.
my %Salutation = $Kernel::OM->Get('Kernel::System::Salutation')->SalutationGet(
ID => $Queue{SalutationID},
);
# do text/plain to text/html convert
if ( $Self->{RichText} && $Salutation{ContentType} =~ /text\/plain/i ) {
$Salutation{ContentType} = 'text/html';
$Salutation{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Salutation{Text},
);
}
# do text/html to text/plain convert
if ( !$Self->{RichText} && $Salutation{ContentType} =~ /text\/html/i ) {
$Salutation{ContentType} = 'text/plain';
$Salutation{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $Salutation{Text},
);
}
# get list unsupported tags for standard template
my @ListOfUnSupportedTag = qw(OTRS_AGENT_SUBJECT OTRS_AGENT_BODY OTRS_CUSTOMER_BODY OTRS_CUSTOMER_SUBJECT);
my $SalutationText = $Self->_RemoveUnSupportedTag(
Text => $Salutation{Text} || '',
ListOfUnSupportedTag => \@ListOfUnSupportedTag,
);
# replace place holder stuff
$SalutationText = $Self->_Replace(
RichText => $Self->{RichText},
Text => $SalutationText,
TicketData => \%Ticket,
Data => $Param{Data},
UserID => $Param{UserID},
);
# add urls
if ( $Self->{RichText} ) {
$SalutationText = $Kernel::OM->Get('Kernel::System::HTMLUtils')->LinkQuote(
String => $SalutationText,
);
}
return $SalutationText;
}
=head2 Signature()
generate salutation
my $Signature = $TemplateGeneratorObject->Signature(
TicketID => 123,
UserID => 123,
Data => $ArticleHashRef,
);
or
my $Signature = $TemplateGeneratorObject->Signature(
QueueID => 123,
UserID => 123,
Data => $ArticleHashRef,
);
returns
Text
ContentType
=cut
sub Signature {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Data UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# need ticket id or queue id
if ( !$Param{TicketID} && !$Param{QueueID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need TicketID or QueueID!'
);
return;
}
# Get ticket data.
my %Ticket;
if ( $Param{TicketID} ) {
%Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
);
}
# Get queue.
my %Queue = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet(
ID => $Ticket{QueueID} || $Param{QueueID},
);
# Get signature.
my %Signature = $Kernel::OM->Get('Kernel::System::Signature')->SignatureGet(
ID => $Queue{SignatureID},
);
# do text/plain to text/html convert
if ( $Self->{RichText} && $Signature{ContentType} =~ /text\/plain/i ) {
$Signature{ContentType} = 'text/html';
$Signature{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Signature{Text},
);
}
# do text/html to text/plain convert
if ( !$Self->{RichText} && $Signature{ContentType} =~ /text\/html/i ) {
$Signature{ContentType} = 'text/plain';
$Signature{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $Signature{Text},
);
}
# get list unsupported tags for standard template
my @ListOfUnSupportedTag = qw(OTRS_AGENT_SUBJECT OTRS_AGENT_BODY OTRS_CUSTOMER_BODY OTRS_CUSTOMER_SUBJECT);
my $SignatureText = $Self->_RemoveUnSupportedTag(
Text => $Signature{Text} || '',
ListOfUnSupportedTag => \@ListOfUnSupportedTag,
);
# replace place holder stuff
$SignatureText = $Self->_Replace(
RichText => $Self->{RichText},
Text => $SignatureText,
TicketData => \%Ticket,
Data => $Param{Data},
QueueID => $Param{QueueID},
UserID => $Param{UserID},
);
# add urls
if ( $Self->{RichText} ) {
$SignatureText = $Kernel::OM->Get('Kernel::System::HTMLUtils')->LinkQuote(
String => $SignatureText,
);
}
return $SignatureText;
}
=head2 Sender()
generate sender address (FROM string) for emails
my $Sender = $TemplateGeneratorObject->Sender(
QueueID => 123,
UserID => 123,
);
returns:
John Doe at Super Support <service@example.com>
and it returns the quoted real name if necessary
"John Doe, Support" <service@example.tld>
=cut
sub Sender {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw( UserID QueueID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get sender attributes
my %Address = $Kernel::OM->Get('Kernel::System::Queue')->GetSystemAddress(
QueueID => $Param{QueueID},
);
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# check config for agent real name
my $UseAgentRealName = $ConfigObject->Get('Ticket::DefineEmailFrom');
if ( $UseAgentRealName && $UseAgentRealName =~ /^(AgentName|AgentNameSystemAddressName)$/ ) {
# get data from current agent
my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $Param{UserID},
NoOutOfOffice => 1,
);
# set real name with user name
if ( $UseAgentRealName eq 'AgentName' ) {
# check for user data
if ( $UserData{UserFullname} ) {
# rewrite RealName
$Address{RealName} = "$UserData{UserFullname}";
}
}
# set real name with user name
if ( $UseAgentRealName eq 'AgentNameSystemAddressName' ) {
# check for user data
if ( $UserData{UserFullname} ) {
# rewrite RealName
my $Separator = ' ' . $ConfigObject->Get('Ticket::DefineEmailFromSeparator')
|| '';
$Address{RealName} = $UserData{UserFullname} . $Separator . ' ' . $Address{RealName};
}
}
}
# prepare realname quote
if ( $Address{RealName} =~ /([.]|,|@|\(|\)|:)/ && $Address{RealName} !~ /^("|')/ ) {
$Address{RealName} =~ s/"//g; # remove any quotes that are already present
$Address{RealName} = '"' . $Address{RealName} . '"';
}
my $Sender = "$Address{RealName} <$Address{Email}>";
return $Sender;
}
=head2 Template()
generate template
my $Template = $TemplateGeneratorObject->Template(
TemplateID => 123
TicketID => 123, # Optional
Data => $ArticleHashRef, # Optional
UserID => 123,
);
Returns:
$Template => 'Some text';
=cut
sub Template {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TemplateID UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
my %Template = $Kernel::OM->Get('Kernel::System::StandardTemplate')->StandardTemplateGet(
ID => $Param{TemplateID},
);
# do text/plain to text/html convert
if (
$Self->{RichText}
&& $Template{ContentType} =~ /text\/plain/i
&& $Template{Template}
)
{
$Template{ContentType} = 'text/html';
$Template{Template} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Template{Template},
);
}
# do text/html to text/plain convert
if (
!$Self->{RichText}
&& $Template{ContentType} =~ /text\/html/i
&& $Template{Template}
)
{
$Template{ContentType} = 'text/plain';
$Template{Template} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $Template{Template},
);
}
# Get user language.
my $Language;
my %Ticket;
if ( defined $Param{TicketID} ) {
# Get ticket data.
%Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
);
# Get recipient.
my %User = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $Ticket{CustomerUserID},
);
$Language = $User{UserLanguage};
}
# If template type is 'Create' and there is customer user information, treat it as a ticket param in order to
# correctly replace customer user tags. See bug#14455.
if ( $Template{TemplateType} eq 'Create' && $Param{CustomerUserID} ) {
$Ticket{CustomerUserID} = $Param{CustomerUserID};
}
# if customer language is not defined, set default language
$Language //= $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage') || 'en';
# get list unsupported tags for standard template
my @ListOfUnSupportedTag = qw(OTRS_AGENT_SUBJECT OTRS_AGENT_BODY OTRS_CUSTOMER_BODY OTRS_CUSTOMER_SUBJECT);
my $TemplateText = $Self->_RemoveUnSupportedTag(
Text => $Template{Template} || '',
ListOfUnSupportedTag => \@ListOfUnSupportedTag,
);
# replace place holder stuff
$TemplateText = $Self->_Replace(
RichText => $Self->{RichText},
Text => $TemplateText || '',
TicketData => \%Ticket,
Data => $Param{Data} || {},
UserID => $Param{UserID},
Language => $Language,
);
return $TemplateText;
}
=head2 GenericAgentArticle()
generate internal or external notes
my $GenericAgentArticle = $TemplateGeneratorObject->GenericAgentArticle(
Notification => $NotificationDataHashRef,
TicketID => 123,
UserID => 123,
Data => $ArticleHashRef, # Optional
);
=cut
sub GenericAgentArticle {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID Notification UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
my %Template = %{ $Param{Notification} };
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# Get ticket data.
my %Ticket = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
);
# do text/plain to text/html convert
if (
$Self->{RichText}
&& $Template{ContentType} =~ /text\/plain/i
&& $Template{Body}
)
{
$Template{ContentType} = 'text/html';
$Template{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Template{Body},
);
}
# do text/html to text/plain convert
if (
!$Self->{RichText}
&& $Template{ContentType} =~ /text\/html/i
&& $Template{Body}
)
{
$Template{ContentType} = 'text/plain';
$Template{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $Template{Body},
);
}
# replace place holder stuff
$Template{Body} = $Self->_Replace(
RichText => $Self->{RichText},
Text => $Template{Body},
Recipient => $Param{Recipient},
Data => $Param{Data} || {},
TicketData => \%Ticket,
UserID => $Param{UserID},
);
$Template{Subject} = $Self->_Replace(
RichText => 0,
Text => $Template{Subject},
Recipient => $Param{Recipient},
Data => $Param{Data} || {},
TicketData => \%Ticket,
UserID => $Param{UserID},
);
$Template{Subject} = $TicketObject->TicketSubjectBuild(
TicketNumber => $Ticket{TicketNumber},
Subject => $Template{Subject} || '',
Type => 'New',
);
# add URLs and verify to be full HTML document
if ( $Self->{RichText} ) {
$Template{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->LinkQuote(
String => $Template{Body},
);
}
return %Template;
}
=head2 Attributes()
generate attributes
my %Attributes = $TemplateGeneratorObject->Attributes(
TicketID => 123,
ArticleID => 123,
ResponseID => 123
UserID => 123,
Action => 'Forward', # Possible values are Reply and Forward, Reply is default.
);
returns
StandardResponse
Salutation
Signature
=cut
sub Attributes {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID Data UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# get queue
my %Ticket = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 0,
);
# prepare subject ...
$Param{Data}->{Subject} = $TicketObject->TicketSubjectBuild(
TicketNumber => $Ticket{TicketNumber},
Subject => $Param{Data}->{Subject} || '',
Action => $Param{Action} || '',
);
# get sender address
$Param{Data}->{From} = $Self->Sender(
QueueID => $Ticket{QueueID},
UserID => $Param{UserID},
);
return %{ $Param{Data} };
}
=head2 AutoResponse()
generate response
AutoResponse
TicketID
Owner
Responsible
CUSTOMER_DATA
ArticleID
CUSTOMER_SUBJECT
CUSTOMER_EMAIL
UserID
To
Cc
Bcc
Subject
Body
ContentType
my %AutoResponse = $TemplateGeneratorObject->AutoResponse(
TicketID => 123,
OrigHeader => {},
AutoResponseType => 'auto reply',
UserID => 123,
);
=cut
sub AutoResponse {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(TicketID AutoResponseType OrigHeader UserID)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# get ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# Get ticket data.
my %Ticket = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
);
# get auto default responses
my %AutoResponse = $Kernel::OM->Get('Kernel::System::AutoResponse')->AutoResponseGetByTypeQueueID(
QueueID => $Ticket{QueueID},
Type => $Param{AutoResponseType},
);
return if !%AutoResponse;
# get old article for quoting
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my @ArticleList = $ArticleObject->ArticleList(
TicketID => $Param{TicketID},
SenderType => 'customer',
OnlyLast => 1,
);
if ( !@ArticleList ) {
@ArticleList = $ArticleObject->ArticleList(
TicketID => $Param{TicketID},
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
}
if (@ArticleList) {
my %Article = $ArticleObject->BackendForArticle( %{ $ArticleList[0] } )->ArticleGet( %{ $ArticleList[0] } );
for (qw(From To Cc Subject Body)) {
if ( !$Param{OrigHeader}->{$_} ) {
$Param{OrigHeader}->{$_} = $Article{$_} || '';
}
chomp $Param{OrigHeader}->{$_};
}
}
# format body (only if longer than 86 chars)
if ( $Param{OrigHeader}->{Body} ) {
if ( length $Param{OrigHeader}->{Body} > 86 ) {
my @Lines = split /\n/, $Param{OrigHeader}->{Body};
LINE:
for my $Line (@Lines) {
my $LineWrapped = $Line =~ s/(^>.+|.{4,86})(?:\s|\z)/$1\n/gm;
next LINE if $LineWrapped;
# if the regex does not match then we need
# to add the missing new line of the split
# else we will lose e.g. empty lines of the body.
# (bug#10679)
$Line .= "\n";
}
$Param{OrigHeader}->{Body} = join '', @Lines;
}
}
# fill up required attributes
for (qw(Subject Body)) {
if ( !$Param{OrigHeader}->{$_} ) {
$Param{OrigHeader}->{$_} = "No $_";
}
}
# get recipient
my %User = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $Ticket{CustomerUserID},
);
# get user language
my $Language = $User{UserLanguage} || $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage') || 'en';
# do text/plain to text/html convert
if ( $Self->{RichText} && $AutoResponse{ContentType} =~ /text\/plain/i ) {
$AutoResponse{ContentType} = 'text/html';
$AutoResponse{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $AutoResponse{Text},
);
}
# do text/html to text/plain convert
if ( !$Self->{RichText} && $AutoResponse{ContentType} =~ /text\/html/i ) {
$AutoResponse{ContentType} = 'text/plain';
$AutoResponse{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $AutoResponse{Text},
);
}
# replace place holder stuff
$AutoResponse{Text} = $Self->_Replace(
RichText => $Self->{RichText},
Text => $AutoResponse{Text},
Data => {
%{ $Param{OrigHeader} },
From => $Param{OrigHeader}->{To},
To => $Param{OrigHeader}->{From},
},
TicketData => \%Ticket,
UserID => $Param{UserID},
Language => $Language,
AddTimezoneInfo => {
AutoResponse => 1,
},
);
$AutoResponse{Subject} = $Self->_Replace(
RichText => 0,
Text => $AutoResponse{Subject},
Data => {
%{ $Param{OrigHeader} },
From => $Param{OrigHeader}->{To},
To => $Param{OrigHeader}->{From},
},
TicketData => \%Ticket,
UserID => $Param{UserID},
Language => $Language,
AddTimezoneInfo => {
AutoResponse => 1,
},
);
$AutoResponse{Subject} = $TicketObject->TicketSubjectBuild(
TicketNumber => $Ticket{TicketNumber},
Subject => $AutoResponse{Subject},
Type => 'New',
NoCleanup => 1,
);
# get sender attributes based on auto response type
if ( $AutoResponse{SystemAddressID} ) {
my %Address = $Kernel::OM->Get('Kernel::System::SystemAddress')->SystemAddressGet(
ID => $AutoResponse{SystemAddressID},
);
$AutoResponse{SenderAddress} = $Address{Name};
$AutoResponse{SenderRealname} = $Address{Realname};
}
# get sender attributes based on queue
else {
my %Address = $Kernel::OM->Get('Kernel::System::Queue')->GetSystemAddress(
QueueID => $Ticket{QueueID},
);
$AutoResponse{SenderAddress} = $Address{Email};
$AutoResponse{SenderRealname} = $Address{RealName};
}
# add urls and verify to be full html document
if ( $Self->{RichText} ) {
$AutoResponse{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->LinkQuote(
String => $AutoResponse{Text},
);
$AutoResponse{Text} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->DocumentComplete(
Charset => 'utf-8',
String => $AutoResponse{Text},
);
}
return %AutoResponse;
}
=head2 NotificationEvent()
replace all OTRS smart tags in the notification body and subject
my %NotificationEvent = $TemplateGeneratorObject->NotificationEvent(
TicketData => $TicketDataHashRef,
Recipient => $UserDataHashRef, # Agent or Customer data get result
Notification => $NotificationDataHashRef,
CustomerMessageParams => $ArticleHashRef, # optional
UserID => 123,
);
=cut
sub NotificationEvent {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(TicketData Notification Recipient UserID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
if ( !IsHashRefWithData( $Param{Notification} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Notification is invalid!",
);
return;
}
my %Notification = %{ $Param{Notification} };
# exchanging original reference prevent it to grow up
if ( ref $Param{CustomerMessageParams} && ref $Param{CustomerMessageParams} eq 'HASH' ) {
my %LocalCustomerMessageParams = %{ $Param{CustomerMessageParams} };
$Param{CustomerMessageParams} = \%LocalCustomerMessageParams;
}
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
# Get last article from customer.
my @CustomerArticles = $ArticleObject->ArticleList(
TicketID => $Param{TicketData}->{TicketID},
SenderType => 'customer',
OnlyLast => 1,
);
my %CustomerArticle;
ARTICLE:
for my $Article (@CustomerArticles) {
next ARTICLE if !$Article->{ArticleID};
%CustomerArticle = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
%{$Article},
DynamicFields => 0,
);
}
# Get last article from agent.
my @AgentArticles = $ArticleObject->ArticleList(
TicketID => $Param{TicketData}->{TicketID},
SenderType => 'agent',
OnlyLast => 1,
);
my %AgentArticle;
AGENTARTICLE:
for my $Article (@AgentArticles) {
next AGENTARTICLE if !$Article->{ArticleID};
%AgentArticle = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
%{$Article},
DynamicFields => 0,
);
# Include the transmission status, if article is an email.
my %CommunicationChannel = $Kernel::OM->Get('Kernel::System::CommunicationChannel')->ChannelGet(
ChannelID => $Article->{CommunicationChannelID},
);
if ( $CommunicationChannel{ChannelName} eq 'Email' ) {
my $TransmissionStatus = $ArticleObject->BackendForArticle( %{$Article} )->ArticleTransmissionStatus(
ArticleID => $Article->{ArticleID},
);
if ( $TransmissionStatus && $TransmissionStatus->{Message} ) {
$AgentArticle{TransmissionStatusMessage} = $TransmissionStatus->{Message};
}
}
}
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
ARTICLE:
for my $ArticleData ( \%CustomerArticle, \%AgentArticle ) {
next ARTICLE if !$ArticleData->{TicketID};
next ARTICLE if !$ArticleData->{ArticleID};
# Get article preview in plain text and store it as Body key.
$ArticleData->{Body} = $LayoutObject->ArticlePreview(
TicketID => $ArticleData->{TicketID},
ArticleID => $ArticleData->{ArticleID},
ResultType => 'plain',
UserID => $Param{UserID},
);
# get accounted time
my $AccountedTime = $ArticleObject->ArticleAccountedTimeGet(
ArticleID => $ArticleData->{ArticleID},
);
# set the accounted time as part of the articles information
$ArticleData->{TimeUnit} = $AccountedTime;
}
# Populate the hash 'CustomerMessageParams' with all the customer-article data
# and overwrite it with 'CustomerMessageParams' passed in the Params (bug #13325).
$Param{CustomerMessageParams} = {
%CustomerArticle,
%{ $Param{CustomerMessageParams} || {} },
};
# get system default language
my $DefaultLanguage = $Kernel::OM->Get('Kernel::Config')->Get('DefaultLanguage') || 'en';
my $Languages = [ $Param{Recipient}->{UserLanguage}, $DefaultLanguage, 'en' ];
my $Language;
LANGUAGE:
for my $Item ( @{$Languages} ) {
next LANGUAGE if !$Item;
next LANGUAGE if !$Notification{Message}->{$Item};
# set language
$Language = $Item;
last LANGUAGE;
}
# if no language, then take the first one available
if ( !$Language ) {
my @NotificationLanguages = sort keys %{ $Notification{Message} };
$Language = $NotificationLanguages[0];
}
# copy the correct language message attributes to a flat structure
for my $Attribute (qw(Subject Body ContentType)) {
$Notification{$Attribute} = $Notification{Message}->{$Language}->{$Attribute};
}
# Get customer article fields.
my %CustomerArticleFields;
if (%CustomerArticle) {
%CustomerArticleFields = $LayoutObject->ArticleFields(
TicketID => $CustomerArticle{TicketID},
ArticleID => $CustomerArticle{ArticleID},
UserID => $Param{UserID},
);
}
ARTICLE_FIELD:
for my $ArticleField ( sort keys %CustomerArticleFields ) {
next ARTICLE_FIELD if !defined $CustomerArticleFields{$ArticleField}->{Value};
if ( !defined $Param{CustomerMessageParams}->{$ArticleField} ) {
$Param{CustomerMessageParams}->{$ArticleField} = $CustomerArticleFields{$ArticleField}->{Value};
}
chomp $Param{CustomerMessageParams}->{$ArticleField};
}
# format body (only if longer the 86 chars)
if ( $Param{CustomerMessageParams}->{Body} ) {
if ( length $Param{CustomerMessageParams}->{Body} > 86 ) {
my @Lines = split /\n/, $Param{CustomerMessageParams}->{Body};
LINE:
for my $Line (@Lines) {
my $LineWrapped = $Line =~ s/(^>.+|.{4,86})(?:\s|\z)/$1\n/gm;
next LINE if $LineWrapped;
# if the regex does not match then we need
# to add the missing new line of the split
# else we will lose e.g. empty lines of the body.
# (bug#10679)
$Line .= "\n";
}
$Param{CustomerMessageParams}->{Body} = join '', @Lines;
}
}
# fill up required attributes
for my $Text (qw(Subject Body)) {
if ( !$Param{CustomerMessageParams}->{$Text} ) {
# Set to last customer article attribute if it is empty string.
# For example, if Body is empty string (not undef!), it is maybe sent from NotificationOwnerUpdate event
# and overrides last customer article body (in %CustomerArticle) above - see bug#14678.
$Param{CustomerMessageParams}->{$Text} = $CustomerArticle{$Text} || "No $Text";
}
}
my $Start = '<';
my $End = '>';
if ( $Notification{ContentType} =~ m{text\/html} ) {
$Start = '&lt;';
$End = '&gt;';
}
# replace <OTRS_CUSTOMER_DATA_*> tags early from CustomerMessageParams, the rests will be replaced
# by ticket customer user
KEY:
for my $Key ( sort keys %{ $Param{CustomerMessageParams} || {} } ) {
next KEY if !$Param{CustomerMessageParams}->{$Key};
$Notification{Body} =~ s/${Start}OTRS_CUSTOMER_DATA_$Key${End}/$Param{CustomerMessageParams}->{$Key}/gi;
$Notification{Subject} =~ s/<OTRS_CUSTOMER_DATA_$Key>/$Param{CustomerMessageParams}->{$Key}{$Key}/gi;
}
# do text/plain to text/html convert
if ( $Self->{RichText} && $Notification{ContentType} =~ /text\/plain/i ) {
$Notification{ContentType} = 'text/html';
$Notification{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Notification{Body},
);
}
# do text/html to text/plain convert
if ( !$Self->{RichText} && $Notification{ContentType} =~ /text\/html/i ) {
$Notification{ContentType} = 'text/plain';
$Notification{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
String => $Notification{Body},
);
}
# get notify texts
for my $Text (qw(Subject Body)) {
if ( !$Notification{$Text} ) {
$Notification{$Text} = "No Notification $Text for $Param{Type} found!";
}
}
# replace place holder stuff
$Notification{Body} = $Self->_Replace(
RichText => $Self->{RichText},
Text => $Notification{Body},
Recipient => $Param{Recipient},
Data => $Param{CustomerMessageParams},
DataAgent => \%AgentArticle,
TicketData => $Param{TicketData},
UserID => $Param{UserID},
Language => $Language,
AddTimezoneInfo => {
NotificationEvent => 1,
},
);
$Notification{Subject} = $Self->_Replace(
RichText => 0,
Text => $Notification{Subject},
Recipient => $Param{Recipient},
Data => $Param{CustomerMessageParams},
DataAgent => \%AgentArticle,
TicketData => $Param{TicketData},
UserID => $Param{UserID},
Language => $Language,
AddTimezoneInfo => {
NotificationEvent => 1,
},
);
# Keep the "original" (unmodified) subject and body for later use.
$Notification{OriginalSubject} = $Notification{Subject};
$Notification{OriginalBody} = $Notification{Body};
$Notification{Subject} = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSubjectBuild(
TicketNumber => $Param{TicketData}->{TicketNumber},
Subject => $Notification{Subject} || '',
Type => 'New',
);
# add URLs and verify to be full HTML document
if ( $Self->{RichText} ) {
$Notification{Body} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->LinkQuote(
String => $Notification{Body},
);
}
return %Notification;
}
=begin Internal:
=cut
sub _Replace {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Text RichText Data UserID)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!"
);
return;
}
}
# check for mailto links
# since the subject and body of those mailto links are
# uri escaped we have to uri unescape them, replace
# possible placeholders and then re-uri escape them
$Param{Text} =~ s{
(href="mailto:[^\?]+\?)([^"]+")
}
{
my $MailToHref = $1;
my $MailToHrefContent = $2;
$MailToHrefContent =~ s{
((?:subject|body)=)(.+?)("|&)
}
{
my $SubjectOrBodyPrefix = $1;
my $SubjectOrBodyContent = $2;
my $SubjectOrBodySuffix = $3;
my $SubjectOrBodyContentUnescaped = URI::Escape::uri_unescape $SubjectOrBodyContent;
my $SubjectOrBodyContentReplaced = $Self->_Replace(
%Param,
Text => $SubjectOrBodyContentUnescaped,
RichText => 0,
);
my $SubjectOrBodyContentEscaped = URI::Escape::uri_escape_utf8 $SubjectOrBodyContentReplaced;
$SubjectOrBodyPrefix . $SubjectOrBodyContentEscaped . $SubjectOrBodySuffix;
}egx;
$MailToHref . $MailToHrefContent;
}egx;
my $Start = '<';
my $End = '>';
if ( $Param{RichText} ) {
$Start = '&lt;';
$End = '&gt;';
$Param{Text} =~ s/(\n|\r)//g;
}
my %Ticket;
if ( $Param{TicketData} ) {
%Ticket = %{ $Param{TicketData} };
}
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
# Determine recipient's timezone if needed.
my $RecipientTimeZone;
if ( $Param{AddTimezoneInfo} ) {
$RecipientTimeZone = $Kernel::OM->Create('Kernel::System::DateTime')->OTRSTimeZoneGet();
my %CustomerUser;
if ( IsHashRefWithData( \%Ticket ) && $Ticket{CustomerUserID} ) {
%CustomerUser = $CustomerUserObject->CustomerUserDataGet( User => $Ticket{CustomerUserID} );
}
my %UserPreferences;
if ( $Param{AddTimezoneInfo}->{NotificationEvent} && $Param{Recipient}->{Type} eq 'Agent' ) {
%UserPreferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
UserID => $Param{Recipient}->{UserID},
);
}
elsif (
$Param{AddTimezoneInfo}->{NotificationEvent}
&& $Param{Recipient}->{Type} eq 'Customer'
&& $Param{Recipient}->{UserID}
)
{
%UserPreferences = $CustomerUserObject->GetPreferences(
UserID => $Param{Recipient}->{UserID},
);
}
elsif (
$Param{AddTimezoneInfo}->{AutoResponse}
&& $Ticket{CustomerUserID}
&& IsHashRefWithData( \%CustomerUser )
)
{
%UserPreferences = $CustomerUserObject->GetPreferences(
UserID => $Ticket{CustomerUserID},
);
}
if ( $UserPreferences{UserTimeZone} ) {
$RecipientTimeZone = $UserPreferences{UserTimeZone};
}
}
# Replace Unix time format tags.
# If language is defined, they will be converted into a correct format in below IF statement.
for my $UnixFormatTime (
qw(RealTillTimeNotUsed EscalationResponseTime EscalationUpdateTime EscalationSolutionTime)
)
{
if ( $Ticket{$UnixFormatTime} ) {
$Ticket{$UnixFormatTime} = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Ticket{$UnixFormatTime},
},
)->ToString();
}
}
# translate ticket values if needed
if ( $Param{Language} ) {
my $LanguageObject = Kernel::Language->new(
UserLanguage => $Param{Language},
);
# Translate the different values.
for my $Field (qw(Type State StateType Lock Priority)) {
$Ticket{$Field} = $LanguageObject->Translate( $Ticket{$Field} );
}
# Transform the date values from the ticket data (but not the dynamic field values).
ATTRIBUTE:
for my $Attribute ( sort keys %Ticket ) {
next ATTRIBUTE if $Attribute =~ m{ \A DynamicField_ }xms;
next ATTRIBUTE if !$Ticket{$Attribute};
if ( $Ticket{$Attribute} =~ m{\A(\d\d\d\d)-(\d\d)-(\d\d)\s(\d\d):(\d\d):(\d\d)\z}xi ) {
# Change time to recipient's timezone if needed
# and later append timezone information.
# For more information,
# see bug#13865 (https://bugs.otrs.org/show_bug.cgi?id=13865)
# and bug#14270 (https://bugs.otrs.org/show_bug.cgi?id=14270).
if ($RecipientTimeZone) {
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Ticket{$Attribute},
},
);
$DateTimeObject->ToTimeZone( TimeZone => $RecipientTimeZone );
$Ticket{$Attribute} = $DateTimeObject->ToString();
}
$Ticket{$Attribute} = $LanguageObject->FormatTimeString(
$Ticket{$Attribute},
'DateFormat',
'NoSeconds',
);
# Append timezone information if needed.
if ($RecipientTimeZone) {
$Ticket{$Attribute} .= " ($RecipientTimeZone)";
}
}
}
my $LocalLayoutObject = Kernel::Output::HTML::Layout->new(
Lang => $Param{Language},
);
# Convert tags in seconds to more readable appropriate format if language is defined.
for my $TimeInSeconds (
qw(UntilTime EscalationTimeWorkingTime EscalationTime FirstResponseTimeWorkingTime FirstResponseTime UpdateTimeWorkingTime
UpdateTime SolutionTimeWorkingTime SolutionTime)
)
{
if ( $Ticket{$TimeInSeconds} ) {
$Ticket{$TimeInSeconds} = $LocalLayoutObject->CustomerAge(
Age => $Ticket{$TimeInSeconds},
Space => ' '
);
}
}
}
my %Queue;
if ( $Param{QueueID} ) {
%Queue = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet(
ID => $Param{QueueID},
);
}
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# Replace config options.
my $Tag = $Start . 'OTRS_CONFIG_';
$Param{Text} =~ s{$Tag(.+?)$End}{
my $Key = $1;
my $Value = $ConfigObject->Get($Key) // '';
# Mask sensitive config options.
my $Replace = $Self->_MaskSensitiveValue(
Key => $Key,
Value => $Value,
IsConfig => 1,
);
$Replace;
}egx;
# cleanup
$Param{Text} =~ s/$Tag.+?$End/-/gi;
my %Recipient = %{ $Param{Recipient} || {} };
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
if ( !%Recipient && $Param{RecipientID} ) {
%Recipient = $UserObject->GetUserData(
UserID => $Param{RecipientID},
NoOutOfOffice => 1,
);
}
my $HashGlobalReplace = sub {
my ( $Tag, %H ) = @_;
# Generate one single matching string for all keys to save performance.
my $Keys = join '|', map {quotemeta} grep { defined $H{$_} } keys %H;
# Set all keys as lowercase to be able to match case insensitive,
# e. g. <OTRS_CUSTOMER_From> and <OTRS_CUSTOMER_FROM>.
# Also mask any values containing sensitive data.
%H = map {
lc $_ => $Self->_MaskSensitiveValue(
Key => $_,
Value => $H{$_},
)
} sort keys %H;
# If tag is 'OTRS_CUSTOMER_' add the body alias 'email/note' to be replaced.
if ( $Tag =~ m/OTRS_(CUSTOMER|AGENT)_/ ) {
KEY:
for my $Key (qw( email note )) {
my $Value = $H{$Key};
next KEY if defined($Value);
$H{$Key} = $H{'body'};
$Keys .= '|' . ucfirst $Key;
}
}
$Param{Text} =~ s/(?:$Tag)($Keys)$End/$H{ lc $1 }/ieg;
};
# get recipient data and replace it with <OTRS_...
$Tag = $Start . 'OTRS_';
# include more readable tag <OTRS_NOTIFICATION_RECIPIENT
my $RecipientTag = $Start . 'OTRS_NOTIFICATION_RECIPIENT_';
if (%Recipient) {
# HTML quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %Recipient ) {
next ATTRIBUTE if !$Recipient{$Attribute};
$Recipient{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Recipient{$Attribute},
);
}
}
$HashGlobalReplace->( "$Tag|$RecipientTag", %Recipient );
}
# cleanup
$Param{Text} =~ s/$RecipientTag.+?$End/-/gi;
# get owner data and replace it with <OTRS_OWNER_...
$Tag = $Start . 'OTRS_OWNER_';
# include more readable version <OTRS_TICKET_OWNER
my $OwnerTag = $Start . 'OTRS_TICKET_OWNER_';
if ( $Ticket{OwnerID} ) {
my %Owner = $UserObject->GetUserData(
UserID => $Ticket{OwnerID},
NoOutOfOffice => 1,
);
# html quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %Owner ) {
next ATTRIBUTE if !$Owner{$Attribute};
$Owner{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Owner{$Attribute},
);
}
}
$HashGlobalReplace->( "$Tag|$OwnerTag", %Owner );
}
# cleanup
$Param{Text} =~ s/$Tag.+?$End/-/gi;
$Param{Text} =~ s/$OwnerTag.+?$End/-/gi;
# get owner data and replace it with <OTRS_RESPONSIBLE_...
$Tag = $Start . 'OTRS_RESPONSIBLE_';
# include more readable version <OTRS_TICKET_RESPONSIBLE
my $ResponsibleTag = $Start . 'OTRS_TICKET_RESPONSIBLE_';
if ( $Ticket{ResponsibleID} ) {
my %Responsible = $UserObject->GetUserData(
UserID => $Ticket{ResponsibleID},
NoOutOfOffice => 1,
);
# HTML quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %Responsible ) {
next ATTRIBUTE if !$Responsible{$Attribute};
$Responsible{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Responsible{$Attribute},
);
}
}
$HashGlobalReplace->( "$Tag|$ResponsibleTag", %Responsible );
}
# cleanup
$Param{Text} =~ s/$Tag.+?$End/-/gi;
$Param{Text} =~ s/$ResponsibleTag.+?$End/-/gi;
$Tag = $Start . 'OTRS_Agent_';
my $Tag2 = $Start . 'OTRS_CURRENT_';
my %CurrentUser = $UserObject->GetUserData(
UserID => $Param{UserID},
NoOutOfOffice => 1,
);
# HTML quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %CurrentUser ) {
next ATTRIBUTE if !$CurrentUser{$Attribute};
$CurrentUser{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $CurrentUser{$Attribute},
);
}
}
$HashGlobalReplace->( "$Tag|$Tag2", %CurrentUser );
# replace other needed stuff
$Param{Text} =~ s/$Start OTRS_FIRST_NAME $End/$CurrentUser{UserFirstname}/gxms;
$Param{Text} =~ s/$Start OTRS_LAST_NAME $End/$CurrentUser{UserLastname}/gxms;
# cleanup
$Param{Text} =~ s/$Tag2.+?$End/-/gi;
# ticket data
$Tag = $Start . 'OTRS_TICKET_';
# html quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %Ticket ) {
next ATTRIBUTE if !$Ticket{$Attribute};
$Ticket{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Ticket{$Attribute},
);
}
}
# Dropdown, Checkbox and MultipleSelect DynamicFields, can store values (keys) that are
# different from the the values to display
# <OTRS_TICKET_DynamicField_NameX> returns the stored key
# <OTRS_TICKET_DynamicField_NameX_Value> returns the display value
my %DynamicFields;
# For systems with many Dynamic fields we do not want to load them all unless needed
# Find what Dynamic Field Values are requested
while ( $Param{Text} =~ m/$Tag DynamicField_(\S+?)(_Value)? $End/gixms ) {
$DynamicFields{$1} = 1;
}
# to store all the required DynamicField display values
my %DynamicFieldDisplayValues;
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get the dynamic fields for ticket object
my $DynamicFieldList = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
) || [];
# cycle through the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicFieldList} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# we only load the ones requested
next DYNAMICFIELD if !$DynamicFields{ $DynamicFieldConfig->{Name} };
my $LanguageObject;
# translate values if needed
if ( $Param{Language} ) {
$LanguageObject = Kernel::Language->new(
UserLanguage => $Param{Language},
);
}
my $DateTimeObject;
# Change DateTime DF value for ticket if needed.
if (
defined $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
&& $DynamicFieldConfig->{FieldType} eq 'DateTime'
&& $RecipientTimeZone
)
{
$DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
},
);
$DateTimeObject->ToTimeZone( TimeZone => $RecipientTimeZone );
$Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $DateTimeObject->ToString();
}
# get the display value for each dynamic field
my $DisplayValue = $DynamicFieldBackendObject->ValueLookup(
DynamicFieldConfig => $DynamicFieldConfig,
Key => $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
LanguageObject => $LanguageObject,
);
# get the readable value (value) for each dynamic field
my $DisplayValueStrg = $DynamicFieldBackendObject->ReadableValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $DisplayValue,
);
# fill the DynamicFielsDisplayValues
if ($DisplayValueStrg) {
$DynamicFieldDisplayValues{ 'DynamicField_' . $DynamicFieldConfig->{Name} . '_Value' }
= $DisplayValueStrg->{Value};
# Add timezone info if needed.
if (
defined $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
&& length $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
&& $DynamicFieldConfig->{FieldType} eq 'DateTime'
&& $RecipientTimeZone
)
{
$DynamicFieldDisplayValues{ 'DynamicField_' . $DynamicFieldConfig->{Name} . '_Value' }
.= " ($RecipientTimeZone)";
}
}
# get the readable value (key) for each dynamic field
my $ValueStrg = $DynamicFieldBackendObject->ReadableValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} },
);
# replace ticket content with the value from ReadableValueRender (if any)
if ( IsHashRefWithData($ValueStrg) ) {
$Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $ValueStrg->{Value};
# Add timezone info if needed.
if (
defined $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
&& length $Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} }
&& $DynamicFieldConfig->{FieldType} eq 'DateTime'
&& $RecipientTimeZone
)
{
$Ticket{ 'DynamicField_' . $DynamicFieldConfig->{Name} } .= " ($RecipientTimeZone)";
}
}
}
# replace it
$HashGlobalReplace->( $Tag, %Ticket, %DynamicFieldDisplayValues );
# COMPAT
$Param{Text} =~ s/$Start OTRS_TICKET_ID $End/$Ticket{TicketID}/gixms;
$Param{Text} =~ s/$Start OTRS_TICKET_NUMBER $End/$Ticket{TicketNumber}/gixms;
if ( $Ticket{TicketID} ) {
$Param{Text} =~ s/$Start OTRS_QUEUE $End/$Ticket{Queue}/gixms;
}
if ( $Param{QueueID} ) {
$Param{Text} =~ s/$Start OTRS_TICKET_QUEUE $End/$Queue{Name}/gixms;
}
# cleanup
$Param{Text} =~ s/$Tag.+?$End/-/gi;
# get customer and agent params and replace it with <OTRS_CUSTOMER_... or <OTRS_AGENT_...
my %ArticleData = (
'OTRS_CUSTOMER_' => $Param{Data} || {},
'OTRS_AGENT_' => $Param{DataAgent} || {},
);
# use a list to get customer first
for my $DataType (qw(OTRS_CUSTOMER_ OTRS_AGENT_)) {
my %Data = %{ $ArticleData{$DataType} };
# HTML quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %Data ) {
next ATTRIBUTE if !$Data{$Attribute};
$Data{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $Data{$Attribute},
);
}
}
if (%Data) {
# replace <OTRS_CUSTOMER_*> and <OTRS_AGENT_*> tags
$Tag = $Start . $DataType;
$HashGlobalReplace->( $Tag, %Data );
# prepare body (insert old email) <OTRS_CUSTOMER_EMAIL[n]>, <OTRS_CUSTOMER_NOTE[n]>
# <OTRS_CUSTOMER_BODY[n]>, <OTRS_AGENT_EMAIL[n]>..., <OTRS_COMMENT>
# Changed this to a 'while' to allow the same key/tag multiple times and different number of lines.
while (
$Param{Text} =~ /$Start(?:$DataType(EMAIL|NOTE|BODY)\[(.+?)\])$End/
||
$Param{Text} =~ /$Start(?:OTRS_COMMENT(\[(.+?)\])?)$End/
)
{
my $Line = $2 || 2500;
my $NewOldBody = '';
my @Body = split( /\n/, $Data{Body} );
for my $Counter ( 0 .. $Line - 1 ) {
# 2002-06-14 patch of Pablo Ruiz Garcia
# http://lists.otrs.org/pipermail/dev/2002-June/000012.html
if ( $#Body >= $Counter ) {
# add no quote char, do it later by using DocumentCleanup()
if ( $Param{RichText} ) {
$NewOldBody .= $Body[$Counter];
}
# add "> " as quote char
else {
$NewOldBody .= "> $Body[$Counter]";
}
# add new line
if ( $Counter < ( $Line - 1 ) ) {
$NewOldBody .= "\n";
}
}
$Counter++;
}
chomp $NewOldBody;
# HTML quoting of content
if ( $Param{RichText} && $NewOldBody ) {
# remove trailing new lines
for ( 1 .. 10 ) {
$NewOldBody =~ s/(<br\/>)\s{0,20}$//gs;
}
# add quote
$NewOldBody = "<blockquote type=\"cite\">$NewOldBody</blockquote>";
$NewOldBody = $Kernel::OM->Get('Kernel::System::HTMLUtils')->DocumentCleanup(
String => $NewOldBody,
);
}
# replace tag
$Param{Text}
=~ s/$Start(?:(?:$DataType(EMAIL|NOTE|BODY)\[(.+?)\]|(?:OTRS_COMMENT(\[(.+?)\])?)))$End/$NewOldBody/;
}
# replace <OTRS_CUSTOMER_SUBJECT[]> and <OTRS_AGENT_SUBJECT[]> tags
$Tag = "$Start$DataType" . 'SUBJECT';
if ( $Param{Text} =~ /$Tag\[(.+?)\]$End/g ) {
my $SubjectChar = $1;
my $Subject = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSubjectClean(
TicketNumber => $Ticket{TicketNumber},
Subject => $Data{Subject},
);
$Subject =~ s/^(.{$SubjectChar}).*$/$1 [...]/;
$Param{Text} =~ s/$Tag\[.+?\]$End/$Subject/g;
}
if ( $DataType eq 'OTRS_CUSTOMER_' ) {
# Arnold Ligtvoet - otrs@ligtvoet.org
# get <OTRS_EMAIL_DATE[]> from body and replace with received date
use POSIX qw(strftime);
$Tag = $Start . 'OTRS_EMAIL_DATE';
if ( $Param{Text} =~ /$Tag\[(.+?)\]$End/g ) {
my $TimeZone = $1;
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
my $EmailDate = $DateTimeObject->Format( Format => '%A, %B %e, %Y at %T ' );
$EmailDate .= "($TimeZone)";
$Param{Text} =~ s/$Tag\[.+?\]$End/$EmailDate/g;
}
}
}
if ( $DataType eq 'OTRS_CUSTOMER_' ) {
# get and prepare realname
$Tag = $Start . 'OTRS_CUSTOMER_REALNAME';
if ( $Param{Text} =~ /$Tag$End/i ) {
my $From;
if ( $Ticket{CustomerUserID} ) {
$From = $CustomerUserObject->CustomerName(
UserLogin => $Ticket{CustomerUserID}
);
}
# try to get the real name directly from the data
$From //= $Recipient{Realname};
# get real name based on reply-to
if ( !$From && $Data{ReplyTo} ) {
$From = $Data{ReplyTo};
# remove email addresses
$From =~ s/&lt;.*&gt;|<.*>|\(.*\)|\"|&quot;|;|,//g;
# remove leading/trailing spaces
$From =~ s/^\s+//g;
$From =~ s/\s+$//g;
}
# generate real name based on sender line
if ( !$From ) {
$From = $Data{To} || '';
# remove email addresses
$From =~ s/&lt;.*&gt;|<.*>|\(.*\)|\"|&quot;|;|,//g;
# remove leading/trailing spaces
$From =~ s/^\s+//g;
$From =~ s/\s+$//g;
}
# replace <OTRS_CUSTOMER_REALNAME> with from
$Param{Text} =~ s/$Tag$End/$From/g;
}
}
}
# get customer data and replace it with <OTRS_CUSTOMER_DATA_...
$Tag = $Start . 'OTRS_CUSTOMER_';
$Tag2 = $Start . 'OTRS_CUSTOMER_DATA_';
if ( $Ticket{CustomerUserID} || $Param{Data}->{CustomerUserID} ) {
my $CustomerUserID = $Param{Data}->{CustomerUserID} || $Ticket{CustomerUserID};
my %CustomerUser = $CustomerUserObject->CustomerUserDataGet(
User => $CustomerUserID,
);
# HTML quoting of content
if ( $Param{RichText} ) {
ATTRIBUTE:
for my $Attribute ( sort keys %CustomerUser ) {
next ATTRIBUTE if !$CustomerUser{$Attribute};
$CustomerUser{$Attribute} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToHTML(
String => $CustomerUser{$Attribute},
);
}
}
# replace it
$HashGlobalReplace->( "$Tag|$Tag2", %CustomerUser );
}
# cleanup all not needed <OTRS_CUSTOMER_DATA_ tags
$Param{Text} =~ s/(?:$Tag|$Tag2).+?$End/-/gi;
# cleanup all not needed <OTRS_AGENT_ tags
$Tag = $Start . 'OTRS_AGENT_';
$Param{Text} =~ s/$Tag.+?$End/-/gi;
return $Param{Text};
}
=head2 _RemoveUnSupportedTag()
cleanup all not supported tags
my $Text = $TemplateGeneratorObject->_RemoveUnSupportedTag(
Text => $SomeTextWithTags,
ListOfUnSupportedTag => \@ListOfUnSupportedTag,
);
=cut
sub _RemoveUnSupportedTag {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Item (qw(Text ListOfUnSupportedTag)) {
if ( !defined $Param{$Item} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Item!"
);
return;
}
}
my $Start = '<';
my $End = '>';
if ( $Self->{RichText} ) {
$Start = '&lt;';
$End = '&gt;';
$Param{Text} =~ s/(\n|\r)//g;
}
# Cleanup all not supported tags with and without number, e.g. OTRS_CUSTOMER_BODY and OTRS_CUSTOMER_BODY[n].
# See https://bugs.otrs.org/show_bug.cgi?id=14369 and https://bugs.otrs.org/show_bug.cgi?id=10825.
my $NotSupportedTag = $Start . "(?:" . join( "|", @{ $Param{ListOfUnSupportedTag} } ) . ")(\\[.*?\\])?" . $End;
$Param{Text} =~ s/$NotSupportedTag/-/gi;
return $Param{Text};
}
=head2 _MaskSensitiveValue()
Mask sensitive value, i.e. a password, a security token, etc.
my $MaskedValue = $Self->_MaskSensitiveValue(
Key => 'DatabasePassword', # (required) Name of the field/key.
Value => 'secretvalue', # (optional) Value to potentially mask.
IsConfig => 1, # (optional) Whether the value is a config option, default: 0.
);
Returns masked value, in case the key is matched:
$MaskedValue = 'xxx';
=cut
sub _MaskSensitiveValue {
my ( $Self, %Param ) = @_;
return '' if !$Param{Key} || !defined $Param{Value};
# Skip masking sensitive values for Dynamic Fields.
return $Param{Value} if $Param{Key} =~ qr{ dynamicfield }xi;
# Match general key names, i.e. from the user preferences.
my $Match = qr{ config|secret|passw|userpw|auth|token }xi;
# Match forbidden config keys.
if ( $Param{IsConfig} ) {
$Match = qr{ (?:password|pw) \d* $ }smxi;
}
return $Param{Value} if $Param{Key} !~ $Match;
return 'xxx';
}
1;
=end Internal:
=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