X-Git-Url: https://git.rapsys.eu/userbundle/blobdiff_plain/763dfb9aac8018366127cbf61415a9546774ef00..b3be5f5ecb45a1120b7821b4e5cf86dfc514a7c9:/Controller/DefaultController.php

diff --git a/Controller/DefaultController.php b/Controller/DefaultController.php
index c8bb8a5..1e6a739 100644
--- a/Controller/DefaultController.php
+++ b/Controller/DefaultController.php
@@ -11,51 +11,74 @@
 
 namespace Rapsys\UserBundle\Controller;
 
-use Doctrine\Bundle\DoctrineBundle\Registry;
-use Doctrine\ORM\EntityManagerInterface;
 use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
-use Psr\Log\LoggerInterface;
 use Symfony\Bridge\Twig\Mime\TemplatedEmail;
 use Symfony\Component\Form\FormError;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
-use Symfony\Component\Mailer\MailerInterface;
 use Symfony\Component\Mime\Address;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
-use Symfony\Component\Routing\RouterInterface;
-use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
 
-use Rapsys\PackBundle\Util\SluggerUtil;
+use Rapsys\UserBundle\RapsysUserBundle;
 
 /**
  * {@inheritdoc}
  */
 class DefaultController extends AbstractController {
+	/**
+	 * User index
+	 *
+	 * @param Request $request The request
+	 * @return Response The response
+	 */
+	public function index(Request $request): Response {
+		//Without admin
+		if (!$this->checker->isGranted($this->config['default']['admin'])) {
+			//Throw 403
+			throw $this->createAccessDeniedException($this->translator->trans('Unable to list users'));
+		}
+
+		//Get count
+		$this->context['count'] = $this->doctrine->getRepository($this->config['class']['user'])->findCountAsInt();
+
+		//With not enough users
+		if ($this->context['count'] - $this->page * $this->limit < 0) {
+			//Throw 404
+			throw $this->createNotFoundException($this->translator->trans('Unable to find users'));
+		}
+
+		//Get users
+		$this->context['users'] = $this->doctrine->getRepository($this->config['class']['user'])->findAllAsArray($this->page, $this->limit);
+
+		//Render view
+		return $this->render(
+			//Template
+			$this->config['index']['view']['name'],
+			//Context
+			$this->context+$this->config['index']['view']['context']
+		);
+	}
+
 	/**
 	 * Confirm account from mail link
 	 *
 	 * @param Request $request The request
-	 * @param Registry $manager The doctrine registry
-	 * @param UserPasswordEncoderInterface $encoder The password encoder
-	 * @param EntityManagerInterface $manager The doctrine entity manager
-	 * @param SluggerUtil $slugger The slugger
-	 * @param MailerInterface $mailer The mailer
-	 * @param string $mail The shorted mail address
 	 * @param string $hash The hashed password
+	 * @param string $mail The shorted mail address
 	 * @return Response The response
 	 */
-	public function confirm(Request $request, Registry $doctrine, UserPasswordEncoderInterface $encoder, EntityManagerInterface $manager, SluggerUtil $slugger, MailerInterface $mailer, $mail, $hash): Response {
+	public function confirm(Request $request, string $hash, string $mail): Response {
 		//With invalid hash
-		if ($hash != $slugger->hash($mail)) {
+		if ($hash != $this->slugger->hash($mail)) {
 			//Throw bad request
 			throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
 		}
 
 		//Get mail
-		$mail = $slugger->unshort($smail = $mail);
+		$mail = $this->slugger->unshort($smail = $mail);
 
 		//Without valid mail
 		if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
@@ -65,93 +88,81 @@ class DefaultController extends AbstractController {
 		}
 
 		//Without existing registrant
-		if (!($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
+		if (!($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
 			//Add error message mail already exists
 			//XXX: prevent slugger reverse engineering by not displaying decoded mail
 			$this->addFlash('error', $this->translator->trans('Account %mail% do not exists', ['%mail%' => $smail]));
 
 			//Redirect to register view
-			return $this->redirectToRoute($this->config['route']['register']['name'], ['mail' => $smail, 'field' => $sfield = $slugger->serialize([]), 'hash' => $slugger->hash($smail.$sfield)]+$this->config['route']['register']['context']);
+			return $this->redirectToRoute($this->config['route']['register']['name'], $this->config['route']['register']['context']);
 		}
 
 		//Set active
 		$user->setActive(true);
 
 		//Persist user
-		$manager->persist($user);
+		$this->manager->persist($user);
 
 		//Send to database
-		$manager->flush();
+		$this->manager->flush();
 
 		//Add error message mail already exists
 		$this->addFlash('notice', $this->translator->trans('Your account has been activated'));
 
 		//Redirect to user view
-		return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+$this->config['route']['edit']['context']);
+		return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']);
 	}
 
 	/**
 	 * Edit account by shorted mail
 	 *
 	 * @param Request $request The request
-	 * @param Registry $manager The doctrine registry
-	 * @param UserPasswordEncoderInterface $encoder The password encoder
-	 * @param EntityManagerInterface $manager The doctrine entity manager
-	 * @param SluggerUtil $slugger The slugger
-	 * @param string $mail The shorted mail address
 	 * @param string $hash The hashed password
+	 * @param string $mail The shorted mail address
 	 * @return Response The response
 	 */
-	public function edit(Request $request, Registry $doctrine, UserPasswordEncoderInterface $encoder, EntityManagerInterface $manager, SluggerUtil $slugger, $mail, $hash): Response {
+	public function edit(Request $request, string $hash, string $mail): Response {
 		//With invalid hash
-		if ($hash != $slugger->hash($mail)) {
+		if ($hash != $this->slugger->hash($mail)) {
 			//Throw bad request
 			throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
 		}
 
 		//Get mail
-		$mail = $slugger->unshort($smail = $mail);
+		$mail = $this->slugger->unshort($smail = $mail);
 
 		//With existing subscriber
-		if (empty($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
+		if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
 			//Throw not found
 			//XXX: prevent slugger reverse engineering by not displaying decoded mail
 			throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
 		}
 
 		//Prevent access when not admin, user is not guest and not currently logged user
-		if (!$this->isGranted('ROLE_ADMIN') && $user != $this->getUser() || !$this->isGranted('IS_AUTHENTICATED_FULLY')) {
+		if (!$this->checker->isGranted($this->config['default']['admin']) && $user != $this->security->getUser() || !$this->checker->isGranted('IS_AUTHENTICATED_FULLY')) {
 			//Throw access denied
 			//XXX: prevent slugger reverse engineering by not displaying decoded mail
 			throw $this->createAccessDeniedException($this->translator->trans('Unable to access user: %mail%', ['%mail%' => $smail]));
 		}
 
-		//Create the RegisterType form and give the proper parameters
+		//Create the EditType form and give the proper parameters
 		$edit = $this->createForm($this->config['edit']['view']['edit'], $user, [
-			//Set action to register route name and context
-			'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+$this->config['route']['edit']['context']),
+			//Set action to edit route name and context
+			'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']),
 			//Set civility class
 			'civility_class' => $this->config['class']['civility'],
 			//Set civility default
-			'civility_default' => $doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
-			//Disable mail
-			'mail' => $this->isGranted('ROLE_ADMIN'),
-			//Disable slug
-			'slug' => $this->isGranted('ROLE_ADMIN'),
-			//Disable password
-			'password' => false,
+			'civility_default' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
 			//Set method
 			'method' => 'POST'
-		]);
+		]+($this->checker->isGranted($this->config['default']['admin'])?$this->config['edit']['admin']:$this->config['edit']['field']));
 
 		//With admin role
-		if ($this->isGranted('ROLE_ADMIN')) {
-			//Create the LoginType form and give the proper parameters
+		if ($this->checker->isGranted($this->config['default']['admin'])) {
+			//Create the EditType form and give the proper parameters
 			$reset = $this->createForm($this->config['edit']['view']['reset'], $user, [
-				//Set action to register route name and context
-				'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+$this->config['route']['edit']['context']),
-				//Disable mail
-				'mail' => false,
+				//Set action to edit route name and context
+				'action' => $this->generateUrl($this->config['route']['edit']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']),
 				//Set method
 				'method' => 'POST'
 			]);
@@ -167,29 +178,24 @@ class DefaultController extends AbstractController {
 					$data = $reset->getData();
 
 					//Set password
-					$data->setPassword($encoder->encodePassword($data, $data->getPassword()));
+					$data->setPassword($this->hasher->hashPassword($data, $data->getPassword()));
 
 					//Queue snippet save
-					$manager->persist($data);
+					$this->manager->persist($data);
 
 					//Flush to get the ids
-					$manager->flush();
+					$this->manager->flush();
 
 					//Add notice
 					$this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail = $data->getMail()]));
 
 					//Redirect to cleanup the form
-					return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail = $slugger->short($mail), 'hash' => $slugger->hash($smail)]+$this->config['route']['edit']['context']);
+					return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail = $this->slugger->short($mail), 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']);
 				}
 			}
 
 			//Add reset view
 			$this->config['edit']['view']['context']['reset'] = $reset->createView();
-		//Without admin role
-		//XXX: prefer a reset on login to force user unspam action
-		} else {
-			//Add notice
-			$this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
 		}
 
 		//With post method
@@ -202,40 +208,30 @@ class DefaultController extends AbstractController {
 				//Set data
 				$data = $edit->getData();
 
-				//Set slug
-				$slug = null;
-
-				//With admin
-				if ($this->isGranted('ROLE_ADMIN')) {
-					//With slug
-					if (!empty($data->getSlug())) {
-						//Set slug
-						$slug = $slugger->slug($data->getPseudonym());
-					}
-
-					//Update slug
-					$data->setSlug($slug);
-				}
-
 				//Queue snippet save
-				$manager->persist($data);
+				$this->manager->persist($data);
 
 				//Try saving in database
 				try {
 					//Flush to get the ids
-					$manager->flush();
+					$this->manager->flush();
 
 					//Add notice
 					$this->addFlash('notice', $this->translator->trans('Account %mail% updated', ['%mail%' => $mail = $data->getMail()]));
 
 					//Redirect to cleanup the form
-					return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail = $slugger->short($mail), 'hash' => $slugger->hash($smail)]+$this->config['route']['edit']['context']);
+					return $this->redirectToRoute($this->config['route']['edit']['name'], ['mail' => $smail = $this->slugger->short($mail), 'hash' => $this->slugger->hash($smail)]+$this->config['route']['edit']['context']);
 				//Catch double slug or mail
 				} catch (UniqueConstraintViolationException $e) {
 					//Add error message mail already exists
-					$this->addFlash('error', $this->translator->trans('Account %mail% or with slug %slug% already exists', ['%mail%' => $data->getMail(), '%slug%' => $slug]));
+					$this->addFlash('error', $this->translator->trans('Account %mail% already exists', ['%mail%' => $data->getMail()]));
 				}
 			}
+		//Without admin role
+		//XXX: prefer a reset on login to force user unspam action
+		} elseif (!$this->checker->isGranted($this->config['default']['admin'])) {
+			//Add notice
+			$this->addFlash('notice', $this->translator->trans('To change your password login with your mail and any password then follow the procedure'));
 		}
 
 		//Render view
@@ -252,19 +248,15 @@ class DefaultController extends AbstractController {
 	 *
 	 * @param Request $request The request
 	 * @param AuthenticationUtils $authenticationUtils The authentication utils
-	 * @param RouterInterface $router The router instance
-	 * @param SluggerUtil $slugger The slugger
-	 * @param string $mail The shorted mail address
-	 * @param string $hash The hashed password
+	 * @param ?string $hash The hashed password
+	 * @param ?string $mail The shorted mail address
 	 * @return Response The response
 	 */
-	public function login(Request $request, AuthenticationUtils $authenticationUtils, RouterInterface $router, SluggerUtil $slugger, $mail, $hash): Response {
+	public function login(Request $request, AuthenticationUtils $authenticationUtils, ?string $hash, ?string $mail): Response {
 		//Create the LoginType form and give the proper parameters
 		$login = $this->createForm($this->config['login']['view']['form'], null, [
 			//Set action to login route name and context
 			'action' => $this->generateUrl($this->config['route']['login']['name'], $this->config['route']['login']['context']),
-			//Disable repeated password
-			'password_repeated' => false,
 			//Set method
 			'method' => 'POST'
 		]);
@@ -275,13 +267,13 @@ class DefaultController extends AbstractController {
 		//With mail
 		if (!empty($mail) && !empty($hash)) {
 			//With invalid hash
-			if ($hash != $slugger->hash($mail)) {
+			if ($hash != $this->slugger->hash($mail)) {
 				//Throw bad request
 				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
 			}
 
 			//Get mail
-			$mail = $slugger->unshort($smail = $mail);
+			$mail = $this->slugger->unshort($smail = $mail);
 
 			//Without valid mail
 			if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
@@ -304,7 +296,7 @@ class DefaultController extends AbstractController {
 			//Add error message to mail field
 			$login->get('mail')->addError(new FormError($error));
 
-			//Create the LoginType form and give the proper parameters
+			//Create the RecoverType form and give the proper parameters
 			$recover = $this->createForm($this->config['recover']['view']['form'], null, [
 				//Set action to recover route name and context
 				'action' => $this->generateUrl($this->config['route']['recover']['name'], $this->config['route']['recover']['context']),
@@ -333,7 +325,7 @@ class DefaultController extends AbstractController {
 			//Template
 			$this->config['login']['view']['name'],
 			//Context
-			['login' => $login->createView()]+$context+$this->config['login']['view']['context']
+			['login' => $login->createView(), 'disabled' => $request->query->get('disabled', 0), 'sent' => $request->query->get('sent', 0)]+$context+$this->config['login']['view']['context']
 		);
 	}
 
@@ -341,198 +333,202 @@ class DefaultController extends AbstractController {
 	 * Recover account
 	 *
 	 * @param Request $request The request
-	 * @param Registry $manager The doctrine registry
-	 * @param UserPasswordEncoderInterface $encoder The password encoder
-	 * @param EntityManagerInterface $manager The doctrine entity manager
-	 * @param SluggerUtil $slugger The slugger
-	 * @param MailerInterface $mailer The mailer
-	 * @param string $mail The shorted mail address
-	 * @param string $pass The shorted password
-	 * @param string $hash The hashed password
+	 * @param ?string $hash The hashed password
+	 * @param ?string $pass The shorted password
+	 * @param ?string $mail The shorted mail address
 	 * @return Response The response
 	 */
-	public function recover(Request $request, Registry $doctrine, UserPasswordEncoderInterface $encoder, EntityManagerInterface $manager, SluggerUtil $slugger, MailerInterface $mailer, $mail, $pass, $hash): Response {
-		//Without mail, pass and hash
-		if (empty($mail) && empty($pass) && empty($hash)) {
-			//Create the LoginType form and give the proper parameters
-			$form = $this->createForm($this->config['recover']['view']['form'], null, [
-				//Set action to recover route name and context
-				'action' => $this->generateUrl($this->config['route']['recover']['name'], $this->config['route']['recover']['context']),
-				//Without password
-				'password' => false,
-				//Set method
-				'method' => 'POST'
-			]);
-
-			if ($request->isMethod('POST')) {
-				//Refill the fields in case the form is not valid.
-				$form->handleRequest($request);
+	public function recover(Request $request, ?string $hash, ?string $pass, ?string $mail): Response {
+		//Set user
+		$user = null;
 
-				if ($form->isValid()) {
-					//Set data
-					$data = $form->getData();
-
-					//Find user by data mail
-					if ($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
-						//Set mail shortcut
-						$recoverMail =& $this->config['recover']['mail'];
-
-						//Set mail
-						$mail = $slugger->short($user->getMail());
-
-						//Set pass
-						$pass = $slugger->hash($user->getPassword());
-
-						//Generate each route route
-						foreach($this->config['recover']['route'] as $route => $tag) {
-							//Only process defined routes
-							if (!empty($this->config['route'][$route])) {
-								//Process for recover mail url
-								if ($route == 'recover') {
-									//Set the url in context
-									$recoverMail['context'][$tag] = $this->get('router')->generate(
-										$this->config['route'][$route]['name'],
-										//Prepend recover context with tag
-										[
-											'mail' => $mail,
-											'pass' => $pass,
-											'hash' => $slugger->hash($mail.$pass)
-										]+$this->config['route'][$route]['context'],
-										UrlGeneratorInterface::ABSOLUTE_URL
-									);
-								}
-							}
-						}
+		//Set context
+		$context = [];
 
-						//Set recipient_name
-						$recoverMail['context']['recipient_mail'] = $user->getMail();
-
-						//Set recipient_name
-						$recoverMail['context']['recipient_name'] = trim($user->getForename().' '.$user->getSurname().($user->getPseudonym()?' ('.$user->getPseudonym().')':''));
-
-						//Init subject context
-						$subjectContext = $slugger->flatten(array_replace_recursive($this->config['recover']['view']['context'], $recoverMail['context']), null, '.', '%', '%');
-
-						//Translate subject
-						$recoverMail['subject'] = ucfirst($this->translator->trans($recoverMail['subject'], $subjectContext));
-
-						//Create message
-						$message = (new TemplatedEmail())
-							//Set sender
-							->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
-							//Set recipient
-							//XXX: remove the debug set in vendor/symfony/mime/Address.php +46
-							->to(new Address($recoverMail['context']['recipient_mail'], $recoverMail['context']['recipient_name']))
-							//Set subject
-							->subject($recoverMail['subject'])
-
-							//Set path to twig templates
-							->htmlTemplate($recoverMail['html'])
-							->textTemplate($recoverMail['text'])
-
-							//Set context
-							//XXX: require recursive merge to avoid loosing subkeys
-							//['subject' => $recoverMail['subject']]+$recoverMail['context']+$this->config['recover']['view']['context']
-							->context(array_replace_recursive($this->config['recover']['view']['context'], $recoverMail['context'], ['subject' => $recoverMail['subject']]));
-
-						//Try sending message
-						//XXX: mail delivery may silently fail
-						try {
-							//Send message
-							$mailer->send($message);
-
-							//Redirect on the same route with sent=1 to cleanup form
-							return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
-						//Catch obvious transport exception
-						} catch(TransportExceptionInterface $e) {
-							//Add error message mail unreachable
-							$form->get('mail')->addError(new FormError($this->translator->trans('Account found but unable to contact: %mail%', array('%mail%' => $data['mail']))));
-						}
-					//Accout not found
-					} else {
-						//Add error message to mail field
-						$form->get('mail')->addError(new FormError($this->translator->trans('Unable to find account %mail%', ['%mail%' => $data['mail']])));
-					}
-				}
+		//With mail, pass and hash
+		if (!empty($mail) && !empty($pass) && !empty($hash)) {
+			//With invalid hash
+			if ($hash != $this->slugger->hash($mail.$pass)) {
+				//Throw bad request
+				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
 			}
 
-			//Render view
-			return $this->render(
-				//Template
-				$this->config['recover']['view']['name'],
-				//Context
-				['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
-			);
-		}
+			//Get mail
+			$mail = $this->slugger->unshort($smail = $mail);
 
-		//With invalid hash
-		if ($hash != $slugger->hash($mail.$pass)) {
-			//Throw bad request
-			throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
-		}
+			//Without valid mail
+			if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
+				//Throw bad request
+				//XXX: prevent slugger reverse engineering by not displaying decoded mail
+				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
+			}
 
-		//Get mail
-		$mail = $slugger->unshort($smail = $mail);
+			//With existing subscriber
+			if (empty($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
+				//Throw not found
+				//XXX: prevent slugger reverse engineering by not displaying decoded mail
+				throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
+			}
 
-		//Without valid mail
-		if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
-			//Throw bad request
-			//XXX: prevent slugger reverse engineering by not displaying decoded mail
-			throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
-		}
+			//With unmatched pass
+			if ($pass != $this->slugger->hash($user->getPassword())) {
+				//Throw not found
+				//XXX: prevent use of outdated recover link
+				throw $this->createNotFoundException($this->translator->trans('Outdated recover link'));
+			}
 
-		//With existing subscriber
-		if (empty($user = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail))) {
-			//Throw not found
-			//XXX: prevent slugger reverse engineering by not displaying decoded mail
-			throw $this->createNotFoundException($this->translator->trans('Unable to find account %mail%', ['%mail%' => $smail]));
-		}
-
-		//With unmatched pass
-		if ($pass != $slugger->hash($user->getPassword())) {
-			//Throw not found
-			//XXX: prevent use of outdated recover link
-			throw $this->createNotFoundException($this->translator->trans('Outdated recover link'));
+			//Set context
+			$context = ['mail' => $smail, 'pass' => $pass, 'hash' => $hash];
 		}
 
 		//Create the LoginType form and give the proper parameters
 		$form = $this->createForm($this->config['recover']['view']['form'], $user, [
 			//Set action to recover route name and context
-			'action' => $this->generateUrl($this->config['route']['recover']['name'], ['mail' => $smail, 'pass' => $pass, 'hash' => $hash]+$this->config['route']['recover']['context']),
-			//Without mail
-			'mail' => false,
+			'action' => $this->generateUrl($this->config['route']['recover']['name'], $context+$this->config['route']['recover']['context']),
+			//With user disable mail
+			'mail' => ($user === null),
+			//With user enable password
+			'password' => ($user !== null),
 			//Set method
 			'method' => 'POST'
 		]);
 
+		//With post method
 		if ($request->isMethod('POST')) {
 			//Refill the fields in case the form is not valid.
 			$form->handleRequest($request);
 
-			if ($form->isValid()) {
+			//With form submitted and valid
+			if ($form->isSubmitted() && $form->isValid()) {
 				//Set data
 				$data = $form->getData();
 
-				//Set encoded password
-				$encoded = $encoder->encodePassword($user, $user->getPassword());
+				//With user
+				if ($user !== null) {
+					//Set hashed password
+					$hashed = $this->hasher->hashPassword($user, $user->getPassword());
 
-				//Update pass
-				$pass = $slugger->hash($encoded);
+					//Update pass
+					$pass = $this->slugger->hash($hashed);
 
-				//Set user password
-				$user->setPassword($encoded);
+					//Set user password
+					$user->setPassword($hashed);
 
-				//Persist user
-				$manager->persist($user);
+					//Persist user
+					$this->manager->persist($user);
+
+					//Send to database
+					$this->manager->flush();
+
+					//Add notice
+					$this->addFlash('notice', $this->translator->trans('Account password updated'));
 
-				//Send to database
-				$manager->flush();
+					//Redirect to user login
+					return $this->redirectToRoute($this->config['route']['login']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['login']['context']);
+				//Find user by data mail
+				} elseif ($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
+					//Set context
+					$context = [
+						'recipient_mail' => $user->getMail(),
+						'recipient_name' => $user->getRecipientName()
+					] + array_replace_recursive(
+						$this->config['context'],
+						$this->config['recover']['view']['context'],
+						$this->config['recover']['mail']['context']
+					);
 
-				//Add notice
-				$this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail]));
+					//Generate each route route
+					foreach($this->config['recover']['route'] as $route => $tag) {
+						//Only process defined routes
+						if (!empty($this->config['route'][$route])) {
+							//Process for recover mail url
+							if ($route == 'recover') {
+								//Set the url in context
+								$context[$tag] = $this->router->generate(
+									$this->config['route'][$route]['name'],
+									//Prepend recover context with tag
+									[
+										'mail' => $smail = $this->slugger->short($context['recipient_mail']),
+										'pass' => $spass = $this->slugger->hash($pass = $user->getPassword()),
+										'hash' => $this->slugger->hash($smail.$spass)
+									]+$this->config['route'][$route]['context'],
+									UrlGeneratorInterface::ABSOLUTE_URL
+								);
+							}
+						}
+					}
+
+					//Iterate on keys to translate
+					foreach($this->config['translate'] as $translate) {
+						//Extract keys
+						$keys = explode('.', $translate);
+
+						//Set current
+						$current =& $context;
+
+						//Iterate on each subkey
+						do {
+							//Skip unset translation keys
+							if (!isset($current[current($keys)])) {
+								continue(2);
+							}
+
+							//Set current to subkey
+							$current =& $current[current($keys)];
+						} while(next($keys));
+
+						//Set translation
+						$current = $this->translator->trans($current);
+
+						//Remove reference
+						unset($current);
+					}
+
+					//Translate subject
+					$context['subject'] = $subject = ucfirst(
+						$this->translator->trans(
+							$this->config['recover']['mail']['subject'],
+							$this->slugger->flatten($context, null, '.', '%', '%')
+						)
+					);
 
-				//Redirect to user login
-				return $this->redirectToRoute($this->config['route']['login']['name'], ['mail' => $smail, 'hash' => $slugger->hash($smail)]+$this->config['route']['login']['context']);
+					//Create message
+					$message = (new TemplatedEmail())
+						//Set sender
+						->from(new Address($this->config['contact']['address'], $this->config['contact']['name']))
+						//Set recipient
+						//XXX: remove the debug set in vendor/symfony/mime/Address.php +46
+						->to(new Address($context['recipient_mail'], $context['recipient_name']))
+						//Set subject
+						->subject($context['subject'])
+
+						//Set path to twig templates
+						->htmlTemplate($this->config['recover']['mail']['html'])
+						->textTemplate($this->config['recover']['mail']['text'])
+
+						//Set context
+						->context($context);
+
+					//Try sending message
+					//XXX: mail delivery may silently fail
+					try {
+						//Send message
+						$this->mailer->send($message);
+
+						//Add notice
+						$this->addFlash('notice', $this->translator->trans('Your recovery mail has been sent, to retrieve your account you must follow the recuperate link inside'));
+
+						//Add junk warning
+						$this->addFlash('warning', $this->translator->trans('If you did not receive a recovery mail, check your Spam or Junk mail folders'));
+
+						//Redirect on the same route with sent=1 to cleanup form
+						return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'), 302);
+					//Catch obvious transport exception
+					} catch(TransportExceptionInterface $e) {
+						//Add error message mail unreachable
+						$form->get('mail')->addError(new FormError($this->translator->trans('Unable to reach account')));
+					}
+				}
 			}
 		}
 
@@ -541,7 +537,7 @@ class DefaultController extends AbstractController {
 			//Template
 			$this->config['recover']['view']['name'],
 			//Context
-			['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
+			['recover' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['recover']['view']['context']
 		);
 	}
 
@@ -549,33 +545,24 @@ class DefaultController extends AbstractController {
 	 * Register an account
 	 *
 	 * @param Request $request The request
-	 * @param Registry $manager The doctrine registry
-	 * @param UserPasswordEncoderInterface $encoder The password encoder
-	 * @param EntityManagerInterface $manager The doctrine entity manager
-	 * @param SluggerUtil $slugger The slugger
-	 * @param MailerInterface $mailer The mailer
-	 * @param LoggerInterface $logger The logger
-	 * @param string $mail The shorted mail address
-	 * @param string $field The serialized then shorted form field array
-	 * @param string $hash The hashed serialized field array
 	 * @return Response The response
 	 */
-	public function register(Request $request, Registry $doctrine, UserPasswordEncoderInterface $encoder, EntityManagerInterface $manager, SluggerUtil $slugger, MailerInterface $mailer, LoggerInterface $logger, $mail, $field, $hash): Response {
+	public function register(Request $request): Response {
 		//With mail
 		if (!empty($_POST['register']['mail'])) {
 			//Log new user infos
-			$logger->emergency(
+			$this->logger->emergency(
 				$this->translator->trans(
 					'register: mail=%mail% locale=%locale% confirm=%confirm%',
 					[
 						'%mail%' => $postMail = $_POST['register']['mail'],
 						'%locale%' => $request->getLocale(),
-						'%confirm%' => $this->get('router')->generate(
+						'%confirm%' => $this->router->generate(
 							$this->config['route']['confirm']['name'],
 							//Prepend subscribe context with tag
 							[
-								'mail' => $postSmail = $slugger->short($postMail),
-								'hash' => $slugger->hash($postSmail)
+								'mail' => $postSmail = $this->slugger->short($postMail),
+								'hash' => $this->slugger->hash($postSmail)
 							]+$this->config['route']['confirm']['context'],
 							UrlGeneratorInterface::ABSOLUTE_URL
 						)
@@ -584,237 +571,44 @@ class DefaultController extends AbstractController {
 			);
 		}
 
-		//With mail and field
-		if (!empty($field) && !empty($hash)) {
-			//With invalid hash
-			if ($hash != $slugger->hash($mail.$field)) {
-				//Throw bad request
-				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'hash', '%value%' => $hash]));
-			}
-
-			//With mail
-			if (!empty($mail)) {
-				//Get mail
-				$mail = $slugger->unshort($smail = $mail);
-
-				//Without valid mail
-				if (filter_var($mail, FILTER_VALIDATE_EMAIL) === false) {
-					//Throw bad request
-					//XXX: prevent slugger reverse engineering by not displaying decoded mail
-					throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'mail', '%value%' => $smail]));
-				}
-
-				//With existing registrant
-				if ($existing = $doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail)) {
-					//With disabled existing
-					if ($existing->isDisabled()) {
-						//Render view
-						return $this->render(
-							//Template
-							$this->config['register']['view']['name'],
-							//Context
-							['title' => $this->translator->trans('Access denied'), 'disabled' => 1]+$this->config['register']['view']['context'],
-							//Set 403
-							new Response('', 403)
-						);
-					//With unactivated existing
-					} elseif (!$existing->isActivated()) {
-						//Set mail shortcut
-						//TODO: change for activate ???
-						$activateMail =& $this->config['register']['mail'];
-
-						//Generate each route route
-						foreach($this->config['register']['route'] as $route => $tag) {
-							//Only process defined routes
-							if (!empty($this->config['route'][$route])) {
-								//Process for confirm url
-								if ($route == 'confirm') {
-									//Set the url in context
-									$activateMail['context'][$tag] = $this->get('router')->generate(
-										$this->config['route'][$route]['name'],
-										//Prepend subscribe context with tag
-										[
-											'mail' => $smail = $slugger->short($existing->getMail()),
-											'hash' => $slugger->hash($smail)
-										]+$this->config['route'][$route]['context'],
-										UrlGeneratorInterface::ABSOLUTE_URL
-									);
-								}
-							}
-						}
-
-						//Set recipient_name
-						$activateMail['context']['recipient_mail'] = $existing->getMail();
-
-						//Set recipient name
-						$activateMail['context']['recipient_name'] = implode(' ', [$existing->getForename(), $existing->getSurname(), $existing->getPseudonym()?'('.$existing->getPseudonym().')':'']);
-
-						//Init subject context
-						$subjectContext = $slugger->flatten(array_replace_recursive($this->config['register']['view']['context'], $activateMail['context']), null, '.', '%', '%');
-
-						//Translate subject
-						$activateMail['subject'] = ucfirst($this->translator->trans($activateMail['subject'], $subjectContext));
-
-						//Create message
-						$message = (new TemplatedEmail())
-							//Set sender
-							->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
-							//Set recipient
-							//XXX: remove the debug set in vendor/symfony/mime/Address.php +46
-							->to(new Address($activateMail['context']['recipient_mail'], $activateMail['context']['recipient_name']))
-							//Set subject
-							->subject($activateMail['subject'])
-
-							//Set path to twig templates
-							->htmlTemplate($activateMail['html'])
-							->textTemplate($activateMail['text'])
-
-							//Set context
-							->context(['subject' => $activateMail['subject']]+$activateMail['context']);
-
-						//Try sending message
-						//XXX: mail delivery may silently fail
-						try {
-							//Send message
-							$mailer->send($message);
-						//Catch obvious transport exception
-						} catch(TransportExceptionInterface $e) {
-							//Add error message mail unreachable
-							$this->addFlash('error', $this->translator->trans('Account %mail% tried activate but unable to contact', ['%mail%' => $existing->getMail()]));
-						}
-
-						//Get route params
-						$routeParams = $request->get('_route_params');
-
-						//Remove mail, field and hash from route params
-						unset($routeParams['mail'], $routeParams['field'], $routeParams['hash']);
-
-						//Redirect on the same route with sent=1 to cleanup form
-						return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$routeParams);
-					}
-
-					//Add error message mail already exists
-					$this->addFlash('warning', $this->translator->trans('Account %mail% already exists', ['%mail%' => $existing->getMail()]));
-
-					//Redirect to user view
-					return $this->redirectToRoute(
-						$this->config['route']['edit']['name'],
-						[
-							'mail' => $smail = $slugger->short($existing->getMail()),
-							'hash' => $slugger->hash($smail)
-						]+$this->config['route']['edit']['context']
-					);
-				}
-			//Without mail
-			} else {
-				//Set smail
-				$smail = $mail;
-			}
-
-			//Try
-			try {
-				//Unshort then unserialize field
-				$field = $slugger->unserialize($sfield = $field);
-			//Catch type error
-			} catch (\Error|\Exception $e) {
-				//Throw bad request
-				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]), $e);
-			}
-
-			//With non array field
-			if (!is_array($field)) {
-				//Throw bad request
-				throw new BadRequestHttpException($this->translator->trans('Invalid %field% field: %value%', ['%field%' => 'field', '%value%' => $field]));
-			}
-		//Without field and hash
-		} else {
-			//Set smail
-			$smail = $mail;
-
-			//Set smail
-			$sfield = $field;
-
-			//Reset field
-			$field = [
-				//Without slug
-				'slug' => false
-			];
-		}
-
 		//Init reflection
 		$reflection = new \ReflectionClass($this->config['class']['user']);
 
 		//Create new user
-		$user = $reflection->newInstance(strval($mail));
+		$user = $reflection->newInstance('', '');
 
 		//Create the RegisterType form and give the proper parameters
-		$form = $this->createForm($this->config['register']['view']['form'], $user, $field+[
+		$form = $this->createForm($this->config['register']['view']['form'], $user, [
 			//Set action to register route name and context
-			'action' => $this->generateUrl($this->config['route']['register']['name'], ['mail' => $smail, 'field' => $sfield, 'hash' => $hash]+$this->config['route']['register']['context']),
+			'action' => $this->generateUrl($this->config['route']['register']['name'], $this->config['route']['register']['context']),
 			//Set civility class
 			'civility_class' => $this->config['class']['civility'],
 			//Set civility default
-			'civility_default' => $doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
-			//With mail
-			'mail' => true,
+			'civility_default' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
 			//Set method
 			'method' => 'POST'
-		]);
+		]+($this->checker->isGranted($this->config['default']['admin'])?$this->config['register']['admin']:$this->config['register']['field']));
 
+		//With post method
 		if ($request->isMethod('POST')) {
 			//Refill the fields in case the form is not valid.
 			$form->handleRequest($request);
 
-			if ($form->isValid()) {
+			//With form submitted and valid
+			if ($form->isSubmitted() && $form->isValid()) {
 				//Set data
 				$data = $form->getData();
 
-				//With existing registrant
-				if ($doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail = $data->getMail())) {
-					//Add error message mail already exists
-					$this->addFlash('warning', $this->translator->trans('Account %mail% already exists', ['%mail%' => $mail]));
-
-					//Redirect to user view
-					return $this->redirectToRoute(
-						$this->config['route']['edit']['name'],
-						[
-							'mail' => $smail = $slugger->short($mail),
-							'hash' => $slugger->hash($smail)
-						]+$this->config['route']['edit']['context']
-					);
-				}
-
-				//Set mail shortcut
-				$registerMail =& $this->config['register']['mail'];
-
-				//Extract names and pseudonym from mail
-				$names = explode(' ', $pseudonym = ucwords(trim(preg_replace('/[^a-zA-Z]+/', ' ', current(explode('@', $data->getMail()))))));
-
-				//Set pseudonym
-				$user->setPseudonym($user->getPseudonym()??$pseudonym);
-
-				//Set forename
-				$user->setForename($user->getForename()??$names[0]);
-
-				//Set surname
-				$user->setSurname($user->getSurname()??$names[1]??$names[0]);
-
 				//Set password
-				$user->setPassword($encoder->encodePassword($user, $user->getPassword()??$data->getMail()));
-
-				//Set created
-				$user->setCreated(new \DateTime('now'));
-
-				//Set updated
-				$user->setUpdated(new \DateTime('now'));
+				$user->setPassword($this->hasher->hashPassword($user, $user->getPassword()));
 
 				//Persist user
-				$manager->persist($user);
+				$this->manager->persist($user);
 
 				//Iterate on default group
 				foreach($this->config['default']['group'] as $i => $groupTitle) {
 					//Fetch group
-					if (($group = $doctrine->getRepository($this->config['class']['group'])->findOneByTitle($groupTitle))) {
+					if (($group = $this->doctrine->getRepository($this->config['class']['group'])->findOneByTitle($groupTitle))) {
 						//Set default group
 						//XXX: see vendor/symfony/security-core/Role/Role.php
 						$user->addGroup($group);
@@ -822,23 +616,33 @@ class DefaultController extends AbstractController {
 					} else {
 						//Throw exception
 						//XXX: consider missing group as fatal
-						throw new \Exception(sprintf('Group from rapsys_user.default.group[%d] not found by title: %s', $i, $groupTitle));
+						throw new \Exception(sprintf('Group %s listed in %s.default.group[%d] not found by title', $groupTitle, RapsysUserBundle::getAlias(), $i));
 					}
 				}
 
+				//Set context
+				$context = [
+					'recipient_mail' => $user->getMail(),
+					'recipient_name' => $user->getRecipientName()
+				] + array_replace_recursive(
+					$this->config['context'],
+					$this->config['register']['view']['context'],
+					$this->config['register']['mail']['context']
+				);
+
 				//Generate each route route
 				foreach($this->config['register']['route'] as $route => $tag) {
 					//Only process defined routes
 					if (!empty($this->config['route'][$route])) {
-						//Process for confirm url
+						//Process for confirm mail url
 						if ($route == 'confirm') {
 							//Set the url in context
-							$registerMail['context'][$tag] = $this->get('router')->generate(
+							$context[$tag] = $this->router->generate(
 								$this->config['route'][$route]['name'],
-								//Prepend subscribe context with tag
+								//Prepend register context with tag
 								[
-									'mail' => $smail = $slugger->short($data->getMail()),
-									'hash' => $slugger->hash($smail)
+									'mail' => $smail = $this->slugger->short($context['recipient_mail']),
+									'hash' => $this->slugger->hash($smail)
 								]+$this->config['route'][$route]['context'],
 								UrlGeneratorInterface::ABSOLUTE_URL
 							);
@@ -846,45 +650,61 @@ class DefaultController extends AbstractController {
 					}
 				}
 
-				//XXX: DEBUG: remove me
-				//die($registerMail['context']['confirm_url']);
+				//Iterate on keys to translate
+				foreach($this->config['translate'] as $translate) {
+					//Extract keys
+					$keys = explode('.', $translate);
 
-				//Set recipient_name
-				$registerMail['context']['recipient_mail'] = $data->getMail();
+					//Set current
+					$current =& $context;
 
-				//Set recipient name
-				$registerMail['context']['recipient_name'] = '';
+					//Iterate on each subkey
+					do {
+						//Skip unset translation keys
+						if (!isset($current[current($keys)])) {
+							continue(2);
+						}
 
-				//Set recipient name
-				$registerMail['context']['recipient_name'] = implode(' ', [$data->getForename(), $data->getSurname(), $data->getPseudonym()?'('.$data->getPseudonym().')':'']);
+						//Set current to subkey
+						$current =& $current[current($keys)];
+					} while(next($keys));
 
-				//Init subject context
-				$subjectContext = $slugger->flatten(array_replace_recursive($this->config['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
+					//Set translation
+					$current = $this->translator->trans($current);
+
+					//Remove reference
+					unset($current);
+				}
 
 				//Translate subject
-				$registerMail['subject'] = ucfirst($this->translator->trans($registerMail['subject'], $subjectContext));
+				$context['subject'] = $subject = ucfirst(
+					$this->translator->trans(
+						$this->config['register']['mail']['subject'],
+						$this->slugger->flatten($context, null, '.', '%', '%')
+					)
+				);
 
 				//Create message
 				$message = (new TemplatedEmail())
 					//Set sender
-					->from(new Address($this->config['contact']['mail'], $this->config['contact']['title']))
+					->from(new Address($this->config['contact']['address'], $this->config['contact']['name']))
 					//Set recipient
 					//XXX: remove the debug set in vendor/symfony/mime/Address.php +46
-					->to(new Address($registerMail['context']['recipient_mail'], $registerMail['context']['recipient_name']))
+					->to(new Address($context['recipient_mail'], $context['recipient_name']))
 					//Set subject
-					->subject($registerMail['subject'])
+					->subject($context['subject'])
 
 					//Set path to twig templates
-					->htmlTemplate($registerMail['html'])
-					->textTemplate($registerMail['text'])
+					->htmlTemplate($this->config['register']['mail']['html'])
+					->textTemplate($this->config['register']['mail']['text'])
 
 					//Set context
-					->context(['subject' => $registerMail['subject']]+$registerMail['context']);
+					->context($context);
 
 				//Try saving in database
 				try {
 					//Send to database
-					$manager->flush();
+					$this->manager->flush();
 
 					//Add error message mail already exists
 					$this->addFlash('notice', $this->translator->trans('Your account has been created'));
@@ -893,19 +713,19 @@ class DefaultController extends AbstractController {
 					//XXX: mail delivery may silently fail
 					try {
 						//Send message
-						$mailer->send($message);
+						$this->mailer->send($message);
 
 						//Redirect on the same route with sent=1 to cleanup form
 						return $this->redirectToRoute($request->get('_route'), ['sent' => 1]+$request->get('_route_params'));
 					//Catch obvious transport exception
 					} catch(TransportExceptionInterface $e) {
 						//Add error message mail unreachable
-						$form->get('mail')->addError(new FormError($this->translator->trans('Account %mail% tried subscribe but unable to contact', ['%mail%' => $data->getMail()])));
+						$form->get('mail')->addError(new FormError($this->translator->trans('Unable to reach account')));
 					}
 				//Catch double subscription
 				} catch (UniqueConstraintViolationException $e) {
 					//Add error message mail already exists
-					$this->addFlash('error', $this->translator->trans('Account %mail% already exists', ['%mail%' => $mail]));
+					$this->addFlash('error', $this->translator->trans('Account already exists'));
 				}
 			}
 		}
@@ -915,7 +735,7 @@ class DefaultController extends AbstractController {
 			//Template
 			$this->config['register']['view']['name'],
 			//Context
-			['form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['register']['view']['context']
+			['register' => $form->createView(), 'sent' => $request->query->get('sent', 0)]+$this->config['register']['view']['context']
 		);
 	}
 }