]> Raphaël G. Git Repositories - userbundle/blob - Controller/UserController.php
Required for argon2id (~95-97+) hashes
[userbundle] / Controller / UserController.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys UserBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Rapsys\UserBundle\Controller;
13
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;
24
25 use Rapsys\UserBundle\RapsysUserBundle;
26
27 /**
28 * {@inheritdoc}
29 */
30 class UserController extends AbstractController {
31 /**
32 * User index
33 *
34 * @param Request $request The request
35 * @return Response The response
36 */
37 public function index(Request $request): Response {
38 //Without admin
39 if (!$this->checker->isGranted($this->config['default']['admin'])) {
40 //Throw 403
41 throw $this->createAccessDeniedException($this->translator->trans('Unable to list users'));
42 }
43
44 //Get count
45 $this->context['count'] = $this->doctrine->getRepository($this->config['class']['user'])->findCountAsInt();
46
47 //With not enough users
48 if ($this->context['count'] - $this->page * $this->limit < 0) {
49 //Throw 404
50 throw $this->createNotFoundException($this->translator->trans('Unable to find users'));
51 }
52
53 //Get users
54 $this->context['users'] = $this->doctrine->getRepository($this->config['class']['user'])->findAllAsArray($this->page, $this->limit);
55
56 //Render view
57 return $this->render(
58 //Template
59 $this->config['index']['view']['name'],
60 //Context
61 $this->context+$this->config['index']['view']['context']
62 );
63 }
64
65 /**
66 * Confirm account from mail link
67 *
68 * @param Request $request The request
69 * @param string $hash The hashed password
70 * @param string $mail The shorted mail address
71 * @return Response The response
72 */
73 public function confirm(Request $request, string $hash, string $mail): Response {
74 //With invalid hash
75 if ($hash != $this->slugger->hash($mail)) {
76 //Throw bad request
77 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
78 }
79
80 //Get mail
81 $mail = $this->slugger->unshort($smail = $mail);
82
83 //Without valid mail
84 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
85 //Throw bad request
86 //XXX: prevent slugger reverse engineering by not displaying decoded mail
87 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
88 }
89
90 //Without existing registrant
91 if (!($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
92 //Add error message mail already exists
93 //XXX: prevent slugger reverse engineering by not displaying decoded mail
94 $this->addFlash('error', $this->translator->trans('Account %mail% do not exists', ['%mail%' => $smail]));
95
96 //Redirect to register view
97 return $this->redirectToRoute($this->config['route']['register']['name'], $this->config['route']['register']['context']);
98 }
99
100 //Set active
101 $user->setActive(true);
102
103 //Persist user
104 $this->manager->persist($user);
105
106 //Send to database
107 $this->manager->flush();
108
109 //Add error message mail already exists
110 $this->addFlash('notice', $this->translator->trans('Your account has been activated'));
111
112 //Redirect to user view
113 return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']);
114 }
115
116 /**
117 * Edit account by shorted mail
118 *
119 * @param Request $request The request
120 * @param string $hash The hashed password
121 * @param string $mail The shorted mail address
122 * @return Response The response
123 */
124 public function edit(Request $request, string $hash, string $mail): Response {
125 //With invalid hash
126 if ($hash != $this->slugger->hash($mail)) {
127 //Throw bad request
128 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
129 }
130
131 //Get mail
132 $mail = $this->slugger->unshort($smail = $mail);
133
134 //With existing subscriber
135 if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
136 //Throw not found
137 //XXX: prevent slugger reverse engineering by not displaying decoded mail
138 throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
139 }
140
141 //Prevent access when not admin, user is not guest and not currently logged user
142 if (!$this->checker->isGranted($this->config['default']['admin']) && $user != $this->security->getUser() || !$this->checker->isGranted('IS_AUTHENTICATED_FULLY')) {
143 //Throw access denied
144 //XXX: prevent slugger reverse engineering by not displaying decoded mail
145 throw $this->createAccessDeniedException($this->translator->trans('Unable to access user: %mail%', ['%mail%' => $smail]));
146 }
147
148 //Create the EditType form and give the proper parameters
149 $edit = $this->createForm($this->config['edit']['view']['edit'], $user, [
150 //Set action to edit route name and context
151 'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']),
152 //Set civility class
153 'civility_class' => $this->config['class']['civility'],
154 //Set civility default
155 'civility_default' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
156 //Set method
157 'method' => 'POST'
158 ]+($this->checker->isGranted($this->config['default']['admin'])?$this->config['edit']['admin']:$this->config['edit']['field']));
159
160 //With admin role
161 if ($this->checker->isGranted($this->config['default']['admin'])) {
162 //Create the EditType form and give the proper parameters
163 $reset = $this->createForm($this->config['edit']['view']['reset'], $user, [
164 //Set action to edit route name and context
165 'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']),
166 //Set method
167 'method' => 'POST'
168 ]);
169
170 //With post method
171 if ($request->isMethod('POST')) {
172 //Refill the fields in case the form is not valid.
173 $reset->handleRequest($request);
174
175 //With reset submitted and valid
176 if ($reset->isSubmitted() && $reset->isValid()) {
177 //Set data
178 $data = $reset->getData();
179
180 //Set password
181 $data->setPassword($this->hasher->hashPassword($data, $data->getPassword()));
182
183 //Queue snippet save
184 $this->manager->persist($data);
185
186 //Flush to get the ids
187 $this->manager->flush();
188
189 //Add notice
190 $this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail = $data->getMail()]));
191
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 }
195 }
196
197 //Add reset view
198 $this->config['edit']['view']['context']['reset'] = $reset->createView();
199 }
200
201 //With post method
202 if ($request->isMethod('POST')) {
203 //Refill the fields in case the form is not valid.
204 $edit->handleRequest($request);
205
206 //With edit submitted and valid
207 if ($edit->isSubmitted() && $edit->isValid()) {
208 //Set data
209 $data = $edit->getData();
210
211 //Queue snippet save
212 $this->manager->persist($data);
213
214 //Try saving in database
215 try {
216 //Flush to get the ids
217 $this->manager->flush();
218
219 //Add notice
220 $this->addFlash('notice', $this->translator->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()]));
221
222 //Redirect to cleanup the form
223 return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail = $this->slugger->short($mail), 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']);
224 //Catch double slug or mail
225 } catch (UniqueConstraintViolationException $e) {
226 //Add error message mail already exists
227 $this->addFlash('error', $this->translator->trans('Account %mail% already exists', ['%mail%' => $data->getMail()]));
228 }
229 }
230 //Without admin role
231 //XXX: prefer a reset on login to force user unspam action
232 } elseif (!$this->checker->isGranted($this->config['default']['admin'])) {
233 //Add notice
234 $this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
235 }
236
237 //Render view
238 return $this->render(
239 //Template
240 $this->config['edit']['view']['name'],
241 //Context
242 ['edit' => $edit->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['edit']['view']['context']
243 );
244 }
245
246 /**
247 * Login
248 *
249 * @param Request $request The request
250 * @param AuthenticationUtils $authenticationUtils The authentication utils
251 * @param ?string $hash The hashed password
252 * @param ?string $mail The shorted mail address
253 * @return Response The response
254 */
255 public function login(Request $request, AuthenticationUtils $authenticationUtils, ?string $hash, ?string $mail): Response {
256 //Create the LoginType form and give the proper parameters
257 $login = $this->createForm($this->config['login']['view']['form'], null, [
258 //Set action to login route name and context
259 'action' => $this->generateUrl($this->config['route']['login']['name'], $this->config['route']['login']['context']),
260 //Set method
261 'method' => 'POST'
262 ]);
263
264 //Init context
265 $context = [];
266
267 //With mail
268 if (!empty($mail) && !empty($hash)) {
269 //With invalid hash
270 if ($hash != $this->slugger->hash($mail)) {
271 //Throw bad request
272 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
273 }
274
275 //Get mail
276 $mail = $this->slugger->unshort($smail = $mail);
277
278 //Without valid mail
279 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
280 //Throw bad request
281 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
282 }
283
284 //Prefilled mail
285 $login->get('mail')->setData($mail);
286 //Last username entered by the user
287 } elseif ($lastUsername = $authenticationUtils->getLastUsername()) {
288 $login->get('mail')->setData($lastUsername);
289 }
290
291 //Get the login error if there is one
292 if ($error = $authenticationUtils->getLastAuthenticationError()) {
293 //Get translated error
294 $error = $this->translator->trans($error->getMessageKey());
295
296 //Add error message to mail field
297 $login->get('mail')->addError(new FormError($error));
298
299 //Create the RecoverType form and give the proper parameters
300 $recover = $this->createForm($this->config['recover']['view']['form'], null, [
301 //Set action to recover route name and context
302 'action' => $this->generateUrl($this->config['route']['recover']['name'], $this->config['route']['recover']['context']),
303 //Without password
304 'password' => false,
305 //Set method
306 'method' => 'POST'
307 ]);
308
309 //Get recover mail entity
310 $recover->get('mail')
311 //Set mail from login form
312 ->setData($login->get('mail')->getData())
313 //Add recover error
314 ->addError(new FormError($this->translator->trans('Use this form to recover your account')));
315
316 //Add recover form to context
317 $context['recover'] = $recover->createView();
318 } else {
319 //Add notice
320 $this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
321 }
322
323 //Render view
324 return $this->render(
325 //Template
326 $this->config['login']['view']['name'],
327 //Context
328 ['login' => $login->createView(), 'disabled' => $request->query->get('disabled', 0), 'sent' => $request->query->get('sent', 0)]+$context+$this->config['login']['view']['context']
329 );
330 }
331
332 /**
333 * Recover account
334 *
335 * @param Request $request The request
336 * @param ?string $hash The hashed password
337 * @param ?string $pass The shorted password
338 * @param ?string $mail The shorted mail address
339 * @return Response The response
340 */
341 public function recover(Request $request, ?string $hash, ?string $pass, ?string $mail): Response {
342 //Set user
343 $user = null;
344
345 //Set context
346 $context = [];
347
348 //With mail, pass and hash
349 if (!empty($mail) && !empty($pass) && !empty($hash)) {
350 //With invalid hash
351 if ($hash != $this->slugger->hash($mail.$pass)) {
352 //Throw bad request
353 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
354 }
355
356 //Get mail
357 $mail = $this->slugger->unshort($smail = $mail);
358
359 //Without valid mail
360 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
361 //Throw bad request
362 //XXX: prevent slugger reverse engineering by not displaying decoded mail
363 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
364 }
365
366 //With existing subscriber
367 if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
368 //Throw not found
369 //XXX: prevent slugger reverse engineering by not displaying decoded mail
370 throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
371 }
372
373 //With unmatched pass
374 if ($pass != $this->slugger->hash($user->getPassword())) {
375 //Throw not found
376 //XXX: prevent use of outdated recover link
377 throw $this->createNotFoundException($this->translator->trans('Outdated recover link'));
378 }
379
380 //Set context
381 $context = ['mail' => $smail, 'pass' => $pass, 'hash' => $hash];
382 }
383
384 //Create the LoginType form and give the proper parameters
385 $form = $this->createForm($this->config['recover']['view']['form'], $user, [
386 //Set action to recover route name and context
387 'action' => $this->generateUrl($this->config['route']['recover']['name'], $context+$this->config['route']['recover']['context']),
388 //With user disable mail
389 'mail' => ($user === null),
390 //With user enable password
391 'password' => ($user !== null),
392 //Set method
393 'method' => 'POST'
394 ]);
395
396 //With post method
397 if ($request->isMethod('POST')) {
398 //Refill the fields in case the form is not valid.
399 $form->handleRequest($request);
400
401 //With form submitted and valid
402 if ($form->isSubmitted() && $form->isValid()) {
403 //Set data
404 $data = $form->getData();
405
406 //With user
407 if ($user !== null) {
408 //Set hashed password
409 $hashed = $this->hasher->hashPassword($user, $user->getPassword());
410
411 //Update pass
412 $pass = $this->slugger->hash($hashed);
413
414 //Set user password
415 $user->setPassword($hashed);
416
417 //Persist user
418 $this->manager->persist($user);
419
420 //Send to database
421 $this->manager->flush();
422
423 //Add notice
424 $this->addFlash('notice', $this->translator->trans('Account password updated'));
425
426 //Redirect to user login
427 return $this->redirectToRoute($this->config['route']['login']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['login']['context']);
428 //Find user by data mail
429 } elseif ($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
430 //Set context
431 $context = [
432 'recipient_mail' => $user->getMail(),
433 'recipient_name' => $user->getRecipientName()
434 ] + array_replace_recursive(
435 $this->config['context'],
436 $this->config['recover']['view']['context'],
437 $this->config['recover']['mail']['context']
438 );
439
440 //Generate each route route
441 foreach($this->config['recover']['route'] as $route => $tag) {
442 //Only process defined routes
443 if (!empty($this->config['route'][$route])) {
444 //Process for recover mail url
445 if ($route == 'recover') {
446 //Set the url in context
447 $context[$tag] = $this->router->generate(
448 $this->config['route'][$route]['name'],
449 //Prepend recover context with tag
450 [
451 'mail' => $smail = $this->slugger->short($context['recipient_mail']),
452 'pass' => $spass = $this->slugger->hash($pass = $user->getPassword()),
453 'hash' => $this->slugger->hash($smail.$spass)
454 ]+$this->config['route'][$route]['context'],
455 UrlGeneratorInterface::ABSOLUTE_URL
456 );
457 }
458 }
459 }
460
461 //Iterate on keys to translate
462 foreach($this->config['translate'] as $translate) {
463 //Extract keys
464 $keys = explode('.', $translate);
465
466 //Set current
467 $current =& $context;
468
469 //Iterate on each subkey
470 do {
471 //Skip unset translation keys
472 if (!isset($current[current($keys)])) {
473 continue(2);
474 }
475
476 //Set current to subkey
477 $current =& $current[current($keys)];
478 } while(next($keys));
479
480 //Set translation
481 $current = $this->translator->trans($current);
482
483 //Remove reference
484 unset($current);
485 }
486
487 //Translate subject
488 $context['subject'] = $subject = ucfirst(
489 $this->translator->trans(
490 $this->config['recover']['mail']['subject'],
491 $this->slugger->flatten($context, null, '.', '%', '%')
492 )
493 );
494
495 //Create message
496 $message = (new TemplatedEmail())
497 //Set sender
498 ->from(new Address($this->config['contact']['address'], $this->config['contact']['name']))
499 //Set recipient
500 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
501 ->to(new Address($context['recipient_mail'], $context['recipient_name']))
502 //Set subject
503 ->subject($context['subject'])
504
505 //Set path to twig templates
506 ->htmlTemplate($this->config['recover']['mail']['html'])
507 ->textTemplate($this->config['recover']['mail']['text'])
508
509 //Set context
510 ->context($context);
511
512 //Try sending message
513 //XXX: mail delivery may silently fail
514 try {
515 //Send message
516 $this->mailer->send($message);
517
518 //Add notice
519 $this->addFlash('notice', $this->translator->trans('Your recovery mail has been sent, to retrieve your account you must follow the recuperate link inside'));
520
521 //Add junk warning
522 $this->addFlash('warning', $this->translator->trans('If you did not receive a recovery mail, check your Spam or Junk mail folders'));
523
524 //Redirect on the same route with sent=1 to cleanup form
525 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'), 302);
526 //Catch obvious transport exception
527 } catch(TransportExceptionInterface $e) {
528 //Add error message mail unreachable
529 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to reach account')));
530 }
531 }
532 }
533 }
534
535 //Render view
536 return $this->render(
537 //Template
538 $this->config['recover']['view']['name'],
539 //Context
540 ['recover' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
541 );
542 }
543
544 /**
545 * Register an account
546 *
547 * @param Request $request The request
548 * @return Response The response
549 */
550 public function register(Request $request): Response {
551 //With mail
552 if (!empty($_POST['register']['mail'])) {
553 //Log new user infos
554 $this->logger->emergency(
555 $this->translator->trans(
556 'register: mail=%mail% locale=%locale% confirm=%confirm%',
557 [
558 '%mail%' => $postMail = $_POST['register']['mail'],
559 '%locale%' => $request->getLocale(),
560 '%confirm%' => $this->router->generate(
561 $this->config['route']['confirm']['name'],
562 //Prepend subscribe context with tag
563 [
564 'mail' => $postSmail = $this->slugger->short($postMail),
565 'hash' => $this->slugger->hash($postSmail)
566 ]+$this->config['route']['confirm']['context'],
567 UrlGeneratorInterface::ABSOLUTE_URL
568 )
569 ]
570 )
571 );
572 }
573
574 //Init reflection
575 $reflection = new \ReflectionClass($this->config['class']['user']);
576
577 //Create new user
578 $user = $reflection->newInstance('', '');
579
580 //Create the RegisterType form and give the proper parameters
581 $form = $this->createForm($this->config['register']['view']['form'], $user, [
582 //Set action to register route name and context
583 'action' => $this->generateUrl($this->config['route']['register']['name'], $this->config['route']['register']['context']),
584 //Set civility class
585 'civility_class' => $this->config['class']['civility'],
586 //Set civility default
587 'civility_default' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
588 //Set method
589 'method' => 'POST'
590 ]+($this->checker->isGranted($this->config['default']['admin'])?$this->config['register']['admin']:$this->config['register']['field']));
591
592 //With post method
593 if ($request->isMethod('POST')) {
594 //Refill the fields in case the form is not valid.
595 $form->handleRequest($request);
596
597 //With form submitted and valid
598 if ($form->isSubmitted() && $form->isValid()) {
599 //Set data
600 $data = $form->getData();
601
602 //Set password
603 $user->setPassword($this->hasher->hashPassword($user, $user->getPassword()));
604
605 //Persist user
606 $this->manager->persist($user);
607
608 //Iterate on default group
609 foreach($this->config['default']['group'] as $i => $groupTitle) {
610 //Fetch group
611 if (($group = $this->doctrine->getRepository($this->config['class']['group'])->findOneByTitle($groupTitle))) {
612 //Set default group
613 //XXX: see vendor/symfony/security-core/Role/Role.php
614 $user->addGroup($group);
615 //Group not found
616 } else {
617 //Throw exception
618 //XXX: consider missing group as fatal
619 throw new \Exception(sprintf('Group %s listed in %s.default.group[%d] not found by title', $groupTitle, RapsysUserBundle::getAlias(), $i));
620 }
621 }
622
623 //Set context
624 $context = [
625 'recipient_mail' => $user->getMail(),
626 'recipient_name' => $user->getRecipientName()
627 ] + array_replace_recursive(
628 $this->config['context'],
629 $this->config['register']['view']['context'],
630 $this->config['register']['mail']['context']
631 );
632
633 //Generate each route route
634 foreach($this->config['register']['route'] as $route => $tag) {
635 //Only process defined routes
636 if (!empty($this->config['route'][$route])) {
637 //Process for confirm mail url
638 if ($route == 'confirm') {
639 //Set the url in context
640 $context[$tag] = $this->router->generate(
641 $this->config['route'][$route]['name'],
642 //Prepend register context with tag
643 [
644 'mail' => $smail = $this->slugger->short($context['recipient_mail']),
645 'hash' => $this->slugger->hash($smail)
646 ]+$this->config['route'][$route]['context'],
647 UrlGeneratorInterface::ABSOLUTE_URL
648 );
649 }
650 }
651 }
652
653 //Iterate on keys to translate
654 foreach($this->config['translate'] as $translate) {
655 //Extract keys
656 $keys = explode('.', $translate);
657
658 //Set current
659 $current =& $context;
660
661 //Iterate on each subkey
662 do {
663 //Skip unset translation keys
664 if (!isset($current[current($keys)])) {
665 continue(2);
666 }
667
668 //Set current to subkey
669 $current =& $current[current($keys)];
670 } while(next($keys));
671
672 //Set translation
673 $current = $this->translator->trans($current);
674
675 //Remove reference
676 unset($current);
677 }
678
679 //Translate subject
680 $context['subject'] = $subject = ucfirst(
681 $this->translator->trans(
682 $this->config['register']['mail']['subject'],
683 $this->slugger->flatten($context, null, '.', '%', '%')
684 )
685 );
686
687 //Create message
688 $message = (new TemplatedEmail())
689 //Set sender
690 ->from(new Address($this->config['contact']['address'], $this->config['contact']['name']))
691 //Set recipient
692 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
693 ->to(new Address($context['recipient_mail'], $context['recipient_name']))
694 //Set subject
695 ->subject($context['subject'])
696
697 //Set path to twig templates
698 ->htmlTemplate($this->config['register']['mail']['html'])
699 ->textTemplate($this->config['register']['mail']['text'])
700
701 //Set context
702 ->context($context);
703
704 //Try saving in database
705 try {
706 //Send to database
707 $this->manager->flush();
708
709 //Add error message mail already exists
710 $this->addFlash('notice', $this->translator->trans('Your account has been created'));
711
712 //Try sending message
713 //XXX: mail delivery may silently fail
714 try {
715 //Send message
716 $this->mailer->send($message);
717
718 //Redirect on the same route with sent=1 to cleanup form
719 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
720 //Catch obvious transport exception
721 } catch(TransportExceptionInterface $e) {
722 //Add error message mail unreachable
723 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to reach account')));
724 }
725 //Catch double subscription
726 } catch (UniqueConstraintViolationException $e) {
727 //Add error message mail already exists
728 $this->addFlash('error', $this->translator->trans('Account already exists'));
729 }
730 }
731 }
732
733 //Render view
734 return $this->render(
735 //Template
736 $this->config['register']['view']['name'],
737 //Context
738 ['register' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['register']['view']['context']
739 );
740 }
741 }