diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 98cbd64acb..bbc1b4a434 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -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 @@ -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 diff --git a/src/bundle/Core/Resources/config/templating.yml b/src/bundle/Core/Resources/config/templating.yml index 14b32ecd15..0675bb6205 100644 --- a/src/bundle/Core/Resources/config/templating.yml +++ b/src/bundle/Core/Resources/config/templating.yml @@ -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} @@ -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 } diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php index 3adab79847..348a7cd7e4 100644 --- a/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php @@ -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; @@ -33,6 +34,8 @@ class ContentExtension extends AbstractExtension /** @var \Ibexa\Core\Helper\FieldHelper */ protected $fieldHelper; + private FieldsGroupsList $fieldsGroupsList; + /** @var \Psr\Log\LoggerInterface */ protected $logger; @@ -40,11 +43,13 @@ 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; } @@ -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'] @@ -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'], @@ -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. diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php new file mode 100644 index 0000000000..0dbcdd6b7f --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php @@ -0,0 +1,54 @@ +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(); + } +} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php index 9bf3fd69ac..7696b0ab8d 100644 --- a/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php @@ -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; @@ -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 */ private $fieldDefinitions = []; /** @var int[] */ @@ -52,7 +53,8 @@ public function getExtensions() [], $this->createMock(LoggerInterface::class) ), - $this->fieldHelperMock + $this->fieldHelperMock, + $this->getFieldsGroupsListMock() ), ]; } @@ -115,6 +117,9 @@ protected function getContent(string $contentTypeIdentifier, array $fieldsData, ), ] ), + 'contentType' => new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection($this->fieldDefinitions[$contentTypeId] ?? []), + ]), ] ); @@ -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]); diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php new file mode 100644 index 0000000000..4c7aeef798 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php @@ -0,0 +1,83 @@ + */ + 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'; + } +} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test new file mode 100644 index 0000000000..d90488afc7 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test @@ -0,0 +1,8 @@ +--TEST-- +"ibexa_field_group_name" function +--TEMPLATE-- +{{ ibexa_field_group_name('content') }} +--DATA-- +return []; +--EXPECT-- +Content diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test new file mode 100644 index 0000000000..bd9964c30a --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test @@ -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 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_get_current_user.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_get_current_user.test new file mode 100644 index 0000000000..618404bf4b --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_get_current_user.test @@ -0,0 +1,8 @@ +--TEST-- +"ibexa_get_current_user" function +--TEMPLATE-- +{{ ibexa_get_current_user().getUserId() }} +--DATA-- +return []; +--EXPECT-- +10 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test new file mode 100644 index 0000000000..6e5c5fb905 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test @@ -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