denyAccessUnlessGranted('ROLE_USER'); $actingUser = $this->getUser(); // 1. Super Admin Case: Just show the list if ($this->isGranted("ROLE_ADMIN")) { return $this->render('organization/index.html.twig', ['hasOrganizations' => true]); } // 2. Organization Admin Case: Get their specific orgs $orgs = $this->userOrganizationService->getAdminOrganizationsForUser($actingUser); // If exactly one org, jump straight to it if (count($orgs) === 1) { return $this->redirectToRoute('organization_show', ['id' => $orgs[0]->getId()]); } // If multiple orgs, show the list if (count($orgs) > 1) { return $this->render('organization/index.html.twig', ['hasOrganizations' => true]); } // 3. Fallback: No access/No orgs found $this->loggerService->logEntityNotFound('Organization', [ 'user_id' => $actingUser->getUserIdentifier(), 'message' => 'No admin organizations found' ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, aucune organisation trouvée.'); return $this->redirectToRoute('app_index'); } #[Route(path: '/', name: 'create', methods: ['POST'])] public function create(Request $request): JsonResponse { $this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN'); $actingUser = $this->getUser(); $data = $request->request->all(); // 2. Access file data specifically $logoFile = $request->files->get('logoUrl'); $organization = new Organizations(); $existingOrg = $this->organizationsRepository->findOneBy(['email' => $data['email']]); if ($existingOrg) { return $this->json(['error' => 'Une organisation avec cet email existe déjà.'], 400); } $form = $this->createForm(OrganizationForm::class, $organization,[ 'csrf_protection' => false, 'allow_extra_fields' => true, ]); $form->submit(array_merge($data, ['logoUrl' => $logoFile])); if ($form->isValid()) { try { if ($logoFile) { $this->organizationsService->handleLogo($organization, $logoFile); } $organization->setProjectPrefix($this->organizationsService->generateUniqueProjectPrefix()); $organization->setName(ucFirst($organization->getName())); $this->entityManager->persist($organization); $this->entityManager->flush(); // Loggers... $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Created via ajax"); return $this->json([ 'message' => 'Organisation créée avec succès.', 'id' => $organization->getId() ], Response::HTTP_CREATED); } catch (Exception $e) { return $this->json(['error' => $e->getMessage()], 500); } } // 4. Return specific validation errors to help debugging $errors = []; foreach ($form->getErrors(true) as $error) { $errors[] = $error->getMessage(); } return $this->json(['error' => 'Validation failed', 'details' => $errors], 400); } #[Route(path: '/{id}', name: 'edit', methods: ['PUT'])] public function edit(Request $request, int $id): JsonResponse { $this->denyAccessUnlessGranted('ROLE_USER'); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for get endpoint' ], $actingUser->getUserIdentifier()); return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND); } if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) { $this->loggerService->logAccessDenied($actingUser->getUserIdentifier()); return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN); } $data = $request->request->all(); $logoFile = $request->files->get('logoUrl'); $form = $this->createForm(OrganizationForm::class, $organization,[ 'csrf_protection' => false, 'allow_extra_fields' => true, ]); if ($logoFile) { $form->submit(array_merge($data, ['logoUrl' => $logoFile]), false); } $form->submit(array_merge($request->request->all(), $request->files->all())); if ($form->isSubmitted() && $form->isValid()) { try { if ($logoFile) { $this->organizationsService->handleLogo($organization, $logoFile); } $organization->setName(ucFirst($organization->getName())); $this->entityManager->persist($organization); $this->entityManager->flush(); $this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName()); $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Edited via ajax"); return $this->json(['message' => 'Organisation modifiée avec succès.']); } catch (Exception $e) { return $this->json(['error' => $e->getMessage()], 500); } } else { $errors = []; foreach ($form->getErrors(true) as $error) { $errors[] = $error->getMessage(); } return $this->json(['error' => 'Validation failed', 'details' => $errors], 400); } } #[Route(path: '/view/{id}', name: 'show', methods: ['GET'])] public function view($id): Response { $this->denyAccessUnlessGranted('ROLE_USER'); $organization = $this->organizationsRepository->find($id); $actingUser = $this->getUser(); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for view' ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); return $this->redirectToRoute('organization_index'); } //check if the user is admin of the organization if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) { $this->loggerService->logAccessDenied($actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, accès refusé.'); throw new AccessDeniedHttpException('Access denied'); } //TODO: add project to the response $allApps = $this->entityManager->getRepository(Apps::class)->findAll(); // appsAll $orgApps = $organization->getApps()->toArray(); // apps $apps = $this->organizationsService->appsAccess($allApps, $orgApps); $actions = $this->entityManager->getRepository(Actions::class)->findBy(['Organization' => $organization], orderBy: ['date' => 'DESC'], limit: 15); $activities = $this->actionService->formatActivities($actions); $this->actionService->createAction("View Organization", $actingUser, $organization, $organization->getName()); return $this->render('organization/show.html.twig', [ 'organization' => $organization, 'applications' => $apps, 'activities' => $activities, ]); } #[Route(path: '/delete/{id}', name: 'delete', methods: ['POST'])] public function delete($id): Response { $this->denyAccessUnlessGranted("ROLE_SUPER_ADMIN"); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for delete' ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } try { $organization->setIsActive(false); $organization->setIsDeleted(true); $this->organizationsService->deleteLogo($organization); // Deactivate all associated UsersOrganizations $this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, null, $organization); $this->entityManager->persist($organization); $this->actionService->createAction("Delete Organization", $actingUser, $organization, $organization->getName()); $this->entityManager->flush(); $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(),'Organization Deleted'); if ($this->isGranted("ROLE_SUPER_ADMIN")) { $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization Deleted', $organization->getId()); } $this->addFlash('success', 'Organisation supprimée avec succès.'); }catch (\Exception $e){ $this->loggerService->logError($actingUser->getUserIdentifier(), ['message' => 'Error deleting organization: '.$e->getMessage()]); $this->addFlash('danger', 'Erreur lors de la suppression de l\'organization.'); } return $this->redirectToRoute('organization_index'); } #[Route(path: '/deactivate/{id}', name: 'deactivate', methods: ['POST'])] public function deactivate($id): Response { $this->denyAccessUnlessGranted("ROLE_SUPER_ADMIN"); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for deactivate' ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } $organization->setIsActive(false); $this->entityManager->persist($organization); $this->actionService->createAction("Deactivate Organization", $actingUser, $organization, $organization->getName()); $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization deactivated', $organization->getId()); $this->addFlash('success', 'Organisation désactivé avec succès.'); return $this->redirectToRoute('organization_index'); } #[Route(path: '/activate/{id}', name: 'activate', methods: ['POST'])] public function activate($id): Response { $this->denyAccessUnlessGranted("ROLE_SUPER_ADMIN"); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for activate' ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } $organization->setIsActive(true); $this->entityManager->persist($organization); $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(),'Organization Activated'); $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization Activated', $organization->getId()); $this->actionService->createAction("Activate Organization", $actingUser, $organization, $organization->getName()); $this->addFlash('success', 'Organisation activée avec succès.'); return $this->redirectToRoute('organization_index'); } // API endpoint to fetch organizations data for Tabulator #[Route(path: '/data', name: 'data', methods: ['GET'])] public function data(Request $request): JsonResponse { $this->denyAccessUnlessGranted('ROLE_USER'); $page = max(1, $request->query->getInt('page', 1)); $size = max(1, $request->query->getInt('size', 10)); $filters = $request->query->all('filter'); // Fetch paginated results $paginator = $this->organizationsRepository->findAdmissibleOrganizations( $this->getUser(), $this->isGranted('ROLE_ADMIN'), // Super Admin check $page, $size, $filters ); $total = count($paginator); $data = array_map(function (Organizations $org) { return [ 'id' => $org->getId(), 'name' => $org->getName(), 'email' => $org->getEmail(), 'logoUrl' => $org->getLogoUrl() ?: null, 'active' => $org->isActive(), 'showUrl' => $this->generateUrl('organization_show', ['id' => $org->getId()]), ]; }, iterator_to_array($paginator)); return $this->json([ 'data' => $data, 'last_page' => (int)ceil($total / $size), 'total' => $total, ]); } /* Ajax route to get users of an organization ( used in select field for admin of an org ) */ #[Route(path: '/{id}/users', name: 'users', methods: ['GET'])] public function users($id): JsonResponse{ $this->denyAccessUnlessGranted("ROLE_USER"); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for users endpoint' ], $actingUser->getUserIdentifier()); return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND); } if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) { $this->loggerService->logAccessDenied($actingUser->getUserIdentifier()); return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN); } $uos = $this->usersOrganizationsRepository->findBy(['organization' => $organization, 'isActive' => true]); $users = array_map(function (UsersOrganizations $uo) { $user = $uo->getUsers(); return [ 'id' => $user->getId(), 'email' => $user->getEmail() ]; }, $uos); return $this->json(['users' => $users]); } /* * Path used to get data on an organization for the edit modal */ #[Route(path: '/{id}', name: 'get', methods: ['GET'])] public function get(int $id): JsonResponse{ $this->denyAccessUnlessGranted('ROLE_USER'); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for get endpoint' ], $actingUser->getUserIdentifier()); return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND); } if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) { $this->loggerService->logAccessDenied($actingUser->getUserIdentifier()); return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN); } return $this->json([ 'id' => $organization->getId(), 'name' => $organization->getName(), 'email' => $organization->getEmail(), 'logoUrl' => $organization->getLogoUrl() ?: null, 'active' => $organization->isActive(), ]); } }