I have a problem with translation. I use
symfony 2.7
sonata admin-bundle 2.3
I have create the interactive login listener, when the user log in the application I get the user locale and set the session _locale, but this is ignore in sonata.
Listener is
class UserLocaleListener {
/**
* #var Session
*/
private $container;
public function __construct(Session $session)
{
$this->session = $session;
}
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$request = $event->getRequest();
$user = $event->getAuthenticationToken()->getUser();
if (null !== $user->getLocale()) {
$this->session->set('_locale', $user->getLocale());
//$request->setLocale($user->getLocale());
var_dump($request->getSession()->get('_locale'));
}
}
}
in service.yml add
app.user_locale_listener:
class: xxxxxx\xxxxxxxx\EventListener\UserLocaleListener
arguments: ["#session"]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }
Where is my mistake ?
the locale is set on the request, and will not "stick" , so each request it will be the default again, unless you do something like this:
http://symfony.com/doc/current/cookbook/session/locale_sticky_session.html
Wich will on each request, take the locale from the session, and set it on the current request.
(make sure that LocaleListener has a lower priority then your UserLocaleListener, so that it runs after it)
Related
What I want to do is show logged user only content that he has access to.
First thing I was doing access_control in security.yml & redirect but i must do at least 30 deferent accounts;/
Next i create twig extension that will connect to DB and get current logged user specific settings -
access to panels. Is this good way?
The problem is
$user = $this->getUser()->getId();
$currentUser = $this->em->getRepository('AppBundle:User')->find($user);
It will not work, Blank page appears in dev env
But when i put 1
$currentUser = $this->em->getRepository('AppBundle:User')->find(1);
& 1 is user id everything is ok.
services.yml
app.twig.users_extension:
class: AppBundle\Twig\Extension\AccesExtension
arguments: ["#doctrine.orm.entity_manager","#security.token_storage"]
tags:
- { name: twig.extension }
Twig Extension
class AccesExtension extends \Twig_Extension
{
protected $em;
protected $tokenStorage;
public function __construct(EntityManager $em, TokenStorage $tokenStorage)
{
$this->em = $em;
$this->tokenStorage = $tokenStorage;
}
public function getUser()
{
return $this->tokenStorage->getToken()->getUser();
}
public function getGlobals()
{
$user = $this->getUser()->getId();
$currentUser = $this->em->getRepository('AppBundle:User')->find($user);
return array (
"acces" => $currentUser,
);
}
public function getName()
{
return "AppBundle:AccesExtension ";
}
}
As people in the comments already told you, you can use security voters.
Here is a simple tutorial with video/text that explains the use of voters and also how to use them in twig, this should help you with the problem you have.
I'm working on a Symfony 2.7 project and I'd like to redirect users on a specific page if their session expires.
I created a Listener that checks roles and routes and if a user is trying to access an authenticated route without being authenticated i redirect to the specific page.
This is not working for the moment because the default redirection to homepage has an higher priority.
This is the listener:
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
class UserDisconnectionListener
{
protected $router;
protected $security;
protected $tokenStorage;
public function __construct(Router $router, AuthorizationChecker $security, TokenStorage $tokenStorage)
{
$this->router = $router;
$this->security = $security;
$this->tokenStorage = $tokenStorage;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$loggedRoutes = ['user_account', 'user_index', '...'];
$url = '';
if($this->tokenStorage->getToken() !== null) {
if(!$this->security->isGranted('IS_AUTHENTICATED_OPENID')) {
if (in_array($request->get('_route'), $loggedRoutes)) {
$url = $this->router->generate('disconnection_route');
}
}
if(strlen($url) > 0) {
$response = new RedirectResponse($url);
$event->setResponse($response);
}
}
}
}
app.userroute.listener:
class: app\UserBundle\Listener\UserDisconnectionListener
arguments: ['#router.default', #security.authorization_checker, #security.token_storage]
scope: request
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Is there a better way to do it ?
I tried to use priority tag but i don't which priority set.
I tried to log and when i tried to access 'user_index' route my listerner log said that i was already redirected on the homepage 'general_index' route.
PS: I'm using FOSUserBundle coupled with an OpenID Bundle.
tags:
- { name: kernel.event_listener, priority: -256, event: kernel.request, method: onKernelRequest }
https://symfony.com/doc/2.7/event_dispatcher.html check the symfony documentation for events, and search for priority
This tag option specify to symfony in which order he haves to call listeners.
It isn't the best way to 'hardcode' the list of authenticated routes, if you forget to add one your code won't work. You should try to retrieve dynamically the authenticated routes
I want to store the last locale used by a user on every Request the user makes. I already created a Field in the Database and only need the best practice way without big modifications in every Controller.
Thank you all.
You could store your locale in session and use it with an event listener. And on user login set user locale in session.
-LocaleListener : Store the locale in session and user locale in session
-UserLocaleListener : On user login set user locale in session
<?php
namespace YourApp\YourBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest17(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request, use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest17', 17)),
);
}
}
Second service:
<?php
namespace YourApp\YourBundle\EventListener;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class UserLocaleListener
{
private $session;
public function setSession(Session $session)
{
$this->session = $session;
}
/**
* kernel.request event. If a guest user doesn't have an opened session, locale is equal to
* "undefined" as configured by default in parameters.ini. If so, set as a locale the user's
* preferred language.
*
* #param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
*/
public function setLocaleForUnauthenticatedUser(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if ('undefined' == $request->getLocale()) {
if ($locale = $request->getSession()->get('_locale')) {
$request->setLocale($locale);
} else {
$request->setLocale($request->getPreferredLanguage());
}
}
}
/**
* security.interactive_login event. If a user chose a language in preferences, it would be set,
* if not, a locale that was set by setLocaleForUnauthenticatedUser remains.
*
* #param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event
*/
public function setLocaleForAuthenticatedUser(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if ($lang = $user->getLocale()) {
$event->getRequest()->setLocale($lang);
$this->session->set('_locale', $lang);
}
}
}
services.yml
services:
yourapp_your.locale_listener:
class: YourApp\YourBundle\EventListener\LocaleListener
arguments: ["%kernel.default_locale%"]
tags:
- { name: kernel.event_subscriber }
yourapp_your.locale.interactive_login_listener:
class: YourApp\YourBundle\EventListener\UserLocaleListener
calls:
- [ setSession, [#session] ]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: setLocaleForAuthenticatedUser }
This is using
https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md#simulate-old-behavior
Translations in Symfony 2.3 locale in request
Symfony2 locale detection: not considering _locale in session
symfony 2 set locale based on user preferences stored in DB
That is my implementation to save the locale on each request in the database.
It is necessary for me because i need the locale persisted for the newsletter.
LocaleListener:
namespace MyApp\MyBundle\EventListener;
use \Symfony\Component\HttpKernel\Event\GetResponseEvent;
use \Symfony\Component\HttpKernel\KernelEvents;
use \Symfony\Component\EventDispatcher\EventSubscriberInterface;
use \FOS\UserBundle\Model\UserManagerInterface;
use \Symfony\Component\Security\Core\SecurityContext;
use \MyApp\MyBundle\Entity\User;
class LocaleListener implements EventSubscriberInterface
{
private $userManager;
private $securityContext;
private $defaultLocale;
public function __construct(UserManagerInterface $userManager, SecurityContext $securityContext, $defaultLocale = 'en')
{
$this->userManager = $userManager;
$this->securityContext = $securityContext;
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest17(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
// try to see if the locale has been set as a _locale routing parameter
$locale = $request->attributes->get('_locale');
if ($locale !== null)
{
$request->getSession()->set('_locale', $locale);
}
else
{
// if no explicit locale has been set on this request, use one from the session
$locale = $request->getSession()->get('_locale', $this->defaultLocale);
$request->setLocale($locale);
}
// save last locale to the user if he is logged in
$user = $this->getUser();
if($user instanceof User)
{
$user->setDefaultLanguage($locale);
$this->userManager->updateUser($user);
}
}
private function getUser()
{
$token = $this->securityContext->getToken();
if($token !== null)
return $this->securityContext->getToken()->getUser();
}
public static function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest17', 17)),
);
}
}
services.yml:
myApp_myBundle.locale_listener:
class: MyApp\MyBundle\EventListener\LocaleListener
arguments: [#fos_user.user_manager, #security.context, "%kernel.default_locale%"]
tags:
- { name: kernel.event_subscriber }
I'm trying to implement a LocaleListener that detects user's preferred language (considering Accept-Language header) and stores it in session to avoid checking it every request. I've developed the code below to accomplish this:
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$preferredLocale = $request->getPreferredLanguage($this->availableLocales);
if ($this->container->has('session')) {
$session = $this->container->get('session');
if (!$session->has('_locale')) {
$session->set('_locale', $preferredLocale);
}
} else {
$request->setLocale($preferredLocale);
}
}
The code is working, the preferred language is being stored in session, but symfony isn't considering the locale stored in session to translate strings. In my case, my preferred language was 'pt_BR' and when I escape:
{{ app.request.locale }}
symfony is escaping 'en'. Shouldn't symfony be considering the value stored in session('_locale') to define request locale? Is this a correct behavior? How can I accomplish that?
Here is a working language listener. the second method is to change the language to the users preferences, which the user chooses. You can omit this method, if your user haven't the facility to define their language.
<?php
namespace Acme\UserBundle\EventListener;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class LanguageListener
{
private $session;
public function setSession(Session $session)
{
$this->session = $session;
}
/**
* kernel.request event. If a guest user doesn't have an opened session, locale is equal to
* "undefined" as configured by default in parameters.ini. If so, set as a locale the user's
* preferred language.
*
* #param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
*/
public function setLocaleForUnauthenticatedUser(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if ('undefined' == $request->getLocale()) {
if ($locale = $request->getSession()->get('_locale')) {
$request->setLocale($locale);
} else {
$request->setLocale($request->getPreferredLanguage());
}
}
}
/**
* security.interactive_login event. If a user chose a language in preferences, it would be set,
* if not, a locale that was set by setLocaleForUnauthenticatedUser remains.
*
* #param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event
*/
public function setLocaleForAuthenticatedUser(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if ($lang = $user->getLanguage()) {
$this->session->set('_locale', $lang);
}
}
}
in your services.yml:
services:
acme.language.interactive_login_listener:
class: Acme\UserBundle\EventListener\LanguageListener
calls:
- [ setSession, [#session] ]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: setLocaleForAuthenticatedUser }
acme.language.kernel_request_listener:
class: Acme\UserBundle\EventListener\LanguageListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: setLocaleForUnauthenticatedUser }
Oh, and you have to define an undefined fallback_language in config.yml to get it work.
framework:
translator: { fallback: "undefined" }
default_locale: "en"
As of symfony 2.1, the locale is not stored in the session, but in the request. What you can do to solve it:
restore the old way of saving the locale. You can read how to do this in the upgrade file
edit the LocaleListener to store the locale in the request:
if (!$request->attributes->has('locale')) {
$request->setLocale(...);
}
Following Symfony2 guide about translation i found that inferred locale from http headers (stored in $this->get('session')->getLocale()) is wrong (sent it, inferred en):
Host localhost User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64;
rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Accept text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Is this a normal behaviour? Or should i set something in order to get localization working out of the box?
I looked more thoroughly onto the code today, because I was experiencing the same problem as you, and it appears that the language comes from Session::getLocale(). But Symfony2 never calls Session::setLocale(), and sets the locale member of the Session object. A google search for "symfony2 session setlocale" leads to this ยง of the documentation
So I ended up putting this line on top of the controller I was working on, and it worked :
$this->getRequest()->getSession()->setLocale(
$this->getRequest()->getPreferredLanguage());
Now I guess this is not acceptable, because you're not going to add this on top of each and every controller. Plus, this should not be done for every request, it should only be done for the first one, when the user has no session yet. If anyone knows how to do this feel free to edit this answer.
per HTTP-Standard you should be using a different URL for each translated version of the page. What remains is a simple action that will infer the best-to-use locale from the request and redirect to the corresponding page:
/**
* #Route("/")
*/
public function localeRedirectAction() {
/* #var $request \Symfony\Component\HttpFoundation\Request */
/* #var $session \Symfony\Component\HttpFoundation\Session */
$req = $this->getRequest();
$session = $this->get('session');
$session->setLocale($req->getPreferredLanguage(array('de', 'en')));
return $this->redirect($this->generateUrl('home'));
}
if you need to do this for any page, you'll basically need to do the same, but within a Listener for the kernel.request-Event. In order to be reliably called after the route-matcher did it's work you should set the priority of the listener to a value < 0:
# services.yml
services:
my_locale_listener:
class: Namespace\LocaleListener
tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
arguments: [ #service_container, [ 'en', 'fr', 'de', 'es', 'it' ] ]
the listener would then look like this:
class LocaleListener {
public function __construct($container, $availableLocales) {
$this->container = $container;
$this->availableLocales = $availableLocales;
}
public function onKernelRequest(GetResponseEvent $e) {
$req = $e->getRequest();
$locale = $req->getPreferredLanguage($this->availableLocales);
// generate a new URL from the current route-name and -params, overwriting
// the current locale
$routeName = $req->attributes->get('_route');
$routeParams = array_merge($req->attributes->all(), array('_locale' => $locale));
$localizedUrl = $this->container->get('router')->generateUrl($routeName, $routeParams);
$e->setResponse(new RedirectResponse($localizedUrl));
}
}
P.S. i'm not entirely confident that this code actually works, but it should give a basic idea on how this could be done.
You can register listener like follow:
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class LocaleListener
{
private $container;
private $defaultLocale;
public function __construct(ContainerInterface $container, $defaultLocale = 'nl')
{
$this->container = $container;
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
if (!$this->container->has('session')) {
return;
}
$session = $this->container->get('session');
$session->setLocale($this->defaultLocale);
}
}
(gist)
Just after framework session setup stage:
<service id="my.event_listener.locale_listener" class="MyBundle\MyEventListener\LocaleListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="100" />
<argument type="service" id="service_container" />
</service>
(gist)
I wrote a LocaleListener which is not redirecting you to a locale-specific url, it does however set the locale settings for you ;)
Code in the services.yml
services:
acme.demobundle.listener.request:
class: Namespace\LocaleListener
tags: [{ name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: -255 }]
arguments: [ #service_container, [ 'en', 'fr', 'de', 'it', 'es' ] ]
and the actual listener:
class LocaleListener
{
protected $container;
protected $availableLocales;
public function __construct(\Symfony\Component\DependencyInjection\Container $container, $availableLocales) {
$this->container = $container;
$this->availableLocales = $availableLocales;
}
public function onKernelRequest(GetResponseEvent $e) {
$req = $e->getRequest();
$locale = $req->getPreferredLanguage($this->availableLocales);
$session = $this->container->get('session');
$session->setLocale($locale);
}
}