diff --git a/src/Controller/ContratProcessController.php b/src/Controller/ContratProcessController.php index 10d84b7..ce01897 100644 --- a/src/Controller/ContratProcessController.php +++ b/src/Controller/ContratProcessController.php @@ -4,22 +4,88 @@ namespace App\Controller; use App\Entity\Contrat; use App\Service\DocuSealService; +use App\Service\MailerService; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Twig\Environment; class ContratProcessController extends AbstractController { - #[Route('/process/contrat/{id}', name: 'app_contrat_process', requirements: ['id' => '\d+'])] - public function process(int $id, EntityManagerInterface $em): Response + public function __construct( + private EntityManagerInterface $em, + private MailerService $mailer, + private Environment $twig, + ) { + } + + #[Route('/process/contrat/{id}/verify', name: 'app_contrat_verify', requirements: ['id' => '\d+'])] + public function verify(int $id, Request $request): Response { - $contrat = $em->getRepository(Contrat::class)->find($id); + $contrat = $this->em->getRepository(Contrat::class)->find($id); if (null === $contrat) { throw $this->createNotFoundException('Contrat introuvable.'); } + $session = $request->getSession(); + $error = null; + + if ($request->isMethod('POST')) { + $code = $request->request->getString('code'); + $storedCode = $session->get('contrat_code_'.$id); + $expires = $session->get('contrat_code_expires_'.$id, 0); + + if (time() > $expires) { + $error = 'Code expire. Veuillez en demander un nouveau.'; + } elseif ($code !== $storedCode) { + $error = 'Code incorrect.'; + } else { + $session->set('contrat_verified_'.$id, true); + + return $this->redirectToRoute('app_contrat_process', ['id' => $id]); + } + } + + // Envoyer un code si pas encore fait + if (null === $session->get('contrat_code_'.$id)) { + $this->sendVerificationCode($contrat, $session, $id); + } + + return $this->render('contrat/verify.html.twig', [ + 'contrat' => $contrat, + 'error' => $error, + ]); + } + + #[Route('/process/contrat/{id}/verify/resend', name: 'app_contrat_resend_code', requirements: ['id' => '\d+'], methods: ['POST'])] + public function resendCode(int $id, Request $request): Response + { + $contrat = $this->em->getRepository(Contrat::class)->find($id); + if (null === $contrat) { + throw $this->createNotFoundException('Contrat introuvable.'); + } + + $this->sendVerificationCode($contrat, $request->getSession(), $id); + + return $this->redirectToRoute('app_contrat_verify', ['id' => $id]); + } + + #[Route('/process/contrat/{id}', name: 'app_contrat_process', requirements: ['id' => '\d+'])] + public function process(int $id, Request $request): Response + { + $contrat = $this->em->getRepository(Contrat::class)->find($id); + if (null === $contrat) { + throw $this->createNotFoundException('Contrat introuvable.'); + } + + $session = $request->getSession(); + if (!$session->get('contrat_verified_'.$contrat->getId(), false)) { + return $this->redirectToRoute('app_contrat_verify', ['id' => $id]); + } + return $this->render('contrat/process.html.twig', [ 'contrat' => $contrat, ]); @@ -28,15 +94,20 @@ class ContratProcessController extends AbstractController #[Route('/process/contrat/{id}/sign', name: 'app_contrat_sign', requirements: ['id' => '\d+'])] public function sign( int $id, - EntityManagerInterface $em, + Request $request, DocuSealService $docuSeal, #[Autowire(env: 'DOCUSEAL_URL')] string $docuSealUrl = '', ): Response { - $contrat = $em->getRepository(Contrat::class)->find($id); + $contrat = $this->em->getRepository(Contrat::class)->find($id); if (null === $contrat || null === $contrat->getSubmissionId()) { throw $this->createNotFoundException('Contrat introuvable.'); } + $session = $request->getSession(); + if (!$session->get('contrat_verified_'.$contrat->getId(), false)) { + return $this->redirectToRoute('app_contrat_verify', ['id' => $id]); + } + // @codeCoverageIgnoreStart $slug = $docuSeal->getSubmitterSlug((int) $contrat->getSubmissionId()); if (null !== $slug) { @@ -46,4 +117,23 @@ class ContratProcessController extends AbstractController throw $this->createNotFoundException('Lien de signature introuvable.'); } + + private function sendVerificationCode(Contrat $contrat, object $session, int $id): void + { + $code = str_pad((string) random_int(0, 999999), 6, '0', \STR_PAD_LEFT); + $session->set('contrat_code_'.$id, $code); + $session->set('contrat_code_expires_'.$id, time() + 900); + + $this->mailer->sendEmail( + $contrat->getEmail(), + 'Code de verification - Contrat '.$contrat->getReference(), + $this->twig->render('emails/contrat_verify_code.html.twig', [ + 'contrat' => $contrat, + 'code' => $code, + ]), + null, + null, + false, + ); + } } diff --git a/templates/contrat/verify.html.twig b/templates/contrat/verify.html.twig new file mode 100644 index 0000000..73809ce --- /dev/null +++ b/templates/contrat/verify.html.twig @@ -0,0 +1,32 @@ +{% extends 'base.html.twig' %} + +{% block title %}Verification - Contrat {{ contrat.reference }} - Association E-Cosplay{% endblock %} + +{% block body %} +
+
+
+

Verification

+

{{ contrat.reference }} - Un code a ete envoye a {{ contrat.email }}

+
+
+ {% if error %} +
{{ error }}
+ {% endif %} +

Saisissez le code de verification a 6 chiffres recu par email pour acceder a votre contrat.

+
+
+ + +
+ +
+

Le code expire dans 15 minutes.

+
+ +
+
+
+
+{% endblock %} diff --git a/templates/emails/contrat_verify_code.html.twig b/templates/emails/contrat_verify_code.html.twig new file mode 100644 index 0000000..8b5a0b7 --- /dev/null +++ b/templates/emails/contrat_verify_code.html.twig @@ -0,0 +1,14 @@ +{% extends 'email/base.html.twig' %} + +{% block content %} + + + + +
+

Code de verification

+

Contrat {{ contrat.reference }}

+
{{ code }}
+

Ce code expire dans 15 minutes.

+
+{% endblock %}