diff --git a/application/Espo/Core/Htmlizer/Htmlizer.php b/application/Espo/Core/Htmlizer/Htmlizer.php
index 8b803d6936..befc142142 100644
--- a/application/Espo/Core/Htmlizer/Htmlizer.php
+++ b/application/Espo/Core/Htmlizer/Htmlizer.php
@@ -105,6 +105,86 @@ class Htmlizer
$this->injectableFactory = $injectableFactory;
}
+/**
+ * Generate an HTML for entity by a given template.
+ *
+ * @param $cacheId @deprecated To be skipped..
+ * @param $additionalData Data will be passed to the template.
+ * @param $skipLinks Do not process related records.
+ */
+ public function render(
+ ?Entity $entity,
+ string $template,
+ ?string $cacheId = null,
+ ?array $additionalData = null,
+ bool $skipLinks = false
+ ): string {
+
+ $template = str_replace('getHelpers();
+
+ $code = LightnCandy::compile($template, [
+ 'flags' => LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_ERROR_EXCEPTION,
+ 'helpers' => $this->getHelpers(),
+ ]);
+
+ $renderer = LightnCandy::prepare($code);
+
+ if ($additionalData === null) {
+ $additionalData = $additionalData ?? [];
+ }
+
+ $data = $entity ?
+ $this->getDataFromEntity($entity, $skipLinks, 0, $template, $additionalData) :
+ $additionalData;
+
+ if (!array_key_exists('today', $data)) {
+ $data['today'] = $this->dateTime->getTodayString();
+ $data['today_RAW'] = date('Y-m-d');
+ }
+
+ if (!array_key_exists('now', $data)) {
+ $data['now'] = $this->dateTime->getNowString();
+ $data['now_RAW'] = date('Y-m-d H:i:s');
+ }
+
+ $data['__injectableFactory'] = $this->injectableFactory;
+ $data['__config'] = $this->config;
+ $data['__dateTime'] = $this->dateTime;
+ $data['__metadata'] = $this->metadata;
+ $data['__entityManager'] = $this->entityManager;
+ $data['__language'] = $this->language;
+ $data['__serviceFactory'] = $this->serviceFactory;
+ $data['__log'] = $this->log;
+ $data['__entityType'] = $entity->getEntityType();
+
+ $html = $renderer($data);
+
+ $html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html);
+
+ if ($this->entityManager) {
+ $html = preg_replace_callback(
+ '/\?entryPoint=attachment\&id=([A-Za-z0-9]*)/',
+ function ($matches) {
+ $id = $matches[1];
+ $attachment = $this->entityManager->getEntity('Attachment', $id);
+
+ if ($attachment) {
+ $filePath = $this->entityManager
+ ->getRepository('Attachment')
+ ->getFilePath($attachment);
+
+ return $filePath;
+ }
+ },
+ $html
+ );
+ }
+
+ return $html;
+ }
+
protected function format($value)
{
if (is_float($value)) {
@@ -633,83 +713,6 @@ class Htmlizer
return $helpers;
}
- /**
- * Generate an HTML for entity by a given template.
- *
- * @param $cacheId @deprecated Skip or specify NULL.
- * @param $additionalData Data will be passed to the template.
- * @param $skipLinks Do not process related records.
- */
- public function render(
- Entity $entity,
- string $template,
- ?string $cacheId = null,
- ?array $additionalData = null,
- bool $skipLinks = false
- ): string {
-
- $template = str_replace('getHelpers();
-
- $code = LightnCandy::compile($template, [
- 'flags' => LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_ERROR_EXCEPTION,
- 'helpers' => $this->getHelpers(),
- ]);
-
- $renderer = LightnCandy::prepare($code);
-
-
- $additionalData = $additionalData ?? [];
-
- $data = $this->getDataFromEntity($entity, $skipLinks, 0, $template, $additionalData);
-
- if (!array_key_exists('today', $data)) {
- $data['today'] = $this->dateTime->getTodayString();
- $data['today_RAW'] = date('Y-m-d');
- }
-
- if (!array_key_exists('now', $data)) {
- $data['now'] = $this->dateTime->getNowString();
- $data['now_RAW'] = date('Y-m-d H:i:s');
- }
-
- $data['__injectableFactory'] = $this->injectableFactory;
- $data['__config'] = $this->config;
- $data['__dateTime'] = $this->dateTime;
- $data['__metadata'] = $this->metadata;
- $data['__entityManager'] = $this->entityManager;
- $data['__language'] = $this->language;
- $data['__serviceFactory'] = $this->serviceFactory;
- $data['__log'] = $this->log;
- $data['__entityType'] = $entity->getEntityType();
-
- $html = $renderer($data);
-
- $html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html);
-
- if ($this->entityManager) {
- $html = preg_replace_callback(
- '/\?entryPoint=attachment\&id=([A-Za-z0-9]*)/',
- function ($matches) {
- $id = $matches[1];
- $attachment = $this->entityManager->getEntity('Attachment', $id);
-
- if ($attachment) {
- $filePath = $this->entityManager
- ->getRepository('Attachment')
- ->getFilePath($attachment);
-
- return $filePath;
- }
- },
- $html
- );
- }
-
- return $html;
- }
-
protected function getFieldType(string $entityType, string $field)
{
if (!$this->metadata) {
diff --git a/application/Espo/Core/Htmlizer/TemplateRenderer.php b/application/Espo/Core/Htmlizer/TemplateRenderer.php
new file mode 100644
index 0000000000..2274ecf58a
--- /dev/null
+++ b/application/Espo/Core/Htmlizer/TemplateRenderer.php
@@ -0,0 +1,164 @@
+htmlizerFactory = $htmlizerFactory;
+ $this->aclManager = $aclManager;
+ }
+
+ /**
+ * Setting a user also sets applyAcl.
+ */
+ public function setUser(User $user): self
+ {
+ $this->user = $user;
+
+ $this->setApplyAcl();
+
+ return $this;
+ }
+
+ public function setEntity(Entity $entity): self
+ {
+ $this->entity = $entity;
+
+ return $this;
+ }
+
+ /**
+ * @param stdClass|array $data Additional data.
+ */
+ public function setData($data): self
+ {
+ if (!is_array($data) && !$data instanceof stdClass) {
+ throw new InvalidArgumentException();
+ }
+
+ if (is_object($data)) {
+ $data = get_object_vars($data);
+ }
+
+ $this->data = $data;
+
+ return $this;
+ }
+
+ public function setSkipRelations(bool $skipRelations = true): self
+ {
+ $this->skipRelations = $skipRelations;
+
+ return $this;
+ }
+
+ public function setApplyAcl(bool $applyAcl = true): self
+ {
+ $this->applyAcl = $applyAcl;
+
+ return $this;
+ }
+
+ public function render(): string
+ {
+ if (!$this->template) {
+ throw new LogicException("No template.");
+ }
+
+ return $this->renderTemplate($this->template);
+ }
+
+ public function renderTemplate(string $template): string
+ {
+ return $this->renderTemplateInternal($template, $this->createHtmlizer());
+ }
+
+ private function renderTemplateInternal(string $template, Htmlizer $htmlizer): string
+ {
+ return $htmlizer->render(
+ $this->entity,
+ $template,
+ null,
+ $this->data,
+ $this->skipRelations
+ );
+ }
+
+ /**
+ * @return string
+ */
+ public function renderMultipleTemplates(string ...$templateList): array
+ {
+ $htmlizer = $this->createHtmlizer();
+
+ $resultList = [];
+
+ foreach ($templateList as $template) {
+ $resultList[] = $this->renderTemplateInternal($template, $htmlizer);
+ }
+
+ return $resultList;
+ }
+
+ private function createHtmlizer(): Htmlizer
+ {
+ return $this->user ?
+ $this->htmlizerFactory->createForUser($this->user) :
+ $this->htmlizerFactory->create(!$this->applyAcl);
+ }
+}
diff --git a/application/Espo/Core/Htmlizer/TemplateRendererFactory.php b/application/Espo/Core/Htmlizer/TemplateRendererFactory.php
new file mode 100644
index 0000000000..7f0ea18f86
--- /dev/null
+++ b/application/Espo/Core/Htmlizer/TemplateRendererFactory.php
@@ -0,0 +1,47 @@
+injectableFactory = $injectableFactory;
+ }
+
+ public function create(): TemplateRenderer
+ {
+ return $this->injectableFactory->create(TemplateRenderer::class);
+ }
+}
diff --git a/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php b/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php
index f24231d8b1..7b34b4c629 100644
--- a/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php
+++ b/application/Espo/Tools/Pdf/Tcpdf/EntityProcessor.php
@@ -30,9 +30,11 @@
namespace Espo\Tools\Pdf\Tcpdf;
use Espo\Core\Utils\Config;
-use Espo\Core\Htmlizer\Htmlizer as Htmlizer;
-use Espo\Core\Htmlizer\HtmlizerFactory as HtmlizerFactory;
+use Espo\Core\Htmlizer\TemplateRendererFactory;
+use Espo\Core\Htmlizer\TemplateRenderer;
+
use Espo\ORM\Entity;
+
use Espo\Tools\Pdf\Template;
use Espo\Tools\Pdf\Data;
use Espo\Tools\Pdf\Params;
@@ -46,21 +48,21 @@ class EntityProcessor
private $config;
- private $htmlizerFactory;
+ private $templateRendererFactory;
- public function __construct(Config $config, HtmlizerFactory $htmlizerFactory)
+ public function __construct(Config $config, TemplateRendererFactory $templateRendererFactory)
{
$this->config = $config;
- $this->htmlizerFactory = $htmlizerFactory;
+ $this->templateRendererFactory = $templateRendererFactory;
}
public function process(Tcpdf $pdf, Template $template, Entity $entity, Params $params, Data $data): void
{
- $additionalData = get_object_vars($data->getAdditionalTemplateData());
-
- $htmlizer = $params->applyAcl() ?
- $this->htmlizerFactory->create() :
- $this->htmlizerFactory->createNoAcl();
+ $renderer = $this->templateRendererFactory
+ ->create()
+ ->setApplyAcl($params->applyAcl())
+ ->setEntity($entity)
+ ->setData($data->getAdditionalTemplateData());
$fontFace = $this->config->get('pdfFontFace', $this->fontFace);
$fontSize = $this->config->get('pdfFontSize', $this->fontSize);
@@ -103,7 +105,7 @@ class EntityProcessor
}
if ($template->hasFooter()) {
- $htmlFooter = $this->render($htmlizer, $entity, $template->getFooter(), $additionalData);
+ $htmlFooter = $this->render($renderer, $template->getFooter());
$pdf->setFooterFont([$fontFace, '', $this->fontSize]);
$pdf->setFooterPosition($template->getFooterPosition());
@@ -115,7 +117,7 @@ class EntityProcessor
}
if ($template->hasHeader()) {
- $htmlHeader = $this->render($htmlizer, $entity, $template->getHeader(), $additionalData);
+ $htmlHeader = $this->render($renderer, $template->getHeader());
$pdf->setHeaderFont([$fontFace, '', $this->fontSize]);
$pdf->setHeaderPosition($template->getHeaderPosition());
@@ -128,21 +130,16 @@ class EntityProcessor
$pdf->addPage($pageOrientationCode, $pageFormat);
- $htmlBody = $this->render($htmlizer, $entity, $template->getBody(), $additionalData);
+ $htmlBody = $this->render($renderer, $template->getBody());
$pdf->writeHTML($htmlBody, true, false, true, false, '');
}
- private function render(Htmlizer $htmlizer, Entity $entity, string $template, array $additionalData): string
+ private function render(TemplateRenderer $renderer, string $template): string
{
- $html = $htmlizer->render(
- $entity,
- $template,
- null,
- $additionalData
- );
+ $html = $renderer->renderTemplate($template);
- $html = preg_replace_callback(
+ return preg_replace_callback(
'//',
function ($matches) {
$dataString = $matches[1];
@@ -153,8 +150,6 @@ class EntityProcessor
},
$html
);
-
- return $html;
}
private function composeBarcodeTag(array $data): string