mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +02:00
Refactored TwigExtensions Part 2
This commit is contained in:
parent
b078389381
commit
a9e527ce2a
22 changed files with 168 additions and 124 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
30
src/Twig/AttachmentExtension.php
Normal file
30
src/Twig/AttachmentExtension.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use App\Services\FAIconGenerator;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class AttachmentExtension extends AbstractExtension
|
||||
{
|
||||
protected $attachmentURLGenerator;
|
||||
protected $FAIconGenerator;
|
||||
|
||||
public function __construct(AttachmentURLGenerator $attachmentURLGenerator, FAIconGenerator $FAIconGenerator)
|
||||
{
|
||||
$this->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']),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class TypeLabelExtension extends AbstractExtension
|
||||
{
|
||||
private $nameGenerator;
|
||||
|
||||
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator)
|
||||
{
|
||||
$this->nameGenerator = $elementTypeNameGenerator;
|
||||
}
|
||||
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('elementType', [$this->nameGenerator, 'getLocalizedTypeLabel']),
|
||||
new TwigFunction('elementTypeName', [$this->nameGenerator, 'getTypeNameCombination']),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<tr>
|
||||
<td>{% trans %}part.minOrderAmount{% endtrans %}</td>
|
||||
<td>{% 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 %}
|
||||
|
|
|
@ -29,16 +29,16 @@
|
|||
<a href="{{ entity_url(part, 'edit') }}"><i class="fas fa-fw fa-sm fa-edit"></i></a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<h6 class="text-muted w-fit" title="{% trans %}description.label{% endtrans %}"><span>{{ part.description|markdown(true) }}</span></h6>
|
||||
<h6 class="text-muted w-fit" title="{% trans %}description.label{% endtrans %}"><span>{{ part.description|format_markdown(true) }}</span></h6>
|
||||
<h6 class="">
|
||||
<i class="fas fa-tag fa-fw" title="{% trans %}category.label{% endtrans %}"></i>
|
||||
<span class="text-muted">{{ helper.structural_entity_link(part.category) }}</span>
|
||||
</h6>
|
||||
<h6><i class="fas fa-shapes fa-fw"></i>
|
||||
<span class="text-muted">
|
||||
<span title="{% trans %}instock.label{% endtrans %}">{{ part.amountSum | amountFormat(part.partUnit) }}</span>
|
||||
<span title="{% trans %}instock.label{% endtrans %}">{{ part.amountSum | format_amount(part.partUnit) }}</span>
|
||||
/
|
||||
<span title="{% trans %}mininstock.label{% endtrans %}">{{ part.minAmount | amountFormat(part.partUnit) }}</span>
|
||||
<span title="{% trans %}mininstock.label{% endtrans %}">{{ part.minAmount | format_amount(part.partUnit) }}</span>
|
||||
</span>
|
||||
</h6>
|
||||
<h6 class="">
|
||||
|
@ -53,10 +53,10 @@
|
|||
<h6>
|
||||
<i class="fas fa-money-bill-alt fa-fw"></i>
|
||||
<span class="text-muted">
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ max_order_amount | amountFormat(part.partUnit) }}">{{ max_order_price | moneyFormat(app.user.currency ?? null) }}</span>
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ max_order_amount | format_amount(part.partUnit) }}">{{ max_order_price | format_money(app.user.currency ?? null) }}</span>
|
||||
{% if min_order_amount < max_order_amount %}
|
||||
<span> - </span>
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ min_order_amount | amountFormat(part.partUnit) }}">{{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | moneyFormat(app.user.currency ?? null) }}</span>
|
||||
<span title="{% trans %}part.avg_price.label{% endtrans %} {{ min_order_amount | format_amount(part.partUnit) }}">{{pricedetail_helper.calculateAvgPrice(part, min_order_amount, app.user.currency ?? null ) | format_money(app.user.currency ?? null) }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</h6>
|
||||
|
|
|
@ -40,20 +40,20 @@
|
|||
<tr>
|
||||
|
||||
<td>
|
||||
{{ detail.MinDiscountQuantity | amountFormat(part.partUnit) }}
|
||||
{{ detail.MinDiscountQuantity | format_amount(part.partUnit) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ 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) %}
|
||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) | moneyFormat(app.user.currency ?? null) }})</span>
|
||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.price, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }})</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ 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) %}
|
||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) | moneyFormat(app.user.currency ?? null) }})</span>
|
||||
<span class="text-muted">({{ pricedetail_helper.convertMoneyToCurrency(detail.PricePerUnit, detail.currency, app.user.currency ?? null) | format_money(app.user.currency ?? null) }})</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<i class="fas fa-question-circle fa-fw"></i> {% trans %}part_lots.instock_unknown{% endtrans %}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ lot.amount | amountFormat(part.partUnit, {'decimals': 5}) }}
|
||||
{{ lot.amount | format_amount(part.partUnit, {'decimals': 5}) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<div class="carousel-caption">
|
||||
<div><b>{{ pic.name }}</b></div>
|
||||
<div>{% if pic.filename %}({{ pic.filename }}) {% endif %}</div>
|
||||
<div>{{ elementTypeName(pic.element) }}</div>
|
||||
<div>{{ entity_type_label(pic.element) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
{% if part.mass %}
|
||||
<div>
|
||||
<h6>
|
||||
<span class="badge bg-secondary" title="{% trans %}part.mass.tooltip{% endtrans %}"><i class="fas fa-weight-hanging fa-fw"></i> {{ part.mass | siFormat("g") }}</span>
|
||||
<span class="badge bg-secondary" title="{% trans %}part.mass.tooltip{% endtrans %}"><i class="fas fa-weight-hanging fa-fw"></i> {{ part.mass | format_si("g") }}</span>
|
||||
</h6>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
{% if part.comment is not empty %}
|
||||
<div class="tab-pane fade show" id="comment" role="tabpanel" aria-labelledby="home-tab">
|
||||
<div class="container-fluid mt-2 latex" data-controller="common--latex">
|
||||
{{ part.comment|markdown }}
|
||||
{{ part.comment|format_markdown }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
{% if entity.comment is not empty %}
|
||||
<div class="tab-pane fade" id="v-pills-comment" role="tabpanel" aria-labelledby="home-tab">
|
||||
<div class="container-fluid mt-2 latex" data-controller="common--latex">
|
||||
{{ entity.comment|markdown }}
|
||||
{{ entity.comment|format_markdown }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<div class="offset-sm-3 row">
|
||||
<div class="col-sm-3">
|
||||
<img width="100%" class="img-fluid" alt="{{ tfa_google.qrContent }}" src="{{ tfa_google.qrContent | barcodeSVG | data_uri("image/svg+xml") }}">
|
||||
<img width="100%" class="img-fluid" alt="{{ tfa_google.qrContent }}" src="{{ barcode_svg(tfa_google.qrContent) | data_uri("image/svg+xml") }}">
|
||||
</div>
|
||||
<div class="col-sm-9 my-auto">
|
||||
<ol class="">
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
aria-hidden="true"></i> {% trans %}user.logout{% endtrans %}</a>
|
||||
{% else %}
|
||||
<a class="dropdown-item"
|
||||
href="{{ path('login', {'_target_path': app.request.pathinfo | loginPath}) }}"
|
||||
href="{{ path('login', {'_target_path': app.request.pathinfo | remove_locale_from_path}) }}"
|
||||
id="login-link"><i class="fa fa-sign-in-alt fa-fw"
|
||||
aria-hidden="true"></i> {% trans %}user.login{% endtrans %}</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
{% endmacro %}
|
||||
|
||||
{% macro treeview(entity) %}
|
||||
<div {{ stimulus_controller('elements/tree') }} data-tree-data="{{ generateTreeData(entity) }}" data-tree-show-tags="true">
|
||||
<div {{ stimulus_controller('elements/tree') }} data-tree-data="{{ tree_data(entity) }}" data-tree-show-tags="true">
|
||||
<div class="row" >
|
||||
<div class="col-8">
|
||||
<input type="search" class="form-control" placeholder="{% trans %}search.placeholder{% endtrans %}" {{ stimulus_action('elements/tree', 'searchInput') }}>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
{% if banner is not empty %}
|
||||
<hr>
|
||||
<div class="latex" data-controller="common--latex">
|
||||
<h5>{{ banner | markdown }}</h5>
|
||||
<h5>{{ banner | format_markdown }}</h5>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
43
tests/Twig/UserExtensionTest.php
Normal file
43
tests/Twig/UserExtensionTest.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Tests\Twig;
|
||||
|
||||
use App\Twig\UserExtension;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class UserExtensionTest extends WebTestCase
|
||||
{
|
||||
protected $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->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');
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue