How to edit sonata user with avatar outside admin bundle - symfony

Almost same ISSUES: link, link
UPDATE INFO: - $user = $this->getUser(); set the old image while edit(error form submit). The image replaced with the submitted one value(only value not displaying). WHILE ERROR FORM SUBMIT - I NEED TO DISPLAY THE OLD MEDIA.
NO RELATION WITH SONATA ADMIN.
I have Admin and User role. Both have seperate admin area. User admin area has more complex structure.
I added an Image(avatar) to SonataUser , it works good at admin. Its OneToOne - User and Media.
To edit profile at User Dashboard( Its not SonataAdmin - its i created seperately, Its a simple symfony style).
code:
public function editProfileAction() {
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw $this->createAccessDeniedException('This user does not have access to this section.');
}
// Check user has allready media?
$om = $this->getUser()->getImage();
$oldPath = $om ? $this->getMediaPath($om->getId()) : NULL;
$form = $this->creteForm();
$formHandler = $this->get('sonata.user.profile.form.handler');
$process = $formHandler->process($user);
if ($process) {
// if new file - delete old file
$this->deleteOldMedia($om, $oldPath);
$this->flashMSG(0, 'Profile updated!');
return $this->redirectToRoute('fz_user');
}
$x = ['cmf' => '', 'pTitle' => 'Profile'];
return $this->render(self::TEMPLATE, ['x' => $x, 'form' => $form->createView()]);
}
By the above code, works - with one problem. The reference image of old file is not deleting at server folder. New files are added and entity works fine (displaying at template - fine).
So I tried with my own code,
public function editProfileAction() {
$request = $this->get('request');
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw $this->createAccessDeniedException('This user does not have access to this section.');
}
// Check user has allready media?
$om = $this->getUser()->getImage();
$oldPath = $om ? $this->getMediaPath($om->getId(), 'reference') : NULL;
$oldTN = $om ? $this->getMediaPath($om->getId(), 'admin') : NULL;
$form = $this->createForm(ProfileType::class, $user);
$form->handleRequest($request);
$em = $this->getDoctrine()->getEntityManager();
$data = $form->getData();
if ($form->isSubmitted() && $form->isValid()) {
if (($oldPath != NULL) && ($data->getImage()->getBinaryContent() != NULL)) {
$this->deleteFile($oldPath);
$this->deleteFile($oldTN);
}
$em->persist($user);
$em->flush();
$this->flashMSG(0, 'Profile updated!');
return $this->redirectToRoute('fz_user');
}
// $$user->setImage($om);
$x = ['cmf' => '', 'pTitle' => 'Profile'];
return $this->render(self::TEMPLATE, ['x' => $x, 'form' => $form->createView()]);
}
My own code works - with one problem, If image validation is error - the all image at the template are disappeared. So to check i added $user->setImage(NULL); , the result is, the Null image is shown.(NULL image means at template i do if(null){ display my image }). The backend process - image upadate works good.
For now - I'm satisfied with my code. Here I need to make $user->setImage(xx); to the real image. while form submit with error on media. ONly at error on media.
If no media and error submit - works (displaying image).
UPDATE:
I used $em->refresh($user); from this answer , also it failed to update my image.
WHAT I FOUND ISSUE WITH USER: Its not using 'ApplicationSonataUserBundle:User' for SYMFONY app.user . Thats why, when i give $em->refresh($user); it not modifing username and other details. But it modifing the new details of ApplicationSonataUserBundle:User

Finally to solve I REDIRECTED with flash msg..
$em = $this->getDoctrine()->getManager();
$user = $this->get('security.token_storage')->getToken()->getUser();
$entity = $em->getRepository('ApplicationSonataUserBundle:User')->find($user->getId());
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createForm(ProfileType::class, $entity);
if ($request->getMethod() === 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
$em->flush();
return $this->redirectToRoute('fz_user');
}
$em->refresh($user);
$this->flashMSG(1, '' . $form->getErrors(true, false));
return $this->redirectToRoute('fz_user_profile_edit');
}

Related

How to use a request post data in findOneBy method

I am writing a method to request a password reset in symfony
I get the email of the user from the posted value
$data = $form->getData();
$email = $data['email'];
It seems that I get the good email value and I can verify it by a
dump($email);
that returns
"firstname.name#domain.ext"
Then I try to fetch a user in the database with
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(["email" => $email]);
but the result is null.
After that I try 2 different ways and both of them work perfectly i.e. give a valid user.
1- I replace
$email = $data['email'];
with
$email = "firstname.name#domain.ext";
2-
I change the line that fetches the user with:
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(["email" => "firstname.name#domain.ext"]);
Obviously the trouble comes from the way the value is passed to the findOneBy method, not because the user is not in the database.
I would like to know what I should do to use the value I got from the Post ?
Here is my controller
/**
* #Route("/passforgotten", name="app_forgotten_password", methods="GET|POST")
*/
public function askResetPass(
Request $request,
UserPasswordEncoderInterface $encoder,
ManagerRegistry $managerRegistry,
\Swift_Mailer $mailer,
TokenGeneratorInterface $tokenGenerator
): Response {
$defaultData = ['message' => 'Type your message here'];
$form = $this->createFormBuilder($defaultData)
->add('email', EmailType::class)
->add('send', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$email = $data['email'];
//alternative that works of course with a real email
//$email="firstname.name#domain.ext";
dump($email);
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy(["email" => $email]);
dump($email);
dump($user);
if ($user === null) {
$this->addFlash('danger', 'Email Inconnu, recommence !');
return $this->redirectToRoute('app_register');
}
$token = $tokenGenerator->generateToken();
$manager = $managerRegistry->getManager();
try {
$user->setResetPasswordToken($token);
$manager->flush();
} catch (\Exception $e) {
$this->addFlash('warning', $e->getMessage());
return $this->redirectToRoute('home');
}
//this has not been tested yet
$url = $this->generateUrl('security/ask_reset_password', array('token' => $token), UrlGeneratorInterface::ABSOLUTE_URL);
$message = (new \Swift_Message('Rénitialisation du mot de pass'))
->setFrom(array('symfony#domain.ext'))
->setTo($user->getEmail())
->setBody('hello ask for reset pass!'
);
$mailer->send($message);
$this->addFlash('notice', 'Mail correctement envoyé !');
//this is not finished
return $this->redirectToRoute('a_route');
}
return $this->render('security/ask_reset_password.html.twig', [
'form' => $form->createView()
]);
}
Sorry but I was passing a misspelled email a letter l just before a b was missing and I used the form memorization of it each time. In fact it works in every case.

How to use Lock Component in Symfony3.4 with username

I am trying to use the lock component in symfony 3.4, like it is described on
https://symfony.com/doc/3.4/components/lock.html
I want to prevent multiple data changes from different users.
For example user1 is calling the same company form with data, then user2
How can I tell user2, that editing data is blocked by user1 (incl username) ?
UPDATE:
It is used in backend, where a lot of employees editing data of customers, order etc.
this form is just for editing. that means, if they want to update some data, they click "edit". They should be informed when another employee changes this record before the data is loaded into the form. It sometimes takes some time for the employee to change everything. If the employee receives a message when saving it, they have to go back,reload the data and start all over again.
an example out of my controller:
public function CompanyEdit(Request $request)
{
$error = null;
$company_id = $request->get('id');
// if (!preg_match('/^\d+$/', $company_id)){
// return $this->showError();
// }
$store = new SemaphoreStore();
$factory = new Factory($store);
$lock = $factory->createLock('company-edit-'.$company_id, 30);
if(!$lock->acquire()) {
//send output with username
// this data is locked by user xy
return 0;
}
$company = $this->getDoctrine()->getRepository(Company::class)->find($company_id);
$payment = $this->getDoctrine()->getRepository(Companypay::class)->findOneBy(array('company_id' => $company_id));
$form = $this->createFormBuilder()
->add('company', CompanyFormType::class, array(
'data_class' => 'AppBundle\Entity\Company',
'data' => $company
))
->add('payment', CompanyPayFormType::class, array(
'data_class' => 'AppBundle\Entity\CompanyPay',
'data' => $payment
))
->getForm();
$form->handleRequest($request);
$company = $form->get('company')->getData();
$payment = $form->get('payment')->getData();
if ($form->isSubmitted() && $form->isValid()) {
$event = new FormEvent($form, $request);
if ($payment->getCompanyId() == null) {
$payment->setCompanyId($company->getId());
}
try {
$this->getDoctrine()->getManager()->persist($company);
$this->getDoctrine()->getManager()->persist($payment);
$this->getDoctrine()->getManager()->flush();
$this->container->get('app.logging')->write('Kundendaten geändert', $company->getId());
} catch (PDOException $e) {
$error = $e->getMessage();
}
if (null === $response = $event->getResponse()) {
return $this->render('customer/edit.html.twig', [
'form' => $form->createView(),
'company' => $company,
'error' => $error,
'success' => true
]);
}
$lock->release();
return $response;
}
You can't (Locks can't have any metadata), but you probably don't want this in the first place.
In this case, you create a Lock when a user opens the edit page and release it when a user submits the form. But what if the users opens the page and doesn't submit the form? And why can't a user even view the form?
This looks like a XY-problem. I think you're trying to prevent users to overwrite data without knowing. Instead, you can add a timestamp or hash to the form that changes after changing the entity. For example:
<form type="hidden" name="updatedAt" value="{{ company.updatedAt()|date('U') }}" />
And in your form:
<?php
if ($company->getUpdatedAt()->format('U') !== $form->get('updatedAt')->getData()) {
throw new \LogicException('The entity has been changed after you opened the page');
}
Disclaimer: code is not tested and just as an example how this solution can look like.

Silex - get right condionals in finish middleware

I want to create a pdf file out of some route-dependant data
{http://example.com/products/123/?action=update}
$app->finish(function (Request $request, Response $response) {
// Make a pdf file, only if:
// - the route is under /products/
// - the action is update
// - the subsequent ProductType form isSubmitted() and isValid()
// - the 'submit' button on the ProductType form isClicked()
});
As a normal form submission process I have:
public function update(Application $app, Request $request)
{
$form = $app['form.factory']->create(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (!$form->get('submit')->isClicked()) {
return $app->redirect('somewhere');
}
$product = $form->getData();
$app['em']->persist($product);
$app['em']->flush();
return $app->redirect('product_page');
}
return $app['twig']->render('products/update.html.twig', array(
'form' => $form->createView(),
));
}
Questions:
Should I duplicate all of the conditionals in finish middleware?
How to access the Product entity in finish middleware?
Consider having multiple resource types like Products, Services, Users, ...

How to remove the null value field from form in symfony form handler

I want to update the data in two conditions:
When user enters all the fields in form (Name, email, password)
When user does not enter password (I have to update only name & email).
I have the Following formHandler Method.
public function process(UserInterface $user)
{
$this->form->setData($user);
if ('POST' === $this->request->getMethod()) {
$password = trim($this->request->get('fos_user_profile_form')['password']) ;
// Checked where password is empty
// But when I remove the password field, it doesn't update anything.
if(empty($password))
{
$this->form->remove('password');
}
$this->form->bind($this->request);
if ($this->form->isValid()) {
$this->onSuccess($user);
return true;
}
// Reloads the user to reset its username. This is needed when the
// username or password have been changed to avoid issues with the
// security layer.
$this->userManager->reloadUser($user);
}
An easy solution to your problem is to disable the mapping of the password field and copy its value to your model manually unless it is empty. Sample code:
$form = $this->createFormBuilder()
->add('name', 'text')
->add('email', 'repeated', array('type' => 'email'))
->add('password', 'repeated', array('type' => 'password', 'mapped' => false))
// ...
->getForm();
// Symfony 2.3+
$form->handleRequest($request);
// Symfony < 2.3
if ('POST' === $request->getMethod()) {
$form->bind($request);
}
// all versions
if ($form->isValid()) {
$user = $form->getData();
if (null !== $form->get('password')->getData()) {
$user->setPassword($form->get('password')->getData());
}
// persist $user
}
You can also add this logic to your form type if you prefer to keep your controllers clean:
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormInterface $form) {
$form = $event->getForm();
$user = $form->getData();
if (null !== $form->get('password')->getData()) {
$user->setPassword($form->get('password')->getData());
}
});
Easier way:
/my/Entity/User
public function setPassword($password)
{
if ($password) {
$this->password = $password;
}
}
So, any form using User with password will act as expected :)

get('security.context')->isGranted in functional test

I want do one functional test over on service Symfony2. The idea is call before to the controller and after that, load the service with the function. The function is this one:
function save($title,$description,$setId,$html,$validate,$articles){
$articles = explode(',', $articles);
if (false === $this->container->get('security.context')->isGranted('ROLE_USER')) {
throw new \Exception("Not allowed");
}else{
$profileId = $this->container->get('security.context')->getToken()->getUser()->getId();
$userName = $this->container->get('security.context')->getToken()->getUser()->getUserName();
}
}
and now my test code is :
$client = static::createClient();
$crawler = $client->request('GET','/sets/save',
array(
"title"=>"rtyui",
"description"=>"aksdjhashdkjahskjdh",
"set_id"=>"",
"html"=>"",
"validate"=>1,
"articels"=>"3,4"
)
);
but doesn't work already that I have this lines:
if (false === $this->container->get('security.context')->isGranted('ROLE_USER')) {
throw new \Exception("Not allowed");
Now, the question is, how i can do the validation process? I've tried to do this validation process as show the documentation:
$client = static::createClient(array(), array(
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'pa$$word',
));
but I got the same error.
Also you can login user by Security Token:
$client = static::createClient();
$container = $client->getContainer();
$container->get('security.context')->setToken(
new UsernamePasswordToken(
$user, null, 'main', $user->getRoles()
)
);
Where:
$user - instance of User entity with role ROLE_USER,
main - your security provider name

Resources