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'); // Liste de toutes les applications (pour créer des groupes même si vides) $apps = $this->appsRepository->findAll(); // Initialisations pour la résolution des UsersOrganizations (UO) $singleUo = null; $uoActive = null; // get uo or uoS based on orgId if ($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' => $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 { // Pas de contexte org : récupérer toutes les UO actives de l'utilisateur $uoList = $this->uoRepository->findBy([ 'users' => $user, 'isActive' => true, ]); } // 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, ]); // Group UOA by app and ensure every app has a group $data['uoas'] = $this->userOrganizationAppService ->groupUserOrganizationAppsByApplication( $uoa, $apps, $singleUo ? $singleUo->getId() : null ); //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); } // ------------------------------------------------------------------- // 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) { // 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, 'organizationId' => $orgId ?? null, 'uoActive' => $uoActive ?? null, 'apps' => $apps ?? [], 'data' => $data ?? [], 'canEdit' => $canEdit ?? false, ]); } //TODO : MONOLOG #[Route('/edit/{id}', name: 'edit', methods: ['GET', 'POST'])] public function edit(int $id, Request $request): Response { $this->denyAccessUnlessGranted('ROLE_USER'); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser)) { $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $form = $this->createForm(UserForm::class, $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // Handle file upload $picture = $form->get('pictureUrl')->getData(); if ($picture) { $this->userService->handleProfilePicture($user, $picture); } $user->setModifiedAt(new \DateTimeImmutable('now')); $this->entityManager->persist($user); $this->entityManager->flush(); if ($request->get('organizationId')) { $org = $this->organizationRepository->find($request->get('organizationId')); if ($org) { $this->actionService->createAction("Edit user information", $actingUser, $org, $user->getUserIdentifier()); } } else { $this->actionService->createAction("Edit user information", $actingUser, null, $user->getUserIdentifier()); } return $this->redirectToRoute('user_show', ['id' => $user->getId(), 'organizationId' => $request->get('organizationId')]); } return $this->render('user/edit.html.twig', [ 'user' => $user, 'form' => $form->createView(), 'organizationId' => $request->get('organizationId') ]); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } #[Route('/new', name: 'new', methods: ['GET', 'POST'])] public function new(Request $request): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); try { $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser)) { $user = new User(); $form = $this->createForm(UserForm::class, $user); $form->handleRequest($request); $orgId = $request->get('organizationId'); if ($form->isSubmitted() && $form->isValid()) { $existingUser = $this->userRepository->findOneBy(['email' => $user->getEmail()]); if ($existingUser && $orgId) { $org = $this->organizationRepository->find($orgId); $uo = new UsersOrganizations(); $uo->setUsers($existingUser); $uo->setOrganization($org); $uo->setStatut("INVITED"); $uo->setIsActive(false); $uo->setModifiedAt(new \DateTimeImmutable('now')); $this->entityManager->persist($uo); $this->entityManager->flush(); $this->actionService->createAction("Create new user", $existingUser, $org, "Added user to organization" . $existingUser->getUserIdentifier() . " for organization " . $org->getName()); $this->logger->notice("User added to organization " . $org->getName()); $this->emailService->sendExistingUserNotificationEmail($existingUser, $org); $this->logger->notice("Existing user notification email sent to " . $existingUser->getUserIdentifier()); $data = ['user' => $uo->getUsers(), 'organization' => $uo->getOrganization()]; $this->organizationsService->notifyOrganizationAdmins($data, 'USER_INVITED'); return $this->redirectToRoute('organization_show', ['id' => $orgId]); } // capitalize name and surname $user->setName(ucfirst(strtolower($user->getName()))); $user->setSurname(ucfirst(strtolower($user->getSurname()))); // Handle file upload $picture = $form->get('pictureUrl')->getData(); if ($picture) { $this->userService->handleProfilePicture($user, $picture); } //FOR TEST PURPOSES, SETTING A DEFAULT RANDOM PASSWORD $user->setPassword($this->userService->generateRandomPassword()); if ($orgId) { $org = $this->organizationRepository->find($orgId); if ($org) { $uo = new UsersOrganizations(); $uo->setUsers($user); $uo->setOrganization($org); $uo->setStatut("INVITED"); $uo->setIsActive(false); $uo->setModifiedAt(new \DateTimeImmutable('now')); $this->entityManager->persist($uo); $this->actionService->createAction("Create new user", $user, $org, "Added user to organization" . $user->getUserIdentifier() . " for organization " . $org->getName()); $this->logger->notice("User added to organization " . $org->getName()); $this->emailService->sendPasswordSetupEmail($user, $orgId); $this->logger->notice("Password setup email sent to " . $user->getUserIdentifier()); $data = ['user' => $uo->getUsers(), 'organization' => $uo->getOrganization()]; $this->organizationsService->notifyOrganizationAdmins($data, 'USER_INVITED'); } } $this->actionService->createAction("Create new user", $actingUser, null, $user->getUserIdentifier()); $this->logger->notice("User created " . $user->getUserIdentifier()); $this->entityManager->persist($user); $this->entityManager->flush(); if ($orgId) { return $this->redirectToRoute('organization_show', ['organizationId' => $orgId]); } return $this->redirectToRoute('user_index'); } } return $this->render('user/new.html.twig', [ 'user' => $user, 'form' => $form->createView(), 'organizationId' => $orgId ]); } catch (\Exception $e) { $this->logger->error($e->getMessage()); if ($orgId) { return $this->redirectToRoute('organization_show', ['id' => $orgId]); } return $this->redirectToRoute('user_index'); } } //TODO : MONOLOG #[Route('/deactivate/{id}', name: 'deactivate', methods: ['GET', 'POST'])] public function deactivate(int $id): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $user->setIsActive(false); $user->setModifiedAt(new \DateTimeImmutable('now')); $this->userOrganizationService->deactivateAllUserOrganizationLinks($user, $actingUser); if ($this->userService->isUserConnected($user->getUserIdentifier())) { $this->userService->revokeUserTokens($user->getUserIdentifier()); } $this->entityManager->persist($user); $this->entityManager->flush(); $this->actionService->createAction("Deactivate user", $actingUser, null, $user->getUserIdentifier()); return $this->redirectToRoute('user_index'); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } //TODO : MONOLOG #[Route('/activate/{id}', name: 'activate', methods: ['GET', 'POST'])] public function activate(int $id): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $user->setIsActive(true); $user->setModifiedAt(new \DateTimeImmutable('now')); $this->entityManager->persist($user); $this->entityManager->flush(); $this->actionService->createAction("Activate user", $actingUser, null, $user->getUserIdentifier()); return $this->redirectToRoute('user_index'); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } //TODO : MONOLOG #[Route('/organization/deactivate/{id}', name: 'deactivate_organization', methods: ['GET', 'POST'])] public function deactivateUserInOrganization(int $id, Request $request): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $orgId = $request->get('organizationId'); $org = $this->organizationRepository->find($orgId); if (!$org) { throw $this->createNotFoundException(self::NOT_FOUND); } $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $org, 'isActive' => true]); if (!$uo) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo->setIsActive(false); $this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo); $data = ['user' => $user, 'organization' => $org]; $this->organizationsService->notifyOrganizationAdmins($data, "USER_DEACTIVATED"); $this->entityManager->persist($uo); $this->entityManager->flush(); $this->actionService->createAction("Deactivate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier()); return new Response('', Response::HTTP_NO_CONTENT); //204 } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } //TODO : MONOLOG #[Route('/organization/activate/{id}', name: 'activate_organization', methods: ['GET', 'POST'])] public function activateUserInOrganization(int $id, Request $request): Response { $this->denyAccessUnlessGranted('ROLE_ADMIN'); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $orgId = $request->get('organizationId'); $org = $this->organizationRepository->find($orgId); if (!$org) { throw $this->createNotFoundException(self::NOT_FOUND); } $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $org, 'isActive' => false]); if (!$uo) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo->setIsActive(true); $this->entityManager->persist($uo); $this->entityManager->flush(); $this->actionService->createAction("Activate user in organization", $actingUser, $org, $org->getName() . " for user " . $user->getUserIdentifier()); $data = ['user' => $user, 'organization' => $org]; $this->organizationsService->notifyOrganizationAdmins($data, "USER_ACTIVATED"); return $this->redirectToRoute('user_index'); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } //TODO : MONOLOG + remove picture from bucket #[Route('/delete/{id}', name: 'delete', methods: ['GET', 'POST'])] public function delete(int $id, Request $request): Response { $this->denyAccessUnlessGranted("ROLE_SUPER_ADMIN"); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); $user = $this->userRepository->find($id); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $user->setIsActive(false); $user->setModifiedAt(new \DateTimeImmutable('now')); $this->userOrganizationService->deactivateAllUserOrganizationLinks($user, $actingUser); $user->setIsDeleted(true); if ($this->userService->isUserConnected($user)) { $this->userService->revokeUserTokens($user->getUserIdentifier()); } $this->entityManager->persist($user); $this->entityManager->flush(); $this->actionService->createAction("Delete user", $actingUser, null, $user->getUserIdentifier()); $data = ['user' => $user, 'organization' => null]; $this->organizationsService->notifyOrganizationAdmins($data, "USER_DELETED"); return new Response('', Response::HTTP_NO_CONTENT); //204 } //TODO : MONOLOG #[Route(path: '/application/roles/{id}', name: 'application_role', methods: ['GET', 'POST'])] public function applicationRole(int $id, Request $request): Response { $this->denyAccessUnlessGranted("ROLE_ADMIN"); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $uo = $this->userOrganizationService->getByIdOrFail($id); $application = $this->entityManager->getRepository(Apps::class)->find($request->get('appId')); if (!$application) { throw $this->createNotFoundException(self::NOT_FOUND); } $selectedRolesIds = $request->get('roles', []); $roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']); if (!$roleUser) { throw $this->createNotFoundException('Default role not found'); } if (!empty($selectedRolesIds)) { if (!in_array((string)$roleUser->getId(), $selectedRolesIds, true)) { $this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo, $application); } else { $this->userOrganizationAppService->syncRolesForUserOrganizationApp( $uo, $application, $selectedRolesIds, $actingUser ); } } else { $this->userOrganizationAppService->deactivateAllUserOrganizationsAppLinks($uo, $application); } $user = $uo->getUsers(); return $this->redirectToRoute('user_show', [ 'user' => $user, 'id' => $user->getId(), 'organizationId' => $uo->getOrganization()->getId() ]); } throw $this->createAccessDeniedException(); } /* * AJAX endpoint for user listing with pagination * Get all the users that aren´t deleted and are active */ #[Route(path: '/data', name: 'data', methods: ['GET'])] public function data(Request $request): JsonResponse { $this->denyAccessUnlessGranted("ROLE_ADMIN"); $page = max(1, (int)$request->query->get('page', 1)); $size = max(1, (int)$request->query->get('size', 10)); // Get filter parameters $filters = $request->query->all('filter', []); $repo = $this->userRepository; // Base query $qb = $repo->createQueryBuilder('u') ->where('u.isDeleted = :del')->setParameter('del', false); // Apply filters if (!empty($filters['name'])) { $qb->andWhere('u.surname LIKE :name') ->setParameter('name', '%' . $filters['name'] . '%'); } if (!empty($filters['prenom'])) { $qb->andWhere('u.name LIKE :prenom') ->setParameter('prenom', '%' . $filters['prenom'] . '%'); } if (!empty($filters['email'])) { $qb->andWhere('u.email LIKE :email') ->setParameter('email', '%' . $filters['email'] . '%'); } $countQb = clone $qb; $total = (int)$countQb->select('COUNT(u.id)')->getQuery()->getSingleScalarResult(); // Pagination $offset = ($page - 1) * $size; $rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult(); // Map to array $data = array_map(function (User $user) { $picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl(); return [ 'id' => $user->getId(), 'pictureUrl' => $picture, 'name' => $user->getSurname(), 'prenom' => $user->getName(), 'email' => $user->getEmail(), 'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()), 'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]), 'statut' => $user->isActive(), ]; }, $rows); $lastPage = (int)ceil($total / $size); return $this->json([ 'data' => $data, 'last_page' => $lastPage, 'total' => $total, ]); } #[Route(path: '/', name: 'index', methods: ['GET'])] public function index(): Response { $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) { $totalUsers = $this->userRepository->count(['isDeleted' => false, 'isActive' => true]); return $this->render('user/index.html.twig', [ 'users' => $totalUsers ]); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } /* * AJAX endpoint for new users listing * Get the 5 most recently created users for an organization */ #[Route(path: '/data/new', name: 'dataNew', methods: ['GET'])] public function dataNew(Request $request): JsonResponse { $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) { $orgId = $request->query->get('orgId'); $uos = $this->uoRepository->findBy(['organization' => $orgId, 'statut' => ["ACCEPTED", "INVITED"]], orderBy: ['createdAt' => 'DESC'], limit: 5); // Map to array (keep isConnected) $data = array_map(function (UsersOrganizations $uo) { $user = $uo->getUsers(); $picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl(); $initials = $user->getName()[0] . $user->getSurname()[0]; return [ 'pictureUrl' => $picture, 'email' => $user->getEmail(), 'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()), 'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]), 'initials' => strtoupper($initials), ]; }, $uos); return $this->json([ 'data' => $data, ]); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } /* * AJAX endpoint for admin users listing * Get all admin users for an organization */ #[Route(path: '/data/admin', name: 'dataAdmin', methods: ['GET'])] public function dataAdmin(Request $request): JsonResponse { $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) { $orgId = $request->query->get('orgId'); $uos = $this->uoRepository->findBy(['organization' => $orgId]); $roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']); $users = []; foreach ($uos as $uo) { if ($this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $uo, 'role' => $roleAdmin])) { $users[] = $uo; } } // Map to array (keep isConnected) $data = array_map(function (UsersOrganizations $uo) { $user = $uo->getUsers(); $picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl(); $initials = $user->getName()[0] . $user->getSurname()[0]; return [ 'pictureUrl' => $picture, 'email' => $user->getEmail(), 'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()), 'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]), 'initials' => strtoupper($initials), ]; }, $users); return $this->json([ 'data' => $data, ]); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } /* * AJAX endpoint for All users in an organization */ #[Route(path: '/data/organization', name: 'dataUserOrganization', methods: ['GET'])] public function dataUserOrganization(Request $request): JsonResponse { $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) { $orgId = $request->query->get('orgId'); $page = max(1, (int)$request->query->get('page', 1)); $size = max(1, (int)$request->query->get('size', 10)); $filters = $request->query->all('filter') ?? []; $repo = $this->uoRepository; // Base query $qb = $repo->createQueryBuilder('uo') ->join('uo.users', 'u') ->where('uo.organization = :orgId') ->setParameter('orgId', $orgId); // Apply filters if (!empty($filters['name'])) { $qb->andWhere('u.surname LIKE :name') ->setParameter('name', '%' . $filters['name'] . '%'); } if (!empty($filters['prenom'])) { $qb->andWhere('u.name LIKE :prenom') ->setParameter('prenom', '%' . $filters['prenom'] . '%'); } if (!empty($filters['email'])) { $qb->andWhere('u.email LIKE :email') ->setParameter('email', '%' . $filters['email'] . '%'); } $countQb = clone $qb; $total = (int)$countQb->select('COUNT(uo.id)')->getQuery()->getSingleScalarResult(); $qb->orderBy('uo.isActive', 'DESC') ->addOrderBy('CASE WHEN uo.statut = :invited THEN 0 ELSE 1 END', 'ASC') ->setParameter('invited', 'INVITED'); $offset = ($page - 1) * $size; $rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult(); $data = $this->userService->formatStatutForOrganizations($rows); $lastPage = (int)ceil($total / $size); return $this->json([ 'data' => $data, 'last_page' => $lastPage, 'total' => $total, ]); } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } #[Route(path: '/organization/resend-invitation/{userId}', name: 'resend_invitation', methods: ['POST'])] public function resendInvitation(int $userId, Request $request): JsonResponse { $this->denyAccessUnlessGranted("ROLE_ADMIN"); $actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier()); if ($this->userService->hasAccessTo($actingUser, true)) { $orgId = $request->get('organizationId'); $org = $this->organizationRepository->find($orgId); if (!$org) { throw $this->createNotFoundException(self::NOT_FOUND); } $user = $this->userRepository->find($userId); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $org, 'statut' => "INVITED"]); if (!$uo) { throw $this->createNotFoundException(self::NOT_FOUND); } $uo->setModifiedAt(new \DateTimeImmutable()); try { $data = ['user' => $uo->getUsers(), 'organization' => $uo->getOrganization()]; $this->emailService->sendPasswordSetupEmail($user, $orgId); $this->logger->info("Invitation email resent to user " . $user->getUserIdentifier() . " for organization " . $org->getName()); $this->organizationsService->notifyOrganizationAdmins($data, 'USER_INVITED'); return $this->json(['message' => 'Invitation envoyée avec success.'], Response::HTTP_OK); } catch (\Exception $e) { $this->logger->error("Error resending invitation email to user " . $user->getUserIdentifier() . " for organization " . $org->getName() . ": " . $e->getMessage()); return $this->json(['message' => 'Erreur lors de l\'envoie du mail.'], Response::HTTP_INTERNAL_SERVER_ERROR); } } throw $this->createAccessDeniedException(self::ACCESS_DENIED); } #[Route(path: '/accept-invitation', name: 'accept', methods: ['GET'])] public function acceptInvitation(Request $request): Response { $token = $request->get('token'); $userId = $request->get('id'); if (!$token || !$userId) { throw $this->createNotFoundException('Invalid invitation link.'); } $user = $this->userRepository->find($userId); if (!$user) { throw $this->createNotFoundException(self::NOT_FOUND); } if (!$this->userService->isPasswordTokenValid($user, $token)) { throw $this->createNotFoundException('Invalid or expired invitation token.'); } $orgId = $this->userService->getOrgFromToken($token); $uo = $this->uoRepository->findOneBy(['users' => $user, 'organization' => $orgId]); if (!$uo || $uo->getStatut() !== 'INVITED') { $this->logger->warning("User " . $user->getUserIdentifier() . " tried to accept an invitation but no pending invitation was found for organization ID " . $orgId); throw $this->createNotFoundException('No pending invitation found for this user and organization.'); } $uo->setModifiedAt(new \DateTimeImmutable()); $uo->setStatut("ACCEPTED"); $uo->setIsActive(true); $this->entityManager->persist($uo); $this->entityManager->flush(); $this->logger->info("User " . $user->getUserIdentifier() . " accepted invitation for organization ID " . $orgId); return $this->render('user/show.html.twig', ['user' => $user, 'orgId' => $orgId]); } }