Compare commits

..

No commits in common. "98df91d785d2f6dbc63d6b464ba95cb75fcec6f3" and "57a0dfdbdbb5f26c4f626c0083ec637532236726" have entirely different histories.

28 changed files with 347 additions and 504 deletions

10
.env
View file

@ -149,16 +149,6 @@ DISABLE_YEAR2038_BUG_CHECK=0
#TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
###################################################################################
# Logging settings
###################################################################################
# The minimum level a deprecation notice must have to be written to the var/log/<env>_deprecations.log file.
# Deprecation notices are logged with level "info", so this disables the deprecation log by default.
# Set to debug to log all deprecation notices
DEPRECATION_LOG_LEVEL=emergency
###> symfony/lock ###
# Choose one of the stores below

View file

@ -129,7 +129,7 @@ jobs:
run: ./bin/phpunit --coverage-clover=coverage.xml
- name: Upload coverage
uses: codecov/codecov-action@v7
uses: codecov/codecov-action@v6
with:
env_vars: PHP_VERSION,DB_TYPE
token: ${{ secrets.CODECOV_TOKEN }}

View file

@ -1 +1 @@
2.12.2
2.12.1

View file

@ -51,7 +51,7 @@ export default class extends Controller {
//Make the state persistent over reloads
if(localStorage.getItem(STORAGE_KEY) === 'true') {
this.hideSidebar();
sidebarHide();
}
}

View file

@ -70,7 +70,6 @@
"symfony/flex": "^v2.3.1",
"symfony/form": "7.4.*",
"symfony/framework-bundle": "7.4.*",
"symfony/html-sanitizer": "7.4.*",
"symfony/http-client": "7.4.*",
"symfony/http-kernel": "7.4.*",
"symfony/mailer": "7.4.*",

424
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -51,7 +51,6 @@ when@prod:
type: stream
channels: [deprecation]
path: "%kernel.logs_dir%/%kernel.environment%_deprecations.log"
level: "%env(DEPRECATION_LOG_LEVEL)%"
when@docker:
monolog:
@ -76,4 +75,3 @@ when@docker:
type: stream
channels: [deprecation]
path: "%kernel.logs_dir%/%kernel.environment%_deprecations.log"
level: "%env(DEPRECATION_LOG_LEVEL)%"

View file

@ -653,7 +653,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* time_based_uuid_node?: scalar|Param|null,
* },
* html_sanitizer?: bool|array{ // HtmlSanitizer configuration
* enabled?: bool|Param, // Default: true
* enabled?: bool|Param, // Default: false
* sanitizers?: array<string, array{ // Default: []
* allow_safe_elements?: bool|Param, // Allows "safe" elements and attributes. // Default: false
* allow_static_elements?: bool|Param, // Allows all static elements and attributes from the W3C Sanitizer API standard. // Default: false
@ -718,7 +718,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connection.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.
@ -769,7 +769,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connection.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.
@ -801,7 +801,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* servicename?: scalar|Param|null, // Overrules dbname parameter if given and used as SERVICE_NAME or SID connection parameter for Oracle depending on the service parameter.
* sessionMode?: scalar|Param|null, // The session mode to use for the oci8 driver
* server?: scalar|Param|null, // The name of a running database server to connect to for SQL Anywhere.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connection.
* default_dbname?: scalar|Param|null, // Override the default database (postgres) to connect to for PostgreSQL connexion.
* sslmode?: scalar|Param|null, // Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server for PostgreSQL.
* sslrootcert?: scalar|Param|null, // The name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities.
* sslcert?: scalar|Param|null, // The path to the SSL client certificate file for PostgreSQL.

View file

@ -279,13 +279,9 @@ See the [information providers]({% link usage/information_provider_system.md %})
* `BANNER`: You can configure the text that should be shown as the banner on the homepage. Useful especially for docker
containers. In all other applications you can just change the `config/banner.md` file.
* `DISABLE_YEAR2038_BUG_CHECK` (env only): If set to `1`, the year 2038 bug check is disabled on 32-bit systems, and dates after
2038 are no longer forbidden. However, this will lead to 500 error messages when rendering dates after 2038 as all current
2038 are no longer forbidden. However this will lead to 500 error messages when rendering dates after 2038 as all current
32-bit PHP versions can not format these dates correctly. This setting is for the case that future PHP versions will
handle this correctly on 32-bit systems. 64-bit systems are not affected by this bug, and the check is always disabled.
* `DEPRECATION_LOG_LEVEL` (default `emergency`) (env only): In the `prod` and `docker` environments, PHP/Symfony
deprecation notices are written to their own `var/log/<env>_deprecations.log` file. This option sets the minimum log
level a deprecation notice must have to be written there. Since deprecation notices are logged with level `info`,
the default value of `emergency` effectively disables this dedicated deprecation log. Set it to `debug` to enable it.
## Banner

View file

@ -1,4 +1,4 @@
# Generated on Mon Jun 8 06:51:27 UTC 2026
# Generated on Mon Jun 1 07:07:44 UTC 2026
# This file contains all footprints available in the offical KiCAD library
Audio_Module:Reverb_BTDR-1H
Audio_Module:Reverb_BTDR-1V
@ -12012,6 +12012,7 @@ Package_DFN_QFN:WDFN-8-1EP_4x3mm_P0.65mm_EP2.4x1.8mm_ThermalVias
Package_DFN_QFN:WDFN-8-1EP_6x5mm_P1.27mm_EP3.4x4mm
Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm
Package_DFN_QFN:WDFN-8-1EP_8x6mm_P1.27mm_EP6x4.8mm_ThermalVias
Package_DFN_QFN:WDFN-8_2x2mm_P0.5mm
Package_DFN_QFN:WFDFPN-8-1EP_3x2mm_P0.5mm_EP1.25x1.35mm
Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm
Package_DFN_QFN:WQFN-14-1EP_2.5x2.5mm_P0.5mm_EP1.45x1.45mm_ThermalVias
@ -12357,6 +12358,7 @@ Package_DirectFET:DirectFET_SQ
Package_DirectFET:DirectFET_ST
Package_LCC:Analog_LCC-8_5x5mm_P1.27mm
Package_LCC:MO047AD_PLCC-52_19.1x19.1mm_P1.27mm
Package_LCC:PLCC-20
Package_LCC:PLCC-20_9.0x9.0mm_P1.27mm
Package_LCC:PLCC-20_SMD-Socket
Package_LCC:PLCC-20_THT-Socket

View file

@ -1,4 +1,4 @@
# Generated on Mon Jun 8 06:52:04 UTC 2026
# Generated on Mon Jun 1 07:08:24 UTC 2026
# This file contains all symbols available in the offical KiCAD library
4xxx:14528
4xxx:14529
@ -15115,7 +15115,6 @@ Memory_Flash:AM29F400Bx-xxEx
Memory_Flash:AM29F400Bx-xxSx
Memory_Flash:AM29PDL128G
Memory_Flash:AT25DF041x-UxN-x
Memory_Flash:AT25SF041B-SSHD-X
Memory_Flash:AT25SF081-SSHD-X
Memory_Flash:AT25SF081-SSHF-X
Memory_Flash:AT25SF081-XMHD-X
@ -16481,7 +16480,6 @@ RF_Module:DWM3000
RF_Module:E18-MS1-PCB
RF_Module:E73-2G4M04S-52810
RF_Module:E73-2G4M04S-52832
RF_Module:ESP-01
RF_Module:ESP-07
RF_Module:ESP-12E
RF_Module:ESP-12F

View file

@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Controller;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
@ -56,7 +55,7 @@ class TreeController extends AbstractController
#[Route(path: '/category/{id}', name: 'tree_category')]
#[Route(path: '/categories', name: 'tree_category_root')]
public function categoryTree(#[MapEntity(id: 'id')] ?Category $category = null): JsonResponse
public function categoryTree(?Category $category = null): JsonResponse
{
if ($this->isGranted('@parts.read') && $this->isGranted('@categories.read')) {
$tree = $this->treeGenerator->getTreeView(Category::class, $category, 'list_parts_root');
@ -69,7 +68,7 @@ class TreeController extends AbstractController
#[Route(path: '/footprint/{id}', name: 'tree_footprint')]
#[Route(path: '/footprints', name: 'tree_footprint_root')]
public function footprintTree(#[MapEntity(id: 'id')] ?Footprint $footprint = null): JsonResponse
public function footprintTree(?Footprint $footprint = null): JsonResponse
{
if ($this->isGranted('@parts.read') && $this->isGranted('@footprints.read')) {
$tree = $this->treeGenerator->getTreeView(Footprint::class, $footprint, 'list_parts_root');
@ -81,7 +80,7 @@ class TreeController extends AbstractController
#[Route(path: '/location/{id}', name: 'tree_location')]
#[Route(path: '/locations', name: 'tree_location_root')]
public function locationTree(#[MapEntity(id: 'id')] ?StorageLocation $location = null): JsonResponse
public function locationTree(?StorageLocation $location = null): JsonResponse
{
if ($this->isGranted('@parts.read') && $this->isGranted('@storelocations.read')) {
$tree = $this->treeGenerator->getTreeView(StorageLocation::class, $location, 'list_parts_root');
@ -94,7 +93,7 @@ class TreeController extends AbstractController
#[Route(path: '/manufacturer/{id}', name: 'tree_manufacturer')]
#[Route(path: '/manufacturers', name: 'tree_manufacturer_root')]
public function manufacturerTree(#[MapEntity(id: 'id')] ?Manufacturer $manufacturer = null): JsonResponse
public function manufacturerTree(?Manufacturer $manufacturer = null): JsonResponse
{
if ($this->isGranted('@parts.read') && $this->isGranted('@manufacturers.read')) {
$tree = $this->treeGenerator->getTreeView(Manufacturer::class, $manufacturer, 'list_parts_root');
@ -107,7 +106,7 @@ class TreeController extends AbstractController
#[Route(path: '/supplier/{id}', name: 'tree_supplier')]
#[Route(path: '/suppliers', name: 'tree_supplier_root')]
public function supplierTree(#[MapEntity(id: 'id')] ?Supplier $supplier = null): JsonResponse
public function supplierTree(?Supplier $supplier = null): JsonResponse
{
if ($this->isGranted('@parts.read') && $this->isGranted('@suppliers.read')) {
$tree = $this->treeGenerator->getTreeView(Supplier::class, $supplier, 'list_parts_root');
@ -120,7 +119,7 @@ class TreeController extends AbstractController
#[Route(path: '/device/{id}', name: 'tree_device')]
#[Route(path: '/devices', name: 'tree_device_root')]
public function deviceTree(#[MapEntity(id: 'id')] ?Project $device = null): JsonResponse
public function deviceTree(?Project $device = null): JsonResponse
{
if ($this->isGranted('@projects.read')) {
$tree = $this->treeGenerator->getTreeView(Project::class, $device, 'devices');

View file

@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\DataTables;
use App\DataTables\Column\HTMLColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\PrettyBoolColumn;
use App\DataTables\Column\RowClassColumn;
@ -41,19 +40,14 @@ use Omines\DataTablesBundle\DataTable;
use Omines\DataTablesBundle\DataTableTypeInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class AttachmentDataTable implements DataTableTypeInterface
final class AttachmentDataTable implements DataTableTypeInterface
{
public function __construct(private TranslatorInterface $translator, private EntityURLGenerator $entityURLGenerator, private AttachmentManager $attachmentHelper, private AttachmentURLGenerator $attachmentURLGenerator, private ElementTypeNameGenerator $elementTypeNameGenerator)
public function __construct(private readonly TranslatorInterface $translator, private readonly EntityURLGenerator $entityURLGenerator, private readonly AttachmentManager $attachmentHelper, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly ElementTypeNameGenerator $elementTypeNameGenerator)
{
}
public function configure(DataTable $dataTable, array $options): void
{
/*************************************************************************************************************
* Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the
* HTMLColumn, if necessary
************************************************************************************************************/
$dataTable->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, Attachment $context): string {
//Mark attachments yellow which have an internal file linked that doesn't exist
@ -65,10 +59,10 @@ final readonly class AttachmentDataTable implements DataTableTypeInterface
},
]);
$dataTable->add('picture', HTMLColumn::class, [
$dataTable->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'data' => function (Attachment $context): string {
'render' => function ($value, Attachment $context): string {
if ($context->isPicture()
&& $this->attachmentHelper->isInternalFileExisting($context)) {
@ -101,65 +95,65 @@ final readonly class AttachmentDataTable implements DataTableTypeInterface
'orderField' => 'NATSORT(attachment.name)',
]);
$dataTable->add('attachment_type', HTMLColumn::class, [
$dataTable->add('attachment_type', TextColumn::class, [
'label' => 'attachment.table.type',
'field' => 'attachment_type.name',
'orderField' => 'NATSORT(attachment_type.name)',
'data' => fn(Attachment $context, $value): string => sprintf(
'render' => fn($value, Attachment $context): string => sprintf(
'<a href="%s">%s</a>',
$this->entityURLGenerator->editURL($context->getAttachmentType()),
htmlspecialchars((string) $value)
),
]);
$dataTable->add('element', HTMLColumn::class, [
$dataTable->add('element', TextColumn::class, [
'label' => 'attachment.table.element',
//'propertyPath' => 'element.name',
'data' => fn(Attachment $context): string => sprintf(
'render' => fn($value, Attachment $context): string => sprintf(
'<a href="%s">%s</a>',
$this->entityURLGenerator->infoURL($context->getElement()),
$this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true)
),
]);
$dataTable->add('internal_link', HTMLColumn::class, [
$dataTable->add('internal_link', TextColumn::class, [
'label' => 'attachment.table.internal_file',
'propertyPath' => 'filename',
'orderField' => 'NATSORT(attachment.original_filename)',
'data' => function (Attachment $context, $value) {
'render' => function ($value, Attachment $context) {
if ($this->attachmentHelper->isInternalFileExisting($context)) {
return sprintf(
'<a href="%s" target="_blank" data-no-ajax>%s</a>',
$this->entityURLGenerator->viewURL($context),
htmlspecialchars((string) $value)
htmlspecialchars($value)
);
}
return htmlspecialchars((string) $value);
},
return $value;
}
]);
$dataTable->add('external_link', HTMLColumn::class, [
$dataTable->add('external_link', TextColumn::class, [
'label' => 'attachment.table.external_link',
'propertyPath' => 'host',
'orderField' => 'attachment.external_path',
'data' => function (Attachment $context, $value) {
'render' => function ($value, Attachment $context) {
if ($context->hasExternal()) {
return sprintf(
'<a href="%s" class="link-external" title="%s" target="_blank" rel="noopener">%s</a>',
htmlspecialchars((string) $context->getExternalPath()),
htmlspecialchars((string) $context->getExternalPath()),
htmlspecialchars((string) $value),
htmlspecialchars($value),
);
}
return htmlspecialchars((string) $value);
},
return $value;
}
]);
$dataTable->add('filesize', HTMLColumn::class, [
$dataTable->add('filesize', TextColumn::class, [
'label' => $this->translator->trans('attachment.table.filesize'),
'data' => function (Attachment $context) {
'render' => function ($value, Attachment $context) {
if (!$context->hasInternal()) {
return sprintf(
'<span class="badge bg-primary">
@ -174,7 +168,7 @@ final readonly class AttachmentDataTable implements DataTableTypeInterface
'<span class="badge bg-secondary">
<i class="fas fa-hdd fa-fw"></i> %s
</span>',
htmlspecialchars($this->attachmentHelper->getHumanFileSize($context))
$this->attachmentHelper->getHumanFileSize($context)
);
}

View file

@ -78,7 +78,7 @@ class EntityColumn extends AbstractColumn
);
}
return sprintf('<i>%s</i>', htmlspecialchars($value));
return sprintf('<i>%s</i>', $value);
}
return '';

View file

@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
namespace App\DataTables\Column;
use Omines\DataTablesBundle\Column\TextColumn;
/**
* A TextColumn whose value is always treated as raw HTML and therefore never passed through htmlspecialchars().
* The value returned by the 'data' option must already contain properly escaped/sanitized HTML, as it is output as-is.
*/
class HTMLColumn extends TextColumn
{
public function isRaw(): bool
{
return true;
}
}

View file

@ -87,9 +87,9 @@ class IconLinkColumn extends AbstractColumn
return sprintf(
'<a class="btn btn-primary btn-sm %s" href="%s" title="%s"><i class="%s"></i></a>',
$disabled ? 'disabled' : '',
htmlspecialchars($href),
htmlspecialchars($title ?? ''),
htmlspecialchars($icon ?? '')
$href,
$title,
$icon
);
}

View file

@ -22,9 +22,9 @@ declare(strict_types=1);
*/
namespace App\DataTables;
use App\DataTables\Column\HTMLColumn;
use App\DataTables\Column\RowClassColumn;
use Omines\DataTablesBundle\Adapter\ArrayAdapter;
use Omines\DataTablesBundle\Column\TextColumn;
use Omines\DataTablesBundle\DataTable;
use Omines\DataTablesBundle\DataTableFactory;
use Omines\DataTablesBundle\DataTableTypeInterface;
@ -32,7 +32,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolver;
final readonly class ErrorDataTable implements DataTableTypeInterface
class ErrorDataTable implements DataTableTypeInterface
{
public function configureOptions(OptionsResolver $optionsResolver): void
{
@ -49,11 +49,6 @@ final readonly class ErrorDataTable implements DataTableTypeInterface
public function configure(DataTable $dataTable, array $options): void
{
/*************************************************************************************************************
* Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the
* HTMLColumn, if necessary
************************************************************************************************************/
$optionsResolver = new OptionsResolver();
$this->configureOptions($optionsResolver);
$options = $optionsResolver->resolve($options);
@ -63,9 +58,9 @@ final readonly class ErrorDataTable implements DataTableTypeInterface
'render' => fn($value, $context): string => 'table-warning',
])
->add('error', HTMLColumn::class, [
->add('error', TextColumn::class, [
'label' => 'error_table.error',
'data' => fn($context, $value): string => '<i class="fa-solid fa-triangle-exclamation fa-fw"></i> ' . htmlspecialchars((string) $value),
'render' => fn($value, $context): string => '<i class="fa-solid fa-triangle-exclamation fa-fw"></i> ' . $value,
])
;

View file

@ -62,7 +62,7 @@ class PartDataTableHelper
}
if ($context->getBuiltProject() instanceof Project) {
$icon = sprintf('<i class="fa-solid fa-box-archive fa-fw me-1" title="%s"></i>',
$this->translator->trans('part.info.projectBuildPart.hint').': '.htmlspecialchars($context->getBuiltProject()->getName()));
$this->translator->trans('part.info.projectBuildPart.hint').': '.$context->getBuiltProject()->getName());
}

View file

@ -25,7 +25,6 @@ namespace App\DataTables;
use App\DataTables\Column\EnumColumn;
use App\Entity\LogSystem\LogTargetType;
use Symfony\Bundle\SecurityBundle\Security;
use App\DataTables\Column\HTMLColumn;
use App\DataTables\Column\IconLinkColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\LogEntryExtraColumn;
@ -60,7 +59,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class LogDataTable implements DataTableTypeInterface
class LogDataTable implements DataTableTypeInterface
{
protected LogEntryRepository $logRepo;
@ -96,11 +95,6 @@ final readonly class LogDataTable implements DataTableTypeInterface
public function configure(DataTable $dataTable, array $options): void
{
/*************************************************************************************************************
* Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the
* HTMLColumn, if necessary
************************************************************************************************************/
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$options = $resolver->resolve($options);
@ -110,10 +104,10 @@ final readonly class LogDataTable implements DataTableTypeInterface
'render' => fn($value, AbstractLogEntry $context) => $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString()),
]);
$dataTable->add('symbol', HTMLColumn::class, [
$dataTable->add('symbol', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'data' => fn(AbstractLogEntry $context): string => sprintf(
'render' => fn($value, AbstractLogEntry $context): string => sprintf(
'<i class="fas fa-fw %s" title="%s"></i>',
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
$context->getLevelString()
@ -134,10 +128,10 @@ final readonly class LogDataTable implements DataTableTypeInterface
)
]);
$dataTable->add('type', HTMLColumn::class, [
$dataTable->add('type', TextColumn::class, [
'label' => 'log.type',
'propertyPath' => 'type',
'data' => function (AbstractLogEntry $context, string $value) {
'render' => function (string $value, AbstractLogEntry $context) {
$text = $this->translator->trans('log.type.'.$value);
if ($context instanceof PartStockChangedLogEntry) {
@ -155,20 +149,20 @@ final readonly class LogDataTable implements DataTableTypeInterface
'label' => 'log.level',
'visible' => 'system_log' === $options['mode'],
'propertyPath' => 'levelString',
'data' => fn(AbstractLogEntry $context, string $value) => $this->translator->trans('log.level.'.$value),
'render' => fn(string $value, AbstractLogEntry $context) => $this->translator->trans('log.level.'.$value),
]);
$dataTable->add('user', HTMLColumn::class, [
$dataTable->add('user', TextColumn::class, [
'label' => 'log.user',
'orderField' => 'NATSORT(user.name)',
'data' => function (AbstractLogEntry $context): string {
'render' => function ($value, AbstractLogEntry $context): string {
$user = $context->getUser();
//If user was deleted, show the info from the username field
if (!$user instanceof User) {
if ($context->isCLIEntry()) {
return sprintf('%s [%s]',
htmlspecialchars((string) $context->getCLIUsername()),
htmlentities((string) $context->getCLIUsername()),
$this->translator->trans('log.cli_user')
);
}
@ -176,7 +170,7 @@ final readonly class LogDataTable implements DataTableTypeInterface
//Else we just deal with a deleted user
return sprintf(
'@%s [%s]',
htmlspecialchars($context->getUsername()),
htmlentities($context->getUsername()),
$this->translator->trans('log.target_deleted'),
);
}
@ -188,7 +182,7 @@ final readonly class LogDataTable implements DataTableTypeInterface
$img_url,
$this->userAvatarHelper->getAvatarMdURL($user),
$this->urlGenerator->generate('user_info', ['id' => $user->getID()]),
htmlspecialchars($user->getFullName(true))
htmlentities($user->getFullName(true))
);
},
]);
@ -200,7 +194,7 @@ final readonly class LogDataTable implements DataTableTypeInterface
'render' => function (LogTargetType $value, AbstractLogEntry $context) {
$class = $value->toClass();
if (null !== $class) {
return $this->elementTypeNameGenerator->typeLabel($class);
return $this->elementTypeNameGenerator->getLocalizedTypeLabel($class);
}
return '';
@ -222,9 +216,9 @@ final readonly class LogDataTable implements DataTableTypeInterface
'icon' => 'fas fa-fw fa-eye',
'href' => function ($value, AbstractLogEntry $context) {
if (
$context instanceof CollectionElementDeleted ||
($context instanceof TimeTravelInterface
&& $context->hasOldDataInformation())
|| $context instanceof CollectionElementDeleted
) {
try {
$target = $this->logRepo->getTargetElement($context);

View file

@ -25,7 +25,6 @@ namespace App\DataTables;
use App\DataTables\Adapters\TwoStepORMAdapter;
use App\DataTables\Column\EntityColumn;
use App\DataTables\Column\EnumColumn;
use App\DataTables\Column\HTMLColumn;
use App\DataTables\Column\IconLinkColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\MarkdownColumn;
@ -59,7 +58,7 @@ use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class PartsDataTable implements DataTableTypeInterface
final class PartsDataTable implements DataTableTypeInterface
{
public const LENGTH_MENU = [[10, 25, 50, 100, 250, 500, -1], [10, 25, 50, 100, 250, 500, "All"]];
@ -95,11 +94,6 @@ final readonly class PartsDataTable implements DataTableTypeInterface
* When adding columns here, add them also to PartTableColumns enum, to make them configurable in the settings!
*************************************************************************************************************/
/*************************************************************************************************************
* Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the
* HTMLColumn, if necessary
************************************************************************************************************/
$this->csh
//Color the table rows depending on the review and favorite status
->add('row_color', RowClassColumn::class, [
@ -115,23 +109,23 @@ final readonly class PartsDataTable implements DataTableTypeInterface
},
], visibility_configurable: false)
->add('select', SelectColumn::class, visibility_configurable: false)
->add('picture', HTMLColumn::class, [
->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'data' => fn(Part $context) => $this->partDataTableHelper->renderPicture($context),
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderPicture($context),
], visibility_configurable: false)
->add('name', HTMLColumn::class, [
->add('name', TextColumn::class, [
'label' => $this->translator->trans('part.table.name'),
'data' => fn(Part $context) => $this->partDataTableHelper->renderName($context),
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderName($context),
'orderField' => 'NATSORT(part.name)'
])
->add('si_value', TextColumn::class, [
'label' => $this->translator->trans('part.table.si_value'),
'data' => function (Part $context): string {
'render' => function ($value, Part $context): string {
$siValue = SiValueSort::sqliteSiValue($context->getName());
if ($siValue !== null) {
//Output it as scientific number with a big E
return sprintf('%G', $siValue);
return htmlspecialchars(sprintf('%G', $siValue));
}
return '';
},
@ -162,38 +156,38 @@ final readonly class PartsDataTable implements DataTableTypeInterface
'label' => $this->translator->trans('part.table.manufacturer'),
'orderField' => 'NATSORT(_manufacturer.name)'
])
->add('storelocation', HTMLColumn::class, [
->add('storelocation', TextColumn::class, [
'label' => $this->translator->trans('part.table.storeLocations'),
//We need to use a aggregate function to get the first store location, as we have a one-to-many relation
'orderField' => 'NATSORT(MIN(_storelocations.name))',
'data' => fn(Part $context) => $this->partDataTableHelper->renderStorageLocations($context),
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderStorageLocations($context),
], alias: 'storage_location')
->add('amount', HTMLColumn::class, [
->add('amount', TextColumn::class, [
'label' => $this->translator->trans('part.table.amount'),
'data' => fn(Part $context) => $this->partDataTableHelper->renderAmount($context),
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderAmount($context),
'orderField' => 'amountSum'
])
->add('minamount', TextColumn::class, [
'label' => $this->translator->trans('part.table.minamount'),
'data' => fn(Part $context, $value): string => $this->amountFormatter->format(
'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format(
$value,
$context->getPartUnit()
),
)),
])
->add('partUnit', TextColumn::class, [
'label' => $this->translator->trans('part.table.partUnit'),
'orderField' => 'NATSORT(_partUnit.name)',
'data' => function (Part $context): string {
'render' => function ($value, Part $context): string {
$partUnit = $context->getPartUnit();
if ($partUnit === null) {
return '';
}
$tmp = $partUnit->getName();
$tmp = htmlspecialchars($partUnit->getName());
if ($partUnit->getUnit()) {
$tmp .= ' (' . $partUnit->getUnit() . ')';
$tmp .= ' (' . htmlspecialchars($partUnit->getUnit()) . ')';
}
return $tmp;
}
@ -201,14 +195,14 @@ final readonly class PartsDataTable implements DataTableTypeInterface
->add('partCustomState', TextColumn::class, [
'label' => $this->translator->trans('part.table.partCustomState'),
'orderField' => 'NATSORT(_partCustomState.name)',
'data' => function(Part $context): string {
'render' => function($value, Part $context): string {
$partCustomState = $context->getPartCustomState();
if ($partCustomState === null) {
return '';
}
return $partCustomState->getName();
return htmlspecialchars($partCustomState->getName());
}
])
->add('addedDate', LocaleDateTimeColumn::class, [
@ -254,25 +248,25 @@ final readonly class PartsDataTable implements DataTableTypeInterface
])
->add('eda_reference', TextColumn::class, [
'label' => $this->translator->trans('part.table.eda_reference'),
'data' => static fn(Part $context) => $context->getEdaInfo()->getReferencePrefix() ?? '',
'render' => static fn($value, Part $context) => htmlspecialchars($context->getEdaInfo()->getReferencePrefix() ?? ''),
'orderField' => 'NATSORT(part.eda_info.reference_prefix)'
])
->add('eda_value', TextColumn::class, [
'label' => $this->translator->trans('part.table.eda_value'),
'data' => static fn(Part $context) => $context->getEdaInfo()->getValue() ?? '',
'render' => static fn($value, Part $context) => htmlspecialchars($context->getEdaInfo()->getValue() ?? ''),
'orderField' => 'NATSORT(part.eda_info.value)'
])
->add('eda_status', HTMLColumn::class, [
->add('eda_status', TextColumn::class, [
'label' => $this->translator->trans('part.table.eda_status'),
'data' => fn(Part $context) => $this->partDataTableHelper->renderEdaStatus($context),
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderEdaStatus($context),
'className' => 'text-center',
]);
//Add a column to list the projects where the part is used, when the user has the permission to see the projects
if ($this->security->isGranted('read', Project::class)) {
$this->csh->add('projects', HTMLColumn::class, [
$this->csh->add('projects', TextColumn::class, [
'label' => $this->translator->trans('project.labelp'),
'data' => function (Part $context): string {
'render' => function ($value, Part $context): string {
//Only show the first 5 projects names
$projects = $context->getProjects();
$tmp = "";
@ -292,7 +286,7 @@ final readonly class PartsDataTable implements DataTableTypeInterface
}
return $tmp;
},
}
]);
}

View file

@ -25,7 +25,6 @@ namespace App\DataTables;
use App\DataTables\Adapters\TwoStepORMAdapter;
use App\DataTables\Column\EntityColumn;
use App\DataTables\Column\EnumColumn;
use App\DataTables\Column\HTMLColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\MarkdownColumn;
use App\DataTables\Helpers\PartDataTableHelper;
@ -49,7 +48,7 @@ use Omines\DataTablesBundle\DataTable;
use Omines\DataTablesBundle\DataTableTypeInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterface
class ProjectBomEntriesDataTable implements DataTableTypeInterface
{
public function __construct(
protected EntityURLGenerator $entityURLGenerator,
@ -64,22 +63,17 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
public function configure(DataTable $dataTable, array $options): void
{
/*************************************************************************************************************
* Avoid using render, as it has no escaping, and is a potential security risk. Use data on TextColumn or the
* HTMLColumn, if necessary
************************************************************************************************************/
$dataTable
//->add('select', SelectColumn::class)
->add('picture', HTMLColumn::class, [
->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
if(!$context->getPart() instanceof Part) {
return '';
}
return $this->partDataTableHelper->renderPicture($context->getPart());
},
}
])
->add('id', TextColumn::class, [
@ -91,27 +85,27 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
'label' => $this->translator->trans('project.bom.quantity'),
'className' => 'text-center',
'orderField' => 'bom_entry.quantity',
'data' => function (ProjectBOMEntry $context): float|string {
'render' => function ($value, ProjectBOMEntry $context): float|string {
//If we have a non-part entry, only show the rounded quantity
if (!$context->getPart() instanceof Part) {
return round($context->getQuantity());
}
//Otherwise use the unit of the part to format the quantity
return $this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit());
return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()));
},
])
->add('partId', TextColumn::class, [
'label' => $this->translator->trans('project.bom.part_id'),
'visible' => true,
'orderField' => 'part.id',
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
return $context->getPart() instanceof Part ? (string) $context->getPart()->getId() : '';
},
])
->add('name', HTMLColumn::class, [
->add('name', TextColumn::class, [
'label' => $this->translator->trans('part.table.name'),
'orderField' => 'NATSORT(part.name)',
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
if(!$context->getPart() instanceof Part) {
return htmlspecialchars((string) $context->getName());
}
@ -129,7 +123,11 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
'label' => $this->translator->trans('part.table.ipn'),
'orderField' => 'NATSORT(part.ipn)',
'visible' => false,
'data' => fn (ProjectBOMEntry $context) => $context->getPart()?->getIpn()
'render' => function ($value, ProjectBOMEntry $context) {
if($context->getPart() instanceof Part) {
return $context->getPart()->getIpn();
}
}
])
->add('description', MarkdownColumn::class, [
'label' => $this->translator->trans('part.table.description'),
@ -174,9 +172,9 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
},
])
->add('mountnames', HTMLColumn::class, [
->add('mountnames', TextColumn::class, [
'label' => 'project.bom.mountnames',
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
$html = '';
foreach (explode(',', $context->getMountnames()) as $mountname) {
@ -186,34 +184,34 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
},
])
->add('instockAmount', HTMLColumn::class, [
->add('instockAmount', TextColumn::class, [
'label' => 'project.bom.instockAmount',
'visible' => false,
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
if ($context->getPart() !== null) {
return $this->partDataTableHelper->renderAmount($context->getPart());
}
return '';
},
}
])
->add('storelocation', HTMLColumn::class, [
->add('storelocation', TextColumn::class, [
'label' => $this->translator->trans('part.table.storeLocations'),
//We need to use a aggregate function to get the first store location, as we have a one-to-many relation
'orderField' => 'NATSORT(MIN(_storelocations.name))',
'visible' => false,
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
if ($context->getPart() !== null) {
return $this->partDataTableHelper->renderStorageLocations($context->getPart());
}
return '';
},
}
])
->add('price', TextColumn::class, [
'label' => 'project.bom.price',
'visible' => false,
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
return $this->moneyFormatter->format($price->toScale(2, RoundingMode::Up)->toFloat(), null, 2, true);
},
@ -221,7 +219,7 @@ final readonly class ProjectBomEntriesDataTable implements DataTableTypeInterfac
->add('ext_price', TextColumn::class, [
'label' => 'project.bom.ext_price',
'visible' => false,
'data' => function (ProjectBOMEntry $context) {
'render' => function ($value, ProjectBOMEntry $context) {
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
return $this->moneyFormatter->format(
$price->multipliedBy(BigDecimal::fromFloatShortest($context->getQuantity()))

View file

@ -35,10 +35,8 @@ class SetSQLModeMiddlewareDriver extends AbstractDriverMiddleware
{
//Only set this on MySQL connections, as other databases don't support this parameter
if($params['driver'] === 'pdo_mysql') {
//PDO::MYSQL_ATTR_INIT_COMMAND is deprecated since PHP 8.5 in favor of Pdo\Mysql::ATTR_INIT_COMMAND,
//but the Pdo\Mysql class only exists since PHP 8.4. Both constants have the same value (1002).
$initCommandAttr = class_exists(\Pdo\Mysql::class) ? \Pdo\Mysql::ATTR_INIT_COMMAND : \PDO::MYSQL_ATTR_INIT_COMMAND;
$params['driverOptions'][$initCommandAttr] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))';
//1002 is \PDO::MYSQL_ATTR_INIT_COMMAND constant value
$params['driverOptions'][\PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode, \'ONLY_FULL_GROUP_BY\', \'\'))';
}
return parent::connect($params);

View file

@ -80,7 +80,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)
],
uriVariables: [
'id' => new Link(fromProperty: 'children', fromClass: StorageLocation::class)
'id' => new Link(fromProperty: 'children', fromClass: Manufacturer::class)
],
normalizationContext: ['groups' => ['location:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
)]

View file

@ -29,13 +29,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Service for validating BOM import data with comprehensive validation rules
* and user-friendly error messages. The results are not HTML safe, and must be escaped before display!
* and user-friendly error messages.
*/
readonly class BOMValidationService
class BOMValidationService
{
public function __construct(
private EntityManagerInterface $entityManager,
private TranslatorInterface $translator
private readonly EntityManagerInterface $entityManager,
private readonly TranslatorInterface $translator
) {
}
@ -473,4 +473,4 @@ readonly class BOMValidationService
: 0,
];
}
}
}

View file

@ -58,7 +58,7 @@
</tr>
<tr>
<td>{% trans %}log.target{% endtrans %}</td>
<td>{{ target_html|sanitize_html }}</td>
<td>{{ target_html|raw }}</td>
</tr>
</table>
@ -111,7 +111,7 @@
{% elseif log_entry is instanceof('App\\Entity\\LogSystem\\CollectionElementDeleted') %}
{% include "log_system/details/_extra_collection_element_deleted.html.twig" %}
{% else %}
{{ extra_html | sanitize_html }}
{{ extra_html | raw }}
{% endif %}
</div>
{% endblock %}
{% endblock %}

View file

@ -68,7 +68,7 @@
<p class="mb-2">{% trans %}project.bom_import.validation.errors.description{% endtrans %}</p>
<ul class="mb-0">
{% for error in validation_result.errors %}
<li>{{ error }}</li>
<li>{{ error|raw }}</li>
{% endfor %}
</ul>
</div>
@ -80,7 +80,7 @@
<p class="mb-2">{% trans %}project.bom_import.validation.warnings.description{% endtrans %}</p>
<ul class="mb-0">
{% for warning in validation_result.warnings %}
<li>{{ warning }}</li>
<li>{{ warning|raw }}</li>
{% endfor %}
</ul>
</div>
@ -91,7 +91,7 @@
<h4><i class="fa-solid fa-info-circle fa-fw"></i> {% trans %}project.bom_import.validation.info.title{% endtrans %}</h4>
<ul class="mb-0">
{% for info in validation_result.info %}
<li>{{ info }}</li>
<li>{{ info|raw }}</li>
{% endfor %}
</ul>
</div>
@ -139,21 +139,21 @@
{% if line_result.errors is not empty %}
<div class="text-danger">
{% for error in line_result.errors %}
<div><i class="fa-solid fa-exclamation-triangle fa-fw"></i> {{ error }}</div>
<div><i class="fa-solid fa-exclamation-triangle fa-fw"></i> {{ error|raw }}</div>
{% endfor %}
</div>
{% endif %}
{% if line_result.warnings is not empty %}
<div class="text-warning">
{% for warning in line_result.warnings %}
<div><i class="fa-solid fa-exclamation-circle fa-fw"></i> {{ warning }}</div>
<div><i class="fa-solid fa-exclamation-circle fa-fw"></i> {{ warning|raw }}</div>
{% endfor %}
</div>
{% endif %}
{% if line_result.info is not empty %}
<div class="text-info">
{% for info in line_result.info %}
<div><i class="fa-solid fa-info-circle fa-fw"></i> {{ info }}</div>
<div><i class="fa-solid fa-info-circle fa-fw"></i> {{ info|raw }}</div>
{% endfor %}
</div>
{% endif %}

View file

@ -132,8 +132,8 @@
<script nonce="{{ csp_nonce('script') }}">
// Function to initialize the field mapping page
function initializeFieldMapping() {
const suggestions = JSON.parse("{{ suggested_mapping|json_encode|escape('js')}}");
const fieldNameMapping = JSON.parse("{{ field_name_mapping|json_encode|escape('js') }}");
const suggestions = {{ suggested_mapping|json_encode|raw }};
const fieldNameMapping = {{ field_name_mapping|json_encode|raw }};
Object.keys(suggestions).forEach(function(field) {
// Use the sanitized field name from the server-side mapping

View file

@ -1887,9 +1887,9 @@
integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
"@types/node@*":
version "25.9.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.3.tgz#11dfe7a33e68fa5c560f0aa76cc5595621ef26b9"
integrity sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==
version "25.9.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.2.tgz#fc8958e757994b71fee516f9634bdb03d1b19e9f"
integrity sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==
dependencies:
undici-types ">=7.24.0 <7.24.7"
@ -2116,9 +2116,9 @@ acorn-walk@^8.0.0:
acorn "^8.11.0"
acorn@^8.0.4, acorn@^8.11.0, acorn@^8.15.0, acorn@^8.16.0:
version "8.17.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.17.0.tgz#1785adb84faf8d8add10369b93826fc2bd08f1fe"
integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==
version "8.16.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a"
integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==
adjust-sourcemap-loader@^4.0.0:
version "4.0.0"
@ -2223,9 +2223,9 @@ base64-js@^1.1.2, base64-js@^1.3.0:
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
baseline-browser-mapping@^2.10.12:
version "2.10.37"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz#3e636475b6b293244e2b23e2c71a2ab9d9e6ba7d"
integrity sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==
version "2.10.34"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz#dedb606362446777cfe328d30d4ee15056d06303"
integrity sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==
big.js@^5.2.2:
version "5.2.2"
@ -2310,9 +2310,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001782:
version "1.0.30001799"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55"
integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==
version "1.0.30001797"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz#1332709e1439f01ff92085dd17001e0a45897ec0"
integrity sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==
ccount@^2.0.0:
version "2.0.1"
@ -2877,9 +2877,9 @@ domhandler@^5.0.2, domhandler@^5.0.3:
domelementtype "^2.3.0"
dompurify@^3.0.3:
version "3.4.10"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.10.tgz#96704295b4d8aeefcc8c7a90caa579b0ad69e46a"
integrity sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==
version "3.4.8"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.4.8.tgz#6c54f8c207160e7f83fcb7f4fd05a82ac36b1cdc"
integrity sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==
optionalDependencies:
"@types/trusted-types" "^2.0.7"
@ -2902,9 +2902,9 @@ domutils@^3.0.1:
domhandler "^5.0.3"
electron-to-chromium@^1.5.328:
version "1.5.372"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz#ae8ac69942a37b231773b8fb01759f73733599e3"
integrity sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==
version "1.5.368"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz#8e8d4600c5f5f01e891f8f843b4a941afb640412"
integrity sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==
emoji-regex@^8.0.0:
version "8.0.0"
@ -2917,9 +2917,9 @@ emojis-list@^3.0.0:
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
enhanced-resolve@^5.0.0, enhanced-resolve@^5.22.0:
version "5.24.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz#cf14b9768a774cb6a5087220c0dc6e55df6ec35a"
integrity sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==
version "5.23.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz#dfdf8d1c9065e4b52f8a598356138931c07305f9"
integrity sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.3.3"
@ -4153,25 +4153,25 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
pdfkit@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.19.1.tgz#633d5f031ce6f1ba6ce141325c6cf87fa6acb0a2"
integrity sha512-6Gzk+wDwTs4VSxsR5rCMTnIl5nlmkye1oWB0l2hDB1EX6ZNSIBroKQEv+2+fPPn+stVjyqzmsqRJVDfB9fo5DA==
pdfkit@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/pdfkit/-/pdfkit-0.18.0.tgz#573efa7f4c78a8ab1362232a05a589b97b292216"
integrity sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==
dependencies:
"@noble/ciphers" "^1.0.0"
"@noble/hashes" "^1.6.0"
fontkit "^2.0.4"
js-md5 "^0.8.3"
linebreak "^1.1.0"
png-js "^1.1.0"
png-js "^1.0.0"
pdfmake@^0.3.7:
version "0.3.11"
resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.3.11.tgz#b4504d19b8f31fa5063dc1b847b060faa4f7c5bb"
integrity sha512-Uc49J9hUMyuqJk+U+PxlpBpPr96A4HOOfesGx609EPr2ue82+5/Smq/KTAkEqh0/jUGSi1fumvqZ5yAWijJTJg==
version "0.3.9"
resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.3.9.tgz#c837b471aa81ea0797b7cdac2a03db8ba65e1b7f"
integrity sha512-V8VIcjMG/yk7oJYhYjtvyLXvop3w3zcIB0bHdG3z1vuW8nVeemzRWu7YrOG7QA9KYaf858MZnCUG6hQ1Bei0vA==
dependencies:
linebreak "^1.1.0"
pdfkit "^0.19.1"
pdfkit "^0.18.0"
xmldoc "^2.0.3"
picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
@ -4196,7 +4196,7 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
png-js@^1.1.0:
png-js@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.1.0.tgz#60a135216601f807b88a6d61ac93bd42a32c5ee1"
integrity sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==
@ -4423,9 +4423,9 @@ postcss-reduce-transforms@^7.0.3:
postcss-value-parser "^4.2.0"
postcss-selector-parser@^7.0.0, postcss-selector-parser@^7.1.1:
version "7.1.4"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz#69dc7a526517572ff6b150e352b36a016017b485"
integrity sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==
version "7.1.1"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz#e75d2e0d843f620e5df69076166f4e16f891cb9f"
integrity sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
@ -4549,9 +4549,9 @@ regjsgen@^0.8.0:
integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==
regjsparser@^0.13.0:
version "0.13.2"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.13.2.tgz#f654734b5c588b22ba3e21693b30523417180808"
integrity sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==
version "0.13.1"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.13.1.tgz#0593cbacb27527927692030928ae4d3b878d6f8d"
integrity sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==
dependencies:
jsesc "~3.1.0"
@ -4733,9 +4733,9 @@ semver@^6.3.1:
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.2, semver@^7.3.4, semver@^7.6.3:
version "7.8.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.4.tgz#c73eceebae0616934be8dff28a7fd70757c8e696"
integrity sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==
version "7.8.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.2.tgz#194bd65723a28cf82542d2bf176b91c26b343be1"
integrity sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==
serialize-javascript@^6.0.2:
version "6.0.2"
@ -5171,10 +5171,11 @@ vfile@^6.0.0:
vfile-message "^4.0.0"
watchpack@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.2.tgz#e12e82d84674266fc1c6dbfe38891b92ff0522ec"
integrity sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==
version "2.5.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102"
integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
web-namespaces@^2.0.0: