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\Bundle\DoctrineBundle\Registry
;
15 use Doctrine\ORM\EntityManagerInterface
;
16 use Doctrine\DBAL\Exception\UniqueConstraintViolationException
;
17 use Psr\Log\LoggerInterface
;
18 use Symfony\Bridge\Twig\Mime\TemplatedEmail
;
19 use Symfony\Component\Form\FormError
;
20 use Symfony\Component\HttpFoundation\Request
;
21 use Symfony\Component\HttpFoundation\Response
;
22 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException
;
23 use Symfony\Component\Mailer\Exception\TransportExceptionInterface
;
24 use Symfony\Component\Mailer\MailerInterface
;
25 use Symfony\Component\Mime\Address
;
26 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
;
27 use Symfony\Component\Routing\RouterInterface
;
28 use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface
;
29 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils
;
31 use Rapsys\PackBundle\Util\SluggerUtil
;
36 class DefaultController
extends AbstractController
{
38 * Confirm account from mail link
40 * @param Request $request The request
41 * @param Registry $manager The doctrine registry
42 * @param UserPasswordEncoderInterface $encoder The password encoder
43 * @param EntityManagerInterface $manager The doctrine entity manager
44 * @param SluggerUtil $slugger The slugger
45 * @param MailerInterface $mailer The mailer
46 * @param string $mail The shorted mail address
47 * @param string $hash The hashed password
48 * @return Response The response
50 public function confirm(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, $mail, $hash): Response
{
52 if ($hash != $slugger->hash($mail)) {
54 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
58 $mail = $slugger->unshort($smail = $mail);
61 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
63 //XXX: prevent slugger reverse engineering by not displaying decoded mail
64 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
67 //Without existing registrant
68 if (!($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
69 //Add error message mail already exists
70 //XXX: prevent slugger reverse engineering by not displaying decoded mail
71 $this->addFlash('error', $this->translator
->trans('Account %mail% do not exists', ['%mail%' => $smail]));
73 //Redirect to register view
74 return $this->redirectToRoute($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield = $slugger->serialize([]), 'hash' => $slugger->hash($smail.$sfield)]+
$this->config
['route']['register']['context']);
78 $user->setActive(true);
81 $manager->persist($user);
86 //Add error message mail already exists
87 $this->addFlash('notice', $this->translator
->trans('Your account has been activated'));
89 //Redirect to user view
90 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']);
94 * Edit account by shorted mail
96 * @param Request $request The request
97 * @param Registry $manager The doctrine registry
98 * @param UserPasswordEncoderInterface $encoder The password encoder
99 * @param EntityManagerInterface $manager The doctrine entity manager
100 * @param SluggerUtil $slugger The slugger
101 * @param string $mail The shorted mail address
102 * @param string $hash The hashed password
103 * @return Response The response
105 public function edit(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, $mail, $hash): Response
{
107 if ($hash != $slugger->hash($mail)) {
109 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
113 $mail = $slugger->unshort($smail = $mail);
115 //With existing subscriber
116 if (empty($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
118 //XXX: prevent slugger reverse engineering by not displaying decoded mail
119 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
122 //Prevent access when not admin, user is not guest and not currently logged user
123 if (!$this->isGranted('ROLE_ADMIN') && $user != $this->getUser() || !$this->isGranted('IS_AUTHENTICATED_FULLY')) {
124 //Throw access denied
125 //XXX: prevent slugger reverse engineering by not displaying decoded mail
126 throw $this->createAccessDeniedException($this->translator
->trans('Unable to access user: %mail%', ['%mail%' => $smail]));
129 //Create the RegisterType form and give the proper parameters
130 $edit = $this->createForm($this->config
['edit']['view']['edit'], $user, [
131 //Set action to register route name and context
132 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']),
134 'civility_class' => $this->config
['class']['civility'],
135 //Set civility default
136 'civility_default' => $doctrine->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
138 'mail' => $this->isGranted('ROLE_ADMIN'),
140 'slug' => $this->isGranted('ROLE_ADMIN'),
148 if ($this->isGranted('ROLE_ADMIN')) {
149 //Create the LoginType form and give the proper parameters
150 $reset = $this->createForm($this->config
['edit']['view']['reset'], $user, [
151 //Set action to register route name and context
152 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']),
160 if ($request->isMethod('POST')) {
161 //Refill the fields in case the form is not valid.
162 $reset->handleRequest($request);
164 //With reset submitted and valid
165 if ($reset->isSubmitted() && $reset->isValid()) {
167 $data = $reset->getData();
170 $data->setPassword($encoder->encodePassword($data, $data->getPassword()));
173 $manager->persist($data);
175 //Flush to get the ids
179 $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail = $data->getMail()]));
181 //Redirect to cleanup the form
182 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $slugger->short($mail), 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']);
187 $this->config
['edit']['view']['context']['reset'] = $reset->createView();
189 //XXX: prefer a reset on login to force user unspam action
192 $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure'));
196 if ($request->isMethod('POST')) {
197 //Refill the fields in case the form is not valid.
198 $edit->handleRequest($request);
200 //With edit submitted and valid
201 if ($edit->isSubmitted() && $edit->isValid()) {
203 $data = $edit->getData();
209 if ($this->isGranted('ROLE_ADMIN')) {
211 if (!empty($data->getSlug())) {
213 $slug = $slugger->slug($data->getPseudonym());
217 $data->setSlug($slug);
221 $manager->persist($data);
223 //Try saving in database
225 //Flush to get the ids
229 $this->addFlash('notice', $this->translator
->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()]));
231 //Redirect to cleanup the form
232 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $slugger->short($mail), 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']);
233 //Catch double slug or mail
234 } catch (UniqueConstraintViolationException
$e) {
235 //Add error message mail already exists
236 $this->addFlash('error', $this->translator
->trans('Account %mail% or with slug %slug% already exists', ['%mail%' => $data->getMail(), '%slug%' => $slug]));
242 return $this->render(
244 $this->config
['edit']['view']['name'],
246 ['edit' => $edit->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['edit']['view']['context']
253 * @param Request $request The request
254 * @param AuthenticationUtils $authenticationUtils The authentication utils
255 * @param RouterInterface $router The router instance
256 * @param SluggerUtil $slugger The slugger
257 * @param string $mail The shorted mail address
258 * @param string $hash The hashed password
259 * @return Response The response
261 public function login(Request
$request, AuthenticationUtils
$authenticationUtils, RouterInterface
$router, SluggerUtil
$slugger, $mail, $hash): Response
{
262 //Create the LoginType form and give the proper parameters
263 $login = $this->createForm($this->config
['login']['view']['form'], null, [
264 //Set action to login route name and context
265 'action' => $this->generateUrl($this->config
['route']['login']['name'], $this->config
['route']['login']['context']),
266 //Disable repeated password
267 'password_repeated' => false,
276 if (!empty($mail) && !empty($hash)) {
278 if ($hash != $slugger->hash($mail)) {
280 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
284 $mail = $slugger->unshort($smail = $mail);
287 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
289 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
293 $login->get('mail')->setData($mail);
294 //Last username entered by the user
295 } elseif ($lastUsername = $authenticationUtils->getLastUsername()) {
296 $login->get('mail')->setData($lastUsername);
299 //Get the login error if there is one
300 if ($error = $authenticationUtils->getLastAuthenticationError()) {
301 //Get translated error
302 $error = $this->translator
->trans($error->getMessageKey());
304 //Add error message to mail field
305 $login->get('mail')->addError(new FormError($error));
307 //Create the LoginType form and give the proper parameters
308 $recover = $this->createForm($this->config
['recover']['view']['form'], null, [
309 //Set action to recover route name and context
310 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
317 //Get recover mail entity
318 $recover->get('mail')
319 //Set mail from login form
320 ->setData($login->get('mail')->getData())
322 ->addError(new FormError($this->translator
->trans('Use this form to recover your account')));
324 //Add recover form to context
325 $context['recover'] = $recover->createView();
328 $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure'));
332 return $this->render(
334 $this->config
['login']['view']['name'],
336 ['login' => $login->createView()]+
$context+
$this->config
['login']['view']['context']
343 * @param Request $request The request
344 * @param Registry $manager The doctrine registry
345 * @param UserPasswordEncoderInterface $encoder The password encoder
346 * @param EntityManagerInterface $manager The doctrine entity manager
347 * @param SluggerUtil $slugger The slugger
348 * @param MailerInterface $mailer The mailer
349 * @param string $mail The shorted mail address
350 * @param string $pass The shorted password
351 * @param string $hash The hashed password
352 * @return Response The response
354 public function recover(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, $mail, $pass, $hash): Response
{
355 //Without mail, pass and hash
356 if (empty($mail) && empty($pass) && empty($hash)) {
357 //Create the LoginType form and give the proper parameters
358 $form = $this->createForm($this->config
['recover']['view']['form'], null, [
359 //Set action to recover route name and context
360 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
367 if ($request->isMethod('POST')) {
368 //Refill the fields in case the form is not valid.
369 $form->handleRequest($request);
371 if ($form->isValid()) {
373 $data = $form->getData();
375 //Find user by data mail
376 if ($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($data['mail'])) {
378 $recoverMail =& $this->config
['recover']['mail'];
381 $mail = $slugger->short($user->getMail());
384 $pass = $slugger->hash($user->getPassword());
386 //Generate each route route
387 foreach($this->config
['recover']['route'] as $route => $tag) {
388 //Only process defined routes
389 if (!empty($this->config
['route'][$route])) {
390 //Process for recover mail url
391 if ($route == 'recover') {
392 //Set the url in context
393 $recoverMail['context'][$tag] = $this->get('router')->generate(
394 $this->config
['route'][$route]['name'],
395 //Prepend recover context with tag
399 'hash' => $slugger->hash($mail.$pass)
400 ]+
$this->config
['route'][$route]['context'],
401 UrlGeneratorInterface
::ABSOLUTE_URL
408 $recoverMail['context']['recipient_mail'] = $user->getMail();
411 $recoverMail['context']['recipient_name'] = trim($user->getForename().' '.$user->getSurname().($user->getPseudonym()?' ('.$user->getPseudonym().')':''));
413 //Init subject context
414 $subjectContext = $slugger->flatten(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context']), null, '.', '%', '%');
417 $recoverMail['subject'] = ucfirst($this->translator
->trans($recoverMail['subject'], $subjectContext));
420 $message = (new TemplatedEmail())
422 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
424 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
425 ->to(new Address($recoverMail['context']['recipient_mail'], $recoverMail['context']['recipient_name']))
427 ->subject($recoverMail['subject'])
429 //Set path to twig templates
430 ->htmlTemplate($recoverMail['html'])
431 ->textTemplate($recoverMail['text'])
434 //XXX: require recursive merge to avoid loosing subkeys
435 //['subject' => $recoverMail['subject']]+$recoverMail['context']+$this->config['recover']['view']['context']
436 ->context(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context'], ['subject' => $recoverMail['subject']]));
438 //Try sending message
439 //XXX: mail delivery may silently fail
442 $mailer->send($message);
444 //Redirect on the same route with sent=1 to cleanup form
445 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
446 //Catch obvious transport exception
447 } catch(TransportExceptionInterface
$e) {
448 //Add error message mail unreachable
449 $form->get('mail')->addError(new FormError($this->translator
->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
453 //Add error message to mail field
454 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $data['mail']])));
460 return $this->render(
462 $this->config
['recover']['view']['name'],
464 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
469 if ($hash != $slugger->hash($mail.$pass)) {
471 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
475 $mail = $slugger->unshort($smail = $mail);
478 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
480 //XXX: prevent slugger reverse engineering by not displaying decoded mail
481 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
484 //With existing subscriber
485 if (empty($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
487 //XXX: prevent slugger reverse engineering by not displaying decoded mail
488 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
491 //With unmatched pass
492 if ($pass != $slugger->hash($user->getPassword())) {
494 //XXX: prevent use of outdated recover link
495 throw $this->createNotFoundException($this->translator
->trans('Outdated recover link'));
498 //Create the LoginType form and give the proper parameters
499 $form = $this->createForm($this->config
['recover']['view']['form'], $user, [
500 //Set action to recover route name and context
501 'action' => $this->generateUrl($this->config
['route']['recover']['name'], ['mail' => $smail, 'pass' => $pass, 'hash' => $hash]+
$this->config
['route']['recover']['context']),
508 if ($request->isMethod('POST')) {
509 //Refill the fields in case the form is not valid.
510 $form->handleRequest($request);
512 if ($form->isValid()) {
514 $data = $form->getData();
516 //Set encoded password
517 $encoded = $encoder->encodePassword($user, $user->getPassword());
520 $pass = $slugger->hash($encoded);
523 $user->setPassword($encoded);
526 $manager->persist($user);
532 $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail]));
534 //Redirect to user login
535 return $this->redirectToRoute($this->config
['route']['login']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['login']['context']);
540 return $this->render(
542 $this->config
['recover']['view']['name'],
544 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
549 * Register an account
551 * @param Request $request The request
552 * @param Registry $manager The doctrine registry
553 * @param UserPasswordEncoderInterface $encoder The password encoder
554 * @param EntityManagerInterface $manager The doctrine entity manager
555 * @param SluggerUtil $slugger The slugger
556 * @param MailerInterface $mailer The mailer
557 * @param LoggerInterface $logger The logger
558 * @param string $mail The shorted mail address
559 * @param string $field The serialized then shorted form field array
560 * @param string $hash The hashed serialized field array
561 * @return Response The response
563 public function register(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, LoggerInterface
$logger, $mail, $field, $hash): Response
{
565 if (!empty($_POST['register']['mail'])) {
568 $this->translator
->trans(
569 'register: mail=%mail% locale=%locale% confirm=%confirm%',
571 '%mail%' => $postMail = $_POST['register']['mail'],
572 '%locale%' => $request->getLocale(),
573 '%confirm%' => $this->get('router')->generate(
574 $this->config
['route']['confirm']['name'],
575 //Prepend subscribe context with tag
577 'mail' => $postSmail = $slugger->short($postMail),
578 'hash' => $slugger->hash($postSmail)
579 ]+
$this->config
['route']['confirm']['context'],
580 UrlGeneratorInterface
::ABSOLUTE_URL
587 //With mail and field
588 if (!empty($field) && !empty($hash)) {
590 if ($hash != $slugger->hash($mail.$field)) {
592 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
598 $mail = $slugger->unshort($smail = $mail);
601 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
603 //XXX: prevent slugger reverse engineering by not displaying decoded mail
604 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
607 //With existing registrant
608 if ($existing = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail)) {
609 //With disabled existing
610 if ($existing->isDisabled()) {
612 return $this->render(
614 $this->config
['register']['view']['name'],
616 ['title' => $this->translator
->trans('Access denied'), 'disabled' => 1]+
$this->config
['register']['view']['context'],
618 new Response('', 403)
620 //With unactivated existing
621 } elseif (!$existing->isActivated()) {
623 //TODO: change for activate ???
624 $activateMail =& $this->config
['register']['mail'];
626 //Generate each route route
627 foreach($this->config
['register']['route'] as $route => $tag) {
628 //Only process defined routes
629 if (!empty($this->config
['route'][$route])) {
630 //Process for confirm url
631 if ($route == 'confirm') {
632 //Set the url in context
633 $activateMail['context'][$tag] = $this->get('router')->generate(
634 $this->config
['route'][$route]['name'],
635 //Prepend subscribe context with tag
637 'mail' => $smail = $slugger->short($existing->getMail()),
638 'hash' => $slugger->hash($smail)
639 ]+
$this->config
['route'][$route]['context'],
640 UrlGeneratorInterface
::ABSOLUTE_URL
647 $activateMail['context']['recipient_mail'] = $existing->getMail();
650 $activateMail['context']['recipient_name'] = implode(' ', [$existing->getForename(), $existing->getSurname(), $existing->getPseudonym()?'('.$existing->getPseudonym().')':'']);
652 //Init subject context
653 $subjectContext = $slugger->flatten(array_replace_recursive($this->config
['register']['view']['context'], $activateMail['context']), null, '.', '%', '%');
656 $activateMail['subject'] = ucfirst($this->translator
->trans($activateMail['subject'], $subjectContext));
659 $message = (new TemplatedEmail())
661 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
663 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
664 ->to(new Address($activateMail['context']['recipient_mail'], $activateMail['context']['recipient_name']))
666 ->subject($activateMail['subject'])
668 //Set path to twig templates
669 ->htmlTemplate($activateMail['html'])
670 ->textTemplate($activateMail['text'])
673 ->context(['subject' => $activateMail['subject']]+
$activateMail['context']);
675 //Try sending message
676 //XXX: mail delivery may silently fail
679 $mailer->send($message);
680 //Catch obvious transport exception
681 } catch(TransportExceptionInterface
$e) {
682 //Add error message mail unreachable
683 $this->addFlash('error', $this->translator
->trans('Account %mail% tried activate but unable to contact', ['%mail%' => $existing->getMail()]));
687 $routeParams = $request->get('_route_params');
689 //Remove mail, field and hash from route params
690 unset($routeParams['mail'], $routeParams['field'], $routeParams['hash']);
692 //Redirect on the same route with sent=1 to cleanup form
693 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$routeParams);
696 //Add error message mail already exists
697 $this->addFlash('warning', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $existing->getMail()]));
699 //Redirect to user view
700 return $this->redirectToRoute(
701 $this->config
['route']['edit']['name'],
703 'mail' => $smail = $slugger->short($existing->getMail()),
704 'hash' => $slugger->hash($smail)
705 ]+
$this->config
['route']['edit']['context']
716 //Unshort then unserialize field
717 $field = $slugger->unserialize($sfield = $field);
719 } catch (\Error
|\Exception
$e) {
721 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]), $e);
724 //With non array field
725 if (!is_array($field)) {
727 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]));
729 //Without field and hash
745 $reflection = new \
ReflectionClass($this->config
['class']['user']);
748 $user = $reflection->newInstance(strval($mail));
750 //Create the RegisterType form and give the proper parameters
751 $form = $this->createForm($this->config
['register']['view']['form'], $user, $field+
[
752 //Set action to register route name and context
753 'action' => $this->generateUrl($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield, 'hash' => $hash]+
$this->config
['route']['register']['context']),
755 'civility_class' => $this->config
['class']['civility'],
756 //Set civility default
757 'civility_default' => $doctrine->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
764 if ($request->isMethod('POST')) {
765 //Refill the fields in case the form is not valid.
766 $form->handleRequest($request);
768 if ($form->isValid()) {
770 $data = $form->getData();
772 //With existing registrant
773 if ($doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail = $data->getMail())) {
774 //Add error message mail already exists
775 $this->addFlash('warning', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
777 //Redirect to user view
778 return $this->redirectToRoute(
779 $this->config
['route']['edit']['name'],
781 'mail' => $smail = $slugger->short($mail),
782 'hash' => $slugger->hash($smail)
783 ]+
$this->config
['route']['edit']['context']
788 $registerMail =& $this->config
['register']['mail'];
790 //Extract names and pseudonym from mail
791 $names = explode(' ', $pseudonym = ucwords(trim(preg_replace('/[^a-zA-Z]+/', ' ', current(explode('@', $data->getMail()))))));
794 $user->setPseudonym($user->getPseudonym()??$pseudonym);
797 $user->setForename($user->getForename()??$names[0]);
800 $user->setSurname($user->getSurname()??$names[1]??$names[0]);
803 $user->setPassword($encoder->encodePassword($user, $user->getPassword()??$data->getMail()));
806 $user->setCreated(new \
DateTime('now'));
809 $user->setUpdated(new \
DateTime('now'));
812 $manager->persist($user);
814 //Iterate on default group
815 foreach($this->config
['default']['group'] as $i => $groupTitle) {
817 if (($group = $doctrine->getRepository($this->config
['class']['group'])->findOneByTitle($groupTitle))) {
819 //XXX: see vendor/symfony/security-core/Role/Role.php
820 $user->addGroup($group);
824 //XXX: consider missing group as fatal
825 throw new \
Exception(sprintf('Group from rapsys_user.default.group[%d] not found by title: %s', $i, $groupTitle));
829 //Generate each route route
830 foreach($this->config
['register']['route'] as $route => $tag) {
831 //Only process defined routes
832 if (!empty($this->config
['route'][$route])) {
833 //Process for confirm url
834 if ($route == 'confirm') {
835 //Set the url in context
836 $registerMail['context'][$tag] = $this->get('router')->generate(
837 $this->config
['route'][$route]['name'],
838 //Prepend subscribe context with tag
840 'mail' => $smail = $slugger->short($data->getMail()),
841 'hash' => $slugger->hash($smail)
842 ]+
$this->config
['route'][$route]['context'],
843 UrlGeneratorInterface
::ABSOLUTE_URL
849 //XXX: DEBUG: remove me
850 //die($registerMail['context']['confirm_url']);
853 $registerMail['context']['recipient_mail'] = $data->getMail();
856 $registerMail['context']['recipient_name'] = '';
859 $registerMail['context']['recipient_name'] = implode(' ', [$data->getForename(), $data->getSurname(), $data->getPseudonym()?'('.$data->getPseudonym().')':'']);
861 //Init subject context
862 $subjectContext = $slugger->flatten(array_replace_recursive($this->config
['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
865 $registerMail['subject'] = ucfirst($this->translator
->trans($registerMail['subject'], $subjectContext));
868 $message = (new TemplatedEmail())
870 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
872 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
873 ->to(new Address($registerMail['context']['recipient_mail'], $registerMail['context']['recipient_name']))
875 ->subject($registerMail['subject'])
877 //Set path to twig templates
878 ->htmlTemplate($registerMail['html'])
879 ->textTemplate($registerMail['text'])
882 ->context(['subject' => $registerMail['subject']]+
$registerMail['context']);
884 //Try saving in database
889 //Add error message mail already exists
890 $this->addFlash('notice', $this->translator
->trans('Your account has been created'));
892 //Try sending message
893 //XXX: mail delivery may silently fail
896 $mailer->send($message);
898 //Redirect on the same route with sent=1 to cleanup form
899 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
900 //Catch obvious transport exception
901 } catch(TransportExceptionInterface
$e) {
902 //Add error message mail unreachable
903 $form->get('mail')->addError(new FormError($this->translator
->trans('Account %mail% tried subscribe but unable to contact', ['%mail%' => $data->getMail()])));
905 //Catch double subscription
906 } catch (UniqueConstraintViolationException
$e) {
907 //Add error message mail already exists
908 $this->addFlash('error', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
914 return $this->render(
916 $this->config
['register']['view']['name'],
918 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['register']['view']['context']