]> Raphaƫl G. Git Repositories - userbundle/blob - Controller/DefaultController.php
Add recover form directly on login page on failure
[userbundle] / Controller / DefaultController.php
1 <?php
2
3 namespace Rapsys\UserBundle\Controller;
4
5 use Rapsys\UserBundle\Utils\Slugger;
6 use Symfony\Bridge\Twig\Mime\TemplatedEmail;
7 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
8 use Symfony\Component\DependencyInjection\ContainerInterface;
9 use Symfony\Component\Form\FormError;
10 use Symfony\Component\HttpFoundation\Request;
11 use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
12 use Symfony\Component\Mailer\MailerInterface;
13 use Symfony\Component\Mime\Address;
14 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
15 use Symfony\Component\Routing\RouterInterface;
16 use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
17 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
18 use Symfony\Component\Translation\TranslatorInterface;
19
20 class DefaultController extends AbstractController {
21 //Config array
22 protected $config;
23
24 //Translator instance
25 protected $translator;
26
27 public function __construct(ContainerInterface $container, TranslatorInterface $translator, RouterInterface $router) {
28 //Retrieve config
29 $this->config = $container->getParameter($this->getAlias());
30
31 //Set the translator
32 $this->translator = $translator;
33
34 //Get current action
35 //XXX: we don't use this as it would be too slow, maybe ???
36 #$action = str_replace(self::getAlias().'_', '', $container->get('request_stack')->getCurrentRequest()->get('_route'));
37
38 //Inject every requested route in view and mail context
39 foreach($this->config as $tag => $current) {
40 //Look for entry with route subkey
41 if (!empty($current['route'])) {
42 //Generate url for both view and mail
43 foreach(['view', 'mail'] as $view) {
44 //Check that context key is usable
45 if (isset($current[$view]['context']) && is_array($current[$view]['context'])) {
46 //Process every routes
47 foreach($current['route'] as $route => $key) {
48 //Skip recover_mail route as it requires some parameters
49 if ($route == 'recover_mail') {
50 continue;
51 }
52 //Check that key is empty
53 if (!isset($current[$view]['context'][$key])) {
54 //Generate the route
55 $this->config[$tag][$view]['context'][$key] = $router->generate(
56 $this->config['route'][$route]['name'],
57 $this->config['route'][$route]['context'],
58 //Generate absolute url for mails
59 $view=='mail'?UrlGeneratorInterface::ABSOLUTE_URL:UrlGeneratorInterface::ABSOLUTE_PATH
60 );
61 }
62 }
63 }
64 }
65 }
66 }
67 }
68
69 public function login(Request $request, AuthenticationUtils $authenticationUtils) {
70 //Create the LoginType form and give the proper parameters
71 $login = $this->createForm($this->config['login']['view']['form'], null, [
72 //Set action to login route name and context
73 'action' => $this->generateUrl($this->config['route']['login']['name'], $this->config['route']['login']['context']),
74 'method' => 'POST'
75 ]);
76
77 //Init context
78 $context = [];
79
80 //Last username entered by the user
81 if ($lastUsername = $authenticationUtils->getLastUsername()) {
82 $login->get('mail')->setData($lastUsername);
83 }
84
85 //Get the login error if there is one
86 if ($error = $authenticationUtils->getLastAuthenticationError()) {
87 //Get translated error
88 $error = $this->translator->trans($error->getMessageKey());
89
90 //Add error message to mail field
91 $login->get('mail')->addError(new FormError($error));
92
93 //Create the RecoverType form and give the proper parameters
94 $recover = $this->createForm($this->config['recover']['view']['form'], null, [
95 //Set action to recover route name and context
96 'action' => $this->generateUrl($this->config['route']['recover']['name'], $this->config['route']['recover']['context']),
97 'method' => 'POST'
98 ]);
99
100 //Set mail from login form
101 $recover->get('mail')->setData($login->get('mail')->getData());
102
103 //Add recover error
104 $recover->addError(new FormError(
105 $this->translator->trans('Use this form to recover your account')
106 ));
107
108 //Add recover form to context
109 $context['recover'] = $recover->createView();
110 }
111
112 //Render view
113 return $this->render(
114 //Template
115 $this->config['login']['view']['name'],
116 //Context
117 ['login' => $login->createView()]+$context+$this->config['login']['view']['context']
118 );
119 }
120
121 public function recover(Request $request, Slugger $slugger, MailerInterface $mailer) {
122 //Create the RecoverType form and give the proper parameters
123 $form = $this->createForm($this->config['recover']['view']['form'], null, array(
124 //Set action to recover route name and context
125 'action' => $this->generateUrl($this->config['route']['recover']['name'], $this->config['route']['recover']['context']),
126 'method' => 'POST'
127 ));
128
129 if ($request->isMethod('POST')) {
130 //Refill the fields in case the form is not valid.
131 $form->handleRequest($request);
132
133 if ($form->isValid()) {
134 //Get doctrine
135 $doctrine = $this->getDoctrine();
136
137 //Set data
138 $data = $form->getData();
139
140 //Try to find user
141 if ($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
142 //Set mail shortcut
143 $mail =& $this->config['recover']['mail'];
144
145 //Generate each route route
146 foreach($this->config['recover']['route'] as $route => $tag) {
147 //Only process defined routes
148 if (empty($mail['context'][$tag]) && !empty($this->config['route'][$route])) {
149 //Process for recover mail url
150 if ($route == 'recover_mail') {
151 //Prepend recover context with tag
152 $this->config['route'][$route]['context'] = [
153 'recipient' => $slugger->short($user->getMail()),
154 'hash' => $slugger->hash($user->getPassword())
155 ]+$this->config['route'][$route]['context'];
156 }
157 //Set the url in context
158 $mail['context'][$tag] = $this->get('router')->generate(
159 $this->config['route'][$route]['name'],
160 $this->config['route'][$route]['context'],
161 UrlGeneratorInterface::ABSOLUTE_URL
162 );
163
164 }
165 }
166
167 //Set recipient_name
168 $mail['context']['recipient_mail'] = $data['mail'];
169
170 //Set recipient_name
171 $mail['context']['recipient_name'] = trim($user->getForename().' '.$user->getSurname().($user->getPseudonym()?' ('.$user->getPseudonym().')':''));
172
173 //Init subject context
174 $subjectContext = [];
175
176 //Process each context pair
177 foreach($mail['context']+$this->config['recover']['view']['context'] as $k => $v) {
178 //Reinsert each context pair with the key surrounded by %
179 $subjectContext['%'.$k.'%'] = $v;
180 }
181
182 //Translate subject
183 $mail['subject'] = ucfirst($this->translator->trans($mail['subject'], $subjectContext));
184
185 //Create message
186 $message = (new TemplatedEmail())
187 //Set sender
188 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['name']))
189 //Set recipient
190 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
191 ->to(new Address($mail['context']['recipient_mail'], $mail['context']['recipient_name']))
192 //Set subject
193 ->subject($mail['subject'])
194
195 //Set path to twig templates
196 ->htmlTemplate($mail['html'])
197 ->textTemplate($mail['text'])
198
199 //Set context
200 ->context(['subject' => $mail['subject']]+$mail['context']+$this->config['recover']['view']['context']);
201
202 //Try sending message
203 //XXX: mail delivery may silently fail
204 try {
205 //Send message
206 $mailer->send($message);
207
208 //Redirect on the same route with sent=1 to cleanup form
209 #return $this->redirectToRoute('rapsys_user_register', array('sent' => 1));
210 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
211 //Catch obvious transport exception
212 } catch(TransportExceptionInterface $e) {
213 //Add error message mail unreachable
214 $form->get('mail')->addError(new FormError($this->translator->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
215 }
216 //Accout not found
217 } else {
218 //Add error message to mail field
219 $form->get('mail')->addError(new FormError($this->translator->trans('Unable to find account: %mail%', ['%mail%' => $data['mail']])));
220 }
221 }
222 }
223
224 //Render view
225 return $this->render(
226 //Template
227 $this->config['recover']['view']['name'],
228 //Context
229 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
230 );
231 }
232
233 public function recoverMail(Request $request, UserPasswordEncoderInterface $encoder, Slugger $slugger, MailerInterface $mailer, $recipient, $hash) {
234 //Create the RecoverType form and give the proper parameters
235 $form = $this->createForm($this->config['recover_mail']['view']['form'], null, array(
236 //Set action to recover route name and context
237 'action' => $this->generateUrl($this->config['route']['recover_mail']['name'], ['recipient' => $recipient, 'hash' => $hash]+$this->config['route']['recover_mail']['context']),
238 'method' => 'POST'
239 ));
240
241 //Get doctrine
242 $doctrine = $this->getDoctrine();
243
244 //Init found
245 $found = false;
246
247 //Retrieve user
248 if (($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($slugger->unshort($recipient))) && $found = ($hash == $slugger->hash($user->getPassword()))) {
249 if ($request->isMethod('POST')) {
250 //Refill the fields in case the form is not valid.
251 $form->handleRequest($request);
252
253 if ($form->isValid()) {
254 //Set data
255 $data = $form->getData();
256
257 //set encoded password
258 $encoded = $encoder->encodePassword($user, $data['password']);
259
260 //Set user password
261 $user->setPassword($encoded);
262
263 //Get manager
264 $manager = $doctrine->getManager();
265
266 //Persist user
267 $manager->persist($user);
268
269 //Send to database
270 $manager->flush();
271
272 //Set mail shortcut
273 $mail =& $this->config['recover_mail']['mail'];
274
275 //Regen hash
276 $hash = $slugger->hash($encoded);
277
278 //Generate each route route
279 foreach($this->config['recover_mail']['route'] as $route => $tag) {
280 //Only process defined routes
281 if (empty($mail['context'][$tag]) && !empty($this->config['route'][$route])) {
282 //Process for recover mail url
283 if ($route == 'recover_mail') {
284 //Prepend recover context with tag
285 $this->config['route'][$route]['context'] = [
286 'recipient' => $recipient,
287 'hash' => $hash
288 ]+$this->config['route'][$route]['context'];
289 }
290 //Set the url in context
291 $mail['context'][$tag] = $this->get('router')->generate(
292 $this->config['route'][$route]['name'],
293 $this->config['route'][$route]['context'],
294 UrlGeneratorInterface::ABSOLUTE_URL
295 );
296 }
297 }
298
299 //Set recipient_name
300 $mail['context']['recipient_mail'] = $user->getMail();
301
302 //Set recipient_name
303 $mail['context']['recipient_name'] = trim($user->getForename().' '.$user->getSurname().($user->getPseudonym()?' ('.$user->getPseudonym().')':''));
304
305 //Init subject context
306 $subjectContext = [];
307
308 //Process each context pair
309 foreach($mail['context']+$this->config['recover_mail']['view']['context'] as $k => $v) {
310 //Reinsert each context pair with the key surrounded by %
311 $subjectContext['%'.$k.'%'] = $v;
312 }
313
314 //Translate subject
315 $mail['subject'] = ucfirst($this->translator->trans($mail['subject'], $subjectContext));
316
317 //Create message
318 $message = (new TemplatedEmail())
319 //Set sender
320 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['name']))
321 //Set recipient
322 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
323 ->to(new Address($mail['context']['recipient_mail'], $mail['context']['recipient_name']))
324 //Set subject
325 ->subject($mail['subject'])
326
327 //Set path to twig templates
328 ->htmlTemplate($mail['html'])
329 ->textTemplate($mail['text'])
330
331 //Set context
332 ->context(['subject' => $mail['subject']]+$mail['context']+$this->config['recover_mail']['view']['context']);
333
334 //Try sending message
335 //XXX: mail delivery may silently fail
336 try {
337 //Send message
338 $mailer->send($message);
339
340 //Redirect on the same route with sent=1 to cleanup form
341 return $this->redirectToRoute($request->get('_route'), ['recipient' => $recipient, 'hash' => $hash, 'sent' => 1]+$request->get('_route_params'));
342 //Catch obvious transport exception
343 } catch(TransportExceptionInterface $e) {
344 //Add error message mail unreachable
345 $form->get('password')->get('first')->addError(new FormError($this->translator->trans('Account password updated but unable to contact: %mail%', array('%mail%' => $mail['context']['recipient_mail']))));
346 }
347 }
348 }
349 //Accout not found
350 } else {
351 //Add error message to mail field
352 $form->addError(new FormError($this->translator->trans('Unable to find account: %mail%', ['%mail%' => $slugger->unshort($recipient)])));
353 }
354
355 //Render view
356 return $this->render(
357 //Template
358 $this->config['recover_mail']['view']['name'],
359 //Context
360 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0), 'found' => $found]+$this->config['recover_mail']['view']['context']
361 );
362 }
363
364 public function register(Request $request, UserPasswordEncoderInterface $encoder, MailerInterface $mailer) {
365 //Create the RegisterType form and give the proper parameters
366 $form = $this->createForm($this->config['register']['view']['form'], null, array(
367 'class_title' => $this->config['class']['title'],
368 //Set action to register route name and context
369 'action' => $this->generateUrl($this->config['route']['register']['name'], $this->config['route']['register']['context']),
370 'method' => 'POST'
371 ));
372
373 if ($request->isMethod('POST')) {
374 //Refill the fields in case the form is not valid.
375 $form->handleRequest($request);
376
377 if ($form->isValid()) {
378 //Set data
379 $data = $form->getData();
380
381 //Set mail shortcut
382 $mail =& $this->config['register']['mail'];
383
384 //Generate each route route
385 foreach($this->config['register']['route'] as $route => $tag) {
386 if (empty($mail['context'][$tag]) && !empty($this->config['route'][$route])) {
387 $mail['context'][$tag] = $this->get('router')->generate(
388 $this->config['route'][$route]['name'],
389 $this->config['route'][$route]['context'],
390 UrlGeneratorInterface::ABSOLUTE_URL
391 );
392 }
393 }
394
395 //Set recipient_name
396 $mail['context']['recipient_mail'] = $data['mail'];
397
398 //Set recipient_name
399 $mail['context']['recipient_name'] = trim($data['forename'].' '.$data['surname'].($data['pseudonym']?' ('.$data['pseudonym'].')':''));
400
401 //Init subject context
402 $subjectContext = [];
403
404 //Process each context pair
405 foreach($mail['context']+$this->config['register']['view']['context'] as $k => $v) {
406 //Reinsert each context pair with the key surrounded by %
407 $subjectContext['%'.$k.'%'] = $v;
408 }
409
410 //Translate subject
411 $mail['subject'] = ucfirst($this->translator->trans($mail['subject'], $subjectContext));
412
413 //Create message
414 $message = (new TemplatedEmail())
415 //Set sender
416 ->from(new Address($this->config['contact']['mail'], $this->config['contact']['name']))
417 //Set recipient
418 //XXX: remove the debug set in vendor/symfony/mime/Address.php +46
419 ->to(new Address($mail['context']['recipient_mail'], $mail['context']['recipient_name']))
420 //Set subject
421 ->subject($mail['subject'])
422
423 //Set path to twig templates
424 ->htmlTemplate($mail['html'])
425 ->textTemplate($mail['text'])
426
427 //Set context
428 ->context(['subject' => $mail['subject']]+$mail['context']+$this->config['register']['view']['context']);
429
430 //Get doctrine
431 $doctrine = $this->getDoctrine();
432
433 //Get manager
434 $manager = $doctrine->getManager();
435
436 //Init reflection
437 $reflection = new \ReflectionClass($this->config['class']['user']);
438
439 //Create new user
440 $user = $reflection->newInstance();
441
442 $user->setMail($data['mail']);
443 $user->setPseudonym($data['pseudonym']);
444 $user->setForename($data['forename']);
445 $user->setSurname($data['surname']);
446 $user->setPhone($data['phone']);
447 $user->setPassword($encoder->encodePassword($user, $data['password']));
448 $user->setActive(true);
449 $user->setTitle($data['title']);
450
451 //XXX: For now there is no point in setting a role at subscription
452 //TODO: see if we can't modify group constructor to set role directly from args
453 //XXX: see vendor/symfony/symfony/src/Symfony/Component/Security/Core/Role/Role.php
454 #$user->addGroup($doctrine->getRepository($this->config['class']['group'])->findOneByRole('ROLE_USER'));
455
456 $user->setCreated(new \DateTime('now'));
457 $user->setUpdated(new \DateTime('now'));
458
459 //Persist user
460 $manager->persist($user);
461
462 //Try saving in database
463 try {
464 //Send to database
465 $manager->flush();
466
467 //Try sending message
468 //XXX: mail delivery may silently fail
469 try {
470 //Send message
471 $mailer->send($message);
472
473 //Redirect on the same route with sent=1 to cleanup form
474 #return $this->redirectToRoute('rapsys_user_register', array('sent' => 1));
475 return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
476 //Catch obvious transport exception
477 } catch(TransportExceptionInterface $e) {
478 //Add error message mail unreachable
479 $form->get('mail')->addError(new FormError($this->translator->trans('Account created but unable to contact: %mail%', array('%mail%' => $data['mail']))));
480 }
481 //Catch double subscription
482 } catch (\Doctrine\DBAL\Exception\UniqueConstraintViolationException $e) {
483 //Add error message mail already exists
484 $form->get('mail')->addError(new FormError($this->translator->trans('Account already exists: %mail%', ['%mail%' => $data['mail']])));
485 }
486 }
487 }
488
489 //Render view
490 return $this->render(
491 //Template
492 $this->config['register']['view']['name'],
493 //Context
494 ['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['register']['view']['context']
495 );
496 }
497
498 /**
499 * {@inheritdoc}
500 */
501 public function getAlias() {
502 return 'rapsys_user';
503 }
504 }