diff --git a/application/Espo/Core/Htmlizer/Htmlizer.php b/application/Espo/Core/Htmlizer/Htmlizer.php index 6ae4fe8800..2a24936119 100644 --- a/application/Espo/Core/Htmlizer/Htmlizer.php +++ b/application/Espo/Core/Htmlizer/Htmlizer.php @@ -270,235 +270,248 @@ class Htmlizer return $data; } + protected function getHelpers() + { + $helpers = [ + 'file' => function () { + $args = func_get_args(); + $id = $args[0] ?? null; + if (!$id) return ''; + return new LightnCandy\SafeString("?entryPoint=attachment&id=" . $id); + }, + 'pagebreak' => function () { + return new LightnCandy\SafeString('
'); + }, + 'imageTag' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + + $field = $context['hash']['field'] ?? null; + if ($field) { + $id = $context['_this'][$field . 'Id'] ?? null; + } else { + if (count($args) > 1) { + $id = $args[0]; + } + } + if (!$id || !is_string($id)) return null; + + $width = $context['hash']['width'] ?? null; + $height = $context['hash']['height'] ?? null; + + $attributesPart = ""; + if ($width) + $attributesPart .= " width=\"" .strval($width) . "\""; + + if ($height) + $attributesPart .= " height=\"" .strval($height) . "\""; + + $html = ""; + + return new LightnCandy\SafeString($html); + }, + 'var' => function () { + $args = func_get_args(); + $c = $args[1] ?? []; + $key = $args[0] ?? null; + if (is_null($key)) return null; + return $c[$key] ?? null; + }, + 'numberFormat' => function () { + $args = func_get_args(); + if (count($args) !== 2) return null; + $context = $args[count($args) - 1]; + $number = $args[0] ?? null; + + if (is_null($number)) return ''; + + $decimals = $context['hash']['decimals'] ?? 0; + $decimalPoint = $context['hash']['decimalPoint'] ?? '.'; + $thousandsSeparator = $context['hash']['thousandsSeparator'] ?? ','; + + return number_format($number, $decimals, $decimalPoint, $thousandsSeparator); + }, + 'dateFormat' => function () { + $args = func_get_args(); + if (count($args) !== 2) return null; + $context = $args[count($args) - 1]; + $dateValue = $args[0]; + + $format = $context['hash']['format'] ?? null; + $timezone = $context['hash']['timezone'] ?? null; + $language = $context['hash']['language'] ?? null; + $dateTime = $context['data']['root']['__dateTime']; + if (strlen($dateValue) > 11) return $dateTime->convertSystemDateTime($dateValue, $timezone, $format, $language); + + return $dateTime->convertSystemDate($dateValue, $format, $language); + }, + 'barcodeImage' => function () { + $args = func_get_args(); + if (count($args) !== 2) return null; + $context = $args[count($args) - 1]; + $value = $args[0]; + + $codeType = $context['hash']['type'] ?? 'CODE128'; + + $typeMap = [ + "CODE128" => 'C128', + "CODE128A" => 'C128A', + "CODE128B" => 'C128B', + "CODE128C" => 'C128C', + "EAN13" => 'EAN13', + "EAN8" => 'EAN8', + "EAN5" => 'EAN5', + "EAN2" => 'EAN2', + "UPC" => 'UPCA', + "UPCE" => 'UPCE', + "ITF14" => 'I25', + "pharmacode" => 'PHARMA', + "QRcode" => 'QRCODE,H', + ]; + + if ($codeType === 'QRcode') { + $function = 'write2DBarcode'; + $params = [ + $value, + $typeMap[$codeType] ?? null, + '', '', + $context['hash']['width'] ?? 40, + $context['hash']['height'] ?? 40, + [ + 'border' => false, + 'vpadding' => $context['hash']['padding'] ?? 2, + 'hpadding' => $context['hash']['padding'] ?? 2, + 'fgcolor' => $context['hash']['color'] ?? [0,0,0], + 'bgcolor' => $context['hash']['bgcolor'] ?? false, + 'module_width' => 1, + 'module_height' => 1, + ], + 'N', + ]; + } else { + $function = 'write1DBarcode'; + $params = [ + $value, + $typeMap[$codeType] ?? null, + '', '', + $context['hash']['width'] ?? 60, + $context['hash']['height'] ?? 30, + 0.4, + [ + 'position' => 'S', + 'border' => false, + 'padding' => $context['hash']['padding'] ?? 0, + 'fgcolor' => $context['hash']['color'] ?? [0,0,0], + 'bgcolor' => $context['hash']['bgcolor'] ?? [255,255,255], + 'text' => $context['hash']['text'] ?? true, + 'font' => 'helvetica', + 'fontsize' => $context['hash']['fontsize'] ?? 14, + 'stretchtext' => 4, + ], + 'N', + ]; + } + + $paramsString = urlencode(json_encode($params)); + + return new LightnCandy\SafeString(""); + }, + 'ifEqual' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + if ($args[0] === $args[1]) { + return $context['fn'](); + } else { + return $context['inverse'] ? $context['inverse']() : ''; + } + }, + 'ifNotEqual' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + if ($args[0] !== $args[1]) { + return $context['fn'](); + } else { + return $context['inverse'] ? $context['inverse']() : ''; + } + }, + 'ifInArray' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + + $array = $args[1] ?? []; + + if (in_array($args[0], $array)) { + return $context['fn'](); + } else { + return $context['inverse'] ? $context['inverse']() : ''; + } + }, + 'tableTag' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + + $border = $context['hash']['border'] ?? '0.5pt'; + $cellpadding = $context['hash']['cellpadding'] ?? '2'; + + + $width = $context['hash']['width'] ?? null; + + $attributesPart = ""; + if ($width) { + $attributesPart .= " width=\"{$width}\""; + } + + return + new LightnCandy\SafeString("") . + $context['fn']() . + new LightnCandy\SafeString("
"); + }, + 'tdTag' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + + $align = strtolower($context['hash']['align'] ?? 'left'); + if (!in_array($align, ['left', 'right', 'center'])) $align = 'left'; + + $width = $context['hash']['width'] ?? null; + + $attributesPart = "align=\"{$align}\""; + if ($width) { + $attributesPart .= " width=\"{$width}\""; + } + + return + new LightnCandy\SafeString("") . + $context['fn']() . + new LightnCandy\SafeString(""); + }, + 'trTag' => function () { + $args = func_get_args(); + $context = $args[count($args) - 1]; + + return + new LightnCandy\SafeString("") . + $context['fn']() . + new LightnCandy\SafeString(""); + }, + ]; + + $additionalHelpers = $this->getMetadata()->get(['app', 'templateHelpers']) ?? []; + + $helpers = array_merge($helpers, $additionalHelpers); + + return $helpers; + } + public function render(Entity $entity, $template, $id = null, $additionalData = [], $skipLinks = false) { $template = str_replace('getHelpers(); + $code = LightnCandy::compile($template, [ 'flags' => LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_ERROR_EXCEPTION, - 'helpers' => [ - 'file' => function () { - $args = func_get_args(); - $id = $args[0] ?? null; - if (!$id) return ''; - return new LightnCandy\SafeString("?entryPoint=attachment&id=" . $id); - }, - 'pagebreak' => function () { - return new LightnCandy\SafeString('
'); - }, - 'imageTag' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - - $field = $context['hash']['field'] ?? null; - if ($field) { - $id = $context['_this'][$field . 'Id'] ?? null; - } else { - if (count($args) > 1) { - $id = $args[0]; - } - } - if (!$id || !is_string($id)) return null; - - $width = $context['hash']['width'] ?? null; - $height = $context['hash']['height'] ?? null; - - $attributesPart = ""; - if ($width) - $attributesPart .= " width=\"" .strval($width) . "\""; - - if ($height) - $attributesPart .= " height=\"" .strval($height) . "\""; - - $html = ""; - - return new LightnCandy\SafeString($html); - }, - 'var' => function () { - $args = func_get_args(); - $c = $args[1] ?? []; - $key = $args[0] ?? null; - if (is_null($key)) return null; - return $c[$key] ?? null; - }, - 'numberFormat' => function () { - $args = func_get_args(); - if (count($args) !== 2) return null; - $context = $args[count($args) - 1]; - $number = $args[0] ?? null; - - if (is_null($number)) return ''; - - $decimals = $context['hash']['decimals'] ?? 0; - $decimalPoint = $context['hash']['decimalPoint'] ?? '.'; - $thousandsSeparator = $context['hash']['thousandsSeparator'] ?? ','; - - return number_format($number, $decimals, $decimalPoint, $thousandsSeparator); - }, - 'dateFormat' => function () { - $args = func_get_args(); - if (count($args) !== 2) return null; - $context = $args[count($args) - 1]; - $dateValue = $args[0]; - - $format = $context['hash']['format'] ?? null; - $timezone = $context['hash']['timezone'] ?? null; - $language = $context['hash']['language'] ?? null; - $dateTime = $context['data']['root']['__dateTime']; - if (strlen($dateValue) > 11) return $dateTime->convertSystemDateTime($dateValue, $timezone, $format, $language); - - return $dateTime->convertSystemDate($dateValue, $format, $language); - }, - 'barcodeImage' => function () { - $args = func_get_args(); - if (count($args) !== 2) return null; - $context = $args[count($args) - 1]; - $value = $args[0]; - - $codeType = $context['hash']['type'] ?? 'CODE128'; - - $typeMap = [ - "CODE128" => 'C128', - "CODE128A" => 'C128A', - "CODE128B" => 'C128B', - "CODE128C" => 'C128C', - "EAN13" => 'EAN13', - "EAN8" => 'EAN8', - "EAN5" => 'EAN5', - "EAN2" => 'EAN2', - "UPC" => 'UPCA', - "UPCE" => 'UPCE', - "ITF14" => 'I25', - "pharmacode" => 'PHARMA', - "QRcode" => 'QRCODE,H', - ]; - - if ($codeType === 'QRcode') { - $function = 'write2DBarcode'; - $params = [ - $value, - $typeMap[$codeType] ?? null, - '', '', - $context['hash']['width'] ?? 40, - $context['hash']['height'] ?? 40, - [ - 'border' => false, - 'vpadding' => $context['hash']['padding'] ?? 2, - 'hpadding' => $context['hash']['padding'] ?? 2, - 'fgcolor' => $context['hash']['color'] ?? [0,0,0], - 'bgcolor' => $context['hash']['bgcolor'] ?? false, - 'module_width' => 1, - 'module_height' => 1, - ], - 'N', - ]; - } else { - $function = 'write1DBarcode'; - $params = [ - $value, - $typeMap[$codeType] ?? null, - '', '', - $context['hash']['width'] ?? 60, - $context['hash']['height'] ?? 30, - 0.4, - [ - 'position' => 'S', - 'border' => false, - 'padding' => $context['hash']['padding'] ?? 0, - 'fgcolor' => $context['hash']['color'] ?? [0,0,0], - 'bgcolor' => $context['hash']['bgcolor'] ?? [255,255,255], - 'text' => $context['hash']['text'] ?? true, - 'font' => 'helvetica', - 'fontsize' => $context['hash']['fontsize'] ?? 14, - 'stretchtext' => 4, - ], - 'N', - ]; - } - - $paramsString = urlencode(json_encode($params)); - - return new LightnCandy\SafeString(""); - }, - 'ifEqual' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - if ($args[0] === $args[1]) { - return $context['fn'](); - } else { - return $context['inverse'] ? $context['inverse']() : ''; - } - }, - 'ifNotEqual' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - if ($args[0] !== $args[1]) { - return $context['fn'](); - } else { - return $context['inverse'] ? $context['inverse']() : ''; - } - }, - 'ifInArray' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - - $array = $args[1] ?? []; - - if (in_array($args[0], $array)) { - return $context['fn'](); - } else { - return $context['inverse'] ? $context['inverse']() : ''; - } - }, - 'tableTag' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - - $border = $context['hash']['border'] ?? '0.5pt'; - $cellpadding = $context['hash']['cellpadding'] ?? '2'; - - - $width = $context['hash']['width'] ?? null; - - $attributesPart = ""; - if ($width) { - $attributesPart .= " width=\"{$width}\""; - } - - return - new LightnCandy\SafeString("") . - $context['fn']() . - new LightnCandy\SafeString("
"); - }, - 'tdTag' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - - $align = strtolower($context['hash']['align'] ?? 'left'); - if (!in_array($align, ['left', 'right', 'center'])) $align = 'left'; - - $width = $context['hash']['width'] ?? null; - - $attributesPart = "align=\"{$align}\""; - if ($width) { - $attributesPart .= " width=\"{$width}\""; - } - - return - new LightnCandy\SafeString("") . - $context['fn']() . - new LightnCandy\SafeString(""); - }, - 'trTag' => function () { - $args = func_get_args(); - $context = $args[count($args) - 1]; - - return - new LightnCandy\SafeString("") . - $context['fn']() . - new LightnCandy\SafeString(""); - }, - ], + 'helpers' => $this->getHelpers(), ]); $renderer = LightnCandy::prepare($code); @@ -521,6 +534,8 @@ class Htmlizer $data['__dateTime'] = $this->dateTime; $data['__metadata'] = $this->metadata; + $data['__entityManager'] = $this->entityManager; + $data['__language'] = $this->language; $html = $renderer($data); diff --git a/application/Espo/Resources/metadata/app/metadata.json b/application/Espo/Resources/metadata/app/metadata.json index fdbec53fed..840afc945f 100644 --- a/application/Espo/Resources/metadata/app/metadata.json +++ b/application/Espo/Resources/metadata/app/metadata.json @@ -8,6 +8,7 @@ ["app", "emailNotifications", "handlerClassNameMap"], ["app", "client"], ["app", "language"], + ["app", "templateHelpers"], ["app", "auth2FAMethods", "__ANY__", "implementationClassName"], ["app", "auth2FAMethods", "__ANY__", "implementationUserClassName"], ["authenticationMethods", "__ANY__", "implementationClassName"]