3 namespace Rapsys\UserBundle\Controller
;
5 use Doctrine\Bundle\DoctrineBundle\Registry
;
6 use Doctrine\ORM\EntityManagerInterface
;
7 use Psr\Log\LoggerInterface
;
8 use Symfony\Bridge\Twig\Mime\TemplatedEmail
;
9 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController
;
10 use Symfony\Component\DependencyInjection\ContainerInterface
;
11 use Symfony\Component\Form\FormError
;
12 use Symfony\Component\HttpFoundation\Request
;
13 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException
;
14 use Symfony\Component\Mailer\Exception\TransportExceptionInterface
;
15 use Symfony\Component\Mailer\MailerInterface
;
16 use Symfony\Component\Mime\Address
;
17 use Symfony\Component\Routing\Exception\MethodNotAllowedException
;
18 use Symfony\Component\Routing\Exception\ResourceNotFoundException
;
19 use Symfony\Component\Routing\Generator\UrlGeneratorInterface
;
20 use Symfony\Component\Routing\RequestContext
;
21 use Symfony\Component\Routing\RouterInterface
;
22 use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
;
23 use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface
;
24 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils
;
25 use Symfony\Component\Translation\TranslatorInterface
;
27 use Rapsys\PackBundle\Util\SluggerUtil
;
28 use Rapsys\UserBundle\RapsysUserBundle
;
30 class DefaultController
extends AbstractController
{
35 protected $translator;
40 * @TODO: move all canonical and other view related stuff in an user AbstractController like in RapsysAir render feature !!!!
41 * @TODO: add resetpassword ? with $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); https://symfony.com/doc/current/security/remember_me.html
43 * @param ContainerInterface $container The containter instance
44 * @param RouterInterface $router The router instance
45 * @param TranslatorInterface $translator The translator instance
47 public function __construct(ContainerInterface
$container, RouterInterface
$router, TranslatorInterface
$translator) {
49 $this->config
= $container->getParameter(self
::getAlias());
52 $this->translator
= $translator;
55 $stack = $container->get('request_stack');
58 $request = $stack->getCurrentRequest();
61 $currentLocale = $request->getLocale();
64 $this->config
['context']['locale'] = str_replace('_', '-', $currentLocale);
69 //Look for keys to translate
70 if (!empty($this->config
['translate'])) {
71 //Iterate on keys to translate
72 foreach($this->config
['translate'] as $translate) {
76 foreach(array_reverse(explode('.', $translate)) as $curkey) {
77 $tmp = array_combine([$curkey], [$tmp]);
80 $translates = array_replace_recursive($translates, $tmp);
84 //Inject every requested route in view and mail context
85 foreach($this->config
as $tag => $current) {
86 //Look for entry with title subkey
87 if (!empty($current['title'])) {
88 //Translate title value
89 $this->config
[$tag]['title'] = $translator->trans($current['title']);
92 //Look for entry with route subkey
93 if (!empty($current['route'])) {
94 //Generate url for both view and mail
95 foreach(['view', 'mail'] as $view) {
96 //Check that context key is usable
97 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
98 //Merge with global context
99 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
['context'], $this->config
[$tag][$view]['context']);
101 //Process every routes
102 foreach($current['route'] as $route => $key) {
104 if ($route == 'confirm') {
105 //Skip route as it requires some parameters
110 $value = $router->generate(
111 $this->config
['route'][$route]['name'],
112 $this->config
['route'][$route]['context'],
113 //Generate absolute url for mails
114 $view=='mail'?UrlGeneratorInterface
::ABSOLUTE_URL
:UrlGeneratorInterface
::ABSOLUTE_PATH
118 if (strpos($key, '.') !== false) {
123 foreach(array_reverse(explode('.', $key)) as $curkey) {
124 $tmp = array_combine([$curkey], [$tmp]);
128 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
132 $this->config
[$tag][$view]['context'][$key] = $value;
136 //Look for successful intersections
137 if (!empty(array_intersect_key($translates, $this->config
[$tag][$view]['context']))) {
138 //Iterate on keys to translate
139 foreach($this->config
['translate'] as $translate) {
141 $keys = explode('.', $translate);
144 $tmp = $this->config
[$tag][$view]['context'];
147 foreach($keys as $curkey) {
149 if (!isset($tmp[$curkey])) {
155 $tmp = $tmp[$curkey];
158 //Translate tmp value
159 $tmp = $translator->trans($tmp);
162 foreach(array_reverse($keys) as $curkey) {
164 $tmp = array_combine([$curkey], [$tmp]);
168 $this->config
[$tag][$view]['context'] = array_replace_recursive($this->config
[$tag][$view]['context'], $tmp);
173 if ($view == 'view') {
175 $pathInfo = $router->getContext()->getPathInfo();
177 //Iterate on locales excluding current one
178 foreach($this->config
['locales'] as $locale) {
182 //Iterate on other locales
183 foreach(array_diff($this->config
['locales'], [$locale]) as $other) {
184 $titles[$other] = $translator->trans($this->config
['languages'][$locale], [], null, $other);
187 //Retrieve route matching path
188 $route = $router->match($pathInfo);
191 $name = $route['_route'];
194 unset($route['_route']);
196 //With current locale
197 if ($locale == $currentLocale) {
198 //Set locale locales context
199 $this->config
[$tag][$view]['context']['canonical'] = $router->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
);
201 //Set locale locales context
202 $this->config
[$tag][$view]['context']['alternates'][$locale] = [
203 'absolute' => $router->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
),
204 'relative' => $router->generate($name, ['_locale' => $locale]+
$route),
205 'title' => implode('/', $titles),
206 'translated' => $translator->trans($this->config
['languages'][$locale], [], null, $locale)
211 if (empty($this->config
[$tag][$view]['context']['alternates'][$slocale = substr($locale, 0, 2)])) {
213 $this->config
[$tag][$view]['context']['alternates'][$slocale] = [
214 'absolute' => $router->generate($name, ['_locale' => $locale]+
$route, UrlGeneratorInterface
::ABSOLUTE_URL
),
215 'relative' => $router->generate($name, ['_locale' => $locale]+
$route),
216 'title' => implode('/', $titles),
217 'translated' => $translator->trans($this->config
['languages'][$locale], [], null, $locale)
229 * Confirm account from mail link
231 * @param Request $request The request
232 * @param Registry $manager The doctrine registry
233 * @param UserPasswordEncoderInterface $encoder The password encoder
234 * @param EntityManagerInterface $manager The doctrine entity manager
235 * @param SluggerUtil $slugger The slugger
236 * @param MailerInterface $mailer The mailer
237 * @param string $mail The shorted mail address
238 * @param string $hash The hashed password
239 * @return Response The response
241 public function confirm(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, $mail, $hash) {
243 if ($hash != $slugger->hash($mail)) {
245 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
249 $mail = $slugger->unshort($smail = $mail);
252 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
254 //XXX: prevent slugger reverse engineering by not displaying decoded mail
255 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
258 //Without existing registrant
259 if (!($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
260 //Add error message mail already exists
261 //XXX: prevent slugger reverse engineering by not displaying decoded mail
262 $this->addFlash('error', $this->translator
->trans('Account %mail% do not exists', ['%mail%' => $smail]));
264 //Redirect to register view
265 return $this->redirectToRoute($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield = $slugger->serialize([]), 'hash' => $slugger->hash($smail.$sfield)]+
$this->config
['route']['register']['context']);
269 $user->setActive(true);
272 $user->setUpdated(new \
DateTime('now'));
275 $manager->persist($user);
280 //Add error message mail already exists
281 $this->addFlash('notice', $this->translator
->trans('Your account has been activated'));
283 //Redirect to user view
284 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']);
288 * Edit account by shorted mail
290 * @param Request $request The request
291 * @param Registry $manager The doctrine registry
292 * @param EntityManagerInterface $manager The doctrine entity manager
293 * @param SluggerUtil $slugger The slugger
294 * @param string $mail The shorted mail address
295 * @param string $hash The hashed password
296 * @return Response The response
298 public function edit(Request
$request, Registry
$doctrine, EntityManagerInterface
$manager, SluggerUtil
$slugger, $mail, $hash) {
300 if ($hash != $slugger->hash($mail)) {
302 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
306 $mail = $slugger->unshort($smail = $mail);
308 //With existing subscriber
309 if (empty($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
311 //XXX: prevent slugger reverse engineering by not displaying decoded mail
312 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
315 //Prevent access when not admin, user is not guest and not currently logged user
316 if (!$this->isGranted('ROLE_ADMIN') && $user != $this->getUser()) {
317 //Throw access denied
318 //XXX: prevent slugger reverse engineering by not displaying decoded mail
319 throw $this->createAccessDeniedException($this->translator
->trans('Unable to access user: %mail%', ['%mail%' => $smail]));
322 //Create the RegisterType form and give the proper parameters
323 $form = $this->createForm($this->config
['register']['view']['form'], $user, [
324 //Set action to register route name and context
325 'action' => $this->generateUrl($this->config
['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']),
327 'civility_class' => $this->config
['class']['civility'],
328 //Set civility default
329 'civility_default' => $doctrine->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
331 'mail' => $this->isGranted('ROLE_ADMIN'),
333 //XXX: prefer a reset on login to force user unspam action
339 if ($request->isMethod('POST')) {
340 //Refill the fields in case the form is not valid.
341 $form->handleRequest($request);
343 if ($form->isValid()) {
345 $data = $form->getData();
348 $manager->persist($data);
350 //Flush to get the ids
354 $this->addFlash('notice', $this->translator
->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()]));
356 //Redirect to user view
357 //TODO: extract referer ??? or useless ???
358 return $this->redirectToRoute($this->config
['route']['edit']['name'], ['mail' => $smail = $slugger->short($mail), 'hash' => $slugger->hash($smail)]+
$this->config
['route']['edit']['context']);
360 //Redirect to cleanup the form
361 return $this->redirectToRoute('rapsys_air', ['user' => $data->getId()]);
365 $this->addFlash('notice', $this->translator
->trans('To change your password relogin with your mail %mail% and any password then follow the procedure', ['%mail%' => $mail]));
369 return $this->render(
371 $this->config
['edit']['view']['name'],
373 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['edit']['view']['context']
380 * @todo When account is not activated, refuse login and send verification mail ?
381 * @todo Redirect to referer if route is not connect ?
383 * @param Request $request The request
384 * @param AuthenticationUtils $authenticationUtils The authentication utils
385 * @param RouterInterface $router The router instance
386 * @param SluggerUtil $slugger The slugger
387 * @param string $mail The shorted mail address
388 * @param string $hash The hashed password
389 * @return Response The response
391 public function login(Request
$request, AuthenticationUtils
$authenticationUtils, RouterInterface
$router, SluggerUtil
$slugger, $mail, $hash) {
392 //Create the LoginType form and give the proper parameters
393 $login = $this->createForm($this->config
['login']['view']['form'], null, [
394 //Set action to login route name and context
395 'action' => $this->generateUrl($this->config
['route']['login']['name'], $this->config
['route']['login']['context']),
396 //Disable repeated password
397 'password_repeated' => false,
406 if (!empty($mail) && !empty($hash)) {
408 if ($hash != $slugger->hash($mail)) {
410 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
414 $mail = $slugger->unshort($smail = $mail);
417 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
419 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
423 $login->get('mail')->setData($mail);
424 //Last username entered by the user
425 } elseif ($lastUsername = $authenticationUtils->getLastUsername()) {
426 $login->get('mail')->setData($lastUsername);
429 //Get the login error if there is one
430 if ($error = $authenticationUtils->getLastAuthenticationError()) {
431 //Get translated error
432 $error = $this->translator
->trans($error->getMessageKey());
434 //Add error message to mail field
435 $login->get('mail')->addError(new FormError($error));
437 //Create the LoginType form and give the proper parameters
438 $recover = $this->createForm($this->config
['recover']['view']['form'], null, [
439 //Set action to recover route name and context
440 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
447 //Get recover mail entity
448 $recover->get('mail')
449 //Set mail from login form
450 ->setData($login->get('mail')->getData())
452 ->addError(new FormError($this->translator
->trans('Use this form to recover your account')));
454 //Add recover form to context
455 $context['recover'] = $recover->createView();
458 //TODO: drop it if referer route is recover ?
459 $this->addFlash('notice', $this->translator
->trans('To change your password login with your mail and any password then follow the procedure'));
463 return $this->render(
465 $this->config
['login']['view']['name'],
467 ['login' => $login->createView()]+
$context+
$this->config
['login']['view']['context']
474 * @param Request $request The request
475 * @param Registry $manager The doctrine registry
476 * @param UserPasswordEncoderInterface $encoder The password encoder
477 * @param EntityManagerInterface $manager The doctrine entity manager
478 * @param SluggerUtil $slugger The slugger
479 * @param MailerInterface $mailer The mailer
480 * @param string $mail The shorted mail address
481 * @param string $pass The shorted password
482 * @param string $hash The hashed password
483 * @return Response The response
485 public function recover(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, $mail, $pass, $hash) {
486 //Without mail, pass and hash
487 if (empty($mail) && empty($pass) && empty($hash)) {
488 //Create the LoginType form and give the proper parameters
489 $form = $this->createForm($this->config
['recover']['view']['form'], null, [
490 //Set action to recover route name and context
491 'action' => $this->generateUrl($this->config
['route']['recover']['name'], $this->config
['route']['recover']['context']),
498 if ($request->isMethod('POST')) {
499 //Refill the fields in case the form is not valid.
500 $form->handleRequest($request);
502 if ($form->isValid()) {
504 $data = $form->getData();
506 //Find user by data mail
507 if ($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($data['mail'])) {
509 $recoverMail =& $this->config
['recover']['mail'];
512 $mail = $slugger->short($user->getMail());
515 $pass = $slugger->hash($user->getPassword());
517 //Generate each route route
518 foreach($this->config
['recover']['route'] as $route => $tag) {
519 //Only process defined routes
520 if (!empty($this->config
['route'][$route])) {
521 //Process for recover mail url
522 if ($route == 'recover') {
523 //Set the url in context
524 $recoverMail['context'][$tag] = $this->get('router')->generate(
525 $this->config
['route'][$route]['name'],
526 //Prepend recover context with tag
530 'hash' => $slugger->hash($mail.$pass)
531 ]+
$this->config
['route'][$route]['context'],
532 UrlGeneratorInterface
::ABSOLUTE_URL
539 $recoverMail['context']['recipient_mail'] = $user->getMail();
542 $recoverMail['context']['recipient_name'] = trim($user->getForename().' '.$user->getSurname().($user->getPseudonym()?' ('.$user->getPseudonym().')':''));
544 //Init subject context
545 $subjectContext = $slugger->flatten(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context']), null, '.', '%', '%');
548 $recoverMail['subject'] = ucfirst($this->translator
->trans($recoverMail['subject'], $subjectContext));
551 $message = (new TemplatedEmail())
553 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
555 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
556 ->to(new Address($recoverMail['context']['recipient_mail'], $recoverMail['context']['recipient_name']))
558 ->subject($recoverMail['subject'])
560 //Set path to twig templates
561 ->htmlTemplate($recoverMail['html'])
562 ->textTemplate($recoverMail['text'])
565 //XXX: require recursive merge to avoid loosing subkeys
566 //['subject' => $recoverMail['subject']]+$recoverMail['context']+$this->config['recover']['view']['context']
567 ->context(array_replace_recursive($this->config
['recover']['view']['context'], $recoverMail['context'], ['subject' => $recoverMail['subject']]));
569 //Try sending message
570 //XXX: mail delivery may silently fail
573 $mailer->send($message);
575 //Redirect on the same route with sent=1 to cleanup form
576 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
577 //Catch obvious transport exception
578 } catch(TransportExceptionInterface
$e) {
579 //Add error message mail unreachable
580 $form->get('mail')->addError(new FormError($this->translator
->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
584 //Add error message to mail field
585 $form->get('mail')->addError(new FormError($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $data['mail']])));
591 return $this->render(
593 $this->config
['recover']['view']['name'],
595 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
600 if ($hash != $slugger->hash($mail.$pass)) {
602 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
606 $mail = $slugger->unshort($smail = $mail);
609 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
611 //XXX: prevent slugger reverse engineering by not displaying decoded mail
612 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
615 //With existing subscriber
616 if (empty($user = $doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail))) {
618 //XXX: prevent slugger reverse engineering by not displaying decoded mail
619 throw $this->createNotFoundException($this->translator
->trans('Unable to find account %mail%', ['%mail%' => $smail]));
622 //With unmatched pass
623 if ($pass != $slugger->hash($user->getPassword())) {
625 //XXX: prevent use of outdated recover link
626 throw $this->createNotFoundException($this->translator
->trans('Outdated recover link'));
629 //Create the LoginType form and give the proper parameters
630 $form = $this->createForm($this->config
['recover']['view']['form'], $user, [
631 //Set action to recover route name and context
632 'action' => $this->generateUrl($this->config
['route']['recover']['name'], ['mail' => $smail, 'pass' => $pass, 'hash' => $hash]+
$this->config
['route']['recover']['context']),
639 if ($request->isMethod('POST')) {
640 //Refill the fields in case the form is not valid.
641 $form->handleRequest($request);
643 if ($form->isValid()) {
645 $data = $form->getData();
647 //Set encoded password
648 $encoded = $encoder->encodePassword($user, $user->getPassword());
651 $pass = $slugger->hash($encoded);
654 $user->setPassword($encoded);
657 $user->setUpdated(new \
DateTime('now'));
660 $manager->persist($user);
666 $this->addFlash('notice', $this->translator
->trans('Account %mail% password updated', ['%mail%' => $mail]));
668 //Redirect to user login
669 return $this->redirectToRoute($this->config
['route']['login']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+
$this->config
['route']['login']['context']);
674 return $this->render(
676 $this->config
['recover']['view']['name'],
678 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['recover']['view']['context']
683 * Register an account
685 * @param Request $request The request
686 * @param Registry $manager The doctrine registry
687 * @param UserPasswordEncoderInterface $encoder The password encoder
688 * @param EntityManagerInterface $manager The doctrine entity manager
689 * @param SluggerUtil $slugger The slugger
690 * @param MailerInterface $mailer The mailer
691 * @param LoggerInterface $logger The logger
692 * @param string $mail The shorted mail address
693 * @param string $field The serialized then shorted form field array
694 * @param string $hash The hashed serialized field array
695 * @return Response The response
697 public function register(Request
$request, Registry
$doctrine, UserPasswordEncoderInterface
$encoder, EntityManagerInterface
$manager, SluggerUtil
$slugger, MailerInterface
$mailer, LoggerInterface
$logger, $mail, $field, $hash) {
699 $reflection = new \
ReflectionClass($this->config
['class']['user']);
702 $user = $reflection->newInstance();
704 //With mail and field
705 if (!empty($field) && !empty($hash)) {
707 if ($hash != $slugger->hash($mail.$field)) {
709 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
715 $mail = $slugger->unshort($smail = $mail);
718 if (filter_var($mail, FILTER_VALIDATE_EMAIL
) === false) {
720 //XXX: prevent slugger reverse engineering by not displaying decoded mail
721 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
725 $user->setMail($mail);
734 //Unshort then unserialize field
735 $field = $slugger->unserialize($sfield = $field);
737 } catch (\Error
|\Exception
$e) {
739 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]), $e);
742 //With non array field
743 if (!is_array($field)) {
745 throw new BadRequestHttpException($this->translator
->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]));
747 //Without field and hash
759 //Create the RegisterType form and give the proper parameters
760 $form = $this->createForm($this->config
['register']['view']['form'], $user, $field+
[
761 //Set action to register route name and context
762 'action' => $this->generateUrl($this->config
['route']['register']['name'], ['mail' => $smail, 'field' => $sfield, 'hash' => $hash]+
$this->config
['route']['register']['context']),
764 'civility_class' => $this->config
['class']['civility'],
765 //Set civility default
766 'civility_default' => $doctrine->getRepository($this->config
['class']['civility'])->findOneByTitle($this->config
['default']['civility']),
773 if ($request->isMethod('POST')) {
774 //Refill the fields in case the form is not valid.
775 $form->handleRequest($request);
777 if ($form->isValid()) {
779 $data = $form->getData();
781 //With existing registrant
782 if ($doctrine->getRepository($this->config
['class']['user'])->findOneByMail($mail = $data->getMail())) {
783 //Add error message mail already exists
784 $this->addFlash('warning', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
786 //Redirect to user view
787 return $this->redirectToRoute(
788 $this->config
['route']['edit']['name'],
790 'mail' => $smail = $slugger->short($mail),
791 'hash' => $slugger->hash($smail)
792 ]+
$this->config
['route']['edit']['context']
797 $registerMail =& $this->config
['register']['mail'];
799 //Extract names and pseudonym from mail
800 $names = explode(' ', $pseudonym = ucwords(trim(preg_replace('/[^a-zA-Z]+/', ' ', current(explode('@', $data->getMail()))))));
803 $user->setPseudonym($user->getPseudonym()??$pseudonym);
806 $user->setForename($user->getForename()??$names[0]);
809 $user->setSurname($user->getSurname()??$names[1]??$names[0]);
812 $user->setPassword($encoder->encodePassword($user, $user->getPassword()??$data->getMail()));
815 $user->setCreated(new \
DateTime('now'));
818 $user->setUpdated(new \
DateTime('now'));
821 $manager->persist($user);
823 //Iterate on default group
824 foreach($this->config
['default']['group'] as $i => $groupTitle) {
826 if (($group = $doctrine->getRepository($this->config
['class']['group'])->findOneByTitle($groupTitle))) {
828 //XXX: see vendor/symfony/security-core/Role/Role.php
829 $user->addGroup($group);
833 //XXX: consider missing group as fatal
834 throw new \
Exception(sprintf('Group from rapsys_user.default.group[%d] not found by title: %s', $i, $groupTitle));
838 //Generate each route route
839 foreach($this->config
['register']['route'] as $route => $tag) {
840 //Only process defined routes
841 if (!empty($this->config
['route'][$route])) {
842 //Process for confirm url
843 if ($route == 'confirm') {
844 //Set the url in context
845 $registerMail['context'][$tag] = $this->get('router')->generate(
846 $this->config
['route'][$route]['name'],
847 //Prepend subscribe context with tag
849 'mail' => $smail = $slugger->short($data->getMail()),
850 'hash' => $slugger->hash($smail)
851 ]+
$this->config
['route'][$route]['context'],
852 UrlGeneratorInterface
::ABSOLUTE_URL
858 //XXX: DEBUG: remove me
859 //die($registerMail['context']['confirm_url']);
864 $this->translator
->trans(
865 'newuser:mail=%mail%|locale=%locale%|confirm=%confirm%',
867 '%mail%' => $data->getMail(),
868 '%locale%' => $request->getLocale(),
869 '%confirm%' => $registerMail['context'][$this->config
['register']['route']['confirm']]
875 $registerMail['context']['recipient_mail'] = $data->getMail();
878 $registerMail['context']['recipient_name'] = '';
881 $registerMail['context']['recipient_name'] = implode(' ', [$data->getForename(), $data->getSurname(), $data->getPseudonym()?'('.$data->getPseudonym().')':'']);
883 //Init subject context
884 $subjectContext = $slugger->flatten(array_replace_recursive($this->config
['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
887 $registerMail['subject'] = ucfirst($this->translator
->trans($registerMail['subject'], $subjectContext));
890 $message = (new TemplatedEmail())
892 ->from(new Address($this->config
['contact']['mail'], $this->config
['contact']['title']))
894 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
895 ->to(new Address($registerMail['context']['recipient_mail'], $registerMail['context']['recipient_name']))
897 ->subject($registerMail['subject'])
899 //Set path to twig templates
900 ->htmlTemplate($registerMail['html'])
901 ->textTemplate($registerMail['text'])
904 ->context(['subject' => $registerMail['subject']]+
$registerMail['context']);
906 //Try saving in database
911 //Add error message mail already exists
912 $this->addFlash('notice', $this->translator
->trans('Your account has been created'));
914 //Try sending message
915 //XXX: mail delivery may silently fail
918 $mailer->send($message);
920 //Redirect on the same route with sent=1 to cleanup form
921 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+
$request->get('_route_params'));
922 //Catch obvious transport exception
923 } catch(TransportExceptionInterface
$e) {
924 //Add error message mail unreachable
925 $form->get('mail')->addError(new FormError($this->translator
->trans('Account %mail% tried subscribe but unable to contact', ['%mail%' => $data->getMail()])));
927 //Catch double subscription
928 } catch (\Doctrine\DBAL\Exception\UniqueConstraintViolationException
$e) {
929 //Add error message mail already exists
930 $this->addFlash('error', $this->translator
->trans('Account %mail% already exists', ['%mail%' => $mail]));
936 return $this->render(
938 $this->config
['register']['view']['name'],
940 ['form' => $form->createView(), 'sent' => $request->query
->get('sent', 0)]+
$this->config
['register']['view']['context']
947 public function getAlias(): string {
948 return RapsysUserBundle
::getAlias();