mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-15 22:19:35 +00:00
Moved remaining twig extensions to new attributes system
This commit is contained in:
parent
f69b0889eb
commit
1996db6a53
12 changed files with 212 additions and 168 deletions
|
|
@ -32,7 +32,7 @@ class LogDiffFormatter
|
||||||
* @param $old_data
|
* @param $old_data
|
||||||
* @param $new_data
|
* @param $new_data
|
||||||
*/
|
*/
|
||||||
public function formatDiff($old_data, $new_data): string
|
public function formatDiff(mixed $old_data, mixed $new_data): string
|
||||||
{
|
{
|
||||||
if (is_string($old_data) && is_string($new_data)) {
|
if (is_string($old_data) && is_string($new_data)) {
|
||||||
return $this->diffString($old_data, $new_data);
|
return $this->diffString($old_data, $new_data);
|
||||||
|
|
|
||||||
|
|
@ -25,22 +25,34 @@ namespace App\Twig;
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
use App\Services\Attachments\AttachmentURLGenerator;
|
use App\Services\Attachments\AttachmentURLGenerator;
|
||||||
use App\Services\Misc\FAIconGenerator;
|
use App\Services\Misc\FAIconGenerator;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
final class AttachmentExtension extends AbstractExtension
|
final readonly class AttachmentExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected AttachmentURLGenerator $attachmentURLGenerator, protected FAIconGenerator $FAIconGenerator)
|
public function __construct(private AttachmentURLGenerator $attachmentURLGenerator, private FAIconGenerator $FAIconGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
/**
|
||||||
|
* Returns the URL of the thumbnail of the given attachment. Returns null if no thumbnail is available.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("attachment_thumbnail")]
|
||||||
|
public function attachmentThumbnail(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string
|
||||||
{
|
{
|
||||||
return [
|
return $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name);
|
||||||
/* Returns the URL to a thumbnail of the given attachment */
|
}
|
||||||
new TwigFunction('attachment_thumbnail', fn(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string => $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name)),
|
|
||||||
/* Returns the font awesome icon class which is representing the given file extension (We allow null here for attachments without extension) */
|
/**
|
||||||
new TwigFunction('ext_to_fa_icon', fn(?string $extension): string => $this->FAIconGenerator->fileExtensionToFAType($extension ?? '')),
|
* Return the font-awesome icon type for the given file extension. Returns "file" if no specific icon is available.
|
||||||
];
|
* Null is allowed for files withot extension
|
||||||
|
* @param string|null $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("ext_to_fa_icon")]
|
||||||
|
public function extToFAIcon(?string $extension): string
|
||||||
|
{
|
||||||
|
return $this->FAIconGenerator->fileExtensionToFAType($extension ?? '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,19 +23,14 @@ declare(strict_types=1);
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use Com\Tecnick\Barcode\Barcode;
|
use Com\Tecnick\Barcode\Barcode;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\TwigFunction;
|
|
||||||
|
|
||||||
final class BarcodeExtension extends AbstractExtension
|
final class BarcodeExtension
|
||||||
{
|
{
|
||||||
public function getFunctions(): array
|
/**
|
||||||
{
|
* Generates a barcode in SVG format for the given content and type.
|
||||||
return [
|
*/
|
||||||
/* Generates a barcode with the given Type and Data and returns it as an SVG represenation */
|
#[AsTwigFunction('barcode_svg')]
|
||||||
new TwigFunction('barcode_svg', fn(string $content, string $type = 'QRCODE'): string => $this->barcodeSVG($content, $type)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function barcodeSVG(string $content, string $type = 'QRCODE'): string
|
public function barcodeSVG(string $content, string $type = 'QRCODE'): string
|
||||||
{
|
{
|
||||||
$barcodeFactory = new Barcode();
|
$barcodeFactory = new Barcode();
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@ use App\Exceptions\EntityNotSupportedException;
|
||||||
use App\Services\ElementTypeNameGenerator;
|
use App\Services\ElementTypeNameGenerator;
|
||||||
use App\Services\EntityURLGenerator;
|
use App\Services\EntityURLGenerator;
|
||||||
use App\Services\Trees\TreeViewGenerator;
|
use App\Services\Trees\TreeViewGenerator;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
|
use Twig\Attribute\AsTwigTest;
|
||||||
|
use Twig\DeprecatedCallableInfo;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
use Twig\TwigTest;
|
use Twig\TwigTest;
|
||||||
|
|
@ -48,61 +51,27 @@ use Twig\TwigTest;
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Twig\EntityExtensionTest
|
* @see \App\Tests\Twig\EntityExtensionTest
|
||||||
*/
|
*/
|
||||||
final class EntityExtension extends AbstractExtension
|
final readonly class EntityExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected EntityURLGenerator $entityURLGenerator, protected TreeViewGenerator $treeBuilder, private readonly ElementTypeNameGenerator $nameGenerator)
|
public function __construct(private EntityURLGenerator $entityURLGenerator, private TreeViewGenerator $treeBuilder, private ElementTypeNameGenerator $nameGenerator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTests(): array
|
/**
|
||||||
|
* Checks if the given variable is an entity (instance of AbstractDBElement).
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("entity")]
|
||||||
|
public function entityTest(mixed $var): bool
|
||||||
{
|
{
|
||||||
return [
|
return $var instanceof AbstractDBElement;
|
||||||
/* Checks if the given variable is an entitity (instance of AbstractDBElement) */
|
|
||||||
new TwigTest('entity', static fn($var) => $var instanceof AbstractDBElement),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
/* Returns a string representation of the given entity */
|
|
||||||
new TwigFunction('entity_type', fn(object $entity): ?string => $this->getEntityType($entity)),
|
|
||||||
/* Returns the URL to the given entity */
|
|
||||||
new TwigFunction('entity_url', fn(AbstractDBElement $entity, string $method = 'info'): string => $this->generateEntityURL($entity, $method)),
|
|
||||||
/* Returns the URL to the given entity in timetravel mode */
|
|
||||||
new TwigFunction('timetravel_url', fn(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string => $this->timeTravelURL($element, $dateTime)),
|
|
||||||
/* Generates a JSON array of the given tree */
|
|
||||||
new TwigFunction('tree_data', fn(AbstractDBElement $element, string $type = 'newEdit'): string => $this->treeData($element, $type)),
|
|
||||||
|
|
||||||
/* Gets a human readable label for the type of the given entity */
|
/**
|
||||||
new TwigFunction('entity_type_label', fn(object|string $entity): string => $this->nameGenerator->getLocalizedTypeLabel($entity)),
|
* Returns a string representation of the given entity
|
||||||
new TwigFunction('type_label', fn(object|string $entity): string => $this->nameGenerator->typeLabel($entity)),
|
*/
|
||||||
new TwigFunction('type_label_p', fn(object|string $entity): string => $this->nameGenerator->typeLabelPlural($entity)),
|
#[AsTwigFunction("entity_type")]
|
||||||
];
|
public function entityType(object $entity): ?string
|
||||||
}
|
|
||||||
|
|
||||||
public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return $this->entityURLGenerator->timeTravelURL($element, $dateTime);
|
|
||||||
} catch (EntityNotSupportedException) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string
|
|
||||||
{
|
|
||||||
$tree = $this->treeBuilder->getTreeView($element::class, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEntityType(object $entity): ?string
|
|
||||||
{
|
{
|
||||||
$map = [
|
$map = [
|
||||||
Part::class => 'part',
|
Part::class => 'part',
|
||||||
|
|
@ -129,4 +98,69 @@ final class EntityExtension extends AbstractExtension
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL for the given entity and method. E.g. for a Part and method "edit", it will return the URL to edit this part.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("entity_url")]
|
||||||
|
public function entityURL(AbstractDBElement $entity, string $method = 'info'): string
|
||||||
|
{
|
||||||
|
return $this->entityURLGenerator->getURL($entity, $method);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL for the given entity in timetravel mode.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("timetravel_url")]
|
||||||
|
public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->entityURLGenerator->timeTravelURL($element, $dateTime);
|
||||||
|
} catch (EntityNotSupportedException) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a tree data structure for the given element, which can be used to display a tree view of the element and its related entities.
|
||||||
|
* The type parameter can be used to specify the type of tree view (e.g. "newEdit" for the tree view in the new/edit pages). The returned data is a JSON string.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("tree_data")]
|
||||||
|
public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string
|
||||||
|
{
|
||||||
|
$tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element);
|
||||||
|
|
||||||
|
return json_encode($tree, JSON_THROW_ON_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German.
|
||||||
|
* @deprecated Use the "type_label" function instead, which does the same but is more concise.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("entity_type_label", deprecationInfo: new DeprecatedCallableInfo("Part-DB", "2", "Use the 'type_label' function instead."))]
|
||||||
|
public function entityTypeLabel(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->getLocalizedTypeLabel($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("type_label")]
|
||||||
|
public function typeLabel(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->typeLabel($entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localized plural type label for the given entity. E.g. for a Part, it will return "Parts" in English and "Bauteile" in German.
|
||||||
|
* @param object|string $entity
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction("type_label_p")]
|
||||||
|
public function typeLabelPlural(object|string $entity): string
|
||||||
|
{
|
||||||
|
return $this->nameGenerator->typeLabelPlural($entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,35 +29,28 @@ use App\Services\Formatters\MarkdownParser;
|
||||||
use App\Services\Formatters\MoneyFormatter;
|
use App\Services\Formatters\MoneyFormatter;
|
||||||
use App\Services\Formatters\SIFormatter;
|
use App\Services\Formatters\SIFormatter;
|
||||||
use Brick\Math\BigDecimal;
|
use Brick\Math\BigDecimal;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Attribute\AsTwigFilter;
|
||||||
use Twig\TwigFilter;
|
|
||||||
|
|
||||||
final class FormatExtension extends AbstractExtension
|
final readonly class FormatExtension
|
||||||
{
|
{
|
||||||
public function __construct(protected MarkdownParser $markdownParser, protected MoneyFormatter $moneyFormatter, protected SIFormatter $siformatter, protected AmountFormatter $amountFormatter)
|
public function __construct(private MarkdownParser $markdownParser, private MoneyFormatter $moneyFormatter, private SIFormatter $siformatter, private AmountFormatter $amountFormatter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
/**
|
||||||
|
* Mark the given text as markdown, which will be rendered in the browser
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_markdown", isSafe: ['html'], preEscape: 'html')]
|
||||||
|
public function formatMarkdown(string $markdown, bool $inline_mode = false): string
|
||||||
{
|
{
|
||||||
return [
|
return $this->markdownParser->markForRendering($markdown, $inline_mode);
|
||||||
/* Mark the given text as markdown, which will be rendered in the browser */
|
|
||||||
new TwigFilter('format_markdown', fn(string $markdown, bool $inline_mode = false): string => $this->markdownParser->markForRendering($markdown, $inline_mode), [
|
|
||||||
'pre_escape' => 'html',
|
|
||||||
'is_safe' => ['html'],
|
|
||||||
]),
|
|
||||||
/* Format the given amount as money, using a given currency */
|
|
||||||
new TwigFilter('format_money', fn($amount, ?Currency $currency = null, int $decimals = 5): string => $this->formatCurrency($amount, $currency, $decimals)),
|
|
||||||
/* Format the given number using SI prefixes and the given unit (string) */
|
|
||||||
new TwigFilter('format_si', fn($value, $unit, $decimals = 2, bool $show_all_digits = false): string => $this->siFormat($value, $unit, $decimals, $show_all_digits)),
|
|
||||||
/** Format the given amount using the given MeasurementUnit */
|
|
||||||
new TwigFilter('format_amount', fn($value, ?MeasurementUnit $unit, array $options = []): string => $this->amountFormat($value, $unit, $options)),
|
|
||||||
/** Format the given number of bytes as human-readable number */
|
|
||||||
new TwigFilter('format_bytes', fn(int $bytes, int $precision = 2): string => $this->formatBytes($bytes, $precision)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formatCurrency($amount, ?Currency $currency = null, int $decimals = 5): string
|
/**
|
||||||
|
* Format the given amount as money, using a given currency
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_money")]
|
||||||
|
public function formatMoney(BigDecimal|float|string $amount, ?Currency $currency = null, int $decimals = 5): string
|
||||||
{
|
{
|
||||||
if ($amount instanceof BigDecimal) {
|
if ($amount instanceof BigDecimal) {
|
||||||
$amount = (string) $amount;
|
$amount = (string) $amount;
|
||||||
|
|
@ -66,19 +59,22 @@ final class FormatExtension extends AbstractExtension
|
||||||
return $this->moneyFormatter->format($amount, $currency, $decimals);
|
return $this->moneyFormatter->format($amount, $currency, $decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function siFormat($value, $unit, $decimals = 2, bool $show_all_digits = false): string
|
/**
|
||||||
|
* Format the given number using SI prefixes and the given unit (string)
|
||||||
|
*/
|
||||||
|
#[AsTwigFilter("format_si")]
|
||||||
|
public function siFormat(float $value, string $unit, int $decimals = 2, bool $show_all_digits = false): string
|
||||||
{
|
{
|
||||||
return $this->siformatter->format($value, $unit, $decimals);
|
return $this->siformatter->format($value, $unit, $decimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function amountFormat($value, ?MeasurementUnit $unit, array $options = []): string
|
#[AsTwigFilter("format_amount")]
|
||||||
|
public function amountFormat(float|int|string $value, ?MeasurementUnit $unit, array $options = []): string
|
||||||
{
|
{
|
||||||
return $this->amountFormatter->format($value, $unit, $options);
|
return $this->amountFormatter->format($value, $unit, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[AsTwigFilter("format_bytes")]
|
||||||
* @param $bytes
|
|
||||||
*/
|
|
||||||
public function formatBytes(int $bytes, int $precision = 2): string
|
public function formatBytes(int $bytes, int $precision = 2): string
|
||||||
{
|
{
|
||||||
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ use App\Services\InfoProviderSystem\Providers\InfoProviderInterface;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
class InfoProviderExtension
|
final readonly class InfoProviderExtension
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ProviderRegistry $providerRegistry
|
private ProviderRegistry $providerRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,4 +64,4 @@ class InfoProviderExtension
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,26 @@ namespace App\Twig;
|
||||||
use App\Entity\LogSystem\AbstractLogEntry;
|
use App\Entity\LogSystem\AbstractLogEntry;
|
||||||
use App\Services\LogSystem\LogDataFormatter;
|
use App\Services\LogSystem\LogDataFormatter;
|
||||||
use App\Services\LogSystem\LogDiffFormatter;
|
use App\Services\LogSystem\LogDiffFormatter;
|
||||||
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
final class LogExtension extends AbstractExtension
|
final readonly class LogExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(private readonly LogDataFormatter $logDataFormatter, private readonly LogDiffFormatter $logDiffFormatter)
|
public function __construct(private LogDataFormatter $logDataFormatter, private LogDiffFormatter $logDiffFormatter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
#[AsTwigFunction(name: 'format_log_data', isSafe: ['html'])]
|
||||||
|
public function formatLogData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string
|
||||||
{
|
{
|
||||||
return [
|
return $this->logDataFormatter->formatData($data, $logEntry, $fieldName);
|
||||||
new TwigFunction('format_log_data', fn($data, AbstractLogEntry $logEntry, string $fieldName): string => $this->logDataFormatter->formatData($data, $logEntry, $fieldName), ['is_safe' => ['html']]),
|
}
|
||||||
new TwigFunction('format_log_diff', fn($old_data, $new_data): string => $this->logDiffFormatter->formatDiff($old_data, $new_data), ['is_safe' => ['html']]),
|
|
||||||
];
|
#[AsTwigFunction(name: 'format_log_diff', isSafe: ['html'])]
|
||||||
|
public function formatLogDiff(mixed $old_data, mixed $new_data): string
|
||||||
|
{
|
||||||
|
return $this->logDiffFormatter->formatDiff($old_data, $new_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ use Twig\TwigFunction;
|
||||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
|
|
||||||
final class MiscExtension
|
final readonly class MiscExtension
|
||||||
{
|
{
|
||||||
public function __construct(private readonly EventCommentNeededHelper $eventCommentNeededHelper)
|
public function __construct(private EventCommentNeededHelper $eventCommentNeededHelper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,10 @@ declare(strict_types=1);
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
use Twig\Attribute\AsTwigFilter;
|
||||||
use Twig\Attribute\AsTwigFunction;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||||
|
use Twig\Attribute\AsTwigTest;
|
||||||
use Twig\Extension\AbstractExtension;
|
use Twig\Extension\AbstractExtension;
|
||||||
use Twig\TwigFilter;
|
use Twig\TwigFilter;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
@ -34,51 +36,54 @@ use Twig\TwigTest;
|
||||||
* The functionalities here extend the Twig with some core functions, which are independently of Part-DB.
|
* The functionalities here extend the Twig with some core functions, which are independently of Part-DB.
|
||||||
* @see \App\Tests\Twig\TwigCoreExtensionTest
|
* @see \App\Tests\Twig\TwigCoreExtensionTest
|
||||||
*/
|
*/
|
||||||
final class TwigCoreExtension
|
final readonly class TwigCoreExtension
|
||||||
{
|
{
|
||||||
private readonly NormalizerInterface $objectNormalizer;
|
private NormalizerInterface $objectNormalizer;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->objectNormalizer = new ObjectNormalizer();
|
$this->objectNormalizer = new ObjectNormalizer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTests(): array
|
/**
|
||||||
|
* Checks if the given variable is an instance of the given class/interface/enum. E.g. `x is instanceof('App\Entity\Parts\Part')`
|
||||||
|
* @param mixed $var
|
||||||
|
* @param string $instance
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("instanceof")]
|
||||||
|
public function testInstanceOf(mixed $var, string $instance): bool
|
||||||
{
|
{
|
||||||
return [
|
if (!class_exists($instance) && !interface_exists($instance) && !enum_exists($instance)) {
|
||||||
/*
|
throw new \InvalidArgumentException(sprintf('The given class/interface/enum "%s" does not exist!', $instance));
|
||||||
* Checks if a given variable is an instance of a given class. E.g. ` x is instanceof('App\Entity\Parts\Part')`
|
}
|
||||||
*/
|
|
||||||
new TwigTest('instanceof', static fn($var, $instance) => $var instanceof $instance),
|
return $var instanceof $instance;
|
||||||
/* Checks if a given variable is an object. E.g. `x is object` */
|
|
||||||
new TwigTest('object', static fn($var): bool => is_object($var)),
|
|
||||||
new TwigTest('enum', fn($var) => $var instanceof \UnitEnum),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $enum_class
|
* Checks if the given variable is an object. This can be used to check if a variable is an object, without knowing the exact class of the object. E.g. `x is object`
|
||||||
* @phpstan-param class-string $enum_class
|
* @param mixed $var
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
#[AsTwigFunction(name: 'enum_cases')]
|
#[AsTwigTest("object")]
|
||||||
public function getEnumCases(string $enum_class): array
|
public function testObject(mixed $var): bool
|
||||||
{
|
{
|
||||||
if (!enum_exists($enum_class)) {
|
return is_object($var);
|
||||||
throw new \InvalidArgumentException(sprintf('The given class "%s" is not an enum!', $enum_class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
return ($enum_class)::cases();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
/**
|
||||||
|
* Checks if the given variable is an enum (instance of UnitEnum). This can be used to check if a variable is an enum, without knowing the exact class of the enum. E.g. `x is enum`
|
||||||
|
* @param mixed $var
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
#[AsTwigTest("enum")]
|
||||||
|
public function testEnum(mixed $var): bool
|
||||||
{
|
{
|
||||||
return [
|
return $var instanceof \UnitEnum;
|
||||||
/* Converts the given object to an array representation of the public/accessible properties */
|
|
||||||
new TwigFilter('to_array', fn($object) => $this->toArray($object)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[AsTwigFilter('to_array')]
|
||||||
public function toArray(object|array $object): array
|
public function toArray(object|array $object): array
|
||||||
{
|
{
|
||||||
//If it is already an array, we can just return it
|
//If it is already an array, we can just return it
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ use Twig\TwigFunction;
|
||||||
/**
|
/**
|
||||||
* Twig extension for update-related functions.
|
* Twig extension for update-related functions.
|
||||||
*/
|
*/
|
||||||
final class UpdateExtension
|
final readonly class UpdateExtension
|
||||||
{
|
{
|
||||||
public function __construct(private readonly UpdateAvailableFacade $updateAvailableManager,
|
public function __construct(private UpdateAvailableFacade $updateAvailableManager,
|
||||||
private readonly Security $security)
|
private Security $security)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use Twig\Attribute\AsTwigFilter;
|
||||||
use Twig\Attribute\AsTwigFunction;
|
use Twig\Attribute\AsTwigFunction;
|
||||||
use App\Entity\Base\AbstractDBElement;
|
use App\Entity\Base\AbstractDBElement;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
|
@ -51,39 +52,34 @@ use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
|
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
|
||||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||||
use Twig\Extension\AbstractExtension;
|
|
||||||
use Twig\TwigFilter;
|
|
||||||
use Twig\TwigFunction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \App\Tests\Twig\UserExtensionTest
|
* @see \App\Tests\Twig\UserExtensionTest
|
||||||
*/
|
*/
|
||||||
final class UserExtension
|
final readonly class UserExtension
|
||||||
{
|
{
|
||||||
private readonly LogEntryRepository $repo;
|
private LogEntryRepository $repo;
|
||||||
|
|
||||||
public function __construct(EntityManagerInterface $em,
|
public function __construct(EntityManagerInterface $em,
|
||||||
private readonly Security $security,
|
private Security $security,
|
||||||
private readonly UrlGeneratorInterface $urlGenerator)
|
private UrlGeneratorInterface $urlGenerator)
|
||||||
{
|
{
|
||||||
$this->repo = $em->getRepository(AbstractLogEntry::class);
|
$this->repo = $em->getRepository(AbstractLogEntry::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
/**
|
||||||
|
* Returns the user which has edited the given entity the last time.
|
||||||
|
*/
|
||||||
|
#[AsTwigFunction('last_editing_user')]
|
||||||
|
public function lastEditingUser(AbstractDBElement $element): ?User
|
||||||
{
|
{
|
||||||
return [
|
return $this->repo->getLastEditingUser($element);
|
||||||
new TwigFilter('remove_locale_from_path', fn(string $path): string => $this->removeLocaleFromPath($path)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFunctions(): array
|
#[AsTwigFunction('creating_user')]
|
||||||
|
public function creatingUser(AbstractDBElement $element): ?User
|
||||||
{
|
{
|
||||||
return [
|
return $this->repo->getCreatingUser($element);
|
||||||
/* Returns the user which has edited the given entity the last time. */
|
|
||||||
new TwigFunction('last_editing_user', fn(AbstractDBElement $element): ?User => $this->repo->getLastEditingUser($element)),
|
|
||||||
/* Returns the user which has created the given entity. */
|
|
||||||
new TwigFunction('creating_user', fn(AbstractDBElement $element): ?User => $this->repo->getCreatingUser($element)),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,6 +121,7 @@ final class UserExtension
|
||||||
/**
|
/**
|
||||||
* This function/filter generates a path.
|
* This function/filter generates a path.
|
||||||
*/
|
*/
|
||||||
|
#[AsTwigFilter(name: 'remove_locale_from_path')]
|
||||||
public function removeLocaleFromPath(string $path): string
|
public function removeLocaleFromPath(string $path): string
|
||||||
{
|
{
|
||||||
//Ensure the path has the correct format
|
//Ensure the path has the correct format
|
||||||
|
|
|
||||||
|
|
@ -55,20 +55,20 @@ final class EntityExtensionTest extends WebTestCase
|
||||||
|
|
||||||
public function testGetEntityType(): void
|
public function testGetEntityType(): void
|
||||||
{
|
{
|
||||||
$this->assertSame('part', $this->service->getEntityType(new Part()));
|
$this->assertSame('part', $this->service->entityType(new Part()));
|
||||||
$this->assertSame('footprint', $this->service->getEntityType(new Footprint()));
|
$this->assertSame('footprint', $this->service->entityType(new Footprint()));
|
||||||
$this->assertSame('storelocation', $this->service->getEntityType(new StorageLocation()));
|
$this->assertSame('storelocation', $this->service->entityType(new StorageLocation()));
|
||||||
$this->assertSame('manufacturer', $this->service->getEntityType(new Manufacturer()));
|
$this->assertSame('manufacturer', $this->service->entityType(new Manufacturer()));
|
||||||
$this->assertSame('category', $this->service->getEntityType(new Category()));
|
$this->assertSame('category', $this->service->entityType(new Category()));
|
||||||
$this->assertSame('device', $this->service->getEntityType(new Project()));
|
$this->assertSame('device', $this->service->entityType(new Project()));
|
||||||
$this->assertSame('attachment', $this->service->getEntityType(new PartAttachment()));
|
$this->assertSame('attachment', $this->service->entityType(new PartAttachment()));
|
||||||
$this->assertSame('supplier', $this->service->getEntityType(new Supplier()));
|
$this->assertSame('supplier', $this->service->entityType(new Supplier()));
|
||||||
$this->assertSame('user', $this->service->getEntityType(new User()));
|
$this->assertSame('user', $this->service->entityType(new User()));
|
||||||
$this->assertSame('group', $this->service->getEntityType(new Group()));
|
$this->assertSame('group', $this->service->entityType(new Group()));
|
||||||
$this->assertSame('currency', $this->service->getEntityType(new Currency()));
|
$this->assertSame('currency', $this->service->entityType(new Currency()));
|
||||||
$this->assertSame('measurement_unit', $this->service->getEntityType(new MeasurementUnit()));
|
$this->assertSame('measurement_unit', $this->service->entityType(new MeasurementUnit()));
|
||||||
$this->assertSame('label_profile', $this->service->getEntityType(new LabelProfile()));
|
$this->assertSame('label_profile', $this->service->entityType(new LabelProfile()));
|
||||||
$this->assertSame('part_custom_state', $this->service->getEntityType(new PartCustomState()));
|
$this->assertSame('part_custom_state', $this->service->entityType(new PartCustomState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue