1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys UserBundle package.
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\UserBundle\Controller
;
14 use Doctrine\DBAL\Exception\UniqueConstraintViolationException
;
15 use Symfony\Bridge\Twig\Mime\TemplatedEmail
;
16 use Symfony\Component\Form\FormError
;
17 use Symfony\Component\HttpFoundation\Request
;
18 use Symfony\Component\HttpFoundation\Response
;
19 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException
;
20 use Symfony\Component\Mailer\Exception\TransportExceptionInterface
;
21 use Symfony\Component\Mime\Address
;
22 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
;
23 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils
;
28 class DefaultController
extends AbstractController
{
30 * Confirm account from mail link
32 * @param Request $request The request
33 * @param string $hash The hashed password
34 * @param string $mail The shorted mail address
35 * @return Response The response
37 public function confirm(Request
$request, string $hash, string $mail): Response
{
39 if ($hash != $this->slugger
->hash($mail)) {
41 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
45 $mail = $this->slugger
->unshort($smail = $mail);
48 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
50 //XXX: prevent slugger reverse engineering by not displaying decoded mail
51 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
54 //Without existing registrant
55 if (!($user = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
56 //Add error message mail already exists
57 //XXX: prevent slugger reverse engineering by not displaying decoded mail
58 $this->addFlash('error', $this->translator
->trans('Account %mail% do not exists', ['%mail%' => $smail]));
60 //Redirect to register view
61 return $this->redirectToRoute($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield = $this->slugger
->serialize([]), 'hash' => $this->slugger
->hash($smail.$sfield)]+
$this->config
['route']['register']['context']);
65 $user->setActive(true);
68 $this->manager
->persist($user);
71 $this->manager
->flush();
73 //Add error message mail already exists
74 $this->addFlash('notice', $this->translator
->trans('Your account has been activated'));
76 //Redirect to user view
77 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']);
81 * Edit account by shorted mail
83 * @param Request $request The request
84 * @param string $hash The hashed password
85 * @param string $mail The shorted mail address
86 * @return Response The response
88 public function edit(Request
$request, string $hash, string $mail): Response
{
90 if ($hash != $this->slugger
->hash($mail)) {
92 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
96 $mail = $this->slugger
->unshort($smail = $mail);
98 //With existing subscriber
99 if (empty($user = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
101 //XXX: prevent slugger reverse engineering by not displaying decoded mail
102 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
105 //Prevent access when not admin, user is not guest and not currently logged user
106 if (!$this->isGranted('ROLE_ADMIN') && $user != $this->getUser() || !$this->isGranted('IS_AUTHENTICATED_FULLY')) {
107 //Throw access denied
108 //XXX: prevent slugger reverse engineering by not displaying decoded mail
109 throw $this->createAccessDeniedException($this->translator
->trans('Unable to access user: %mail%', ['%mail%' => $smail]));
112 //Create the RegisterType form and give the proper parameters
113 $edit = $this->createForm($this->config
['edit']['view']['edit'], $user, [
114 //Set action to register route name and context
115 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']),
117 'civility_class' => $this->config
['class']['civility'],
118 //Set civility default
119 'civility_default' => $this->doctrine
->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
121 'mail' => $this->isGranted('ROLE_ADMIN'),
126 ]+
$this->config
['edit']['field']);
129 if ($this->isGranted('ROLE_ADMIN')) {
130 //Create the LoginType form and give the proper parameters
131 $reset = $this->createForm($this->config
['edit']['view']['reset'], $user, [
132 //Set action to register route name and context
133 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']),
141 if ($request->isMethod('POST')) {
142 //Refill the fields in case the form is not valid.
143 $reset->handleRequest($request);
145 //With reset submitted and valid
146 if ($reset->isSubmitted() && $reset->isValid()) {
148 $data = $reset->getData();
151 $data->setPassword($this->hasher
->hashPassword($data, $data->getPassword()));
154 $this->manager
->persist($data);
156 //Flush to get the ids
157 $this->manager
->flush();
160 $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail = $data->getMail()]));
162 //Redirect to cleanup the form
163 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $this->slugger
->short($mail), 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']);
168 $this->config
['edit']['view']['context']['reset'] = $reset->createView();
172 if ($request->isMethod('POST')) {
173 //Refill the fields in case the form is not valid.
174 $edit->handleRequest($request);
176 //With edit submitted and valid
177 if ($edit->isSubmitted() && $edit->isValid()) {
179 $data = $edit->getData();
182 $this->manager
->persist($data);
184 //Try saving in database
186 //Flush to get the ids
187 $this->manager
->flush();
190 $this->addFlash('notice', $this->translator
->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()]));
192 //Redirect to cleanup the form
193 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $this->slugger
->short($mail), 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['edit']['context']);
194 //Catch double slug or mail
195 } catch (UniqueConstraintViolationException
$e) {
196 //Add error message mail already exists
197 $this->addFlash('error', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $data->getMail()]));
201 //XXX: prefer a reset on login to force user unspam action
202 } elseif (!$this->isGranted('ROLE_ADMIN')) {
204 $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure'));
208 return $this->render(
210 $this->config
['edit']['view']['name'],
212 ['edit' => $edit->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['edit']['view']['context']
219 * @param Request $request The request
220 * @param AuthenticationUtils $authenticationUtils The authentication utils
221 * @param ?string $hash The hashed password
222 * @param ?string $mail The shorted mail address
223 * @return Response The response
225 public function login(Request
$request, AuthenticationUtils
$authenticationUtils, ?string $hash, ?string $mail): Response
{
226 //Create the LoginType form and give the proper parameters
227 $login = $this->createForm($this->config
['login']['view']['form'], null, [
228 //Set action to login route name and context
229 'action' => $this->generateUrl($this->config
['route']['login']['name'], $this->config
['route']['login']['context']),
230 //Disable repeated password
231 'password_repeated' => false,
240 if (!empty($mail) && !empty($hash)) {
242 if ($hash != $this->slugger
->hash($mail)) {
244 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
248 $mail = $this->slugger
->unshort($smail = $mail);
251 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
253 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
257 $login->get('mail')->setData($mail);
258 //Last username entered by the user
259 } elseif ($lastUsername = $authenticationUtils->getLastUsername()) {
260 $login->get('mail')->setData($lastUsername);
263 //Get the login error if there is one
264 if ($error = $authenticationUtils->getLastAuthenticationError()) {
265 //Get translated error
266 $error = $this->translator
->trans($error->getMessageKey());
268 //Add error message to mail field
269 $login->get('mail')->addError(new FormError($error));
271 //Create the LoginType form and give the proper parameters
272 $recover = $this->createForm($this->config
['recover']['view']['form'], null, [
273 //Set action to recover route name and context
274 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
281 //Get recover mail entity
282 $recover->get('mail')
283 //Set mail from login form
284 ->setData($login->get('mail')->getData())
286 ->addError(new FormError($this->translator
->trans('Use this form to recover your account')));
288 //Add recover form to context
289 $context['recover'] = $recover->createView();
292 $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure'));
296 return $this->render(
298 $this->config
['login']['view']['name'],
300 ['login' => $login->createView()]+
$context+
$this->config
['login']['view']['context']
307 * @param Request $request The request
308 * @param ?string $hash The hashed password
309 * @param ?string $pass The shorted password
310 * @param ?string $mail The shorted mail address
311 * @return Response The response
313 public function recover(Request
$request, ?string $hash, ?string $pass, ?string $mail): Response
{
314 //Without mail, pass and hash
315 if (empty($mail) && empty($pass) && empty($hash)) {
316 //Create the LoginType form and give the proper parameters
317 $form = $this->createForm($this->config
['recover']['view']['form'], null, [
318 //Set action to recover route name and context
319 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
327 if ($request->isMethod('POST')) {
328 //Refill the fields in case the form is not valid.
329 $form->handleRequest($request);
331 //With form submitted and valid
332 if ($form->isSubmitted() && $form->isValid()) {
334 $data = $form->getData();
336 //Find user by data mail
337 if ($user = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($data['mail'])) {
339 $recoverMail =& $this->config
['recover']['mail'];
342 $mail = $this->slugger
->short($user->getMail());
345 $pass = $this->slugger
->hash($user->getPassword());
347 //Generate each route route
348 foreach($this->config
['recover']['route'] as $route => $tag) {
349 //Only process defined routes
350 if (!empty($this->config
['route'][$route])) {
351 //Process for recover mail url
352 if ($route == 'recover') {
353 //Set the url in context
354 $recoverMail['context'][$tag] = $this->router
->generate(
355 $this->config
['route'][$route]['name'],
356 //Prepend recover context with tag
360 'hash' => $this->slugger
->hash($mail.$pass)
361 ]+
$this->config
['route'][$route]['context'],
362 UrlGeneratorInterface
::ABSOLUTE_URL
369 $recoverMail['context']['recipient_mail'] = $user->getMail();
372 $recoverMail['context']['recipient_name'] = $user->getRecipientName();
374 //Init subject context
375 $subjectContext = $this->slugger
->flatten(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context']), null, '.', '%', '%');
378 $recoverMail['subject'] = ucfirst($this->translator
->trans($recoverMail['subject'], $subjectContext));
381 $message = (new TemplatedEmail())
383 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
385 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
386 ->to(new Address($recoverMail['context']['recipient_mail'], $recoverMail['context']['recipient_name']))
388 ->subject($recoverMail['subject'])
390 //Set path to twig templates
391 ->htmlTemplate($recoverMail['html'])
392 ->textTemplate($recoverMail['text'])
395 //XXX: require recursive merge to avoid loosing subkeys
396 //['subject' => $recoverMail['subject']]+$recoverMail['context']+$this->config['recover']['view']['context']
397 ->context(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context'], ['subject' => $recoverMail['subject']]));
399 //Try sending message
400 //XXX: mail delivery may silently fail
403 $this->mailer
->send($message);
405 //Redirect on the same route with sent=1 to cleanup form
406 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
407 //Catch obvious transport exception
408 } catch(TransportExceptionInterface
$e) {
409 //Add error message mail unreachable
410 $form->get('mail')->addError(new FormError($this->translator
->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
414 //Add error message to mail field
415 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $data['mail']])));
421 return $this->render(
423 $this->config
['recover']['view']['name'],
425 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
430 if ($hash != $this->slugger
->hash($mail.$pass)) {
432 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
436 $mail = $this->slugger
->unshort($smail = $mail);
439 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
441 //XXX: prevent slugger reverse engineering by not displaying decoded mail
442 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
445 //With existing subscriber
446 if (empty($user = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
448 //XXX: prevent slugger reverse engineering by not displaying decoded mail
449 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
452 //With unmatched pass
453 if ($pass != $this->slugger
->hash($user->getPassword())) {
455 //XXX: prevent use of outdated recover link
456 throw $this->createNotFoundException($this->translator
->trans('Outdated recover link'));
459 //Create the LoginType form and give the proper parameters
460 $form = $this->createForm($this->config
['recover']['view']['form'], $user, [
461 //Set action to recover route name and context
462 'action' => $this->generateUrl($this->config
['route']['recover']['name'], ['mail' => $smail, 'pass' => $pass, 'hash' => $hash]+
$this->config
['route']['recover']['context']),
470 if ($request->isMethod('POST')) {
471 //Refill the fields in case the form is not valid.
472 $form->handleRequest($request);
474 //With form submitted and valid
475 if ($form->isSubmitted() && $form->isValid()) {
477 $data = $form->getData();
479 //Set hashed password
480 $hashed = $this->hasher
->hashPassword($user, $user->getPassword());
483 $pass = $this->slugger
->hash($hashed);
486 $user->setPassword($hashed);
489 $this->manager
->persist($user);
492 $this->manager
->flush();
495 $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail]));
497 //Redirect to user login
498 return $this->redirectToRoute($this->config
['route']['login']['name'], ['mail' => $smail, 'hash' => $this->slugger
->hash($smail)]+
$this->config
['route']['login']['context']);
503 return $this->render(
505 $this->config
['recover']['view']['name'],
507 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
512 * Register an account
514 * @param Request $request The request
515 * @param ?string $hash The hashed serialized field array
516 * @param ?string $field The serialized then shorted form field array
517 * @param ?string $mail The shorted mail address
518 * @return Response The response
520 public function register(Request
$request, ?string $hash, ?string $field, ?string $mail): Response
{
522 if (!empty($_POST['register']['mail'])) {
524 $this->logger
->emergency(
525 $this->translator
->trans(
526 'register: mail=%mail% locale=%locale% confirm=%confirm%',
528 '%mail%' => $postMail = $_POST['register']['mail'],
529 '%locale%' => $request->getLocale(),
530 '%confirm%' => $this->router
->generate(
531 $this->config
['route']['confirm']['name'],
532 //Prepend subscribe context with tag
534 'mail' => $postSmail = $this->slugger
->short($postMail),
535 'hash' => $this->slugger
->hash($postSmail)
536 ]+
$this->config
['route']['confirm']['context'],
537 UrlGeneratorInterface
::ABSOLUTE_URL
544 //With mail and field
545 if (!empty($field) && !empty($hash)) {
547 if ($hash != $this->slugger
->hash($mail.$field)) {
549 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
555 $mail = $this->slugger
->unshort($smail = $mail);
558 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
560 //XXX: prevent slugger reverse engineering by not displaying decoded mail
561 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
564 //With existing registrant
565 if ($existing = $this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail)) {
566 //With disabled existing
567 if ($existing->isDisabled()) {
569 $response = $this->render(
571 $this->config
['register']['view']['name'],
573 ['title' => $this->translator
->trans('Access denied'), 'disabled' => 1]+
$this->config
['register']['view']['context']
577 $response->setStatusCode(403);
581 //With unactivated existing
582 } elseif (!$existing->isActivated()) {
584 $activateMail =& $this->config
['register']['mail'];
586 //Generate each route route
587 foreach($this->config
['register']['route'] as $route => $tag) {
588 //Only process defined routes
589 if (!empty($this->config
['route'][$route])) {
590 //Process for confirm url
591 if ($route == 'confirm') {
592 //Set the url in context
593 $activateMail['context'][$tag] = $this->router
->generate(
594 $this->config
['route'][$route]['name'],
595 //Prepend subscribe context with tag
597 'mail' => $smail = $this->slugger
->short($existing->getMail()),
598 'hash' => $this->slugger
->hash($smail)
599 ]+
$this->config
['route'][$route]['context'],
600 UrlGeneratorInterface
::ABSOLUTE_URL
607 $activateMail['context']['recipient_mail'] = $existing->getMail();
610 $activateMail['context']['recipient_name'] = $existing->getRecipientName();
612 //Init subject context
613 $subjectContext = $this->slugger
->flatten(array_replace_recursive($this->config
['register']['view']['context'], $activateMail['context']), null, '.', '%', '%');
616 $activateMail['subject'] = ucfirst($this->translator
->trans($activateMail['subject'], $subjectContext));
619 $message = (new TemplatedEmail())
621 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
623 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
624 ->to(new Address($activateMail['context']['recipient_mail'], $activateMail['context']['recipient_name']))
626 ->subject($activateMail['subject'])
628 //Set path to twig templates
629 ->htmlTemplate($activateMail['html'])
630 ->textTemplate($activateMail['text'])
633 ->context(['subject' => $activateMail['subject']]+
$activateMail['context']);
635 //Try sending message
636 //XXX: mail delivery may silently fail
639 $this->mailer
->send($message);
640 //Catch obvious transport exception
641 } catch(TransportExceptionInterface
$e) {
642 //Add error message mail unreachable
643 $this->addFlash('error', $this->translator
->trans('Account %mail% tried activate but unable to contact', ['%mail%' => $existing->getMail()]));
647 $routeParams = $request->get('_route_params');
649 //Remove mail, field and hash from route params
650 unset($routeParams['mail'], $routeParams['field'], $routeParams['hash']);
652 //Redirect on the same route with sent=1 to cleanup form
653 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$routeParams);
656 //Add error message mail already exists
657 $this->addFlash('warning', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $existing->getMail()]));
659 //Redirect to user view
660 return $this->redirectToRoute(
661 $this->config
['route']['edit']['name'],
663 'mail' => $smail = $this->slugger
->short($existing->getMail()),
664 'hash' => $this->slugger
->hash($smail)
665 ]+
$this->config
['route']['edit']['context']
676 //Unshort then unserialize field
677 $field = $this->slugger
->unserialize($sfield = $field);
679 } catch (\Error
|\Exception
$e) {
681 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]), $e);
684 //With non array field
685 if (!is_array($field)) {
687 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]));
689 //Without field and hash
702 $reflection = new \
ReflectionClass($this->config
['class']['user']);
705 $user = $reflection->newInstance(strval($mail));
707 //Create the RegisterType form and give the proper parameters
708 $form = $this->createForm($this->config
['register']['view']['form'], $user, $field+
[
709 //Set action to register route name and context
710 'action' => $this->generateUrl($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield, 'hash' => $hash]+
$this->config
['route']['register']['context']),
712 'civility_class' => $this->config
['class']['civility'],
713 //Set civility default
714 'civility_default' => $this->doctrine
->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
719 ]+
$this->config
['register']['field']);
722 if ($request->isMethod('POST')) {
723 //Refill the fields in case the form is not valid.
724 $form->handleRequest($request);
726 //With form submitted and valid
727 if ($form->isSubmitted() && $form->isValid()) {
729 $data = $form->getData();
731 //With existing registrant
732 if ($this->doctrine
->getRepository($this->config
['class']['user'])->findOneByMail($mail = $data->getMail())) {
733 //Add error message mail already exists
734 $this->addFlash('warning', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
736 //Redirect to user view
737 return $this->redirectToRoute(
738 $this->config
['route']['edit']['name'],
740 'mail' => $smail = $this->slugger
->short($mail),
741 'hash' => $this->slugger
->hash($smail)
742 ]+
$this->config
['route']['edit']['context']
747 $registerMail =& $this->config
['register']['mail'];
750 $user->setPassword($this->hasher
->hashPassword($user, $user->getPassword()));
753 $this->manager
->persist($user);
755 //Iterate on default group
756 foreach($this->config
['default']['group'] as $i => $groupTitle) {
758 if (($group = $this->doctrine
->getRepository($this->config
['class']['group'])->findOneByTitle($groupTitle))) {
760 //XXX: see vendor/symfony/security-core/Role/Role.php
761 $user->addGroup($group);
765 //XXX: consider missing group as fatal
766 throw new \
Exception(sprintf('Group from rapsys_user.default.group[%d] not found by title: %s', $i, $groupTitle));
770 //Generate each route route
771 foreach($this->config
['register']['route'] as $route => $tag) {
772 //Only process defined routes
773 if (!empty($this->config
['route'][$route])) {
774 //Process for confirm url
775 if ($route == 'confirm') {
776 //Set the url in context
777 $registerMail['context'][$tag] = $this->router
->generate(
778 $this->config
['route'][$route]['name'],
779 //Prepend subscribe context with tag
781 'mail' => $smail = $this->slugger
->short($data->getMail()),
782 'hash' => $this->slugger
->hash($smail)
783 ]+
$this->config
['route'][$route]['context'],
784 UrlGeneratorInterface
::ABSOLUTE_URL
791 $registerMail['context']['recipient_mail'] = $data->getMail();
794 $registerMail['context']['recipient_name'] = $data->getRecipientName();
796 //Init subject context
797 $subjectContext = $this->slugger
->flatten(array_replace_recursive($this->config
['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
800 $registerMail['subject'] = ucfirst($this->translator
->trans($registerMail['subject'], $subjectContext));
803 $message = (new TemplatedEmail())
805 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
807 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
808 ->to(new Address($registerMail['context']['recipient_mail'], $registerMail['context']['recipient_name']))
810 ->subject($registerMail['subject'])
812 //Set path to twig templates
813 ->htmlTemplate($registerMail['html'])
814 ->textTemplate($registerMail['text'])
817 ->context(['subject' => $registerMail['subject']]+
$registerMail['context']);
819 //Try saving in database
822 $this->manager
->flush();
824 //Add error message mail already exists
825 $this->addFlash('notice', $this->translator
->trans('Your account has been created'));
827 //Try sending message
828 //XXX: mail delivery may silently fail
831 $this->mailer
->send($message);
833 //Redirect on the same route with sent=1 to cleanup form
834 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
835 //Catch obvious transport exception
836 } catch(TransportExceptionInterface
$e) {
837 //Add error message mail unreachable
838 $form->get('mail')->addError(new FormError($this->translator
->trans('Account %mail% tried subscribe but unable to contact', ['%mail%' => $data->getMail()])));
840 //Catch double subscription
841 } catch (UniqueConstraintViolationException
$e) {
842 //Add error message mail already exists
843 $this->addFlash('error', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
849 return $this->render(
851 $this->config
['register']['view']['name'],
853 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['register']['view']['context']