src/Controller/CossBundle/ParticipationController.php line 34

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * User: grego
  5.  * Date: 02/02/2026
  6.  * Time: 11:22
  7.  */
  8. namespace App\Controller\CossBundle;
  9. use App\Entity\BatchEmail;
  10. use App\Entity\Campaign;
  11. use App\Entity\MailTemplate;
  12. use App\Entity\Participation;
  13. use App\Entity\Recipient;
  14. use App\Manager\MailerManager;
  15. use App\Repository\BatchEmailRepository;
  16. use App\Repository\MailTemplateRepository;
  17. use App\Repository\RecipientRepository;
  18. use DateTime;
  19. use Doctrine\ORM\EntityManagerInterface;
  20. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  21. use Symfony\Component\HttpFoundation\JsonResponse;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Contracts\Translation\TranslatorInterface;
  24. use Symfony\Component\Routing\Annotation\Route;
  25. class ParticipationController extends AbstractController
  26. {
  27.     /**
  28.      * @Route("/participation/{campaignToken}", name="participation_recipients")
  29.      */
  30.     public function participation(Request $request$campaignToken nullEntityManagerInterface $emTranslatorInterface $translator)
  31.     {
  32.         $user $this->getUser();
  33.         // Si l'utilisateur est déjà connecté, afficher la campagne
  34.         if ($user) {
  35.             if($campaignToken){
  36.                 $campaign $em->getRepository(Campaign::class)->findOneBy(['token' => $campaignToken]);
  37.                 $participation $em->getRepository(Participation::class)->findOneBy(['participant' => $user'campaign' => $campaign]);
  38.                 if(!$participation){
  39.                     $participation = new Participation();
  40.                     $participation->setParticipant($user);
  41.                     $participation->setCampaign($campaign);
  42.                     $participation->setStartDate($campaign->getStartDate());
  43.                     $participation->setEndDate($campaign->getEndDate());
  44.                     $em->persist($participation);
  45.                     $em->flush();
  46.                 }
  47.                 $today = new DateTime();
  48.                 if($participation->getEndDate() < $today){
  49.                     return $this->render('CossBundle/Participation/expired.html.twig');
  50.                 }
  51.                 // Récupérer les répondants déjà saisis
  52.                 $recipients $em->getRepository(Recipient::class)->getParticipantRecipients($participation);
  53.                 $pendingRecipients $em->getRepository(Recipient::class)->getPendingParticipantRecipients($participation);
  54.                 $activeRecipients $em->getRepository(Recipient::class)->getActiveParticipantRecipients($participation);
  55.                 if(count($pendingRecipients) > 0){
  56.                     $showValidateButton true;
  57.                 } else {
  58.                     $showValidateButton false;
  59.                 }
  60.                 $showRecallButton false;
  61.                 $yesterday = new \DateTime('-24 hours');
  62.                 foreach ($recipients as $recipient) {
  63.                     $recalledAt $recipient->getRecalledAt();
  64.                     if ($recalledAt === null || $recalledAt $yesterday
  65.                     ) {
  66.                         $showRecallButton true;
  67.                         break; // inutile de continuer
  68.                     }
  69.                 }
  70.                 if(count($activeRecipients) > 0){
  71.                     $btnText $translator->trans(
  72.                         "Valider les nouveaux répondants",
  73.                         array(), 'coss'
  74.                     );
  75.                 } else {
  76.                     $btnText $translator->trans(
  77.                         "Lancer mon évaluation 360",
  78.                         array(), 'coss'
  79.                     );
  80.                 }
  81.                 return $this->render('CossBundle/Participation/index.html.twig', [
  82.                     'showRecallButton' => $showRecallButton,
  83.                     'showValidateButton' => $showValidateButton,
  84.                     'btnText' => $btnText,
  85.                     'campaignToken' => $campaignToken,
  86.                     'participation' => $participation,
  87.                     'recipients' => $recipients,
  88.                 ]);
  89.             } else {
  90.                 $lastParticipation $em->getRepository(Participation::class)->findOneBy(['participant' => $user], ['createdAt' => 'DESC']);
  91.                 if ($lastParticipation) {
  92.                     $today = new DateTime();
  93.                     if($lastParticipation->getEndDate() < $today){
  94.                         return $this->render('CossBundle/Participation/expired.html.twig');
  95.                     }
  96.                     // Récupérer les répondants déjà saisis
  97.                     $recipients $em->getRepository(Recipient::class)->getParticipantRecipients($lastParticipation);
  98.                     $pendingRecipients $em->getRepository(Recipient::class)->getPendingParticipantRecipients($lastParticipation);
  99.                     $activeRecipients $em->getRepository(Recipient::class)->getActiveParticipantRecipients($lastParticipation);
  100.                     if(count($pendingRecipients) > 0){
  101.                         $showValidateButton true;
  102.                     } else {
  103.                         $showValidateButton false;
  104.                     }
  105.                     $showRecallButton false;
  106.                     $yesterday = new \DateTime('-24 hours');
  107.                     foreach ($recipients as $recipient) {
  108.                         $recalledAt $recipient->getRecalledAt();
  109.                         if ($recalledAt === null || $recalledAt $yesterday
  110.                         ) {
  111.                             $showRecallButton true;
  112.                             break; // inutile de continuer
  113.                         }
  114.                     }
  115.                     if(count($activeRecipients) > 0){
  116.                         $btnText $translator->trans(
  117.                             "Valider les nouveaux répondants",
  118.                             array(), 'coss'
  119.                         );
  120.                     } else {
  121.                         $btnText $translator->trans(
  122.                             "Lancer mon évaluation 360",
  123.                             array(), 'coss'
  124.                         );
  125.                     }
  126.                     return $this->render('CossBundle/Participation/index.html.twig', [
  127.                         'showRecallButton' => $showRecallButton,
  128.                         'showValidateButton' => $showValidateButton,
  129.                         'btnText' => $btnText,
  130.                         'campaignToken' => $lastParticipation->getCampaign()->getToken(),
  131.                         'participation' => $lastParticipation,
  132.                         'recipients' => $recipients,
  133.                     ]);
  134.                 } else {
  135.                     $msg $translator->trans(
  136.                         "Une erreur est survenue, merci de réessayer en utilisant le lien initialement reçu",
  137.                         array(), 'coss'
  138.                     );
  139.                     $this->addFlash("error"$msg);
  140.                     return $this->render('CossBundle/error.html.twig');
  141.                 }
  142.             }
  143.         } else {
  144.             // Sinon, stocker l'URL dans la session pour redirection après login
  145.             $request->getSession()->set('participation_redirect'$request->getUri());
  146.             // Rediriger vers le login
  147.             return $this->redirectToRoute('participation_auth_login');
  148.         }
  149.     }
  150.     /**
  151.      * @Route("/participation/{participationId}/recipient/add", name="participation_recipient_add", methods={"POST"})
  152.      */
  153.     public function addRecipient(int $participationIdRequest $requestEntityManagerInterface $em): JsonResponse
  154.     {
  155.         $participation $em->getRepository(Participation::class)->find($participationId);
  156.         if (!$participation) {
  157.             return $this->json(['error'=>'Participation introuvable'],404);
  158.         }
  159.         $today = new DateTime();
  160.         if($participation->getEndDate() < $today){
  161.             return $this->json(['error'=>'Participation expirée'],404);
  162.         }
  163.         $firstname trim($request->request->get('firstname'));
  164.         $lastname trim($request->request->get('lastname'));
  165.         $email trim($request->request->get('email'));
  166.         // Validations
  167.         if (!$firstname || !$lastname || !$email) {
  168.             return $this->json(['error'=>'Tous les champs sont obligatoires'],400);
  169.         }
  170.         if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  171.             return $this->json(['error'=>'Email invalide'],400);
  172.         }
  173.         // Vérifier doublon
  174.         $existing $em->getRepository(Recipient::class)->getParticipationExistingRecipient($participation$email);
  175.         if ($existing) {
  176.             return $this->json(['error'=>'Ce répondant existe déjà'],400);
  177.         }
  178.         $recipient = new Recipient();
  179.         $recipient->setParticipation($participation);
  180.         $recipient->setFirstname($firstname);
  181.         $recipient->setLastname($lastname);
  182.         $recipient->setEmail($email);
  183.         $recipient->setStatus(RecipientRepository::STATUS_PENDING);
  184.         $em->persist($recipient);
  185.         $em->flush();
  186.         return $this->json([
  187.             'id' => $recipient->getId(),
  188.             'firstname' => $recipient->getFirstname(),
  189.             'lastname' => $recipient->getLastname(),
  190.             'email' => $recipient->getEmail(),
  191.             'createdAt' => $recipient->getCreatedAt(),
  192.             'recalledAt' => $recipient->getRecalledAt(),
  193.             'status' => $recipient->getStatusFormatted()
  194.         ]);
  195.     }
  196.     /**
  197.      * @Route("/participation/{participationId}/recipient/edit/{id}", name="participation_recipient_edit", methods={"POST"})
  198.      */
  199.     public function editRecipient(int $participationIdint $idRequest $requestEntityManagerInterface $em): JsonResponse
  200.     {
  201.         $recipient $em->getRepository(Recipient::class)->find($id);
  202.         if (!$recipient) return $this->json(['error'=>'Répondant introuvable'],404);
  203.         $participation $recipient->getParticipation();
  204.         $user $this->getUser();
  205.         if ($participation->getId() !== $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
  206.             return $this->json(['error'=>'Accès interdit'],403);
  207.         }
  208.         $today = new DateTime();
  209.         if($participation->getEndDate() < $today){
  210.             return $this->json(['error'=>'Participation expirée'],404);
  211.         }
  212.         $firstname trim($request->request->get('firstname'));
  213.         $lastname trim($request->request->get('lastname'));
  214.         $email trim($request->request->get('email'));
  215.         if (!$firstname || !$lastname || !$email) {
  216.             return $this->json(['error'=>'Tous les champs sont obligatoires'],400);
  217.         }
  218.         if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  219.             return $this->json(['error'=>'Email invalide'],400);
  220.         }
  221.         // Vérifier doublon
  222.         $existing $em->getRepository(Recipient::class)->getParticipationExistingRecipient($participation$email);
  223.         if ($existing && $existing->getId() !== $recipient->getId()) {
  224.             return $this->json(['error'=>'Ce répondant existe déjà'],400);
  225.         }
  226.         $recipient->setFirstname($firstname);
  227.         $recipient->setLastname($lastname);
  228.         $recipient->setEmail($email);
  229.         $em->flush();
  230.         return $this->json([
  231.             'id' => $recipient->getId(),
  232.             'firstname' => $recipient->getFirstname(),
  233.             'lastname' => $recipient->getLastname(),
  234.             'email' => $recipient->getEmail(),
  235.             'createdAt' => $recipient->getCreatedAt(),
  236.             'recalledAt' => $recipient->getRecalledAt(),
  237.             'status' => $recipient->getStatusFormatted()
  238.         ]);
  239.     }
  240.     /**
  241.      * @Route("/participation/{participationId}/recipient/delete/{id}", name="participation_recipient_delete", methods={"DELETE"})
  242.      */
  243.     public function deleteRecipient($participationId$idEntityManagerInterface $em)
  244.     {
  245.         $recipient $em->getRepository(Recipient::class)->find($id);
  246.         if (!$recipient) return $this->json(['error'=>'Répondant introuvable'],404);
  247.         $participation $recipient->getParticipation();
  248.         $user $this->getUser();
  249.         if ($participation->getId() !== $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
  250.             return $this->json(['error'=>'Accès interdit'],403);
  251.         }
  252.         $today = new DateTime();
  253.         if($participation->getEndDate() < $today){
  254.             return $this->json(['error'=>'Participation expirée'],404);
  255.         }
  256.         if ($recipient) {
  257.             if(count($recipient->getQuizAnswers()) > 0){
  258.                 $recipient->setStatus(RecipientRepository::STATUS_DELETED);
  259.                 $em->persist($recipient);
  260.             } else {
  261.                 $em->remove($recipient);
  262.             }
  263.             $em->flush();
  264.         }
  265.         return $this->json(['success' => true]);
  266.     }
  267.     /**
  268.      * @Route("/participation/{participationId}/recipient/recall", name="participation_recipient_recall", methods={"GET"})
  269.      */
  270.     public function recallRecipient($participationIdRequest $requestEntityManagerInterface $emTranslatorInterface $translator)
  271.     {
  272.         $user $this->getUser();
  273.         $participation $em->getRepository(Participation::class)->find($participationId);
  274.         if ($participation->getId() != $participationId || $participation->getParticipant()->getId() !== $user->getId()) {
  275.             $msg $translator->trans(
  276.                 "Impossible d'accéder à cette participation.",
  277.                 array(), 'coss'
  278.             );
  279.             $this->addFlash("error"$msg);
  280.             return $this->render('CossBundle/error.html.twig');
  281.         }
  282.         $today = new DateTime();
  283.         if($participation->getEndDate() < $today){
  284.             $msg $translator->trans(
  285.                 "Participation expirée.",
  286.                 array(), 'coss'
  287.             );
  288.             $this->addFlash("error"$msg);
  289.             return $this->render('CossBundle/error.html.twig');
  290.         }
  291.         $recipients $em->getRepository(Recipient::class)->getActiveParticipantRecipients($participation);
  292.         foreach ($recipients as $recipient) {
  293.             $canRecall false;
  294.             if (!$recipient->getRecalledAt()) {
  295.                 // jamais relancé → peut relancer
  296.                 $canRecall true;
  297.             } else {
  298.                 // date de relance précédente + 24h
  299.                 $now = new \DateTime();
  300.                 $interval $now->getTimestamp() - $recipient->getRecalledAt()->getTimestamp();
  301.                 if ($interval >= 24 60 60) {
  302.                     $canRecall true;
  303.                 }
  304.             }
  305.             if ($canRecall) {
  306.                 $batchEmail = new BatchEmail();
  307.                 $batchEmail->setRecipient($recipient);
  308.                 $batchEmail->setStatus(BatchEmailRepository::STATUS_PENDING);
  309.                 $batchEmail->setType(MailTemplateRepository::MANUAL_RECALL);
  310.                 $em->persist($batchEmail);
  311.                 $recipient->setRecalledAt(new Datetime());
  312.                 $em->persist($recipient);
  313.             }
  314.         }
  315.         $em->flush();
  316.         return $this->render('CossBundle/Participation/recall_success.html.twig', array(
  317.             'campaignToken' => $participation->getCampaign()->getToken(),
  318.             'participation' => $participation
  319.         ));
  320.     }
  321.     /**
  322.      * @Route("/participation/{campaignToken}/validate", name="participation_validate_recipients", methods={"GET"})
  323.      */
  324.     public function validateRecipients(Request $request$campaignToken nullEntityManagerInterface $emTranslatorInterface $translator)
  325.     {
  326.         $user $this->getUser();
  327.         if ($user) {
  328.             if ($campaignToken) {
  329.                 $campaign $em->getRepository(Campaign::class)->findOneBy(['token' => $campaignToken]);
  330.                 $participation $em->getRepository(Participation::class)->findOneBy(['participant' => $user'campaign' => $campaign]);
  331.                 $today = new DateTime();
  332.                 if($participation->getEndDate() < $today){
  333.                     return $this->render('CossBundle/Participation/expired.html.twig');
  334.                 }
  335.                 // Récupérer les répondants déjà saisis
  336.                 $recipients $em->getRepository(Recipient::class)->getPendingParticipantRecipients($participation);
  337.                 foreach ($recipients as $recipient){
  338.                     $batchEmail = new BatchEmail();
  339.                     $batchEmail->setRecipient($recipient);
  340.                     $batchEmail->setStatus(BatchEmailRepository::STATUS_PENDING);
  341.                     $batchEmail->setType(MailTemplateRepository::QUIZ_EMAIL);
  342.                     $em->persist($batchEmail);
  343.                     $recipient->setStatus(RecipientRepository::STATUS_ACTIVE);
  344.                     $recipient->setRecalledAt(new DateTime());
  345.                     $em->persist($recipient);
  346.                     $em->flush();
  347.                 }
  348.                 $mailTemplate $em->getRepository(MailTemplate::class)->findOneBy(array('type' => MailTemplateRepository::INFORM_RECIPIENTS'program' => $campaign->getProgram()));
  349.                 $emails = [];
  350.                 foreach ($recipients as $r) {
  351.                     $emails[] = $r->getEmail();
  352.                 }
  353.                 $to array_shift($emails);
  354.                 return $this->render('CossBundle/Participation/success.html.twig', array(
  355.                     'campaignToken' => $campaignToken,
  356.                     'recipients' => $recipients,
  357.                     'participation' => $participation,
  358.                     'mailSubject' => $mailTemplate->getObject(),
  359.                     'mailBody' => $mailTemplate->getContent(),
  360.                     'to' => $to,
  361.                     'bccEmails' => implode(','$emails),
  362.                 ));
  363.             } else {
  364.                 $msg $translator->trans(
  365.                     "Une erreur est survenue, campagne introuvable",
  366.                     array(), 'coss'
  367.                 );
  368.                 $this->addFlash("error"$msg);
  369.                 return $this->render('CossBundle/error.html.twig');
  370.             }
  371.         } else {
  372.             // Rediriger vers le login
  373.             return $this->redirectToRoute('participation_auth_login');
  374.         }
  375.     }
  376. }