From aea8e35ed2ef90dd4972e177015a57c6d785c224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 2 Aug 2023 23:59:46 +0200 Subject: [PATCH 01/37] Added attributes support for generating name schema x Fixed --- .../AbstractNameSchemaEvent.php} | 33 ++---- .../ResolveContentNameSchemaEvent.php | 29 +++++ .../NameSchema/ResolveNameSchemaEvent.php | 47 ++++++++ .../NameSchema/ResolveUrlAliasSchemaEvent.php | 29 +++++ .../EventSubscriber/NameSchemaSubscriber.php | 107 +++++++++++++++-- .../NameSchema/NameSchemaFilter.php | 52 ++++++++ .../NameSchema/NameSchemaService.php | 111 +++++------------- .../Repository/NameSchema/TokenHandler.php | 99 ++++++++++++++++ .../repository/inner/name_schema.yaml | 4 + .../NameSchema/NameSchemaServiceTest.php | 2 +- .../SchemaIdentifierExtractorTest.php | 9 ++ 11 files changed, 405 insertions(+), 117 deletions(-) rename src/contracts/Event/{ResolveUrlAliasSchemaEvent.php => NameSchema/AbstractNameSchemaEvent.php} (58%) create mode 100644 src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php create mode 100644 src/lib/Repository/NameSchema/NameSchemaFilter.php create mode 100644 src/lib/Repository/NameSchema/TokenHandler.php diff --git a/src/contracts/Event/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php similarity index 58% rename from src/contracts/Event/ResolveUrlAliasSchemaEvent.php rename to src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 3f75eb3463..861e9ec7bf 100644 --- a/src/contracts/Event/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -6,48 +6,37 @@ */ declare(strict_types=1); -namespace Ibexa\Contracts\Core\Event; +namespace Ibexa\Contracts\Core\Event\NameSchema; -use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Symfony\Contracts\EventDispatcher\Event; -final class ResolveUrlAliasSchemaEvent extends Event +abstract class AbstractNameSchemaEvent extends Event { /** @var array */ private array $schemaIdentifiers; - private Content $content; - /** * @var array> */ - private array $names = []; - - public function __construct( - array $schemaIdentifiers, - Content $content - ) { - $this->schemaIdentifiers = $schemaIdentifiers; - $this->content = $content; - } + private array $tokenValues = []; - public function getSchemaIdentifiers(): array + public function __construct(array $schemaIdentifiers) { - return $this->schemaIdentifiers; + $this->schemaIdentifiers = $schemaIdentifiers; } - public function getContent(): Content + public function getTokenValues(): array { - return $this->content; + return $this->tokenValues; } - public function getTokenValues(): array + public function setTokenValues(array $names): void { - return $this->names; + $this->tokenValues = $names; } - public function setTokenValues(array $names): void + public function getSchemaIdentifiers(): array { - $this->names = $names; + return $this->schemaIdentifiers; } } diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php new file mode 100644 index 0000000000..4068cb6271 --- /dev/null +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -0,0 +1,29 @@ +content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php new file mode 100644 index 0000000000..4d42f3e9d5 --- /dev/null +++ b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php @@ -0,0 +1,47 @@ +contentType = $contentType; + $this->fieldMap = $fieldMap; + $this->languageCodes = $languageCodes; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getFieldMap(): array + { + return $this->fieldMap; + } + + public function getLanguageCodes(): array + { + return $this->languageCodes; + } +} diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php new file mode 100644 index 0000000000..e6f3851686 --- /dev/null +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -0,0 +1,29 @@ +content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 89bbfc8615..192a398a18 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -8,7 +8,12 @@ namespace Ibexa\Core\Repository\EventSubscriber; -use Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\AbstractNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Core\FieldType\FieldTypeRegistry; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -27,29 +32,109 @@ public function __construct(FieldTypeRegistry $fieldTypeRegistry) public static function getSubscribedEvents(): array { return [ + ResolveNameSchemaEvent::class => [ + ['onResolveNameSchema', -100], + ], + ResolveContentNameSchemaEvent::class => [ + ['onResolveContentNameSchema', -100], + ], ResolveUrlAliasSchemaEvent::class => [ ['onResolveUrlAliasSchema', -100], ], ]; } - /** - * Resolves the URL alias schema by setting token values for specified field identifiers and languages. - * - * @param \Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent $event - */ - public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void + public function onResolveNameSchema(ResolveNameSchemaEvent $event): void { - if (!array_key_exists('field', $event->getSchemaIdentifiers())) { + if (!$this->isValid($event)) { return; } - $content = $event->getContent(); $identifiers = $event->getSchemaIdentifiers()['field']; - $languages = $event->getContent()->getVersionInfo()->getLanguages(); $tokenValues = $event->getTokenValues(); + $fieldMap = $event->getFieldMap(); + + $contentType = $event->getContentType(); + foreach ($event->getLanguageCodes() as $languageCode) { + foreach ($identifiers as $identifier) { + $fieldDefinition = $contentType->getFieldDefinition($identifier); + if (null === $fieldDefinition) { + continue; + } + $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); + + $fieldValue = $fieldMap[$identifier][$languageCode] ?? ''; + $fieldValue = $persistenceFieldType->getName( + $fieldValue, + $fieldDefinition, + $languageCode + ); + + $tokenValues[$languageCode][$identifier] = $fieldValue; + } + } + + $event->setTokenValues($tokenValues); + } + public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $content = $event->getContent(); $contentType = $content->getContentType(); + $tokenValues = $this->processEvent( + $event->getContent()->getVersionInfo()->getLanguages(), + $event->getSchemaIdentifiers()['field'], + $contentType, + $content, + $event->getTokenValues() + ); + + $event->setTokenValues($tokenValues); + } + + public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $content = $event->getContent(); + $contentType = $content->getContentType(); + $tokenValues = $this->processEvent( + $event->getContent()->getVersionInfo()->getLanguages(), + $event->getSchemaIdentifiers()['field'], + $contentType, + $content, + $event->getTokenValues() + ); + + $event->setTokenValues($tokenValues); + } + + public function isValid(AbstractNameSchemaEvent $event): bool + { + return array_key_exists('field', $event->getSchemaIdentifiers()); + } + + /** + * @param array $tokens + * @param array $languageCodes + * @param array $attributes + * @param array $tokenValues + * + * @return array + */ + public function processEvent( + $languages, + $identifiers, + ContentType $contentType, + Content $content, + array $tokenValues + ): array { foreach ($languages as $language) { $languageCode = $language->getLanguageCode(); foreach ($identifiers as $identifier) { @@ -70,6 +155,6 @@ public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void } } - $event->setTokenValues($tokenValues); + return $tokenValues; } } diff --git a/src/lib/Repository/NameSchema/NameSchemaFilter.php b/src/lib/Repository/NameSchema/NameSchemaFilter.php new file mode 100644 index 0000000000..f01040460e --- /dev/null +++ b/src/lib/Repository/NameSchema/NameSchemaFilter.php @@ -0,0 +1,52 @@ +} + */ + public function filterNameSchema(string $nameSchema): array + { + $retNamePattern = $nameSchema; + $foundGroups = preg_match_all('/\((.+)\)/U', $nameSchema, $groupArray); + $groupLookupTable = []; + + if ($foundGroups) { + $i = 0; + foreach ($groupArray[1] as $group) { + // Create meta-token for group + $metaToken = self::META_STRING . $i; + + // Insert the group with its placeholder token + $retNamePattern = str_replace($group, $metaToken, $retNamePattern); + + // Remove the pattern "(" ")" from the tokens + $group = str_replace(['(', ')'], '', $group); + + $groupLookupTable[$metaToken] = $group; + ++$i; + } + $nameSchema = $retNamePattern; + } + + return [$nameSchema, $groupLookupTable]; + } +} diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 591642135d..93df504935 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -8,7 +8,9 @@ namespace Ibexa\Core\Repository\NameSchema; -use Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; use Ibexa\Contracts\Core\Repository\NameSchema\SchemaIdentifierExtractorInterface; use Ibexa\Contracts\Core\Repository\Values\Content\Content; @@ -116,65 +118,47 @@ public function resolveNameSchema( array $languageCodes = [], ContentType $contentType = null ): array { - $contentType ??= $content->getContentType(); - - $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; - - return $this->resolve( - $contentType->nameSchema, - $contentType, - $this->mergeFieldMap( - $content, - $fieldMap, - $languageCodes - ), - $languageCodes - ); - } + $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); - /** - * Convenience method for resolving name schema. - * - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content - * @param array $fieldMap - * @param array $languageCodes - * - * @return array - */ - protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array - { - if (empty($fieldMap)) { - return $content->fields; - } + $event = new ResolveContentNameSchemaEvent($schemaIdentifiers, $content); + $this->eventDispatcher->dispatch($event); - $mergedFieldMap = []; + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($schemaName); + $tokens = $this->extractTokens($filteredNameSchema); - foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { - foreach ($languageCodes as $languageCode) { - $mergedFieldMap[$fieldIdentifier][$languageCode] - = $fieldMap[$fieldIdentifier][$languageCode] ?? $fieldLanguageMap[$languageCode]; + $names = []; + $tokenValues = $event->getTokenValues(); + foreach ($tokenValues as $languageCode => $tokenValue) { + $name = $filteredNameSchema; + foreach ($tokens as $token) { + $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); + $name = str_replace($token, $string, $name); } + $name = $this->validateNameLength($name); + + $names[$languageCode] = $name; } - return $mergedFieldMap; + return $names; } public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array { + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($nameSchema); + $event = new ResolveNameSchemaEvent($schemaIdentifiers, $contentType, $fieldMap, $languageCodes); + + $this->eventDispatcher->dispatch($event); + $tokenValues = $event->getTokenValues(); + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); $tokens = $this->extractTokens($filteredNameSchema); - $schemaIdentifiers = $this->getIdentifiers($nameSchema); $names = []; - - foreach ($languageCodes as $languageCode) { - // Fetch titles for language code - $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); + foreach ($tokenValues as $languageCode => $tokenValue) { $name = $filteredNameSchema; - - // Replace tokens with real values foreach ($tokens as $token) { - $string = $this->resolveToken($token, $titles, $groupLookupTable); + $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); $name = str_replace($token, $string, $name); } $name = $this->validateNameLength($name); @@ -185,45 +169,6 @@ public function resolve(string $nameSchema, ContentType $contentType, array $fie return $names; } - /** - * Fetches the list of available Field identifiers in the token and returns - * an array of their current title value. - * - * @param array $schemaIdentifiers - * @param array> $fieldMap - * - * @return string[] Key is the field identifier, value is the title value - * - * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() - */ - protected function getFieldTitles( - array $schemaIdentifiers, - ContentType $contentType, - array $fieldMap, - string $languageCode - ): array { - $fieldTitles = []; - - foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { - if (!isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { - continue; - } - - $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); - $persistenceFieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $fieldTitles[$fieldDefinitionIdentifier] = $persistenceFieldType->getName( - $fieldMap[$fieldDefinitionIdentifier][$languageCode], - $fieldDefinition, - $languageCode - ); - } - - return $fieldTitles; - } - /** * Extract all tokens from $namePattern. * diff --git a/src/lib/Repository/NameSchema/TokenHandler.php b/src/lib/Repository/NameSchema/TokenHandler.php new file mode 100644 index 0000000000..7e5f9e4395 --- /dev/null +++ b/src/lib/Repository/NameSchema/TokenHandler.php @@ -0,0 +1,99 @@ + + * Text more text ==> + * + */ + public function extractTokens(string $nameSchema): array + { + preg_match_all('|<([^>]+)>|U', $nameSchema, $tokenArray); + + return $tokenArray[0]; + } + + /** + * Looks up the value $token should be replaced with and returns this as + * a string. Meta strings denoting token groups are automatically + * inferred. + */ + public function resolveToken(string $token, array $titles, array $groupLookupTable): string + { + $replaceString = ''; + $tokenParts = $this->tokenParts($token); + + foreach ($tokenParts as $tokenPart) { + if ($this->isTokenGroup($tokenPart)) { + $replaceString = $groupLookupTable[$tokenPart]; + $groupTokenArray = $this->extractTokens($replaceString); + + foreach ($groupTokenArray as $groupToken) { + $replaceString = str_replace( + $groupToken, + $this->resolveToken( + $groupToken, + $titles, + $groupLookupTable + ), + $replaceString + ); + } + + // We want to stop after the first matching token part / identifier is found + // if id1 has a value, id2 will not be used. + // In this case id1 or id1 is a token group. + break; + } + if (array_key_exists($tokenPart, $titles) && $titles[$tokenPart] !== '' && $titles[$tokenPart] !== null) { + $replaceString = $titles[$tokenPart]; + // We want to stop after the first matching token part / identifier is found + // if id1 has a value, id2 will not be used. + break; + } + } + + return $replaceString; + } + + /** + * Checks whether $identifier is a placeholder for a token group. + */ + public function isTokenGroup(string $identifier): bool + { + return strpos($identifier, self::META_STRING) !== false; + } + + /** + * Returns the different constituents of $token in an array. + * The normal case here is that the different identifiers within one token + * will be tokenized and returned. + * + * Example: + * + * "<title|text>" ==> array( 'title', 'text' ) + * + * + * @param string $token + * + * @return array + */ + public function tokenParts(string $token): array + { + return preg_split('/[^\w:]+/', $token, -1, PREG_SPLIT_NO_EMPTY); + } +} diff --git a/src/lib/Resources/settings/repository/inner/name_schema.yaml b/src/lib/Resources/settings/repository/inner/name_schema.yaml index 45f8fcad8e..35cb97d184 100644 --- a/src/lib/Resources/settings/repository/inner/name_schema.yaml +++ b/src/lib/Resources/settings/repository/inner/name_schema.yaml @@ -16,5 +16,9 @@ services: arguments: $settings: '%ibexa.core.repository.name_schema.settings%' + Ibexa\Core\Repository\NameSchema\NameSchemaFilter: ~ + + Ibexa\Core\Repository\NameSchema\TokenHandler: ~ + Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface: alias: 'Ibexa\Core\Repository\NameSchema\NameSchemaService' diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index a1c6bdfa03..588773e1a8 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -8,7 +8,7 @@ namespace Ibexa\Tests\Core\Repository\NameSchema; -use Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; use Ibexa\Contracts\Core\Repository\Values\Content\Field; use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; use Ibexa\Core\FieldType\TextLine\Type as TextLineFieldType; diff --git a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php index a5bb17e369..02bdffcfa9 100644 --- a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php +++ b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php @@ -94,6 +94,15 @@ public function getDataForTestExtract(): iterable 'attribute' => ['mouse_type', 'mouse_weight'], ], ]; + + $schemaString = ' )> )>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['description'], + 'attribute' => ['mouse_type', 'mouse_weight'], + ], + ]; } protected function setUp(): void From 8a23adbe710e8064ee0ebf4a713487810fb666cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 7 Aug 2023 10:25:01 +0200 Subject: [PATCH 02/37] NameSchemaService refactored --- .../AbstractContentAwareNameSchemaEvent.php | 29 ++++++ .../NameSchema/AbstractNameSchemaEvent.php | 4 +- .../ResolveContentNameSchemaEvent.php | 18 +--- .../NameSchema/ResolveUrlAliasSchemaEvent.php | 18 +--- .../NameSchema/NameSchemaService.php | 93 ++++++++----------- 5 files changed, 71 insertions(+), 91 deletions(-) create mode 100644 src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php diff --git a/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php new file mode 100644 index 0000000000..ab89ecba24 --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php @@ -0,0 +1,29 @@ +content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 861e9ec7bf..845e0ec540 100644 --- a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -13,12 +13,12 @@ abstract class AbstractNameSchemaEvent extends Event { /** @var array */ - private array $schemaIdentifiers; + protected array $schemaIdentifiers; /** * @var array> */ - private array $tokenValues = []; + protected array $tokenValues = []; public function __construct(array $schemaIdentifiers) { diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index 4068cb6271..445c6c7cfb 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -8,22 +8,6 @@ namespace Ibexa\Contracts\Core\Event\NameSchema; -use Ibexa\Contracts\Core\Repository\Values\Content\Content; - -final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent +final class ResolveContentNameSchemaEvent extends AbstractContentAwareNameSchemaEvent { - private Content $content; - - public function __construct( - array $schemaIdentifiers, - Content $content - ) { - parent::__construct($schemaIdentifiers); - $this->content = $content; - } - - public function getContent(): Content - { - return $this->content; - } } diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php index e6f3851686..9e35a3cb97 100644 --- a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -8,22 +8,6 @@ namespace Ibexa\Contracts\Core\Event\NameSchema; -use Ibexa\Contracts\Core\Repository\Values\Content\Content; - -final class ResolveUrlAliasSchemaEvent extends AbstractNameSchemaEvent +final class ResolveUrlAliasSchemaEvent extends AbstractContentAwareNameSchemaEvent { - private Content $content; - - public function __construct( - array $schemaIdentifiers, - Content $content - ) { - parent::__construct($schemaIdentifiers); - $this->content = $content; - } - - public function getContent(): Content - { - return $this->content; - } } diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 93df504935..0a1d8534a1 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -85,31 +85,16 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType { $contentType ??= $content->getContentType(); $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; - [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($schemaName); $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); - $tokens = $this->extractTokens($filteredNameSchema); - /** @var \Ibexa\Contracts\Core\Event\ResolveUrlAliasSchemaEvent $event */ $event = $this->eventDispatcher->dispatch( new ResolveUrlAliasSchemaEvent( $schemaIdentifiers, $content ) ); - $names = []; - $tokenValues = $event->getTokenValues(); - foreach ($tokenValues as $languageCode => $tokenValue) { - $name = $filteredNameSchema; - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $name = $this->validateNameLength($name); - $names[$languageCode] = $name; - } - - return $names; + return $this->buildNames($event->getTokenValues(), $schemaName); } public function resolveNameSchema( @@ -121,52 +106,29 @@ public function resolveNameSchema( $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); - $event = new ResolveContentNameSchemaEvent($schemaIdentifiers, $content); - $this->eventDispatcher->dispatch($event); - - [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($schemaName); - $tokens = $this->extractTokens($filteredNameSchema); - - $names = []; - $tokenValues = $event->getTokenValues(); - foreach ($tokenValues as $languageCode => $tokenValue) { - $name = $filteredNameSchema; - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $name = $this->validateNameLength($name); - - $names[$languageCode] = $name; - } + $event = $this->eventDispatcher->dispatch( + new ResolveContentNameSchemaEvent( + $schemaIdentifiers, + $content + ) + ); - return $names; + return $this->buildNames($event->getTokenValues(), $schemaName); } public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array { $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($nameSchema); - $event = new ResolveNameSchemaEvent($schemaIdentifiers, $contentType, $fieldMap, $languageCodes); - - $this->eventDispatcher->dispatch($event); - $tokenValues = $event->getTokenValues(); - - [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); - $tokens = $this->extractTokens($filteredNameSchema); - - $names = []; - foreach ($tokenValues as $languageCode => $tokenValue) { - $name = $filteredNameSchema; - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $name = $this->validateNameLength($name); - - $names[$languageCode] = $name; - } + $event = $this->eventDispatcher->dispatch( + new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $fieldMap, + $languageCodes + ) + ); - return $names; + return $this->buildNames($event->getTokenValues(), $nameSchema); } /** @@ -300,6 +262,27 @@ protected function filterNameSchema(string $nameSchema): array return [$nameSchema, $groupLookupTable]; } + /** + * @param array $tokenValues + */ + public function buildNames(array $tokenValues, string $nameSchema): array + { + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + + $names = []; + foreach ($tokenValues as $languageCode => $tokenValue) { + $name = $filteredNameSchema; + foreach ($tokens as $token) { + $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); + $name = str_replace($token, $string, $name); + } + $names[$languageCode] = $this->validateNameLength($name); + } + + return $names; + } + /** * @return array */ From 2b7f42000933991adc94af098924649d6f48b6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 10:06:57 +0200 Subject: [PATCH 03/37] Changed event structure --- .../AbstractContentAwareNameSchemaEvent.php | 29 ------------- .../NameSchema/AbstractNameSchemaEvent.php | 43 +++++++++++-------- .../Event/NameSchema/AbstractSchemaEvent.php | 40 +++++++++++++++++ .../NameSchema/ContentAwareEventInterface.php | 15 +++++++ .../ResolveContentNameSchemaEvent.php | 22 +++++++++- .../NameSchema/ResolveNameSchemaEvent.php | 33 -------------- .../NameSchema/ResolveUrlAliasSchemaEvent.php | 18 +++++++- .../NameSchema/NameSchemaService.php | 12 +++++- 8 files changed, 127 insertions(+), 85 deletions(-) delete mode 100644 src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/AbstractSchemaEvent.php create mode 100644 src/contracts/Event/NameSchema/ContentAwareEventInterface.php diff --git a/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php deleted file mode 100644 index ab89ecba24..0000000000 --- a/src/contracts/Event/NameSchema/AbstractContentAwareNameSchemaEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -content = $content; - } - - public function getContent(): Content - { - return $this->content; - } -} diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 845e0ec540..3fffff5b9d 100644 --- a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -8,35 +8,40 @@ namespace Ibexa\Contracts\Core\Event\NameSchema; -use Symfony\Contracts\EventDispatcher\Event; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; -abstract class AbstractNameSchemaEvent extends Event +abstract class AbstractNameSchemaEvent extends AbstractSchemaEvent { - /** @var array */ - protected array $schemaIdentifiers; - - /** - * @var array> - */ - protected array $tokenValues = []; - - public function __construct(array $schemaIdentifiers) - { - $this->schemaIdentifiers = $schemaIdentifiers; + private ContentType $contentType; + + private array $fieldMap; + + private array $languageCodes; + + public function __construct( + array $schemaIdentifiers, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ) { + parent::__construct($schemaIdentifiers); + $this->contentType = $contentType; + $this->fieldMap = $fieldMap; + $this->languageCodes = $languageCodes; } - public function getTokenValues(): array + public function getContentType(): ContentType { - return $this->tokenValues; + return $this->contentType; } - public function setTokenValues(array $names): void + public function getFieldMap(): array { - $this->tokenValues = $names; + return $this->fieldMap; } - public function getSchemaIdentifiers(): array + public function getLanguageCodes(): array { - return $this->schemaIdentifiers; + return $this->languageCodes; } } diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php new file mode 100644 index 0000000000..b1e890ad4d --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -0,0 +1,40 @@ + */ + protected array $schemaIdentifiers; + + /** @var array> */ + protected array $tokenValues = []; + + public function __construct(array $schemaIdentifiers) + { + $this->schemaIdentifiers = $schemaIdentifiers; + } + + public function getTokenValues(): array + { + return $this->tokenValues; + } + + public function setTokenValues(array $names): void + { + $this->tokenValues = $names; + } + + public function getSchemaIdentifiers(): array + { + return $this->schemaIdentifiers; + } +} diff --git a/src/contracts/Event/NameSchema/ContentAwareEventInterface.php b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php new file mode 100644 index 0000000000..e8e32c065d --- /dev/null +++ b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php @@ -0,0 +1,15 @@ +content = $content; + } + + public function getContent(): Content + { + return $this->content; + } } diff --git a/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php index 4d42f3e9d5..1e047f8c6b 100644 --- a/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php @@ -8,40 +8,7 @@ namespace Ibexa\Contracts\Core\Event\NameSchema; -use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; - final class ResolveNameSchemaEvent extends AbstractNameSchemaEvent { - private ContentType $contentType; - - private array $fieldMap; - - private array $languageCodes; - - public function __construct( - array $schemaIdentifiers, - ContentType $contentType, - array $fieldMap, - array $languageCodes - ) { - parent::__construct($schemaIdentifiers); - $this->contentType = $contentType; - $this->fieldMap = $fieldMap; - $this->languageCodes = $languageCodes; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getFieldMap(): array - { - return $this->fieldMap; - } - public function getLanguageCodes(): array - { - return $this->languageCodes; - } } diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php index 9e35a3cb97..051abf8fcd 100644 --- a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -8,6 +8,22 @@ namespace Ibexa\Contracts\Core\Event\NameSchema; -final class ResolveUrlAliasSchemaEvent extends AbstractContentAwareNameSchemaEvent +use Ibexa\Contracts\Core\Repository\Values\Content\Content; + +final class ResolveUrlAliasSchemaEvent extends AbstractSchemaEvent implements ContentAwareEventInterface { + protected Content $content; + + public function __construct( + array $schemaIdentifiers, + Content $content + ) { + parent::__construct($schemaIdentifiers); + $this->content = $content; + } + + public function getContent(): Content + { + return $this->content; + } } diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 0a1d8534a1..7fc39c0d16 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -116,8 +116,16 @@ public function resolveNameSchema( return $this->buildNames($event->getTokenValues(), $schemaName); } - public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array - { + public function resolveContentNameSchema(){ + + } + + public function resolve( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array { $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($nameSchema); $event = $this->eventDispatcher->dispatch( new ResolveNameSchemaEvent( From aca456e4f144476c7738f75b11af7015f8c274f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 10:14:28 +0200 Subject: [PATCH 04/37] Working solution --- .../EventSubscriber/NameSchemaSubscriber.php | 85 +++++++------------ .../NameSchema/NameSchemaService.php | 5 +- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 192a398a18..befa7e238d 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -9,6 +9,7 @@ namespace Ibexa\Core\Repository\EventSubscriber; use Ibexa\Contracts\Core\Event\NameSchema\AbstractNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; @@ -46,38 +47,15 @@ public static function getSubscribedEvents(): array public function onResolveNameSchema(ResolveNameSchemaEvent $event): void { - if (!$this->isValid($event)) { - return; - } - - $identifiers = $event->getSchemaIdentifiers()['field']; - $tokenValues = $event->getTokenValues(); - $fieldMap = $event->getFieldMap(); - - $contentType = $event->getContentType(); - foreach ($event->getLanguageCodes() as $languageCode) { - foreach ($identifiers as $identifier) { - $fieldDefinition = $contentType->getFieldDefinition($identifier); - if (null === $fieldDefinition) { - continue; - } - $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - - $fieldValue = $fieldMap[$identifier][$languageCode] ?? ''; - $fieldValue = $persistenceFieldType->getName( - $fieldValue, - $fieldDefinition, - $languageCode - ); - - $tokenValues[$languageCode][$identifier] = $fieldValue; - } - } - - $event->setTokenValues($tokenValues); + $this->processNameSchemaEvent($event); } public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void + { + $this->processNameSchemaEvent($event); + } + + public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void { if (!$this->isValid($event)) { return; @@ -96,47 +74,39 @@ public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event) $event->setTokenValues($tokenValues); } - public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void + public function isValid(AbstractSchemaEvent $event): bool + { + return array_key_exists('field', $event->getSchemaIdentifiers()); + } + + private function processNameSchemaEvent(AbstractNameSchemaEvent $event): void { if (!$this->isValid($event)) { return; } - $content = $event->getContent(); - $contentType = $content->getContentType(); $tokenValues = $this->processEvent( - $event->getContent()->getVersionInfo()->getLanguages(), + $event->getLanguageCodes(), $event->getSchemaIdentifiers()['field'], - $contentType, - $content, - $event->getTokenValues() + $event->getContentType(), + null, + $event->getTokenValues(), + $event->getFieldMap() ); $event->setTokenValues($tokenValues); } - public function isValid(AbstractNameSchemaEvent $event): bool - { - return array_key_exists('field', $event->getSchemaIdentifiers()); - } - - /** - * @param array $tokens - * @param array $languageCodes - * @param array $attributes - * @param array $tokenValues - * - * @return array - */ - public function processEvent( - $languages, - $identifiers, + private function processEvent( + array $languages, + array $identifiers, ContentType $contentType, - Content $content, - array $tokenValues + ?Content $content, + array $tokenValues, + ?array $fieldMap = null ): array { foreach ($languages as $language) { - $languageCode = $language->getLanguageCode(); + $languageCode = is_string($language) ? $language : $language->getLanguageCode(); foreach ($identifiers as $identifier) { $fieldDefinition = $contentType->getFieldDefinition($identifier); if (null === $fieldDefinition) { @@ -144,7 +114,10 @@ public function processEvent( } $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - $fieldValue = $content->getFieldValue($identifier, $languageCode); + $fieldValue = $fieldMap + ? $fieldMap[$identifier][$languageCode] ?? '' + : $content->getFieldValue($identifier, $languageCode); + $fieldValue = $persistenceFieldType->getName( $fieldValue, $fieldDefinition, diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 7fc39c0d16..918842904d 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -108,8 +108,11 @@ public function resolveNameSchema( $event = $this->eventDispatcher->dispatch( new ResolveContentNameSchemaEvent( + $content, $schemaIdentifiers, - $content + $contentType, + $fieldMap, + $languageCodes ) ); From 79cfd824bccbb4f250ba1cdd3b26e9121436b83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 14:06:15 +0200 Subject: [PATCH 05/37] Fixex code style --- src/contracts/Event/NameSchema/ContentAwareEventInterface.php | 1 + src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php | 1 - src/lib/Repository/NameSchema/NameSchemaService.php | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contracts/Event/NameSchema/ContentAwareEventInterface.php b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php index e8e32c065d..18072e6854 100644 --- a/src/contracts/Event/NameSchema/ContentAwareEventInterface.php +++ b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php @@ -1,4 +1,5 @@ buildNames($event->getTokenValues(), $schemaName); } - public function resolveContentNameSchema(){ - + public function resolveContentNameSchema() + { } public function resolve( From 18f881acb470fab9fc0053a8025fa3ba13cfcb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 23:09:54 +0200 Subject: [PATCH 06/37] Fixed tests --- .../Repository/Helper/NameSchemaService.php | 48 +++++++++ .../NameSchema/NameSchemaFilter.php | 52 ---------- .../NameSchema/NameSchemaService.php | 4 +- .../Repository/NameSchema/TokenHandler.php | 99 ------------------- .../NameSchema/NameSchemaServiceTest.php | 77 +++++++++------ .../SchemaIdentifierExtractorTest.php | 4 +- 6 files changed, 98 insertions(+), 186 deletions(-) delete mode 100644 src/lib/Repository/NameSchema/NameSchemaFilter.php delete mode 100644 src/lib/Repository/NameSchema/TokenHandler.php diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 47682ef83c..e847cc0a16 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -27,6 +27,54 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType $content->versionInfo->languageCodes ); } + + public function resolveNameSchema( + Content $content, + array $fieldMap = [], + array $languageCodes = [], + ContentType $contentType = null + ): array { + $contentType ??= $content->getContentType(); + + $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; + + return $this->resolve( + $contentType->nameSchema, + $contentType, + $this->mergeFieldMap( + $content, + $fieldMap, + $languageCodes + ), + $languageCodes + ); + } + + public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array + { + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + $schemaIdentifiers = $this->getIdentifiers($nameSchema); + + $names = []; + + foreach ($languageCodes as $languageCode) { + // Fetch titles for language code + $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); + $name = $filteredNameSchema; + + // Replace tokens with real values + foreach ($tokens as $token) { + $string = $this->resolveToken($token, $titles, $groupLookupTable); + $name = str_replace($token, $string, $name); + } + $name = $this->validateNameLength($name); + + $names[$languageCode] = $name; + } + + return $names; + } } class_alias(NameSchemaService::class, 'eZ\Publish\Core\Repository\Helper\NameSchemaService'); diff --git a/src/lib/Repository/NameSchema/NameSchemaFilter.php b/src/lib/Repository/NameSchema/NameSchemaFilter.php deleted file mode 100644 index f01040460e..0000000000 --- a/src/lib/Repository/NameSchema/NameSchemaFilter.php +++ /dev/null @@ -1,52 +0,0 @@ -} - */ - public function filterNameSchema(string $nameSchema): array - { - $retNamePattern = $nameSchema; - $foundGroups = preg_match_all('/\((.+)\)/U', $nameSchema, $groupArray); - $groupLookupTable = []; - - if ($foundGroups) { - $i = 0; - foreach ($groupArray[1] as $group) { - // Create meta-token for group - $metaToken = self::META_STRING . $i; - - // Insert the group with its placeholder token - $retNamePattern = str_replace($group, $metaToken, $retNamePattern); - - // Remove the pattern "(" ")" from the tokens - $group = str_replace(['(', ')'], '', $group); - - $groupLookupTable[$metaToken] = $group; - ++$i; - } - $nameSchema = $retNamePattern; - } - - return [$nameSchema, $groupLookupTable]; - } -} diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 423931612a..363c6041b3 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -58,9 +58,9 @@ class NameSchemaService implements NameSchemaServiceInterface */ protected array $settings; - private EventDispatcherInterface $eventDispatcher; + protected EventDispatcherInterface $eventDispatcher; - private SchemaIdentifierExtractorInterface $schemaIdentifierExtractor; + protected SchemaIdentifierExtractorInterface $schemaIdentifierExtractor; /** * @param array{limit?: integer, sequence?: string} $settings diff --git a/src/lib/Repository/NameSchema/TokenHandler.php b/src/lib/Repository/NameSchema/TokenHandler.php deleted file mode 100644 index 7e5f9e4395..0000000000 --- a/src/lib/Repository/NameSchema/TokenHandler.php +++ /dev/null @@ -1,99 +0,0 @@ - - * Text more text ==> - * - */ - public function extractTokens(string $nameSchema): array - { - preg_match_all('|<([^>]+)>|U', $nameSchema, $tokenArray); - - return $tokenArray[0]; - } - - /** - * Looks up the value $token should be replaced with and returns this as - * a string. Meta strings denoting token groups are automatically - * inferred. - */ - public function resolveToken(string $token, array $titles, array $groupLookupTable): string - { - $replaceString = ''; - $tokenParts = $this->tokenParts($token); - - foreach ($tokenParts as $tokenPart) { - if ($this->isTokenGroup($tokenPart)) { - $replaceString = $groupLookupTable[$tokenPart]; - $groupTokenArray = $this->extractTokens($replaceString); - - foreach ($groupTokenArray as $groupToken) { - $replaceString = str_replace( - $groupToken, - $this->resolveToken( - $groupToken, - $titles, - $groupLookupTable - ), - $replaceString - ); - } - - // We want to stop after the first matching token part / identifier is found - // if id1 has a value, id2 will not be used. - // In this case id1 or id1 is a token group. - break; - } - if (array_key_exists($tokenPart, $titles) && $titles[$tokenPart] !== '' && $titles[$tokenPart] !== null) { - $replaceString = $titles[$tokenPart]; - // We want to stop after the first matching token part / identifier is found - // if id1 has a value, id2 will not be used. - break; - } - } - - return $replaceString; - } - - /** - * Checks whether $identifier is a placeholder for a token group. - */ - public function isTokenGroup(string $identifier): bool - { - return strpos($identifier, self::META_STRING) !== false; - } - - /** - * Returns the different constituents of $token in an array. - * The normal case here is that the different identifiers within one token - * will be tokenized and returned. - * - * Example: - * - * "<title|text>" ==> array( 'title', 'text' ) - * - * - * @param string $token - * - * @return array - */ - public function tokenParts(string $token): array - { - return preg_split('/[^\w:]+/', $token, -1, PREG_SPLIT_NO_EMPTY); - } -} diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 588773e1a8..368714886e 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -8,6 +8,9 @@ namespace Ibexa\Tests\Core\Repository\NameSchema; +use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; use Ibexa\Contracts\Core\Repository\Values\Content\Field; use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; @@ -36,11 +39,10 @@ public function testResolveUrlAliasSchema(): void $content = $this->buildTestContentObject(); $contentType = $this->buildTestContentTypeStub(); - $nameSchemaService = $this->buildNameSchemaService( - ['field' => ['']], - $content, - ['eng-GB' => ['url_alias_schema' => 'foo']] - ); + $event = new ResolveUrlAliasSchemaEvent(['field' => ['']], $content); + $event->setTokenValues(['eng-GB' => ['url_alias_schema' => 'foo']]); + + $nameSchemaService = $this->buildNameSchemaService($event); $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); @@ -52,12 +54,10 @@ public function testResolveUrlAliasSchemaFallbackToNameSchema(): void $content = $this->buildTestContentObject(); $contentType = $this->buildTestContentTypeStub(self::NAME_SCHEMA, ''); - $nameSchemaService = $this->buildNameSchemaService( - ['field' => [self::NAME_SCHEMA]], - $content, - ['eng-GB' => ['name_schema' => 'bar']] - ); + $event = new ResolveUrlAliasSchemaEvent(['field' => [self::NAME_SCHEMA]], $content); + $event->setTokenValues(['eng-GB' => ['name_schema' => 'bar']]); + $nameSchemaService = $this->buildNameSchemaService($event); $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); self::assertEquals(['eng-GB' => 'bar'], $result); @@ -70,6 +70,10 @@ public static function getDataForTestResolveNameSchema(): iterable { yield 'Default: Field Map and Languages taken from Content Version' => [ [], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], [], [ 'eng-GB' => 'two', @@ -83,6 +87,10 @@ public static function getDataForTestResolveNameSchema(): iterable 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], 'text3' => ['eng-GB' => new TextLineValue('three')], ], + [ + 'eng-GB' => ['text2' => 'two', 'text3' => 'three'], + 'cro-HR' => ['text2' => 'Dva'], + ], ['eng-GB', 'cro-HR'], [ 'eng-GB' => 'three', @@ -95,19 +103,27 @@ public static function getDataForTestResolveNameSchema(): iterable * @dataProvider getDataForTestResolveNameSchema * * @param array> $fieldMap A map of Field Definition Identifier and Language Code to Field Value + * @param array> $tokenValues * @param array $languageCodes * @param array $expectedNames */ - public function testResolveNameSchema(array $fieldMap, array $languageCodes, array $expectedNames): void + public function testResolveNameSchema(array $fieldMap, array $tokenValues, array $languageCodes, array $expectedNames): void { $content = $this->buildTestContentObject(); $nameSchema = ''; - $nameSchemaService = $this->buildNameSchemaService( - ['field' => [$nameSchema]], + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + $event = new ResolveContentNameSchemaEvent( $content, - [] + ['field' => ['text3', 'text2']], + $contentType, + $fieldMap, + $languageCodes + ); + $event->setTokenValues($tokenValues); + + $nameSchemaService = $this->buildNameSchemaService( + $event ); - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); $result = $nameSchemaService->resolveNameSchema($content, $fieldMap, $languageCodes, $contentType); @@ -184,13 +200,21 @@ public function testResolve( array $settings = [] ): void { $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + + $event = new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $content->fields, + $content->versionInfo->languageCodes + ); + + $event->setTokenValues($fieldTitles); + $nameSchemaService = $this->buildNameSchemaService( - ['field' => [$nameSchema]], - $content, - [], + $event, $settings ); - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); $result = $nameSchemaService->resolve( $nameSchema, @@ -299,13 +323,8 @@ protected function buildTestContentTypeStub( * @param array> $schemaIdentifiers */ protected function getEventDispatcherMock( - array $schemaIdentifiers, - Content $content, - array $tokenValues + AbstractSchemaEvent $event ): EventDispatcherInterface { - $event = new ResolveUrlAliasSchemaEvent($schemaIdentifiers, $content); - $event->setTokenValues($tokenValues); - $eventDispatcherMock = $this->getEventDispatcher(); $eventDispatcherMock->method('dispatch') ->willReturn($event); @@ -314,14 +333,10 @@ protected function getEventDispatcherMock( } /** - * @param array> $schemaIdentifiers - * @param array> $tokenValues * @param array{limit?: integer, sequence?: string} $settings */ private function buildNameSchemaService( - array $schemaIdentifiers, - Content $content, - array $tokenValues, + AbstractSchemaEvent $event, array $settings = [] ): NameSchemaService { $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); @@ -333,7 +348,7 @@ private function buildNameSchemaService( return new NameSchemaService( $fieldTypeRegistryMock, new SchemaIdentifierExtractor(), - $this->getEventDispatcherMock($schemaIdentifiers, $content, $tokenValues), + $this->getEventDispatcherMock($event), $settings ); } diff --git a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php index 02bdffcfa9..29517d557c 100644 --- a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php +++ b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php @@ -99,8 +99,8 @@ public function getDataForTestExtract(): iterable yield $schemaString => [ $schemaString, [ - 'field' => ['description'], - 'attribute' => ['mouse_type', 'mouse_weight'], + 'field' => ['abc', 'xyz', 'name'], + 'attribute' => ['color'], ], ]; } From 8ea01471b26556e84d42565bdb626b15432e6f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 23:16:52 +0200 Subject: [PATCH 07/37] Refactored names --- .../Repository/NameSchema/NameSchemaServiceInterface.php | 4 ++-- src/lib/Repository/ContentService.php | 4 ++-- src/lib/Repository/Helper/NameSchemaService.php | 8 ++++---- src/lib/Repository/NameSchema/NameSchemaService.php | 8 ++------ tests/lib/Repository/NameSchema/NameSchemaServiceTest.php | 4 ++-- tests/lib/Repository/Service/Mock/ContentTest.php | 4 ++-- 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php index 1d27bb149e..ac7ba9fbe5 100644 --- a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php +++ b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php @@ -29,7 +29,7 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType * * @return array */ - public function resolveNameSchema( + public function resolveContentNameSchema( Content $content, array $fieldMap = [], array $languageCodes = [], @@ -46,5 +46,5 @@ public function resolveNameSchema( * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array; + public function resolveNameSchema(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array; } diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 0f88850e33..80d8f9d0c2 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -739,7 +739,7 @@ public function createContent(APIContentCreateStruct $contentCreateStruct, array $spiContentCreateStruct = new SPIContentCreateStruct( [ - 'name' => $this->nameSchemaService->resolve( + 'name' => $this->nameSchemaService->resolveNameSchema( $contentCreateStruct->contentType->nameSchema, $contentCreateStruct->contentType, $fieldValues, @@ -1406,7 +1406,7 @@ protected function internalUpdateContent( $spiContentUpdateStruct = new SPIContentUpdateStruct( [ - 'name' => $this->nameSchemaService->resolveNameSchema( + 'name' => $this->nameSchemaService->resolveContentNameSchema( $content, $fieldValues, $allLanguageCodes, diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index e847cc0a16..002b0179d7 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -20,7 +20,7 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType { $contentType = $contentType ?? $content->getContentType(); - return $this->resolve( + return $this->resolveNameSchema( empty($contentType->urlAliasSchema) ? $contentType->nameSchema : $contentType->urlAliasSchema, $contentType, $content->fields, @@ -28,7 +28,7 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType ); } - public function resolveNameSchema( + public function resolveContentNameSchema( Content $content, array $fieldMap = [], array $languageCodes = [], @@ -38,7 +38,7 @@ public function resolveNameSchema( $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; - return $this->resolve( + return $this->resolveNameSchema( $contentType->nameSchema, $contentType, $this->mergeFieldMap( @@ -50,7 +50,7 @@ public function resolveNameSchema( ); } - public function resolve(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array + public function resolveNameSchema(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array { [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); $tokens = $this->extractTokens($filteredNameSchema); diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 363c6041b3..89bbe32ef8 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -97,7 +97,7 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType return $this->buildNames($event->getTokenValues(), $schemaName); } - public function resolveNameSchema( + public function resolveContentNameSchema( Content $content, array $fieldMap = [], array $languageCodes = [], @@ -119,11 +119,7 @@ public function resolveNameSchema( return $this->buildNames($event->getTokenValues(), $schemaName); } - public function resolveContentNameSchema() - { - } - - public function resolve( + public function resolveNameSchema( string $nameSchema, ContentType $contentType, array $fieldMap, diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 368714886e..19813b391f 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -125,7 +125,7 @@ public function testResolveNameSchema(array $fieldMap, array $tokenValues, array $event ); - $result = $nameSchemaService->resolveNameSchema($content, $fieldMap, $languageCodes, $contentType); + $result = $nameSchemaService->resolveContentNameSchema($content, $fieldMap, $languageCodes, $contentType); self::assertEquals( $expectedNames, @@ -216,7 +216,7 @@ public function testResolve( $settings ); - $result = $nameSchemaService->resolve( + $result = $nameSchemaService->resolveNameSchema( $nameSchema, $contentType, $content->fields, diff --git a/tests/lib/Repository/Service/Mock/ContentTest.php b/tests/lib/Repository/Service/Mock/ContentTest.php index 5f6a4558c7..bea81d54d1 100644 --- a/tests/lib/Repository/Service/Mock/ContentTest.php +++ b/tests/lib/Repository/Service/Mock/ContentTest.php @@ -1406,7 +1406,7 @@ static function (ValueStub $value) { $languageCodes ); $nameSchemaServiceMock->expects(self::once()) - ->method('resolve') + ->method('resolveNameSchema') ->with( self::equalTo($contentType->nameSchema), self::equalTo($contentType), @@ -3462,7 +3462,7 @@ static function (SPIValue $value) use ($emptyValue) { $languageCodes ); $nameSchemaServiceMock->expects($this->once()) - ->method('resolveNameSchema') + ->method('resolveContentNameSchema') ->with( $this->equalTo($content), $this->equalTo($values), From db1140b4d0baeb6074bd35f5108f9c79ab1a9331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 23:21:46 +0200 Subject: [PATCH 08/37] Fixed code styles --- .../Repository/NameSchema/NameSchemaServiceInterface.php | 7 ++++++- src/lib/Repository/Helper/NameSchemaService.php | 7 ++++++- tests/lib/Repository/NameSchema/NameSchemaServiceTest.php | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php index ac7ba9fbe5..6f3a6cf8a1 100644 --- a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php +++ b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php @@ -46,5 +46,10 @@ public function resolveContentNameSchema( * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function resolveNameSchema(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array; + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array; } diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 002b0179d7..2ad482c492 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -50,7 +50,12 @@ public function resolveContentNameSchema( ); } - public function resolveNameSchema(string $nameSchema, ContentType $contentType, array $fieldMap, array $languageCodes): array + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array { [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); $tokens = $this->extractTokens($filteredNameSchema); diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 19813b391f..b4dd2b00ac 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -107,7 +107,12 @@ public static function getDataForTestResolveNameSchema(): iterable * @param array $languageCodes * @param array $expectedNames */ - public function testResolveNameSchema(array $fieldMap, array $tokenValues, array $languageCodes, array $expectedNames): void + public function testResolveNameSchema( + array $fieldMap, + array $tokenValues, + array $languageCodes, + array $expectedNames + ): void { $content = $this->buildTestContentObject(); $nameSchema = ''; From 2d15853c5aaeac6b2be3d18b998b245e797b94b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 9 Aug 2023 23:50:36 +0200 Subject: [PATCH 09/37] Fixed code styles --- src/lib/Repository/Helper/NameSchemaService.php | 3 +-- tests/lib/Repository/NameSchema/NameSchemaServiceTest.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 2ad482c492..da5ad1fb15 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -55,8 +55,7 @@ public function resolveNameSchema( ContentType $contentType, array $fieldMap, array $languageCodes - ): array - { + ): array { [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); $tokens = $this->extractTokens($filteredNameSchema); $schemaIdentifiers = $this->getIdentifiers($nameSchema); diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index b4dd2b00ac..00f0fb366a 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -112,8 +112,7 @@ public function testResolveNameSchema( array $tokenValues, array $languageCodes, array $expectedNames - ): void - { + ): void { $content = $this->buildTestContentObject(); $nameSchema = ''; $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); From c8fe208088c0499ccb573a9189fe29942f2b2745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 10 Aug 2023 00:36:50 +0200 Subject: [PATCH 10/37] Fixed bc --- .../Repository/Helper/NameSchemaService.php | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index da5ad1fb15..f233147e2a 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -6,9 +6,16 @@ */ namespace Ibexa\Core\Repository\Helper; +use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper; use Ibexa\Core\Repository\NameSchema\NameSchemaService as NativeNameSchemaService; +use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @deprecated inject \Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface instead. @@ -16,6 +23,32 @@ */ class NameSchemaService extends NativeNameSchemaService { + protected ContentTypeHandler $contentTypeHandler; + + protected ContentTypeDomainMapper $contentTypeDomainMapper; + + public function __construct( + ContentTypeHandler $contentTypeHandler, + ContentTypeDomainMapper $contentTypeDomainMapper, + FieldTypeRegistry $fieldTypeRegistry, + EventDispatcherInterface $eventDispatcher, + array $settings = [] + ) { + $this->settings = $settings + [ + 'limit' => 150, + 'sequence' => '...', + ]; + + parent::__construct( + $fieldTypeRegistry, + new SchemaIdentifierExtractor(), + $eventDispatcher, + $settings + ); + $this->contentTypeHandler = $contentTypeHandler; + $this->contentTypeDomainMapper = $contentTypeDomainMapper; + } + public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array { $contentType = $contentType ?? $content->getContentType(); @@ -79,6 +112,84 @@ public function resolveNameSchema( return $names; } + + protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes) + { + if (empty($fieldMap)) { + return $content->fields; + } + + $mergedFieldMap = []; + + foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { + foreach ($languageCodes as $languageCode) { + $mergedFieldMap[$fieldIdentifier][$languageCode] = isset($fieldMap[$fieldIdentifier][$languageCode]) + ? $fieldMap[$fieldIdentifier][$languageCode] + : $fieldLanguageMap[$languageCode]; + } + } + + return $mergedFieldMap; + } + + /** + * Fetches the list of available Field identifiers in the token and returns + * an array of their current title value. + * + * @param string[] $schemaIdentifiers + * @param \Ibexa\Contracts\Core\Persistence\Content\Type|\Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param array $fieldMap + * @param string $languageCode + * + * @return string[] Key is the field identifier, value is the title value + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * + * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() + */ + protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode) + { + $fieldTitles = []; + + foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { + if (isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { + if ($contentType instanceof SPIContentType) { + $fieldDefinition = null; + foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { + if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { + $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( + $spiFieldDefinition, + // This is probably not main language, but as we don't expose it, it's ok for now. + $languageCode + ); + break; + } + } + + if ($fieldDefinition === null) { + $fieldTitles[$fieldDefinitionIdentifier] = ''; + continue; + } + } elseif ($contentType instanceof ContentType) { + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + } else { + throw new InvalidArgumentType('$contentType', 'API or SPI variant of a Content Type'); + } + + $fieldTypeService = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + + $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( + $fieldMap[$fieldDefinitionIdentifier][$languageCode], + $fieldDefinition, + $languageCode + ); + } + } + + return $fieldTitles; + } } class_alias(NameSchemaService::class, 'eZ\Publish\Core\Repository\Helper\NameSchemaService'); From 214d4041ecaa14f457384a8c20cf0135119bb6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 17 Aug 2023 14:55:40 +0200 Subject: [PATCH 11/37] Added language mapping. Fixed tests --- .../EventSubscriber/NameSchemaSubscriber.php | 17 ++++++++++++++--- .../Repository/NameSchema/NameSchemaService.php | 4 ++++ .../UnresolvedTokenNamesException.php | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index befa7e238d..69d12ac60c 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -14,6 +14,7 @@ use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Core\FieldType\FieldTypeRegistry; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -64,7 +65,12 @@ public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void $content = $event->getContent(); $contentType = $content->getContentType(); $tokenValues = $this->processEvent( - $event->getContent()->getVersionInfo()->getLanguages(), + array_map( + static function (Language $language) { + return $language->getLanguageCode(); + }, + $event->getContent()->getVersionInfo()->getLanguages() + ), $event->getSchemaIdentifiers()['field'], $contentType, $content, @@ -97,6 +103,11 @@ private function processNameSchemaEvent(AbstractNameSchemaEvent $event): void $event->setTokenValues($tokenValues); } + /** + * @param array $languages + * @param array> $identifiers + * @param array> $tokenValues + */ private function processEvent( array $languages, array $identifiers, @@ -105,9 +116,9 @@ private function processEvent( array $tokenValues, ?array $fieldMap = null ): array { - foreach ($languages as $language) { - $languageCode = is_string($language) ? $language : $language->getLanguageCode(); + foreach ($languages as $languageCode) { foreach ($identifiers as $identifier) { + $tokenValues[$languageCode] = []; $fieldDefinition = $contentType->getFieldDefinition($identifier); if (null === $fieldDefinition) { continue; diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 89bbe32ef8..8e1d4becc3 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -274,6 +274,10 @@ protected function filterNameSchema(string $nameSchema): array */ public function buildNames(array $tokenValues, string $nameSchema): array { + if (empty($tokenValues)) { + throw new UnresolvedTokenNamesException(); + } + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); $tokens = $this->extractTokens($filteredNameSchema); diff --git a/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php new file mode 100644 index 0000000000..b5db688671 --- /dev/null +++ b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php @@ -0,0 +1,15 @@ + Date: Thu, 17 Aug 2023 16:21:19 +0200 Subject: [PATCH 12/37] Fixed Content service --- src/lib/Repository/ContentService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php index 80d8f9d0c2..b02bc75a8f 100644 --- a/src/lib/Repository/ContentService.php +++ b/src/lib/Repository/ContentService.php @@ -1565,7 +1565,7 @@ protected function copyNonTranslatableFieldsFromPublishedVersion(APIContent $cur } $updateStruct = new SPIContentUpdateStruct(); - $updateStruct->name = $this->nameSchemaService->resolveNameSchema( + $updateStruct->name = $this->nameSchemaService->resolveContentNameSchema( $currentVersionContent, $fieldValues, $versionInfo->languageCodes, From cf2146544b037dd018373796b3323033d21bd46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 21 Aug 2023 09:58:01 +0200 Subject: [PATCH 13/37] Fixed name schema generation issue --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 69d12ac60c..9252813f22 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -117,8 +117,8 @@ private function processEvent( ?array $fieldMap = null ): array { foreach ($languages as $languageCode) { + $tokenValues[$languageCode] = []; foreach ($identifiers as $identifier) { - $tokenValues[$languageCode] = []; $fieldDefinition = $contentType->getFieldDefinition($identifier); if (null === $fieldDefinition) { continue; From 68623ed8755aff4328d73f631445820ae00c688c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Tue, 22 Aug 2023 08:43:23 +0200 Subject: [PATCH 14/37] Fixes after review --- .../Event/NameSchema/AbstractSchemaEvent.php | 8 +-- .../ResolveContentNameSchemaEvent.php | 2 +- .../Repository/Helper/NameSchemaService.php | 64 +++++++++---------- .../NameSchema/NameSchemaService.php | 4 +- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php index b1e890ad4d..d736c63e38 100644 --- a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -13,22 +13,22 @@ abstract class AbstractSchemaEvent extends Event { /** @var array */ - protected array $schemaIdentifiers; + private array $schemaIdentifiers; /** @var array> */ - protected array $tokenValues = []; + private array $tokenValues = []; public function __construct(array $schemaIdentifiers) { $this->schemaIdentifiers = $schemaIdentifiers; } - public function getTokenValues(): array + final public function getTokenValues(): array { return $this->tokenValues; } - public function setTokenValues(array $names): void + final public function setTokenValues(array $names): void { $this->tokenValues = $names; } diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index 8f4815a061..01435a74cf 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -13,7 +13,7 @@ final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent implements ContentAwareEventInterface { - protected Content $content; + private Content $content; public function __construct( Content $content, diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index f233147e2a..3b348022c2 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -123,9 +123,7 @@ protected function mergeFieldMap(Content $content, array $fieldMap, array $langu foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { foreach ($languageCodes as $languageCode) { - $mergedFieldMap[$fieldIdentifier][$languageCode] = isset($fieldMap[$fieldIdentifier][$languageCode]) - ? $fieldMap[$fieldIdentifier][$languageCode] - : $fieldLanguageMap[$languageCode]; + $mergedFieldMap[$fieldIdentifier][$languageCode] = $fieldMap[$fieldIdentifier][$languageCode] ?? $fieldLanguageMap[$languageCode]; } } @@ -152,40 +150,38 @@ protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldTitles = []; foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { - if (isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { - if ($contentType instanceof SPIContentType) { - $fieldDefinition = null; - foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { - if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { - $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( - $spiFieldDefinition, - // This is probably not main language, but as we don't expose it, it's ok for now. - $languageCode - ); - break; - } - } - - if ($fieldDefinition === null) { - $fieldTitles[$fieldDefinitionIdentifier] = ''; - continue; + if (!isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { + continue; + } + if ($contentType instanceof SPIContentType) { + $fieldDefinition = null; + foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { + if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { + $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( + $spiFieldDefinition, + // This is probably not main language, but as we don't expose it, it's ok for now. + $languageCode + ); + break; } - } elseif ($contentType instanceof ContentType) { - $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); - } else { - throw new InvalidArgumentType('$contentType', 'API or SPI variant of a Content Type'); } - - $fieldTypeService = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( - $fieldMap[$fieldDefinitionIdentifier][$languageCode], - $fieldDefinition, - $languageCode - ); + if ($fieldDefinition === null) { + $fieldTitles[$fieldDefinitionIdentifier] = ''; + continue; + } + } elseif ($contentType instanceof ContentType) { + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + } else { + throw new InvalidArgumentType('$contentType', 'API or SPI variant of a Content Type'); } + $fieldTypeService = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( + $fieldMap[$fieldDefinitionIdentifier][$languageCode], + $fieldDefinition, + $languageCode + ); } return $fieldTitles; diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 8e1d4becc3..3ed92f7371 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -58,9 +58,9 @@ class NameSchemaService implements NameSchemaServiceInterface */ protected array $settings; - protected EventDispatcherInterface $eventDispatcher; + private EventDispatcherInterface $eventDispatcher; - protected SchemaIdentifierExtractorInterface $schemaIdentifierExtractor; + private SchemaIdentifierExtractorInterface $schemaIdentifierExtractor; /** * @param array{limit?: integer, sequence?: string} $settings From 893fe92c65839b610d7ac03c7251d373e2aaa702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 24 Aug 2023 10:05:24 +0200 Subject: [PATCH 15/37] Fixed styling --- src/lib/Repository/Helper/NameSchemaService.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 3b348022c2..e169e808a8 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -35,9 +35,9 @@ public function __construct( array $settings = [] ) { $this->settings = $settings + [ - 'limit' => 150, - 'sequence' => '...', - ]; + 'limit' => 150, + 'sequence' => '...', + ]; parent::__construct( $fieldTypeRegistry, From cdbcde8d72b05f8101e9c35e213b6743994e5542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 31 Aug 2023 10:26:58 +0200 Subject: [PATCH 16/37] Small fixes --- .../EventSubscriber/NameSchemaSubscriber.php | 2 +- src/lib/Repository/Helper/NameSchemaService.php | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 9252813f22..777a690f5f 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -66,7 +66,7 @@ public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void $contentType = $content->getContentType(); $tokenValues = $this->processEvent( array_map( - static function (Language $language) { + static function (Language $language): string { return $language->getLanguageCode(); }, $event->getContent()->getVersionInfo()->getLanguages() diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index e169e808a8..b39150a28b 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -4,6 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ + namespace Ibexa\Core\Repository\Helper; use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; @@ -35,9 +36,9 @@ public function __construct( array $settings = [] ) { $this->settings = $settings + [ - 'limit' => 150, - 'sequence' => '...', - ]; + 'limit' => 150, + 'sequence' => '...', + ]; parent::__construct( $fieldTypeRegistry, @@ -113,7 +114,7 @@ public function resolveNameSchema( return $names; } - protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes) + protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array { if (empty($fieldMap)) { return $content->fields; @@ -145,7 +146,7 @@ protected function mergeFieldMap(Content $content, array $fieldMap, array $langu * * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() */ - protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode) + protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode): array { $fieldTitles = []; From 89acc4e1516092473d1084e0b3a3336fdaea67f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 6 Sep 2023 10:10:47 +0200 Subject: [PATCH 17/37] Fixed issue with ovewriting array key --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 2 +- src/lib/Repository/Helper/NameSchemaService.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 777a690f5f..375e027b3c 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -117,7 +117,7 @@ private function processEvent( ?array $fieldMap = null ): array { foreach ($languages as $languageCode) { - $tokenValues[$languageCode] = []; + $tokenValues[$languageCode] ?? $tokenValues[$languageCode] = []; foreach ($identifiers as $identifier) { $fieldDefinition = $contentType->getFieldDefinition($identifier); if (null === $fieldDefinition) { diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index b39150a28b..4490ca32a9 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -4,7 +4,6 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ - namespace Ibexa\Core\Repository\Helper; use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; From 7cfc5237ba58125b9934ba9ffba737ceb10f6dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 7 Sep 2023 12:33:29 +0200 Subject: [PATCH 18/37] PHP Stan --- .../NameSchema/AbstractNameSchemaEvent.php | 9 ++ .../Event/NameSchema/AbstractSchemaEvent.php | 16 +++- .../ResolveContentNameSchemaEvent.php | 7 ++ .../NameSchema/ResolveUrlAliasSchemaEvent.php | 3 + .../NameSchema/NameSchemaServiceInterface.php | 14 ++- .../EventSubscriber/NameSchemaSubscriber.php | 94 +++++++++++-------- .../Repository/Helper/NameSchemaService.php | 15 ++- .../NameSchema/NameSchemaService.php | 5 +- 8 files changed, 114 insertions(+), 49 deletions(-) diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 3fffff5b9d..790ba3f5a7 100644 --- a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -14,10 +14,17 @@ abstract class AbstractNameSchemaEvent extends AbstractSchemaEvent { private ContentType $contentType; + /** @var array> */ private array $fieldMap; + /** @var array */ private array $languageCodes; + /** + * @param array> $schemaIdentifiers + * @param array> $fieldMap + * @param array $languageCodes + */ public function __construct( array $schemaIdentifiers, ContentType $contentType, @@ -35,11 +42,13 @@ public function getContentType(): ContentType return $this->contentType; } + /** @return array> */ public function getFieldMap(): array { return $this->fieldMap; } + /** @return array */ public function getLanguageCodes(): array { return $this->languageCodes; diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php index d736c63e38..ac93f8bf6f 100644 --- a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -12,27 +12,39 @@ abstract class AbstractSchemaEvent extends Event { - /** @var array */ + /** @var array> */ private array $schemaIdentifiers; - /** @var array> */ + /** @var array> */ private array $tokenValues = []; + /** + * @param array> $schemaIdentifiers + */ public function __construct(array $schemaIdentifiers) { $this->schemaIdentifiers = $schemaIdentifiers; } + /** + * @return array> + */ final public function getTokenValues(): array { return $this->tokenValues; } + /** + * @param array> $names + */ final public function setTokenValues(array $names): void { $this->tokenValues = $names; } + /** + * @return array> + */ public function getSchemaIdentifiers(): array { return $this->schemaIdentifiers; diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index 01435a74cf..bebb2a57ca 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -15,6 +15,13 @@ final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent implem { private Content $content; + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param array> $schemaIdentifiers + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param array> $fieldMap + * @param array $languageCodes + */ public function __construct( Content $content, array $schemaIdentifiers, diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php index 051abf8fcd..a7e7ce77aa 100644 --- a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -14,6 +14,9 @@ final class ResolveUrlAliasSchemaEvent extends AbstractSchemaEvent implements Co { protected Content $content; + /** + * @param array> $schemaIdentifiers + */ public function __construct( array $schemaIdentifiers, Content $content diff --git a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php index 6f3a6cf8a1..e78109156c 100644 --- a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php +++ b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php @@ -21,13 +21,16 @@ interface NameSchemaServiceInterface /** * @return array key value map of names for a language code */ - public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array; + public function resolveUrlAliasSchema( + Content $content, + ContentType $contentType = null + ): array; /** - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * - * @return array + * @return array */ public function resolveContentNameSchema( Content $content, @@ -39,10 +42,11 @@ public function resolveContentNameSchema( /** * Returns the real name for a content name pattern. * - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * - * @return array + * @return array * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 375e027b3c..009dd84542 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -8,7 +8,6 @@ namespace Ibexa\Core\Repository\EventSubscriber; -use Ibexa\Contracts\Core\Event\NameSchema\AbstractNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; @@ -48,12 +47,43 @@ public static function getSubscribedEvents(): array public function onResolveNameSchema(ResolveNameSchemaEvent $event): void { - $this->processNameSchemaEvent($event); + if (!$this->isValid($event)) { + return; + } + + $tokenValues = $this->processEvent( + $event->getLanguageCodes(), + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + null, + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); } public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void { - $this->processNameSchemaEvent($event); + if (!$this->isValid($event)) { + return; + } + + $languageList = array_map( + static fn (Language $language): string => $language->getLanguageCode(), + (array)$event->getContent()->getVersionInfo()->getLanguages() + ); + + $tokenValues = $this->processEvent( + $languageList, + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + $event->getContent(), + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); } public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void @@ -62,15 +92,15 @@ public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void return; } + $languageList = array_map( + static fn (Language $language): string => $language->getLanguageCode(), + (array)$event->getContent()->getVersionInfo()->getLanguages() + ); + $content = $event->getContent(); $contentType = $content->getContentType(); $tokenValues = $this->processEvent( - array_map( - static function (Language $language): string { - return $language->getLanguageCode(); - }, - $event->getContent()->getVersionInfo()->getLanguages() - ), + $languageList, $event->getSchemaIdentifiers()['field'], $contentType, $content, @@ -85,28 +115,15 @@ public function isValid(AbstractSchemaEvent $event): bool return array_key_exists('field', $event->getSchemaIdentifiers()); } - private function processNameSchemaEvent(AbstractNameSchemaEvent $event): void - { - if (!$this->isValid($event)) { - return; - } - - $tokenValues = $this->processEvent( - $event->getLanguageCodes(), - $event->getSchemaIdentifiers()['field'], - $event->getContentType(), - null, - $event->getTokenValues(), - $event->getFieldMap() - ); - - $event->setTokenValues($tokenValues); - } - /** * @param array $languages - * @param array> $identifiers - * @param array> $tokenValues + * @param array $identifiers + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content + * @param array> $tokenValues + * @param array> $fieldMap + * + * @return array> */ private function processEvent( array $languages, @@ -114,7 +131,7 @@ private function processEvent( ContentType $contentType, ?Content $content, array $tokenValues, - ?array $fieldMap = null + array $fieldMap = [] ): array { foreach ($languages as $languageCode) { $tokenValues[$languageCode] ?? $tokenValues[$languageCode] = []; @@ -123,19 +140,22 @@ private function processEvent( if (null === $fieldDefinition) { continue; } + $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - $fieldValue = $fieldMap - ? $fieldMap[$identifier][$languageCode] ?? '' - : $content->getFieldValue($identifier, $languageCode); + if ($content === null && !$fieldMap) { + continue; + } + + $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? null : $content->getFieldValue($identifier, $languageCode); - $fieldValue = $persistenceFieldType->getName( - $fieldValue, + $field = $field ? $persistenceFieldType->getName( + $field, $fieldDefinition, $languageCode - ); + ) : ''; - $tokenValues[$languageCode][$identifier] = $fieldValue; + $tokenValues[$languageCode][$identifier] = $field; } } diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index 4490ca32a9..b9d4c37f32 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -113,6 +113,13 @@ public function resolveNameSchema( return $names; } + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param array> $fieldMap + * @param array $languageCodes + * + * @return array + */ protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array { if (empty($fieldMap)) { @@ -134,12 +141,12 @@ protected function mergeFieldMap(Content $content, array $fieldMap, array $langu * Fetches the list of available Field identifiers in the token and returns * an array of their current title value. * - * @param string[] $schemaIdentifiers + * @param array> $schemaIdentifiers * @param \Ibexa\Contracts\Core\Persistence\Content\Type|\Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * @param array $fieldMap - * @param string $languageCode + * @param array> $fieldMap + * @param array $languageCode * - * @return string[] Key is the field identifier, value is the title value + * @return array Key is the field identifier, value is the title value * * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType * diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 3ed92f7371..15809baf8d 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -103,6 +103,7 @@ public function resolveContentNameSchema( array $languageCodes = [], ContentType $contentType = null ): array { + $contentType ??= $content->getContentType(); $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); @@ -270,7 +271,9 @@ protected function filterNameSchema(string $nameSchema): array } /** - * @param array $tokenValues + * @param array> $tokenValues + * + * @return array */ public function buildNames(array $tokenValues, string $nameSchema): array { From 381ad75905b57cb97f51192333774bb9420a3c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Fri, 8 Sep 2023 10:31:32 +0200 Subject: [PATCH 19/37] Fixed PHPstan issues --- .../NameSchema/AbstractNameSchemaEvent.php | 6 +- .../ResolveContentNameSchemaEvent.php | 2 +- .../NameSchema/NameSchemaServiceInterface.php | 5 +- .../EventSubscriber/NameSchemaSubscriber.php | 4 +- .../Repository/Helper/NameSchemaService.php | 24 +- .../NameSchema/NameSchemaServiceTest.php | 341 ------------------ .../User/PasswordHashServiceTest.php | 66 ---- 7 files changed, 17 insertions(+), 431 deletions(-) diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 790ba3f5a7..6f7c3ff117 100644 --- a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -14,7 +14,7 @@ abstract class AbstractNameSchemaEvent extends AbstractSchemaEvent { private ContentType $contentType; - /** @var array> */ + /** @var array> */ private array $fieldMap; /** @var array */ @@ -22,7 +22,7 @@ abstract class AbstractNameSchemaEvent extends AbstractSchemaEvent /** * @param array> $schemaIdentifiers - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes */ public function __construct( @@ -42,7 +42,7 @@ public function getContentType(): ContentType return $this->contentType; } - /** @return array> */ + /** @return array> */ public function getFieldMap(): array { return $this->fieldMap; diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index bebb2a57ca..f235b01c9a 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -19,7 +19,7 @@ final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent implem * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param array> $schemaIdentifiers * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes */ public function __construct( diff --git a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php index e78109156c..05920c32d7 100644 --- a/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php +++ b/src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php @@ -27,7 +27,7 @@ public function resolveUrlAliasSchema( ): array; /** - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * * @return array @@ -42,8 +42,7 @@ public function resolveContentNameSchema( /** * Returns the real name for a content name pattern. * - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * * @return array diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 009dd84542..3ce0f1c6f9 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -121,7 +121,7 @@ public function isValid(AbstractSchemaEvent $event): bool * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content * @param array> $tokenValues - * @param array> $fieldMap + * @param array> $fieldMap * * @return array> */ @@ -147,7 +147,7 @@ private function processEvent( continue; } - $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? null : $content->getFieldValue($identifier, $languageCode); + $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? '' : $content->getFieldValue($identifier, $languageCode); $field = $field ? $persistenceFieldType->getName( $field, diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php index b9d4c37f32..20990ffdc8 100644 --- a/src/lib/Repository/Helper/NameSchemaService.php +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -53,11 +53,9 @@ public function resolveUrlAliasSchema(Content $content, ContentType $contentType { $contentType = $contentType ?? $content->getContentType(); - return $this->resolveNameSchema( - empty($contentType->urlAliasSchema) ? $contentType->nameSchema : $contentType->urlAliasSchema, - $contentType, - $content->fields, - $content->versionInfo->languageCodes + return $this->resolveUrlAliasSchema( + $content, + $contentType ); } @@ -115,22 +113,18 @@ public function resolveNameSchema( /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content - * @param array> $fieldMap + * @param array> $fieldMap * @param array $languageCodes * - * @return array + * @return array> */ protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array { - if (empty($fieldMap)) { - return $content->fields; - } - $mergedFieldMap = []; foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { foreach ($languageCodes as $languageCode) { - $mergedFieldMap[$fieldIdentifier][$languageCode] = $fieldMap[$fieldIdentifier][$languageCode] ?? $fieldLanguageMap[$languageCode]; + $mergedFieldMap[$fieldIdentifier][$languageCode] = $fieldMap[$fieldIdentifier][$languageCode]; } } @@ -141,10 +135,10 @@ protected function mergeFieldMap(Content $content, array $fieldMap, array $langu * Fetches the list of available Field identifiers in the token and returns * an array of their current title value. * - * @param array> $schemaIdentifiers + * @param array $schemaIdentifiers * @param \Ibexa\Contracts\Core\Persistence\Content\Type|\Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * @param array> $fieldMap - * @param array $languageCode + * @param array> $fieldMap + * @param string $languageCode * * @return array Key is the field identifier, value is the title value * diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 00f0fb366a..7c3277e8fb 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -8,352 +8,11 @@ namespace Ibexa\Tests\Core\Repository\NameSchema; -use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; -use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; -use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; -use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; -use Ibexa\Contracts\Core\Repository\Values\Content\Field; -use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; -use Ibexa\Core\FieldType\TextLine\Type as TextLineFieldType; -use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; -use Ibexa\Core\Repository\NameSchema\NameSchemaService; -use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; -use Ibexa\Core\Repository\Values\Content\Content; -use Ibexa\Core\Repository\Values\Content\VersionInfo; -use Ibexa\Core\Repository\Values\ContentType\ContentType; -use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; -use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Traversable; /** * @covers \Ibexa\Core\Repository\NameSchema\NameSchemaService */ final class NameSchemaServiceTest extends BaseServiceMockTest { - private const NAME_SCHEMA = ''; - - public function testResolveUrlAliasSchema(): void - { - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentTypeStub(); - - $event = new ResolveUrlAliasSchemaEvent(['field' => ['']], $content); - $event->setTokenValues(['eng-GB' => ['url_alias_schema' => 'foo']]); - - $nameSchemaService = $this->buildNameSchemaService($event); - - $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); - - self::assertEquals(['eng-GB' => 'foo'], $result); - } - - public function testResolveUrlAliasSchemaFallbackToNameSchema(): void - { - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentTypeStub(self::NAME_SCHEMA, ''); - - $event = new ResolveUrlAliasSchemaEvent(['field' => [self::NAME_SCHEMA]], $content); - $event->setTokenValues(['eng-GB' => ['name_schema' => 'bar']]); - - $nameSchemaService = $this->buildNameSchemaService($event); - $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); - - self::assertEquals(['eng-GB' => 'bar'], $result); - } - - /** - * @return iterable>, array, array> - */ - public static function getDataForTestResolveNameSchema(): iterable - { - yield 'Default: Field Map and Languages taken from Content Version' => [ - [], - [ - 'eng-GB' => ['text2' => 'two'], - 'cro-HR' => ['text2' => 'dva'], - ], - [], - [ - 'eng-GB' => 'two', - 'cro-HR' => 'dva', - ], - ]; - - yield 'Field Map and Languages for update' => [ - [ - 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], - 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], - 'text3' => ['eng-GB' => new TextLineValue('three')], - ], - [ - 'eng-GB' => ['text2' => 'two', 'text3' => 'three'], - 'cro-HR' => ['text2' => 'Dva'], - ], - ['eng-GB', 'cro-HR'], - [ - 'eng-GB' => 'three', - 'cro-HR' => 'Dva', - ], - ]; - } - - /** - * @dataProvider getDataForTestResolveNameSchema - * - * @param array> $fieldMap A map of Field Definition Identifier and Language Code to Field Value - * @param array> $tokenValues - * @param array $languageCodes - * @param array $expectedNames - */ - public function testResolveNameSchema( - array $fieldMap, - array $tokenValues, - array $languageCodes, - array $expectedNames - ): void { - $content = $this->buildTestContentObject(); - $nameSchema = ''; - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); - $event = new ResolveContentNameSchemaEvent( - $content, - ['field' => ['text3', 'text2']], - $contentType, - $fieldMap, - $languageCodes - ); - $event->setTokenValues($tokenValues); - - $nameSchemaService = $this->buildNameSchemaService( - $event - ); - - $result = $nameSchemaService->resolveContentNameSchema($content, $fieldMap, $languageCodes, $contentType); - - self::assertEquals( - $expectedNames, - $result - ); - } - - /** - * Data provider for the testResolve method. - * - * @see testResolve - */ - public static function getDataForTestResolve(): array - { - return [ - [ - ['text1'], - '', - [ - 'eng-GB' => 'one', - 'cro-HR' => 'jedan', - ], - [ - 'eng-GB' => ['text1' => 'one'], - 'cro-HR' => ['text1' => 'jedan'], - ], - ], - [ - ['text2'], - '', - [ - 'eng-GB' => 'two', - 'cro-HR' => 'dva', - ], - [ - 'eng-GB' => ['text2' => 'two'], - 'cro-HR' => ['text2' => 'dva'], - ], - ], - [ - ['text1', 'text2'], - 'Hello, and and then goodbye and hello again', - [ - 'eng-GB' => 'Hello, one and two and then goodbye...', - 'cro-HR' => 'Hello, jedan and dva and then goodb...', - ], - [ - 'eng-GB' => ['text1' => 'one', 'text2' => 'two'], - 'cro-HR' => ['text1' => 'jedan', 'text2' => 'dva'], - ], - [ - 'limit' => 38, - 'sequence' => '...', - ], - ], - ]; - } - - /** - * @dataProvider getDataForTestResolve - * - * @param string[] $schemaIdentifiers - * @param string[] $languageFieldValues field value translations - * @param string[] $fieldTitles [language => [field_identifier => title]] - * @param array $settings NameSchemaService settings - */ - public function testResolve( - array $schemaIdentifiers, - string $nameSchema, - array $languageFieldValues, - array $fieldTitles, - array $settings = [] - ): void { - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); - - $event = new ResolveNameSchemaEvent( - $schemaIdentifiers, - $contentType, - $content->fields, - $content->versionInfo->languageCodes - ); - - $event->setTokenValues($fieldTitles); - - $nameSchemaService = $this->buildNameSchemaService( - $event, - $settings - ); - - $result = $nameSchemaService->resolveNameSchema( - $nameSchema, - $contentType, - $content->fields, - $content->versionInfo->languageCodes - ); - - self::assertEquals($languageFieldValues, $result); - } - - /** - * @return \Traversable<\Ibexa\Contracts\Core\Repository\Values\Content\Field> - */ - protected function getFields(): Traversable - { - $translatedFieldValueMap = [ - 'eng-GB' => [ - 'text1' => 'one', - 'text2' => 'two', - 'text3' => '', - ], - 'cro-HR' => [ - 'text1' => 'jedan', - 'text2' => 'dva', - 'text3' => '', - ], - ]; - - foreach ($translatedFieldValueMap as $languageCode => $fieldValues) { - foreach ($fieldValues as $fieldDefinitionIdentifier => $textValue) { - yield new Field( - [ - 'languageCode' => $languageCode, - 'fieldDefIdentifier' => $fieldDefinitionIdentifier, - 'value' => new TextLineValue($textValue), - 'fieldTypeIdentifier' => 'ezstring', - ] - ); - } - } - } - - protected function getFieldDefinitions(): APIFieldDefinitionCollection - { - return new FieldDefinitionCollection( - [ - new FieldDefinition( - [ - 'id' => '1', - 'identifier' => 'text1', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new FieldDefinition( - [ - 'id' => '2', - 'identifier' => 'text2', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new FieldDefinition( - [ - 'id' => '3', - 'identifier' => 'text3', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ] - ); - } - - /** - * Build Content Object stub for testing purpose. - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - */ - protected function buildTestContentObject() - { - return new Content( - [ - 'internalFields' => iterator_to_array($this->getFields()), - 'versionInfo' => new VersionInfo( - [ - 'languageCodes' => ['eng-GB', 'cro-HR'], - ] - ), - ] - ); - } - - protected function buildTestContentTypeStub( - string $nameSchema = '', - string $urlAliasSchema = '' - ): ContentType { - return new ContentType( - [ - 'nameSchema' => $nameSchema, - 'urlAliasSchema' => $urlAliasSchema, - 'fieldDefinitions' => $this->getFieldDefinitions(), - ] - ); - } - - /** - * @param array> $schemaIdentifiers - */ - protected function getEventDispatcherMock( - AbstractSchemaEvent $event - ): EventDispatcherInterface { - $eventDispatcherMock = $this->getEventDispatcher(); - $eventDispatcherMock->method('dispatch') - ->willReturn($event); - - return $eventDispatcherMock; - } - - /** - * @param array{limit?: integer, sequence?: string} $settings - */ - private function buildNameSchemaService( - AbstractSchemaEvent $event, - array $settings = [] - ): NameSchemaService { - $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); - $fieldTypeRegistryMock - ->method('getFieldType') - ->with('ezstring') - ->willReturn(new TextLineFieldType()); - - return new NameSchemaService( - $fieldTypeRegistryMock, - new SchemaIdentifierExtractor(), - $this->getEventDispatcherMock($event), - $settings - ); - } } diff --git a/tests/lib/Repository/User/PasswordHashServiceTest.php b/tests/lib/Repository/User/PasswordHashServiceTest.php index 35c303c3c1..e1f9d4e575 100644 --- a/tests/lib/Repository/User/PasswordHashServiceTest.php +++ b/tests/lib/Repository/User/PasswordHashServiceTest.php @@ -8,76 +8,10 @@ namespace Ibexa\Tests\Core\Repository\User; -use Ibexa\Contracts\Core\Repository\Values\User\User; -use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; -use Ibexa\Core\Repository\User\PasswordHashService; use PHPUnit\Framework\TestCase; final class PasswordHashServiceTest extends TestCase { - private const NON_EXISTING_PASSWORD_HASH = PHP_INT_MAX; - - /** @var \Ibexa\Core\Repository\User\PasswordHashService */ - private $passwordHashService; - - protected function setUp(): void - { - $this->passwordHashService = new PasswordHashService(); - } - - public function testGetSupportedHashTypes(): void - { - $this->assertEquals( - [ - User::PASSWORD_HASH_BCRYPT, - User::PASSWORD_HASH_PHP_DEFAULT, - ], - $this->passwordHashService->getSupportedHashTypes() - ); - } - - public function testIsHashTypeSupported(): void - { - $this->assertTrue($this->passwordHashService->isHashTypeSupported(User::DEFAULT_PASSWORD_HASH)); - $this->assertFalse($this->passwordHashService->isHashTypeSupported(self::NON_EXISTING_PASSWORD_HASH)); - } - - public function testCreatePasswordHashExceptionHidesSensitiveParameter(): void - { - $ignoreArgs = ini_get('zend.exception_ignore_args'); - $paramMax = ini_get('zend.exception_string_param_max_len'); - - ini_set('zend.exception_ignore_args', '0'); - ini_set('zend.exception_string_param_max_len', '10'); - - $password = 'secret'; - - try { - $this->passwordHashService->createPasswordHash($password, self::NON_EXISTING_PASSWORD_HASH); - self::fail(sprintf( - 'Expected exception %s to be thrown.', - UnsupportedPasswordHashType::class, - )); - } catch (UnsupportedPasswordHashType $e) { - $stackTrace = $e->getTrace(); - self::assertIsArray($stackTrace); - self::assertGreaterThan(1, count($stackTrace)); - self::assertArrayHasKey('function', $stackTrace[0]); - self::assertEquals('createPasswordHash', $stackTrace[0]['function']); - self::assertArrayHasKey('args', $stackTrace[0]); - - // SensitiveParameter was introduced in PHP 8.2, in older versions it is ignored - if (\PHP_VERSION_ID < 80200) { - self::assertEquals($password, $stackTrace[0]['args'][0]); - } else { - // @phpstan-ignore-next-line This class is PHP 8.2+ only, but older PHP never reaches this else-block. - self::assertInstanceOf(\SensitiveParameterValue::class, $stackTrace[0]['args'][0]); - } - } - - ini_set('zend.exception_ignore_args', (string)$ignoreArgs); - ini_set('zend.exception_string_param_max_len', (string)$paramMax); - } } class_alias(PasswordHashServiceTest::class, 'eZ\Publish\Core\Repository\Tests\User\PasswordHashServiceTest'); From 1cbe919d330d5e707579c184d716638f93d98d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Fri, 8 Sep 2023 11:04:37 +0200 Subject: [PATCH 20/37] Regenerated PHPstan baseline --- phpstan-baseline-7.4.neon | 10 ++++++++++ phpstan-baseline-8.0.neon | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index 63e48aa8de..0952ce82da 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -345,6 +345,16 @@ parameters: count: 1 path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + - message: "#^Parameter \\#1 \\$str of function md5 expects string, string\\|false given\\.$#" count: 4 diff --git a/phpstan-baseline-8.0.neon b/phpstan-baseline-8.0.neon index a9b6e51067..eafabef296 100644 --- a/phpstan-baseline-8.0.neon +++ b/phpstan-baseline-8.0.neon @@ -285,6 +285,16 @@ parameters: count: 1 path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + - message: "#^Parameter \\#1 \\$string of function md5 expects string, string\\|false given\\.$#" count: 4 From 8beb9426f53e40f28c195aedec0e646c5941e33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Fri, 8 Sep 2023 11:14:40 +0200 Subject: [PATCH 21/37] Regenerated phpstan-baseline.neon --- phpstan-baseline.neon | 125 ------------------------------------------ 1 file changed, 125 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 09579e6f87..b8e2534195 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -5035,31 +5035,6 @@ parameters: count: 1 path: src/contracts/Container/Encore/ConfigurationDumper.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:__construct\\(\\) has parameter \\$schemaIdentifiers with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:getSchemaIdentifiers\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:getTokenValues\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:setTokenValues\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Event\\\\ResolveUrlAliasSchemaEvent\\:\\:\\$schemaIdentifiers type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Event/ResolveUrlAliasSchemaEvent.php - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGenerator\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" count: 1 @@ -7015,11 +6990,6 @@ parameters: count: 1 path: src/contracts/Repository/LocationService.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolveNameSchema\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/contracts/Repository/NameSchema/NameSchemaServiceInterface.php - - message: "#^PHPDoc tag @param for parameter \\$objectStateGroupId with type mixed is not subtype of native type int\\.$#" count: 1 @@ -20245,16 +20215,6 @@ parameters: count: 1 path: src/lib/Repository/ContentService.php - - - message: "#^Parameter \\#2 \\$fieldMap of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolveNameSchema\\(\\) expects array\\\\>, array\\\\> given\\.$#" - count: 1 - path: src/lib/Repository/ContentService.php - - - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:resolve\\(\\) expects array\\\\>, array\\\\> given\\.$#" - count: 1 - path: src/lib/Repository/ContentService.php - - message: "#^Parameter \\#3 \\$prioritizedLanguages of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectFromPersistence\\(\\) expects array\\, array\\\\|null given\\.$#" count: 1 @@ -20335,16 +20295,6 @@ parameters: count: 1 path: src/lib/Repository/ContentTypeService.php - - - message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null given\\.$#" - count: 1 - path: src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php - - - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolve\\(\\) expects array\\\\>, array\\ given\\.$#" - count: 1 - path: src/lib/Repository/Helper/NameSchemaService.php - - message: "#^Argument of an invalid type array\\\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation supplied for foreach, only iterables are supported\\.$#" count: 1 @@ -20645,16 +20595,6 @@ parameters: count: 1 path: src/lib/Repository/Mapper/RoleDomainMapper.php - - - message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:extractTokens\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -20665,26 +20605,6 @@ parameters: count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) has parameter \\$fieldMap with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:mergeFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveNameSchema\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveToken\\(\\) has parameter \\$groupLookupTable with no value type specified in iterable type array\\.$#" count: 1 @@ -20705,16 +20625,6 @@ parameters: count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value, string given\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - - - message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/NameSchemaService.php - - message: "#^Property Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" count: 1 @@ -58210,41 +58120,6 @@ parameters: count: 2 path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getDataForTestResolve\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getDataForTestResolveNameSchema\\(\\) return type has no value type specified in iterable type iterable\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:getEventDispatcherMock\\(\\) has parameter \\$tokenValues with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:testResolve\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^PHPDoc tag @return contains unresolvable type\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Parameter \\#2 \\$content of method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceTest\\:\\:buildNameSchemaService\\(\\) expects Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content given\\.$#" - count: 4 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - - - message: "#^Parameter \\#3 \\$fieldMap of method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolve\\(\\) expects array\\\\>, array\\ given\\.$#" - count: 1 - path: tests/lib/Repository/NameSchema/NameSchemaServiceTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\AllValidationErrorsOccur\\:\\:extractTranslatable\\(\\) has parameter \\$fieldErrors with no value type specified in iterable type array\\.$#" count: 1 From b1abca7b77a937ceada0b2c57f1898982768a325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Niedzielski?= Date: Fri, 8 Sep 2023 11:21:41 +0200 Subject: [PATCH 22/37] Regenerated phpstan-baseline.neon --- phpstan-baseline-7.4.neon | 10 ---------- phpstan-baseline-8.0.neon | 10 ---------- phpstan-baseline.neon | 10 ++++++++++ 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index 0952ce82da..63e48aa8de 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -345,16 +345,6 @@ parameters: count: 1 path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php - - - message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" - count: 1 - path: src/lib/Repository/Helper/NameSchemaService.php - - - - message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" - count: 1 - path: src/lib/Repository/Helper/NameSchemaService.php - - message: "#^Parameter \\#1 \\$str of function md5 expects string, string\\|false given\\.$#" count: 4 diff --git a/phpstan-baseline-8.0.neon b/phpstan-baseline-8.0.neon index eafabef296..a9b6e51067 100644 --- a/phpstan-baseline-8.0.neon +++ b/phpstan-baseline-8.0.neon @@ -285,16 +285,6 @@ parameters: count: 1 path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php - - - message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" - count: 1 - path: src/lib/Repository/Helper/NameSchemaService.php - - - - message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" - count: 1 - path: src/lib/Repository/Helper/NameSchemaService.php - - message: "#^Parameter \\#1 \\$string of function md5 expects string, string\\|false given\\.$#" count: 4 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b8e2534195..7a99465f42 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20295,6 +20295,16 @@ parameters: count: 1 path: src/lib/Repository/ContentTypeService.php + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + - message: "#^Argument of an invalid type array\\\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation supplied for foreach, only iterables are supported\\.$#" count: 1 From 8d9bee926a6b5b0517b4db4c4c80b54f00436cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Fri, 8 Sep 2023 12:52:28 +0200 Subject: [PATCH 23/37] Restored deleted files --- .../NameSchema/NameSchemaServiceTest.php | 341 ++++++++++++++++++ .../User/PasswordHashServiceTest.php | 66 ++++ 2 files changed, 407 insertions(+) diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 7c3277e8fb..00f0fb366a 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -8,11 +8,352 @@ namespace Ibexa\Tests\Core\Repository\NameSchema; +use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; +use Ibexa\Core\FieldType\TextLine\Type as TextLineFieldType; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\Repository\NameSchema\NameSchemaService; +use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Traversable; /** * @covers \Ibexa\Core\Repository\NameSchema\NameSchemaService */ final class NameSchemaServiceTest extends BaseServiceMockTest { + private const NAME_SCHEMA = ''; + + public function testResolveUrlAliasSchema(): void + { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub(); + + $event = new ResolveUrlAliasSchemaEvent(['field' => ['']], $content); + $event->setTokenValues(['eng-GB' => ['url_alias_schema' => 'foo']]); + + $nameSchemaService = $this->buildNameSchemaService($event); + + $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); + + self::assertEquals(['eng-GB' => 'foo'], $result); + } + + public function testResolveUrlAliasSchemaFallbackToNameSchema(): void + { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub(self::NAME_SCHEMA, ''); + + $event = new ResolveUrlAliasSchemaEvent(['field' => [self::NAME_SCHEMA]], $content); + $event->setTokenValues(['eng-GB' => ['name_schema' => 'bar']]); + + $nameSchemaService = $this->buildNameSchemaService($event); + $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); + + self::assertEquals(['eng-GB' => 'bar'], $result); + } + + /** + * @return iterable>, array, array> + */ + public static function getDataForTestResolveNameSchema(): iterable + { + yield 'Default: Field Map and Languages taken from Content Version' => [ + [], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], + [], + [ + 'eng-GB' => 'two', + 'cro-HR' => 'dva', + ], + ]; + + yield 'Field Map and Languages for update' => [ + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], + [ + 'eng-GB' => ['text2' => 'two', 'text3' => 'three'], + 'cro-HR' => ['text2' => 'Dva'], + ], + ['eng-GB', 'cro-HR'], + [ + 'eng-GB' => 'three', + 'cro-HR' => 'Dva', + ], + ]; + } + + /** + * @dataProvider getDataForTestResolveNameSchema + * + * @param array> $fieldMap A map of Field Definition Identifier and Language Code to Field Value + * @param array> $tokenValues + * @param array $languageCodes + * @param array $expectedNames + */ + public function testResolveNameSchema( + array $fieldMap, + array $tokenValues, + array $languageCodes, + array $expectedNames + ): void { + $content = $this->buildTestContentObject(); + $nameSchema = ''; + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + $event = new ResolveContentNameSchemaEvent( + $content, + ['field' => ['text3', 'text2']], + $contentType, + $fieldMap, + $languageCodes + ); + $event->setTokenValues($tokenValues); + + $nameSchemaService = $this->buildNameSchemaService( + $event + ); + + $result = $nameSchemaService->resolveContentNameSchema($content, $fieldMap, $languageCodes, $contentType); + + self::assertEquals( + $expectedNames, + $result + ); + } + + /** + * Data provider for the testResolve method. + * + * @see testResolve + */ + public static function getDataForTestResolve(): array + { + return [ + [ + ['text1'], + '', + [ + 'eng-GB' => 'one', + 'cro-HR' => 'jedan', + ], + [ + 'eng-GB' => ['text1' => 'one'], + 'cro-HR' => ['text1' => 'jedan'], + ], + ], + [ + ['text2'], + '', + [ + 'eng-GB' => 'two', + 'cro-HR' => 'dva', + ], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], + ], + [ + ['text1', 'text2'], + 'Hello, and and then goodbye and hello again', + [ + 'eng-GB' => 'Hello, one and two and then goodbye...', + 'cro-HR' => 'Hello, jedan and dva and then goodb...', + ], + [ + 'eng-GB' => ['text1' => 'one', 'text2' => 'two'], + 'cro-HR' => ['text1' => 'jedan', 'text2' => 'dva'], + ], + [ + 'limit' => 38, + 'sequence' => '...', + ], + ], + ]; + } + + /** + * @dataProvider getDataForTestResolve + * + * @param string[] $schemaIdentifiers + * @param string[] $languageFieldValues field value translations + * @param string[] $fieldTitles [language => [field_identifier => title]] + * @param array $settings NameSchemaService settings + */ + public function testResolve( + array $schemaIdentifiers, + string $nameSchema, + array $languageFieldValues, + array $fieldTitles, + array $settings = [] + ): void { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + + $event = new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $content->fields, + $content->versionInfo->languageCodes + ); + + $event->setTokenValues($fieldTitles); + + $nameSchemaService = $this->buildNameSchemaService( + $event, + $settings + ); + + $result = $nameSchemaService->resolveNameSchema( + $nameSchema, + $contentType, + $content->fields, + $content->versionInfo->languageCodes + ); + + self::assertEquals($languageFieldValues, $result); + } + + /** + * @return \Traversable<\Ibexa\Contracts\Core\Repository\Values\Content\Field> + */ + protected function getFields(): Traversable + { + $translatedFieldValueMap = [ + 'eng-GB' => [ + 'text1' => 'one', + 'text2' => 'two', + 'text3' => '', + ], + 'cro-HR' => [ + 'text1' => 'jedan', + 'text2' => 'dva', + 'text3' => '', + ], + ]; + + foreach ($translatedFieldValueMap as $languageCode => $fieldValues) { + foreach ($fieldValues as $fieldDefinitionIdentifier => $textValue) { + yield new Field( + [ + 'languageCode' => $languageCode, + 'fieldDefIdentifier' => $fieldDefinitionIdentifier, + 'value' => new TextLineValue($textValue), + 'fieldTypeIdentifier' => 'ezstring', + ] + ); + } + } + } + + protected function getFieldDefinitions(): APIFieldDefinitionCollection + { + return new FieldDefinitionCollection( + [ + new FieldDefinition( + [ + 'id' => '1', + 'identifier' => 'text1', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new FieldDefinition( + [ + 'id' => '2', + 'identifier' => 'text2', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new FieldDefinition( + [ + 'id' => '3', + 'identifier' => 'text3', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ] + ); + } + + /** + * Build Content Object stub for testing purpose. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function buildTestContentObject() + { + return new Content( + [ + 'internalFields' => iterator_to_array($this->getFields()), + 'versionInfo' => new VersionInfo( + [ + 'languageCodes' => ['eng-GB', 'cro-HR'], + ] + ), + ] + ); + } + + protected function buildTestContentTypeStub( + string $nameSchema = '', + string $urlAliasSchema = '' + ): ContentType { + return new ContentType( + [ + 'nameSchema' => $nameSchema, + 'urlAliasSchema' => $urlAliasSchema, + 'fieldDefinitions' => $this->getFieldDefinitions(), + ] + ); + } + + /** + * @param array> $schemaIdentifiers + */ + protected function getEventDispatcherMock( + AbstractSchemaEvent $event + ): EventDispatcherInterface { + $eventDispatcherMock = $this->getEventDispatcher(); + $eventDispatcherMock->method('dispatch') + ->willReturn($event); + + return $eventDispatcherMock; + } + + /** + * @param array{limit?: integer, sequence?: string} $settings + */ + private function buildNameSchemaService( + AbstractSchemaEvent $event, + array $settings = [] + ): NameSchemaService { + $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); + $fieldTypeRegistryMock + ->method('getFieldType') + ->with('ezstring') + ->willReturn(new TextLineFieldType()); + + return new NameSchemaService( + $fieldTypeRegistryMock, + new SchemaIdentifierExtractor(), + $this->getEventDispatcherMock($event), + $settings + ); + } } diff --git a/tests/lib/Repository/User/PasswordHashServiceTest.php b/tests/lib/Repository/User/PasswordHashServiceTest.php index e1f9d4e575..35c303c3c1 100644 --- a/tests/lib/Repository/User/PasswordHashServiceTest.php +++ b/tests/lib/Repository/User/PasswordHashServiceTest.php @@ -8,10 +8,76 @@ namespace Ibexa\Tests\Core\Repository\User; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; +use Ibexa\Core\Repository\User\PasswordHashService; use PHPUnit\Framework\TestCase; final class PasswordHashServiceTest extends TestCase { + private const NON_EXISTING_PASSWORD_HASH = PHP_INT_MAX; + + /** @var \Ibexa\Core\Repository\User\PasswordHashService */ + private $passwordHashService; + + protected function setUp(): void + { + $this->passwordHashService = new PasswordHashService(); + } + + public function testGetSupportedHashTypes(): void + { + $this->assertEquals( + [ + User::PASSWORD_HASH_BCRYPT, + User::PASSWORD_HASH_PHP_DEFAULT, + ], + $this->passwordHashService->getSupportedHashTypes() + ); + } + + public function testIsHashTypeSupported(): void + { + $this->assertTrue($this->passwordHashService->isHashTypeSupported(User::DEFAULT_PASSWORD_HASH)); + $this->assertFalse($this->passwordHashService->isHashTypeSupported(self::NON_EXISTING_PASSWORD_HASH)); + } + + public function testCreatePasswordHashExceptionHidesSensitiveParameter(): void + { + $ignoreArgs = ini_get('zend.exception_ignore_args'); + $paramMax = ini_get('zend.exception_string_param_max_len'); + + ini_set('zend.exception_ignore_args', '0'); + ini_set('zend.exception_string_param_max_len', '10'); + + $password = 'secret'; + + try { + $this->passwordHashService->createPasswordHash($password, self::NON_EXISTING_PASSWORD_HASH); + self::fail(sprintf( + 'Expected exception %s to be thrown.', + UnsupportedPasswordHashType::class, + )); + } catch (UnsupportedPasswordHashType $e) { + $stackTrace = $e->getTrace(); + self::assertIsArray($stackTrace); + self::assertGreaterThan(1, count($stackTrace)); + self::assertArrayHasKey('function', $stackTrace[0]); + self::assertEquals('createPasswordHash', $stackTrace[0]['function']); + self::assertArrayHasKey('args', $stackTrace[0]); + + // SensitiveParameter was introduced in PHP 8.2, in older versions it is ignored + if (\PHP_VERSION_ID < 80200) { + self::assertEquals($password, $stackTrace[0]['args'][0]); + } else { + // @phpstan-ignore-next-line This class is PHP 8.2+ only, but older PHP never reaches this else-block. + self::assertInstanceOf(\SensitiveParameterValue::class, $stackTrace[0]['args'][0]); + } + } + + ini_set('zend.exception_ignore_args', (string)$ignoreArgs); + ini_set('zend.exception_string_param_max_len', (string)$paramMax); + } } class_alias(PasswordHashServiceTest::class, 'eZ\Publish\Core\Repository\Tests\User\PasswordHashServiceTest'); From ad47dfaa45ed33f9ab1aaf004e7da2f6871fd711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 11 Sep 2023 09:11:35 +0200 Subject: [PATCH 24/37] PHPStan fixes part2 --- .../NameSchema/AbstractNameSchemaEvent.php | 2 +- .../Event/NameSchema/AbstractSchemaEvent.php | 6 +- .../ResolveContentNameSchemaEvent.php | 2 +- .../NameSchema/ResolveUrlAliasSchemaEvent.php | 2 +- .../SchemaIdentifierExtractorInterface.php | 2 +- .../EventSubscriber/NameSchemaSubscriber.php | 2 +- .../NameSchema/SchemaIdentifierExtractor.php | 2 +- .../NameSchema/NameSchemaServiceTest.php | 56 ++++++++++++++----- 8 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php index 6f7c3ff117..35e59cb927 100644 --- a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -21,7 +21,7 @@ abstract class AbstractNameSchemaEvent extends AbstractSchemaEvent private array $languageCodes; /** - * @param array> $schemaIdentifiers + * @param array> $schemaIdentifiers * @param array> $fieldMap * @param array $languageCodes */ diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php index ac93f8bf6f..869ecc0d90 100644 --- a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -12,14 +12,14 @@ abstract class AbstractSchemaEvent extends Event { - /** @var array> */ + /** @var array> */ private array $schemaIdentifiers; /** @var array> */ private array $tokenValues = []; /** - * @param array> $schemaIdentifiers + * @param array> $schemaIdentifiers */ public function __construct(array $schemaIdentifiers) { @@ -43,7 +43,7 @@ final public function setTokenValues(array $names): void } /** - * @return array> + * @return array> */ public function getSchemaIdentifiers(): array { diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index f235b01c9a..9d3ff475e7 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -17,7 +17,7 @@ final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent implem /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content - * @param array> $schemaIdentifiers + * @param array> $schemaIdentifiers * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param array> $fieldMap * @param array $languageCodes diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php index a7e7ce77aa..d7cfe8c1ab 100644 --- a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -15,7 +15,7 @@ final class ResolveUrlAliasSchemaEvent extends AbstractSchemaEvent implements Co protected Content $content; /** - * @param array> $schemaIdentifiers + * @param array> $schemaIdentifiers */ public function __construct( array $schemaIdentifiers, diff --git a/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php index e86980e817..51cc0d4a46 100644 --- a/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php +++ b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php @@ -11,7 +11,7 @@ interface SchemaIdentifierExtractorInterface { /** - * @return array> + * @return array> */ public function extract(string $schemaString): array; } diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 3ce0f1c6f9..bb237ad09d 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -117,7 +117,7 @@ public function isValid(AbstractSchemaEvent $event): bool /** * @param array $languages - * @param array $identifiers + * @param array $identifiers * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content * @param array> $tokenValues diff --git a/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php index d61478fada..032e8ef279 100644 --- a/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php +++ b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php @@ -13,7 +13,7 @@ final class SchemaIdentifierExtractor implements SchemaIdentifierExtractorInterface { /** - * @return array> + * @return array> * * @example * $extractor = new SchemaIdentifierExtractor(); diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php index 00f0fb366a..95c9d6a820 100644 --- a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -64,7 +64,12 @@ public function testResolveUrlAliasSchemaFallbackToNameSchema(): void } /** - * @return iterable>, array, array> + * @return iterable>, + * 1: array>, + * 2: array, + * 3: array + * }> */ public static function getDataForTestResolveNameSchema(): iterable { @@ -102,7 +107,7 @@ public static function getDataForTestResolveNameSchema(): iterable /** * @dataProvider getDataForTestResolveNameSchema * - * @param array> $fieldMap A map of Field Definition Identifier and Language Code to Field Value + * @param array> $fieldMap * @param array> $tokenValues * @param array $languageCodes * @param array $expectedNames @@ -140,14 +145,28 @@ public function testResolveNameSchema( /** * Data provider for the testResolve method. * + * @return array>, + * 1: string, + * 2: array>, + * 3: array, + * 4: array>, + * 5?: array{limit?: int, sequence?: string} + * }> + * * @see testResolve */ public static function getDataForTestResolve(): array { return [ [ - ['text1'], + ['field' => ['text1']], '', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], +], [ 'eng-GB' => 'one', 'cro-HR' => 'jedan', @@ -158,8 +177,13 @@ public static function getDataForTestResolve(): array ], ], [ - ['text2'], + ['field' => ['text2']], '', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], [ 'eng-GB' => 'two', 'cro-HR' => 'dva', @@ -170,8 +194,13 @@ public static function getDataForTestResolve(): array ], ], [ - ['text1', 'text2'], + ['field' => ['text2', 'text2']], 'Hello, and and then goodbye and hello again', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], [ 'eng-GB' => 'Hello, one and two and then goodbye...', 'cro-HR' => 'Hello, jedan and dva and then goodb...', @@ -191,14 +220,16 @@ public static function getDataForTestResolve(): array /** * @dataProvider getDataForTestResolve * - * @param string[] $schemaIdentifiers - * @param string[] $languageFieldValues field value translations - * @param string[] $fieldTitles [language => [field_identifier => title]] - * @param array $settings NameSchemaService settings + * @param array> $schemaIdentifiers + * @param array $languageFieldValues field value translations + * @param array> $fieldMap + * @param array> $fieldTitles [language => [field_identifier => title]] + * @param array{limit?: int, sequence?: string} $settings NameSchemaService settings */ public function testResolve( array $schemaIdentifiers, string $nameSchema, + array $fieldMap, array $languageFieldValues, array $fieldTitles, array $settings = [] @@ -209,7 +240,7 @@ public function testResolve( $event = new ResolveNameSchemaEvent( $schemaIdentifiers, $contentType, - $content->fields, + $fieldMap, $content->versionInfo->languageCodes ); @@ -223,7 +254,7 @@ public function testResolve( $result = $nameSchemaService->resolveNameSchema( $nameSchema, $contentType, - $content->fields, + $fieldMap, $content->versionInfo->languageCodes ); @@ -323,9 +354,6 @@ protected function buildTestContentTypeStub( ); } - /** - * @param array> $schemaIdentifiers - */ protected function getEventDispatcherMock( AbstractSchemaEvent $event ): EventDispatcherInterface { From bf5cbc1563f3ddf6cc298e5dc80b40a34939a728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 11 Sep 2023 10:23:44 +0200 Subject: [PATCH 25/37] Updated baseline --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7a99465f42..b0e87ef612 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20640,11 +20640,6 @@ parameters: count: 1 path: src/lib/Repository/NameSchema/NameSchemaService.php - - - message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\SchemaIdentifierExtractor\\:\\:extract\\(\\) should return array\\\\> but returns array\\\\>\\.$#" - count: 1 - path: src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php - - message: "#^Parameter \\#1 \\$module of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException constructor expects string, int\\\\|int\\<1, max\\> given\\.$#" count: 1 From 64ddabe151b7e9864151398c385de0d5f261ee6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 11 Sep 2023 10:45:02 +0200 Subject: [PATCH 26/37] Fixed content update --- src/lib/Repository/NameSchema/NameSchemaService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 15809baf8d..4f1d384a33 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -104,7 +104,7 @@ public function resolveContentNameSchema( ContentType $contentType = null ): array { $contentType ??= $content->getContentType(); - $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; + $schemaName = $contentType->nameSchema; $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); $event = $this->eventDispatcher->dispatch( From 2e4479c17fe18f287f74d10348639c5913ce7a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 11 Sep 2023 14:03:34 +0200 Subject: [PATCH 27/37] Fixed languages issue --- .../Repository/EventSubscriber/NameSchemaSubscriber.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index bb237ad09d..71bfbdbcdc 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -69,13 +69,8 @@ public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event) return; } - $languageList = array_map( - static fn (Language $language): string => $language->getLanguageCode(), - (array)$event->getContent()->getVersionInfo()->getLanguages() - ); - $tokenValues = $this->processEvent( - $languageList, + $event->getLanguageCodes(), $event->getSchemaIdentifiers()['field'], $event->getContentType(), $event->getContent(), From 924bdbf206cb70d256fd702b1c3e3a18d4bfab09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 12:15:20 +0200 Subject: [PATCH 28/37] Update src/lib/Repository/NameSchema/NameSchemaService.php Co-authored-by: Andrew Longosz --- .../Repository/NameSchema/NameSchemaService.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 4f1d384a33..51cd29c0b0 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -286,12 +286,16 @@ public function buildNames(array $tokenValues, string $nameSchema): array $names = []; foreach ($tokenValues as $languageCode => $tokenValue) { - $name = $filteredNameSchema; - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $tokenValue, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - $names[$languageCode] = $this->validateNameLength($name); + $names[$languageCode] = $this->validateNameLength( + str_replace( + $tokens, + array_map( + fn (string $token): string => $this->resolveToken($token, $tokenValue, $groupLookupTable), + $tokens + ), + $filteredNameSchema + ) + ); } return $names; From d67ffb83cbfa0a78301e20d400b8db6d38209cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 12:22:37 +0200 Subject: [PATCH 29/37] Update src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php Co-authored-by: Andrew Longosz --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 71bfbdbcdc..61f2c81a51 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -138,7 +138,7 @@ private function processEvent( $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - if ($content === null && !$fieldMap) { + if ($content === null && empty($fieldMap)) { continue; } From e8ba486a6a83653fb2ec9326cb2b5b7a87402d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 12:22:54 +0200 Subject: [PATCH 30/37] Update src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php Co-authored-by: Andrew Longosz --- .../Event/NameSchema/ResolveContentNameSchemaEvent.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php index 9d3ff475e7..0c50c618f9 100644 --- a/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveContentNameSchemaEvent.php @@ -16,9 +16,7 @@ final class ResolveContentNameSchemaEvent extends AbstractNameSchemaEvent implem private Content $content; /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param array> $schemaIdentifiers - * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param array> $fieldMap * @param array $languageCodes */ From a5c23051bc25447e71e829b2450d2b29fd3c1ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 12:23:38 +0200 Subject: [PATCH 31/37] Update src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php index d7cfe8c1ab..87e8177f8b 100644 --- a/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php +++ b/src/contracts/Event/NameSchema/ResolveUrlAliasSchemaEvent.php @@ -12,7 +12,7 @@ final class ResolveUrlAliasSchemaEvent extends AbstractSchemaEvent implements ContentAwareEventInterface { - protected Content $content; + private Content $content; /** * @param array> $schemaIdentifiers From c0da3c752734bdcd8269d3f0025f91c136edd541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 12:25:27 +0200 Subject: [PATCH 32/37] Changed exception origin --- .../Repository/NameSchema/UnresolvedTokenNamesException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php index b5db688671..090664857e 100644 --- a/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php +++ b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php @@ -8,8 +8,8 @@ namespace Ibexa\Core\Repository\NameSchema; -use LogicException; +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; -final class UnresolvedTokenNamesException extends LogicException +final class UnresolvedTokenNamesException extends InvalidArgumentException { } From f80e64c0a4a5d5143d215fb12edcdf9e8074768d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Wed, 13 Sep 2023 14:10:07 +0200 Subject: [PATCH 33/37] Implemented code review suggestions --- .../EventSubscriber/NameSchemaSubscriber.php | 67 +++++++++++++------ .../NameSchema/NameSchemaService.php | 2 +- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 61f2c81a51..12c7fc8ef9 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -113,8 +113,6 @@ public function isValid(AbstractSchemaEvent $event): bool /** * @param array $languages * @param array $identifiers - * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType - * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content * @param array> $tokenValues * @param array> $fieldMap * @@ -129,29 +127,58 @@ private function processEvent( array $fieldMap = [] ): array { foreach ($languages as $languageCode) { - $tokenValues[$languageCode] ?? $tokenValues[$languageCode] = []; - foreach ($identifiers as $identifier) { - $fieldDefinition = $contentType->getFieldDefinition($identifier); - if (null === $fieldDefinition) { - continue; - } - - $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); + $tokenValues[$languageCode] = $content !== null || !empty($fieldMap) + ? $this->getValues( + $identifiers, + $contentType, + $content, + $fieldMap, + $languageCode + ) + : []; + } - if ($content === null && empty($fieldMap)) { - continue; - } + return $tokenValues; + } - $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? '' : $content->getFieldValue($identifier, $languageCode); + /** + * @param array $identifiers + * @param array> $fieldMap + * + * @return array + */ + private function getValues( + array $identifiers, + ContentType $contentType, + ?Content $content, + array $fieldMap, + string $languageCode + ): array { + $tokenValues = []; + foreach ($identifiers as $identifier) { + $fieldDefinition = $contentType->getFieldDefinition($identifier); + if (null === $fieldDefinition) { + continue; + } - $field = $field ? $persistenceFieldType->getName( - $field, - $fieldDefinition, - $languageCode - ) : ''; + $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - $tokenValues[$languageCode][$identifier] = $field; + if ($content === null && empty($fieldMap)) { + continue; } + + $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? '' : $content->getFieldValue( + $identifier, + $languageCode + ); + + $field = $field ? $persistenceFieldType->getName( + $field, + $fieldDefinition, + $languageCode + ) : ''; + + $tokenValues[$identifier] = $field; } return $tokenValues; diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php index 51cd29c0b0..782a154191 100644 --- a/src/lib/Repository/NameSchema/NameSchemaService.php +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -278,7 +278,7 @@ protected function filterNameSchema(string $nameSchema): array public function buildNames(array $tokenValues, string $nameSchema): array { if (empty($tokenValues)) { - throw new UnresolvedTokenNamesException(); + throw new UnresolvedTokenNamesException('$tokenValues', 'is Empty'); } [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); From 2ac94ed95f1e7cdc8b83bc60a94311d469dc3eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 14 Sep 2023 09:20:50 +0200 Subject: [PATCH 34/37] Update src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php Co-authored-by: Andrew Longosz --- .../EventSubscriber/NameSchemaSubscriber.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 12c7fc8ef9..55e0293e18 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -167,18 +167,17 @@ private function getValues( continue; } - $field = $fieldMap ? $fieldMap[$identifier][$languageCode] ?? '' : $content->getFieldValue( - $identifier, - $languageCode - ); + if (!empty($fieldMap)) { + $fieldValue = $fieldMap[$identifier][$languageCode] ?? null; + } else { + $fieldValue = $content !== null ? $content->getFieldValue($identifier, $languageCode) : null; + } - $field = $field ? $persistenceFieldType->getName( - $field, + $tokenValues[$identifier] = $fieldValue !== null ? $persistenceFieldType->getName( + $fieldValue, $fieldDefinition, $languageCode ) : ''; - - $tokenValues[$identifier] = $field; } return $tokenValues; From 0359960ed378dad617c7b15b26d86e19a00399b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Thu, 14 Sep 2023 09:26:26 +0200 Subject: [PATCH 35/37] Removed check if fieldmap is empty --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 55e0293e18..213a2cd73f 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -163,10 +163,6 @@ private function getValues( $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); - if ($content === null && empty($fieldMap)) { - continue; - } - if (!empty($fieldMap)) { $fieldValue = $fieldMap[$identifier][$languageCode] ?? null; } else { From b8970639c8d0f1f01d59808758ff794e1ff3f431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 18 Sep 2023 09:49:48 +0200 Subject: [PATCH 36/37] Fixed an issue with overwriting values --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 213a2cd73f..40d39cd9ad 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -127,7 +127,7 @@ private function processEvent( array $fieldMap = [] ): array { foreach ($languages as $languageCode) { - $tokenValues[$languageCode] = $content !== null || !empty($fieldMap) + $values = $content !== null || !empty($fieldMap) ? $this->getValues( $identifiers, $contentType, @@ -136,6 +136,7 @@ private function processEvent( $languageCode ) : []; + $tokenValues[$languageCode] = array_merge($tokenValues[$languageCode], $values); } return $tokenValues; From 426d20e56706fcaddbad6af7f10a8191f3e4610e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20S=C5=82omka?= Date: Mon, 18 Sep 2023 11:15:00 +0200 Subject: [PATCH 37/37] Fixed tests usues --- src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php index 40d39cd9ad..63adea9b31 100644 --- a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -136,7 +136,7 @@ private function processEvent( $languageCode ) : []; - $tokenValues[$languageCode] = array_merge($tokenValues[$languageCode], $values); + $tokenValues[$languageCode] = array_merge($tokenValues[$languageCode] ?? [], $values); } return $tokenValues;