diff --git a/src/Services/FAIconGenerator.php b/src/Services/FAIconGenerator.php index afbcfbba..d759004f 100644 --- a/src/Services/FAIconGenerator.php +++ b/src/Services/FAIconGenerator.php @@ -97,7 +97,7 @@ class FAIconGenerator * * @return string The final html */ - public function generateIconHTML(string $icon_class, string $style = 'fas', string $options = ''): string + public function generateIconHTML(string $icon_class, string $style = 'fa-solid', string $options = ''): string { //XSS protection $icon_class = htmlspecialchars($icon_class); diff --git a/src/Services/LabelSystem/SandboxedTwigProvider.php b/src/Services/LabelSystem/SandboxedTwigProvider.php index ded552d3..574e8533 100644 --- a/src/Services/LabelSystem/SandboxedTwigProvider.php +++ b/src/Services/LabelSystem/SandboxedTwigProvider.php @@ -41,7 +41,7 @@ use App\Entity\PriceInformations\Currency; use App\Entity\PriceInformations\Orderdetail; use App\Entity\PriceInformations\Pricedetail; use App\Entity\UserSystem\User; -use App\Twig\AppExtension; +use App\Twig\FormatExtension; use App\Twig\Sandbox\InheritanceSecurityPolicy; use InvalidArgumentException; use Twig\Environment; @@ -98,7 +98,7 @@ final class SandboxedTwigProvider private $appExtension; - public function __construct(AppExtension $appExtension) + public function __construct(FormatExtension $appExtension) { $this->appExtension = $appExtension; } diff --git a/src/Twig/AttachmentExtension.php b/src/Twig/AttachmentExtension.php new file mode 100644 index 00000000..ed120e85 --- /dev/null +++ b/src/Twig/AttachmentExtension.php @@ -0,0 +1,30 @@ +attachmentURLGenerator = $attachmentURLGenerator; + $this->FAIconGenerator = $FAIconGenerator; + } + + public function getFunctions() + { + return [ + /* Returns the URL to a thumbnail of the given attachment */ + new TwigFunction('attachment_thumbnail', [$this->attachmentURLGenerator, 'getThumbnailURL']), + /* Returns the font awesome icon class which is representing the given file extension */ + new TwigFunction('ext_to_fa_icon', [$this->FAIconGenerator, 'fileExtensionToFAType']), + ]; + } +} \ No newline at end of file diff --git a/src/Twig/BarcodeExtension.php b/src/Twig/BarcodeExtension.php index b61b874e..b074473d 100644 --- a/src/Twig/BarcodeExtension.php +++ b/src/Twig/BarcodeExtension.php @@ -23,18 +23,21 @@ namespace App\Twig; use Com\Tecnick\Barcode\Barcode; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; +use Twig\TwigFunction; -class BarcodeExtension extends AbstractExtension +final class BarcodeExtension extends AbstractExtension { - public function getFilters(): array + public function getFunctions() { return [ - new TwigFilter('barcodeSVG', static function (string $content, string $type = 'QRCODE') { - $barcodeFactory = new Barcode(); - $barcode = $barcodeFactory->getBarcodeObj($type, $content); - - return $barcode->getSvgCode(); - }), + /* Generates a barcode with the given Type and Data and returns it as an SVG represenation */ + new TwigFunction('barcode_svg', [$this, 'barcodeSVG']), ]; } + + public function barcodeSVG(string $content, string $type): string + { + $barcodeFactory = new Barcode(); + return $barcodeFactory->getBarcodeObj($type, $content)->getSvgCode(); + } } diff --git a/src/Twig/EntityExtension.php b/src/Twig/EntityExtension.php index ca7b4fb7..a51d5783 100644 --- a/src/Twig/EntityExtension.php +++ b/src/Twig/EntityExtension.php @@ -16,26 +16,24 @@ use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\Group; use App\Entity\UserSystem\User; +use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; +use App\Services\Trees\TreeViewGenerator; use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; -class EntityExtension extends AbstractExtension +final class EntityExtension extends AbstractExtension { protected $entityURLGenerator; + protected $treeBuilder; + private $nameGenerator; - public function __construct(EntityURLGenerator $entityURLGenerator) + public function __construct(EntityURLGenerator $entityURLGenerator, TreeViewGenerator $treeBuilder, ElementTypeNameGenerator $elementTypeNameGenerator) { $this->entityURLGenerator = $entityURLGenerator; - } - - public function getFilters(): array - { - return [ - - ]; + $this->treeBuilder = $treeBuilder; + $this->nameGenerator = $elementTypeNameGenerator; } public function getTests(): array @@ -53,10 +51,23 @@ class EntityExtension extends AbstractExtension return [ /* Returns a string representation of the given entity */ new TwigFunction('entity_type', [$this, 'getEntityType']), + /* Returns the URL to the given entity */ new TwigFunction('entity_url', [$this, 'generateEntityURL']), + /* Generates a JSON array of the given tree */ + new TwigFunction('tree_data', [$this, 'treeData']), + + /* Gets a human readable label for the type of the given entity */ + new TwigFunction('entity_type_label', [$this->nameGenerator, 'getLocalizedTypeLabel']), ]; } + public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string + { + $tree = $this->treeBuilder->getTreeView(get_class($element), null, $type, $element); + + return json_encode($tree, JSON_THROW_ON_ERROR); + } + public function generateEntityURL(AbstractDBElement $entity, string $method = 'info'): string { return $this->entityURLGenerator->getURL($entity, $method); diff --git a/src/Twig/AppExtension.php b/src/Twig/FormatExtension.php similarity index 67% rename from src/Twig/AppExtension.php rename to src/Twig/FormatExtension.php index 0bbc6365..21a34106 100644 --- a/src/Twig/AppExtension.php +++ b/src/Twig/FormatExtension.php @@ -75,84 +75,40 @@ use Twig\TwigTest; use function get_class; -class AppExtension extends AbstractExtension +final class FormatExtension extends AbstractExtension { protected $markdownParser; - protected $serializer; - protected $treeBuilder; protected $moneyFormatter; protected $siformatter; protected $amountFormatter; - protected $attachmentURLGenerator; - protected $FAIconGenerator; - protected $translator; - public function __construct(MarkdownParser $markdownParser, - SerializerInterface $serializer, TreeViewGenerator $treeBuilder, - MoneyFormatter $moneyFormatter, - SIFormatter $SIFormatter, AmountFormatter $amountFormatter, - AttachmentURLGenerator $attachmentURLGenerator, - FAIconGenerator $FAIconGenerator, TranslatorInterface $translator) + + public function __construct(MarkdownParser $markdownParser, MoneyFormatter $moneyFormatter, + SIFormatter $SIFormatter, AmountFormatter $amountFormatter) { $this->markdownParser = $markdownParser; - $this->serializer = $serializer; - $this->treeBuilder = $treeBuilder; $this->moneyFormatter = $moneyFormatter; $this->siformatter = $SIFormatter; $this->amountFormatter = $amountFormatter; - $this->attachmentURLGenerator = $attachmentURLGenerator; - $this->FAIconGenerator = $FAIconGenerator; - $this->translator = $translator; } public function getFilters(): array { return [ - new TwigFilter('markdown', [$this->markdownParser, 'markForRendering'], [ + /* Mark the given text as markdown, which will be rendered in the browser */ + new TwigFilter('format_markdown', [$this->markdownParser, 'markForRendering'], [ 'pre_escape' => 'html', 'is_safe' => ['html'], ]), - new TwigFilter('moneyFormat', [$this, 'formatCurrency']), - new TwigFilter('siFormat', [$this, 'siFormat']), - new TwigFilter('amountFormat', [$this, 'amountFormat']), - new TwigFilter('loginPath', [$this, 'loginPath']), + /* Format the given amount as money, using a given currency */ + new TwigFilter('format_money', [$this, 'formatCurrency']), + /* Format the given number using SI prefixes and the given unit (string) */ + new TwigFilter('format_si', [$this, 'siFormat']), + /** Format the given amount using the given MeasurementUnit */ + new TwigFilter('format_amount', [$this, 'amountFormat']), ]; } - - - public function getFunctions(): array - { - return [ - new TwigFunction('generateTreeData', [$this, 'treeData']), - new TwigFunction('attachment_thumbnail', [$this->attachmentURLGenerator, 'getThumbnailURL']), - new TwigFunction('ext_to_fa_icon', [$this->FAIconGenerator, 'fileExtensionToFAType']), - ]; - } - - public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string - { - $tree = $this->treeBuilder->getTreeView(get_class($element), null, $type, $element); - - return json_encode($tree, JSON_THROW_ON_ERROR); - } - - - - /** - * This function/filter generates an path. - */ - public function loginPath(string $path): string - { - $parts = explode('/', $path); - //Remove the part with - unset($parts[1]); - - return implode('/', $parts); - } - - - public function formatCurrency($amount, ?Currency $currency = null, int $decimals = 5): string { if ($amount instanceof BigDecimal) { diff --git a/src/Twig/TwigCoreExtension.php b/src/Twig/TwigCoreExtension.php index 04f9f20f..60495b7a 100644 --- a/src/Twig/TwigCoreExtension.php +++ b/src/Twig/TwigCoreExtension.php @@ -11,7 +11,7 @@ use Twig\TwigTest; /** * The functionalities here extend the Twig with some core functions, which are independently of Part-DB. */ -class TwigCoreExtension extends AbstractExtension +final class TwigCoreExtension extends AbstractExtension { protected $objectNormalizer; diff --git a/src/Twig/TypeLabelExtension.php b/src/Twig/TypeLabelExtension.php deleted file mode 100644 index 4c4ce5d6..00000000 --- a/src/Twig/TypeLabelExtension.php +++ /dev/null @@ -1,25 +0,0 @@ -nameGenerator = $elementTypeNameGenerator; - } - - public function getFunctions(): array - { - return [ - new TwigFunction('elementType', [$this->nameGenerator, 'getLocalizedTypeLabel']), - new TwigFunction('elementTypeName', [$this->nameGenerator, 'getTypeNameCombination']), - ]; - } -} diff --git a/src/Twig/UserExtension.php b/src/Twig/UserExtension.php index b6e051eb..723f2ced 100644 --- a/src/Twig/UserExtension.php +++ b/src/Twig/UserExtension.php @@ -27,9 +27,10 @@ use App\Entity\LogSystem\AbstractLogEntry; use App\Repository\LogEntryRepository; use Doctrine\ORM\EntityManagerInterface; use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; use Twig\TwigFunction; -class UserExtension extends AbstractExtension +final class UserExtension extends AbstractExtension { /** @var LogEntryRepository */ private $repo; @@ -39,6 +40,13 @@ class UserExtension extends AbstractExtension $this->repo = $em->getRepository(AbstractLogEntry::class); } + public function getFilters(): array + { + return [ + new TwigFilter('remove_locale_from_path', [$this, 'removeLocaleFromPath']), + ]; + } + public function getFunctions(): array { return [ @@ -48,4 +56,22 @@ class UserExtension extends AbstractExtension new TwigFunction('creating_user', [$this->repo, 'getCreatingUser']), ]; } + + /** + * This function/filter generates an path. + */ + public function removeLocaleFromPath(string $path): string + { + //Ensure the path has the correct format + if (!preg_match('/^\/\w{2}\//', $path)) { + throw new \InvalidArgumentException('The given path is not a localized path!'); + } + + $parts = explode('/', $path); + //Remove the part with locale + unset($parts[1]); + + return implode('/', $parts); + } + } diff --git a/templates/Parts/info/_extended_infos.html.twig b/templates/Parts/info/_extended_infos.html.twig index a9d45469..8fdc18e1 100644 --- a/templates/Parts/info/_extended_infos.html.twig +++ b/templates/Parts/info/_extended_infos.html.twig @@ -45,7 +45,7 @@ {% trans %}part.minOrderAmount{% endtrans %} {% if pricedetail_helper.minOrderAmount(part) %} - {{ pricedetail_helper.minOrderAmount(part) | amountFormat(part.partUnit) }} + {{ pricedetail_helper.minOrderAmount(part) | format_amount(part.partUnit) }} {% else %} {% trans %}Unknown{% endtrans %} {% endif %} diff --git a/templates/Parts/info/_main_infos.html.twig b/templates/Parts/info/_main_infos.html.twig index 0b42cd4d..158aa95b 100644 --- a/templates/Parts/info/_main_infos.html.twig +++ b/templates/Parts/info/_main_infos.html.twig @@ -29,16 +29,16 @@ {% endif %} -
{{ part.description|markdown(true) }}
+
{{ part.description|format_markdown(true) }}
{{ helper.structural_entity_link(part.category) }}
- {{ part.amountSum | amountFormat(part.partUnit) }} + {{ part.amountSum | format_amount(part.partUnit) }} / - {{ part.minAmount | amountFormat(part.partUnit) }} + {{ part.minAmount | format_amount(part.partUnit) }}
@@ -53,10 +53,10 @@
- {{ max_order_price | moneyFormat(app.user.currency ?? null) }} + {{ max_order_price | format_money(app.user.currency ?? null) }} {% if min_order_amount < max_order_amount %} - - {{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | moneyFormat(app.user.currency ?? null) }} + {{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | format_money(app.user.currency ?? null) }} {% endif %}
diff --git a/templates/Parts/info/_order_infos.html.twig b/templates/Parts/info/_order_infos.html.twig index 23965f72..716bc179 100644 --- a/templates/Parts/info/_order_infos.html.twig +++ b/templates/Parts/info/_order_infos.html.twig @@ -40,20 +40,20 @@ - {{ detail.MinDiscountQuantity | amountFormat(part.partUnit) }} + {{ detail.MinDiscountQuantity | format_amount(part.partUnit) }} - {{ detail.price | moneyFormat(detail.currency) }} / {{ detail.PriceRelatedQuantity | amountFormat(part.partUnit) }} + {{ detail.price | format_money(detail.currency) }} / {{ detail.PriceRelatedQuantity | format_amount(part.partUnit) }} {% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency) %} {% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %} - ({{ pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) | moneyFormat(app.user.currency ?? null) }}) + ({{ pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }}) {% endif %} - {{ detail.PricePerUnit | moneyFormat(detail.currency) }} + {{ detail.PricePerUnit | format_money(detail.currency) }} {% set tmp = pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency) %} {% if detail.currency != (app.user.currency ?? null) and tmp is not null and tmp.GreaterThan(0) %} - ({{ pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) | moneyFormat(app.user.currency ?? null) }}) + ({{ pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }}) {% endif %} diff --git a/templates/Parts/info/_part_lots.html.twig b/templates/Parts/info/_part_lots.html.twig index 48a455ee..c28dc2ac 100644 --- a/templates/Parts/info/_part_lots.html.twig +++ b/templates/Parts/info/_part_lots.html.twig @@ -31,7 +31,7 @@ {% trans %}part_lots.instock_unknown{% endtrans %} {% else %} - {{ lot.amount | amountFormat(part.partUnit, {'decimals': 5}) }} + {{ lot.amount | format_amount(part.partUnit, {'decimals': 5}) }} {% endif %} diff --git a/templates/Parts/info/_picture.html.twig b/templates/Parts/info/_picture.html.twig index f4645e9c..29ea936e 100644 --- a/templates/Parts/info/_picture.html.twig +++ b/templates/Parts/info/_picture.html.twig @@ -18,7 +18,7 @@ diff --git a/templates/Parts/info/_sidebar.html.twig b/templates/Parts/info/_sidebar.html.twig index 2f4d61bc..65a7d576 100644 --- a/templates/Parts/info/_sidebar.html.twig +++ b/templates/Parts/info/_sidebar.html.twig @@ -18,7 +18,7 @@ {% if part.mass %}
- {{ part.mass | siFormat("g") }} + {{ part.mass | format_si("g") }}
{% endif %} diff --git a/templates/Parts/info/show_part_info.html.twig b/templates/Parts/info/show_part_info.html.twig index 57131725..649e690b 100644 --- a/templates/Parts/info/show_part_info.html.twig +++ b/templates/Parts/info/show_part_info.html.twig @@ -109,7 +109,7 @@ {% if part.comment is not empty %}
- {{ part.comment|markdown }} + {{ part.comment|format_markdown }}
{% endif %} diff --git a/templates/Parts/lists/_info_card.html.twig b/templates/Parts/lists/_info_card.html.twig index 116b6152..dc5cc370 100644 --- a/templates/Parts/lists/_info_card.html.twig +++ b/templates/Parts/lists/_info_card.html.twig @@ -122,7 +122,7 @@ {% if entity.comment is not empty %}
- {{ entity.comment|markdown }} + {{ entity.comment|format_markdown }}
{% endif %} diff --git a/templates/Users/_2fa_settings.html.twig b/templates/Users/_2fa_settings.html.twig index a93149c9..fc621fc7 100644 --- a/templates/Users/_2fa_settings.html.twig +++ b/templates/Users/_2fa_settings.html.twig @@ -41,7 +41,7 @@
- {{ tfa_google.qrContent }} + {{ tfa_google.qrContent }}
    diff --git a/templates/_navbar.html.twig b/templates/_navbar.html.twig index a3779054..972db282 100644 --- a/templates/_navbar.html.twig +++ b/templates/_navbar.html.twig @@ -53,7 +53,7 @@ aria-hidden="true"> {% trans %}user.logout{% endtrans %} {% else %} {% trans %}user.login{% endtrans %} {% endif %} diff --git a/templates/components/tree_macros.html.twig b/templates/components/tree_macros.html.twig index 395769e2..9cb8bdc7 100644 --- a/templates/components/tree_macros.html.twig +++ b/templates/components/tree_macros.html.twig @@ -40,7 +40,7 @@ {% endmacro %} {% macro treeview(entity) %} -
    +
    diff --git a/templates/homepage.html.twig b/templates/homepage.html.twig index dcc7ebbe..1015d4b8 100644 --- a/templates/homepage.html.twig +++ b/templates/homepage.html.twig @@ -12,7 +12,7 @@ {% if banner is not empty %}
    -
    {{ banner | markdown }}
    +
    {{ banner | format_markdown }}
    {% endif %}
    diff --git a/tests/Twig/UserExtensionTest.php b/tests/Twig/UserExtensionTest.php new file mode 100644 index 00000000..cabbfda2 --- /dev/null +++ b/tests/Twig/UserExtensionTest.php @@ -0,0 +1,43 @@ +service = self::getContainer()->get(UserExtension::class); + } + + public function removeeLocaleFromPathDataSet() + { + yield ['/', '/de/']; + yield ['/test', '/de/test']; + yield ['/test/foo', '/en/test/foo']; + yield ['/test/foo/bar?param1=val1¶m2=val2', '/en/test/foo/bar?param1=val1¶m2=val2']; + } + + /** + * @dataProvider removeeLocaleFromPathDataSet + * @param string $expected + * @param string $input + * @return void + */ + public function testRemoveLocaleFromPath(string $expected, string $input): void + { + $this->assertEquals($expected, $this->service->removeLocaleFromPath($input)); + } + + public function testRemoveLocaleFromPathException(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->service->removeLocaleFromPath('/part/info/1'); + } +}