diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 36106c1..471c41e 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -2,7 +2,9 @@ namespace App\Controller; +use App\Entity\Organizations; use App\Entity\User; +use App\Entity\UsersOrganizations; use App\Form\UserForm; use App\Service\ActionService; use App\Service\UserOrganizationService; @@ -25,7 +27,8 @@ class UserController extends AbstractController private readonly EntityManagerInterface $entityManager, private readonly UserService $userService, private readonly ActionService $actionService, - ) { + ) + { } #[Route('/', name: 'index', methods: ['GET'])] @@ -33,14 +36,20 @@ class UserController extends AbstractController { $this->denyAccessUnlessGranted('ROLE_USER'); - $user = $this->getUser(); + $user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->isGranted('ROLE_SUPER_ADMIN')) { - $usersByOrganization = $this->userService->getUsersGroupedForIndex(); + $uo = $this->entityManager->getRepository(UsersOrganizations::class)->findUsersWithOrganization(); + $noOrgUsers = $this->userService->formatNoOrgUsersAsAssoc( + $this->entityManager->getRepository(User::class)->findActiveUsersWithoutOrganization()); + $usersByOrganization = $this->userService->groupByOrganization($uo); + $usersByOrganization += $noOrgUsers; + + //Log action + $this->actionService->createAction("View all users", $user, null, "All" ); - } elseif ($this->isGranted('ROLE_ADMIN')) { - $usersByOrganization = $this->userService->getUsersGroupedForAdmin($user); + dd("dsaf"); } else { $usersByOrganization = []; } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 27142a1..252317b 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -8,6 +8,7 @@ use Doctrine\Persistence\ManagerRegistry; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; +use App\Entity\UsersOrganizations; /** * @extends ServiceEntityRepository @@ -33,16 +34,6 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader $this->getEntityManager()->flush(); } -// Rewrote this function to return less data - public function getAllActiveUsers(): array{ - $queryBuilder = $this->createQueryBuilder('u') - ->select('u.surname', 'u.email', 'u.id', 'u.isActive', 'u.name', 'u.pictureUrl') - ->where('u.isActive = :isActive') - ->orderBy('u.surname', 'ASC'); - $queryBuilder->setParameter('isActive', true); - return $queryBuilder->getQuery()->getResult(); - } - /** * Returns active users that are NOT in any UsersOrganizations mapping. * Returns User entities. @@ -53,7 +44,7 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader { $qb = $this->createQueryBuilder('u') ->select('u') - ->leftJoin('App\\Entity\\UsersOrganizations', 'uo', 'WITH', 'uo.users = u') + ->leftJoin(UsersOrganizations::class, 'uo', 'WITH', 'uo.users = u') ->where('u.isActive = :uActive') ->andWhere('u.isDeleted = :uDeleted') ->andWhere('uo.id IS NULL') @@ -63,32 +54,4 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader return $qb->getQuery()->getResult(); } - - /** - * Get all active users with their organization relationships - * @return array - */ - public function getAllActiveUsersWithOrganizations(): array - { - $queryBuilder = $this->createQueryBuilder('u') - ->select('u', 'uo', 'o') - ->leftJoin('App\Entity\UsersOrganizations', 'uo', 'WITH', 'u.id = uo.users') - ->leftJoin('App\Entity\Organizations', 'o', 'WITH', 'uo.organization = o.id') - ->where('u.isActive = :isActive') - ->andWhere('u.isDeleted = :isDeleted') - ->andWhere('(uo.isActive = :uoActive OR uo.isActive IS NULL)') - ->andWhere('(o.isActive = :oActive OR o.isActive IS NULL)') - ->andWhere('(o.isDeleted = :oDeleted OR o.isDeleted IS NULL)') - ->orderBy('o.name', 'ASC') - ->addOrderBy('u.surname', 'ASC'); - - $queryBuilder->setParameter('isActive', true); - $queryBuilder->setParameter('isDeleted', false); - $queryBuilder->setParameter('uoActive', true); - $queryBuilder->setParameter('oActive', true); - $queryBuilder->setParameter('oDeleted', false); - - return $queryBuilder->getQuery()->getResult(); - } - } diff --git a/src/Repository/UsersOrganizationsRepository.php b/src/Repository/UsersOrganizationsRepository.php index cf48976..064f757 100644 --- a/src/Repository/UsersOrganizationsRepository.php +++ b/src/Repository/UsersOrganizationsRepository.php @@ -22,20 +22,18 @@ class UsersOrganizationsRepository extends ServiceEntityRepository * * @return UsersOrganizations[] */ - public function findActiveWithUserAndOrganization(): array + public function findUsersWithOrganization(): array { $qb = $this->createQueryBuilder('uo') ->addSelect('u', 'o') ->leftJoin('uo.users', 'u') ->leftJoin('uo.organization', 'o') - ->where('uo.isActive = :uoActive') ->andWhere('u.isActive = :uActive') ->andWhere('u.isDeleted = :uDeleted') ->andWhere('o.isActive = :oActive') ->andWhere('o.isDeleted = :oDeleted') ->orderBy('o.name', 'ASC') ->addOrderBy('u.surname', 'ASC') - ->setParameter('uoActive', true) ->setParameter('uActive', true) ->setParameter('uDeleted', false) ->setParameter('oActive', true) diff --git a/src/Service/UserService.php b/src/Service/UserService.php index a26e42b..40d744c 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -3,17 +3,26 @@ namespace App\Service; +use App\Entity\Roles; use App\Entity\User; use App\Entity\UserOrganizatonApp; use App\Entity\UsersOrganizations; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityNotFoundException; +use Exception; use League\Bundle\OAuth2ServerBundle\Model\AccessToken; use Random\RandomException; +use Symfony\Bundle\SecurityBundle\Security; class UserService { - public function __construct(private readonly EntityManagerInterface $entityManager) + public const NOT_FOUND = 'Entity not found'; + + public function __construct(private readonly EntityManagerInterface $entityManager, + private readonly Security $security, + ) { // Constructor logic if needed } @@ -45,7 +54,7 @@ class UserService */ public function isUserConnected(string $userIdentifier): bool { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Paris')); + $now = new DateTimeImmutable(); $tokens = $this->entityManager->getRepository(AccessToken::class)->findBy([ 'userIdentifier' => $userIdentifier, 'revoked' => false @@ -62,28 +71,103 @@ class UserService } /** - * Returns UsersOrganizations rows joined with User and Organization, grouped by organization name. - * @return array }> + * Check if the user have the rights to access the page + * + * @param User $user + * @return bool + * @throws Exception */ - private function groupUserOrganizationsByOrganizationFromRows(array $rows): array + public function hasAccessTo(User $user): bool{ + if($user->getUserIdentifier() === $this->security->getUser()->getUserIdentifier()){ + return true; + } + $userOrganization = $this->entityManager->getRepository(UserOrganizatonApp::class)->findBy(['user' => $user]); + if($userOrganization) { + foreach ($userOrganization as $uo) { + if ($this->isAdminOfOrganization($uo)) { + return true; + } + } + } + if($this->security->isGranted('ROLE_SUPER_ADMIN')){ + return true; + } + return false; + + } + + /** + * Check if the user is an admin of the organization + * A user is considered an admin of an organization if they have the 'ROLE_ADMIN' AND have the link to the + * entity role 'ROLE_ADMIN' in the UsersOrganizationsApp entity + * (if he is admin for any application of the organization). + * + * @param UsersOrganizations $usersOrganizations + * @return bool + * @throws Exception + */ + public function isAdminOfOrganization(UsersOrganizations $usersOrganizations): bool{ + $actingUser = $this->getUserByIdentifier($this->security->getUser()->getUserIdentifier()); + $uo = $this->entityManager->getRepository(UsersOrganizations::class)->findOneBy(['user' => $actingUser]); + $roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['role' => 'ADMIN']); + if ($uo){ + $uoa = $this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $uo, + 'role'=> $roleAdmin]); + if ($uoa && $this->security->isGranted('ROLE_ADMIN')) { + return true; + } + } + return false; + } + + /** + * Get the user by their identifier. + * + * @param string $userIdentifier + * @return User|null + * @throws Exception + */ + public function getUserByIdentifier(string $userIdentifier): ?User + { + $user = $this->entityManager->getRepository(User::class)->findOneBy(['userIdentifier' => $userIdentifier]); + if (!$user) { + throw new EntityNotFoundException(self::NOT_FOUND); + } + return $user; + } + + /** + * Get users grouped by their organizations for the index page. + * This method should return an array of users grouped by their organizations. + * + * @return array + */ + public function groupByOrganization(array $usersOrganizations): array { $grouped = []; - foreach ($rows as $userOrg) { - $organization = $userOrg->getOrganization(); - $user = $userOrg->getUsers(); - $orgName = $organization?->getName() ?? 'No Organization'; - if (!isset($grouped[$orgName])) { - $grouped[$orgName] = [ - 'organization' => $organization, + foreach ($usersOrganizations as $userOrg) { + $org = $userOrg->getOrganization(); + if (!$org) { + continue; + } + + $orgId = $org->getId(); + $orgName = $org->getName(); + + if (!isset($grouped[$orgId])) { + $grouped[$orgId] = [ + 'id' => $orgId, + 'name' => $orgName, 'users' => [], ]; } - $grouped[$orgName]['users'][] = [ - 'users' => $user, - 'userOrganization' => $userOrg, - 'is_connected' => $this->isUserConnected($user->getEmail()), + $user = $userOrg->getUsers(); + $grouped[$orgId]['users'][] = [ + 'entity' => $user, + 'connected' => $this->isUserConnected($user->getUserIdentifier()), + 'isActive' => (bool) $userOrg->isActive() ]; } @@ -91,64 +175,27 @@ class UserService } /** - * Returns users that have no UsersOrganizations mapping. - * @return array + * Format users without organization for admin view. + * + * @param array $users + * @return array */ - private function buildUsersWithoutOrganization(): array + public function formatNoOrgUsersAsAssoc(array $noOrgUsers): array { - $userRepository = $this->entityManager->getRepository(User::class); - $users = $userRepository->findActiveUsersWithoutOrganization(); + $group = [ + 'id' => null, + 'name' => 'Utilisateurs', + 'users' => [], + ]; - $list = []; - foreach ($users as $user) { - $list[] = [ - 'users' => $user, - 'userOrganization' => null, - 'is_connected' => $this->isUserConnected($user->getEmail()), + foreach ($noOrgUsers as $user) { + $group['users'][] = [ + 'entity' => $user, + 'connected' => $this->isUserConnected($user->getUserIdentifier()), ]; } - return $list; - } - - /** - * Public API: build the final structure for the index view. - * Groups users by organization and adds a "No Organization" group for users without mapping. - * @return array }> - */ - public function getUsersGroupedForIndex(): array - { - $usersOrganizationsRepo = $this->entityManager->getRepository(UsersOrganizations::class); - $rows = $usersOrganizationsRepo->findActiveWithUserAndOrganization(); - - $grouped = $this->groupUserOrganizationsByOrganizationFromRows($rows); - $noOrgUsers = $this->buildUsersWithoutOrganization(); - - if (!empty($noOrgUsers)) { - $grouped['No Organization'] = [ - 'organization' => null, - 'users' => $noOrgUsers, - ]; - } - - ksort($grouped); - return $grouped; - } - - /** - * For admins: return users grouped only for organizations where the given user is ADMIN. - * @return array }> - */ - public function getUsersGroupedForAdmin(User $adminUser): array - { - $uoaRepo = $this->entityManager->getRepository(UserOrganizatonApp::class); - $orgIds = $uoaRepo->findAdminOrganizationIdsForUser($adminUser); - - $uoRepo = $this->entityManager->getRepository(UsersOrganizations::class); - $rows = $uoRepo->findActiveWithUserAndOrganizationByOrganizationIds($orgIds); - - $grouped = $this->groupUserOrganizationsByOrganizationFromRows($rows); - ksort($grouped); - return $grouped; + // Use a fixed key (e.g., 0 or 'none') to avoid collisions with real org IDs + return ['none' => $group]; } } diff --git a/templates/user/index.html.twig b/templates/user/index.html.twig index 431f328..3f0012c 100644 --- a/templates/user/index.html.twig +++ b/templates/user/index.html.twig @@ -7,7 +7,7 @@

Gestion Utilisateurs

{% if is_granted('ROLE_SUPER_ADMIN') %} - Ajouter un utilisateur +{# Ajouter un utilisateur#} {% endif %}
@@ -15,14 +15,13 @@ {% if usersByOrganization|length == 0 %}

Aucun utilisateur trouvé

-

Il n'y a actuellement aucun utilisateur correspondant à votre périmètre.

{% else %} - {% for orgName, orgData in usersByOrganization %} + {% for org in usersByOrganization %} {% include 'user/userList.html.twig' with { - title: orgName, - org: orgData, - organizationId: orgData.organization ? orgData.organization.id : null + title: org.name, + organizationId: org.id|default(null), + users: org.users } %} {% endfor %} {% endif %} diff --git a/templates/user/userList.html.twig b/templates/user/userList.html.twig index 57c6026..f22090a 100644 --- a/templates/user/userList.html.twig +++ b/templates/user/userList.html.twig @@ -6,7 +6,7 @@

{{ title }}

{% if organizationId %} -{# ID: {{ organizationId }}#} + {% endif %}
{% endif %} @@ -24,47 +24,62 @@ - {% if org.users|length == 0 %} + {% if users|length == 0 %} Aucun utilisateur trouvé dans cette organisation. {% else %} - {% for userData in org.users %} + {% for user in users %} - {% if userData.users.pictureUrl %} - User profile pic {% else %}
- {{ userData.users.name|first|upper }}{{ userData.users.surname|first|upper }} + {{ user.entity.name|first|upper }}{{ user.entity.surname|first|upper }}
{% endif %} - {{ userData.users.surname }} - {{ userData.users.name }} - {{ userData.users.email }} + {{ user.entity.surname }} + {{ user.entity.name }} + {{ user.entity.email }} + {# Logic for status #} - {% if userData.is_connected %} - Actif + {# check if the user is active in the organization link #} + {% if user.isActive is defined %} + {% if user.isActive %} + {% if user.connected %} + Actif + {% else %} + Inactif + {% endif %} + {% else %} + Désactivé + {% endif %} + {# if no organization link #} {% else %} - Inactif + {% if user.connected %} + Actif + {% else %} + Inactif + {% endif %} {% endif %} -{# {% if organizationId is defined and organizationId %}#} -{# #} -{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}#} -{# #} -{# {% else %}#} -{# #} -{# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}#} -{# #} -{# {% endif %}#} + {# {% if organizationId is defined and organizationId %} #} + {# #} + {# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }} #} + {# #} + {# {% else %} #} + {# #} + {# {{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }} #} + {# #} + {# {% endif %} #} {% endfor %}