From dd7a392070a09dd9e8f41c845732f33a5546c0a2 Mon Sep 17 00:00:00 2001 From: Piotr Date: Mon, 12 Oct 2026 12:53:24 +0200 Subject: [PATCH 1/5] Prepare the user and emit event only when the user entity is new --- Behat/Context/AdminContext.php | 4 ++ Behat/Context/AdminUserManagementContext.php | 34 +++++++++++++-- Behat/Context/MailContext.php | 2 +- Behat/Page/UserEdit.php | 42 +++++++++++++++++++ Behat/Page/UserList.php | 9 ++++ EventListener/PrepareUserListener.php | 8 +++- .../config/datagrid/admin_security_user.yml | 10 +++++ .../translations/FSiAdminSecurity.pl.yml | 4 +- features/admin/user_managment.feature | 9 ++++ .../project/src/Resources/config/services.xml | 1 + .../EventListener/PrepareUserListenerSpec.php | 21 ++++++++-- 11 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 Behat/Page/UserEdit.php diff --git a/Behat/Context/AdminContext.php b/Behat/Context/AdminContext.php index 2f3e2b4d..687a85a2 100644 --- a/Behat/Context/AdminContext.php +++ b/Behat/Context/AdminContext.php @@ -26,6 +26,7 @@ use FSi\Bundle\AdminSecurityBundle\Behat\Page\Login; use FSi\Bundle\AdminSecurityBundle\Behat\Page\Page; use FSi\Bundle\AdminSecurityBundle\Behat\Page\PasswordResetRequest; +use FSi\Bundle\AdminSecurityBundle\Behat\Page\UserEdit; use FSi\Bundle\AdminSecurityBundle\Behat\Page\UserList; use FSi\Bundle\AdminSecurityBundle\Security\User\UserIdentifierHelper; use InvalidArgumentException; @@ -60,6 +61,9 @@ public function getPage(string $name): Page case 'User list': $page = $this->getPageObject(UserList::class); break; + case 'User edit': + $page = $this->getPageObject(UserEdit::class); + break; case 'Password reset request': $page = $this->getPageObject(PasswordResetRequest::class); break; diff --git a/Behat/Context/AdminUserManagementContext.php b/Behat/Context/AdminUserManagementContext.php index ec79af98..dbb10d52 100644 --- a/Behat/Context/AdminUserManagementContext.php +++ b/Behat/Context/AdminUserManagementContext.php @@ -13,11 +13,11 @@ use Assert\Assertion; use Behat\Gherkin\Node\TableNode; -use Behat\Mink\Driver\BrowserKitDriver; use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException; use FriendsOfBehat\SymfonyExtension\Driver\SymfonyDriver; use FSi\Bundle\AdminSecurityBundle\Behat\Element\BatchAction; use FSi\Bundle\AdminSecurityBundle\Behat\Element\Datagrid; +use FSi\Bundle\AdminSecurityBundle\Behat\Page\UserEdit; use FSi\Bundle\AdminSecurityBundle\Behat\Page\UserList; use function count; @@ -91,14 +91,35 @@ public function iPressLink(string $name): void $this->getUserListPage()->clickLink($name); } + /** + * @When I click :name in :nthLiteral row + */ + public function iPressButtonInRow(string $name, string $nthLiteral): void + { + $nthRow = match ($nthLiteral) { + 'first' => 1, + 'second' => 2, + default => throw new \InvalidArgumentException(sprintf('Unknown row "%s"', $nthLiteral)), + }; + $this->getUserListPage()->clickButtonInRow($name, $nthRow); + } + /** * @Then I fill form with valid user data */ public function iFillFormWithValidUserData(): void { - $userListPage = $this->getUserListPage(); - $userListPage->fillField('Email', 'new-user@fsi.pl'); - $userListPage->checkField('ROLE_ADMIN'); + $userEditPage = $this->getUserEditPage(); + $userEditPage->fillField('Email', 'new-user@fsi.pl'); + $userEditPage->checkField('ROLE_ADMIN'); + } + + /** + * @Then I check :value + */ + public function iCheckField(string $value): void + { + $this->getUserEditPage()->checkField($value); } private function performBatchAction(string $action, int $cellIndex): void @@ -139,6 +160,11 @@ private function getUserListPage(): UserList return $this->getPageObject(UserList::class); } + private function getUserEditPage(): UserEdit + { + return $this->getPageObject(UserEdit::class); + } + private function getDatagrid(): Datagrid { /** @var Datagrid $datagrid */ diff --git a/Behat/Context/MailContext.php b/Behat/Context/MailContext.php index b374442a..2836b1fa 100644 --- a/Behat/Context/MailContext.php +++ b/Behat/Context/MailContext.php @@ -51,7 +51,7 @@ public function thereShouldBeNoEmailSent(): void Assertion::count( $this->mailerCollector->getEmails(), 0, - 'There were "%s" emails sent, where none should.' + 'The email list must contain %d email addresses, but %d was provided.' ); } diff --git a/Behat/Page/UserEdit.php b/Behat/Page/UserEdit.php new file mode 100644 index 00000000..3811ba61 --- /dev/null +++ b/Behat/Page/UserEdit.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace FSi\Bundle\AdminSecurityBundle\Behat\Page; + +use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException; + +final class UserEdit extends Page +{ + /** + * @param array $urlParameters + * @return void + * @throws UnexpectedPageException + */ + public function verify(array $urlParameters = []): void + { + parent::verify($urlParameters); + + if (false === $this->getDocument()->has('css', '#page-header:contains("Edit element")')) { + throw new UnexpectedPageException( + sprintf("%s page is missing \"Edit element\" header", $this->getUrl($urlParameters)) + ); + } + } + + /** + * @param array $urlParameters + * @return string + */ + protected function getUrl(array $urlParameters = []): string + { + return $this->getParameter('base_url') . '/admin/en/form/admin_security_user/' . $urlParameters['id']; + } +} diff --git a/Behat/Page/UserList.php b/Behat/Page/UserList.php index e0427764..1095f4f0 100644 --- a/Behat/Page/UserList.php +++ b/Behat/Page/UserList.php @@ -11,6 +11,7 @@ namespace FSi\Bundle\AdminSecurityBundle\Behat\Page; +use Assert\Assertion; use Behat\Mink\Element\NodeElement; use FriendsOfBehat\PageObjectExtension\Page\UnexpectedPageException; @@ -62,6 +63,14 @@ public function getBatchActions(): array ); } + public function clickButtonInRow(string $name, int $nth): void + { + $locator = sprintf('tbody > tr:nth-child(%d) > td a[title="%s"]', $nth, $name); + $element = $this->getElement('table')->find('css', $locator); + Assertion::notNull($element, sprintf('"%s" not found in row %d', $name, $nth)); + $element->click(); + } + /** * @return array */ diff --git a/EventListener/PrepareUserListener.php b/EventListener/PrepareUserListener.php index 88ae452d..f2ff6857 100644 --- a/EventListener/PrepareUserListener.php +++ b/EventListener/PrepareUserListener.php @@ -11,6 +11,7 @@ namespace FSi\Bundle\AdminSecurityBundle\EventListener; +use FSi\Bundle\AdminBundle\Doctrine\Admin\Element; use FSi\Bundle\AdminBundle\Event\FormDataPreSaveEvent; use FSi\Bundle\AdminBundle\Event\FormEvent; use FSi\Bundle\AdminSecurityBundle\Event\UserCreatedEvent; @@ -47,8 +48,13 @@ public function prepareAndDispatchUserCreated(FormEvent $event): void return; } + $element = $event->getElement(); + if (false === $element instanceof Element) { + return; + } + // do not dispatch event and do not set random password for existing users - if (null !== $entity->getPassword()) { + if (true === $element->getObjectManager()->contains($entity)) { return; } diff --git a/Resources/config/datagrid/admin_security_user.yml b/Resources/config/datagrid/admin_security_user.yml index 22135da3..ad9b3410 100644 --- a/Resources/config/datagrid/admin_security_user.yml +++ b/Resources/config/datagrid/admin_security_user.yml @@ -41,3 +41,13 @@ columns: resend_activation_token: label: admin.user_list.batch_action.resend_activation_token element: admin_security_reset_activation_token + actions: + type: action + options: + label: admin.user_list.actions.label + field_mapping: ['id'] + actions: + edit: + route_name: fsi_admin_form + additional_parameters: { element: admin_security_user } + parameters_field_mapping: { id: id } diff --git a/Resources/translations/FSiAdminSecurity.pl.yml b/Resources/translations/FSiAdminSecurity.pl.yml index a2e2a1d7..f4c03bca 100644 --- a/Resources/translations/FSiAdminSecurity.pl.yml +++ b/Resources/translations/FSiAdminSecurity.pl.yml @@ -66,6 +66,8 @@ admin: delete: Usuń password_reset: Przypomnij hasło resend_activation_token: Wyślij ponownie email aktywacyjny + actions: + label: Akcje message: delete_current_user: Nie można usunąć swojego użytkownika admin_user: @@ -82,4 +84,4 @@ crud: datagrid: boolean: yes: Tak - no: Nie \ No newline at end of file + no: Nie diff --git a/features/admin/user_managment.feature b/features/admin/user_managment.feature index 2c7f4052..ab53dfce 100644 --- a/features/admin/user_managment.feature +++ b/features/admin/user_managment.feature @@ -64,3 +64,12 @@ Feature: User management | from | activation@fsi.pl | | to | new-user@fsi.pl | | reply_to | do-not-reply@example.com | + + @email + Scenario: Edit User + Given I am on the "User list" page + And I click "Edit" in second row + And I check "ROLE_ADMIN" + And I press "Save" button + And I should be redirected to "User list" page + And no emails were sent diff --git a/features/fixtures/project/src/Resources/config/services.xml b/features/fixtures/project/src/Resources/config/services.xml index 7be95913..91267ae3 100644 --- a/features/fixtures/project/src/Resources/config/services.xml +++ b/features/fixtures/project/src/Resources/config/services.xml @@ -16,6 +16,7 @@ + diff --git a/spec/FSi/Bundle/AdminSecurityBundle/EventListener/PrepareUserListenerSpec.php b/spec/FSi/Bundle/AdminSecurityBundle/EventListener/PrepareUserListenerSpec.php index a8b77730..a968a51d 100644 --- a/spec/FSi/Bundle/AdminSecurityBundle/EventListener/PrepareUserListenerSpec.php +++ b/spec/FSi/Bundle/AdminSecurityBundle/EventListener/PrepareUserListenerSpec.php @@ -11,6 +11,9 @@ namespace spec\FSi\Bundle\AdminSecurityBundle\EventListener; +use Doctrine\Persistence\ObjectManager; +use FSi\Bundle\AdminBundle\Doctrine\Admin\CRUDElement; +use FSi\Bundle\AdminBundle\Doctrine\Admin\Element; use FSi\Bundle\AdminBundle\Event\FormEvent; use FSi\Bundle\AdminSecurityBundle\Event\UserCreatedEvent; use FSi\Bundle\AdminSecurityBundle\Security\User\UserInterface; @@ -36,11 +39,17 @@ public function it_should_prepare_new_user_and_dispatch_user_created( FormEvent $event, FormInterface $form, UserInterface $user, - EventDispatcherInterface $eventDispatcher + EventDispatcherInterface $eventDispatcher, + CRUDElement $element, + ObjectManager $objectManager, ): void { + $event->getElement()->willReturn($element); $event->getForm()->willReturn($form); $form->getData()->willReturn($user); + $element->getObjectManager()->willReturn($objectManager); + $objectManager->contains($user)->willReturn(false); + $user->getPassword()->willReturn(null); $user->setPlainPassword(Argument::type('string'))->shouldBeCalled(); @@ -54,16 +63,20 @@ public function it_should_prepare_new_user_and_dispatch_user_created( $this->prepareAndDispatchUserCreated($event); } - public function it_should_do_not_set_random_password_when_user_already_exists( + public function it_should_not_preapre_existing_user( FormEvent $event, FormInterface $form, UserInterface $user, - EventDispatcherInterface $eventDispatcher + EventDispatcherInterface $eventDispatcher, + CRUDElement $element, + ObjectManager $objectManager, ): void { + $event->getElement()->willReturn($element); $event->getForm()->willReturn($form); $form->getData()->willReturn($user); - $user->getPassword()->willReturn('password-hash'); + $element->getObjectManager()->willReturn($objectManager); + $objectManager->contains($user)->willReturn(true); $user->setPlainPassword(Argument::any())->shouldNotBeCalled(); $user->setEnabled(Argument::any())->shouldNotBeCalled(); From 74aa6f094259601fd853eeeb3811a4944dbcc738 Mon Sep 17 00:00:00 2001 From: Piotr Date: Mon, 12 Oct 2026 14:01:47 +0200 Subject: [PATCH 2/5] Update symfony/twig-bridge to fix EmptyNode compile error Compiling templates that combine {% trans_default_domain %} with |trans filters called without parentheses failed with: LogicException: EmptyNode cannot have children. Since Twig 3.15, a filter called without arguments gets an EmptyNode as its "arguments" node, and EmptyNode::setNode() always throws. Older versions of Symfony's TranslationDefaultDomainNodeVisitor inject the default domain by calling setNode() on that node directly, which triggers the exception during compilation. Newer symfony/twig-bridge releases replace the EmptyNode with a fresh Nodes instance before injecting the domain. Updating the bridge fixes the error for all affected templates, including vendor ones such as @FSiAdminSecurity/Activation/mail/activation.html.twig. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b86d8de6..8206f6ab 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "symfony/monolog-bridge": "^5.4|^6.0|^7.0", "symfony/monolog-bundle": "^3.8", "symfony/routing": "^5.4|^6.0|^7.0", - "symfony/twig-bridge": "^5.4|^6.0|^7.0", + "symfony/twig-bridge": "~5.4.48|^6.0|^7.0", "symfony/var-dumper": "^5.4|^6.0|^7.0", "symfony/var-exporter": "^5.4|^6.0|^7.0", "rize/uri-template": "^0.3.5|^0.4.0" From 9a7294c1abbbcde401ad9399fc353695f0d1a1b9 Mon Sep 17 00:00:00 2001 From: Piotr Date: Tue, 13 Oct 2026 09:57:12 +0200 Subject: [PATCH 3/5] Cleanup --- .gitignore | 2 +- ...itory.yml => fsi_resource_repository.yaml} | 0 .../packages/{mailer.yml => mailer.yaml} | 0 features/fixtures/project/config/services.php | 39 +++++++++++ .../FSiFixturesExtension.php | 4 -- features/fixtures/project/src/Kernel.php | 67 ------------------- .../project/src/Resources/config/services.xml | 27 -------- 7 files changed, 40 insertions(+), 99 deletions(-) rename features/fixtures/project/config/packages/{fsi_resource_repository.yml => fsi_resource_repository.yaml} (100%) rename features/fixtures/project/config/packages/{mailer.yml => mailer.yaml} (100%) create mode 100644 features/fixtures/project/config/services.php delete mode 100644 features/fixtures/project/src/Resources/config/services.xml diff --git a/.gitignore b/.gitignore index 07ef76fd..fe374b4d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ /features/fixtures/project/public/bundles /features/fixtures/project/public/uploaded /features/fixtures/project/var/cache/* -/features/fixtures/project/var/logs/* +/features/fixtures/project/var/log/* /features/fixtures/project/var/mysql/* !/features/fixtures/project/var/.gitkeep !/features/fixtures/project/var/data/.gitkeep diff --git a/features/fixtures/project/config/packages/fsi_resource_repository.yml b/features/fixtures/project/config/packages/fsi_resource_repository.yaml similarity index 100% rename from features/fixtures/project/config/packages/fsi_resource_repository.yml rename to features/fixtures/project/config/packages/fsi_resource_repository.yaml diff --git a/features/fixtures/project/config/packages/mailer.yml b/features/fixtures/project/config/packages/mailer.yaml similarity index 100% rename from features/fixtures/project/config/packages/mailer.yml rename to features/fixtures/project/config/packages/mailer.yaml diff --git a/features/fixtures/project/config/services.php b/features/fixtures/project/config/services.php new file mode 100644 index 00000000..7b47747d --- /dev/null +++ b/features/fixtures/project/config/services.php @@ -0,0 +1,39 @@ +services(); + + $services->defaults() + ->autowire() + ->autoconfigure() + ->private() + ->bind('$session', service('behat.mink.default_session')) + ->bind('$minkParameters', service('behat.mink.parameters')); + + $services->load( + 'FSi\\Bundle\\AdminSecurityBundle\\Behat\\Context\\', + '../../../../Behat/Context' + ); + + $services->set(DataContext::class) + ->arg('$container', service('behat.driver.service_container')->nullOnInvalid()); + + $services->set(News::class); + $services->set(PageSettings::class); + $services->set(MailCollector::class); + + $services->set(Clock::class); + $services->alias(ClockInterface::class, Clock::class)->public(); +}; diff --git a/features/fixtures/project/src/DependencyInjection/FSiFixturesExtension.php b/features/fixtures/project/src/DependencyInjection/FSiFixturesExtension.php index cd66dd4c..d6fda30f 100644 --- a/features/fixtures/project/src/DependencyInjection/FSiFixturesExtension.php +++ b/features/fixtures/project/src/DependencyInjection/FSiFixturesExtension.php @@ -12,10 +12,8 @@ namespace FSi\FixturesBundle\DependencyInjection; use Composer\InstalledVersions; -use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Security\Core\User\InMemoryUser; @@ -44,7 +42,5 @@ public function prepend(ContainerBuilder $container): void */ public function load(array $configs, ContainerBuilder $container): void { - $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - $loader->load('services.xml'); } } diff --git a/features/fixtures/project/src/Kernel.php b/features/fixtures/project/src/Kernel.php index a565a753..f7ef9c55 100644 --- a/features/fixtures/project/src/Kernel.php +++ b/features/fixtures/project/src/Kernel.php @@ -12,81 +12,14 @@ namespace FSi\FixturesBundle; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; -use Symfony\Component\Config\Loader\LoaderInterface; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface; -use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\Kernel as BaseKernel; -use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; -use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; -use Symfony\Component\Routing\RouteCollectionBuilder; - -use function interface_exists; -use function sprintf; class Kernel extends BaseKernel { use MicroKernelTrait; - private const CONFIG_EXTS = '.{php,xml,yaml,yml}'; - - public function registerBundles(): iterable - { - /** @var array, array> $contents */ - $contents = require $this->getProjectDir() . '/config/bundles.php'; - foreach ($contents as $class => $envs) { - if ($envs[$this->environment] ?? $envs['all'] ?? false) { - yield new $class(); - } - } - } - public function getProjectDir(): string { return dirname(__DIR__); } - - public function getCacheDir(): string - { - return $this->getProjectDir() . '/var/cache'; - } - - public function getLogDir(): string - { - return $this->getProjectDir() . '/var/logs'; - } - - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void - { - $configDirectory = $this->getProjectDir() . '/config'; - $container->addResource(new FileResource($configDirectory . '/bundles.php')); - $container->setParameter('container.dumper.inline_class_loader', $this->debug); - $container->setParameter('container.dumper.inline_factories', true); - - $loader->load($configDirectory . '/{packages}/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($configDirectory . '/{packages}/' . $this->environment . '/*' . self::CONFIG_EXTS, 'glob'); - $loader->load($configDirectory . '/{services}' . self::CONFIG_EXTS, 'glob'); - $loader->load($configDirectory . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob'); - - $loader->load(sprintf('%s/../src/Resources/config/services.xml', __DIR__)); - } - - /** - * @param RouteCollectionBuilder|RoutingConfigurator $routes - */ - protected function configureRoutes($routes): void - { - $confDir = $this->getProjectDir() . '/config'; - - if (true === $routes instanceof RouteCollectionBuilder) { - $routes->import($confDir . '/{routes}/' . $this->environment . '/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob'); - $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob'); - } else { - $routes->import($confDir . '/{routes}/' . $this->environment . '/*' . self::CONFIG_EXTS, 'glob'); - $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, 'glob'); - $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, 'glob'); - } - } } diff --git a/features/fixtures/project/src/Resources/config/services.xml b/features/fixtures/project/src/Resources/config/services.xml deleted file mode 100644 index 91267ae3..00000000 --- a/features/fixtures/project/src/Resources/config/services.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From 6732689a657f7531a9dfbe97db2d74e68be83dc9 Mon Sep 17 00:00:00 2001 From: Piotr Date: Tue, 13 Oct 2026 10:28:10 +0200 Subject: [PATCH 4/5] Do not add action column to default grid config --- Behat/Page/UserList.php | 6 +- .../config/datagrid/admin_security_user.yml | 10 -- .../translations/FSiAdminSecurity.pl.yml | 2 - .../fixtures/project/config/admin_menu.yml | 2 +- features/fixtures/project/src/Admin/User.php | 99 +++++++++++++++++++ features/fixtures/project/src/Kernel.php | 10 +- 6 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 features/fixtures/project/src/Admin/User.php diff --git a/Behat/Page/UserList.php b/Behat/Page/UserList.php index 1095f4f0..91ad2db4 100644 --- a/Behat/Page/UserList.php +++ b/Behat/Page/UserList.php @@ -65,7 +65,7 @@ public function getBatchActions(): array public function clickButtonInRow(string $name, int $nth): void { - $locator = sprintf('tbody > tr:nth-child(%d) > td a[title="%s"]', $nth, $name); + $locator = sprintf('tbody > tr:nth-child(%d) > td a:contains("%s")', $nth, $name); $element = $this->getElement('table')->find('css', $locator); Assertion::notNull($element, sprintf('"%s" not found in row %d', $name, $nth)); $element->click(); @@ -77,7 +77,7 @@ public function clickButtonInRow(string $name, int $nth): void protected function getDefinedElements(): array { return [ - 'table' => '#admin_security_user', + 'table' => 'table#user', 'batch actions' => '#batch_action_action', ]; } @@ -88,6 +88,6 @@ protected function getDefinedElements(): array */ protected function getUrl(array $urlParameters = []): string { - return $this->getParameter('base_url') . '/admin/en/list/admin_security_user'; + return $this->getParameter('base_url') . '/admin/en/list/user'; } } diff --git a/Resources/config/datagrid/admin_security_user.yml b/Resources/config/datagrid/admin_security_user.yml index ad9b3410..22135da3 100644 --- a/Resources/config/datagrid/admin_security_user.yml +++ b/Resources/config/datagrid/admin_security_user.yml @@ -41,13 +41,3 @@ columns: resend_activation_token: label: admin.user_list.batch_action.resend_activation_token element: admin_security_reset_activation_token - actions: - type: action - options: - label: admin.user_list.actions.label - field_mapping: ['id'] - actions: - edit: - route_name: fsi_admin_form - additional_parameters: { element: admin_security_user } - parameters_field_mapping: { id: id } diff --git a/Resources/translations/FSiAdminSecurity.pl.yml b/Resources/translations/FSiAdminSecurity.pl.yml index f4c03bca..9e39ca74 100644 --- a/Resources/translations/FSiAdminSecurity.pl.yml +++ b/Resources/translations/FSiAdminSecurity.pl.yml @@ -66,8 +66,6 @@ admin: delete: Usuń password_reset: Przypomnij hasło resend_activation_token: Wyślij ponownie email aktywacyjny - actions: - label: Akcje message: delete_current_user: Nie można usunąć swojego użytkownika admin_user: diff --git a/features/fixtures/project/config/admin_menu.yml b/features/fixtures/project/config/admin_menu.yml index 5c8192e3..d082174d 100644 --- a/features/fixtures/project/config/admin_menu.yml +++ b/features/fixtures/project/config/admin_menu.yml @@ -1,4 +1,4 @@ menu: - news - { id: page_settings, name: Page settings } - - { id: admin_security_user, name: Users } + - { id: user, name: Users } diff --git a/features/fixtures/project/src/Admin/User.php b/features/fixtures/project/src/Admin/User.php new file mode 100644 index 00000000..55433915 --- /dev/null +++ b/features/fixtures/project/src/Admin/User.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace FSi\FixturesBundle\Admin; + +use FSi\Bundle\AdminSecurityBundle\Doctrine\Admin\UserElement; +use FSi\Component\DataGrid\DataGridFactoryInterface; +use FSi\Component\DataGrid\DataGridInterface; +use FSi\FixturesBundle\Entity\User as UserEntity; + +/** + * @template-extends UserElement + */ +class User extends UserElement +{ + public function getId(): string + { + return 'user'; + } + + protected function initDataGrid(DataGridFactoryInterface $factory): DataGridInterface + { + $dataGrid = $factory->createDataGrid($this->getId()); + + $dataGrid->addColumn('batch', 'batch', [ + 'display_order' => 1, + 'translation_domain' => 'FSiAdminSecurity', + 'actions' => [ + 'delete' => [ + 'label' => 'admin.user_list.batch_action.delete', + 'route_name' => 'fsi_admin_batch', + 'additional_parameters' => ['element' => $this->getId()] + ], + 'password_reset' => [ + 'label' => 'admin.user_list.batch_action.password_reset', + 'element' => 'admin_security_password_reset' + ], + 'resend_activation_token' => [ + 'label' => 'admin.user_list.batch_action.resend_activation_token', + 'element' => 'admin_security_reset_activation_token' + ], + ] + ]); + + $dataGrid->addColumn('email', 'text', [ + 'display_order' => 2, + 'label' => 'admin.user_list.datagrid.email', + 'translation_domain' => 'FSiAdminSecurity', + ]); + + $dataGrid->addColumn('enabled', 'boolean', [ + 'display_order' => 3, + 'label' => 'admin.user_list.datagrid.enabled', + 'translation_domain' => 'FSiAdminSecurity', + ]); + + $dataGrid->addColumn('last_login', 'datetime', [ + 'display_order' => 4, + 'label' => 'admin.user_list.datagrid.last_login', + 'translation_domain' => 'FSiAdminSecurity', + ]); + + $dataGrid->addColumn('password_reset_token_non_expired', 'token_non_expired', [ + 'display_order' => 5, + 'label' => 'admin.user_list.datagrid.password_reset_token', + 'translation_domain' => 'FSiAdminSecurity', + 'field_mapping' => ['passwordResetToken'], + ]); + + $dataGrid->addColumn('activation_token_non_expired', 'token_non_expired', [ + 'display_order' => 6, + 'label' => 'admin.user_list.datagrid.activation_token', + 'translation_domain' => 'FSiAdminSecurity', + 'field_mapping' => ['activationToken'], + ]); + + $dataGrid->addColumn('actions', 'action', [ + 'display_order' => 7, + 'field_mapping' => ['id'], + 'actions' => [ + 'Edit' => [ + 'route_name' => 'fsi_admin_form', + 'additional_parameters' => ['element' => $this->getId()], + 'parameters_field_mapping' => [ 'id' => 'id' ], + ] + ] + ]); + + return $dataGrid; + } +} diff --git a/features/fixtures/project/src/Kernel.php b/features/fixtures/project/src/Kernel.php index f7ef9c55..e806c9ca 100644 --- a/features/fixtures/project/src/Kernel.php +++ b/features/fixtures/project/src/Kernel.php @@ -11,13 +11,21 @@ namespace FSi\FixturesBundle; +use FSi\FixturesBundle\Admin\User; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Kernel as BaseKernel; -class Kernel extends BaseKernel +class Kernel extends BaseKernel implements CompilerPassInterface { use MicroKernelTrait; + public function process(ContainerBuilder $container): void + { + $container->getDefinition('admin_security.admin.user')->setClass(User::class); + } + public function getProjectDir(): string { return dirname(__DIR__); From fdc9e430d391d106a81f89b1bee02152d3c5646a Mon Sep 17 00:00:00 2001 From: Piotr Date: Tue, 13 Oct 2026 12:35:06 +0200 Subject: [PATCH 5/5] Use yaml config for datagrid --- Behat/Page/UserList.php | 2 +- features/fixtures/project/config/bundles.php | 4 +- features/fixtures/project/src/Admin/User.php | 68 +------------------ .../config/datagrid/admin_security_user.yml | 55 +++++++++++++++ 4 files changed, 59 insertions(+), 70 deletions(-) create mode 100644 features/fixtures/project/src/Resources/config/datagrid/admin_security_user.yml diff --git a/Behat/Page/UserList.php b/Behat/Page/UserList.php index 91ad2db4..d6c1df06 100644 --- a/Behat/Page/UserList.php +++ b/Behat/Page/UserList.php @@ -77,7 +77,7 @@ public function clickButtonInRow(string $name, int $nth): void protected function getDefinedElements(): array { return [ - 'table' => 'table#user', + 'table' => 'table#admin_security_user', 'batch actions' => '#batch_action_action', ]; } diff --git a/features/fixtures/project/config/bundles.php b/features/fixtures/project/config/bundles.php index 3d64977c..e12413cc 100644 --- a/features/fixtures/project/config/bundles.php +++ b/features/fixtures/project/config/bundles.php @@ -12,7 +12,7 @@ FSi\Component\Translatable\Integration\Symfony\TranslatableBundle::class => ['all' => true], FSi\Bundle\ResourceRepositoryBundle\FSiResourceRepositoryBundle::class => ['all' => true], FSi\Bundle\AdminBundle\FSiAdminBundle::class => ['all' => true], - FSi\FixturesBundle\FSiFixturesBundle::class => ['all' => true], FSi\Bundle\AdminSecurityBundle\FSiAdminSecurityBundle::class => ['all' => true], - FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['all' => true] + FSi\FixturesBundle\FSiFixturesBundle::class => ['all' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['all' => true], ]; diff --git a/features/fixtures/project/src/Admin/User.php b/features/fixtures/project/src/Admin/User.php index 55433915..d042b00f 100644 --- a/features/fixtures/project/src/Admin/User.php +++ b/features/fixtures/project/src/Admin/User.php @@ -28,72 +28,6 @@ public function getId(): string protected function initDataGrid(DataGridFactoryInterface $factory): DataGridInterface { - $dataGrid = $factory->createDataGrid($this->getId()); - - $dataGrid->addColumn('batch', 'batch', [ - 'display_order' => 1, - 'translation_domain' => 'FSiAdminSecurity', - 'actions' => [ - 'delete' => [ - 'label' => 'admin.user_list.batch_action.delete', - 'route_name' => 'fsi_admin_batch', - 'additional_parameters' => ['element' => $this->getId()] - ], - 'password_reset' => [ - 'label' => 'admin.user_list.batch_action.password_reset', - 'element' => 'admin_security_password_reset' - ], - 'resend_activation_token' => [ - 'label' => 'admin.user_list.batch_action.resend_activation_token', - 'element' => 'admin_security_reset_activation_token' - ], - ] - ]); - - $dataGrid->addColumn('email', 'text', [ - 'display_order' => 2, - 'label' => 'admin.user_list.datagrid.email', - 'translation_domain' => 'FSiAdminSecurity', - ]); - - $dataGrid->addColumn('enabled', 'boolean', [ - 'display_order' => 3, - 'label' => 'admin.user_list.datagrid.enabled', - 'translation_domain' => 'FSiAdminSecurity', - ]); - - $dataGrid->addColumn('last_login', 'datetime', [ - 'display_order' => 4, - 'label' => 'admin.user_list.datagrid.last_login', - 'translation_domain' => 'FSiAdminSecurity', - ]); - - $dataGrid->addColumn('password_reset_token_non_expired', 'token_non_expired', [ - 'display_order' => 5, - 'label' => 'admin.user_list.datagrid.password_reset_token', - 'translation_domain' => 'FSiAdminSecurity', - 'field_mapping' => ['passwordResetToken'], - ]); - - $dataGrid->addColumn('activation_token_non_expired', 'token_non_expired', [ - 'display_order' => 6, - 'label' => 'admin.user_list.datagrid.activation_token', - 'translation_domain' => 'FSiAdminSecurity', - 'field_mapping' => ['activationToken'], - ]); - - $dataGrid->addColumn('actions', 'action', [ - 'display_order' => 7, - 'field_mapping' => ['id'], - 'actions' => [ - 'Edit' => [ - 'route_name' => 'fsi_admin_form', - 'additional_parameters' => ['element' => $this->getId()], - 'parameters_field_mapping' => [ 'id' => 'id' ], - ] - ] - ]); - - return $dataGrid; + return $factory->createDataGrid('admin_security_user'); } } diff --git a/features/fixtures/project/src/Resources/config/datagrid/admin_security_user.yml b/features/fixtures/project/src/Resources/config/datagrid/admin_security_user.yml new file mode 100644 index 00000000..66ac62a8 --- /dev/null +++ b/features/fixtures/project/src/Resources/config/datagrid/admin_security_user.yml @@ -0,0 +1,55 @@ +columns: + # All columns below are copied directly from the default configuration Resources/config/datagrid/admin_security_user.yml + email: + type: text + options: + label: admin.user_list.datagrid.email + translation_domain: FSiAdminSecurity + enabled: + type: boolean + options: + label: admin.user_list.datagrid.enabled + translation_domain: FSiAdminSecurity + last_login: + type: datetime + options: + label: admin.user_list.datagrid.last_login + translation_domain: FSiAdminSecurity + password_reset_token_non_expired: + type: token_non_expired + options: + label: admin.user_list.datagrid.password_reset_token + translation_domain: FSiAdminSecurity + field_mapping: [ passwordResetToken ] + activation_token_non_expired: + type: token_non_expired + options: + label: admin.user_list.datagrid.activation_token + translation_domain: FSiAdminSecurity + field_mapping: [ activationToken ] + batch: + type: batch + options: + translation_domain: FSiAdminSecurity + actions: + delete: + label: admin.user_list.batch_action.delete + route_name: fsi_admin_batch + additional_parameters: { element: user } # Changed element to user + password_reset: + label: admin.user_list.batch_action.password_reset + element: admin_security_password_reset + resend_activation_token: + label: admin.user_list.batch_action.resend_activation_token + element: admin_security_reset_activation_token + # New column that is not present in default configuration + actions: + type: action + options: + translation_domain: FSiAdminSecurity + field_mapping: [ id ] + actions: + Edit: + route_name: fsi_admin_form + additional_parameters: { element: user } + parameters_field_mapping: { id: id }