fileManager = $fileManager; $this->dateTime = $dateTime; $this->number = $number; $this->acl = $acl; $this->entityManager = $entityManager; $this->metadata = $metadata; $this->language = $language; $this->config = $config; } protected function getAcl() { return $this->acl; } protected function getEntityManager() { return $this->entityManager; } protected function getMetadata() { return $this->metadata; } protected function format($value) { if (is_float($value)) { $value = $this->number->format($value, 2); } else if (is_int($value)) { $value = $this->number->format($value); } else if (is_string($value)) { $value = nl2br($value); } return $value; } protected function getDataFromEntity(Entity $entity, $skipLinks = false, $level = 0, ?string $template = null) { $data = $entity->toArray(); $attributeDefs = $entity->getAttributes(); $attributeList = array_keys($attributeDefs); $forbiddenAttributeList = []; $skipAttributeList = []; $forbiddenLinkList = []; if ($this->getAcl()) { $forbiddenAttributeList = $this->getAcl()->getScopeForbiddenAttributeList($entity->getEntityType(), 'read'); $forbiddenAttributeList = array_merge( $forbiddenAttributeList, $this->getAcl()->getScopeRestrictedAttributeList($entity->getEntityType(), ['forbidden', 'internal', 'onlyAdmin']) ); $forbiddenLinkList = $this->getAcl()->getScopeRestrictedLinkList($entity->getEntityType(), ['forbidden', 'internal', 'onlyAdmin']); } $relationList = $entity->getRelationList(); if (!$skipLinks && $level === 0) { foreach ($relationList as $relation) { $collection = null; if ($entity->hasLinkMultipleField($relation)) { $toLoad = true; $collection = $entity->getLinkCollection($relation); } else { if ( $template && $entity->getRelationType($relation, ['hasMany', 'manyMany', 'hasChildren']) && mb_stripos($template, '{{#each '.$relation.'}}') !== false ) { $limit = 100; if ($this->config) { $limit = $this->config->get('htmlizerLinkLimit') ?? $limit; } $collection = $entity->getLinkCollection($relation, ['limit' => $limit]); } } if ($collection) { $data[$relation] = $collection; } } } foreach ($data as $key => $value) { if ($value instanceof \Espo\ORM\ICollection) { $skipAttributeList[] = $key; $collection = $value; $list = []; foreach ($collection as $item) { $list[] = $this->getDataFromEntity($item, $skipLinks, $level + 1); } $data[$key] = $list; } } foreach ($attributeList as $attribute) { if (in_array($attribute, $forbiddenAttributeList)) { unset($data[$attribute]); continue; } if (in_array($attribute, $skipAttributeList)) { continue; } $type = $entity->getAttributeType($attribute); $fieldType = $entity->getAttributeParam($attribute, 'fieldType'); if ($type == Entity::DATETIME) { if (!empty($data[$attribute])) { $data[$attribute . '_RAW'] = $data[$attribute]; $data[$attribute] = $this->dateTime->convertSystemDateTime($data[$attribute]); } } else if ($type == Entity::DATE) { if (!empty($data[$attribute])) { $data[$attribute . '_RAW'] = $data[$attribute]; $data[$attribute] = $this->dateTime->convertSystemDate($data[$attribute]); } } else if ($type == Entity::JSON_ARRAY) { if (!empty($data[$attribute])) { $list = $data[$attribute]; $newList = []; foreach ($list as $item) { $v = $item; if ($item instanceof \StdClass) { $v = json_decode(json_encode($v, \JSON_PRESERVE_ZERO_FRACTION), true); } if (is_array($v)) { foreach ($v as $k => $w) { $keyRaw = $k . '_RAW'; $v[$keyRaw] = $v[$k]; $v[$k] = $this->format($v[$k]); } } $newList[] = $v; } $data[$attribute] = $newList; } } else if ($type == Entity::JSON_OBJECT) { if (!empty($data[$attribute])) { $value = $data[$attribute]; if ($value instanceof \StdClass) { $data[$attribute] = json_decode(json_encode($value, \JSON_PRESERVE_ZERO_FRACTION), true); } foreach ($data[$attribute] as $k => $w) { $keyRaw = $k . '_RAW'; $data[$attribute][$keyRaw] = $data[$attribute][$k]; $data[$attribute][$k] = $this->format($data[$attribute][$k]); } } } else if ($type === Entity::PASSWORD) { unset($data[$attribute]); } if ($fieldType === 'currency' && $this->metadata) { if ($entity->getAttributeParam($attribute, 'attributeRole') === 'currency') { if ($currencyValue = $data[$attribute]) { $data[$attribute . 'Symbol'] = $this->metadata->get(['app', 'currency', 'symbolMap', $currencyValue]); } } } if (array_key_exists($attribute, $data)) { $keyRaw = $attribute . '_RAW'; if (!isset($data[$keyRaw])) $data[$keyRaw] = $data[$attribute]; $fieldType = $this->getFieldType($entity->getEntityType(), $attribute); if ($fieldType === 'enum') { if ($this->language) { $data[$attribute] = $this->language->translateOption($data[$attribute], $attribute, $entity->getEntityType()); } } $data[$attribute] = $this->format($data[$attribute]); } } if (!$skipLinks) { $relationDefs = $entity->getRelations(); foreach ($entity->getRelationList() as $relation) { if (in_array($relation, $forbiddenLinkList)) continue; if ( !empty($relationDefs[$relation]['type']) && ($entity->getRelationType($relation) === 'belongsTo' || $entity->getRelationType($relation) === 'belongsToParent') ) { $relatedEntity = $entity->get($relation); if (!$relatedEntity) continue; if ($this->getAcl()) { if (!$this->getAcl()->check($relatedEntity, 'read')) continue; } $data[$relation] = $this->getDataFromEntity($relatedEntity, true, $level + 1); } } } return $data; } public function render(Entity $entity, $template, $id = null, $additionalData = [], $skipLinks = false) { $template = str_replace(' LightnCandy::FLAG_HANDLEBARSJS | LightnCandy::FLAG_ERROR_EXCEPTION, 'helpers' => [ 'file' => function () { $args = func_get_args(); $id = $args[0] ?? null; if (!$id) return ''; return "?entryPoint=attachment&id=" . $id; }, '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', ]; $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']() : ''; } }, ], ]); $renderer = LightnCandy::prepare($code); $data = $this->getDataFromEntity($entity, $skipLinks, 0, $template); 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'); } foreach ($additionalData as $k => $value) { $data[$k] = $value; } $data['__dateTime'] = $this->dateTime; $data['__metadata'] = $this->metadata; $html = $renderer($data); $html = str_replace('?entryPoint=attachment&', '?entryPoint=attachment&', $html); if ($this->getEntityManager()) { $html = preg_replace_callback('/\?entryPoint=attachment\&id=([A-Za-z0-9]*)/', function ($matches) { $id = $matches[1]; $attachment = $this->getEntityManager()->getEntity('Attachment', $id); if ($attachment) { $filePath = $this->getEntityManager()->getRepository('Attachment')->getFilePath($attachment); return $filePath; } }, $html); } return $html; } protected function getFieldType($entityType, $field) { if (!$this->metadata) return; return $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']); } }