From 0a320cb3b3aa42c780491b59fe607eb45307fd78 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 22 Sep 2021 11:58:02 +0300 Subject: [PATCH] templete helper interface --- .../Classes/TemplateHelpers/GoogleMaps.php | 142 ++++++++++-------- .../Espo/Classes/TemplateHelpers/TableTag.php | 56 +++++++ .../Espo/Classes/TemplateHelpers/TdTag.php | 60 ++++++++ .../Espo/Classes/TemplateHelpers/TrTag.php | 46 ++++++ application/Espo/Core/Htmlizer/Helper.php | 38 +++++ .../Espo/Core/Htmlizer/Helper/Data.php | 122 +++++++++++++++ .../Espo/Core/Htmlizer/Helper/Result.php | 73 +++++++++ .../Espo/Core/Htmlizer/Helper/SafeString.php | 57 +++++++ application/Espo/Core/Htmlizer/Htmlizer.php | 124 +++++++-------- .../metadata/app/templateHelpers.json | 5 +- 10 files changed, 600 insertions(+), 123 deletions(-) create mode 100644 application/Espo/Classes/TemplateHelpers/TableTag.php create mode 100644 application/Espo/Classes/TemplateHelpers/TdTag.php create mode 100644 application/Espo/Classes/TemplateHelpers/TrTag.php create mode 100644 application/Espo/Core/Htmlizer/Helper.php create mode 100644 application/Espo/Core/Htmlizer/Helper/Data.php create mode 100644 application/Espo/Core/Htmlizer/Helper/Result.php create mode 100644 application/Espo/Core/Htmlizer/Helper/SafeString.php diff --git a/application/Espo/Classes/TemplateHelpers/GoogleMaps.php b/application/Espo/Classes/TemplateHelpers/GoogleMaps.php index dae6b774de..dbb9e43183 100644 --- a/application/Espo/Classes/TemplateHelpers/GoogleMaps.php +++ b/application/Espo/Classes/TemplateHelpers/GoogleMaps.php @@ -29,61 +29,79 @@ namespace Espo\Classes\TemplateHelpers; -class GoogleMaps +use Espo\Core\Htmlizer\Helper; +use Espo\Core\Htmlizer\Helper\Data; +use Espo\Core\Htmlizer\Helper\Result; + +use Espo\Core\Utils\Metadata; +use Espo\Core\Utils\Config; +use Espo\Core\Utils\Log; + +class GoogleMaps implements Helper { - public static function image() + private const DEFAULT_SIZE = '400x400'; + + private $metadata; + + private $config; + + private $log; + + public function __construct( + Metadata $metadata, + Config $config, + Log $log + ) { + $this->metadata = $metadata; + $this->config = $config; + $this->log = $log; + } + + public function render(Data $data): Result { - $args = func_get_args(); - $context = $args[count($args) - 1]; - $hash = $context['hash']; - $data = $context['data']['root']; + $rootContext = $data->getRootContext(); - $em = $data['__entityManager']; - $metadata = $data['__metadata']; - $config = $data['__config']; - $log = $data['__log']; + $entityType = $rootContext['__entityType']; - $entityType = $data['__entityType']; - - $field = $hash['field'] ?? null; - - $size = $hash['size'] ?? '400x400'; - $zoom = $hash['zoom'] ?? null; - $language = $hash['language'] ?? $config->get('language'); + $field = $data->getOption('field'); + $size = $data->getOption('size') ?? self::DEFAULT_SIZE; + $zoom = $data->getOption('zoom'); + $language = $data->getOption('language') ?? $this->config->get('language'); if (strpos($size, 'x') === false) { - $size = $size .'x' . $size; + $size = $size . 'x' . $size; } - if ($field && $metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']) !== 'address') { - $log->warning("Template helper _googleMapsImage: Specified field is not of address type."); + if ($field && $this->metadata->get(['entityDefs', $entityType, 'fields', $field, 'type']) !== 'address') { + $this->log->warning("Template helper _googleMapsImage: Specified field is not of address type."); - return null; + return Result::createEmpty(); } if ( !$field && - !array_key_exists('street', $hash) && - !array_key_exists('city', $hash) && - !array_key_exists('country', $hash) && - !array_key_exists('state', $hash) && - !array_key_exists('postalCode', $hash) + !$data->hasOption('street') && + !$data->hasOption('city') && + !$data->hasOption('country') && + !$data->hasOption('state') && + !$data->hasOption('postalCode') ) { $field = ($entityType === 'Account') ? 'billingAddress' : 'address'; } if ($field) { - $street = $data[$field . 'Street'] ?? null; - $city = $data[$field . 'City'] ?? null; - $country = $data[$field . 'Country'] ?? null; - $state = $data[$field . 'State'] ?? null; - $postalCode = $data[$field . 'postalCode'] ?? null; - } else { - $street = $hash['street'] ?? null; - $city = $hash['city'] ?? null; - $country = $hash['country'] ?? null; - $state = $hash['state'] ?? null; - $postalCode = $hash['postalCode'] ?? null; + $street = $rootContext[$field . 'Street'] ?? null; + $city = $rootContext[$field . 'City'] ?? null; + $country = $rootContext[$field . 'Country'] ?? null; + $state = $rootContext[$field . 'State'] ?? null; + $postalCode = $rootContext[$field . 'postalCode'] ?? null; + } + else { + $street = $data->getOption('street'); + $city = $data->getOption('city'); + $country = $data->getOption('country'); + $state = $data->getOption('state'); + $postalCode = $data->getOption('postalCode'); } $address = ''; @@ -96,6 +114,7 @@ class GoogleMaps if ($address != '') { $address .= ', '; } + $address .= $city; } @@ -103,16 +122,16 @@ class GoogleMaps if ($address != '') { $address .= ', '; } + $address .= $state; } if ($postalCode) { if ($state || $city) { $address .= ' '; - } else { - if ($address) { - $address .= ', '; - } + } + else if ($address) { + $address .= ', '; } $address .= $postalCode; @@ -126,26 +145,26 @@ class GoogleMaps $address .= $country; } - $address = urlencode($address); - - $apiKey = $config->get('googleMapsApiKey'); + $apiKey = $this->config->get('googleMapsApiKey'); if (!$apiKey) { - $log->error("Template helper _googleMapsImage: No Google Maps API key."); + $this->log->error("Template helper _googleMapsImage: No Google Maps API key."); - return null; + return Result::createEmpty(); } - if (!$address) { - $log->debug("Template helper _googleMapsImage: No address to display."); + $addressEncoded = urlencode($address); - return null; + if (!$addressEncoded) { + $this->log->debug("Template helper _googleMapsImage: No address to display."); + + return Result::createEmpty(); } $format = 'jpg;'; $url = "https://maps.googleapis.com/maps/api/staticmap?" . - 'center=' . $address . + 'center=' . $addressEncoded . 'format=' . $format . '&size=' . $size . '&key=' . $apiKey; @@ -158,13 +177,12 @@ class GoogleMaps $url .= '&language=' . $language; } - $log->debug("Template helper _googleMapsImage: URL: {$url}."); + $this->log->debug("Template helper _googleMapsImage: URL: {$url}."); - // Need to use fully-qualified class name. - $image = \Espo\Classes\TemplateHelpers\GoogleMaps::getImage($url); + $image = $this->getImage($url); if (!$image) { - return null; + return Result::createEmpty(); } list($width, $height) = explode('x', $size); @@ -173,14 +191,18 @@ class GoogleMaps $tag = ""; - return new LightnCandy\SafeString($tag); + return Result::createSafeString($tag); } - public static function getImage(string $url) + /** + * @return string|bool + */ + private function getImage(string $url) { - $headers = []; - $headers[] = 'Accept: image/jpeg, image/pjpeg'; - $headers[] = 'Connection: Keep-Alive'; + $headers = [ + 'Accept: image/jpeg, image/pjpeg', + 'Connection: Keep-Alive', + ]; $agent = 'Mozilla/5.0'; @@ -195,8 +217,8 @@ class GoogleMaps curl_setopt($c, \CURLOPT_FOLLOWLOCATION, 1); curl_setopt($c, \CURLOPT_BINARYTRANSFER, 1); - $raw = curl_exec($c); + curl_close($c); return $raw; diff --git a/application/Espo/Classes/TemplateHelpers/TableTag.php b/application/Espo/Classes/TemplateHelpers/TableTag.php new file mode 100644 index 0000000000..c6e9d94e46 --- /dev/null +++ b/application/Espo/Classes/TemplateHelpers/TableTag.php @@ -0,0 +1,56 @@ +getOption('border') ?? '0.5pt'; + $cellpadding = $data->getOption('cellpadding') ?? '2'; + $width = $data->getOption('width') ?? null; + + $attributesPart = ""; + + if ($width) { + $attributesPart .= " width=\"{$width}\""; + } + + return Result::createSafeString( + "" . + $data->getFunction()() . + "
" + ); + } +} diff --git a/application/Espo/Classes/TemplateHelpers/TdTag.php b/application/Espo/Classes/TemplateHelpers/TdTag.php new file mode 100644 index 0000000000..8ef2045623 --- /dev/null +++ b/application/Espo/Classes/TemplateHelpers/TdTag.php @@ -0,0 +1,60 @@ +getOption('align') ?? 'left'); + + if (!in_array($align, ['left', 'right', 'center'])) { + $align = 'left'; + } + + $width = $data->getOption('width') ?? null; + + $attributesPart = "align=\"{$align}\""; + + if ($width) { + $attributesPart .= " width=\"{$width}\""; + } + + return Result::createSafeString( + "" . + $data->getFunction()() . + "" + ); + } +} diff --git a/application/Espo/Classes/TemplateHelpers/TrTag.php b/application/Espo/Classes/TemplateHelpers/TrTag.php new file mode 100644 index 0000000000..b131eba389 --- /dev/null +++ b/application/Espo/Classes/TemplateHelpers/TrTag.php @@ -0,0 +1,46 @@ +" . + $data->getFunction()() . + "" + ); + } +} diff --git a/application/Espo/Core/Htmlizer/Helper.php b/application/Espo/Core/Htmlizer/Helper.php new file mode 100644 index 0000000000..a4e3d8136e --- /dev/null +++ b/application/Espo/Core/Htmlizer/Helper.php @@ -0,0 +1,38 @@ +name = $name; + $this->argumentList = $argumentList; + $this->options = $options; + $this->context = $context; + $this->rootContext = $rootContext; + $this->blockParams = $blockParams; + $this->func = $func; + $this->inverseFunc = $inverseFunc; + } + + public function getName(): string + { + return $this->name; + } + + public function getContext(): array + { + return $this->context; + } + + public function getRootContext(): array + { + return $this->rootContext; + } + + public function getOptions(): stdClass + { + return $this->options; + } + + /** + * @return mixed[] + */ + public function getArgumentList(): array + { + return $this->argumentList; + } + + public function hasOption(string $name): bool + { + return property_exists($this->options, $name); + } + + /** + * @return mixed + */ + public function getOption(string $name) + { + return $this->options->$name ?? null; + } + + public function getFunction(): ?callable + { + return $this->func; + } + + public function getInverseFunction(): ?callable + { + return $this->inverseFunc; + } +} diff --git a/application/Espo/Core/Htmlizer/Helper/Result.php b/application/Espo/Core/Htmlizer/Helper/Result.php new file mode 100644 index 0000000000..d03ccb8324 --- /dev/null +++ b/application/Espo/Core/Htmlizer/Helper/Result.php @@ -0,0 +1,73 @@ +value = new SafeString($value); + + return $obj; + } + + public static function createEmpty(): self + { + $obj = new self(); + $obj->value = ''; + + return $obj; + } + + public static function create(string $value): self + { + $obj = new self(); + $obj->value = $value; + + return $obj; + } + + /** + * @return SafeString|string + */ + public function getValue() + { + if ($this->value instanceof SafeString) { + return $this->value; + } + + return (string) $this->value; + } +} diff --git a/application/Espo/Core/Htmlizer/Helper/SafeString.php b/application/Espo/Core/Htmlizer/Helper/SafeString.php new file mode 100644 index 0000000000..16d98719e4 --- /dev/null +++ b/application/Espo/Core/Htmlizer/Helper/SafeString.php @@ -0,0 +1,57 @@ +wrappee = new LightnCandySafeString($value); + } + + public static function create(string $value): self + { + return new self($value); + } + + public function getWrappee(): LightnCandySafeString + { + return $this->wrappee; + } + + public function __toString() + { + return (string) $this->wrappee; + } +} diff --git a/application/Espo/Core/Htmlizer/Htmlizer.php b/application/Espo/Core/Htmlizer/Htmlizer.php index 33bc3154de..b16d044cfd 100644 --- a/application/Espo/Core/Htmlizer/Htmlizer.php +++ b/application/Espo/Core/Htmlizer/Htmlizer.php @@ -187,7 +187,7 @@ class Htmlizer return $html; } - protected function format($value) + private function format($value) { if (is_float($value)) { return $this->number->format($value, 2); @@ -204,7 +204,7 @@ class Htmlizer return $value; } - protected function getDataFromEntity( + private function getDataFromEntity( Entity $entity, bool $skipLinks = false, int $level = 0, @@ -443,7 +443,7 @@ class Htmlizer return $data; } - protected function getHelpers() + private function getHelpers(): array { $helpers = [ 'file' => function () { @@ -610,63 +610,6 @@ class Htmlizer 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(""); - }, 'checkboxTag' => function () { $args = func_get_args(); @@ -706,16 +649,73 @@ class Htmlizer }, ]; + $customHelper = function () { + $args = func_get_args(); + $agumentList = array_slice($args, 0, -1); + $context = $args[count($args) - 1]; + + $options = $context['hash']; + $rootData = $context['data']['root']; + + $injectableFactory = $rootData['__injectableFactory']; + $metadata = $rootData['__metadata']; + + $name = $context['name']; + + $className = $metadata->get(['app', 'templateHelpers', $name]); + + $data = new \Espo\Core\Htmlizer\Helper\Data( + $name, + $agumentList, + (object) $options, + $context['_this'], + $rootData, + $context['fn.blockParams'], + $context['fn'] ?? null, + $context['inverse'] ?? null + ); + + $helper = $injectableFactory->create($className); + + $result = $helper->render($data); + + $value = $result->getValue(); + + if ($value instanceof \Espo\Core\Htmlizer\Helper\SafeString) { + return $value->getWrappee(); + } + + return $value; + }; + if ($this->metadata) { - $additionalHelpers = $this->metadata->get(['app', 'templateHelpers']) ?? []; + $additionalHelpers = array_filter( + $this->metadata->get(['app', 'templateHelpers']) ?? [], + function (string $item) { + return strpos($item, '::') !== false; + } + ); $helpers = array_merge($helpers, $additionalHelpers); + + $additionalHelper2NameList = array_keys( + array_filter( + $this->metadata->get(['app', 'templateHelpers']) ?? [], + function (string $item) { + return strpos($item, '::') == false; + } + ) + ); + + foreach ($additionalHelper2NameList as $name) { + $helpers[$name] = $customHelper; + } } return $helpers; } - protected function getFieldType(string $entityType, string $field) + private function getFieldType(string $entityType, string $field) { if (!$this->metadata) { return null; diff --git a/application/Espo/Resources/metadata/app/templateHelpers.json b/application/Espo/Resources/metadata/app/templateHelpers.json index ed41cddcfd..5485904792 100644 --- a/application/Espo/Resources/metadata/app/templateHelpers.json +++ b/application/Espo/Resources/metadata/app/templateHelpers.json @@ -1,3 +1,6 @@ { - "googleMapsImage": "Espo\\Classes\\TemplateHelpers\\GoogleMaps::image" + "googleMapsImage": "Espo\\Classes\\TemplateHelpers\\GoogleMaps", + "tableTag": "Espo\\Classes\\TemplateHelpers\\TableTag", + "trTag": "Espo\\Classes\\TemplateHelpers\\TrTag", + "tdTag": "Espo\\Classes\\TemplateHelpers\\TdTag" }