diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b0c234..b1ce2afc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,44 @@ All notable changes to this project will be documented in this file, in reverse - Nothing. +## 3.4.3 - 2024-07-07 + + +----- + +### Release Notes for [3.4.3](https://github.com/nucleos/NucleosUserBundle/milestone/44) + +3.4.x bugfix release (patch) + +### 3.4.3 + +- Total issues resolved: **0** +- Total pull requests resolved: **1** +- Total contributors: **1** + +#### Bug + + - [759: Fix setting locale](https://github.com/nucleos/NucleosUserBundle/pull/759) thanks to @core23 + +## 3.4.2 - 2024-07-07 + + +----- + +### Release Notes for [3.4.2](https://github.com/nucleos/NucleosUserBundle/milestone/43) + +3.4.x bugfix release (patch) + +### 3.4.2 + +- Total issues resolved: **0** +- Total pull requests resolved: **1** +- Total contributors: **1** + +#### Bug + + - [757: Fix unsetting locale](https://github.com/nucleos/NucleosUserBundle/pull/757) thanks to @core23 + ## 3.4.1 - 2024-05-20 diff --git a/src/EventListener/LocaleEventListener.php b/src/EventListener/LocaleEventListener.php index 66eb523e..cd7e4700 100644 --- a/src/EventListener/LocaleEventListener.php +++ b/src/EventListener/LocaleEventListener.php @@ -26,6 +26,10 @@ final class LocaleEventListener implements EventSubscriberInterface { + private const ATTR_LOCALE = '_locale'; + + private const ATTR_TIMEZONE = '_timezone'; + private readonly LocaleAwareTranslator $translator; public function __construct(LocaleAwareTranslator $translator) @@ -52,8 +56,8 @@ public function onImplicitLogin(UserEvent $event): void return; } - $this->setLocale($event->getRequest(), $user); - $this->setTimezone($event->getRequest(), $user); + $this->setLocale($event->getRequest(), $user->getLocale()); + $this->setTimezone($event->getRequest(), $user->getTimezone()); } public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void @@ -64,8 +68,8 @@ public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void return; } - $this->setLocale($event->getRequest(), $user); - $this->setTimezone($event->getRequest(), $user); + $this->setLocale($event->getRequest(), $user->getLocale()); + $this->setTimezone($event->getRequest(), $user->getTimezone()); } public function onKernelRequest(RequestEvent $event): void @@ -78,9 +82,8 @@ public function onKernelRequest(RequestEvent $event): void $session = $request->getSession(); - if (null !== $locale = $session->get('_locale')) { - $this->translator->setLocale($locale); - $request->setLocale($locale); + if (null !== $locale = $session->get(self::ATTR_LOCALE)) { + $this->setLocale($request, $locale); } } @@ -89,7 +92,7 @@ public function onTimezoneChanged(UserEvent $event): void $user = $event->getUser(); if ($user instanceof LocaleAwareUser && null !== $event->getRequest()) { - $this->setTimezone($event->getRequest(), $user); + $this->setTimezone($event->getRequest(), $user->getTimezone()); } } @@ -98,11 +101,11 @@ public function onLocaleChanged(UserEvent $event): void $user = $event->getUser(); if ($user instanceof LocaleAwareUser && null !== $event->getRequest()) { - $this->setLocale($event->getRequest(), $user); + $this->setLocale($event->getRequest(), $user->getLocale()); } } - private function setLocale(Request $request, LocaleAwareUser $user): void + private function setLocale(Request $request, ?string $locale): void { if (!$request->hasSession()) { return; @@ -110,30 +113,31 @@ private function setLocale(Request $request, LocaleAwareUser $user): void $session = $request->getSession(); - $locale = $user->getLocale(); - if ('' === $locale || null === $locale) { + $session->remove(self::ATTR_LOCALE); + return; } $this->translator->setLocale($locale); $request->setLocale($locale); - $session->set('_locale', $locale); + $session->set(self::ATTR_LOCALE, $locale); } - private function setTimezone(Request $request, LocaleAwareUser $user): void + private function setTimezone(Request $request, ?string $timezone): void { if (!$request->hasSession()) { return; } - $timezone = $user->getTimezone(); + $session = $request->getSession(); if ('' === $timezone || null === $timezone) { + $session->remove(self::ATTR_TIMEZONE); + return; } - $session = $request->getSession(); - $session->set('_timezone', $timezone); + $session->set(self::ATTR_TIMEZONE, $timezone); } } diff --git a/tests/EventListener/LocaleEventListenerTest.php b/tests/EventListener/LocaleEventListenerTest.php new file mode 100644 index 00000000..16a40d57 --- /dev/null +++ b/tests/EventListener/LocaleEventListenerTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nucleos\UserBundle\Tests\EventListener; + +use Nucleos\UserBundle\Event\UserEvent; +use Nucleos\UserBundle\EventListener\LocaleEventListener; +use Nucleos\UserBundle\NucleosUserEvents; +use Nucleos\UserBundle\Tests\App\Entity\TestUser; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\TestBrowserToken; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\SecurityEvents; +use Symfony\Contracts\Translation\LocaleAwareInterface as LocaleAwareTranslator; + +final class LocaleEventListenerTest extends TestCase +{ + /** + * @var LocaleAwareTranslator&MockObject + */ + private LocaleAwareTranslator $translator; + + private LocaleEventListener $listener; + + protected function setUp(): void + { + $this->translator = $this->createMock(LocaleAwareTranslator::class); + $this->listener = new LocaleEventListener($this->translator); + } + + #[Test] + public function getSubscribedEvents(): void + { + self::assertSame([ + NucleosUserEvents::SECURITY_IMPLICIT_LOGIN => 'onImplicitLogin', + SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', + KernelEvents::REQUEST => [['onKernelRequest', 20]], + NucleosUserEvents::USER_LOCALE_CHANGED => 'onLocaleChanged', + NucleosUserEvents::USER_TIMEZONE_CHANGED => 'onTimezoneChanged', + ], LocaleEventListener::getSubscribedEvents()); + } + + #[Test] + public function onImplicitLogin(): void + { + $session = new Session(new MockArraySessionStorage()); + + $request = new Request(); + $request->setSession($session); + + $user = new TestUser(); + $user->setLocale('fr'); + $user->setTimezone('Europe/Paris'); + + $event = new UserEvent($user, $request); + + $this->translator->expects(self::once()) + ->method('setLocale') + ->with('fr') + ; + + $this->listener->onImplicitLogin($event); + + self::assertSame('fr', $request->getLocale()); + self::assertSame('fr', $session->get('_locale')); + self::assertSame('Europe/Paris', $session->get('_timezone')); + } + + #[Test] + public function onSecurityInteractiveLogin(): void + { + $session = new Session(new MockArraySessionStorage()); + + $request = new Request(); + $request->setSession($session); + + $user = new TestUser(); + $user->setLocale('fr'); + $user->setTimezone('Europe/Paris'); + + $event = new InteractiveLoginEvent($request, new TestBrowserToken(user: $user)); + + $this->translator->expects(self::once()) + ->method('setLocale') + ->with('fr') + ; + + $this->listener->onSecurityInteractiveLogin($event); + + self::assertSame('fr', $request->getLocale()); + self::assertSame('fr', $session->get('_locale')); + self::assertSame('Europe/Paris', $session->get('_timezone')); + } + + #[Test] + public function onKernelRequest(): void + { + $session = new Session(new MockArraySessionStorage()); + $session->set('_locale', 'fr'); + $session->set('_timezone', 'Europe/Paris'); + + $request = new Request(); + $request->setSession($session); + $request->cookies->set($session->getName(), $session->getId()); + + $event = new RequestEvent($this->createMock(KernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST); + + $this->translator->expects(self::once()) + ->method('setLocale') + ->with('fr') + ; + + $this->listener->onKernelRequest($event); + + self::assertSame('fr', $request->getLocale()); + self::assertSame('fr', $session->get('_locale')); + self::assertSame('Europe/Paris', $session->get('_timezone')); + } + + #[Test] + public function onTimezoneChanged(): void + { + $session = new Session(new MockArraySessionStorage()); + + $request = new Request(); + $request->setSession($session); + + $user = new TestUser(); + $user->setTimezone('Europe/Paris'); + + $event = new UserEvent($user, $request); + + $this->listener->onTimezoneChanged($event); + + self::assertSame('Europe/Paris', $session->get('_timezone')); + } + + #[Test] + public function onLocaleChanged(): void + { + $session = new Session(new MockArraySessionStorage()); + + $request = new Request(); + $request->setSession($session); + + $user = new TestUser(); + $user->setLocale('de'); + + $event = new UserEvent($user, $request); + + $this->translator->expects(self::once()) + ->method('setLocale') + ->with('de') + ; + + $this->listener->onLocaleChanged($event); + + self::assertSame('de', $request->getLocale()); + self::assertSame('de', $session->get('_locale')); + } +}