Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/bundle/Resources/config/services/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,14 @@ services:

Ibexa\Contracts\AdminUi\ContentType\ContentTypeFieldsByExpressionServiceInterface:
'@Ibexa\AdminUi\ContentType\ContentTypeFieldsByExpressionService'

Ibexa\AdminUi\Request\Resolver\AdminUiContentLanguageCodeResolver:
tags:
- { name: ibexa.admin_ui.content_language_code_resolver, priority: 100 }

Ibexa\AdminUi\Request\Resolver\ChainContentLanguageCodeResolver:
arguments:
$resolvers: !tagged_iterator ibexa.admin_ui.content_language_code_resolver

Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface:
'@Ibexa\AdminUi\Request\Resolver\ChainContentLanguageCodeResolver'
14 changes: 14 additions & 0 deletions src/contracts/Request/ContentLanguageContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\AdminUi\Request;

final class ContentLanguageContext
{
public const ATTRIBUTE_NAME = 'contentLanguageCode';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Contracts\AdminUi\Request\Resolver;

use Symfony\Component\HttpFoundation\Request;

interface ContentLanguageCodeResolverInterface
{
public function resolve(Request $request): ?string;
}
50 changes: 50 additions & 0 deletions src/lib/EventListener/NormalizedLanguageCodeListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\EventListener;

use Ibexa\Contracts\AdminUi\Request\ContentLanguageContext;
use Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;

final class NormalizedLanguageCodeListener implements EventSubscriberInterface
{
private ContentLanguageCodeResolverInterface $resolver;

public function __construct(ContentLanguageCodeResolverInterface $resolver)
{
$this->resolver = $resolver;
}

public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 12],
];
}

public function onKernelRequest(RequestEvent $event): void
{
if (HttpKernelInterface::MAIN_REQUEST !== $event->getRequestType()) {
return;
}

$request = $event->getRequest();
$normalizedLanguageCode = $request->attributes->get(ContentLanguageContext::ATTRIBUTE_NAME);
if (is_string($normalizedLanguageCode) && $normalizedLanguageCode !== '') {
return;
}

if (null !== $languageCode = $this->resolver->resolve($request)) {
$request->attributes->set(ContentLanguageContext::ATTRIBUTE_NAME, $languageCode);
}
}
}
35 changes: 35 additions & 0 deletions src/lib/Request/Resolver/AdminUiContentLanguageCodeResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\Request\Resolver;

use Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface;
use Ibexa\Contracts\Core\Repository\Values\Content\Language;
use Symfony\Component\HttpFoundation\Request;

final class AdminUiContentLanguageCodeResolver implements ContentLanguageCodeResolverInterface
{
public function resolve(Request $request): ?string

Check warning on line 17 in src/lib/Request/Resolver/AdminUiContentLanguageCodeResolver.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This method has 4 returns, which is more than the 3 allowed.

See more on https://sonarcloud.io/project/issues?id=ibexa_admin-ui&issues=AZ7f3kM40X-JifGOFK3C&open=AZ7f3kM40X-JifGOFK3C&pullRequest=1945
{
$language = $request->attributes->get('language');
if ($language instanceof Language) {
return $language->getLanguageCode();
}

if (is_string($language) && $language !== '') {
return $language;
}

$languageCode = $request->attributes->get('languageCode');
if (is_string($languageCode) && $languageCode !== '') {
return $languageCode;
}

return null;
}
}
39 changes: 39 additions & 0 deletions src/lib/Request/Resolver/ChainContentLanguageCodeResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\AdminUi\Request\Resolver;

use Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface;
use Symfony\Component\HttpFoundation\Request;

final class ChainContentLanguageCodeResolver implements ContentLanguageCodeResolverInterface
{
/** @var iterable<\Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface> */
private iterable $resolvers;

/**
* @param iterable<\Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface> $resolvers
*/
public function __construct(iterable $resolvers)
{
$this->resolvers = $resolvers;
}

public function resolve(Request $request): ?string
{
foreach ($this->resolvers as $resolver) {
$languageCode = $resolver->resolve($request);

if (is_string($languageCode) && $languageCode !== '') {
return $languageCode;
}
}

return null;
}
}
103 changes: 103 additions & 0 deletions tests/lib/EventListener/NormalizedLanguageCodeListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\AdminUi\EventListener;

use Ibexa\AdminUi\EventListener\NormalizedLanguageCodeListener;
use Ibexa\Contracts\AdminUi\Request\ContentLanguageContext;
use Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;

final class NormalizedLanguageCodeListenerTest extends TestCase
{
private NormalizedLanguageCodeListener $listener;

/**
* @var \Ibexa\Contracts\AdminUi\Request\Resolver\ContentLanguageCodeResolverInterface&\PHPUnit\Framework\MockObject\MockObject
*/
private ContentLanguageCodeResolverInterface $resolver;

protected function setUp(): void
{
$this->resolver = $this->createMock(ContentLanguageCodeResolverInterface::class);
$this->listener = new NormalizedLanguageCodeListener($this->resolver);
}

public function testLanguageCodeIsSetFromResolver(): void
{
$request = new Request();
$this->resolver
->expects(self::once())
->method('resolve')
->with($request)
->willReturn('eng-GB');

$this->listener->onKernelRequest($this->createEvent($request, HttpKernelInterface::MAIN_REQUEST));

self::assertSame('eng-GB', $request->attributes->get(ContentLanguageContext::ATTRIBUTE_NAME));
}

public function testNormalizedLanguageCodeHasPriorityOverResolver(): void
{
$request = new Request([], [], [ContentLanguageContext::ATTRIBUTE_NAME => 'eng-GB']);
$this->resolver
->expects(self::never())
->method('resolve');

$this->listener->onKernelRequest($this->createEvent($request, HttpKernelInterface::MAIN_REQUEST));

self::assertSame('eng-GB', $request->attributes->get(ContentLanguageContext::ATTRIBUTE_NAME));
}

public function testNullValueFromResolverIsIgnored(): void
{
$request = new Request();
$this->resolver
->expects(self::once())
->method('resolve')
->with($request)
->willReturn(null);

$this->listener->onKernelRequest($this->createEvent($request, HttpKernelInterface::MAIN_REQUEST));

self::assertNull($request->attributes->get(ContentLanguageContext::ATTRIBUTE_NAME));
}

public function testLanguageCodeIsNotChangedOnSubRequest(): void
{
$request = new Request();
$this->resolver
->expects(self::never())
->method('resolve');

$this->listener->onKernelRequest($this->createEvent($request, HttpKernelInterface::SUB_REQUEST));

self::assertNull($request->attributes->get(ContentLanguageContext::ATTRIBUTE_NAME));
}

public function testSubscribedEvents(): void
{
self::assertSame(
[KernelEvents::REQUEST => ['onKernelRequest', 12]],
NormalizedLanguageCodeListener::getSubscribedEvents()
);
}

private function createEvent(Request $request, int $requestType): RequestEvent
{
return new RequestEvent(
$this->createMock(HttpKernelInterface::class),
$request,
$requestType
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\AdminUi\Request\Resolver;

use Ibexa\AdminUi\Request\Resolver\AdminUiContentLanguageCodeResolver;
use Ibexa\Contracts\Core\Repository\Values\Content\Language;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;

final class AdminUiContentLanguageCodeResolverTest extends TestCase
{
private AdminUiContentLanguageCodeResolver $resolver;

protected function setUp(): void
{
$this->resolver = new AdminUiContentLanguageCodeResolver();
}

/**
* @dataProvider provideResolvableLanguageContext
*
* @param array<string, mixed> $attributes
*/
public function testResolvesLanguageCode(array $attributes, string $expectedLanguageCode): void
{
$request = new Request([], [], $attributes);

self::assertSame($expectedLanguageCode, $this->resolver->resolve($request));
}

/**
* @dataProvider provideUnresolvableLanguageContext
*
* @param array<string, mixed> $attributes
*/
public function testReturnsNullForUnresolvableLanguageContext(array $attributes): void
{
self::assertNull($this->resolver->resolve(new Request([], [], $attributes)));
}

/**
* @return iterable<string, array{array<string, mixed>, string}>
*/
public function provideResolvableLanguageContext(): iterable
{
yield 'language object' => [
['language' => new Language(['languageCode' => 'eng-GB'])],
'eng-GB',
];
yield 'language string' => [
['language' => 'ger-DE'],
'ger-DE',
];
yield 'languageCode string' => [
['languageCode' => 'pol-PL'],
'pol-PL',
];
}

/**
* @return iterable<string, array{array<string, mixed>}>
*/
public function provideUnresolvableLanguageContext(): iterable
{
yield 'missing context' => [[]];
yield 'empty context' => [['language' => '', 'languageCode' => '']];
}
}
Loading
Loading