FOSUserBundle: Change Password within actual project - symfony

I have successfully installed FOSUserBundle in my project and everything works as expected. However, I am struggling with how to implement it in my actual project.
I want to create the following setup:
A page displaying some user settings in one form (like newsletter subscription), the possibility to change the password in a second form and maybe also a third form to change the username.
The settings form as well as some more information is coming from an existing action in my controller and is working well.
I did try a few things but things are not really working out yet:
I copied some functionality from FOSUserBundle\Controller\ChangePasswordController\changePasswordAction() to my own action. This way I could get the change password form, create the view and pass it to my template.
I added the form to my template with {{ form_widget(form) }}. The form is being displayed and it's even working. I can change the password. However, the labels are being lost, simply reading Current, First, and Second. Also there is no error messaging showing up when the two new passwords don't match or are being left empty.
Over all I have the feeling I am probably doing this in a wrong way. Could you please help me how I should handle this task and point out where I am likely doing something stupid?
Here is the code of my action, reduced to what's important here:
# src/Acme/MyBundle/Controller/BackendController.php
public function accountAction(){
//pretty much a copy of FOSUserBundle\Controller\ChangePasswordController\changePasswordAction()
$user = $this->get('security.context')->getToken()->getUser();
$form = $this->container->get('fos_user.change_password.form');
$formHandler = $this->container->get('fos_user.change_password.form.handler');
$process = $formHandler->process($user);
if ($process) {
//password has been changed, response will be generated
}
//more stuff going on here
$moreStuff = ...
//render view
return $this->render('AcmeMyBundle:Backend:account.html.twig', array(
'form' => $form->createView(),
'moreStuff' => $moreStuff
));
}

IMO rendering more than one form in one action is not a good idea.
Always try to separate things and let an action handle only one feature.
In your twig template I suggest to use the render method :
{% render 'AcmeBundle:SomeAction' with{'param:param} %}
It will generate a GET request on the action provided with some params if needed.
Create one action that will render the twig template with subrequests :
// AcmeUserBundle:editAction
{% render 'AcmeUserBundle:changePasswordAction' %}
{% render 'AcmeUserBundle:settingsAction' %}
{% render 'AcmeUserBundle:profileAction' %}
And then you'll need to create one action per form.
For password and username modification you can also override FOSUserBundle views if your needs are only visual. If you need to add/remove a field on the form you will need to create a new service.
I sugget reading FOSUserBundle documentation about overriding :
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md#next-steps

Related

Symfony2 app.request.get('_route') is empty when throw new AccessDeniedException

I'm working on implementing a ROLE based admin application. I have a custom voter and at some point I'm doing something like:
if($role && VoterInterface::ACCESS_GRANTED !== $voteResult) {
throw new AccessDeniedException('Unauthorized access!');
}
and the result is that a custom error403.html.twig template is rendered.
So far so good.
The error403 template extends the main template in which at some point I'm building a menu using app.request.get('_route') for generating the links.
The problem is app.request.get('_route') is null.
xDebug-ing the issue I've noticed that somehow the $request->attributes->parameters array does not contain _route or _route_params keys.
Any thoughts?
The problem is Symfony uses sub-request for rendering error pages. It doesn't need a router and you have not exatly the same request object as in master request.
Github issue
https://github.com/symfony/symfony/issues/5804
Same question on SO
app.request.attributes.get('_route') is empty when I override 404 error page
Some theory
https://knpuniversity.com/screencast/symfony-journey/sub-request-internals
You can write your own exception listener and modify this behaviour in some way.

Creating menu on symfony2.3 using render

on my Admin template I've used render function to add the menu.
The controller sidebar add all links from db.
The problem is made when i want to add "current" class because i can't access of current url/controller from a render request.
{{ render(controller('AdminDashboardBundle:Template:sidebar')) }}
How I can access to all informations from the render controller (without pass a var )?
Thanks
The RequestStack service has been built with Symfony 2.4. If you declare your Template controller as a service and inject RequestStack, you'll be able to use your current render call without passing arguments.
But you are speaking about Symfony 2.3, and unfortunately I don't think it is possible to do what you want without arguments. Here are some example on how to pass the current route / the URL as an argument of your controller.
1) Passing the URL :
{{
render(controller('AdminDashboardBundle:Template:sidebar', {
'url': app.request.requesturi
}))
}}
2) Passing the route :
{{
render(controller('AdminDashboardBundle:Template:sidebar', {
'route': app.request.attributes.get('_route'),
'route_params': app.request.attributes.get('_route_params')
}))
}}
I know you want to use Symfony2.3 and this call without passing vars, I think that's simply not possible because of how work scopes.

How to send data to base template without controller?

I need to get the data from entity in the base template (twig), but not rendering this data from controller.
Specifically, I want to realize the menu. Menu labels stored in the database (Page entity). I have many controllers and I don't want to repeat the code of the entity handling in each controller.
I could extend the controller's classes, but I want to avoid stuff like this:
return $this->render('... .html.twig',
array(
...
'menu' => $labels,
...
)
);
in each of the controllers.
This is the perfect use-case for an embedded controller. You can call a new controller from your template for rendering a part of your response, in this case the menu. It can have any logic a controller can, meaning you can query your database, build your menu structure and render a twig file for outputting it as html.

Creating forms manually in Symfony2, but still use its CSRF and isValid() functionalily

OK, I googled this hard, but everything I find talks about Symfony forms in context of regular Symfony form processing (e.g. form_widget(), creating FormType class, etc.). I have many such forms in my Symfony project, they work great.
BUT:
I also have some pretty complex AJAX forms that I would like to build manually (using plain old HTML and JS). I do still want to utilize Symfony's Form validation capabilities and CSRF protection. However, for some reason I can't get CSRF working when using isValid() for manually created forms.
This is an example of what I am trying to accomplish:
In my view controller I set _token:
$_token = $this->get('form.csrf_provider')->generateCsrfToken('form');
In my view (manually created form) (getting _token from my view controller):
<html>
<form method="post">
<input type="hidden" name="form[_token]" value="{{ _token }}">
<input type="hidden" name="form[id]" value="1">
<input type="submit" value="Submit">
</form>
</html>
In my action controller (when form submitted, I am TRYING to do the following):
//Create form (for validation purposes)
$form = $this->get('form.factory')
->createBuilder('form', array('id' => $request->get('id')))
->add('id', 'hidden')
->getForm();
//Bind form
$form->bind($request)
//Validate form
if($form->isValid()) {
//... save data
}
//Return response...
For some reason I can't get isValid() working, I suspect that my _token is thing not properly used, but I am out of ideas why. Have anyone actually made manually forms work with Symfony components? Does anyone have any suggestions on how to make this work?
Basically, what I want to accomplish is:
Manually create HTML form (with CSFR protection and without TWIG form widget functions)
Use Symfony's form functionality to validate that form
Thank you.
I think you're mismatching the intention here (argument passed to your CSRF provider). I tried generating form as you wrote above and break-pointed the generation of token. The value was unknown.
So, try passing unknown instead of form to your generateCsrfToken call and hopefully it should work. ;)
EDIT:
I have just finished some digging and it now does make perfect sense.
Look at the class FormTypeCsrfExtension. Apparently, it's the default extension used for CSRF token protection. On the line #80 (might not be this one exactly in your case) there is method setDefaultOptions that is usually overridden in your form types. Anyhow, there is a default options called intention that has a value of unknown ==> the one we are seeing here.
My guess is that you could easily override this option in your own form type just by passing intention and setting your own value (just as you would pass csrf_protection => false when you would want to disable CSRF protection altogether).
Note: intention is no longer default to be unknown. When I checked this out in symfony 2.3, it would appear it to default to the type name:
$options['intention'] ?: ($builder->getName() ?: get_class($builder->getType()->getInnerType()))
It would be good if there was some programatic way to get the intention out that is used instead of having to rely on these defaults.
I have seen that this is resolved but I have some form troubles too and saw an other post here :
symfony2 CSRF invalid
And their solution seems to me better than making my own token :
There is no problem using {{ form_widget(form) }} to build your custom form. All you have to do is add the _token like this: {{ form_widget(form._token) }}
My solution using Symfony 2.8:
in the action Controller (when the form is submitted):
$theToken = $request->get('Token');
//Token Verification
$isValidToken = $this->isCsrfTokenValid('your_intention', $theToken);
if ($isValidToken === false)
{
// Error
}
check this page where I found the info:
http://api.symfony.com/2.8/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.html#method_isCsrfTokenValid

drupal: logged in status message?

when an user login in Drupal, the login block disappear from my pages.
I would like instead to keep it there with a message stating the user is logged in.
Something like this (with logout link): "Welcome Patrick. Logout"
thanks
The login-form will not work for not logged in-users.
What is easiest, is to make a new block (admin » build » blocks » new(tab))
Then set that block to show only for role "authenticated". That way, the block will work exact opposite from the user-login block, which shows only for role: anonymous.
You can add any custom text in there, as long as it is static text. If you want to include dynamic text, such as "Hello $username, you will have to go for the tip by Jeremy French: create a module with its own hook_block.
The login block is created by the follwing code from the user_block() function.
if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
$block['subject'] = t('User login');
$block['content'] = drupal_get_form('user_login_block');
}
There is no code to display a message if you are logged in.
One simple way to do what you want would be to create a module which implements the above code in hook_block, and has an elese statement which displays what you want.
A quick way to solve your problem, is to create a new block with whatever you want in it, and only show it when the user is logged in. If you arrange the placement and style, it will look like the same block, even though they are different.
You can create the new block with a custom module or views.

Resources