]> Raphaël G. Git Repositories - userbundle/commitdiff
Add index function
authorRaphaël Gertz <git@rapsys.eu>
Mon, 11 Dec 2023 01:42:05 +0000 (02:42 +0100)
committerRaphaël Gertz <git@rapsys.eu>
Mon, 11 Dec 2023 01:42:05 +0000 (02:42 +0100)
Remove field, hash and mail from register context
Use checker isGranted member function
Use security getUser member function
Cleanup
Set edit form field from admin or user set
Add disabled and sent query parameter to login context
Cleanup recover function
Cleanup register function
Fix user constructor arguments
Set register form field from admin or user set
Fix translation of context

Controller/DefaultController.php

index de4ff4e4a4afa574b4d1e10a5febf1e1d788a3d9..6fc41114a618dea4642c000fa9729e05f0fc5ee4 100644 (file)
@@ -22,10 +22,46 @@ use Symfony\Component\Mime\Address;
 use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
 
+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
         *
@@ -58,7 +94,7 @@ class DefaultController extends AbstractController {
                        $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 = $this->slugger->serialize([]), 'hash' => $this->slugger->hash($smail.$sfield)]+$this->config['route']['register']['context']);
+                       return $this->redirectToRoute($this->config['route']['register']['name'], $this->config['route']['register']['context']);
                }
 
                //Set active
@@ -103,36 +139,30 @@ class DefaultController extends AbstractController {
                }
 
                //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
+                       //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' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
-                       //Disable mail
-                       'mail' => $this->isGranted('ROLE_ADMIN'),
-                       //Disable password
-                       'password' => false,
                        //Set method
                        'method' => 'POST'
-               ]+$this->config['edit']['field']);
+               ]+($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
+                               //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']),
-                               //Disable mail
-                               'mail' => false,
                                //Set method
                                'method' => 'POST'
                        ]);
@@ -199,7 +229,7 @@ class DefaultController extends AbstractController {
                        }
                //Without admin role
                //XXX: prefer a reset on login to force user unspam action
-               } elseif (!$this->isGranted('ROLE_ADMIN')) {
+               } 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'));
                }
@@ -227,8 +257,6 @@ class DefaultController extends AbstractController {
                $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'
                ]);
@@ -268,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']),
@@ -297,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']
                );
        }
 
@@ -311,157 +339,56 @@ class DefaultController extends AbstractController {
         * @return Response The response
         */
        public function recover(Request $request, ?string $hash, ?string $pass, ?string $mail): 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'
-                       ]);
-
-                       //With post method
-                       if ($request->isMethod('POST')) {
-                               //Refill the fields in case the form is not valid.
-                               $form->handleRequest($request);
+               //Set user
+               $user = null;
 
-                               //With form submitted and valid
-                               if ($form->isSubmitted() && $form->isValid()) {
-                                       //Set data
-                                       $data = $form->getData();
-
-                                       //Find user by data mail
-                                       if ($user = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($data['mail'])) {
-                                               //Set mail shortcut
-                                               $recoverMail =& $this->config['recover']['mail'];
-
-                                               //Set mail
-                                               $mail = $this->slugger->short($user->getMail());
-
-                                               //Set pass
-                                               $pass = $this->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->router->generate(
-                                                                               $this->config['route'][$route]['name'],
-                                                                               //Prepend recover context with tag
-                                                                               [
-                                                                                       'mail' => $mail,
-                                                                                       'pass' => $pass,
-                                                                                       'hash' => $this->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'] = $user->getRecipientName();
-
-                                               //Init subject context
-                                               $subjectContext = $this->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
-                                                       $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 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']
-                       );
-               }
-
-               //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]));
-               }
+                       //Get mail
+                       $mail = $this->slugger->unshort($smail = $mail);
 
-               //Get mail
-               $mail = $this->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]));
+                       }
 
-               //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 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]));
+                       }
 
-               //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]));
-               }
+                       //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 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'));
+                       //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'
                ]);
@@ -476,26 +403,132 @@ class DefaultController extends AbstractController {
                                //Set data
                                $data = $form->getData();
 
-                               //Set hashed password
-                               $hashed = $this->hasher->hashPassword($user, $user->getPassword());
+                               //With user
+                               if ($user !== null) {
+                                       //Set hashed password
+                                       $hashed = $this->hasher->hashPassword($user, $user->getPassword());
 
-                               //Update pass
-                               $pass = $this->slugger->hash($hashed);
+                                       //Update pass
+                                       $pass = $this->slugger->hash($hashed);
 
-                               //Set user password
-                               $user->setPassword($hashed);
+                                       //Set user password
+                                       $user->setPassword($hashed);
 
-                               //Persist user
-                               $this->manager->persist($user);
+                                       //Persist user
+                                       $this->manager->persist($user);
+
+                                       //Send to database
+                                       $this->manager->flush();
 
-                               //Send to database
-                               $this->manager->flush();
+                                       //Add notice
+                                       $this->addFlash('notice', $this->translator->trans('Account password updated'));
+
+                                       //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']
+                                       );
+
+                                       //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, '.', '%', '%')
+                                               )
+                                       );
+
+                                       //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'])
 
-                               //Add notice
-                               $this->addFlash('notice', $this->translator->trans('Account %mail% password updated', ['%mail%' => $mail]));
+                                               //Set path to twig templates
+                                               ->htmlTemplate($this->config['recover']['mail']['html'])
+                                               ->textTemplate($this->config['recover']['mail']['text'])
 
-                               //Redirect to user login
-                               return $this->redirectToRoute($this->config['route']['login']['name'], ['mail' => $smail, 'hash' => $this->slugger->hash($smail)]+$this->config['route']['login']['context']);
+                                               //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')));
+                                       }
+                               }
                        }
                }
 
@@ -504,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']
                );
        }
 
@@ -512,12 +545,9 @@ class DefaultController extends AbstractController {
         * Register an account
         *
         * @param Request $request The request
-        * @param ?string $hash The hashed serialized field array
-        * @param ?string $field The serialized then shorted form field array
-        * @param ?string $mail The shorted mail address
         * @return Response The response
         */
-       public function register(Request $request, ?string $hash, ?string $field, ?string $mail): Response {
+       public function register(Request $request): Response {
                //With mail
                if (!empty($_POST['register']['mail'])) {
                        //Log new user infos
@@ -541,182 +571,23 @@ class DefaultController extends AbstractController {
                        );
                }
 
-               //With mail and field
-               if (!empty($field) && !empty($hash)) {
-                       //With invalid hash
-                       if ($hash != $this->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 = $this->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 = $this->doctrine->getRepository($this->config['class']['user'])->findOneByMail($mail)) {
-                                       //With disabled existing
-                                       if ($existing->isDisabled()) {
-                                               //Render view
-                                               $response = $this->render(
-                                                       //Template
-                                                       $this->config['register']['view']['name'],
-                                                       //Context
-                                                       ['title' => $this->translator->trans('Access denied'), 'disabled' => 1]+$this->config['register']['view']['context']
-                                               );
-
-                                               //Set 403
-                                               $response->setStatusCode(403);
-
-                                               //Return response
-                                               return $response;
-                                       //With unactivated existing
-                                       } elseif (!$existing->isActivated()) {
-                                               //Set mail shortcut
-                                               $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->router->generate(
-                                                                               $this->config['route'][$route]['name'],
-                                                                               //Prepend subscribe context with tag
-                                                                               [
-                                                                                       'mail' => $smail = $this->slugger->short($existing->getMail()),
-                                                                                       'hash' => $this->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'] = $existing->getRecipientName();
-
-                                               //Init subject context
-                                               $subjectContext = $this->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
-                                                       $this->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 = $this->slugger->short($existing->getMail()),
-                                                       'hash' => $this->slugger->hash($smail)
-                                               ]+$this->config['route']['edit']['context']
-                                       );
-                               }
-                       //Without mail
-                       } else {
-                               //Set smail
-                               $smail = $mail;
-                       }
-
-                       //Try
-                       try {
-                               //Unshort then unserialize field
-                               $field = $this->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 = [];
-               }
-
                //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' => $this->doctrine->getRepository($this->config['class']['civility'])->findOneByTitle($this->config['default']['civility']),
-                       //With mail
-                       'mail' => true,
                        //Set method
                        'method' => 'POST'
-               ]+$this->config['register']['field']);
+               ]+($this->checker->isGranted($this->config['default']['admin'])?$this->config['register']['admin']:$this->config['register']['field']));
 
                //With post method
                if ($request->isMethod('POST')) {
@@ -728,24 +599,6 @@ class DefaultController extends AbstractController {
                                //Set data
                                $data = $form->getData();
 
-                               //With existing registrant
-                               if ($this->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 = $this->slugger->short($mail),
-                                                       'hash' => $this->slugger->hash($smail)
-                                               ]+$this->config['route']['edit']['context']
-                                       );
-                               }
-
-                               //Set mail shortcut
-                               $registerMail =& $this->config['register']['mail'];
-
                                //Set password
                                $user->setPassword($this->hasher->hashPassword($user, $user->getPassword()));
 
@@ -763,22 +616,32 @@ 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->router->generate(
+                                                       $context[$tag] = $this->router->generate(
                                                                $this->config['route'][$route]['name'],
-                                                               //Prepend subscribe context with tag
+                                                               //Prepend register context with tag
                                                                [
-                                                                       'mail' => $smail = $this->slugger->short($data->getMail()),
+                                                                       'mail' => $smail = $this->slugger->short($context['recipient_mail']),
                                                                        'hash' => $this->slugger->hash($smail)
                                                                ]+$this->config['route'][$route]['context'],
                                                                UrlGeneratorInterface::ABSOLUTE_URL
@@ -787,34 +650,56 @@ class DefaultController extends AbstractController {
                                        }
                                }
 
-                               //Set recipient_name
-                               $registerMail['context']['recipient_mail'] = $data->getMail();
+                               //Iterate on keys to translate
+                               foreach($this->config['translate'] as $translate) {
+                                       //Extract keys
+                                       $keys = explode('.', $translate);
 
-                               //Set recipient name
-                               $registerMail['context']['recipient_name'] = $data->getRecipientName();
+                                       //Set current
+                                       $current =& $context;
 
-                               //Init subject context
-                               $subjectContext = $this->slugger->flatten(array_replace_recursive($this->config['register']['view']['context'], $registerMail['context']), null, '.', '%', '%');
+                                       //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
-                               $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 {
@@ -835,12 +720,12 @@ class DefaultController extends AbstractController {
                                        //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'));
                                }
                        }
                }
@@ -850,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']
                );
        }
 }