From 6443d8e2bfbf35559b2607779f6c9a056a34d4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Fri, 7 Apr 2023 22:44:59 +0200 Subject: [PATCH 01/17] Log the name of the CLI user, when actions were done from the CLI. --- src/Command/Logs/ShowEventLogCommand.php | 12 ++++- src/DataTables/LogDataTable.php | 8 +++ src/Entity/LogSystem/AbstractLogEntry.php | 20 +++++++ src/Services/LogSystem/EventLogger.php | 11 +++- src/Services/Misc/ConsoleInfoHelper.php | 63 +++++++++++++++++++++++ translations/messages.en.xlf | 12 +++++ 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/Services/Misc/ConsoleInfoHelper.php diff --git a/src/Command/Logs/ShowEventLogCommand.php b/src/Command/Logs/ShowEventLogCommand.php index d3ac5c7f..517c582f 100644 --- a/src/Command/Logs/ShowEventLogCommand.php +++ b/src/Command/Logs/ShowEventLogCommand.php @@ -147,11 +147,21 @@ class ShowEventLogCommand extends Command $target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass()); } + if ($entry->getUser()) { + $user = $entry->getUser()->getFullName(true); + } else { + if ($entry->isCLIUser()) { + $user = $entry->getCLIUsername() . ' [CLI]'; + } else { + $user = $entry->getUsername() . ' [deleted]'; + } + } + $row = [ $entry->getID(), $entry->getTimestamp()->format('Y-m-d H:i:s'), $entry->getType(), - $entry->getUser()->getFullName(true), + $user, $target_class, $target_name, ]; diff --git a/src/DataTables/LogDataTable.php b/src/DataTables/LogDataTable.php index 5d23446a..0833021c 100644 --- a/src/DataTables/LogDataTable.php +++ b/src/DataTables/LogDataTable.php @@ -226,6 +226,14 @@ class LogDataTable implements DataTableTypeInterface //If user was deleted, show the info from the username field if ($user === null) { + if ($context->isCLIUser()) { + return sprintf('%s [%s]', + htmlentities($context->getCLIUsername()), + $this->translator->trans('log.cli_user') + ); + } + + //Else we just deal with a deleted user return sprintf( '@%s [%s]', htmlentities($context->getUsername()), diff --git a/src/Entity/LogSystem/AbstractLogEntry.php b/src/Entity/LogSystem/AbstractLogEntry.php index e2dca513..460e561a 100644 --- a/src/Entity/LogSystem/AbstractLogEntry.php +++ b/src/Entity/LogSystem/AbstractLogEntry.php @@ -216,6 +216,26 @@ abstract class AbstractLogEntry extends AbstractDBElement return $this; } + public function setCLIUser(?string $cli_username): self + { + $this->user = null; + $this->username = '!!!CLI ' . $cli_username; + return $this; + } + + public function isCLIUser(): bool + { + return strpos($this->username, '!!!CLI ') === 0; + } + + public function getCLIUsername(): ?string + { + if ($this->isCLIUser()) { + return substr($this->username, 7); + } + return null; + } + /** * Retuns the username of the user that caused the event (useful if the user was deleted). * diff --git a/src/Services/LogSystem/EventLogger.php b/src/Services/LogSystem/EventLogger.php index 80ee067e..9b4349b5 100644 --- a/src/Services/LogSystem/EventLogger.php +++ b/src/Services/LogSystem/EventLogger.php @@ -24,6 +24,7 @@ namespace App\Services\LogSystem; use App\Entity\LogSystem\AbstractLogEntry; use App\Entity\UserSystem\User; +use App\Services\Misc\ConsoleInfoHelper; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Security\Core\Security; @@ -34,14 +35,17 @@ class EventLogger protected array $whitelist; protected EntityManagerInterface $em; protected Security $security; + protected ConsoleInfoHelper $console_info_helper; - public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em, Security $security) + public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em, + Security $security, ConsoleInfoHelper $console_info_helper) { $this->minimum_log_level = $minimum_log_level; $this->blacklist = $blacklist; $this->whitelist = $whitelist; $this->em = $em; $this->security = $security; + $this->console_info_helper = $console_info_helper; } /** @@ -67,6 +71,11 @@ class EventLogger $logEntry->setUser($user); } + //Set the console user info, if the log entry was created in a console command + if ($this->console_info_helper->isCLI()) { + $logEntry->setCLIUser($this->console_info_helper->getCLIUser() ?? 'Unknown'); + } + if ($this->shouldBeAdded($logEntry)) { $this->em->persist($logEntry); diff --git a/src/Services/Misc/ConsoleInfoHelper.php b/src/Services/Misc/ConsoleInfoHelper.php new file mode 100644 index 00000000..8aea004e --- /dev/null +++ b/src/Services/Misc/ConsoleInfoHelper.php @@ -0,0 +1,63 @@ +. + */ + +namespace App\Services\Misc; + +class ConsoleInfoHelper +{ + /** + * Returns true if the current script is executed in a CLI environment. + * @return bool true if the current script is executed in a CLI environment, false otherwise + */ + public function isCLI(): bool + { + return \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true); + } + + /** + * Returns the username of the user who started the current script if possible. + * @return string|null the username of the user who started the current script if possible, null otherwise + */ + public function getCLIUser(): ?string + { + if (!$this->isCLI()) { + return null; + } + + //Try to use the posix extension if available (Linux) + if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { + $user = posix_getpwuid(posix_geteuid()); + return $user['name']; + } + + //Try to retrieve the name via the environment variable Username (Windows) + if (isset($_SERVER['USERNAME'])) { + return $_SERVER['USERNAME']; + } + + //Try to retrieve the name via the environment variable USER (Linux) + if (isset($_SERVER['USER'])) { + return $_SERVER['USER']; + } + + //Otherwise we can't determine the username + return null; + } +} \ No newline at end of file diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index e2cb67fb..eeee86b6 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11259,5 +11259,17 @@ Element 3 Less than desired + + + log.cli_user + CLI user + + + + + log.element_edited.changed_fields.part_owner_must_match + Part owner must match storage location owner + + From 8bccab258a107fbe89961700e8ee3396a2d1f072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Fri, 7 Apr 2023 23:11:29 +0200 Subject: [PATCH 02/17] Prevent appearance of a popup for a short time after deletion of an element on firefox Related to issue #258 --- assets/js/error_handler.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/js/error_handler.js b/assets/js/error_handler.js index 579f4298..7f047af9 100644 --- a/assets/js/error_handler.js +++ b/assets/js/error_handler.js @@ -155,6 +155,11 @@ class ErrorHandlerHelper { return; } + //Skip 404 errors, on admin pages (as this causes a popup on deletion in firefox) + if (response.status == 404 && event.target.id === 'admin-content-frame') { + return; + } + if(!response.ok) { response.text().then(responseHTML => { From f91b719542144c6c7ee5acb682862d777ebaf67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 00:35:31 +0200 Subject: [PATCH 03/17] Added a filter constraint for parts where instock is "less than desired" Fixes issue #257 --- .../Part/LessThanDesiredConstraint.php | 47 +++++++++++++++++++ src/DataTables/Filters/PartFilter.php | 11 +++++ src/Form/Filters/PartFilterType.php | 4 ++ templates/parts/lists/_filter.html.twig | 1 + translations/messages.en.xlf | 6 +++ 5 files changed, 69 insertions(+) create mode 100644 src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php diff --git a/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php new file mode 100644 index 00000000..34d5c157 --- /dev/null +++ b/src/DataTables/Filters/Constraints/Part/LessThanDesiredConstraint.php @@ -0,0 +1,47 @@ +. + */ + +namespace App\DataTables\Filters\Constraints\Part; + +use App\DataTables\Filters\Constraints\BooleanConstraint; +use Doctrine\ORM\QueryBuilder; + +class LessThanDesiredConstraint extends BooleanConstraint +{ + public function __construct(string $property = null, string $identifier = null, ?bool $default_value = null) + { + parent::__construct($property ?? 'amountSum', $identifier, $default_value); + } + + public function apply(QueryBuilder $queryBuilder): void + { + //Do not apply a filter if value is null (filter is set to ignore) + if(!$this->isEnabled()) { + return; + } + + //If value is true, we want to filter for parts with stock < desired stock + if ($this->value) { + $queryBuilder->andHaving('amountSum < part.minamount'); + } else { + $queryBuilder->andHaving('amountSum >= part.minamount'); + } + } +} \ No newline at end of file diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index dabf005e..9ce90cd6 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -26,6 +26,7 @@ use App\DataTables\Filters\Constraints\DateTimeConstraint; use App\DataTables\Filters\Constraints\EntityConstraint; use App\DataTables\Filters\Constraints\IntConstraint; use App\DataTables\Filters\Constraints\NumberConstraint; +use App\DataTables\Filters\Constraints\Part\LessThanDesiredConstraint; use App\DataTables\Filters\Constraints\Part\ParameterConstraint; use App\DataTables\Filters\Constraints\Part\TagsConstraint; use App\DataTables\Filters\Constraints\TextConstraint; @@ -68,6 +69,15 @@ class PartFilter implements FilterInterface protected EntityConstraint $storelocation; protected IntConstraint $lotCount; protected IntConstraint $amountSum; + protected LessThanDesiredConstraint $lessThanDesired; + + /** + * @return LessThanDesiredConstraint + */ + public function getLessThanDesired(): LessThanDesiredConstraint + { + return $this->lessThanDesired; + } protected BooleanConstraint $lotNeedsRefill; protected TextConstraint $lotDescription; protected BooleanConstraint $lotUnknownAmount; @@ -108,6 +118,7 @@ class PartFilter implements FilterInterface //We have to use Having here, as we use an alias column which is not supported on the where clause and would result in an error $this->amountSum = (new IntConstraint('amountSum'))->useHaving(); $this->lotCount = new IntConstraint('COUNT(partLots)'); + $this->lessThanDesired = new LessThanDesiredConstraint(); $this->storelocation = new EntityConstraint($nodesListBuilder, Storelocation::class, 'partLots.storage_location'); $this->lotNeedsRefill = new BooleanConstraint('partLots.needs_refill'); diff --git a/src/Form/Filters/PartFilterType.php b/src/Form/Filters/PartFilterType.php index 347948a2..668ea5ec 100644 --- a/src/Form/Filters/PartFilterType.php +++ b/src/Form/Filters/PartFilterType.php @@ -206,6 +206,10 @@ class PartFilterType extends AbstractType 'min' => 0, ]); + $builder->add('lessThanDesired', BooleanConstraintType::class, [ + 'label' => 'part.filter.lessThanDesired' + ]); + $builder->add('lotNeedsRefill', BooleanConstraintType::class, [ 'label' => 'part.filter.lotNeedsRefill' ]); diff --git a/templates/parts/lists/_filter.html.twig b/templates/parts/lists/_filter.html.twig index 9cf2ee02..4c40cd09 100644 --- a/templates/parts/lists/_filter.html.twig +++ b/templates/parts/lists/_filter.html.twig @@ -62,6 +62,7 @@ {{ form_row(filterForm.storelocation) }} {{ form_row(filterForm.minAmount) }} {{ form_row(filterForm.amountSum) }} + {{ form_row(filterForm.lessThanDesired) }} {{ form_row(filterForm.lotCount) }} {{ form_row(filterForm.lotExpirationDate) }} {{ form_row(filterForm.lotDescription) }} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index eeee86b6..f436a5e1 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11271,5 +11271,11 @@ Element 3 Part owner must match storage location owner + + + part.filter.lessThanDesired + + + From d32e902d1757ed2c458334258b317bb532f0711d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 00:44:34 +0200 Subject: [PATCH 04/17] Allow to filter by the lot owner --- src/DataTables/Filters/PartFilter.php | 28 ++++++++++++++++++------- src/Form/Filters/PartFilterType.php | 5 +++++ templates/parts/lists/_filter.html.twig | 1 + translations/messages.en.xlf | 6 ++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index 9ce90cd6..14ab1a9c 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -37,6 +37,8 @@ use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Storelocation; use App\Entity\Parts\Supplier; +use App\Entity\UserSystem\User; +use App\Form\Filters\Constraints\UserEntityConstraintType; use App\Services\Trees\NodesListBuilder; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\QueryBuilder; @@ -71,17 +73,12 @@ class PartFilter implements FilterInterface protected IntConstraint $amountSum; protected LessThanDesiredConstraint $lessThanDesired; - /** - * @return LessThanDesiredConstraint - */ - public function getLessThanDesired(): LessThanDesiredConstraint - { - return $this->lessThanDesired; - } protected BooleanConstraint $lotNeedsRefill; protected TextConstraint $lotDescription; protected BooleanConstraint $lotUnknownAmount; protected DateTimeConstraint $lotExpirationDate; + protected EntityConstraint $lotOwner; + protected EntityConstraint $measurementUnit; protected TextConstraint $manufacturer_product_url; protected TextConstraint $manufacturer_product_number; @@ -125,6 +122,7 @@ class PartFilter implements FilterInterface $this->lotUnknownAmount = new BooleanConstraint('partLots.instock_unknown'); $this->lotExpirationDate = new DateTimeConstraint('partLots.expiration_date'); $this->lotDescription = new TextConstraint('partLots.description'); + $this->lotOwner = new EntityConstraint($nodesListBuilder, User::class, 'partLots.owner'); $this->manufacturer = new EntityConstraint($nodesListBuilder, Manufacturer::class, 'part.manufacturer'); $this->manufacturer_product_number = new TextConstraint('part.manufacturer_product_number'); @@ -291,6 +289,14 @@ class PartFilter implements FilterInterface return $this->lotCount; } + /** + * @return EntityConstraint + */ + public function getLotOwner(): EntityConstraint + { + return $this->lotOwner; + } + /** * @return TagsConstraint */ @@ -394,7 +400,13 @@ class PartFilter implements FilterInterface return $this->obsolete; } - + /** + * @return LessThanDesiredConstraint + */ + public function getLessThanDesired(): LessThanDesiredConstraint + { + return $this->lessThanDesired; + } } diff --git a/src/Form/Filters/PartFilterType.php b/src/Form/Filters/PartFilterType.php index 668ea5ec..249b0c1c 100644 --- a/src/Form/Filters/PartFilterType.php +++ b/src/Form/Filters/PartFilterType.php @@ -36,6 +36,7 @@ use App\Form\Filters\Constraints\ParameterConstraintType; use App\Form\Filters\Constraints\StructuralEntityConstraintType; use App\Form\Filters\Constraints\TagsConstraintType; use App\Form\Filters\Constraints\TextConstraintType; +use App\Form\Filters\Constraints\UserEntityConstraintType; use Svg\Tag\Text; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; @@ -227,6 +228,10 @@ class PartFilterType extends AbstractType 'label' => 'part.filter.lotDescription', ]); + $builder->add('lotOwner', UserEntityConstraintType::class, [ + 'label' => 'part.filter.lotOwner', + ]); + /** * Attachments count */ diff --git a/templates/parts/lists/_filter.html.twig b/templates/parts/lists/_filter.html.twig index 4c40cd09..b2a2d712 100644 --- a/templates/parts/lists/_filter.html.twig +++ b/templates/parts/lists/_filter.html.twig @@ -66,6 +66,7 @@ {{ form_row(filterForm.lotCount) }} {{ form_row(filterForm.lotExpirationDate) }} {{ form_row(filterForm.lotDescription) }} + {{ form_row(filterForm.lotOwner) }} {{ form_row(filterForm.lotNeedsRefill) }} {{ form_row(filterForm.lotUnknownAmount) }} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index f436a5e1..c92e8a6f 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11277,5 +11277,11 @@ Element 3 + + + part.filter.lotOwner + Lot owner + + From bcda71cb2517c8492d485ceb5924df652f16c2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 00:50:42 +0200 Subject: [PATCH 05/17] Ensure that the a lot / storage location owner is not the anonymous user --- src/Entity/Parts/PartLot.php | 7 +++++++ src/Entity/Parts/Storelocation.php | 1 + translations/validators.en.xlf | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/src/Entity/Parts/PartLot.php b/src/Entity/Parts/PartLot.php index e3f0fcef..32db4828 100644 --- a/src/Entity/Parts/PartLot.php +++ b/src/Entity/Parts/PartLot.php @@ -343,6 +343,13 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named */ public function validate(ExecutionContextInterface $context, $payload) { + //Ensure that the owner is not the anonymous user + if ($this->getOwner() && $this->getOwner()->isAnonymousUser()) { + $context->buildViolation('validator.part_lot.owner_must_not_be_anonymous') + ->atPath('owner') + ->addViolation(); + } + //When the storage location sets the owner must match, the part lot owner must match the storage location owner if ($this->getStorageLocation() && $this->getStorageLocation()->isPartOwnerMustMatch() && $this->getStorageLocation()->getOwner() && $this->getOwner()) { diff --git a/src/Entity/Parts/Storelocation.php b/src/Entity/Parts/Storelocation.php index 6ea009e2..0d90f96a 100644 --- a/src/Entity/Parts/Storelocation.php +++ b/src/Entity/Parts/Storelocation.php @@ -94,6 +94,7 @@ class Storelocation extends AbstractPartsContainingDBElement * @var User|null The owner of this storage location * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User") * @ORM\JoinColumn(name="id_owner", referencedColumnName="id", nullable=true, onDelete="SET NULL") + * @Assert\Expression("this.getOwner() == null or this.getOwner().isAnonymousUser() === false", message="validator.part_lot.owner_must_not_be_anonymous") */ protected ?User $owner = null; diff --git a/translations/validators.en.xlf b/translations/validators.en.xlf index 771f3163..1f3438de 100644 --- a/translations/validators.en.xlf +++ b/translations/validators.en.xlf @@ -311,5 +311,11 @@ The owner of this lot must match the owner of the selected storage location (%owner_name%)! + + + validator.part_lot.owner_must_not_be_anonymous + A lot owner must not be the anonymous user! + + From b0d2a22f625bc14a02618856394564c6dbdc1c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 01:04:10 +0200 Subject: [PATCH 06/17] Make user info page public for all logged in user --- src/Controller/UserController.php | 29 ++++++++++++++++------------- src/Security/Voter/UserVoter.php | 21 +++++++++++++++++---- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 6dad4159..9949b8c7 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -202,21 +202,24 @@ class UserController extends AdminPages\BaseAdminController $user = $tmp; } else { //Else we must check, if the current user is allowed to access $user - $this->denyAccessUnlessGranted('read', $user); + $this->denyAccessUnlessGranted('info', $user); } - $table = $this->dataTableFactory->createFromType( - LogDataTable::class, - [ - 'filter_elements' => $user, - 'mode' => 'element_history', - ], - ['pageLength' => 10] - ) - ->handleRequest($request); + //Only show the history table, if the user is the current user + if ($user === $this->getUser()) { + $table = $this->dataTableFactory->createFromType( + LogDataTable::class, + [ + 'filter_elements' => $user, + 'mode' => 'element_history', + ], + ['pageLength' => 10] + ) + ->handleRequest($request); - if ($table->isCallback()) { - return $table->getResponse(); + if ($table->isCallback()) { + return $table->getResponse(); + } } //Show permissions to user @@ -230,7 +233,7 @@ class UserController extends AdminPages\BaseAdminController return $this->renderForm('users/user_info.html.twig', [ 'user' => $user, 'form' => $builder->getForm(), - 'datatable' => $table, + 'datatable' => $table ?? null, ]); } } diff --git a/src/Security/Voter/UserVoter.php b/src/Security/Voter/UserVoter.php index dcd7cb20..a311e4db 100644 --- a/src/Security/Voter/UserVoter.php +++ b/src/Security/Voter/UserVoter.php @@ -38,10 +38,13 @@ class UserVoter extends ExtendedVoter protected function supports(string $attribute, $subject): bool { if (is_a($subject, User::class, true)) { - return in_array($attribute, array_merge( - $this->resolver->listOperationsForPermission('users'), - $this->resolver->listOperationsForPermission('self')), - false + return in_array($attribute, + array_merge( + $this->resolver->listOperationsForPermission('users'), + $this->resolver->listOperationsForPermission('self'), + ['info'] + ), + false ); } @@ -56,6 +59,16 @@ class UserVoter extends ExtendedVoter */ protected function voteOnUser(string $attribute, $subject, User $user): bool { + if ($attribute === 'info') { + //Every logged-in user (non-anonymous) can see the info pages of other users + if (!$user->isAnonymousUser()) { + return true; + } + + //For the anonymous user, use the user read permission + $attribute = 'read'; + } + //Check if the checked user is the user itself if (($subject instanceof User) && $subject->getID() === $user->getID() && $this->resolver->isValidOperation('self', $attribute)) { From 72dab2bc4e6ebd191f6b754fed417cb1bfe5d950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 01:07:59 +0200 Subject: [PATCH 07/17] Added tests for CLI user functions on AbstractLogEntry --- .../Entity/LogSystem/AbstractLogEntryTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Entity/LogSystem/AbstractLogEntryTest.php b/tests/Entity/LogSystem/AbstractLogEntryTest.php index fe4cdb58..0fde2daf 100644 --- a/tests/Entity/LogSystem/AbstractLogEntryTest.php +++ b/tests/Entity/LogSystem/AbstractLogEntryTest.php @@ -44,6 +44,7 @@ namespace App\Tests\Entity\LogSystem; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; use App\Entity\Attachments\PartAttachment; +use App\Entity\LogSystem\UserLoginLogEntry; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Entity\LogSystem\AbstractLogEntry; @@ -160,4 +161,25 @@ class AbstractLogEntryTest extends TestCase $this->assertNull($log->getTargetClass()); $this->assertNull($log->getTargetID()); } + + public function testCLIUsername(): void + { + $log = new UserLoginLogEntry('1.1.1.1'); + + //By default no no CLI username is set + $this->assertNull($log->getCLIUsername()); + $this->assertFalse($log->isCLIUser()); + + $user = new User(); + $user->setName('test'); + $log->setUser($user); + + //Set a CLI username + $log->setCLIUser('root'); + $this->assertSame('root', $log->getCLIUsername()); + $this->assertTrue($log->isCLIUser()); + + //Normal user must be null now + $this->assertNull($log->getUser()); + } } From c060d6ebb1f5e9e80561fff2501fe53422dc8629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 01:09:45 +0200 Subject: [PATCH 08/17] Updated dependencies --- composer.lock | 96 +++++++++++++++++++++------------------- yarn.lock | 119 ++++++++++++++++++++++++-------------------------- 2 files changed, 108 insertions(+), 107 deletions(-) diff --git a/composer.lock b/composer.lock index 0ccc8933..db97b11f 100644 --- a/composer.lock +++ b/composer.lock @@ -4771,16 +4771,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.16.1", + "version": "1.18.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f", + "reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f", "shasum": "" }, "require": { @@ -4810,9 +4810,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.1" }, - "time": "2023-02-07T18:11:17+00:00" + "time": "2023-04-07T11:51:11+00:00" }, { "name": "psr/cache", @@ -5070,25 +5070,25 @@ }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -5117,9 +5117,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/1.1" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:50:52+00:00" }, { "name": "psr/link", @@ -14120,20 +14120,21 @@ }, { "name": "doctrine/data-fixtures", - "version": "1.6.3", + "version": "1.6.5", "source": { "type": "git", "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f" + "reference": "e6b97f557942ea17564bbc30ae3ebc9bd2209363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/c27821d038e64f1bfc852a94064d65d2a75ad01f", - "reference": "c27821d038e64f1bfc852a94064d65d2a75ad01f", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/e6b97f557942ea17564bbc30ae3ebc9bd2209363", + "reference": "e6b97f557942ea17564bbc30ae3ebc9bd2209363", "shasum": "" }, "require": { - "doctrine/persistence": "^1.3.3|^2.0|^3.0", + "doctrine/deprecations": "^0.5.3 || ^1.0", + "doctrine/persistence": "^1.3.3 || ^2.0 || ^3.0", "php": "^7.2 || ^8.0" }, "conflict": { @@ -14142,16 +14143,15 @@ "doctrine/phpcr-odm": "<1.3.0" }, "require-dev": { - "doctrine/coding-standard": "^10.0", + "doctrine/coding-standard": "^11.0", "doctrine/dbal": "^2.13 || ^3.0", - "doctrine/deprecations": "^1.0", "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", "doctrine/orm": "^2.12", "ext-sqlite3": "*", "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpunit/phpunit": "^8.5 || ^9.5 || ^10.0", "symfony/cache": "^5.0 || ^6.0", - "vimeo/psalm": "^4.10" + "vimeo/psalm": "^4.10 || ^5.9" }, "suggest": { "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", @@ -14162,7 +14162,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" + "Doctrine\\Common\\DataFixtures\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -14182,7 +14182,7 @@ ], "support": { "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/1.6.3" + "source": "https://github.com/doctrine/data-fixtures/tree/1.6.5" }, "funding": [ { @@ -14198,7 +14198,7 @@ "type": "tidelift" } ], - "time": "2023-01-07T15:10:22+00:00" + "time": "2023-04-03T14:58:58+00:00" }, { "name": "doctrine/doctrine-fixtures-bundle", @@ -14607,16 +14607,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.9", + "version": "1.10.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "9b13dafe3d66693d20fe5729c3dde1d31bb64703" + "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b13dafe3d66693d20fe5729c3dde1d31bb64703", - "reference": "9b13dafe3d66693d20fe5729c3dde1d31bb64703", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", + "reference": "8aa62e6ea8b58ffb650e02940e55a788cbc3fe21", "shasum": "" }, "require": { @@ -14665,7 +14665,7 @@ "type": "tidelift" } ], - "time": "2023-03-30T08:58:01+00:00" + "time": "2023-04-04T19:17:42+00:00" }, { "name": "phpstan/phpstan-doctrine", @@ -14739,16 +14739,16 @@ }, { "name": "phpstan/phpstan-symfony", - "version": "1.2.24", + "version": "1.2.25", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "db81b1861aac7cc2e66115cb33b4d1ea2a73d096" + "reference": "1da7bf450c6b351fec08ca0aa97298473d4f6ab3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/db81b1861aac7cc2e66115cb33b4d1ea2a73d096", - "reference": "db81b1861aac7cc2e66115cb33b4d1ea2a73d096", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/1da7bf450c6b351fec08ca0aa97298473d4f6ab3", + "reference": "1da7bf450c6b351fec08ca0aa97298473d4f6ab3", "shasum": "" }, "require": { @@ -14804,9 +14804,9 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.24" + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.25" }, - "time": "2023-03-30T08:38:10+00:00" + "time": "2023-04-05T12:16:20+00:00" }, { "name": "psalm/plugin-symfony", @@ -14879,12 +14879,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "945aadf4c174c61973464b4000f4d7529aeb84a0" + "reference": "6efa800243b92a3601e0101b0333d45df35832a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/945aadf4c174c61973464b4000f4d7529aeb84a0", - "reference": "945aadf4c174c61973464b4000f4d7529aeb84a0", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/6efa800243b92a3601e0101b0333d45df35832a4", + "reference": "6efa800243b92a3601e0101b0333d45df35832a4", "shasum": "" }, "conflict": { @@ -14902,6 +14902,7 @@ "amphp/http-client": ">=4,<4.4", "anchorcms/anchor-cms": "<=0.12.7", "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<=1.0.1|>=2,<=2.2.4", "apereo/phpcas": "<1.6", "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", "appwrite/server-ce": "<0.11.1|>=0.12,<0.12.2", @@ -14920,6 +14921,7 @@ "barzahlen/barzahlen-php": "<2.0.1", "baserproject/basercms": "<4.7.5", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", + "bigfork/silverstripe-form-capture": ">=3,<=3.1", "billz/raspap-webgui": "<=2.6.6", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", "bmarshall511/wordpress_zero_spam": "<5.2.13", @@ -15003,7 +15005,7 @@ "ezsystems/ezplatform-graphql": ">=1-rc.1,<1.0.13|>=2-beta.1,<2.3.12", "ezsystems/ezplatform-kernel": "<1.2.5.1|>=1.3,<1.3.26", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<=2.3.7", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": "<6.13.8.2|>=7,<7.5.30", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", @@ -15051,7 +15053,7 @@ "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.8", + "grumpydictator/firefly-iii": "<6", "guzzlehttp/guzzle": "<6.5.8|>=7,<7.4.5", "guzzlehttp/psr7": "<1.8.4|>=2,<2.1.1", "harvesthq/chosen": "<1.8.7", @@ -15096,7 +15098,7 @@ "kimai/kimai": "<1.1", "kitodo/presentation": "<3.1.2", "klaviyo/magento2-extension": ">=1,<3", - "knplabs/knp-snappy": "<=1.4.1", + "knplabs/knp-snappy": "<1.4.2", "krayin/laravel-crm": "<1.2.2", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", @@ -15136,7 +15138,7 @@ "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", - "microweber/microweber": "<=1.3.2", + "microweber/microweber": "<1.3.3", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", @@ -15204,6 +15206,7 @@ "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", "pimcore/data-hub": "<1.2.4", + "pimcore/perspective-editor": "<1.5.1", "pimcore/pimcore": "<10.5.20", "pixelfed/pixelfed": "<=0.11.4", "pocketmine/bedrock-protocol": "<8.0.2", @@ -15230,6 +15233,7 @@ "rainlab/debugbar-plugin": "<3.1", "rankmath/seo-by-rank-math": "<=1.0.95", "react/http": ">=0.7,<1.7", + "really-simple-plugins/complianz-gdpr": "<6.4.2", "remdex/livehelperchat": "<3.99", "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": "<3.0.4", @@ -15339,7 +15343,7 @@ "thelia/thelia": ">=2.1-beta.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<=5.1.7", - "thorsten/phpmyfaq": "<3.1.11", + "thorsten/phpmyfaq": "<3.1.12", "tinymce/tinymce": "<5.10.7|>=6,<6.3.1", "tinymighty/wiki-seo": "<1.2.2", "titon/framework": ">=0,<9.9.99", @@ -15467,7 +15471,7 @@ "type": "tidelift" } ], - "time": "2023-03-30T21:04:19+00:00" + "time": "2023-04-06T17:04:19+00:00" }, { "name": "sebastian/diff", diff --git a/yarn.lock b/yarn.lock index 9db7ca7c..df440a43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,11 @@ "@ampproject/remapping@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": @@ -1411,18 +1411,10 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1433,28 +1425,33 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== "@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" + integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" @@ -2403,9 +2400,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449: - version "1.0.30001473" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz#3859898b3cab65fc8905bb923df36ad35058153c" - integrity sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg== + version "1.0.30001474" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz#13b6fe301a831fe666cce8ca4ef89352334133d5" + integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== chalk@^2.0.0, chalk@^2.3.2: version "2.4.2" @@ -2500,9 +2497,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.6.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== clipboard@^2.0.4: version "2.0.11" @@ -2669,16 +2666,16 @@ cookie@0.5.0: integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== core-js-compat@^3.25.1: - version "3.29.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" - integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + version "3.30.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.0.tgz#99aa2789f6ed2debfa1df3232784126ee97f4d80" + integrity sha512-P5A2h/9mRYZFIAP+5Ab8ns6083IyVpSclU74UNvbGVQ8VM7n3n3/g2yF3AkKQ9NXz2O+ioxLbEWKnDtgsFamhg== dependencies: browserslist "^4.21.5" core-js@^3.23.0: - version "3.29.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.1.tgz#40ff3b41588b091aaed19ca1aa5cb111803fa9a6" - integrity sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw== + version "3.30.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea" + integrity sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg== core-util-is@~1.0.0: version "1.0.3" @@ -3174,9 +3171,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.284: - version "1.4.348" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz#f49379dc212d79f39112dd026f53e371279e433d" - integrity sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ== + version "1.4.356" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.356.tgz#b75a8a8c31d571f6024310cc980a08cd6c15a8c5" + integrity sha512-nEftV1dRX3omlxAj42FwqRZT0i4xd2dIg39sog/CnCJeCcL1TRd2Uh0i9Oebgv8Ou0vzTPw++xc+Z20jzS2B6A== emoji-regex@^8.0.0: version "8.0.0" @@ -3794,9 +3791,9 @@ has@^1.0.1, has@^1.0.3: function-bind "^1.1.1" hotkeys-js@>=3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.10.1.tgz#0c67e72298f235c9200e421ab112d156dc81356a" - integrity sha512-mshqjgTqx8ee0qryHvRgZaZDxTwxam/2yTQmQlqAWS3+twnq1jsY9Yng9zB7lWq6WRrjTbTOc7knNwccXQiAjQ== + version "3.10.2" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.10.2.tgz#cf52661904f5a13a973565cb97085fea2f5ae257" + integrity sha512-Z6vLmJTYzkbZZXlBkhrYB962Q/rZGc/WHQiyEGu9ZZVF7bAeFDjjDa31grWREuw9Ygb4zmlov2bTkPYqj0aFnQ== hpack.js@^2.1.6: version "2.1.6" @@ -4026,7 +4023,7 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.9.0: +is-core-module@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== @@ -4436,9 +4433,9 @@ media-typer@0.3.0: integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== memfs@^3.4.3: - version "3.4.13" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.13.tgz#248a8bd239b3c240175cd5ec548de5227fc4f345" - integrity sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg== + version "3.5.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.0.tgz#9da86405fca0a539addafd37dbd452344fd1c0bd" + integrity sha512-yK6o8xVJlQerz57kvPROwTMgx5WtGwC2ZxDtOUsnGl49rHjYkfQoPNZPCKH73VdLE1BwBu/+Fx/NL8NYMUw2aA== dependencies: fs-monkey "^1.0.3" @@ -4664,9 +4661,9 @@ nth-check@^2.0.1: boolbase "^1.0.0" nwsapi@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + version "2.2.3" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.3.tgz#00e04dfd5a4a751e5ec2fecdc75dfd2f0db820fa" + integrity sha512-jscxIO4/VKScHlbmFBdV1Z6LXnLO+ZR4VMtypudUdfwtKxUN3TQcNFIHLwKtrUbDyHN4/GycY9+oRGZ2XMXYPw== object-assign@^4.0.1: version "4.1.1" @@ -5512,11 +5509,11 @@ resolve@1.1.7: integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.9.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.11.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -5752,9 +5749,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.7.3: - version "1.8.0" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" - integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== shelljs@^0.8.1: version "0.8.5" @@ -6444,9 +6441,9 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.8.0: - version "4.13.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.1.tgz#6417a9b5d2f528e7644b68d6ed335e392dccffe8" - integrity sha512-5tWg00bnWbYgkN+pd5yISQKDejRBYGEw15RaEEslH+zdbNDxxaZvEAO2WulaSaFKb5n3YG8JXsGaDsut1D0xdA== + version "4.13.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz#d97445481d78691efe6d9a3b230833d802fc31f9" + integrity sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -6517,9 +6514,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.74.0: - version "5.77.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.77.0.tgz#dea3ad16d7ea6b84aa55fa42f4eac9f30e7eb9b4" - integrity sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q== + version "5.78.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.78.0.tgz#836452a12416af2a7beae906b31644cb2562f9e6" + integrity sha512-gT5DP72KInmE/3azEaQrISjTvLYlSM0j1Ezhht/KLVkrqtv10JoP/RXhwmX/frrutOPuSq3o5Vq0ehR/4Vmd1g== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" From d2582354304e248bbfd096179433c04d362652a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 01:13:13 +0200 Subject: [PATCH 09/17] Improved naming and documentation of CLIUser functions on AbstractLogEntry --- src/Command/Logs/ShowEventLogCommand.php | 2 +- src/DataTables/LogDataTable.php | 2 +- src/Entity/LogSystem/AbstractLogEntry.php | 29 ++++++++++++++----- src/Services/LogSystem/EventLogger.php | 2 +- .../Entity/LogSystem/AbstractLogEntryTest.php | 6 ++-- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Command/Logs/ShowEventLogCommand.php b/src/Command/Logs/ShowEventLogCommand.php index 517c582f..7eef7a2d 100644 --- a/src/Command/Logs/ShowEventLogCommand.php +++ b/src/Command/Logs/ShowEventLogCommand.php @@ -150,7 +150,7 @@ class ShowEventLogCommand extends Command if ($entry->getUser()) { $user = $entry->getUser()->getFullName(true); } else { - if ($entry->isCLIUser()) { + if ($entry->isCLIEntry()) { $user = $entry->getCLIUsername() . ' [CLI]'; } else { $user = $entry->getUsername() . ' [deleted]'; diff --git a/src/DataTables/LogDataTable.php b/src/DataTables/LogDataTable.php index 0833021c..85ab3113 100644 --- a/src/DataTables/LogDataTable.php +++ b/src/DataTables/LogDataTable.php @@ -226,7 +226,7 @@ class LogDataTable implements DataTableTypeInterface //If user was deleted, show the info from the username field if ($user === null) { - if ($context->isCLIUser()) { + if ($context->isCLIEntry()) { return sprintf('%s [%s]', htmlentities($context->getCLIUsername()), $this->translator->trans('log.cli_user') diff --git a/src/Entity/LogSystem/AbstractLogEntry.php b/src/Entity/LogSystem/AbstractLogEntry.php index 460e561a..5b6a728b 100644 --- a/src/Entity/LogSystem/AbstractLogEntry.php +++ b/src/Entity/LogSystem/AbstractLogEntry.php @@ -216,21 +216,36 @@ abstract class AbstractLogEntry extends AbstractDBElement return $this; } - public function setCLIUser(?string $cli_username): self + /** + * Returns true if this log entry was created by a CLI command, false otherwise. + * @return bool + */ + public function isCLIEntry(): bool + { + return strpos($this->username, '!!!CLI ') === 0; + } + + /** + * Marks this log entry as a CLI entry, and set the username of the CLI user. + * This removes the association to a user object in database, as CLI users are not really related to logged in + * Part-DB users. + * @param string $cli_username + * @return $this + */ + public function setCLIUsername(string $cli_username): self { $this->user = null; $this->username = '!!!CLI ' . $cli_username; return $this; } - public function isCLIUser(): bool - { - return strpos($this->username, '!!!CLI ') === 0; - } - + /** + * Retrieves the username of the CLI user that caused the event. + * @return string|null The username of the CLI user, or null if this log entry was not created by a CLI command. + */ public function getCLIUsername(): ?string { - if ($this->isCLIUser()) { + if ($this->isCLIEntry()) { return substr($this->username, 7); } return null; diff --git a/src/Services/LogSystem/EventLogger.php b/src/Services/LogSystem/EventLogger.php index 9b4349b5..8155819b 100644 --- a/src/Services/LogSystem/EventLogger.php +++ b/src/Services/LogSystem/EventLogger.php @@ -73,7 +73,7 @@ class EventLogger //Set the console user info, if the log entry was created in a console command if ($this->console_info_helper->isCLI()) { - $logEntry->setCLIUser($this->console_info_helper->getCLIUser() ?? 'Unknown'); + $logEntry->setCLIUsername($this->console_info_helper->getCLIUser() ?? 'Unknown'); } if ($this->shouldBeAdded($logEntry)) { diff --git a/tests/Entity/LogSystem/AbstractLogEntryTest.php b/tests/Entity/LogSystem/AbstractLogEntryTest.php index 0fde2daf..243895aa 100644 --- a/tests/Entity/LogSystem/AbstractLogEntryTest.php +++ b/tests/Entity/LogSystem/AbstractLogEntryTest.php @@ -168,16 +168,16 @@ class AbstractLogEntryTest extends TestCase //By default no no CLI username is set $this->assertNull($log->getCLIUsername()); - $this->assertFalse($log->isCLIUser()); + $this->assertFalse($log->isCLIEntry()); $user = new User(); $user->setName('test'); $log->setUser($user); //Set a CLI username - $log->setCLIUser('root'); + $log->setCLIUsername('root'); $this->assertSame('root', $log->getCLIUsername()); - $this->assertTrue($log->isCLIUser()); + $this->assertTrue($log->isCLIEntry()); //Normal user must be null now $this->assertNull($log->getUser()); From 363b7bc314ecf956d0c15ed802fb5deed42fd1c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 01:24:17 +0200 Subject: [PATCH 10/17] Do not show a unecessary label in front of the boolean constraint types checkboxes --- src/Form/Filters/Constraints/BooleanConstraintType.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Form/Filters/Constraints/BooleanConstraintType.php b/src/Form/Filters/Constraints/BooleanConstraintType.php index e04e88d3..ebc5ce09 100644 --- a/src/Form/Filters/Constraints/BooleanConstraintType.php +++ b/src/Form/Filters/Constraints/BooleanConstraintType.php @@ -24,6 +24,8 @@ use App\DataTables\Filters\Constraints\BooleanConstraint; use App\Form\Type\TriStateCheckboxType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class BooleanConstraintType extends AbstractType @@ -43,4 +45,10 @@ class BooleanConstraintType extends AbstractType 'required' => false, ]); } + + public function finishView(FormView $view, FormInterface $form, array $options) + { + //Remove the label from the compound form, as the checkbox already has a label + $view->vars['label'] = false; + } } \ No newline at end of file From 71b0c2d83e6ee8a0d3e9bc801f30e1f55657bfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 19:51:29 +0200 Subject: [PATCH 11/17] Properly quote users table for compatibility with newer MySQL databases --- migrations/Version20230402170923.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/Version20230402170923.php b/migrations/Version20230402170923.php index 2bb7acf3..016a10d0 100644 --- a/migrations/Version20230402170923.php +++ b/migrations/Version20230402170923.php @@ -27,7 +27,7 @@ final class Version20230402170923 extends AbstractMultiPlatformMigration $this->addSql('ALTER TABLE storelocations ADD id_owner INT DEFAULT NULL, ADD part_owner_must_match TINYINT(1) DEFAULT 0 NOT NULL'); $this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES `users` (id) ON DELETE SET NULL'); $this->addSql('CREATE INDEX IDX_751702021E5A74C ON storelocations (id_owner)'); - $this->addSql('ALTER TABLE users ADD about_me LONGTEXT NOT NULL'); + $this->addSql('ALTER TABLE `users` ADD about_me LONGTEXT NOT NULL'); $this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL'); } From 5b5e8a4fd5470a9020a73c5c6157ca4127f38610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 19:53:05 +0200 Subject: [PATCH 12/17] Allow users (and admins) to decide whether their email should be shown on their public profile --- migrations/Version20230408170059.php | 52 +++++++++++++++++++++++++ src/Entity/UserSystem/User.php | 28 +++++++++++++ src/Form/UserAdminForm.php | 6 ++- src/Form/UserSettingsType.php | 6 +++ templates/admin/user_admin.html.twig | 1 + templates/users/user_info.html.twig | 7 +++- templates/users/user_settings.html.twig | 1 + translations/messages.en.xlf | 6 +++ 8 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 migrations/Version20230408170059.php diff --git a/migrations/Version20230408170059.php b/migrations/Version20230408170059.php new file mode 100644 index 00000000..9f686c38 --- /dev/null +++ b/migrations/Version20230408170059.php @@ -0,0 +1,52 @@ +addSql('ALTER TABLE `users` ADD show_email_on_profile TINYINT(1) DEFAULT 0 NOT NULL'); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql('ALTER TABLE `users` DROP show_email_on_profile'); + } + + public function sqLiteUp(Schema $schema): void + { + $this->addSql('ALTER TABLE users ADD COLUMN show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL'); + } + + public function sqLiteDown(Schema $schema): void + { + $this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"'); + $this->addSql('DROP TABLE "users"'); + $this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json) + , google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json) + , backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json) + , CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users'); + $this->addSql('DROP TABLE __temp__users'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)'); + $this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)'); + $this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)'); + $this->addSql('CREATE INDEX user_idx_username ON "users" (name)'); + } +} diff --git a/src/Entity/UserSystem/User.php b/src/Entity/UserSystem/User.php index a468d355..57ac1f43 100644 --- a/src/Entity/UserSystem/User.php +++ b/src/Entity/UserSystem/User.php @@ -168,6 +168,12 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe */ protected ?string $email = ''; + /** + * @var bool True if the user wants to show his email address on his (public) profile + * @ORM\Column(type="boolean", options={"default": false}) + */ + protected bool $show_email_on_profile = false; + /** * @var string|null The department the user is working * @ORM\Column(type="string", length=255, nullable=true) @@ -632,6 +638,28 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe return $this; } + /** + * Gets whether the email address of the user is shown on the public profile page. + * @return bool + */ + public function isShowEmailOnProfile(): bool + { + return $this->show_email_on_profile; + } + + /** + * Sets whether the email address of the user is shown on the public profile page. + * @param bool $show_email_on_profile + * @return User + */ + public function setShowEmailOnProfile(bool $show_email_on_profile): User + { + $this->show_email_on_profile = $show_email_on_profile; + return $this; + } + + + /** * Returns the about me text of the user. * @return string diff --git a/src/Form/UserAdminForm.php b/src/Form/UserAdminForm.php index 9f97d8f3..ce9cab04 100644 --- a/src/Form/UserAdminForm.php +++ b/src/Form/UserAdminForm.php @@ -117,7 +117,11 @@ class UserAdminForm extends AbstractType 'required' => false, 'disabled' => !$this->security->isGranted('edit_infos', $entity), ]) - + ->add('showEmailOnProfile', CheckboxType::class, [ + 'required' => false, + 'label' => 'user.show_email_on_profile.label', + 'disabled' => !$this->security->isGranted('edit_infos', $entity), + ]) ->add('department', TextType::class, [ 'empty_data' => '', 'label' => 'user.department.label', diff --git a/src/Form/UserSettingsType.php b/src/Form/UserSettingsType.php index c54e90e9..cf75b0f8 100644 --- a/src/Form/UserSettingsType.php +++ b/src/Form/UserSettingsType.php @@ -28,6 +28,7 @@ use App\Form\Type\RichTextEditorType; use App\Form\Type\ThemeChoiceType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Event\PreSetDataEvent; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\FileType; @@ -80,6 +81,11 @@ class UserSettingsType extends AbstractType 'label' => 'user.email.label', 'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode, ]) + ->add('showEmailOnProfile', CheckboxType::class, [ + 'required' => false, + 'label' => 'user.show_email_on_profile.label', + 'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode, + ]) ->add('avatar_file', FileType::class, [ 'label' => 'user_settings.change_avatar.label', 'mapped' => false, diff --git a/templates/admin/user_admin.html.twig b/templates/admin/user_admin.html.twig index e24ce36d..ccbd7b0a 100644 --- a/templates/admin/user_admin.html.twig +++ b/templates/admin/user_admin.html.twig @@ -21,6 +21,7 @@ {{ form_row(form.first_name) }} {{ form_row(form.last_name) }} {{ form_row(form.email) }} + {{ form_row(form.showEmailOnProfile) }} {{ form_row(form.department) }} {{ form_row(form.aboutMe) }} {% endblock %} diff --git a/templates/users/user_info.html.twig b/templates/users/user_info.html.twig index 28d720b6..42c399c2 100644 --- a/templates/users/user_info.html.twig +++ b/templates/users/user_info.html.twig @@ -29,8 +29,11 @@
- {#

{{ user.email }}

#} - {{ user.email }} + {% if user.showEmailOnProfile %} + {{ user.email }} + {% else %} + - + {% endif %}
diff --git a/templates/users/user_settings.html.twig b/templates/users/user_settings.html.twig index cdd419bc..650ebe97 100644 --- a/templates/users/user_settings.html.twig +++ b/templates/users/user_settings.html.twig @@ -25,6 +25,7 @@ {{ form_row(settings_form.last_name) }} {{ form_row(settings_form.department) }} {{ form_row(settings_form.email) }} + {{ form_row(settings_form.showEmailOnProfile) }} {{ form_row(settings_form.avatar_file) }}
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index c92e8a6f..05b7046f 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -11283,5 +11283,11 @@ Element 3 Lot owner + + + user.show_email_on_profile.label + Show email on public profile page + + From 5f2408b791ecd8f84c8b88cdd37ff559091a2cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 20:06:08 +0200 Subject: [PATCH 13/17] Reveal invalid fields in dropdowns while browser validation Preparation work for issue #220 --- assets/js/tab_remember.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/js/tab_remember.js b/assets/js/tab_remember.js index 7405fcfa..9ecd71c5 100644 --- a/assets/js/tab_remember.js +++ b/assets/js/tab_remember.js @@ -19,7 +19,7 @@ "use strict"; -import {Tab} from "bootstrap"; +import {Tab, Dropdown} from "bootstrap"; import tab from "bootstrap/js/src/tab"; /** @@ -63,6 +63,16 @@ class TabRememberHelper { */ onInvalid(event) { this.revealElementOnTab(event.target); + this.revealElementInDropdown(event.target); + } + + revealElementInDropdown(element) { + let dropdown = element.closest('.dropdown-menu'); + + if(dropdown) { + let bs_dropdown = Dropdown.getOrCreateInstance(dropdown); + bs_dropdown.show(); + } } revealElementOnTab(element) { From 29af14f588056f0b85d29bedcddd1fbc2edfb22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 20:43:19 +0200 Subject: [PATCH 14/17] Added an option to enforce log comments for certain actions This implements issue #220 --- .docker/symfony.conf | 2 +- .env | 5 ++ config/parameters.yaml | 3 + config/services.yaml | 4 ++ .../AdminPages/AttachmentTypeAdminForm.php | 5 +- src/Form/AdminPages/BaseEntityAdminForm.php | 7 ++- src/Form/Part/PartBaseType.php | 8 ++- .../LogSystem/EventCommentNeededHelper.php | 60 +++++++++++++++++++ src/Twig/MiscExtension.php | 43 +++++++++++++ templates/admin/_delete_form.html.twig | 3 +- templates/parts/edit/edit_part_info.html.twig | 1 - templates/parts/info/_tools.html.twig | 4 +- .../parts/info/_withdraw_modal.html.twig | 2 +- .../EventCommentNeededHelperTest.php | 43 +++++++++++++ 14 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 src/Services/LogSystem/EventCommentNeededHelper.php create mode 100644 src/Twig/MiscExtension.php create mode 100644 tests/Services/LogSystem/EventCommentNeededHelperTest.php diff --git a/.docker/symfony.conf b/.docker/symfony.conf index 60597dd6..2f8e7f66 100644 --- a/.docker/symfony.conf +++ b/.docker/symfony.conf @@ -26,7 +26,7 @@ # Pass the configuration from the docker env to the PHP environment (here you should list all .env options) PassEnv APP_ENV APP_DEBUG APP_SECRET - PassEnv DATABASE_URL + PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA diff --git a/.env b/.env index 43cffcd1..7db81e46 100644 --- a/.env +++ b/.env @@ -39,6 +39,11 @@ MAX_ATTACHMENT_FILE_SIZE="100M" # This must end with a slash! DEFAULT_URI="https://partdb.changeme.invalid/" +# With this option you can configure, where users are enforced to give a change reason, which will be logged +# This is a comma separated list of values, see documentation for available values +# Leave this empty, to make all change reasons optional +ENFORCE_CHANGE_COMMENTS_FOR="" + ################################################################################### # Email settings ################################################################################### diff --git a/config/parameters.yaml b/config/parameters.yaml index 773b61e8..a7a23db3 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -12,6 +12,7 @@ parameters: partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme partdb.locale_menu: ['en', 'de', 'fr', 'ru', 'ja'] # The languages that are shown in user drop down menu + partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all. partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails @@ -105,6 +106,8 @@ parameters: env(USE_GRAVATAR): '0' env(MAX_ATTACHMENT_FILE_SIZE): '100M' + env(ENFORCE_CHANGE_COMMENTS_FOR): '' + env(ERROR_PAGE_ADMIN_EMAIL): '' env(ERROR_PAGE_SHOW_HELP): 1 diff --git a/config/services.yaml b/config/services.yaml index 961f6258..b075684a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -102,6 +102,10 @@ services: event: 'Symfony\Component\Security\Http\Event\LogoutEvent' dispatcher: security.event_dispatcher.main + App\Services\LogSystem\EventCommentNeededHelper: + arguments: + $enforce_change_comments_for: '%partdb.enforce_change_comments_for%' + #################################################################################################################### # Attachment system #################################################################################################################### diff --git a/src/Form/AdminPages/AttachmentTypeAdminForm.php b/src/Form/AdminPages/AttachmentTypeAdminForm.php index 75174279..57ba6fed 100644 --- a/src/Form/AdminPages/AttachmentTypeAdminForm.php +++ b/src/Form/AdminPages/AttachmentTypeAdminForm.php @@ -24,6 +24,7 @@ namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Services\Attachments\FileTypeFilterTools; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -33,10 +34,10 @@ class AttachmentTypeAdminForm extends BaseEntityAdminForm { protected FileTypeFilterTools $filterTools; - public function __construct(Security $security, FileTypeFilterTools $filterTools) + public function __construct(Security $security, FileTypeFilterTools $filterTools, EventCommentNeededHelper $eventCommentNeededHelper) { $this->filterTools = $filterTools; - parent::__construct($security); + parent::__construct($security, $eventCommentNeededHelper); } protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void diff --git a/src/Form/AdminPages/BaseEntityAdminForm.php b/src/Form/AdminPages/BaseEntityAdminForm.php index a28e0211..1a95a119 100644 --- a/src/Form/AdminPages/BaseEntityAdminForm.php +++ b/src/Form/AdminPages/BaseEntityAdminForm.php @@ -31,6 +31,7 @@ use App\Form\ParameterType; use App\Form\Type\MasterPictureAttachmentType; use App\Form\Type\RichTextEditorType; use App\Form\Type\StructuralEntityType; +use App\Services\LogSystem\EventCommentNeededHelper; use FOS\CKEditorBundle\Form\Type\CKEditorType; use function get_class; use Symfony\Component\Form\AbstractType; @@ -46,10 +47,12 @@ use Symfony\Component\Security\Core\Security; class BaseEntityAdminForm extends AbstractType { protected Security $security; + protected EventCommentNeededHelper $eventCommentNeededHelper; - public function __construct(Security $security) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper) { $this->security = $security; + $this->eventCommentNeededHelper = $eventCommentNeededHelper; } public function configureOptions(OptionsResolver $resolver): void @@ -141,7 +144,7 @@ class BaseEntityAdminForm extends AbstractType $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? 'datastructure_create': 'datastructure_edit'), 'empty_data' => null, ]); diff --git a/src/Form/Part/PartBaseType.php b/src/Form/Part/PartBaseType.php index 3951a2ac..ef9bd60c 100644 --- a/src/Form/Part/PartBaseType.php +++ b/src/Form/Part/PartBaseType.php @@ -37,6 +37,7 @@ use App\Form\Type\RichTextEditorType; use App\Form\Type\SIUnitType; use App\Form\Type\StructuralEntityType; use App\Form\WorkaroundCollectionType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -54,17 +55,20 @@ class PartBaseType extends AbstractType { protected Security $security; protected UrlGeneratorInterface $urlGenerator; + protected EventCommentNeededHelper $event_comment_needed_helper; - public function __construct(Security $security, UrlGeneratorInterface $urlGenerator) + public function __construct(Security $security, UrlGeneratorInterface $urlGenerator, EventCommentNeededHelper $event_comment_needed_helper) { $this->security = $security; $this->urlGenerator = $urlGenerator; + $this->event_comment_needed_helper = $event_comment_needed_helper; } public function buildForm(FormBuilderInterface $builder, array $options): void { /** @var Part $part */ $part = $builder->getData(); + $new_part = null === $part->getID(); $status_choices = [ 'm_status.unknown' => '', @@ -250,7 +254,7 @@ class PartBaseType extends AbstractType $builder->add('log_comment', TextType::class, [ 'label' => 'edit.log_comment', 'mapped' => false, - 'required' => false, + 'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? 'part_create' : 'part_edit'), 'empty_data' => null, ]); diff --git a/src/Services/LogSystem/EventCommentNeededHelper.php b/src/Services/LogSystem/EventCommentNeededHelper.php new file mode 100644 index 00000000..7305b304 --- /dev/null +++ b/src/Services/LogSystem/EventCommentNeededHelper.php @@ -0,0 +1,60 @@ +. + */ + +namespace App\Services\LogSystem; + +/** + * This service is used to check if a log change comment is needed for a given operation type. + * It is configured using the "enforce_change_comments_for" config parameter. + */ +class EventCommentNeededHelper +{ + protected array $enforce_change_comments_for; + + public const VALID_OPERATION_TYPES = [ + 'part_edit', + 'part_create', + 'part_delete', + 'part_stock_operation', + 'datastructure_edit', + 'datastructure_create', + 'datastructure_delete', + ]; + + public function __construct(array $enforce_change_comments_for) + { + $this->enforce_change_comments_for = $enforce_change_comments_for; + } + + /** + * Checks if a log change comment is needed for the given operation type + * @param string $comment_type + * @return bool + */ + public function isCommentNeeded(string $comment_type): bool + { + //Check if the comment type is valid + if (! in_array($comment_type, self::VALID_OPERATION_TYPES, true)) { + throw new \InvalidArgumentException('The comment type "'.$comment_type.'" is not valid!'); + } + + return in_array($comment_type, $this->enforce_change_comments_for, true); + } +} \ No newline at end of file diff --git a/src/Twig/MiscExtension.php b/src/Twig/MiscExtension.php new file mode 100644 index 00000000..e154ccf8 --- /dev/null +++ b/src/Twig/MiscExtension.php @@ -0,0 +1,43 @@ +. + */ + +namespace App\Twig; + +use App\Services\LogSystem\EventCommentNeededHelper; +use Twig\Extension\AbstractExtension; + +final class MiscExtension extends AbstractExtension +{ + private EventCommentNeededHelper $eventCommentNeededHelper; + + public function __construct(EventCommentNeededHelper $eventCommentNeededHelper) + { + $this->eventCommentNeededHelper = $eventCommentNeededHelper; + } + + public function getFunctions() + { + return [ + new \Twig\TwigFunction('event_comment_needed', + fn(string $operation_type) => $this->eventCommentNeededHelper->isCommentNeeded($operation_type) + ), + ]; + } +} \ No newline at end of file diff --git a/templates/admin/_delete_form.html.twig b/templates/admin/_delete_form.html.twig index 0423bdca..762b91b6 100644 --- a/templates/admin/_delete_form.html.twig +++ b/templates/admin/_delete_form.html.twig @@ -14,7 +14,8 @@
diff --git a/templates/parts/edit/edit_part_info.html.twig b/templates/parts/edit/edit_part_info.html.twig index 5e5dc243..34ec2d67 100644 --- a/templates/parts/edit/edit_part_info.html.twig +++ b/templates/parts/edit/edit_part_info.html.twig @@ -107,7 +107,6 @@ {{ form_widget(form.save_and_new, {'attr': {'class': 'dropdown-item'}}) }}
- {{ form_row(form.log_comment)}}
diff --git a/templates/parts/info/_tools.html.twig b/templates/parts/info/_tools.html.twig index 3be32f3c..9f60b49f 100644 --- a/templates/parts/info/_tools.html.twig +++ b/templates/parts/info/_tools.html.twig @@ -44,7 +44,9 @@
diff --git a/templates/parts/info/_withdraw_modal.html.twig b/templates/parts/info/_withdraw_modal.html.twig index 79ae2ea2..bb29c8d6 100644 --- a/templates/parts/info/_withdraw_modal.html.twig +++ b/templates/parts/info/_withdraw_modal.html.twig @@ -46,7 +46,7 @@ {% trans %}part.info.withdraw_modal.comment{% endtrans %}
- +
{% trans %}part.info.withdraw_modal.comment.hint{% endtrans %}
diff --git a/tests/Services/LogSystem/EventCommentNeededHelperTest.php b/tests/Services/LogSystem/EventCommentNeededHelperTest.php new file mode 100644 index 00000000..90b9d3ab --- /dev/null +++ b/tests/Services/LogSystem/EventCommentNeededHelperTest.php @@ -0,0 +1,43 @@ +. + */ + +namespace App\Tests\Services\LogSystem; + +use App\Services\LogSystem\EventCommentNeededHelper; +use PHPUnit\Framework\TestCase; + +class EventCommentNeededHelperTest extends TestCase +{ + public function testIsCommentNeeded() + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->assertTrue($service->isCommentNeeded('part_edit')); + $this->assertTrue($service->isCommentNeeded('part_create')); + $this->assertFalse($service->isCommentNeeded('part_delete')); + $this->assertFalse($service->isCommentNeeded('part_lot_operation')); + } + + public function testIsCommentNeededInvalidTypeException() + { + $service = new EventCommentNeededHelper(['part_edit', 'part_create']); + $this->expectException(\InvalidArgumentException::class); + $service->isCommentNeeded('this_is_not_valid'); + } +} From 3edc0a7f53b823399b1fb8fa7402cce08bbf11a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 20:52:46 +0200 Subject: [PATCH 15/17] Added documentation for ENFORCE_CHANGE_COMMENTS_FOR Related to issue #220 --- docs/configuration.md | 8 ++++++++ docs/usage/tips_tricks.md | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 01121efe..1132f6a7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -28,6 +28,14 @@ The following configuration options can only be changed by the server administra * `USE_GRAVATAR`: Set to `1` to use [gravatar.com](gravatar.com) images for user avatars (as long as they have not set their own picture). The users browsers have to download the pictures from a third-party (gravatars) server, so this might be a privacy risk. * `MAX_ATTACHMENT_FILE_SIZE`: The maximum file size (in bytes) for attachments. You can use the suffix `K`, `M` or `G` to specify the size in kilobytes, megabytes or gigabytes. By default `100M` (100 megabytes). Please note that this only the limit of Part-DB. You still need to configure the php.ini `upload_max_filesize` and `post_max_size` to allow bigger files to be uploaded. * `DEFAULT_URI`: The default URI base to use for the Part-DB, when no URL can be determined from the browser request. This should be the primary URL/Domain, which is used to access Part-DB. This value is used to create correct links in emails and other places, where the URL is needed. It is also used, when SAML is enabled.s If you are using a reverse proxy, you should set this to the URL of the reverse proxy (e.g. `https://part-db.example.com`). **This value must end with a slash**. +* `ENFORCE_CHANGE_COMMENTS_FOR`: With this option you can configure, where users are enforced to give a change reason, which will be written to the log. This is a comma separated list of values (e.g. `part_edit,part_delete`). Leave empty to make change comments optional everywhere. Possible values are: + * `part_edit`: Edit operation of a existing part + * `part_delete`: Delete operation of a existing part + * `part_create`: Creation of a new part + * `part_stock_operation`: Stock operation on a part (therefore withdraw, add or move stock) + * `datastructure_edit`: Edit operation of a existing datastructure (e.g. category, manufacturer, ...) + * `datastructure_delete`: Delete operation of a existing datastructure (e.g. category, manufacturer, ...) + * `datastructure_create`: Creation of a new datastructure (e.g. category, manufacturer, ...) ### E-Mail settings * `MAILER_DSN`: You can configure the mail provider which should be used for email delivery (see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587` diff --git a/docs/usage/tips_tricks.md b/docs/usage/tips_tricks.md index a9ca994a..d8285deb 100644 --- a/docs/usage/tips_tricks.md +++ b/docs/usage/tips_tricks.md @@ -62,4 +62,10 @@ by calling the `php bin/console partdb:currencies:update-exchange-rates`. If you call this command regularly (e.g. with a cronjob), you can keep the exchange rates up-to-date. Please note that if you use a base currency, which is not the Euro, you have to configure an exchange rate API, as the -free API used by default only supports the Euro as base currency. \ No newline at end of file +free API used by default only supports the Euro as base currency. + +## Enforce log comments +On almost any editing operation it is possible to add a comment describing, what or why you changed something. +This comment will be written to change log and can be viewed later. +If you want to enforce your users to add comments to certain operations, you can do this by setting the `ENFORCE_CHANGE_COMMENTS_FOR` option. +See the configuration reference for more information. \ No newline at end of file From 80c7680d17a952acf31af958fad77e86146a0ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 20:57:01 +0200 Subject: [PATCH 16/17] Do not use a horizontal layout in the comment dropdown for edit_part_info --- templates/parts/edit/edit_part_info.html.twig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/parts/edit/edit_part_info.html.twig b/templates/parts/edit/edit_part_info.html.twig index 34ec2d67..51b5d865 100644 --- a/templates/parts/edit/edit_part_info.html.twig +++ b/templates/parts/edit/edit_part_info.html.twig @@ -106,8 +106,9 @@ {{ form_widget(form.save_and_clone, {'attr': {'class': 'dropdown-item'}}) }} {{ form_widget(form.save_and_new, {'attr': {'class': 'dropdown-item'}}) }} -
- {{ form_row(form.log_comment)}} +
+ + {{ form_widget(form.log_comment)}}
From 0eea7f8d4df572d9794bc5153dc1efaba923becb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 8 Apr 2023 21:00:41 +0200 Subject: [PATCH 17/17] Fixed static analyis issue --- src/Form/AdminPages/CurrencyAdminForm.php | 5 +++-- src/Form/AdminPages/SupplierForm.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Form/AdminPages/CurrencyAdminForm.php b/src/Form/AdminPages/CurrencyAdminForm.php index 19123465..754d7c66 100644 --- a/src/Form/AdminPages/CurrencyAdminForm.php +++ b/src/Form/AdminPages/CurrencyAdminForm.php @@ -24,6 +24,7 @@ namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Form\Type\BigDecimalMoneyType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\Extension\Core\Type\CurrencyType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; @@ -33,9 +34,9 @@ class CurrencyAdminForm extends BaseEntityAdminForm { private string $default_currency; - public function __construct(Security $security, string $default_currency) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, string $default_currency) { - parent::__construct($security); + parent::__construct($security, $eventCommentNeededHelper); $this->default_currency = $default_currency; } diff --git a/src/Form/AdminPages/SupplierForm.php b/src/Form/AdminPages/SupplierForm.php index 95cecfd3..db798db8 100644 --- a/src/Form/AdminPages/SupplierForm.php +++ b/src/Form/AdminPages/SupplierForm.php @@ -26,6 +26,7 @@ use App\Entity\Base\AbstractNamedDBElement; use App\Entity\PriceInformations\Currency; use App\Form\Type\BigDecimalMoneyType; use App\Form\Type\StructuralEntityType; +use App\Services\LogSystem\EventCommentNeededHelper; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Security\Core\Security; @@ -33,9 +34,9 @@ class SupplierForm extends CompanyForm { protected string $default_currency; - public function __construct(Security $security, string $default_currency) + public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, string $default_currency) { - parent::__construct($security); + parent::__construct($security, $eventCommentNeededHelper); $this->default_currency = $default_currency; }