From 7ce57766947d11c872c88bbb2df3d5cf6baa8202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Tue, 14 Apr 2020 17:21:30 +0200 Subject: [PATCH] Added basic placeholder providers for parts. --- composer.json | 1 + composer.lock | 48 +++++++- config/services.yaml | 9 ++ .../LabelSystem/LabelHTMLGenerator.php | 6 +- .../LabelSystem/LabelTextReplacer.php | 58 +++++++++ .../AbstractDBElementProvider.php | 55 +++++++++ .../PlaceholderProviders/GlobalProviders.php | 107 +++++++++++++++++ .../NamedElementProvider.php | 40 +++++++ .../PlaceholderProviders/PartProvider.php | 112 ++++++++++++++++++ .../PlaceholderProviderInterface.php | 36 ++++++ .../TimestampableElementProvider.php | 48 ++++++++ src/Services/SIFormatter.php | 1 + symfony.lock | 3 + 13 files changed, 521 insertions(+), 3 deletions(-) create mode 100644 src/Services/LabelSystem/LabelTextReplacer.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/PartProvider.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/PlaceholderProviderInterface.php create mode 100644 src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php diff --git a/composer.json b/composer.json index e7007369..8792e11c 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "doctrine/annotations": "^1.6", "doctrine/doctrine-bundle": "^2.0", "dompdf/dompdf": "^0.8.5", + "erusev/parsedown": "^1.7", "florianv/swap": "^4.0", "friendsofsymfony/ckeditor-bundle": "^2.0", "gregwar/captcha-bundle": "^2.1.0", diff --git a/composer.lock b/composer.lock index 6d5691e3..4fe3da4f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6590c3a2eb19714f486c24335194ad9b", + "content-hash": "2f3beef4790ec9595fcb7f0a03abbaec", "packages": [ { "name": "beberlei/assert", @@ -1390,6 +1390,52 @@ ], "time": "2020-02-13T22:36:52+00:00" }, + { + "name": "erusev/parsedown", + "version": "1.7.4", + "source": { + "type": "git", + "url": "https://github.com/erusev/parsedown.git", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35" + }, + "type": "library", + "autoload": { + "psr-0": { + "Parsedown": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" + } + ], + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", + "keywords": [ + "markdown", + "parser" + ], + "time": "2019-12-30T22:54:17+00:00" + }, { "name": "florianv/exchanger", "version": "2.4.0", diff --git a/config/services.yaml b/config/services.yaml index a08ad9c0..ef7c9f92 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -35,6 +35,11 @@ services: bool $gpdr_compliance : '%gpdr_compliance%' bool $kernel_debug: '%kernel.debug%' string $kernel_cache_dir: '%kernel.cache_dir%' + string $partdb_title: '%partdb_title%' + + _instanceof: + App\Services\LabelSystem\PlaceholderProviders\PlaceholderProviderInterface: + tags: ['app.label_placeholder_provider'] # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name @@ -163,6 +168,10 @@ services: $code_length: 8 $code_count: 15 + App\Services\LabelSystem\LabelTextReplacer: + arguments: + $providers: !tagged_iterator 'app.label_placeholder_provider' + App\Services\TranslationExtractor\PermissionExtractor: tags: - { name: 'translation.extractor', alias: 'permissionExtractor'} \ No newline at end of file diff --git a/src/Services/LabelSystem/LabelHTMLGenerator.php b/src/Services/LabelSystem/LabelHTMLGenerator.php index bf009ce0..81788bb8 100644 --- a/src/Services/LabelSystem/LabelHTMLGenerator.php +++ b/src/Services/LabelSystem/LabelHTMLGenerator.php @@ -29,18 +29,20 @@ class LabelHTMLGenerator { protected $twig; protected $elementTypeNameGenerator; + protected $replacer; - public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, Environment $twig) + public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, LabelTextReplacer $replacer, Environment $twig) { $this->twig = $twig; $this->elementTypeNameGenerator = $elementTypeNameGenerator; + $this->replacer = $replacer; } public function getLabelHTML(LabelOptions $options, object $element): string { return $this->twig->render('labels/base_label.html.twig', [ 'meta_title' => $this->getPDFTitle($options, $element), - 'lines' => $options->getLines(), + 'lines' => $this->replacer->replace($options->getLines(), $element), ]); } diff --git a/src/Services/LabelSystem/LabelTextReplacer.php b/src/Services/LabelSystem/LabelTextReplacer.php new file mode 100644 index 00000000..a254ee68 --- /dev/null +++ b/src/Services/LabelSystem/LabelTextReplacer.php @@ -0,0 +1,58 @@ +. + */ + +namespace App\Services\LabelSystem; + + +use App\Services\LabelSystem\PlaceholderProviders\PlaceholderProviderInterface; + +class LabelTextReplacer +{ + protected $providers; + + public function __construct(iterable $providers) + { + $this->providers = $providers; + } + + public function handlePlaceholder(string $placeholder, object $target): string + { + foreach ($this->providers as $provider) { + /** @var PlaceholderProviderInterface $provider */ + $ret = $provider->replace($placeholder, $target); + if ($ret !== null) { + return $ret; + } + } + + return $placeholder; + } + + public function replace(string $lines, object $target): string + { + $patterns = [ + '/(%%.*%%)/' => function ($match) use ($target) { + return $this->handlePlaceholder($match[0], $target); + }, + ]; + + return preg_replace_callback_array($patterns, $lines); + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php new file mode 100644 index 00000000..50467915 --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/AbstractDBElementProvider.php @@ -0,0 +1,55 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + + +use App\Entity\Base\AbstractDBElement; +use App\Services\ElementTypeNameGenerator; + +class AbstractDBElementProvider implements PlaceholderProviderInterface +{ + protected $elementTypeNameGenerator; + + public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator) + { + $this->elementTypeNameGenerator = $elementTypeNameGenerator; + } + + /** + * @inheritDoc + */ + public function replace(string $placeholder, object $label_target, array $options = []): ?string + { + if ($label_target instanceof AbstractDBElement) { + + if ($placeholder === '%%TYPE%%') { + return $this->elementTypeNameGenerator->getLocalizedTypeLabel($label_target); + } + + if ($placeholder === '%%ID%%') { + return (string) ($label_target->getID() ?? 'unknown'); + } + + } + + return null; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php b/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php new file mode 100644 index 00000000..8f4ba7ae --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/GlobalProviders.php @@ -0,0 +1,107 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + + +use App\Entity\UserSystem\User; +use IntlDateFormatter; +use Locale; +use Symfony\Component\Security\Core\Security; + +/** + * Provides Placeholders for infos about global infos like Installation name or datetimes. + * @package App\Services\LabelSystem\PlaceholderProviders + */ +class GlobalProviders implements PlaceholderProviderInterface +{ + + protected $partdb_title; + protected $security; + + public function __construct(string $partdb_title, Security $security) + { + $this->partdb_title = $partdb_title; + $this->security = $security; + } + + /** + * @inheritDoc + */ + public function replace(string $placeholder, object $label_target, array $options = []): ?string + { + if ($placeholder === "%%INSTALL_NAME%%") { + return $this->partdb_title; + } + + + $user = $this->security->getUser(); + if ($placeholder === "%%USERNAME%%") { + if ($user instanceof User) { + return $user->getName(); + } + return 'anonymous'; + } + + if ($placeholder === "%%USERNAME_FULL%%") { + if ($user instanceof User) { + return $user->getFullName(true); + } + return 'anonymous'; + } + + $now = new \DateTime(); + + if ($placeholder === '%%DATETIME%%') { + $formatter = IntlDateFormatter::create( + Locale::getDefault(), + IntlDateFormatter::SHORT, + IntlDateFormatter::SHORT, + $now->getTimezone() + ); + + return $formatter->format($now); + } + + if ($placeholder === '%%DATE%%') { + $formatter = IntlDateFormatter::create( + Locale::getDefault(), + IntlDateFormatter::SHORT, + IntlDateFormatter::NONE, + $now->getTimezone() + ); + + return $formatter->format($now); + } + + if ($placeholder === '%%TIME%%') { + $formatter = IntlDateFormatter::create( + Locale::getDefault(), + IntlDateFormatter::NONE, + IntlDateFormatter::SHORT, + $now->getTimezone() + ); + + return $formatter->format($now); + } + + return null; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php new file mode 100644 index 00000000..5acd461c --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/NamedElementProvider.php @@ -0,0 +1,40 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + + +use App\Entity\Contracts\NamedElementInterface; + +class NamedElementProvider implements PlaceholderProviderInterface +{ + + /** + * @inheritDoc + */ + public function replace(string $placeholder, object $label_target, array $options = []): ?string + { + if ($label_target instanceof NamedElementInterface && $placeholder === '%%NAME%%') { + return $label_target->getName(); + } + + return null; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php b/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php new file mode 100644 index 00000000..da8e1d14 --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/PartProvider.php @@ -0,0 +1,112 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + + +use App\Entity\Parts\Part; +use App\Services\SIFormatter; +use Symfony\Contracts\Translation\TranslatorInterface; + +class PartProvider implements PlaceholderProviderInterface +{ + + protected $siFormatter; + protected $translator; + + public function __construct(SIFormatter $SIFormatter, TranslatorInterface $translator) + { + $this->siFormatter = $SIFormatter; + $this->translator = $translator; + } + + /** + * @inheritDoc + */ + public function replace(string $placeholder, object $part, array $options = []): ?string + { + if (!$part instanceof Part) { + return null; + } + + if ($placeholder === '%%CATEGORY%%') { + return $part->getCategory() ? $part->getCategory()->getName() : ''; + } + + if ($placeholder === '%%CATEGORY_FULL%%') { + return $part->getCategory() ? $part->getCategory()->getFullPath() : ''; + } + + if ($placeholder === '%%MANUFACTURER%%') { + return $part->getManufacturer() ? $part->getManufacturer()->getName() : ''; + } + + if ($placeholder === '%%MANUFACTURER_FULL%%') { + return $part->getManufacturer() ? $part->getManufacturer()->getFullPath() : ''; + } + + if ($placeholder === '%%FOOTPRINT%%') { + return $part->getFootprint() ? $part->getFootprint()->getName() : ''; + } + + if ($placeholder === '%%FOOTPRINT_FULL%%') { + return $part->getFootprint() ? $part->getFootprint()->getFullPath() : ''; + } + + if ($placeholder === '%%MASS%%') { + return $part->getMass() ? $this->siFormatter->format($part->getMass(), 'g', 1) : ''; + } + + if ($placeholder === '%%MPN%%') { + return $part->getManufacturerProductNumber(); + } + + if ($placeholder === '%%TAGS%%') { + return $part->getTags(); + } + + if ($placeholder === '%%M_STATUS%%') { + if ($part->getManufacturingStatus() === '') { + return ''; + } + return $this->translator->trans('m_status.' . $part->getManufacturingStatus()); + } + + $parsedown = new \Parsedown(); + + if ($placeholder === '%%DESCRIPTION%%') { + return $parsedown->line($part->getDescription()); + } + + if ($placeholder === '%%DESCRIPTION_T%%') { + return strip_tags($parsedown->line($part->getDescription())); + } + + if ($placeholder === '%%COMMENT%%') { + return $parsedown->line($part->getComment()); + } + + if ($placeholder === '%%COMMENT_T%%') { + return strip_tags($parsedown->line($part->getComment())); + } + + return null; + } +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/PlaceholderProviderInterface.php b/src/Services/LabelSystem/PlaceholderProviders/PlaceholderProviderInterface.php new file mode 100644 index 00000000..4bf62049 --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/PlaceholderProviderInterface.php @@ -0,0 +1,36 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + + +interface PlaceholderProviderInterface +{ + + /** + * Determines the real value of this placeholder. + * If the placeholder is not supported, null must be returned. + * @param string $placeholder The placeholder (e.g. "%%PLACEHOLDER%%") that should be replaced + * @param object $label_target The object that is targeted by the label + * @param array $options A list of options that can be used to specify the generated output further. + * @return string|null The real value of this placeholder, null if not supported. + */ + public function replace(string $placeholder, object $label_target, array $options = []): ?string; +} \ No newline at end of file diff --git a/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php b/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php new file mode 100644 index 00000000..aa2a50ee --- /dev/null +++ b/src/Services/LabelSystem/PlaceholderProviders/TimestampableElementProvider.php @@ -0,0 +1,48 @@ +. + */ + +namespace App\Services\LabelSystem\PlaceholderProviders; + +use App\Entity\Contracts\TimeStampableInterface; +use IntlDateFormatter; +use Locale; + +class TimestampableElementProvider implements PlaceholderProviderInterface +{ + + /** + * @inheritDoc + */ + public function replace(string $placeholder, object $label_target, array $options = []): ?string + { + if ($label_target instanceof TimeStampableInterface) { + if ($placeholder === '%%LAST_MODIFIED%%') { + return IntlDateFormatter::formatObject($label_target->getLastModified() ?? new \DateTime(), IntlDateFormatter::SHORT, Locale::getDefault()); + } + + if ($placeholder === '%%CREATION_DATE%%') { + return IntlDateFormatter::formatObject($label_target->getAddedDate() ?? new \DateTime(), IntlDateFormatter::SHORT, Locale::getDefault()); + } + + } + + return null; + } +} \ No newline at end of file diff --git a/src/Services/SIFormatter.php b/src/Services/SIFormatter.php index 7dbbae58..0a736d84 100644 --- a/src/Services/SIFormatter.php +++ b/src/Services/SIFormatter.php @@ -105,6 +105,7 @@ class SIFormatter * @param float $value The value that should be converted * @param string $unit The unit that should be appended after the prefix * @param int $decimals the number of decimals (after decimal dot) that should be outputed + * @return string The formatted value */ public function format(float $value, string $unit = '', int $decimals = 2): string { diff --git a/symfony.lock b/symfony.lock index 214f109d..93d0255d 100644 --- a/symfony.lock +++ b/symfony.lock @@ -129,6 +129,9 @@ "ekino/phpstan-banned-code": { "version": "v0.3.1" }, + "erusev/parsedown": { + "version": "1.7.4" + }, "felixfbecker/advanced-json-rpc": { "version": "v3.0.4" },