<?php
namespace Roothirsch\CoreBundle\Security\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Roothirsch\CoreBundle\Entity\Address;
use Roothirsch\CoreBundle\Entity\ContactValidation;
use Roothirsch\CoreBundle\Entity\User;
use Roothirsch\CoreBundle\Messaging\MessagingService;
use Roothirsch\CoreBundle\Repository\GroupRepository;
use Roothirsch\CoreBundle\Repository\UserRepository;
use Roothirsch\CoreBundle\Security\Event\RegistrationCompletedEvent;
use Roothirsch\CoreBundle\Security\Event\RegistrationTokenCreatedEvent;
use Roothirsch\CoreBundle\Security\Form\RegistrationUserType;
use Roothirsch\CoreBundle\Security\Form\RegistrationRequestFormType;
use Roothirsch\CoreBundle\Security\UserManager;
use Roothirsch\CoreBundle\Site\Repository\SiteRepository;
use Roothirsch\CoreBundle\Site\SiteProvider;
use Roothirsch\CoreBundle\Translation\Services\CoreTranslator;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
/**
* @Route("/registration", name="app_registration_")
*/
class RegistrationController extends AbstractController
{
/** @var UserRepository */
private $userRepository;
/** @var SiteProvider */
private $siteProvider;
/** @var MailerInterface */
private $mailer;
/** @var EntityManagerInterface */
private $entityManager;
/** @var UserPasswordHasherInterface */
private $passwordHasher;
/** @var EventDispatcherInterface */
private $dispatcher;
/** @var GroupRepository */
private $groupRepository;
/** @var MessagingService */
private $messaging;
/** @var CoreTranslator */
private $translationService;
/**
* @param UserRepository $userRepository
* @param SiteProvider $sideProvider
* @param MailerInterface $mailer
* @param EntityManagerInterface $entityManager
* @param UserPasswordHasherInterface $passwordHasher
* @param EventDispatcherInterface $dispatcher
*/
public function __construct(UserRepository $userRepository, SiteProvider $sideProvider, MailerInterface $mailer, EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher, EventDispatcherInterface $dispatcher, GroupRepository $groupRepository, MessagingService $messaging, CoreTranslator $translator)
{
$this->userRepository = $userRepository;
$this->siteProvider = $sideProvider;
$this->mailer = $mailer;
$this->entityManager = $entityManager;
$this->passwordHasher = $passwordHasher;
$this->dispatcher = $dispatcher;
$this->groupRepository = $groupRepository;
$this->messaging = $messaging;
$this->translationService = $translator;
}
/**
* @Route("", name="request")
* @param Request $request
* @return Response
*/
public function request(Request $request): Response
{
$form = $this->createForm(RegistrationRequestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->processRegistrationData($form->get('email')->getData(), $request->getLocale());
}
return $this->render(
'security/registration/request.html.twig', [
'requestForm' => $form->createView()
]
);
}
/**
* Confirmation page after a user has requested a password reset.
*
* @Route("/check-email", name="check_email")
*/
public function checkEmail(): Response
{
// Generate a fake token if the user does not exist or someone hit this page directly.
// This prevents exposing whether or not a user was found with the given email address or not
// if (null === ($resetToken = $this->getTokenObjectFromSession())) {
// $resetToken = $this->resetPasswordHelper->generateFakeResetToken();
// }
return $this->render(
'security/registration/check_email.html.twig', [
]
);
}
/**
* Confirmation page after a user has requested a password reset.
*
* @Route("/completed", name="completed")
*/
public function completed(): Response
{
return $this->render('security/registration/completed.html.twig');
}
/**
* Confirmation page after a user has requested a password reset.
*
* @Route("/confirm/{token?}", name="confirm")
*/
public function confirm(string $token=null, Request $request): Response
{
if($token){
$this->getSessionService()->set("token", $token);
return $this->redirectToRoute('app_registration_confirm');
}
$token = $this->getSessionService()->get("token");
if(!$token){
throw $this->createNotFoundException('Beim Aufruf wurde kein Schlüssel übergeben.');
}
$user = $this->userRepository->findUserByRegistrationToken($token);
if(!$user){
$this->addFlash('error', 'Beim Versuch Ihre Registrierung abzuschließen ist ein Problem aufgetreten');
return $this->redirectToRoute('app_registration_request');
}
if(!$user->getContactValidation()){
$contactValidation = new ContactValidation();
$contactValidation->setAddress(new Address());
$contactValidation->setUser($user);
$user->setContactValidation($contactValidation);
}
$form = $this->createForm(RegistrationUserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->addGroup($this->groupRepository->getOrCreateRole('ROLE_USER'));
$user->setPassword($this->passwordHasher->hashPassword($user, $form->get('password')->getData()));
return $this->sendRegistrationNotification($user);
}
return $this->render(
'security/registration/confirm.html.twig', [
'form' => $form->createView(),
]
);
}
private function processRegistrationData(string $emailFormData, $locale = 'de'): RedirectResponse
{
$user = $this->userRepository->findUserByEmail($emailFormData);
// Do not reveal whether a user account was found or not.
if ($user && $user->getActive()) {
return $this->redirectToRoute('app_registration_check_email');
}
if ($user instanceof User) {
$token = $user->getRegistrationToken();
if (empty($token)) {
$token = bin2hex(random_bytes(24));
$user->setRegistrationToken($token);
$this->entityManager->persist($user);
$this->entityManager->flush();
}
} else {
$token = bin2hex(random_bytes(24));
$user = new User();
$user->setUsername($emailFormData);
$user->setEmail($emailFormData);
$user->setActive(false);
$user->setAdmin(false);
$user->setRegistrationToken($token);
$this->entityManager->persist($user);
$this->entityManager->flush();
}
$url = $this->generateUrl('app_registration_confirm', [ 'token' => $user->getRegistrationToken(), 'locale' => $locale ], UrlGeneratorInterface::ABSOLUTE_URL);
$email = $this->messaging->createTranslatedEmail('security/registration/email.html.twig', ['url' => $url], $locale);
$email
->to($user->getEmail())
->subject(
$this->translationService->translate('email.registration.user_email.subject', 'Ihre Registrierung bei {siteTitle}', ['siteTitle' => $this->siteProvider->getTitle()])
);
$event = new RegistrationTokenCreatedEvent($user, $this->currentRequest());
$event->addEmail('user_token', $email);
$this->dispatcher->dispatch($event, RegistrationTokenCreatedEvent::EVENT);
if(!$event->isPropagationStopped()){
foreach ($event->getEmails() as $email){
$this->mailer->send($email);
}
}
// Store the token object in session for retrieval in check-email route.
// $this->setTokenObjectInSession($resetToken);
return $this->redirectToRoute('app_registration_check_email');
}
public function sendRegistrationNotification($user)
{
$user->setRegistrationToken(null);
$user->setActive(true);
// Dispatching an event so E-Mails can be properly send.
$event = new RegistrationCompletedEvent($user, $this->currentRequest());
$this->dispatcher->dispatch($event, RegistrationCompletedEvent::EVENT);
if(!$event->isPropagationStopped()){
foreach ($event->getEmails() as $email){
$this->mailer->send($email);
}
}
$this->entityManager->persist($user);
$this->entityManager->flush();
$this->getSessionService()->remove("token");
return $this->redirectToRoute('app_registration_completed');
}
private function getSessionService(): SessionInterface
{
return $this->currentRequest()->getSession();
}
private function currentRequest(): Request{
return $this->container->get('request_stack')->getCurrentRequest();
}
}