Skip to content

Commit e982e69

Browse files
committed
implement automatic account selection and validate identity profiles
- Add `SelectAccountListenerTrait` to automatically manage the `selectedAccount` for identities based on profile activity and backoffice access. - Update `IdentityProfileFormTrait` to ensure an identity has at least one role or profile assigned. - Optimize `AuthPresenterTrait` to prevent redundant database flushes when the selected account has not changed. - Add `IsActiveFilterTrait` to the base query trait.
1 parent 2bee5f3 commit e982e69

4 files changed

Lines changed: 119 additions & 6 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ADT\FancyAdmin\Model\Listeners;
6+
7+
use ADT\FancyAdmin\Model\Entities\Identity;
8+
use ADT\FancyAdmin\Model\Entities\Profile;
9+
use ADT\FancyAdmin\Model\FancyAdmin;
10+
use ADT\FancyAdmin\Model\Security\SecurityUser;
11+
use Doctrine\ORM\Event\OnFlushEventArgs;
12+
use Doctrine\ORM\Events;
13+
14+
trait SelectAccountListenerTrait
15+
{
16+
public function __construct(
17+
protected readonly SecurityUser $securityUser,
18+
protected readonly FancyAdmin $fancyAdmin,
19+
) {
20+
}
21+
22+
public function getSubscribedEvents(): array
23+
{
24+
return [
25+
Events::onFlush,
26+
];
27+
}
28+
29+
public function onFlushCallback(OnFlushEventArgs $args): void
30+
{
31+
if (!$this->securityUser->isLoggedIn()) {
32+
return;
33+
}
34+
35+
$em = $args->getObjectManager();
36+
$uow = $em->getUnitOfWork();
37+
38+
$identities = [];
39+
40+
foreach (array_merge($uow->getScheduledEntityUpdates(), $uow->getScheduledEntityInsertions()) as $entity) {
41+
if ($entity instanceof Identity) {
42+
$identities[$entity->getId()] = $entity;
43+
} elseif ($entity instanceof Profile) {
44+
$identity = $entity->getIdentity();
45+
$identities[$identity->getId()] = $identity;
46+
}
47+
}
48+
49+
foreach ($identities as $identity) {
50+
$selectedAccount = $identity->getSelectedAccount();
51+
$hasBackoffice = $identity->isAllowed($this->fancyAdmin->getBackofficeAclResource());
52+
53+
// 1) nemá backoffice přístup a nemá nastavený selectedAccount -> nastav první aktivní profil
54+
if (!$hasBackoffice && !$selectedAccount) {
55+
$identity->setSelectedAccount($this->getFirstActiveProfile($identity)?->getAccount());
56+
$this->entitiesToRecompute[] = $identity;
57+
}
58+
59+
// 2) má nastavený selectedAccount, ale odpovídající profil je neaktivní
60+
if ($selectedAccount) {
61+
$profileIsActive = false;
62+
foreach ($identity->getProfiles() as $_profile) {
63+
if ($_profile->getAccount() === $selectedAccount && $_profile->getIsActive()) {
64+
$profileIsActive = true;
65+
break;
66+
}
67+
}
68+
69+
if (!$profileIsActive) {
70+
if ($hasBackoffice) {
71+
$identity->setSelectedAccount(null);
72+
} else {
73+
$identity->setSelectedAccount($this->getFirstActiveProfile($identity)?->getAccount());
74+
}
75+
$this->entitiesToRecompute[] = $identity;
76+
}
77+
}
78+
}
79+
}
80+
81+
private function getFirstActiveProfile(Identity $identity): ?Profile
82+
{
83+
foreach ($identity->getProfiles() as $_profile) {
84+
if ($_profile->getIsActive()) {
85+
return $_profile;
86+
}
87+
}
88+
89+
return null;
90+
}
91+
}

src/Model/Queries/Abstract/BaseQueryTrait.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use ADT\Components\AjaxSelect\Traits\OrByIdFilterTrait;
88
use ADT\DoctrineComponents\QueryObject\Filters\IsActiveFilter;
9+
use ADT\DoctrineComponents\QueryObject\Filters\IsActiveFilterTrait;
910
use ADT\DoctrineComponents\QueryObject\QueryObjectByMode;
1011
use ADT\FancyAdmin\Model\Entities\Account;
1112
use ADT\FancyAdmin\Model\Security\SecurityUser;
@@ -14,6 +15,7 @@
1415
trait BaseQueryTrait
1516
{
1617
use OrByIdFilterTrait;
18+
use IsActiveFilterTrait;
1719

1820
abstract protected function applySecurityFilter(): void;
1921
abstract protected function applyAccountFilter(QueryBuilder $qb, Account $account): void;

src/UI/Components/Forms/IdentityProfileFormTrait.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ public function addFormFields(Form $form, Identity|Profile|null $entity, bool $i
5959

6060
if ($isEdit || $primaryEl['email']->getValue()) {
6161
$this->addIsActiveField($form);
62-
if (!$isEdit) {
62+
$submittedBySearch = $form->isSubmitted() instanceof SubmitterControl && $form->isSubmitted()->getName() === 'search';
63+
if ($submittedBySearch) {
6364
$form['isActive']->setValue(true);
6465
}
6566

@@ -68,10 +69,13 @@ public function addFormFields(Form $form, Identity|Profile|null $entity, bool $i
6869

6970
$form->addDynamicContainer(
7071
'profiles',
71-
function (StaticContainer $container) use ($form, $entity) {
72+
function (StaticContainer $container) use ($form, $entity, $submittedBySearch) {
7273
$_profile = $entity?->getProfiles()[$container->getName()] ?? null;
7374

7475
$container->addCheckbox('isActive', 'fcadmin.forms.user.labels.isActive');
76+
if ($submittedBySearch) {
77+
$container['isActive']->setValue(true);
78+
}
7579
$this->addRoles($container, $this->getProfileRoles($this->_fancyAdmin->getContext()), required: true);
7680
$container->addSelect('account', 'fcadmin.forms.user.labels.company', $this->_accountQueryFactory->create()->disableAccountFilter()->orById($_profile?->getAccount()->getId())->fetchPairs('fullName'))
7781
->setPrompt('---');
@@ -194,6 +198,18 @@ public function processUserForm(Identity $identity): void
194198
}
195199
}
196200

201+
protected function validateForm(Form $form): void
202+
{
203+
if (!isset($form['profiles'])) {
204+
return;
205+
}
206+
207+
$hasProfiles = count($form['profiles']->getContainers()) > 0;
208+
if (!$hasProfiles && !$form['roles']->getValue()) {
209+
$form['roles']->addError('Role je povinná, pokud není přidaný žádný profil.');
210+
}
211+
}
212+
197213
protected function getIdentityRoles(?string $context): array
198214
{
199215
return $this->_aclRoleQueryFactory->create()->byType(AclRoleTypeEnum::IDENTITY)->byContext($context)->fetch();

src/UI/Presenters/AuthPresenterTrait.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,15 @@ protected function startup(): void
6565
}
6666

6767
if ($this->getParameter('selectedAccount')) {
68-
$this->getUser()->getIdentity()->setSelectedAccount($this->_accountQueryFactory->create()->disableAccountFilter()->byId($this->getParameter('selectedAccount'))->fetchOne());
69-
$this->_em->flush();
68+
if ($this->getUser()->getIdentity()->getSelectedAccount()?->getId() !== $this->getParameter('selectedAccount')) {
69+
$this->getUser()->getIdentity()->setSelectedAccount($this->_accountQueryFactory->create()->disableAccountFilter()->byId($this->getParameter('selectedAccount'))->fetchOne());
70+
$this->_em->flush();
71+
}
7072
} elseif ($this->getUser()->isAllowed($this->_fancyAdmin->getBackofficeAclResource())) {
71-
$this->getUser()->getIdentity()->setSelectedAccount(null);
72-
$this->_em->flush();
73+
if ($this->getUser()->getIdentity()->getSelectedAccount()) {
74+
$this->getUser()->getIdentity()->setSelectedAccount(null);
75+
$this->_em->flush();
76+
}
7377
} elseif ($this->getUser()->getIdentity()->getSelectedAccount()) {
7478
// TODO odstranit
7579
try {

0 commit comments

Comments
 (0)