From 372bf461360dbc8c441e54fc43e4ee9296e05ca5 Mon Sep 17 00:00:00 2001 From: Serreau Jovann Date: Sun, 22 Mar 2026 20:53:46 +0100 Subject: [PATCH] Add organizer suspension: toggle, badge, block access, audit log - User.isSuspended (nullable bool, null = not suspended) - Admin: toggle suspend/reactivate button on organizers list - SuspendedUserSubscriber: redirects suspended users to homepage with error - Audit log: organizer_suspended / organizer_reactivated - Badge 'Suspendu' (red) replaces offer badge when suspended Co-Authored-By: Claude Opus 4.6 (1M context) --- TASK_CHECKUP.md | 4 +- migrations/Version20260322130000.php | 26 ++++++++++ src/Controller/AdminController.php | 20 ++++++++ src/Entity/User.php | 15 ++++++ .../SuspendedUserSubscriber.php | 51 +++++++++++++++++++ templates/admin/organizers.html.twig | 11 +++- 6 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 migrations/Version20260322130000.php create mode 100644 src/EventSubscriber/SuspendedUserSubscriber.php diff --git a/TASK_CHECKUP.md b/TASK_CHECKUP.md index 2ec47aa..e5e91b4 100644 --- a/TASK_CHECKUP.md +++ b/TASK_CHECKUP.md @@ -20,13 +20,13 @@ ### Paiements & Finances - [ ] Ajouter le dashboard financier pour l'orga (total encaissé, en attente, remboursé) -- [ ] Ajouter les virements Stripe (payouts) dans l'onglet de l'orga +- [ ] Ajouter les virements Stripe (payouts) dans l'onglet de l'orga (il exite déja c'est "Virement") - [ ] Générer un récapitulatif mensuel des ventes (export CSV/PDF) ### Admin - [ ] Dashboard admin : stats globales (CA global, commission E-Ticket totale, commission Stripe totale, nb commandes, nb billets, nb orgas) - [ ] Admin : liste de toutes les commandes avec filtres -- [ ] Admin : pouvoir suspendre un organisateur +- [x] Admin : pouvoir suspendre/réactiver un organisateur (badge, bouton toggle, redirect si suspendu, audit log) - [x] Admin : pouvoir modifier l'offre/commission d'un orga existant - [ ] Vérifier que les permissions des sous-comptes sont respectées (scanner, events, tickets) - [x] Admin : logs des actions importantes (audit trail: commande, paiement, annulation, remboursement) diff --git a/migrations/Version20260322130000.php b/migrations/Version20260322130000.php new file mode 100644 index 0000000..0b1423a --- /dev/null +++ b/migrations/Version20260322130000.php @@ -0,0 +1,26 @@ +addSql('ALTER TABLE "user" ADD COLUMN IF NOT EXISTS is_suspended BOOLEAN DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE "user" DROP COLUMN IF EXISTS is_suspended'); + } +} diff --git a/src/Controller/AdminController.php b/src/Controller/AdminController.php index 3e47d9c..8a7a4b1 100644 --- a/src/Controller/AdminController.php +++ b/src/Controller/AdminController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Entity\AuditLog; use App\Entity\OrganizerInvitation; use App\Entity\User; +use App\Service\AuditService; use App\Service\MailerService; use App\Service\MeilisearchService; use App\Service\SiretService; @@ -430,6 +431,25 @@ class AdminController extends AbstractController ]); } + #[Route('/organisateur/{id}/suspendre', name: 'app_admin_suspend_organizer', methods: ['POST'])] + public function suspendOrganizer(User $user, EntityManagerInterface $em, AuditService $audit): Response + { + $suspended = !$user->isSuspended(); + $user->setIsSuspended($suspended ?: null); + $em->flush(); + + $audit->log($suspended ? 'organizer_suspended' : 'organizer_reactivated', 'User', $user->getId(), [ + 'email' => $user->getEmail(), + 'companyName' => $user->getCompanyName(), + ]); + + $this->addFlash('success', $suspended + ? 'Organisateur '.$user->getCompanyName().' suspendu.' + : 'Organisateur '.$user->getCompanyName().' reactive.'); + + return $this->redirectToRoute('app_admin_organizers', ['tab' => 'approved']); + } + #[Route('/evenements', name: 'app_admin_events')] public function events(Request $request, PaginatorInterface $paginator, \App\Service\EventIndexService $eventIndex): Response { diff --git a/src/Entity/User.php b/src/Entity/User.php index 9e2515f..e93b55b 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -94,6 +94,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column] private bool $isApproved = false; + #[ORM\Column(nullable: true)] + private ?bool $isSuspended = null; + #[ORM\Column(length: 255, nullable: true)] private ?string $offer = null; @@ -510,6 +513,18 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + public function isSuspended(): ?bool + { + return $this->isSuspended; + } + + public function setIsSuspended(?bool $isSuspended): static + { + $this->isSuspended = $isSuspended; + + return $this; + } + public function getOffer(): ?string { return $this->offer; diff --git a/src/EventSubscriber/SuspendedUserSubscriber.php b/src/EventSubscriber/SuspendedUserSubscriber.php new file mode 100644 index 0000000..3c8812c --- /dev/null +++ b/src/EventSubscriber/SuspendedUserSubscriber.php @@ -0,0 +1,51 @@ + ['onKernelRequest', 8], + ]; + } + + public function onKernelRequest(RequestEvent $event): void + { + if (!$event->isMainRequest()) { + return; + } + + $user = $this->security->getUser(); + if (!$user instanceof User) { + return; + } + + if (!$user->isSuspended()) { + return; + } + + $route = $event->getRequest()->attributes->getString('_route'); + if (\in_array($route, ['app_logout', 'app_home', 'app_login'], true)) { + return; + } + + $event->getRequest()->getSession()->getFlashBag()->add('error', 'Votre compte a ete suspendu. Contactez contact@e-cosplay.fr.'); + $event->setResponse(new RedirectResponse($this->urlGenerator->generate('app_home'))); + } +} diff --git a/templates/admin/organizers.html.twig b/templates/admin/organizers.html.twig index 838c240..a249289 100644 --- a/templates/admin/organizers.html.twig +++ b/templates/admin/organizers.html.twig @@ -69,9 +69,16 @@ {% if not orga.approved %} Voir la demande {% else %} -
- {{ orga.offer ?? '—' }} — {{ orga.commissionRate ?? '3' }}% +
+ {% if orga.suspended %} + Suspendu + {% else %} + {{ orga.offer ?? '—' }} — {{ orga.commissionRate ?? '3' }}% + {% endif %} Modifier +
+ +
{% endif %}