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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions src/lib/Persistence/Legacy/Content/FieldHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition;
use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct;
use Ibexa\Contracts\Core\Persistence\Content\VersionInfo;
use Ibexa\Core\FieldType\FieldTypeAliasResolverInterface;
use Ibexa\Core\Persistence\FieldTypeRegistry;

/**
Expand Down Expand Up @@ -59,6 +60,8 @@ class FieldHandler
*/
protected $fieldTypes;

private FieldTypeAliasResolverInterface $fieldTypeAliasResolver;

/**
* Creates a new Field Handler.
*
Expand All @@ -67,19 +70,22 @@ class FieldHandler
* @param \Ibexa\Core\Persistence\Legacy\Content\StorageHandler $storageHandler
* @param \Ibexa\Contracts\Core\Persistence\Content\Language\Handler $languageHandler
* @param \Ibexa\Core\Persistence\FieldTypeRegistry $fieldTypeRegistry
* @param \Ibexa\Core\FieldType\FieldTypeAliasResolverInterface $fieldTypeAliasResolver
*/
Comment on lines 65 to 74

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire PHPDoc block can be actually dropped while at it, it doesn't provide any extra value due to strict typing.

public function __construct(
Gateway $contentGateway,
Mapper $mapper,
StorageHandler $storageHandler,
LanguageHandler $languageHandler,
FieldTypeRegistry $fieldTypeRegistry
FieldTypeRegistry $fieldTypeRegistry,
FieldTypeAliasResolverInterface $fieldTypeAliasResolver
) {
$this->contentGateway = $contentGateway;
$this->mapper = $mapper;
$this->storageHandler = $storageHandler;
$this->languageHandler = $languageHandler;
$this->fieldTypeRegistry = $fieldTypeRegistry;
$this->fieldTypeAliasResolver = $fieldTypeAliasResolver;
}

/**
Expand Down Expand Up @@ -454,7 +460,11 @@ protected function getFieldMap(array $fields, &$languageCodes = null)
*/
public function deleteFields($contentId, VersionInfo $versionInfo)
{
foreach ($this->contentGateway->getFieldIdsByType($contentId, $versionInfo->versionNo) as $fieldType => $ids) {
$fieldTypeIdsMap = $this->resolveFieldTypeIdentifiers(
$this->contentGateway->getFieldIdsByType($contentId, $versionInfo->versionNo)
);

foreach ($fieldTypeIdsMap as $fieldType => $ids) {
$this->storageHandler->deleteFieldData($fieldType, $versionInfo, $ids);
}
$this->contentGateway->deleteFields($contentId, $versionInfo->versionNo);
Expand All @@ -471,10 +481,12 @@ public function deleteTranslationFromContentFields($contentId, array $versions,
{
foreach ($versions as $versionInfo) {
// FT-specific implementations require VersionInfo to delete data
$fieldTypeIdsMap = $this->contentGateway->getFieldIdsByType(
$versionInfo->contentInfo->id,
$versionInfo->versionNo,
$languageCode
$fieldTypeIdsMap = $this->resolveFieldTypeIdentifiers(
$this->contentGateway->getFieldIdsByType(
$versionInfo->contentInfo->id,
$versionInfo->versionNo,
$languageCode
)
);

foreach ($fieldTypeIdsMap as $fieldType => $ids) {
Expand All @@ -493,10 +505,12 @@ public function deleteTranslationFromContentFields($contentId, array $versions,
*/
public function deleteTranslationFromVersionFields(VersionInfo $versionInfo, $languageCode)
{
$fieldTypeIdsMap = $this->contentGateway->getFieldIdsByType(
$versionInfo->contentInfo->id,
$versionInfo->versionNo,
$languageCode
$fieldTypeIdsMap = $this->resolveFieldTypeIdentifiers(
$this->contentGateway->getFieldIdsByType(
$versionInfo->contentInfo->id,
$versionInfo->versionNo,
$languageCode
)
);
foreach ($fieldTypeIdsMap as $fieldType => $ids) {
$this->storageHandler->deleteFieldData($fieldType, $versionInfo, $ids);
Expand All @@ -507,4 +521,26 @@ public function deleteTranslationFromVersionFields(VersionInfo $versionInfo, $la
$versionInfo->versionNo
);
}

/**
* @param array<string, int[]> $fieldTypeIdsMap
*
* @return array<string, int[]>
*/
private function resolveFieldTypeIdentifiers(array $fieldTypeIdsMap): array
{
$resolvedFieldTypeIdsMap = [];
foreach ($fieldTypeIdsMap as $fieldTypeIdentifier => $ids) {
$resolvedFieldTypeIdentifier = $this->fieldTypeAliasResolver->resolveIdentifier(
$fieldTypeIdentifier
);

$resolvedFieldTypeIdsMap[$resolvedFieldTypeIdentifier] = array_merge(
$resolvedFieldTypeIdsMap[$resolvedFieldTypeIdentifier] ?? [],
$ids
);
}

return $resolvedFieldTypeIdsMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ services:
- '@Ibexa\Core\Persistence\Legacy\Content\StorageHandler'
- '@ibexa.spi.persistence.legacy.language.handler'
- '@Ibexa\Core\Persistence\FieldTypeRegistry'
- '@Ibexa\Core\FieldType\FieldTypeAliasResolverInterface'
lazy: true

Ibexa\Core\Persistence\Legacy\Content\TreeHandler:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

namespace Ibexa\Tests\Integration\Core\Repository\FieldType;

use Doctrine\DBAL\ParameterType;
use Ibexa\Contracts\Core\Repository;
use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\Persistence\Legacy\Content\Gateway;
use Ibexa\Tests\Integration\Core\Repository\BaseTestCase;

/**
Expand Down Expand Up @@ -953,6 +955,29 @@ public function testUpdateContentFails($failingValue, $expectedException)
$this->updateContent($failingValue);
}

protected function downgradeFieldTypeIdentifierToLegacyAlias(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected function downgradeFieldTypeIdentifierToLegacyAlias(
/**
* @throws \ErrorException
* @throws \Doctrine\DBAL\Exception
*/
protected function downgradeFieldTypeIdentifierToLegacyAlias(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: either add to PHPDoc block that this touches ibexa_content_field not ibexa_class_attribute or somehow (not sure) make the method name self-commenting.

string $legacyAlias,
int $contentId,
int $versionNo,
int $fieldDefinitionId
): void {
$connection = $this->getRawDatabaseConnection();

$query = $connection->createQueryBuilder();
$query
->update(Gateway::CONTENT_FIELD_TABLE)
->set('data_type_string', ':data_type_string')
->setParameter('data_type_string', $legacyAlias, ParameterType::STRING)
->andWhere('content_type_field_definition_id = :content_type_field_definition_id')
->andWhere('version = :version')
->andWhere('contentobject_id = :contentobject_id')
->setParameter('content_type_field_definition_id', $fieldDefinitionId, ParameterType::INTEGER)
->setParameter('version', $versionNo, ParameterType::INTEGER)
->setParameter('contentobject_id', $contentId, ParameterType::INTEGER);

$query->executeStatement();
}

protected function removeFieldDefinition()
{
$repository = $this->getRepository();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,44 @@ public function testDeleteImageWithCorruptedName(): void
// Expect no League\Flysystem\CorruptedPathDetected thrown
}

public function testDeleteContentRemovesImageStoredUnderLegacyFieldTypeIdentifier(): void
{
$ioService = $this->getSetupFactory()->getServiceContainer()->get(LegacyIOService::class);
$repository = $this->getRepository();
$contentService = $repository->getContentService();

self::assertInstanceOf(IOServiceInterface::class, $ioService);

$content = $this->publishNewImage(
__METHOD__,
new ImageValue(
[
'inputUri' => __DIR__ . '/_fixtures/image.jpg',
'fileName' => 'image.jpg',
'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'),
'alternativeText' => 'Alternative',
]
),
[2]
);

$imageFieldDefinition = $content->getContentType()->getFieldDefinition('image');
self::assertNotNull($imageFieldDefinition);

$this->assertImageExists(true, $ioService, $content);
Comment on lines +847 to +869

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code analysis fails due to the tests already being marked for code duplication before my additions, to fix that I would need to completely refactor these 3 test classes

This is not true for this portion. It's a duplicate of testDeleteImageWithCorruptedName.


$this->downgradeFieldTypeIdentifierToLegacyAlias(
'ezimage',
$content->id,
$content->getVersionInfo()->versionNo,
$imageFieldDefinition->id
);

$contentService->deleteContent($content->getVersionInfo()->getContentInfo());

$this->assertImageExists(false, $ioService, $content);
}

/**
* @return array<string,mixed>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
namespace Ibexa\Tests\Integration\Core\Repository\FieldType;

use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\ParameterType;
use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException;
use Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType;
use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition;
use Ibexa\Core\FieldType\User\Type;
use Ibexa\Core\FieldType\User\Value as UserValue;
use Ibexa\Core\Persistence\Legacy\User\Gateway as UserGateway;
use Ibexa\Core\Repository\Values\User\User;
use Ibexa\Tests\Core\FieldType\DataProvider\UserValidatorConfigurationSchemaProvider;

Expand Down Expand Up @@ -511,6 +513,47 @@ public function testUpdateFieldDefinitionWithIncompleteSettingsSchema()
self::assertNull($userFieldDefinition->fieldSettings[Type::PASSWORD_TTL_WARNING_SETTING]);
}

public function testDeleteContentRemovesUserAccountStoredUnderLegacyFieldTypeIdentifier(): void
{
$repository = $this->getRepository();
$contentService = $repository->getContentService();
$contentTypeService = $repository->getContentTypeService();

$user = $this->createUserVersion1('legacy-identifier-user');
$contentInfo = $user->getContentInfo();

self::assertTrue($this->userAccountExists($contentInfo->getId()));

$userFieldDefinition = $this->getUserFieldDefinition(
$contentTypeService->loadContentType($contentInfo->contentTypeId)
);

$this->downgradeFieldTypeIdentifierToLegacyAlias(
'ezuser',
$contentInfo->getId(),
$contentInfo->currentVersionNo,
$userFieldDefinition->id
);

$contentService->deleteContent($contentInfo);

self::assertFalse($this->userAccountExists($contentInfo->getId()));
}

private function userAccountExists(int $contentId): bool
{
$connection = $this->getRawDatabaseConnection();

$query = $connection->createQueryBuilder();
$query
->select('COUNT(contentobject_id)')
->from(UserGateway::USER_TABLE)
->where('contentobject_id = :contentobject_id')
->setParameter('contentobject_id', $contentId, ParameterType::INTEGER);

return (int)$query->executeQuery()->fetchOne() > 0;
}

/**
* Finds ibexa_user field definition in given $contentType or mark test as failed if it doens't exists.
*
Expand Down
Loading
Loading