formatter = $formatter; $this->entityManager = $entityManager; $this->aclManager = $aclManager; $this->recordServiceContainer = $recordServiceContainer; $this->config = $config; $this->fileStorageManager = $fileStorageManager; $this->user = $user; $this->htmlizerFactory = $htmlizerFactory; $this->dateTime = $dateTime; } public function process(EmailTemplate $template, Params $params, Data $data): Result { $entityHash = $data->getEntityHash(); $user = $data->getUser() ?? $this->user; if (!isset($entityHash[User::ENTITY_TYPE])) { $entityHash[User::ENTITY_TYPE] = $user; } $foundByAddressEntity = null; if ($data->getEmailAddress()) { $foundByAddressEntity = $this->getEmailAddressRepository() ->getEntityByAddress( $data->getEmailAddress(), null, [ Contact::ENTITY_TYPE, Lead::ENTITY_TYPE, Account::ENTITY_TYPE, User::ENTITY_TYPE, ] ); } if ($foundByAddressEntity) { if ($foundByAddressEntity instanceof Person) { $entityHash[PersonTemplate::TEMPLATE_TYPE] = $foundByAddressEntity; } if (!isset($entityHash[$foundByAddressEntity->getEntityType()])) { $entityHash[$foundByAddressEntity->getEntityType()] = $foundByAddressEntity; } } if ( !$data->getParent() && $data->getParentId() && $data->getParentType() ) { $parent = $this->entityManager->getEntity($data->getParentType(), $data->getParentId()); if ($parent) { $service = $this->recordServiceContainer->get($data->getParentType()); $service->loadAdditionalFields($parent); if ( $params->applyAcl() && !$this->aclManager->checkEntityRead($this->user, $parent) ) { $parent = null; } $data = $data->withParent($parent); } } if ($data->getParent()) { $parent = $data->getParent(); $entityHash[$parent->getEntityType()] = $parent; $entityHash[self::KEY_PARENT] = $parent; if ( !isset($entityHash[PersonTemplate::TEMPLATE_TYPE]) && $parent instanceof Person ) { $entityHash[PersonTemplate::TEMPLATE_TYPE] = $parent; } } if ($data->getRelatedId() && $data->getRelatedType()) { $related = $this->entityManager->getEntity($data->getRelatedType(), $data->getRelatedId()); if ( $related && $params->applyAcl() && !$this->aclManager->checkEntityRead($this->user, $related) ) { $related = null; } if ($related) { $entityHash[$related->getEntityType()] = $related; } } $subject = $template->getSubject() ?? ''; $body = $template->getBody() ?? ''; $parent = $entityHash[self::KEY_PARENT] ?? 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->createHtmlizer($params, $user); if ($handlebarsInSubject) { $subject = $htmlizer->render($parent, $subject); } if ($handlebarsInBody) { $body = $htmlizer->render($parent, $body, null, null, false, true); } } } foreach ($entityHash as $type => $entity) { $subject = $this->processText( $type, $entity, $subject, $user, false, null, !$params->applyAcl() ); } foreach ($entityHash as $type => $entity) { $body = $this->processText( $type, $entity, $body, $user, false, null, !$params->applyAcl() ); } $attachmentList = $params->copyAttachments() ? $this->copyAttachments($template) : []; return new Result( $subject, $body, $template->isHtml(), $attachmentList ); } private function processText( string $type, Entity $entity, string $text, User $user, bool $skipLinks = false, ?string $prefixLink = null, bool $skipAcl = false ): string { $attributeList = $entity->getAttributeList(); $forbiddenAttributeList = []; if (!$skipAcl) { $forbiddenAttributeList = array_merge( $this->aclManager->getScopeForbiddenAttributeList($user, $entity->getEntityType()), $this->aclManager->getScopeRestrictedAttributeList( $entity->getEntityType(), [ GlobalRestriction::TYPE_FORBIDDEN, GlobalRestriction::TYPE_INTERNAL, GlobalRestriction::TYPE_ONLY_ADMIN, ] ) ); } foreach ($attributeList as $attribute) { if (in_array($attribute, $forbiddenAttributeList)) { continue; } if (is_object($entity->get($attribute))) { continue; } if (!$entity->getAttributeType($attribute)) { continue; } $value = $this->formatter->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->hasId()) { $text = $this->processLinks( $type, $entity, $text, $user, $skipAcl ); } $now = new DateTime('now', new DateTimezone($this->config->get('timeZone'))); $replaceData = [ 'today' => $this->dateTime->getTodayString(), 'now' => $this->dateTime->getNowString(), 'currentYear' => $now->format('Y'), ]; foreach ($replaceData as $key => $value) { $text = str_replace('{' . $key . '}', $value, $text); } return $text; } private function processLinks( string $type, Entity $entity, string $text, User $user, bool $skipAcl ): string { $forbiddenLinkList = $skipAcl ? $this->aclManager->getScopeRestrictedLinkList( $entity->getEntityType(), [ GlobalRestriction::TYPE_FORBIDDEN, GlobalRestriction::TYPE_INTERNAL, GlobalRestriction::TYPE_ONLY_ADMIN, ] ) : []; foreach ($entity->getRelationList() as $relation) { if (in_array($relation, $forbiddenLinkList)) { continue; } $relationType = $entity->getRelationType($relation); $relationTypeIsOk = $relationType === Entity::BELONGS_TO || $relationType === Entity::BELONGS_TO_PARENT; if (!$relationTypeIsOk) { continue; } $relatedEntity = $this->entityManager ->getRDBRepository($entity->getEntityType()) ->getRelation($entity, $relation) ->findOne(); if (!$relatedEntity) { continue; } try { $hasAccess = $this->aclManager->checkEntityRead($user, $relatedEntity); } catch (Exception $e) { continue; } if (!$hasAccess) { continue; } $text = $this->processText( $type, $relatedEntity, $text, $user, true, $relation, $skipAcl ); } return $text; } /** * @return Attachment[] */ private function copyAttachments(EmailTemplate $template): array { $copiedAttachmentList = []; /** @var iterable $attachmentList */ $attachmentList = $this->entityManager ->getRDBRepositoryByClass(EmailTemplate::class) ->getRelation($template, 'attachments') ->find(); foreach ($attachmentList as $attachment) { /** @var Attachment $clone */ $clone = $this->entityManager->getNewEntity(Attachment::ENTITY_TYPE); $data = $attachment->getValueMap(); unset($data->parentType); unset($data->parentId); unset($data->id); $clone->set($data); $clone->set('sourceId', $attachment->getSourceId()); $clone->set('storage', $attachment->getStorage()); if (!$this->fileStorageManager->exists($attachment)) { continue; } $this->entityManager->saveEntity($clone); $copiedAttachmentList[] = $clone; } return $copiedAttachmentList; } private function createHtmlizer(Params $params, User $user): Htmlizer { if (!$params->applyAcl()) { return $this->htmlizerFactory->createNoAcl(); } return $this->htmlizerFactory->createForUser($user); } private function getEmailAddressRepository(): EmailAddressRepository { /** @var EmailAddressRepository */ return $this->entityManager->getRepository(EmailAddress::ENTITY_TYPE); } }