This repository has been archived on 2026-07-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
espocrm-base/application/Espo/Services/EmailTemplate.php
T
HungNguyenPTN 03cf443b14 Fix issue #2009 (#2035)
* ISSUE #2009:
-Fix loadAdditionalFields does not work for email templates
-Similar to PDF template

* Update EmailTemplate.php
2021-07-09 09:08:04 +03:00

538 lines
16 KiB
PHP

<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Services;
use Espo\ORM\Entity;
use Espo\Core\{
Entities\Person,
Acl\GlobalRestricton,
};
use Espo\Core\Exceptions\NotFound;
use Espo\Core\Di;
use DateTime;
use DateTimezone;
use StdClass;
class EmailTemplate extends Record implements
Di\FileStorageManagerAware,
Di\DateTimeAware,
Di\LanguageAware,
Di\NumberAware,
Di\HtmlizerFactoryAware,
Di\FieldUtilAware
{
use Di\FileStorageManagerSetter;
use Di\DateTimeSetter;
use Di\LanguageSetter;
use Di\NumberSetter;
use Di\HtmlizerFactorySetter;
use Di\FieldUtilSetter;
public function parseTemplate(
Entity $emailTemplate,
array $params = [],
$copyAttachments = false,
$skipAcl = false
): array {
$entityHash = [];
if (!empty($params['entityHash']) && is_array($params['entityHash'])) {
$entityHash = $params['entityHash'];
}
if (!isset($entityHash['User'])) {
$entityHash['User'] = $this->getUser();
}
if (!empty($params['emailAddress'])) {
$emailAddress = $this->getEntityManager()
->getRepository('EmailAddress')
->where([
'lower' => $params['emailAddress'],
])
->findOne();
$entity = $this->getEntityManager()
->getRepository('EmailAddress')
->getEntityByAddress($params['emailAddress'],
null, ['Contact', 'Lead', 'Account', 'User']
);
if ($entity) {
if ($entity instanceof Person) {
$entityHash['Person'] = $entity;
}
if (empty($entityHash[$entity->getEntityType()])) {
$entityHash[$entity->getEntityType()] = $entity;
}
}
}
if (empty($params['parent'])) {
if (!empty($params['parentId']) && !empty($params['parentType'])) {
$parent = $this->getEntityManager()->getEntity($params['parentType'], $params['parentId']);
if ($parent) {
$services = $this->recordServiceContainer->get($params['parentType']);
$services->loadAdditionalFields($parent);
$params['parent'] = $parent;
}
}
}
if (!empty($params['parent'])) {
$parent = $params['parent'];
$entityHash[$parent->getEntityType()] = $parent;
$entityHash['Parent'] = $parent;
if (empty($entityHash['Person']) && ($parent instanceof Person)) {
$entityHash['Person'] = $parent;
}
}
if (!empty($params['relatedId']) && !empty($params['relatedType'])) {
$related = $this->getEntityManager()->getEntity($params['relatedType'], $params['relatedId']);
if ($related) {
$entityHash[$related->getEntityType()] = $related;
}
}
$subject = $emailTemplate->get('subject') ?? '';
$body = $emailTemplate->get('body') ?? '';
$parent = $entityHash['Parent'] ?? null;
$htmlizer = null;
if ($parent && !$this->config->get('emailTemplateHtmlizerDisabled')) {
$handlebarsInSubject = strpos($subject, '{{') !== false && strpos($subject, '}}') !== false;
$handlebarsInBody = strpos($body, '{{') !== false && strpos($body, '}}') !== false;
if ($handlebarsInSubject || $handlebarsInBody) {
$htmlizer = $this->htmlizerFactory->create($skipAcl);
if ($handlebarsInSubject) {
$subject = $htmlizer->render($parent, $subject);
}
if ($handlebarsInBody) {
$body = $htmlizer->render($parent, $body);
}
}
}
foreach ($entityHash as $type => $entity) {
$subject = $this->parseText($type, $entity, $subject, false, null, $skipAcl);
}
foreach ($entityHash as $type => $entity) {
$body = $this->parseText($type, $entity, $body, false, null, $skipAcl);
}
$attachmentsIds = [];
$attachmentsNames = (object) [];
if ($copyAttachments) {
$attachmentList = $this->getEntityManager()
->getRepository('EmailTemplate')
->getRelation($emailTemplate, 'attachments')
->find();
if (!empty($attachmentList)) {
foreach ($attachmentList as $attachment) {
$clone = $this->getEntityManager()->getEntity('Attachment');
$data = $attachment->toArray();
unset($data['parentType']);
unset($data['parentId']);
unset($data['id']);
$clone->set($data);
$clone->set('sourceId', $attachment->getSourceId());
$clone->set('storage', $attachment->get('storage'));
if (!$this->fileStorageManager->exists($attachment)) {
continue;
}
$this->getEntityManager()->saveEntity($clone);
$attachmentsIds[] = $id = $clone->id;
$attachmentsNames->$id = $clone->get('name');
}
}
}
return [
'subject' => $subject,
'body' => $body,
'attachmentsIds' => $attachmentsIds,
'attachmentsNames' => $attachmentsNames,
'isHtml' => $emailTemplate->get('isHtml'),
];
}
public function parse(string $id, array $params = [], bool $copyAttachments = false): array
{
$emailTemplate = $this->getEntity($id);
if (empty($emailTemplate)) {
throw new NotFound();
}
return $this->parseTemplate($emailTemplate, $params, $copyAttachments);
}
protected function parseText(
string $type,
Entity $entity,
string $text,
bool $skipLinks = false,
?string $prefixLink = null,
bool $skipAcl = false
): string {
$attributeList = $entity->getAttributeList();
if ($skipAcl) {
$forbiddenAttributeList = [];
$forbiddenLinkList = [];
}
else {
$forbiddenAttributeList = $this->acl
->getScopeForbiddenAttributeList($entity->getEntityType(), 'read');
$forbiddenAttributeList = array_merge(
$forbiddenAttributeList,
$this->acl
->getScopeRestrictedAttributeList(
$entity->getEntityType(),
[
GlobalRestricton::TYPE_FORBIDDEN,
GlobalRestricton::TYPE_INTERNAL,
GlobalRestricton::TYPE_ONLY_ADMIN,
]
)
);
$forbiddenLinkList = $this->acl->getScopeRestrictedLinkList(
$entity->getEntityType(),
[
GlobalRestricton::TYPE_FORBIDDEN,
GlobalRestricton::TYPE_INTERNAL,
GlobalRestricton::TYPE_ONLY_ADMIN,
]
);
}
foreach ($attributeList as $attribute) {
if (in_array($attribute, $forbiddenAttributeList)) {
continue;
}
$value = $entity->get($attribute);
if (is_object($value)) {
continue;
}
if (!$entity->getAttributeType($attribute)) {
continue;
}
$value = $this->formatAttributeValue($entity, $attribute);
if (is_null($value)) {
continue;
}
$variableName = $attribute;
if (!is_null($prefixLink)) {
$variableName = $prefixLink . '.' . $attribute;
}
$text = str_replace('{' . $type . '.' . $variableName . '}', $value, $text);
}
if (!$skipLinks && $entity->id) {
foreach ($entity->getRelationList() as $relation) {
if (in_array($relation, $forbiddenLinkList)) {
continue;
}
$relationType = $entity->getRelationType($relation);
if (
$relationType === 'belongsTo' ||
$relationType === 'belongsToParent'
) {
$relatedEntity = $this->getEntityManager()
->getRepository($entity->getEntityType())
->getRelation($entity, $relation)
->findOne();
if (!$relatedEntity) {
continue;
}
if ($this->acl) {
if (!$this->acl->check($relatedEntity, 'read')) {
continue;
}
}
$text = $this->parseText($type, $relatedEntity, $text, true, $relation, $skipAcl);
}
}
}
$replaceData = [];
$replaceData['today'] = $this->dateTime->getTodayString();
$replaceData['now'] = $this->dateTime->getNowString();
$timeZone = $this->config->get('timeZone');
$now = new DateTime('now', new DateTimezone($timeZone));
$replaceData['currentYear'] = $now->format('Y');
foreach ($replaceData as $key => $value) {
$text = str_replace('{' . $key . '}', $value, $text);
}
return $text;
}
public function formatAttributeValue(Entity $entity, string $attribute): ?string
{
$value = $entity->get($attribute);
$fieldType = $this->getMetadata()
->get(['entityDefs', $entity->getEntityType(), 'fields', $attribute, 'type']);
$attributeType = $entity->getAttributeType($attribute);
if ($fieldType === 'enum') {
if ($value === null) {
return '';
}
return (string) $this->language->translateOption($value, $attribute, $entity->getEntityType());
}
if ($fieldType === 'array' || $fieldType === 'multiEnum' || $fieldType === 'checklist') {
$valueList = [];
if (!is_array($value)) {
return '';
}
foreach ($value as $v) {
$valueList[] = $this->language->translateOption($v, $attribute, $entity->getEntityType());
}
return implode(', ', $valueList);
}
if ($attributeType === 'date') {
if (!$value) {
return '';
}
return $this->dateTime->convertSystemDate($value);
}
if ($attributeType === 'datetime') {
if (!$value) {
return '';
}
return $this->dateTime->convertSystemDateTime($value);
}
if ($attributeType === 'text') {
if (!is_string($value)) {
return '';
}
return nl2br($value);
}
if ($attributeType === 'float') {
if (!is_float($value)) {
return '';
}
$decimalPlaces = 2;
if ($fieldType === 'currency') {
$decimalPlaces = $this->config->get('currencyDecimalPlaces');
}
return $this->number->format($value, $decimalPlaces);
}
if ($attributeType === 'int') {
if (!is_int($value)) {
return '';
}
if (
$fieldType === 'autoincrement' ||
$fieldType === 'int' &&
$this->metadata
->get(['entityDefs', $entity->getEntityType(), 'fields', $attribute, 'disableFormatting'])
) {
return (string) $value;
}
return $this->number->format($value);
}
if (!is_string($value) && is_scalar($value) || is_callable([$value, '__toString'])) {
return strval($value);
}
if ($value === null) {
return '';
}
if (!is_string($value)) {
return null;
}
return $value;
}
public function getInsertFieldData(array $params): StdClass
{
$to = $params['to'] ?? null;
$parentId = $params['parentId'] ?? null;
$parentType = $params['parentType'] ?? null;
$result = (object) [];
$emailTemplateService = $this->getServiceFactory()->create('EmailTemplate');
$dataList = [];
if ($parentId && $parentType) {
$e = $this->getEntityManager()->getEntity($parentType, $parentId);
if ($e && $this->acl->check($e)) {
$dataList[] = [
'type' => 'parent',
'entity' => $e,
];
}
}
if ($to) {
$e = $this->getEntityManager()
->getRepository('EmailAddress')
->getEntityByAddress($to, null, ['Contact', 'Lead', 'Account']);
if ($e && $e->getEntityType() !== 'User' && $this->acl->check($e)) {
$dataList[] = [
'type' => 'to',
'entity' => $e,
];
}
}
$fm = $this->fieldUtil;
foreach ($dataList as $item) {
$type = $item['type'];
$e = $item['entity'];
$entityType = $e->getEntityType();
$recordService = $this->getServiceFactory()->create($entityType);
$recordService->prepareEntityForOutput($e);
$ignoreTypeList = ['image', 'file', 'map', 'wysiwyg', 'linkMultiple', 'attachmentMultiple', 'bool'];
foreach ($fm->getEntityTypeFieldList($entityType) as $field) {
$fieldType = $fm->getEntityTypeFieldParam($entityType, $field, 'type');
$fieldAttributeList = $fm->getAttributeList($entityType, $field);
if (
$fm->getEntityTypeFieldParam($entityType, $field, 'disabled') ||
$fm->getEntityTypeFieldParam($entityType, $field, 'directAccessDisabled') ||
$fm->getEntityTypeFieldParam($entityType, $field, 'templatePlaceholderDisabled') ||
in_array($fieldType, $ignoreTypeList)
) {
foreach ($fieldAttributeList as $a) {
$e->clear($a);
}
}
}
$attributeList = $fm->getEntityTypeAttributeList($entityType);
$values = (object) [];
foreach ($attributeList as $a) {
if (!$e->has($a)) {
continue;
}
$value = $emailTemplateService->formatAttributeValue($e, $a);
if ($value != '') {
$values->$a = $value;
}
}
$result->$type = (object) [
'entityType' => $e->getEntityType(),
'id' => $e->id,
'values' => $values,
'name' => $e->get('name'),
];
}
return $result;
}
}