From 9d394c34f4282d5a159c8bf312c8f80809ffc3a7 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 24 Nov 2025 15:53:19 +0100 Subject: [PATCH] Refactor user view --- src/Controller/UserController.php | 141 +++++++-------------- src/Service/UserOrganizationAppService.php | 47 +++---- src/Service/UserService.php | 52 +++++--- templates/user/show.html.twig | 56 ++------ templates/user/userInformation.html.twig | 49 +++---- 5 files changed, 144 insertions(+), 201 deletions(-) diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 5485746..f1106ca 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -57,150 +57,105 @@ class UserController extends AbstractController { } -//TODO : REMOVE DEAD CODE due to the use of tabulator in the frontend + #[Route('/view/{id}', name: 'show', methods: ['GET'])] public function view(int $id, Request $request): Response { + // Accès : uniquement utilisateur authentifié $this->denyAccessUnlessGranted('ROLE_USER'); + // Utilisateur courant (acting user) via UserService $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); + // Vérification des droits d'accès supplémentaires if (!$this->userService->hasAccessTo($actingUser)) { throw $this->createAccessDeniedException(self::ACCESS_DENIED); } + // Chargement de l'utilisateur cible à afficher $user = $this->userRepository->find($id); try { + // Paramètre optionnel de contexte organisationnel $orgId = $request->query->get('organizationId'); - $apps = $this->appsRepository->findAll(); - $roles = $this->rolesRepository->findAll(); - $data = [ - 'roles' => $roles, - ]; + // Liste de toutes les applications (pour créer des groupes même si vides) + $apps = $this->appsRepository->findAll(); - $uoList = []; + // Initialisations pour la résolution des UsersOrganizations (UO) $singleUo = null; $uoActive = null; - $orgs = []; + // get uo or uoS based on orgId if ($orgId) { - // Specific organization context - $orgs = $this->organizationRepository->findBy(['id' => $orgId]); + // Contexte organisation précis : récupérer l'organisation et les liens UO + $organization = $this->organizationRepository->findBy(['id' => $orgId]); $uoList = $this->uoRepository->findBy([ - 'users' => $user, - 'organization' => $orgs, + 'users' => $user, + 'organization' => $organization, + 'isActive' => true, ]); if (!$uoList) { throw $this->createNotFoundException(self::NOT_FOUND); } + // Si contexte org donné, on retient la première UO (singleUo) $singleUo = $uoList[0]; + $data["singleUo"] = $singleUo; $uoActive = $singleUo->isActive(); } else { - // All active organizations + // Pas de contexte org : récupérer toutes les UO actives de l'utilisateur $uoList = $this->uoRepository->findBy([ - 'users' => $user, - 'isActive'=> true, + 'users' => $user, + 'isActive' => true, ]); - - foreach ($uoList as $u) { - $orgs[] = $u->getOrganization(); - } - - if (count($uoList) === 1) { - $singleUo = $uoList[0]; - $uoActive = $singleUo->isActive(); - } } - $data['uoList'] = $uoList; - $data['singleUo'] = $singleUo; - + // Charger les liens UserOrganizationApp (UOA) actifs pour les UO trouvées // Load user-organization-app roles (can be empty) $uoa = $this->entityManager ->getRepository(UserOrganizatonApp::class) ->findBy([ 'userOrganization' => $uoList, - 'isActive' => true, + 'isActive' => true, ]); - // Group existing UOA per app - $uoas = $this->userOrganizationAppService - ->groupUserOrganizationAppsByApplication($uoa); + // Group UOA by app and ensure every app has a group + $data['uoas'] = $this->userOrganizationAppService + ->groupUserOrganizationAppsByApplication( + $uoa, + $apps, + $singleUo ? $singleUo->getId() : null + ); - // ---------- HERE: create empty groups for apps with no UOA ---------- - // Index existing groups by app id - $indexedUoas = []; - foreach ($uoas as $group) { - $indexedUoas[$group['application']->getId()] = $group; + //Build roles based on user permissions. + //Admin can't see or edit a super admin user + if ($this->isGranted('ROLE_SUPER_ADMIN')) { + $data['rolesArray'] = $this->rolesRepository->findAll(); + } elseif (!$orgId) { + $data['rolesArray'] = $this->userService->getRolesArrayForUser($actingUser, true); + } else { + $data['rolesArray'] = $this->userService->getRolesArrayForUser($actingUser); } - // Load all possible roles once - $allRoles = $this->entityManager->getRepository(Roles::class)->findAll(); - - foreach ($apps as $app) { - $appId = $app->getId(); - - if (!isset($indexedUoas[$appId])) { - // No UOA for this app yet: create an empty group - $indexedUoas[$appId] = [ - 'uoId' => $singleUo ? $singleUo->getId() : null, - 'application' => $app, - 'roles' => [], - 'rolesArray' => [], - 'selectedRoleIds' => [], - ]; - - foreach ($allRoles as $role) { - // Same security logic: ADMIN cannot assign SUPER ADMIN - if ($this->isGranted('ROLE_ADMIN') - && !$this->isGranted('ROLE_SUPER_ADMIN') - && $role->getName() === 'SUPER ADMIN') { - continue; - } - - $indexedUoas[$appId]['rolesArray'][] = [ - 'id' => $role->getId(), - 'name' => $role->getName(), - ]; - } - } - } - if(!$orgId){ - $data['singleUo'] = null; - } - // Overwrite $uoas to include groups for *all* apps - $uoas = array_values($indexedUoas); - $data['uoas'] = $uoas; // ------------------------------------------------------------------- - // Compute "can edit" flag: admin AND exactly one UO - $canEditRoles = $this->isGranted('ROLE_ADMIN') && count($uoList) === 1; - - $this->actionService->createAction( - "View user information", - $actingUser, - null, - $user->getUserIdentifier() - ); + // Calcul du flag de modification : utilisateur admin ET exactement 1 UO + $canEdit = $this->userService->canEditRolesCheck($actingUser, $user, $organization, $this->isGranted('ROLE_ADMIN')); } catch (\Exception $e) { - $canEditRoles = false; + // En cas d'erreur, désactiver l'édition et logger l'exception + $canEdit = false; $this->logger->error($e->getMessage()); } - return $this->render('user/show.html.twig', [ - 'user' => $user, - 'uoas' => $uoas ?? null, - 'orgs' => $orgs ?? null, + 'user' => $user, 'organizationId' => $orgId ?? null, - 'uoActive' => $uoActive ?? null, - 'apps' => $apps ?? [], - 'data' => $data ?? [], - 'canEditRoles' => $canEditRoles ?? false, + 'uoActive' => $uoActive ?? null, + 'apps' => $apps ?? [], + 'data' => $data ?? [], + 'canEdit' => $canEdit ?? false, ]); } @@ -510,9 +465,9 @@ class UserController extends AbstractController } if (!empty($selectedRolesIds)) { - if (!in_array((string)$roleUser->getId(), $selectedRolesIds, true)){ + if (!in_array((string)$roleUser->getId(), $selectedRolesIds, true)) { $this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo, $application); - }else{ + } else { $this->userOrganizationAppService->syncRolesForUserOrganizationApp( $uo, $application, diff --git a/src/Service/UserOrganizationAppService.php b/src/Service/UserOrganizationAppService.php index 64dbce4..67c9613 100644 --- a/src/Service/UserOrganizationAppService.php +++ b/src/Service/UserOrganizationAppService.php @@ -19,18 +19,21 @@ class UserOrganizationAppService } /** - * Groups UserOrganizationApp entities by Application - * and prepares data for Twig. + * Groups UserOrganizationApp by application and ensures every app has a group (even if empty). * - * @param UserOrganizatonApp[] $userOrgApps - * @return array + * @param array $userOrgApps Array of UserOrganizatonApp entities + * @param array $allApps Array of all Application entities + * @param int|null $defaultUoId The UserOrganization ID to use for apps with no UOA + * @return array Indexed by app ID: ['uoId' => int|null, 'application' => App, 'selectedRoleIds' => int[]] */ - public function groupUserOrganizationAppsByApplication(array $userOrgApps): array - { + public function groupUserOrganizationAppsByApplication( + array $userOrgApps, + array $allApps, + ?int $defaultUoId = null + ): array { $grouped = []; foreach ($userOrgApps as $uoa) { - $app = $uoa->getApplication(); $appId = $app->getId(); $roleEntity = $uoa->getRole(); @@ -39,39 +42,27 @@ class UserOrganizationAppService $grouped[$appId] = [ 'uoId' => $uoa->getUserOrganization()->getId(), 'application' => $app, - 'roles' => [], - 'rolesArray' => [], 'selectedRoleIds' => [], ]; } - $grouped[$appId]['roles'][] = [ - 'id' => $roleEntity->getId(), - 'name' => $roleEntity->getName(), - ]; $grouped[$appId]['selectedRoleIds'][] = $roleEntity->getId(); } - // Load all possible roles once - $allRoles = $this->entityManager->getRepository(Roles::class)->findAll(); + // Ensure every app has a group + foreach ($allApps as $app) { + $appId = $app->getId(); - foreach ($grouped as &$appGroup) { - foreach ($allRoles as $role) { - // exclude SUPER ADMIN from assignable roles if current user is just ADMIN - if ($this->security->isGranted('ROLE_ADMIN') - && !$this->security->isGranted('ROLE_SUPER_ADMIN') - && $role->getName() === 'SUPER ADMIN') { - continue; - } - - $appGroup['rolesArray'][] = [ - 'id' => $role->getId(), - 'name' => $role->getName(), + if (!isset($grouped[$appId])) { + $grouped[$appId] = [ + 'uoId' => $defaultUoId, + 'application' => $app, + 'selectedRoleIds' => [], ]; } } - return array_values($grouped); + return $grouped; // IMPORTANT: keep indexed by appId } /** diff --git a/src/Service/UserService.php b/src/Service/UserService.php index d78ca4d..01f0b2f 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -261,21 +261,6 @@ class UserService throw new FileException('File upload failed: ' . $e->getMessage()); } - - -// // Define upload directory -// $uploadDirectory = $this->profileDirectory; -// // Create directory if it doesn't exist -// if (!is_dir($uploadDirectory) && !mkdir($uploadDirectory, 0755, true) && !is_dir($uploadDirectory)) { -// throw new DirectoryCouldNotBeCreatedException(sprintf('Directory "%s" was not created', $uploadDirectory)); -// } -// try { -// -// // Move the file to the upload directory -// $picture->move($uploadDirectory, $customFilename); -// -// // Update user entity with the file path (relative to public directory) -// $user->setPictureUrl('uploads/profile/' . $customFilename); // // } @@ -471,4 +456,41 @@ class UserService return null; } + /** + * Get roles array for a user, optionally including super admin roles. + * ViewSAdminRoles flag determines if super admin roles should be included. + * + * @param User $actingUser + * @param bool $viewSAdminRoles + * @return array + */ + public function getRolesArrayForUser(User $actingUser, bool $viewSAdminRoles = false): array + { + $roles = $this->entityManager->getRepository(Roles::class)->findAll(); + $rolesArray = []; + foreach ($roles as $role) { + if (!$viewSAdminRoles && $role->getName() === 'SUPER ADMIN') { + continue; + } + $rolesArray[] = [ + 'id' => $role->getId(), + 'name' => $role->getName(), + ]; + } + + return $rolesArray; + } + + public function canEditRolesCheck(User $actingUser, User $user, $org, bool $isAdmin): bool + { + $userRoles = $user->getRoles(); + $actingUserRoles = $actingUser->getRoles(); + // if acting user is admin, he can´t edit super admin roles + + if (in_array('ROLE_SUPER_ADMIN', $userRoles, true) && !in_array('ROLE_SUPER_ADMIN', $actingUserRoles, true)) { + return false; + } + return $isAdmin && !empty($org); + + } } diff --git a/templates/user/show.html.twig b/templates/user/show.html.twig index bd17032..1a6a8e8 100644 --- a/templates/user/show.html.twig +++ b/templates/user/show.html.twig @@ -33,15 +33,9 @@
- {% if orgs|length >0 %}

Vos applications

- {% else %} -
-

Aucune application

-
- {% endif %}
@@ -62,30 +56,23 @@
-

Description - : {{ app.descriptionSmall|default('Aucune description disponible.')|raw }} +

+ Description : + {{ app.descriptionSmall|default('Aucune description disponible.')|raw }}

- {# EDITABLE if admin and exactly one UO #} - {% if canEditRoles and data.singleUo is not null %} + + {# find appGroup once, used in both editable and read-only branches #} + {% set appGroup = data.uoas[app.id]|default(null) %} + + {% if canEdit %}
- {# for this app, find its grouped info #} - {# Find the group for this specific app #} - {% set appGroup = null %} - {% for group in data.uoas|default([]) %} - {% if group.application.id == app.id %} - {% set appGroup = group %} - {% endif %} - {% endfor %} -
- {% if appGroup %} - {# Use rolesArray: filtered by current user's level (no SUPER ADMIN for plain ADMIN, etc.) #} - {% for role in appGroup.rolesArray %} + {% for role in data.rolesArray %} {% endfor %} - {% else %} - -

Aucun rôle défini pour cette - application.

- +

Aucun rôle défini pour cette application.

{% endif %}
- - {# READ ONLY otherwise #} {% else %} - {% set appGroup = null %} - {% for group in data.uoas|default([]) %} - {% if group.application.id == app.id %} - {% set appGroup = group %} - {% endif %} - {% endfor %} -
- {% if appGroup %} - {# Use rolesArray: filtered by current user's level (no SUPER ADMIN for plain ADMIN, etc.) #} - {% for role in appGroup.rolesArray %} + {% for role in data.rolesArray %} + {% if role.id in appGroup.selectedRoleIds %}checked{% endif %}> {% endfor %} {% else %} -

Aucun rôle défini pour cette - application.

+

Aucun rôle défini pour cette application.

{% endif %} -
{% endif %} diff --git a/templates/user/userInformation.html.twig b/templates/user/userInformation.html.twig index 04b163c..cf0398e 100644 --- a/templates/user/userInformation.html.twig +++ b/templates/user/userInformation.html.twig @@ -1,23 +1,25 @@ {% block body %} -
-
-
- {% if user.pictureUrl is not empty %} - user - {% endif %} -
-

{{ user.surname|capitalize }} {{ user.name|capitalize }}

-
+
+
+
+ {% if user.pictureUrl is not empty %} + user + {% endif %} +
+

{{ user.surname|capitalize }} {{ user.name|capitalize }}

+
-
+
+ {% if canEdit %} {% if organizationId is not null %} {% if uoActive %}
-
{% else %} @@ -29,20 +31,23 @@ {% endif %} {% endif %} + + Modifier -
-
- - -
-

Email: {{ user.email }}

-

Dernière connection: {{ user.lastConnection|date('d/m/Y') }} - à {{ user.lastConnection|date('H:m:s') }}

-

Compte crée le: {{ user.createdAt|date('d/m/Y') }}

-

Numéro de téléphone: {{ user.phoneNumber ? user.phoneNumber : 'Non renseigné' }}

+ {% endif %}
+
+

Email: {{ user.email }}

+

Dernière connection: {{ user.lastConnection|date('d/m/Y') }} + à {{ user.lastConnection|date('H:m:s') }}

+

Compte crée le: {{ user.createdAt|date('d/m/Y') }}

+

Numéro de téléphone: {{ user.phoneNumber ? user.phoneNumber : 'Non renseigné' }}

+
+
+ + {% endblock %}