Symfony 2 : HWiOauthBundle : Overriding connect_success.html.twig - symfony

I have implemented both FOSUserBundle and HWIOauthBundle in my Symfony 2 project. Basically, everything works fine but I would like to customize a litle bit more. Typically I use the connect functionnality of the HWIOauthBundle to connect oauth account to user already connected with the authentication form (FOSUserBundle).
In case of succes, the controller action HWIOAuthBundle:Connect:connectService displays the twig template connect_success.html.twig. At this point, I would like to override this template and do the following actions :
Create a flash message
Display the flash message on my homepage
You can obtain this behavior easily with FOSUserBundle as this bundle dispatchs many events to hook into the controllers. But with HWIOauthBundle this is not possible.
My solution is the following :
1/ I override connect_success.html.twig by placing the same named file in app/Ressources/HWIOauthBundle/views/Connect with the following code:
{{ render(controller('MyUserBundle:User:HWIOAuthFlash')) }}
2/ In my user controller (MyUserBundle:User), I create an action HWIOAuthFlashAction() that defines a flash message and forwards to the controller action that displays he homepage(MyMainBundle:Main:homepage)
public function HWIOAuthFlashAction()
{
// Here : flash message definition
return $this->forward('MyMainBundle:Main:homepage');
}
At this point the homepage is displayed with the flash message. But I had to remove two links in the homepage template (homepage.twig.html)that permits the user to switch between two locales.
The following code is the one that I had to remove from my template :
<ul>
<li>FR</li>
<li>EN</li>
</ul>
I understand that that the special variable _route is null. And I get the following message :
An exception has been thrown during the rendering of a template
("Error when rendering
"xxxxx/web/app_dev.php/connect/service/google?key=yyyyyyy" (Status
code is 500).") in HWIOAuthBundle:Connect:connect_success.html.twig at
line 3.
I have two questions :
To achieve my goal, is it the good way ?
How to say to symfony that I want my route to be homepage ?

As proposed in my comment, here is my solution :
In my homepage template homepage.twig.html :
{% set route = app.request.get('_route') ? app.request.get('_route') : 'homepage' %}
<ul>
<li>FR</li>
<li>EN</li>
</ul>

Related

Can't use render(controller) in SonataAdminBundle template

I create custom controller(extending Sonata\AdminBundle\Controller\CRUDController) and action.
When render this action {{render(controller('MainBundle:SonataAdmin/Order:searchCertificate'))}}
I get Symfony error:
An exception has been thrown during the rendering of a template ("There is no _sonata_admin defined for the controller MainBundle\Controller\SonataAdmin\OrderController and the current route").
I found answer in official documentation:
If you want to render a custom controller action in a template by
using the render function in twig you need to add _sonata_admin as an
attribute. For example; {{
render(controller('AppBundle:XxxxCRUD:comment', {'_sonata_admin':
'sonata.admin.xxxx' })) }}. This has to be done because the moment the
rendering should happen the routing, which usually sets the value of
this parameter, is not involved at all, and then you will get an error
"There is no _sonata_admin defined for the controller
AppBundleControllerXxxxCRUDController and the current route ' '."
i have solved this Problem by setting the _sonata_admin in the comming request:
with normal Controller:
$request->request->set('_sonata_admin','admin.template');

How to hide link by guard angular2

How to hide admin link in view html. I have to guard :Admin and Manager
Router config :
{
path: 'manager',
component: ManagerComponent,
canActivate: [ManagerGuard]
},
{
path: 'user',
component: UserAdminComponent,
canActivate: [AdminGuard]
}
In view :
<li>
<a routerLink="/user" routerLinkActive="active-link">User</a>
</li>
I want to hide link on /user for ManagerGuard when but show for AdminGuard.
Also if already answered in a way which might be helpful, I used here another approach. As you already have your canActivate method in the Guard, you could also inject your navigation component with that Guards and you can call these canActivate methods directly:
In your component which holds the navigation
constructor(private adminGuard: AdminGuard)
and then in the template
<li *ngIf="adminGuard.canActivate()">
<a routerLink="/user" routerLinkActive="active-link">User</a>
</li>
Edit
This does not work in prod mode in my case (if you use parameters which have to be injected). I just tried to compile it but angular complains about the missing parameters. If you do not use parameters, this works fine, or, if you do not use the parameters in your function - then simply pass
<li *ngIf="adminGuard.canActivate(null,null)">
<a routerLink="/user" routerLinkActive="active-link">User</a>
</li>
Another thing: If you is variables like the 'adminGuard' above in the template, it must be public - not private.
In my opinion this has nothing to do with the router itself.
Your Guards may call another service which has the information about which kind of user is logged in: Admin or Manager.
You should create a service that knows the type of the user. Then inject this service with Dependency Injection into your component where you have the routerLink.
There you can ask the service and toggle the link with *ngIf:
<li *ngIf="myService.getCurrentUser().isAdmin()">
<a routerLink="/user" routerLinkActive="active-link">User</a>
</li>
So the service provides a function that gives you the user which is currently logged in and the isAdmin()-function of the user returns true or false. If the user is admin then the <li> will be shown, otherwise it will be hidden.
This is just an example but I hope you get the point. It has more to do with basic functionality of Angular 2 rather than the router of Angular 2. More information about *ngIf can be found here.

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.

FOSUserBundle: Change Password within actual project

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

Resources