Skip to content

Commit

Permalink
IBX-7276: Extended built-in Twig functions library (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamwojs authored Dec 4, 2023
1 parent faa57c2 commit 42f3f9d
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 12 deletions.
10 changes: 0 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -48910,11 +48910,6 @@ parameters:
count: 1
path: tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php

-
message: "#^Cannot access offset mixed on Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\.$#"
count: 1
path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php

-
message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#"
count: 1
Expand All @@ -48940,11 +48935,6 @@ parameters:
count: 1
path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php

-
message: "#^Parameter \\#1 \\$fieldDefinitions of class Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection constructor expects iterable, Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition given\\.$#"
count: 1
path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php

-
message: "#^Parameter \\#1 \\$repository of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#"
count: 1
Expand Down
8 changes: 8 additions & 0 deletions src/bundle/Core/Resources/config/templating.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
- '@ibexa.api.repository'
- '@Ibexa\Core\Helper\TranslationHelper'
- '@Ibexa\Core\Helper\FieldHelper'
- '@Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList'
- "@?logger"
tags:
- {name: twig.extension}
Expand Down Expand Up @@ -301,3 +302,10 @@ services:
$twigVariableProviders: !tagged ezplatform.view.variable_provider

Ibexa\Core\MVC\Symfony\View\VariableProviderRegistry: '@Ibexa\Core\MVC\Symfony\View\GenericVariableProviderRegistry'

Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserExtension:
arguments:
$userService: '@Ibexa\Contracts\Core\Repository\UserService'
$permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver'
tags:
- { name: twig.extension }
23 changes: 23 additions & 0 deletions src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Ibexa\Contracts\Core\Repository\Values\ValueObject;
use Ibexa\Core\Base\Exceptions\InvalidArgumentType;
use Ibexa\Core\Helper\FieldHelper;
use Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList;
use Ibexa\Core\Helper\TranslationHelper;
use Psr\Log\LoggerInterface;
use Twig\Extension\AbstractExtension;
Expand All @@ -33,18 +34,22 @@ class ContentExtension extends AbstractExtension
/** @var \Ibexa\Core\Helper\FieldHelper */
protected $fieldHelper;

private FieldsGroupsList $fieldsGroupsList;

/** @var \Psr\Log\LoggerInterface */
protected $logger;

public function __construct(
Repository $repository,
TranslationHelper $translationHelper,
FieldHelper $fieldHelper,
FieldsGroupsList $fieldsGroupsList,
LoggerInterface $logger = null
) {
$this->repository = $repository;
$this->translationHelper = $translationHelper;
$this->fieldHelper = $fieldHelper;
$this->fieldsGroupsList = $fieldsGroupsList;
$this->logger = $logger;
}

Expand Down Expand Up @@ -100,6 +105,10 @@ public function getFunctions()
'alternative' => 'ibexa_field_is_empty',
]
),
new TwigFunction(
'ibexa_has_field',
[$this, 'hasField']
),
new TwigFunction(
'ibexa_field_is_empty',
[$this, 'isFieldEmpty']
Expand Down Expand Up @@ -128,6 +137,10 @@ public function getFunctions()
'ibexa_field_description',
[$this, 'getTranslatedFieldDefinitionDescription']
),
new TwigFunction(
'ibexa_field_group_name',
[$this, 'getFieldGroupName']
),
new TwigFunction(
'ez_content_field_identifier_first_filled_image',
[$this, 'getFirstFilledImageFieldIdentifier'],
Expand Down Expand Up @@ -243,6 +256,16 @@ public function getTranslatedFieldDefinitionDescription(ValueObject $content, $f
throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content);
}

public function hasField(Content $content, string $fieldDefIdentifier): bool
{
return $content->getContentType()->hasFieldDefinition($fieldDefIdentifier);
}

public function getFieldGroupName(string $identifier): ?string
{
return $this->fieldsGroupsList->getGroups()[$identifier] ?? null;
}

/**
* Checks if a given field is considered empty.
* This method accepts field as Objects or by identifiers.
Expand Down
54 changes: 54 additions & 0 deletions src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?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\Core\MVC\Symfony\Templating\Twig\Extension;

use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Core\Repository\Values\User\User;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class UserExtension extends AbstractExtension
{
private UserService $userService;

private PermissionResolver $permissionResolver;

public function __construct(UserService $userService, PermissionResolver $permissionResolver)
{
$this->userService = $userService;
$this->permissionResolver = $permissionResolver;
}

public function getFunctions(): array
{
return [
new TwigFunction(
'ibexa_get_current_user',
[$this, 'getCurrentUser']
),
new TwigFunction(
'ibexa_is_current_user',
[$this, 'isCurrentUser']
),
];
}

public function getCurrentUser(): User
{
return $this->userService->loadUser(
$this->permissionResolver->getCurrentUserReference()->getUserId()
);
}

public function isCurrentUser(User $user): bool
{
return $this->permissionResolver->getCurrentUserReference()->getUserId() === $user->getUserId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface;
use Ibexa\Core\Helper\FieldHelper;
use Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList;
use Ibexa\Core\Helper\TranslationHelper;
use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension;
use Ibexa\Core\Repository\Values\Content\Content;
Expand All @@ -32,7 +33,7 @@ class ContentExtensionTest extends FileSystemTwigIntegrationTestCase
/** @var \Ibexa\Contracts\Core\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */
private $fieldHelperMock;

/** @var \Ibexa\Core\Repository\Values\ContentType\FieldDefinition[] */
/** @var array<int, \Ibexa\Core\Repository\Values\ContentType\FieldDefinition[]> */
private $fieldDefinitions = [];

/** @var int[] */
Expand All @@ -52,7 +53,8 @@ public function getExtensions()
[],
$this->createMock(LoggerInterface::class)
),
$this->fieldHelperMock
$this->fieldHelperMock,
$this->getFieldsGroupsListMock()
),
];
}
Expand Down Expand Up @@ -115,6 +117,9 @@ protected function getContent(string $contentTypeIdentifier, array $fieldsData,
),
]
),
'contentType' => new ContentType([
'fieldDefinitions' => new FieldDefinitionCollection($this->fieldDefinitions[$contentTypeId] ?? []),
]),
]
);

Expand Down Expand Up @@ -143,6 +148,16 @@ private function getConfigResolverMock()
return $mock;
}

private function getFieldsGroupsListMock(): FieldsGroupsList
{
$fieldsGroupsList = $this->createMock(FieldsGroupsList::class);
$fieldsGroupsList->method('getGroups')->willReturn([
'content' => 'Content',
]);

return $fieldsGroupsList;
}

protected function getField($isEmpty)
{
$field = new Field(['fieldDefIdentifier' => 'testfield', 'value' => null]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?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\Core\MVC\Symfony\Templating\Twig\Extension;

use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\Contracts\Core\Repository\Values\User\User;
use Ibexa\Contracts\Core\Repository\Values\User\UserReference;
use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserExtension;
use Twig\Test\IntegrationTestCase;

final class UserExtensionTest extends IntegrationTestCase
{
/** @var \Ibexa\Contracts\Core\Repository\PermissionResolver&\PHPUnit\Framework\MockObject\MockObject */
private PermissionResolver $permissionResolver;

/** @var \Ibexa\Contracts\Core\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject */
private UserService $userService;

/** @var array<int, \Ibexa\Contracts\Core\Repository\Values\User\User> */
private array $users = [];

private int $currentUserId;

protected function setUp(): void
{
parent::setUp();

$this->userService = $this->createMock(UserService::class);
$this->userService
->method('loadUser')
->willReturnCallback(fn (int $id): User => $this->users[$id]);

$this->permissionResolver = $this->createMock(PermissionResolver::class);
$this->permissionResolver
->method('getCurrentUserReference')
->willReturnCallback(function (): UserReference {
$reference = $this->createMock(UserReference::class);
$reference->method('getUserId')->willReturn($this->currentUserId);

return $reference;
});

$this->getUser(10, true);
}

protected function getExtensions(): array
{
return [
new UserExtension(
$this->userService,
$this->permissionResolver
),
];
}

public function getUser(int $id, bool $isCurrent = false): User
{
if (!isset($this->users[$id])) {
$user = $this->createMock(User::class);
$user->method('getUserId')->willReturn($id);

$this->users[$id] = $user;

if ($isCurrent) {
$this->currentUserId = $id;
}
}

return $this->users[$id];
}

protected function getFixturesDir(): string
{
return __DIR__ . '/_fixtures/user_functions';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
"ibexa_field_group_name" function
--TEMPLATE--
{{ ibexa_field_group_name('content') }}
--DATA--
return [];
--EXPECT--
Content
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
"ibexa_has_field" function
--TEMPLATE--
{{ ibexa_has_field(content, 'existing') ? 'YES' : 'NO' }}
{{ ibexa_has_field(content, 'non-existing') ? 'YES' : 'NO' }}

--DATA--
return [
'content' => $this->getContent(
'test_content',
[
'ezstring' => [
'id' => 125,
'fieldDefIdentifier' => 'existing',
'value' => 'value',
'languageCode' => 'eng-GB',
],
]
),
];
--EXPECT--
YES
NO
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
"ibexa_get_current_user" function
--TEMPLATE--
{{ ibexa_get_current_user().getUserId() }}
--DATA--
return [];
--EXPECT--
10
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
"ibexa_is_current_user" function
--TEMPLATE--
{{ ibexa_is_current_user(user_foo) ? 'YES' : 'NO' }}
{{ ibexa_is_current_user(user_bar) ? 'YES' : 'NO' }}
--DATA--
return [
'user_foo' => $this->getUser(10, true),
'user_bar' => $this->getUser(11, false),
];
--EXPECT--
YES
NO

0 comments on commit 42f3f9d

Please sign in to comment.