<?php
/**
* Created by PhpStorm.
* User: grego
* Date: 02/02/2026
* Time: 11:22
*/
namespace App\Controller\CossBundle;
use App\Entity\BatchEmail;
use App\Entity\Campaign;
use App\Entity\MailTemplate;
use App\Entity\Participation;
use App\Entity\Recipient;
use App\Manager\MailerManager;
use App\Repository\BatchEmailRepository;
use App\Repository\MailTemplateRepository;
use App\Repository\RecipientRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Routing\Annotation\Route;
class ParticipationController extends AbstractController
{
/**
* @Route("/participation/{campaignToken}", name="participation_recipients")
*/
public function participation(Request $request, $campaignToken = null, EntityManagerInterface $em, TranslatorInterface $translator)
{
$user = $this->getUser();
// Si l'utilisateur est déjà connecté, afficher la campagne
if ($user) {
if($campaignToken){
$campaign = $em->getRepository(Campaign::class)->findOneBy(['token' => $campaignToken]);
$participation = $em->getRepository(Participation::class)->findOneBy(['participant' => $user, 'campaign' => $campaign]);
if(!$participation){
$participation = new Participation();
$participation->setParticipant($user);
$participation->setCampaign($campaign);
$participation->setStartDate($campaign->getStartDate());
$participation->setEndDate($campaign->getEndDate());
$em->persist($participation);
$em->flush();
}
$today = new DateTime();
if($participation->getEndDate() < $today){
return $this->render('CossBundle/Participation/expired.html.twig');
}
// Récupérer les répondants déjà saisis
$recipients = $em->getRepository(Recipient::class)->getParticipantRecipients($participation);
$pendingRecipients = $em->getRepository(Recipient::class)->getPendingParticipantRecipients($participation);
$activeRecipients = $em->getRepository(Recipient::class)->getActiveParticipantRecipients($participation);
if(count($pendingRecipients) > 0){
$showValidateButton = true;
} else {
$showValidateButton = false;
}
$showRecallButton = false;
$yesterday = new \DateTime('-24 hours');
foreach ($recipients as $recipient) {
$recalledAt = $recipient->getRecalledAt();
if ($recalledAt === null || $recalledAt < $yesterday
) {
$showRecallButton = true;
break; // inutile de continuer
}
}
if(count($activeRecipients) > 0){
$btnText = $translator->trans(
"Valider les nouveaux répondants",
array(), 'coss'
);
} else {
$btnText = $translator->trans(
"Lancer mon évaluation 360",
array(), 'coss'
);
}
return $this->render('CossBundle/Participation/index.html.twig', [
'showRecallButton' => $showRecallButton,
'showValidateButton' => $showValidateButton,
'btnText' => $btnText,
'campaignToken' => $campaignToken,
'participation' => $participation,
'recipients' => $recipients,
]);
} else {
$lastParticipation = $em->getRepository(Participation::class)->findOneBy(['participant' => $user], ['createdAt' => 'DESC']);
if ($lastParticipation) {
$today = new DateTime();
if($lastParticipation->getEndDate() < $today){
return $this->render('CossBundle/Participation/expired.html.twig');
}
// Récupérer les répondants déjà saisis
$recipients = $em->getRepository(Recipient::class)->getParticipantRecipients($lastParticipation);
$pendingRecipients = $em->getRepository(Recipient::class)->getPendingParticipantRecipients($lastParticipation);
$activeRecipients = $em->getRepository(Recipient::class)->getActiveParticipantRecipients($lastParticipation);
if(count($pendingRecipients) > 0){
$showValidateButton = true;
} else {
$showValidateButton = false;
}
$showRecallButton = false;
$yesterday = new \DateTime('-24 hours');
foreach ($recipients as $recipient) {
$recalledAt = $recipient->getRecalledAt();
if ($recalledAt === null || $recalledAt < $yesterday
) {
$showRecallButton = true;
break; // inutile de continuer
}
}
if(count($activeRecipients) > 0){
$btnText = $translator->trans(
"Valider les nouveaux répondants",
array(), 'coss'
);
} else {
$btnText = $translator->trans(
"Lancer mon évaluation 360",
array(), 'coss'
);
}
return $this->render('CossBundle/Participation/index.html.twig', [
'showRecallButton' => $showRecallButton,
'showValidateButton' => $showValidateButton,
'btnText' => $btnText,
'campaignToken' => $lastParticipation->getCampaign()->getToken(),
'participation' => $lastParticipation,
'recipients' => $recipients,
]);
} else {
$msg = $translator->trans(
"Une erreur est survenue, merci de réessayer en utilisant le lien initialement reçu",
array(), 'coss'
);
$this->addFlash("error", $msg);
return $this->render('CossBundle/error.html.twig');
}
}
} else {
// Sinon, stocker l'URL dans la session pour redirection après login
$request->getSession()->set('participation_redirect', $request->getUri());
// Rediriger vers le login
return $this->redirectToRoute('participation_auth_login');
}
}
/**
* @Route("/participation/{participationId}/recipient/add", name="participation_recipient_add", methods={"POST"})
*/
public function addRecipient(int $participationId, Request $request, EntityManagerInterface $em): JsonResponse
{
$participation = $em->getRepository(Participation::class)->find($participationId);
if (!$participation) {
return $this->json(['error'=>'Participation introuvable'],404);
}
$today = new DateTime();
if($participation->getEndDate() < $today){
return $this->json(['error'=>'Participation expirée'],404);
}
$firstname = trim($request->request->get('firstname'));
$lastname = trim($request->request->get('lastname'));
$email = trim($request->request->get('email'));
// Validations
if (!$firstname || !$lastname || !$email) {
return $this->json(['error'=>'Tous les champs sont obligatoires'],400);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->json(['error'=>'Email invalide'],400);
}
// Vérifier doublon
$existing = $em->getRepository(Recipient::class)->getParticipationExistingRecipient($participation, $email);
if ($existing) {
return $this->json(['error'=>'Ce répondant existe déjà'],400);
}
$recipient = new Recipient();
$recipient->setParticipation($participation);
$recipient->setFirstname($firstname);
$recipient->setLastname($lastname);
$recipient->setEmail($email);
$recipient->setStatus(RecipientRepository::STATUS_PENDING);
$em->persist($recipient);
$em->flush();
return $this->json([
'id' => $recipient->getId(),
'firstname' => $recipient->getFirstname(),
'lastname' => $recipient->getLastname(),
'email' => $recipient->getEmail(),
'createdAt' => $recipient->getCreatedAt(),
'recalledAt' => $recipient->getRecalledAt(),
'status' => $recipient->getStatusFormatted()
]);
}
/**
* @Route("/participation/{participationId}/recipient/edit/{id}", name="participation_recipient_edit", methods={"POST"})
*/
public function editRecipient(int $participationId, int $id, Request $request, EntityManagerInterface $em): JsonResponse
{
$recipient = $em->getRepository(Recipient::class)->find($id);
if (!$recipient) return $this->json(['error'=>'Répondant introuvable'],404);
$participation = $recipient->getParticipation();
$user = $this->getUser();
if ($participation->getId() !== $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
return $this->json(['error'=>'Accès interdit'],403);
}
$today = new DateTime();
if($participation->getEndDate() < $today){
return $this->json(['error'=>'Participation expirée'],404);
}
$firstname = trim($request->request->get('firstname'));
$lastname = trim($request->request->get('lastname'));
$email = trim($request->request->get('email'));
if (!$firstname || !$lastname || !$email) {
return $this->json(['error'=>'Tous les champs sont obligatoires'],400);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->json(['error'=>'Email invalide'],400);
}
// Vérifier doublon
$existing = $em->getRepository(Recipient::class)->getParticipationExistingRecipient($participation, $email);
if ($existing && $existing->getId() !== $recipient->getId()) {
return $this->json(['error'=>'Ce répondant existe déjà'],400);
}
$recipient->setFirstname($firstname);
$recipient->setLastname($lastname);
$recipient->setEmail($email);
$em->flush();
return $this->json([
'id' => $recipient->getId(),
'firstname' => $recipient->getFirstname(),
'lastname' => $recipient->getLastname(),
'email' => $recipient->getEmail(),
'createdAt' => $recipient->getCreatedAt(),
'recalledAt' => $recipient->getRecalledAt(),
'status' => $recipient->getStatusFormatted()
]);
}
/**
* @Route("/participation/{participationId}/recipient/delete/{id}", name="participation_recipient_delete", methods={"DELETE"})
*/
public function deleteRecipient($participationId, $id, EntityManagerInterface $em)
{
$recipient = $em->getRepository(Recipient::class)->find($id);
if (!$recipient) return $this->json(['error'=>'Répondant introuvable'],404);
$participation = $recipient->getParticipation();
$user = $this->getUser();
if ($participation->getId() !== $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
return $this->json(['error'=>'Accès interdit'],403);
}
$today = new DateTime();
if($participation->getEndDate() < $today){
return $this->json(['error'=>'Participation expirée'],404);
}
if ($recipient) {
if(count($recipient->getQuizAnswers()) > 0){
$recipient->setStatus(RecipientRepository::STATUS_DELETED);
$em->persist($recipient);
} else {
$em->remove($recipient);
}
$em->flush();
}
return $this->json(['success' => true]);
}
/**
* @Route("/participation/{participationId}/recipient/recall", name="participation_recipient_recall", methods={"GET"})
*/
public function recallRecipient($participationId, Request $request, EntityManagerInterface $em, TranslatorInterface $translator)
{
$user = $this->getUser();
$participation = $em->getRepository(Participation::class)->find($participationId);
if ($participation->getId() != $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
$msg = $translator->trans(
"Impossible d'accéder à cette participation.",
array(), 'coss'
);
$this->addFlash("error", $msg);
return $this->render('CossBundle/error.html.twig');
}
$today = new DateTime();
if($participation->getEndDate() < $today){
$msg = $translator->trans(
"Participation expirée.",
array(), 'coss'
);
$this->addFlash("error", $msg);
return $this->render('CossBundle/error.html.twig');
}
$recipients = $em->getRepository(Recipient::class)->getActiveParticipantRecipients($participation);
foreach ($recipients as $recipient) {
$canRecall = false;
if (!$recipient->getRecalledAt()) {
// jamais relancé → peut relancer
$canRecall = true;
} else {
// date de relance précédente + 24h
$now = new \DateTime();
$interval = $now->getTimestamp() - $recipient->getRecalledAt()->getTimestamp();
if ($interval >= 24 * 60 * 60) {
$canRecall = true;
}
}
if ($canRecall) {
$batchEmail = new BatchEmail();
$batchEmail->setRecipient($recipient);
$batchEmail->setStatus(BatchEmailRepository::STATUS_PENDING);
$batchEmail->setType(MailTemplateRepository::MANUAL_RECALL);
$em->persist($batchEmail);
$recipient->setRecalledAt(new Datetime());
$em->persist($recipient);
}
}
$em->flush();
return $this->render('CossBundle/Participation/recall_success.html.twig', array(
'campaignToken' => $participation->getCampaign()->getToken(),
'participation' => $participation
));
}
/**
* @Route("/participation/{campaignToken}/validate", name="participation_validate_recipients", methods={"GET"})
*/
public function validateRecipients(Request $request, $campaignToken = null, EntityManagerInterface $em, TranslatorInterface $translator)
{
$user = $this->getUser();
if ($user) {
if ($campaignToken) {
$campaign = $em->getRepository(Campaign::class)->findOneBy(['token' => $campaignToken]);
$participation = $em->getRepository(Participation::class)->findOneBy(['participant' => $user, 'campaign' => $campaign]);
$today = new DateTime();
if($participation->getEndDate() < $today){
return $this->render('CossBundle/Participation/expired.html.twig');
}
// Récupérer les répondants déjà saisis
$recipients = $em->getRepository(Recipient::class)->getPendingParticipantRecipients($participation);
foreach ($recipients as $recipient){
$batchEmail = new BatchEmail();
$batchEmail->setRecipient($recipient);
$batchEmail->setStatus(BatchEmailRepository::STATUS_PENDING);
$batchEmail->setType(MailTemplateRepository::QUIZ_EMAIL);
$em->persist($batchEmail);
$recipient->setStatus(RecipientRepository::STATUS_ACTIVE);
$recipient->setRecalledAt(new DateTime());
$em->persist($recipient);
$em->flush();
}
$mailTemplate = $em->getRepository(MailTemplate::class)->findOneBy(array('type' => MailTemplateRepository::INFORM_RECIPIENTS, 'program' => $campaign->getProgram()));
$emails = [];
foreach ($recipients as $r) {
$emails[] = $r->getEmail();
}
$to = array_shift($emails);
return $this->render('CossBundle/Participation/success.html.twig', array(
'campaignToken' => $campaignToken,
'recipients' => $recipients,
'participation' => $participation,
'mailSubject' => $mailTemplate->getObject(),
'mailBody' => $mailTemplate->getContent(),
'to' => $to,
'bccEmails' => implode(',', $emails),
));
} else {
$msg = $translator->trans(
"Une erreur est survenue, campagne introuvable",
array(), 'coss'
);
$this->addFlash("error", $msg);
return $this->render('CossBundle/error.html.twig');
}
} else {
// Rediriger vers le login
return $this->redirectToRoute('participation_auth_login');
}
}
}