Reputation: 77
I have a problem when I want to edit an existing user from the Backend (using SonataAdminBundle and FOSUserBundle). In configureFormFields method of my UserAdmin class, the password field appears empty and this is a problem when I need to edit another fields (for example the lastname) keeping the same user password. This field (and the password verification field) must be filled again! (I do not want modify the user password)
In my UserAdmin class, I have:
public function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('User Data')
->add('username')
->add('plainPassword', 'repeated', array(
'type' => 'password',
'options' => array('translation_domain' => 'FOSUserBundle'),
'first_options' => array('label' => 'form.password'),
'second_options' => array('label' => 'form.password_confirmation'),
'invalid_message' => 'fos_user.password.mismatch',
))
->add('firstname')
->add('lastname')
->add('email')
->add('user_roles')
->add('enabled', 'checkbox', array(
'label' => 'Enable Account',
'required' => false,
))
->end()
;
}
I tried to overwrite prePersist and preUpdate methods in my UserAdmin class, but these do not work. The password is encripted in the database following the FOS standard (with salt and sha512).
Any solution? Many thanks!
Upvotes: 7
Views: 6690
Reputation: 53
Maybe too late but useful for others. With thanks to other posts. So simple. Just use :
class UserAdmin extends AbstractAdmin
{
protected $formOptions = array(
'validation_groups' => array('Profile')
);
//..
}
It uses the profile validation group defined in:
friendsofsymfony/user-bundle/Resources/config/validation.xml
Upvotes: 1
Reputation: 806
I have the same problem than you but without SonataAdminBundle.
In a classic user admin management (Symfony3), this works :
// src/AppBundle/Form/UserType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// By default, password is required (create user case)
$passwordOptions = array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat password'),
'required' => true
);
// If edit user : password is optional
// User object is stored in $options['data']
$recordId = $options['data']->getId();
if (!empty($recordId)) {
$passwordOptions['required'] = false;
}
$builder
->add('prenom', TextType::class, array('label' => 'First name'))
->add('nom', TextType::class, array('label' => 'Last name'))
->add('email', EmailType::class)
->add('username', TextType::class)
->add('plainPassword', RepeatedType::class, $passwordOptions)
->add('roleList', ChoiceType::class, array(
'label' => 'Role',
'choices' => array(
'Admin' => 'ROLE_ADMIN',
'User' => 'ROLE_USER'
),
))
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User',
));
}
}
Don't forget to remove @Assert\NotBlank() on plainPassword in your User Entity :
/**
* @Assert\Length(max=4096)
*/
private $plainPassword;
My editAction() in UserController.php :
/**
* @Route("/users/edit/{id}", name="user_edit")
*/
public function editAction($id, Request $request) {
// get user from database
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($id);
// user doesn't exist
if (!$user) {
throw $this->createNotFoundException('No user found for id '. $id);
}
// build the form with user data
$originalPassword = $user->getPassword(); # encoded password
$form = $this->createForm(UserType::class, $user);
// form POST
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Encode the password if needed
# password has changed
if (!empty($form->get('plainPassword')->getData())) {
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
# password not changed
} else {
$user->setPassword($originalPassword);
}
$role = $form->get('roleList')->getData();
$user->setRoles(array($role));
// update the User
$em->flush();
// success message
$this->addFlash('notice', 'User has been updated successfully !');
// redirection
return $this->redirectToRoute('user');
}
// show form
return $this->render('users/form.html.twig', array(
'h1_title' => 'Edit user : ' . $user->getPrenom() . " " . $user->getNom(),
'form' => $form->createView()
));
}
Now, password is required when you create a new user, but it isn't when you edit one.
Upvotes: 2
Reputation: 64476
You can override your the preUpdate
function in your admin class this is how i have done
public function preUpdate($object)
{
$DM = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getManager();
$repository = $DM->getRepository('Namespace\YourBundle\Entity\User')->find($object->getId());
$Password = $object->getPassword();
if (!empty($Password)) {
$salt = md5(time());
$encoderservice = $this->getConfigurationPool()->getContainer()->get('security.encoder_factory');
$encoder = $encoderservice->getEncoder($object);
$encoded_pass = $encoder->encodePassword($object->getPassword(), $salt);
$object->setSalt($salt);
$object->setPassword($encoded_pass);
} else {
$object->setPassword($repository->getPassword());
}
}
And my configureFormFields
function
protected function configureFormFields(FormMapper $formMapper)
{
$passwordoptions=array('type' => 'password','invalid_message' => 'The password fields must match.',
'options' => array('attr' => array('class' => 'password-field')),'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Confirm password'),'translation_domain' => 'FOSUserBundle'
);
$this->record_id = $this->request->get($this->getIdParameter());
if (!empty($this->record_id)) {
$passwordoptions['required'] = false;
$passwordoptions['constraints'] = array(new Assert\Length(array('min' => 10))
,new Assert\Regex(array('pattern' => '/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{10,}$/','match'=>true,'message'=>'Password must contain atleast 1 special character 1 upper case letter 1 number and 1 lower case letter !'))
);
} else {
$passwordoptions['required'] = true;
$passwordoptions['constraints'] = array(new Assert\Length(array('min' => 10))
,new Assert\Regex(array('pattern' => '/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{10,}$/','match'=>true,'message'=>'Password must contain atleast 1 special character 1 upper case letter 1 number and 1 lower case letter !'))
);
}
$formMapper->add('password', 'repeated', $passwordoptions); /*you can add your other fields*/
}
Upvotes: 2
Reputation: 544
There are some ways to do that
Change configureFormField behavoir
You could get the current object ($this->subject
or $this->getSubject();
) inside the configureFormFields and check if its a new Object or an existing one and change the fields behaviour (validation for example)
Using saveHooks and FOSUser
here is an example
http://sonata-project.org/bundles/admin/master/doc/reference/saving_hooks.html
this will show the hashed password inside the password field but should update it when entering a new plain one ( if i remember correctly)
Combine both and implement your own logic
inside of the hook you can get the FOSUserManager and handle the user update with that
container : $this->getConfigurationPool()->getContainer();
fosusermanager: $userManager = $container->get('fos_user.user_manager');
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/user_manager.md
Upvotes: 0