]> Raphaël G. Git Repositories - userbundle/blob - Controller/DefaultController.php
Fix encodePassword renaming in hashPassword
[userbundle] / Controller / DefaultController.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 /**
26 * {@inheritdoc}
27 */
28 class DefaultController extends AbstractController {
29 /**
30 * Confirm account from mail link
31 *
32 * @param Request $request The request
33 * @param string $mail The shorted mail address
34 * @param string $hash The hashed password
35 * @return Response The response
36 */
37 public function confirm(Request $request, $mail, $hash): Response {
38 //With invalid hash
39 if ($hash != $this->slugger->hash($mail)) {
40 //Throw bad request
41 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
42 }
43
44 //Get mail
45 $mail = $this->slugger->unshort($smail = $mail);
46
47 //Without valid mail
48 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
49 //Throw bad request
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]));
52 }
53
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]));
59
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']);
62 }
63
64 //Set active
65 $user->setActive(true);
66
67 //Persist user
68 $this->manager->persist($user);
69
70 //Send to database
71 $this->manager->flush();
72
73 //Add error message mail already exists
74 $this->addFlash('notice', $this->translator->trans('Your account has been activated'));
75
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']);
78 }
79
80 /**
81 * Edit account by shorted mail
82 *
83 * @param Request $request The request
84 * @param string $mail The shorted mail address
85 * @param string $hash The hashed password
86 * @return Response The response
87 */
88 public function edit(Request $request, $mail, $hash): Response {
89 //With invalid hash
90 if ($hash != $this->slugger->hash($mail)) {
91 //Throw bad request
92 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
93 }
94
95 //Get mail
96 $mail = $this->slugger->unshort($smail = $mail);
97
98 //With existing subscriber
99 if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
100 //Throw not found
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]));
103 }
104
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]));
110 }
111
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']),
116 //Set civility class
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']),
120 //Disable mail
121 'mail' => $this->isGranted('ROLE_ADMIN'),
122 //Disable password
123 'password' => false,
124 //Set method
125 'method' => 'POST'
126 ]+$this->config['edit']['field']);
127
128 //With admin role
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']),
134 //Disable mail
135 'mail' => false,
136 //Set method
137 'method' => 'POST'
138 ]);
139
140 //With post method
141 if ($request->isMethod('POST')) {
142 //Refill the fields in case the form is not valid.
143 $reset->handleRequest($request);
144
145 //With reset submitted and valid
146 if ($reset->isSubmitted() && $reset->isValid()) {
147 //Set data
148 $data = $reset->getData();
149
150 //Set password
151 $data->setPassword($this->hasher->hashPassword($data, $data->getPassword()));
152
153 //Queue snippet save
154 $this->manager->persist($data);
155
156 //Flush to get the ids
157 $this->manager->flush();
158
159 //Add notice
160 $this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail = $data->getMail()]));
161
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']);
164 }
165 }
166
167 //Add reset view
168 $this->config['edit']['view']['context']['reset'] = $reset->createView();
169 }
170
171 //With post method
172 if ($request->isMethod('POST')) {
173 //Refill the fields in case the form is not valid.
174 $edit->handleRequest($request);
175
176 //With edit submitted and valid
177 if ($edit->isSubmitted() && $edit->isValid()) {
178 //Set data
179 $data = $edit->getData();
180
181 //Queue snippet save
182 $this->manager->persist($data);
183
184 //Try saving in database
185 try {
186 //Flush to get the ids
187 $this->manager->flush();
188
189 //Add notice
190 $this->addFlash('notice', $this->translator->trans('Account %mail% 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 //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()]));
198 }
199 }
200 //Without admin role
201 //XXX: prefer a reset on login to force user unspam action
202 } elseif (!$this->isGranted('ROLE_ADMIN')) {
203 //Add notice
204 $this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
205 }
206
207 //Render view
208 return $this->render(
209 //Template
210 $this->config['edit']['view']['name'],
211 //Context
212 ['edit' => $edit->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['edit']['view']['context']
213 );
214 }
215
216 /**
217 * Login
218 *
219 * @param Request $request The request
220 * @param AuthenticationUtils $authenticationUtils The authentication utils
221 * @param string $mail The shorted mail address
222 * @param string $hash The hashed password
223 * @return Response The response
224 */
225 public function login(Request $request, AuthenticationUtils $authenticationUtils, $mail, $hash): 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,
232 //Set method
233 'method' => 'POST'
234 ]);
235
236 //Init context
237 $context = [];
238
239 //With mail
240 if (!empty($mail) && !empty($hash)) {
241 //With invalid hash
242 if ($hash != $this->slugger->hash($mail)) {
243 //Throw bad request
244 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
245 }
246
247 //Get mail
248 $mail = $this->slugger->unshort($smail = $mail);
249
250 //Without valid mail
251 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
252 //Throw bad request
253 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
254 }
255
256 //Prefilled mail
257 $login->get('mail')->setData($mail);
258 //Last username entered by the user
259 } elseif ($lastUsername = $authenticationUtils->getLastUsername()) {
260 $login->get('mail')->setData($lastUsername);
261 }
262
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());
267
268 //Add error message to mail field
269 $login->get('mail')->addError(new FormError($error));
270
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']),
275 //Without password
276 'password' => false,
277 //Set method
278 'method' => 'POST'
279 ]);
280
281 //Get recover mail entity
282 $recover->get('mail')
283 //Set mail from login form
284 ->setData($login->get('mail')->getData())
285 //Add recover error
286 ->addError(new FormError($this->translator->trans('Use this form to recover your account')));
287
288 //Add recover form to context
289 $context['recover'] = $recover->createView();
290 } else {
291 //Add notice
292 $this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
293 }
294
295 //Render view
296 return $this->render(
297 //Template
298 $this->config['login']['view']['name'],
299 //Context
300 ['login' => $login->createView()]+$context+$this->config['login']['view']['context']
301 );
302 }
303
304 /**
305 * Recover account
306 *
307 * @param Request $request The request
308 * @param string $mail The shorted mail address
309 * @param string $pass The shorted password
310 * @param string $hash The hashed password
311 * @return Response The response
312 */
313 public function recover(Request $request, $mail, $pass, $hash): 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']),
320 //Without password
321 'password' => false,
322 //Set method
323 'method' => 'POST'
324 ]);
325
326 if ($request->isMethod('POST')) {
327 //Refill the fields in case the form is not valid.
328 $form->handleRequest($request);
329
330 if ($form->isValid()) {
331 //Set data
332 $data = $form->getData();
333
334 //Find user by data mail
335 if ($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
336 //Set mail shortcut
337 $recoverMail =& $this->config['recover']['mail'];
338
339 //Set mail
340 $mail = $this->slugger->short($user->getMail());
341
342 //Set pass
343 $pass = $this->slugger->hash($user->getPassword());
344
345 //Generate each route route
346 foreach($this->config['recover']['route'] as $route => $tag) {
347 //Only process defined routes
348 if (!empty($this->config['route'][$route])) {
349 //Process for recover mail url
350 if ($route == 'recover') {
351 //Set the url in context
352 $recoverMail['context'][$tag] = $this->router->generate(
353 $this->config['route'][$route]['name'],
354 //Prepend recover context with tag
355 [
356 'mail' => $mail,
357 'pass' => $pass,
358 'hash' => $this->slugger->hash($mail.$pass)
359 ]+$this->config['route'][$route]['context'],
360 UrlGeneratorInterface::ABSOLUTE_URL
361 );
362 }
363 }
364 }
365
366 //Set recipient_name
367 $recoverMail['context']['recipient_mail'] = $user->getMail();
368
369 //Set recipient_name
370 $recoverMail['context']['recipient_name'] = $user->getRecipientName();
371
372 //Init subject context
373 $subjectContext = $this->slugger->flatten(array_replace_recursive($this->config['recover']['view']['context'], $recoverMail['context']), null, '.', '%', '%');
374
375 //Translate subject
376 $recoverMail['subject'] = ucfirst($this->translator->trans($recoverMail['subject'], $subjectContext));
377
378 //Create message
379 $message = (new TemplatedEmail())
380 //Set sender
381 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
382 //Set recipient
383 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
384 ->to(new Address($recoverMail['context']['recipient_mail'], $recoverMail['context']['recipient_name']))
385 //Set subject
386 ->subject($recoverMail['subject'])
387
388 //Set path to twig templates
389 ->htmlTemplate($recoverMail['html'])
390 ->textTemplate($recoverMail['text'])
391
392 //Set context
393 //XXX: require recursive merge to avoid loosing subkeys
394 //['subject' => $recoverMail['subject']]+$recoverMail['context']+$this->config['recover']['view']['context']
395 ->context(array_replace_recursive($this->config['recover']['view']['context'], $recoverMail['context'], ['subject' => $recoverMail['subject']]));
396
397 //Try sending message
398 //XXX: mail delivery may silently fail
399 try {
400 //Send message
401 $this->mailer->send($message);
402
403 //Redirect on the same route with sent=1 to cleanup form
404 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
405 //Catch obvious transport exception
406 } catch(TransportExceptionInterface $e) {
407 //Add error message mail unreachable
408 $form->get('mail')->addError(new FormError($this->translator->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
409 }
410 //Accout not found
411 } else {
412 //Add error message to mail field
413 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to find account %mail%', ['%mail%' => $data['mail']])));
414 }
415 }
416 }
417
418 //Render view
419 return $this->render(
420 //Template
421 $this->config['recover']['view']['name'],
422 //Context
423 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
424 );
425 }
426
427 //With invalid hash
428 if ($hash != $this->slugger->hash($mail.$pass)) {
429 //Throw bad request
430 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
431 }
432
433 //Get mail
434 $mail = $this->slugger->unshort($smail = $mail);
435
436 //Without valid mail
437 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
438 //Throw bad request
439 //XXX: prevent slugger reverse engineering by not displaying decoded mail
440 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
441 }
442
443 //With existing subscriber
444 if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
445 //Throw not found
446 //XXX: prevent slugger reverse engineering by not displaying decoded mail
447 throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
448 }
449
450 //With unmatched pass
451 if ($pass != $this->slugger->hash($user->getPassword())) {
452 //Throw not found
453 //XXX: prevent use of outdated recover link
454 throw $this->createNotFoundException($this->translator->trans('Outdated recover link'));
455 }
456
457 //Create the LoginType form and give the proper parameters
458 $form = $this->createForm($this->config['recover']['view']['form'], $user, [
459 //Set action to recover route name and context
460 'action' => $this->generateUrl($this->config['route']['recover']['name'], ['mail' => $smail, 'pass' => $pass, 'hash' => $hash]+$this->config['route']['recover']['context']),
461 //Without mail
462 'mail' => false,
463 //Set method
464 'method' => 'POST'
465 ]);
466
467 if ($request->isMethod('POST')) {
468 //Refill the fields in case the form is not valid.
469 $form->handleRequest($request);
470
471 if ($form->isValid()) {
472 //Set data
473 $data = $form->getData();
474
475 //Set hashed password
476 $hashed = $this->hasher->hashPassword($user, $user->getPassword());
477
478 //Update pass
479 $pass = $this->slugger->hash($hashed);
480
481 //Set user password
482 $user->setPassword($hashed);
483
484 //Persist user
485 $this->manager->persist($user);
486
487 //Send to database
488 $this->manager->flush();
489
490 //Add notice
491 $this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail]));
492
493 //Redirect to user login
494 return $this->redirectToRoute($this->config['route']['login']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['login']['context']);
495 }
496 }
497
498 //Render view
499 return $this->render(
500 //Template
501 $this->config['recover']['view']['name'],
502 //Context
503 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
504 );
505 }
506
507 /**
508 * Register an account
509 *
510 * @param Request $request The request
511 * @param string $mail The shorted mail address
512 * @param string $field The serialized then shorted form field array
513 * @param string $hash The hashed serialized field array
514 * @return Response The response
515 */
516 public function register(Request $request, $mail, $field, $hash): Response {
517 //With mail
518 if (!empty($_POST['register']['mail'])) {
519 //Log new user infos
520 $this->logger->emergency(
521 $this->translator->trans(
522 'register: mail=%mail% locale=%locale% confirm=%confirm%',
523 [
524 '%mail%' => $postMail = $_POST['register']['mail'],
525 '%locale%' => $request->getLocale(),
526 '%confirm%' => $this->router->generate(
527 $this->config['route']['confirm']['name'],
528 //Prepend subscribe context with tag
529 [
530 'mail' => $postSmail = $this->slugger->short($postMail),
531 'hash' => $this->slugger->hash($postSmail)
532 ]+$this->config['route']['confirm']['context'],
533 UrlGeneratorInterface::ABSOLUTE_URL
534 )
535 ]
536 )
537 );
538 }
539
540 //With mail and field
541 if (!empty($field) && !empty($hash)) {
542 //With invalid hash
543 if ($hash != $this->slugger->hash($mail.$field)) {
544 //Throw bad request
545 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
546 }
547
548 //With mail
549 if (!empty($mail)) {
550 //Get mail
551 $mail = $this->slugger->unshort($smail = $mail);
552
553 //Without valid mail
554 if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
555 //Throw bad request
556 //XXX: prevent slugger reverse engineering by not displaying decoded mail
557 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
558 }
559
560 //With existing registrant
561 if ($existing = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail)) {
562 //With disabled existing
563 if ($existing->isDisabled()) {
564 //Render view
565 $response = $this->render(
566 //Template
567 $this->config['register']['view']['name'],
568 //Context
569 ['title' => $this->translator->trans('Access denied'), 'disabled' => 1]+$this->config['register']['view']['context']
570 );
571
572 //Set 403
573 $response->setStatusCode(403);
574
575 //Return response
576 return $response;
577 //With unactivated existing
578 } elseif (!$existing->isActivated()) {
579 //Set mail shortcut
580 $activateMail =& $this->config['register']['mail'];
581
582 //Generate each route route
583 foreach($this->config['register']['route'] as $route => $tag) {
584 //Only process defined routes
585 if (!empty($this->config['route'][$route])) {
586 //Process for confirm url
587 if ($route == 'confirm') {
588 //Set the url in context
589 $activateMail['context'][$tag] = $this->router->generate(
590 $this->config['route'][$route]['name'],
591 //Prepend subscribe context with tag
592 [
593 'mail' => $smail = $this->slugger->short($existing->getMail()),
594 'hash' => $this->slugger->hash($smail)
595 ]+$this->config['route'][$route]['context'],
596 UrlGeneratorInterface::ABSOLUTE_URL
597 );
598 }
599 }
600 }
601
602 //Set recipient_name
603 $activateMail['context']['recipient_mail'] = $existing->getMail();
604
605 //Set recipient name
606 $activateMail['context']['recipient_name'] = $existing->getRecipientName();
607
608 //Init subject context
609 $subjectContext = $this->slugger->flatten(array_replace_recursive($this->config['register']['view']['context'], $activateMail['context']), null, '.', '%', '%');
610
611 //Translate subject
612 $activateMail['subject'] = ucfirst($this->translator->trans($activateMail['subject'], $subjectContext));
613
614 //Create message
615 $message = (new TemplatedEmail())
616 //Set sender
617 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
618 //Set recipient
619 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
620 ->to(new Address($activateMail['context']['recipient_mail'], $activateMail['context']['recipient_name']))
621 //Set subject
622 ->subject($activateMail['subject'])
623
624 //Set path to twig templates
625 ->htmlTemplate($activateMail['html'])
626 ->textTemplate($activateMail['text'])
627
628 //Set context
629 ->context(['subject' => $activateMail['subject']]+$activateMail['context']);
630
631 //Try sending message
632 //XXX: mail delivery may silently fail
633 try {
634 //Send message
635 $this->mailer->send($message);
636 //Catch obvious transport exception
637 } catch(TransportExceptionInterface $e) {
638 //Add error message mail unreachable
639 $this->addFlash('error', $this->translator->trans('Account %mail% tried activate but unable to contact', ['%mail%' => $existing->getMail()]));
640 }
641
642 //Get route params
643 $routeParams = $request->get('_route_params');
644
645 //Remove mail, field and hash from route params
646 unset($routeParams['mail'], $routeParams['field'], $routeParams['hash']);
647
648 //Redirect on the same route with sent=1 to cleanup form
649 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$routeParams);
650 }
651
652 //Add error message mail already exists
653 $this->addFlash('warning', $this->translator->trans('Account %mail% already exists', ['%mail%' => $existing->getMail()]));
654
655 //Redirect to user view
656 return $this->redirectToRoute(
657 $this->config['route']['edit']['name'],
658 [
659 'mail' => $smail = $this->slugger->short($existing->getMail()),
660 'hash' => $this->slugger->hash($smail)
661 ]+$this->config['route']['edit']['context']
662 );
663 }
664 //Without mail
665 } else {
666 //Set smail
667 $smail = $mail;
668 }
669
670 //Try
671 try {
672 //Unshort then unserialize field
673 $field = $this->slugger->unserialize($sfield = $field);
674 //Catch type error
675 } catch (\Error|\Exception $e) {
676 //Throw bad request
677 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]), $e);
678 }
679
680 //With non array field
681 if (!is_array($field)) {
682 //Throw bad request
683 throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]));
684 }
685 //Without field and hash
686 } else {
687 //Set smail
688 $smail = $mail;
689
690 //Set smail
691 $sfield = $field;
692
693 //Reset field
694 $field = [];
695 }
696
697 //Init reflection
698 $reflection = new \ReflectionClass($this->config['class']['user']);
699
700 //Create new user
701 $user = $reflection->newInstance(strval($mail));
702
703 //Create the RegisterType form and give the proper parameters
704 $form = $this->createForm($this->config['register']['view']['form'], $user, $field+[
705 //Set action to register route name and context
706 'action' => $this->generateUrl($this->config['route']['register']['name'], ['mail' => $smail, 'field' => $sfield, 'hash' => $hash]+$this->config['route']['register']['context']),
707 //Set civility class
708 'civility_class' => $this->config['class']['civility'],
709 //Set civility default
710 'civility_default' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
711 //With mail
712 'mail' => true,
713 //Set method
714 'method' => 'POST'
715 ]+$this->config['register']['field']);
716
717 if ($request->isMethod('POST')) {
718 //Refill the fields in case the form is not valid.
719 $form->handleRequest($request);
720
721 if ($form->isValid()) {
722 //Set data
723 $data = $form->getData();
724
725 //With existing registrant
726 if ($this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail = $data->getMail())) {
727 //Add error message mail already exists
728 $this->addFlash('warning', $this->translator->trans('Account %mail% already exists', ['%mail%' => $mail]));
729
730 //Redirect to user view
731 return $this->redirectToRoute(
732 $this->config['route']['edit']['name'],
733 [
734 'mail' => $smail = $this->slugger->short($mail),
735 'hash' => $this->slugger->hash($smail)
736 ]+$this->config['route']['edit']['context']
737 );
738 }
739
740 //Set mail shortcut
741 $registerMail =& $this->config['register']['mail'];
742
743 //Set password
744 $user->setPassword($this->hasher->hashPassword($user, $user->getPassword()));
745
746 //Persist user
747 $this->manager->persist($user);
748
749 //Iterate on default group
750 foreach($this->config['default']['group'] as $i => $groupTitle) {
751 //Fetch group
752 if (($group = $this->doctrine->getRepository($this->config['class']['group'])->findOneByTitle($groupTitle))) {
753 //Set default group
754 //XXX: see vendor/symfony/security-core/Role/Role.php
755 $user->addGroup($group);
756 //Group not found
757 } else {
758 //Throw exception
759 //XXX: consider missing group as fatal
760 throw new \Exception(sprintf('Group from rapsys_user.default.group[%d] not found by title: %s', $i, $groupTitle));
761 }
762 }
763
764 //Generate each route route
765 foreach($this->config['register']['route'] as $route => $tag) {
766 //Only process defined routes
767 if (!empty($this->config['route'][$route])) {
768 //Process for confirm url
769 if ($route == 'confirm') {
770 //Set the url in context
771 $registerMail['context'][$tag] = $this->router->generate(
772 $this->config['route'][$route]['name'],
773 //Prepend subscribe context with tag
774 [
775 'mail' => $smail = $this->slugger->short($data->getMail()),
776 'hash' => $this->slugger->hash($smail)
777 ]+$this->config['route'][$route]['context'],
778 UrlGeneratorInterface::ABSOLUTE_URL
779 );
780 }
781 }
782 }
783
784 //Set recipient_name
785 $registerMail['context']['recipient_mail'] = $data->getMail();
786
787 //Set recipient name
788 $registerMail['context']['recipient_name'] = $data->getRecipientName();
789
790 //Init subject context
791 $subjectContext = $this->slugger->flatten(array_replace_recursive($this->config['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
792
793 //Translate subject
794 $registerMail['subject'] = ucfirst($this->translator->trans($registerMail['subject'], $subjectContext));
795
796 //Create message
797 $message = (new TemplatedEmail())
798 //Set sender
799 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
800 //Set recipient
801 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
802 ->to(new Address($registerMail['context']['recipient_mail'], $registerMail['context']['recipient_name']))
803 //Set subject
804 ->subject($registerMail['subject'])
805
806 //Set path to twig templates
807 ->htmlTemplate($registerMail['html'])
808 ->textTemplate($registerMail['text'])
809
810 //Set context
811 ->context(['subject' => $registerMail['subject']]+$registerMail['context']);
812
813 //Try saving in database
814 try {
815 //Send to database
816 $this->manager->flush();
817
818 //Add error message mail already exists
819 $this->addFlash('notice', $this->translator->trans('Your account has been created'));
820
821 //Try sending message
822 //XXX: mail delivery may silently fail
823 try {
824 //Send message
825 $this->mailer->send($message);
826
827 //Redirect on the same route with sent=1 to cleanup form
828 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
829 //Catch obvious transport exception
830 } catch(TransportExceptionInterface $e) {
831 //Add error message mail unreachable
832 $form->get('mail')->addError(new FormError($this->translator->trans('Account %mail% tried subscribe but unable to contact', ['%mail%' => $data->getMail()])));
833 }
834 //Catch double subscription
835 } catch (UniqueConstraintViolationException $e) {
836 //Add error message mail already exists
837 $this->addFlash('error', $this->translator->trans('Account %mail% already exists', ['%mail%' => $mail]));
838 }
839 }
840 }
841
842 //Render view
843 return $this->render(
844 //Template
845 $this->config['register']['view']['name'],
846 //Context
847 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['register']['view']['context']
848 );
849 }
850 }