mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-05 17:01:35 +00:00
Compare commits
10 commits
247fed7d74
...
7054c51490
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7054c51490 | ||
|
|
808af0d3cd | ||
|
|
b14fc0e22a | ||
|
|
f7259a118b | ||
|
|
be60c4363c | ||
|
|
631db7df31 | ||
|
|
781ea45633 | ||
|
|
0eee161630 | ||
|
|
7a1b9b8ce1 | ||
|
|
3fcb5ce82e |
19 changed files with 1653 additions and 1229 deletions
|
|
@ -13,8 +13,8 @@
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"amphp/http-client": "^5.1",
|
"amphp/http-client": "^5.1",
|
||||||
"api-platform/doctrine-orm": "^4.1",
|
"api-platform/doctrine-orm": "^4.1",
|
||||||
"api-platform/symfony": "^4.0.0",
|
|
||||||
"api-platform/json-api": "^4.0.0",
|
"api-platform/json-api": "^4.0.0",
|
||||||
|
"api-platform/symfony": "^4.0.0",
|
||||||
"beberlei/doctrineextensions": "^1.2",
|
"beberlei/doctrineextensions": "^1.2",
|
||||||
"brick/math": "^0.13.1",
|
"brick/math": "^0.13.1",
|
||||||
"composer/ca-bundle": "^1.5",
|
"composer/ca-bundle": "^1.5",
|
||||||
|
|
@ -25,16 +25,16 @@
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.0",
|
"doctrine/doctrine-migrations-bundle": "^3.0",
|
||||||
"doctrine/orm": "^3.2.0",
|
"doctrine/orm": "^3.2.0",
|
||||||
"dompdf/dompdf": "^v3.0.0",
|
"dompdf/dompdf": "^v3.0.0",
|
||||||
"erusev/parsedown": "^1.7",
|
|
||||||
"florianv/swap": "^4.0",
|
"florianv/swap": "^4.0",
|
||||||
"florianv/swap-bundle": "dev-master",
|
"florianv/swap-bundle": "dev-master",
|
||||||
"gregwar/captcha-bundle": "^2.1.0",
|
"gregwar/captcha-bundle": "^2.1.0",
|
||||||
"hshn/base64-encoded-file": "^5.0",
|
"hshn/base64-encoded-file": "^5.0",
|
||||||
"jbtronics/2fa-webauthn": "^v2.2.0",
|
"jbtronics/2fa-webauthn": "^3.0.0",
|
||||||
"jbtronics/dompdf-font-loader-bundle": "^1.0.0",
|
"jbtronics/dompdf-font-loader-bundle": "^1.0.0",
|
||||||
"jbtronics/settings-bundle": "^v2.6.0",
|
"jbtronics/settings-bundle": "^v2.6.0",
|
||||||
"jfcherng/php-diff": "^6.14",
|
"jfcherng/php-diff": "^6.14",
|
||||||
"knpuniversity/oauth2-client-bundle": "^2.15",
|
"knpuniversity/oauth2-client-bundle": "^2.15",
|
||||||
|
"league/commonmark": "^2.7",
|
||||||
"league/csv": "^9.8.0",
|
"league/csv": "^9.8.0",
|
||||||
"league/html-to-markdown": "^5.0.1",
|
"league/html-to-markdown": "^5.0.1",
|
||||||
"liip/imagine-bundle": "^2.2",
|
"liip/imagine-bundle": "^2.2",
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"twig/intl-extra": "^3.8",
|
"twig/intl-extra": "^3.8",
|
||||||
"twig/markdown-extra": "^3.8",
|
"twig/markdown-extra": "^3.8",
|
||||||
"twig/string-extra": "^3.8",
|
"twig/string-extra": "^3.8",
|
||||||
"web-auth/webauthn-symfony-bundle": "^4.0.0"
|
"web-auth/webauthn-symfony-bundle": "^5.0.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"dama/doctrine-test-bundle": "^v8.0.0",
|
"dama/doctrine-test-bundle": "^v8.0.0",
|
||||||
|
|
|
||||||
1075
composer.lock
generated
1075
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -32,5 +32,5 @@ return [
|
||||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||||
Jbtronics\SettingsBundle\JbtronicsSettingsBundle::class => ['all' => true],
|
Jbtronics\SettingsBundle\JbtronicsSettingsBundle::class => ['all' => true],
|
||||||
Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true],
|
Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true],
|
||||||
\ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
|
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,6 @@ doctrine:
|
||||||
tinyint:
|
tinyint:
|
||||||
class: App\Doctrine\Types\TinyIntType
|
class: App\Doctrine\Types\TinyIntType
|
||||||
|
|
||||||
# This was removed in doctrine/orm 4.0 but we need it for the WebauthnKey entity
|
|
||||||
array:
|
|
||||||
class: App\Doctrine\Types\ArrayType
|
|
||||||
|
|
||||||
schema_filter: ~^(?!internal)~
|
schema_filter: ~^(?!internal)~
|
||||||
# Only enable this when needed
|
# Only enable this when needed
|
||||||
profiling_collect_backtrace: false
|
profiling_collect_backtrace: false
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ security:
|
||||||
|
|
||||||
firewalls:
|
firewalls:
|
||||||
dev:
|
dev:
|
||||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
pattern: ^/(_(profiler|wdt)|css|images|js|\.well-known)/
|
||||||
security: false
|
security: false
|
||||||
main:
|
main:
|
||||||
provider: app_user_provider
|
provider: app_user_provider
|
||||||
|
|
|
||||||
27
docs/upgrade/1_to_2.md
Normal file
27
docs/upgrade/1_to_2.md
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Upgrade from Part-DB 1.x to 2.x
|
||||||
|
nav_order: 1
|
||||||
|
has_children: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Upgrade from Part-DB 1.x to 2.x
|
||||||
|
|
||||||
|
Part-DB 2.0 is a major release that changes a lot of things internally, but it is still compatible with Part-DB 1.x.
|
||||||
|
Depending on your preferences, you will have to do some changes to your Part-DB installation, this document will guide
|
||||||
|
you through the upgrade process.
|
||||||
|
|
||||||
|
## New requirements
|
||||||
|
*If you are running Part-DB inside a docker container, you can skip this section, as the new requirements are already
|
||||||
|
fulfilled by the official Part-DB docker image.*
|
||||||
|
|
||||||
|
Part-DB 2.0 requires at least PHP 8.2 (newer versions are recommended). So if your existing Part-DB installation is still
|
||||||
|
running PHP 8.1, you will have to upgrade your PHP version first.
|
||||||
|
The minimum required version of node.js is now 20.0 or newer, so if you are using 18.0, you will have to upgrade it too.
|
||||||
|
|
||||||
|
Most distributions should have the possibility to get backports for PHP 8.4 and modern nodejs, so you should be able to
|
||||||
|
easily upgrade your system to the new requirements. Otherwise, you can use the official Part-DB docker image, which
|
||||||
|
ships all required dependencies and is always up to date with the latest requirements, so that you do not have to worry
|
||||||
|
about the requirements at all.
|
||||||
|
|
||||||
|
|
||||||
9
docs/upgrade/index.md
Normal file
9
docs/upgrade/index.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Upgrade
|
||||||
|
nav_order: 7
|
||||||
|
has_children: true
|
||||||
|
---
|
||||||
|
|
||||||
|
This section provides information on how to upgrade Part-DB to the latest version.
|
||||||
|
This is intended for major release upgrades, where requirements or things changes significantly.
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
layout: default
|
layout: default
|
||||||
title: Upgrade from legacy Part-DB version (<1.0)
|
title: Upgrade from legacy Part-DB version (<1.0)
|
||||||
nav_order: 100
|
nav_order: 100
|
||||||
|
redirect_from: /upgrade_legacy
|
||||||
---
|
---
|
||||||
|
|
||||||
# Upgrade from legacy Part-DB version
|
# Upgrade from legacy Part-DB version
|
||||||
75
migrations/Version20250813214628.php
Normal file
75
migrations/Version20250813214628.php
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use App\Migration\AbstractMultiPlatformMigration;
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20250813214628 extends AbstractMultiPlatformMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Migrate webauthn_keys transports and other_ui fields to JSON type';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function convertArrayToJson(): void
|
||||||
|
{
|
||||||
|
$connection = $this->connection;
|
||||||
|
$rows = $connection->fetchAllAssociative('SELECT id, transports, other_ui FROM webauthn_keys');
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$id = $row['id'];
|
||||||
|
$new_transports = json_encode(unserialize($row['transports'], ['allowed_classes' => false]),
|
||||||
|
JSON_THROW_ON_ERROR);
|
||||||
|
$new_other_ui = json_encode(unserialize($row['other_ui'], ['allowed_classes' => false]),
|
||||||
|
JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
$connection->executeStatement(
|
||||||
|
'UPDATE webauthn_keys SET transports = :transports, other_ui = :other_ui WHERE id = :id',
|
||||||
|
[
|
||||||
|
'transports' => $new_transports,
|
||||||
|
'other_ui' => $new_other_ui,
|
||||||
|
'id' => $id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mySQLUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->convertArrayToJson();
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys CHANGE transports transports JSON NOT NULL, CHANGE other_ui other_ui JSON DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mySQLDown(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys CHANGE transports transports LONGTEXT NOT NULL, CHANGE other_ui other_ui LONGTEXT DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sqLiteUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
//As there is no JSON type in SQLite, we only need to convert the data.
|
||||||
|
$this->convertArrayToJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sqLiteDown(Schema $schema): void
|
||||||
|
{
|
||||||
|
//Nothing to do here, as SQLite does not support JSON type and we are not changing the column type.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postgreSQLUp(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->convertArrayToJson();
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys ALTER transports TYPE JSON USING transports::JSON');
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys ALTER other_ui TYPE JSON USING other_ui::JSON');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postgreSQLDown(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys ALTER transports TYPE TEXT');
|
||||||
|
$this->addSql('ALTER TABLE webauthn_keys ALTER other_ui TYPE TEXT');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2024 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Doctrine\Types;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
|
||||||
use Doctrine\DBAL\Types\Exception\SerializationFailed;
|
|
||||||
use Doctrine\DBAL\Types\Type;
|
|
||||||
use Doctrine\Deprecations\Deprecation;
|
|
||||||
|
|
||||||
use function is_resource;
|
|
||||||
use function restore_error_handler;
|
|
||||||
use function serialize;
|
|
||||||
use function set_error_handler;
|
|
||||||
use function stream_get_contents;
|
|
||||||
use function unserialize;
|
|
||||||
|
|
||||||
use const E_DEPRECATED;
|
|
||||||
use const E_USER_DEPRECATED;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is taken from doctrine ORM 3.8. https://github.com/doctrine/dbal/blob/3.8.x/src/Types/ArrayType.php
|
|
||||||
*
|
|
||||||
* It was removed in doctrine ORM 4.0. However, we require it for backward compatibility with WebauthnKey.
|
|
||||||
* Therefore, we manually added it here as a custom type as a forward compatibility layer.
|
|
||||||
*/
|
|
||||||
class ArrayType extends Type
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
|
|
||||||
{
|
|
||||||
return $platform->getClobTypeDeclarationSQL($column);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): string
|
|
||||||
{
|
|
||||||
return serialize($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed
|
|
||||||
{
|
|
||||||
if ($value === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = is_resource($value) ? stream_get_contents($value) : $value;
|
|
||||||
|
|
||||||
set_error_handler(function (int $code, string $message): bool {
|
|
||||||
if ($code === E_DEPRECATED || $code === E_USER_DEPRECATED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Change to original code. Use SerializationFailed instead of ConversionException.
|
|
||||||
throw new SerializationFailed("Serialization failed (Code $code): " . $message);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
//Change to original code. Use false for allowed_classes, to avoid unsafe unserialization of objects.
|
|
||||||
return unserialize($value, ['allowed_classes' => false]);
|
|
||||||
} finally {
|
|
||||||
restore_error_handler();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return "array";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
|
|
||||||
{
|
|
||||||
Deprecation::triggerIfCalledFromOutside(
|
|
||||||
'doctrine/dbal',
|
|
||||||
'https://github.com/doctrine/dbal/pull/5509',
|
|
||||||
'%s is deprecated.',
|
|
||||||
__METHOD__,
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -100,16 +100,19 @@ class WebauthnKey extends BasePublicKeyCredentialSource implements TimeStampable
|
||||||
public static function fromRegistration(BasePublicKeyCredentialSource $registration): self
|
public static function fromRegistration(BasePublicKeyCredentialSource $registration): self
|
||||||
{
|
{
|
||||||
return new self(
|
return new self(
|
||||||
$registration->getPublicKeyCredentialId(),
|
publicKeyCredentialId: $registration->publicKeyCredentialId,
|
||||||
$registration->getType(),
|
type: $registration->type,
|
||||||
$registration->getTransports(),
|
transports: $registration->transports,
|
||||||
$registration->getAttestationType(),
|
attestationType: $registration->attestationType,
|
||||||
$registration->getTrustPath(),
|
trustPath: $registration->trustPath,
|
||||||
$registration->getAaguid(),
|
aaguid: $registration->aaguid,
|
||||||
$registration->getCredentialPublicKey(),
|
credentialPublicKey: $registration->credentialPublicKey,
|
||||||
$registration->getUserHandle(),
|
userHandle: $registration->userHandle,
|
||||||
$registration->getCounter(),
|
counter: $registration->counter,
|
||||||
$registration->getOtherUI()
|
otherUI: $registration->otherUI,
|
||||||
|
backupEligible: $registration->backupEligible,
|
||||||
|
backupStatus: $registration->backupStatus,
|
||||||
|
uvInitialized: $registration->uvInitialized,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\TwoFactorProviderInterface
|
||||||
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
|
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated;
|
use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated;
|
||||||
|
use Webauthn\PublicKeyCredential;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class decorates the Webauthn TwoFactorProvider and adds additional logic which allows us to set a last used date
|
* This class decorates the Webauthn TwoFactorProvider and adds additional logic which allows us to set a last used date
|
||||||
|
|
@ -88,10 +89,12 @@ class WebauthnKeyLastUseTwoFactorProvider implements TwoFactorProviderInterface
|
||||||
|
|
||||||
private function getWebauthnKeyFromCode(string $authenticationCode): ?WebauthnKey
|
private function getWebauthnKeyFromCode(string $authenticationCode): ?WebauthnKey
|
||||||
{
|
{
|
||||||
$publicKeyCredentialLoader = $this->webauthnProvider->getPublicKeyCredentialLoader();
|
$serializer = $this->webauthnProvider->getWebauthnSerializer();
|
||||||
|
|
||||||
//Try to load the public key credential from the code
|
//Try to load the public key credential from the code
|
||||||
$publicKeyCredential = $publicKeyCredentialLoader->load($authenticationCode);
|
$publicKeyCredential = $serializer->deserialize($authenticationCode, PublicKeyCredential::class, 'json', [
|
||||||
|
'json_decode_options' => JSON_THROW_ON_ERROR
|
||||||
|
]);
|
||||||
|
|
||||||
//Find the credential source for the given credential id
|
//Find the credential source for the given credential id
|
||||||
$publicKeyCredentialSource = $this->publicKeyCredentialSourceRepository->findOneByCredentialId($publicKeyCredential->rawId);
|
$publicKeyCredentialSource = $this->publicKeyCredentialSourceRepository->findOneByCredentialId($publicKeyCredential->rawId);
|
||||||
|
|
@ -103,4 +106,4 @@ class WebauthnKeyLastUseTwoFactorProvider implements TwoFactorProviderInterface
|
||||||
|
|
||||||
return $publicKeyCredentialSource;
|
return $publicKeyCredentialSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,12 @@ class AttachmentURLGenerator
|
||||||
/**
|
/**
|
||||||
* Returns a URL to a thumbnail of the attachment file.
|
* Returns a URL to a thumbnail of the attachment file.
|
||||||
* For external files the original URL is returned.
|
* For external files the original URL is returned.
|
||||||
* @return string|null The URL or null if the attachment file is not existing
|
* @return string|null The URL or null if the attachment file is not existing or is invalid
|
||||||
*/
|
*/
|
||||||
public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string
|
public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string
|
||||||
{
|
{
|
||||||
if (!$attachment->isPicture()) {
|
if (!$attachment->isPicture()) {
|
||||||
throw new InvalidArgumentException('Thumbnail creation only works for picture attachments!');
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$attachment->hasInternal()){
|
if (!$attachment->hasInternal()){
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class EntityImporter
|
||||||
/**
|
/**
|
||||||
* Creates many entries at once, based on a (text) list of name.
|
* Creates many entries at once, based on a (text) list of name.
|
||||||
* The created entities are not persisted to database yet, so you have to do it yourself.
|
* The created entities are not persisted to database yet, so you have to do it yourself.
|
||||||
|
* It returns all entities in the hierachy chain (even if they are already persisted).
|
||||||
*
|
*
|
||||||
* @template T of AbstractNamedDBElement
|
* @template T of AbstractNamedDBElement
|
||||||
* @param string $lines The list of names seperated by \n
|
* @param string $lines The list of names seperated by \n
|
||||||
|
|
@ -132,32 +133,38 @@ class EntityImporter
|
||||||
//We can only use the getNewEntityFromPath function, if the repository is a StructuralDBElementRepository
|
//We can only use the getNewEntityFromPath function, if the repository is a StructuralDBElementRepository
|
||||||
if ($repo instanceof StructuralDBElementRepository) {
|
if ($repo instanceof StructuralDBElementRepository) {
|
||||||
$entities = $repo->getNewEntityFromPath($new_path);
|
$entities = $repo->getNewEntityFromPath($new_path);
|
||||||
$entity = end($entities);
|
if ($entities === []) {
|
||||||
if ($entity === false) {
|
|
||||||
throw new InvalidArgumentException('getNewEntityFromPath returned an empty array!');
|
throw new InvalidArgumentException('getNewEntityFromPath returned an empty array!');
|
||||||
}
|
}
|
||||||
} else { //Otherwise just create a new entity
|
} else { //Otherwise just create a new entity
|
||||||
$entity = new $class_name;
|
$entity = new $class_name;
|
||||||
$entity->setName($name);
|
$entity->setName($name);
|
||||||
|
$entities = [$entity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Validate entity
|
//Validate entity
|
||||||
$tmp = $this->validator->validate($entity);
|
foreach ($entities as $entity) {
|
||||||
//If no error occured, write entry to DB:
|
$tmp = $this->validator->validate($entity);
|
||||||
if (0 === count($tmp)) {
|
//If no error occured, write entry to DB:
|
||||||
$valid_entities[] = $entity;
|
if (0 === count($tmp)) {
|
||||||
} else { //Otherwise log error
|
$valid_entities[] = $entity;
|
||||||
$errors[] = [
|
} else { //Otherwise log error
|
||||||
'entity' => $entity,
|
$errors[] = [
|
||||||
'violations' => $tmp,
|
'entity' => $entity,
|
||||||
];
|
'violations' => $tmp,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$last_element = $entity;
|
$last_element = end($entities);
|
||||||
|
if ($last_element === false) {
|
||||||
|
$last_element = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $valid_entities;
|
//Only return objects once
|
||||||
|
return array_values(array_unique($valid_entities));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ use App\Entity\Parts\Manufacturer;
|
||||||
use App\Entity\Parts\Footprint;
|
use App\Entity\Parts\Footprint;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Services\Formatters\SIFormatter;
|
use App\Services\Formatters\SIFormatter;
|
||||||
use Parsedown;
|
use League\CommonMark\Environment\Environment;
|
||||||
|
use League\CommonMark\Extension\InlinesOnly\InlinesOnlyExtension;
|
||||||
|
use League\CommonMark\MarkdownConverter;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,8 +56,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
*/
|
*/
|
||||||
final class PartProvider implements PlaceholderProviderInterface
|
final class PartProvider implements PlaceholderProviderInterface
|
||||||
{
|
{
|
||||||
|
private readonly MarkdownConverter $inlineConverter;
|
||||||
|
|
||||||
public function __construct(private readonly SIFormatter $siFormatter, private readonly TranslatorInterface $translator)
|
public function __construct(private readonly SIFormatter $siFormatter, private readonly TranslatorInterface $translator)
|
||||||
{
|
{
|
||||||
|
$environment = new Environment();
|
||||||
|
$environment->addExtension(new InlinesOnlyExtension());
|
||||||
|
$this->inlineConverter = new MarkdownConverter($environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function replace(string $placeholder, object $part, array $options = []): ?string
|
public function replace(string $placeholder, object $part, array $options = []): ?string
|
||||||
|
|
@ -112,22 +119,20 @@ final class PartProvider implements PlaceholderProviderInterface
|
||||||
return $this->translator->trans($part->getManufacturingStatus()->toTranslationKey());
|
return $this->translator->trans($part->getManufacturingStatus()->toTranslationKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsedown = new Parsedown();
|
|
||||||
|
|
||||||
if ('[[DESCRIPTION]]' === $placeholder) {
|
if ('[[DESCRIPTION]]' === $placeholder) {
|
||||||
return $parsedown->line($part->getDescription());
|
return trim($this->inlineConverter->convert($part->getDescription())->getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[DESCRIPTION_T]]' === $placeholder) {
|
if ('[[DESCRIPTION_T]]' === $placeholder) {
|
||||||
return strip_tags((string) $parsedown->line($part->getDescription()));
|
return trim(strip_tags($this->inlineConverter->convert($part->getDescription())->getContent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[COMMENT]]' === $placeholder) {
|
if ('[[COMMENT]]' === $placeholder) {
|
||||||
return $parsedown->line($part->getComment());
|
return trim($this->inlineConverter->convert($part->getComment())->getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('[[COMMENT_T]]' === $placeholder) {
|
if ('[[COMMENT_T]]' === $placeholder) {
|
||||||
return strip_tags((string) $parsedown->line($part->getComment()));
|
return trim(strip_tags($this->inlineConverter->convert($part->getComment())->getContent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -133,9 +133,6 @@
|
||||||
"ekino/phpstan-banned-code": {
|
"ekino/phpstan-banned-code": {
|
||||||
"version": "v0.3.1"
|
"version": "v0.3.1"
|
||||||
},
|
},
|
||||||
"erusev/parsedown": {
|
|
||||||
"version": "1.7.4"
|
|
||||||
},
|
|
||||||
"florianv/exchanger": {
|
"florianv/exchanger": {
|
||||||
"version": "1.4.1"
|
"version": "1.4.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,8 @@ class EntityImporterTest extends WebTestCase
|
||||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
$em = self::getContainer()->get(EntityManagerInterface::class);
|
||||||
$parent = $em->find(AttachmentType::class, 1);
|
$parent = $em->find(AttachmentType::class, 1);
|
||||||
$results = $this->service->massCreation($lines, AttachmentType::class, $parent, $errors);
|
$results = $this->service->massCreation($lines, AttachmentType::class, $parent, $errors);
|
||||||
$this->assertCount(3, $results);
|
$this->assertCount(4, $results);
|
||||||
$this->assertSame($parent, $results[0]->getParent());
|
$this->assertSame("Test 1", $results[1]->getName());
|
||||||
|
|
||||||
//Test for addition of existing elements
|
//Test for addition of existing elements
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
@ -113,6 +113,31 @@ EOT;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMassCreationArrow(): void
|
||||||
|
{
|
||||||
|
$input = <<<EOT
|
||||||
|
Test1 -> Test1.1
|
||||||
|
Test1 -> Test1.2
|
||||||
|
Test2 -> Test2.1
|
||||||
|
Test1
|
||||||
|
Test1.3
|
||||||
|
EOT;
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
$results = $this->service->massCreation($input, AttachmentType::class, null, $errors);
|
||||||
|
|
||||||
|
//We have 6 elements, and 0 errors
|
||||||
|
$this->assertCount(0, $errors);
|
||||||
|
$this->assertCount(6, $results);
|
||||||
|
|
||||||
|
$this->assertEquals('Test1', $results[0]->getName());
|
||||||
|
$this->assertEquals('Test1.1', $results[1]->getName());
|
||||||
|
$this->assertEquals('Test1.2', $results[2]->getName());
|
||||||
|
$this->assertEquals('Test2', $results[3]->getName());
|
||||||
|
$this->assertEquals('Test2.1', $results[4]->getName());
|
||||||
|
$this->assertEquals('Test1.3', $results[5]->getName());
|
||||||
|
}
|
||||||
|
|
||||||
public function testMassCreationNested(): void
|
public function testMassCreationNested(): void
|
||||||
{
|
{
|
||||||
$input = <<<EOT
|
$input = <<<EOT
|
||||||
|
|
@ -132,15 +157,15 @@ EOT;
|
||||||
|
|
||||||
//We have 7 elements, and 0 errors
|
//We have 7 elements, and 0 errors
|
||||||
$this->assertCount(0, $errors);
|
$this->assertCount(0, $errors);
|
||||||
$this->assertCount(7, $results);
|
$this->assertCount(8, $results);
|
||||||
|
|
||||||
$element1 = $results[0];
|
$element1 = $results[1];
|
||||||
$element11 = $results[1];
|
$element11 = $results[2];
|
||||||
$element111 = $results[2];
|
$element111 = $results[3];
|
||||||
$element112 = $results[3];
|
$element112 = $results[4];
|
||||||
$element12 = $results[4];
|
$element12 = $results[5];
|
||||||
$element121 = $results[5];
|
$element121 = $results[6];
|
||||||
$element2 = $results[6];
|
$element2 = $results[7];
|
||||||
|
|
||||||
$this->assertSame('Test 1', $element1->getName());
|
$this->assertSame('Test 1', $element1->getName());
|
||||||
$this->assertSame('Test 1.1', $element11->getName());
|
$this->assertSame('Test 1.1', $element11->getName());
|
||||||
|
|
|
||||||
|
|
@ -7157,12 +7157,15 @@ Exampletown</target>
|
||||||
</notes>
|
</notes>
|
||||||
<segment state="translated">
|
<segment state="translated">
|
||||||
<source>mass_creation.lines.placeholder</source>
|
<source>mass_creation.lines.placeholder</source>
|
||||||
<target>Element 1
|
<target><![CDATA[Element 1
|
||||||
Element 1.1
|
Element 1.1
|
||||||
Element 1.1.1
|
Element 1.1.1
|
||||||
Element 1.2
|
Element 1.2
|
||||||
Element 2
|
Element 2
|
||||||
Element 3</target>
|
Element 3
|
||||||
|
|
||||||
|
Element 1 -> Element 1.1
|
||||||
|
Element 1 -> Element 1.2]]></target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="TWSqPFi" name="entity.mass_creation.btn">
|
<unit id="TWSqPFi" name="entity.mass_creation.btn">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue