diff --git a/src/Tech/Controller/ActivationController.php b/src/Tech/Controller/ActivationController.php index f467829..c2dcc3b 100644 --- a/src/Tech/Controller/ActivationController.php +++ b/src/Tech/Controller/ActivationController.php @@ -31,7 +31,7 @@ class ActivationController extends AbstractController try { $emailVerifier->handleEmailConfirmation($request, $user); } catch (VerifyEmailExceptionInterface $exception) { - $this->addFlash('error', $exception->getReason()); + $this->addFlash('error', $exception->getReason() . ' If the link has expired, you can request a new one here.'); return $this->redirectToRoute('app_register'); } diff --git a/src/Tech/Controller/RegistrationController.php b/src/Tech/Controller/RegistrationController.php index 7cb2d20..55d29f6 100644 --- a/src/Tech/Controller/RegistrationController.php +++ b/src/Tech/Controller/RegistrationController.php @@ -4,6 +4,8 @@ namespace App\Tech\Controller; use App\Tech\Entity\User; use App\Tech\Form\RegistrationFormType; +use App\Tech\Form\ResendVerificationEmailFormType; +use App\Tech\Repository\UserRepository; use App\Tech\Service\EmailVerifier; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bridge\Twig\Mime\TemplatedEmail; @@ -54,4 +56,41 @@ class RegistrationController extends AbstractController 'registrationForm' => $form->createView(), ]); } + + #[Route('/verify/resend', name: 'app_verify_resend_email')] + public function resendVerificationEmail(Request $request, UserRepository $userRepository, EmailVerifier $emailVerifier): Response + { + $form = $this->createForm(ResendVerificationEmailFormType::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $email = $form->get('email')->getData(); + $user = $userRepository->findOneBy(['email' => $email]); + + if ($user) { + if (!$user->isVerified()) { + $emailVerifier->sendEmailConfirmation('app_verify_email', $user, + (new TemplatedEmail()) + ->from($this->getParameter('mailer_from')) + ->to($user->getEmail()) + ->subject('Please Confirm your Email') + ->htmlTemplate('tech/registration/confirmation_email.html.twig') + ); + } else { + $this->addFlash('info', 'This email address is already verified.'); + return $this->redirectToRoute('app_login'); + } + } + + // We show the same success message regardless of whether the user exists or not, + // to avoid revealing whether an email is registered. + $this->addFlash('success', 'If an account exists with this email, a new confirmation link has been sent.'); + + return $this->redirectToRoute('website_home'); + } + + return $this->render('tech/registration/resend_verification_email.html.twig', [ + 'resendForm' => $form->createView(), + ]); + } } diff --git a/src/Tech/Form/ResendVerificationEmailFormType.php b/src/Tech/Form/ResendVerificationEmailFormType.php new file mode 100644 index 0000000..e1d5d17 --- /dev/null +++ b/src/Tech/Form/ResendVerificationEmailFormType.php @@ -0,0 +1,34 @@ +add('email', EmailType::class, [ + 'constraints' => [ + new NotBlank([ + 'message' => 'Please enter your email', + ]), + new Email([ + 'message' => 'Please enter a valid email address', + ]), + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([]); + } +} diff --git a/src/Tech/Service/UserChecker.php b/src/Tech/Service/UserChecker.php index 11a621e..66d6558 100644 --- a/src/Tech/Service/UserChecker.php +++ b/src/Tech/Service/UserChecker.php @@ -3,6 +3,7 @@ namespace App\Tech\Service; use App\Tech\Entity\User; +use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -16,7 +17,7 @@ class UserChecker implements UserCheckerInterface } if (!$user->isVerified()) { - throw new CustomUserMessageAuthenticationException('Your email address is not verified.'); + throw new CustomUserMessageAuthenticationException('Your email address is not verified.', ['%resend_link%' => '/verify/resend']); } } diff --git a/templates/tech/registration/confirmation_email.html.twig b/templates/tech/registration/confirmation_email.html.twig index bc307f2..b256fa1 100644 --- a/templates/tech/registration/confirmation_email.html.twig +++ b/templates/tech/registration/confirmation_email.html.twig @@ -4,6 +4,7 @@ Please confirm your email address by clicking the following link:

Confirm my Email. This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}. + If the link has expired or doesn't work, you can request a new one.

diff --git a/templates/tech/registration/resend_verification_email.html.twig b/templates/tech/registration/resend_verification_email.html.twig new file mode 100644 index 0000000..556e1f2 --- /dev/null +++ b/templates/tech/registration/resend_verification_email.html.twig @@ -0,0 +1,17 @@ +{% extends 'base.html.twig' %} + +{% block title %}Resend verification email{% endblock %} + +{% block body %} +

Resend verification email

+ +

Enter your email address and we will send you a new link to verify your account.

+ + {{ form_errors(resendForm) }} + + {{ form_start(resendForm) }} + {{ form_row(resendForm.email) }} + + + {{ form_end(resendForm) }} +{% endblock %} diff --git a/templates/tech/security/login.html.twig b/templates/tech/security/login.html.twig index 8d4fb16..2e0b1a6 100644 --- a/templates/tech/security/login.html.twig +++ b/templates/tech/security/login.html.twig @@ -5,7 +5,12 @@ {% block body %}
{% if error %} -
{{ error.messageKey|trans(error.messageData, 'security') }}
+
+ {{ error.messageKey|trans(error.messageData, 'security')|raw }} + {% if error.messageData['%resend_link%'] is defined %} + Resend activation link + {% endif %} +
{% endif %} {% if app.user %} @@ -41,5 +46,8 @@
Forgot your password?
+
+ Didn't receive activation email? +
{% endblock %}