Skip to content

Commit

Permalink
IBX-4031: Ensured non-translatable field values are copied when publi…
Browse files Browse the repository at this point in the history
…shing a draft

For more details see https://issues.ibexa.co/browse/IBX-4031 and #380

Key changes:

* Forced copying non-translatable field values from published version to current version draft when publishing it

* [Tests] Isolated integration test
  • Loading branch information
barw4 authored Aug 10, 2023
1 parent 576d699 commit 20bebf9
Show file tree
Hide file tree
Showing 2 changed files with 255 additions and 0 deletions.
82 changes: 82 additions & 0 deletions eZ/Publish/Core/Repository/ContentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,7 @@ public function publishVersion(APIVersionInfo $versionInfo, array $translations
$this->repository->beginTransaction();
try {
$this->copyTranslationsFromPublishedVersion($content->versionInfo, $translations);
$this->copyNonTranslatableFieldsFromPublishedVersion($content);
$content = $this->internalPublishVersion($content->getVersionInfo(), null, $translations);
$this->repository->commit();
} catch (Exception $e) {
Expand All @@ -1505,6 +1506,87 @@ public function publishVersion(APIVersionInfo $versionInfo, array $translations
return $content;
}

/**
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
*/
protected function copyNonTranslatableFieldsFromPublishedVersion(APIContent $currentVersionContent): void
{
$versionInfo = $currentVersionContent->getVersionInfo();
$contentType = $currentVersionContent->getContentType();

$publishedContent = $this->internalLoadContentById($versionInfo->getContentInfo()->getId());
$publishedVersionInfo = $publishedContent->getVersionInfo();

if (
!$publishedVersionInfo->isPublished()
|| ($versionInfo->versionNo >= $publishedVersionInfo->versionNo)
) {
return;
}

$publishedContentFieldsInMainLanguage = $publishedContent->getFieldsByLanguage(
$publishedContent->getVersionInfo()->getContentInfo()->getMainLanguageCode()
);

$fieldValues = [];
$persistenceFields = [];
foreach ($currentVersionContent->getFields() as $field) {
$fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier);
$fieldValues[$fieldDefinition->identifier][$field->languageCode] = $field->getValue();

if (
$fieldDefinition->isTranslatable
|| $field->languageCode === $versionInfo->initialLanguageCode
) {
continue;
}

$fieldType = $this->fieldTypeRegistry->getFieldType(
$fieldDefinition->fieldTypeIdentifier
);

$newValue = $publishedContentFieldsInMainLanguage[$field->fieldDefIdentifier]->getValue();
$fieldValues[$fieldDefinition->identifier][$field->languageCode] = $newValue;

$persistenceFields[] = new SPIField(
[
'id' => $field->id,
'fieldDefinitionId' => $fieldDefinition->id,
'type' => $fieldDefinition->fieldTypeIdentifier,
'value' => $fieldType->toPersistenceValue($newValue),
'languageCode' => $field->languageCode,
'versionNo' => $versionInfo->versionNo,
]
);
}

if (count($persistenceFields) === 0) {
return;
}

$updateStruct = new SPIContentUpdateStruct();
$updateStruct->name = $this->nameSchemaService->resolveNameSchema(
$currentVersionContent,
$fieldValues,
$versionInfo->languageCodes,
$contentType
);
$updateStruct->initialLanguageId = $this->persistenceHandler
->contentLanguageHandler()
->loadByLanguageCode(
$versionInfo->initialLanguageCode
)->id;
$updateStruct->creatorId = $versionInfo->creatorId;
$updateStruct->modificationDate = time();
$updateStruct->fields = $persistenceFields;

$this->persistenceHandler->contentHandler()->updateContent(
$versionInfo->getContentInfo()->getId(),
$versionInfo->versionNo,
$updateStruct
);
}

/**
* @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
* @param array $translations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

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

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

use DateTime;
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct;
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
use Ibexa\Tests\Integration\Core\RepositoryTestCase;

/**
* @covers \eZ\Publish\API\Repository\ContentService
*/
final class CopyNonTranslatableFieldsFromPublishedVersionTest extends RepositoryTestCase
{
private const GER_DE = 'ger-DE';
private const ENG_US = 'eng-US';
private const CONTENT_TYPE_IDENTIFIER = 'nontranslatable';
private const TEXT_LINE_FIELD_TYPE_IDENTIFIER = 'ezstring';

/**
* @throws \eZ\Publish\API\Repository\Exceptions\Exception
*/
public function testCopyNonTranslatableFieldsFromPublishedVersionToDraft(): void
{
$this->createNonTranslatableContentType();

$contentService = self::getContentService();
$contentTypeService = self::getContentTypeService();
$locationService = self::getLocationService();

// Creating start content in eng-US language
$contentType = $contentTypeService->loadContentTypeByIdentifier(self::CONTENT_TYPE_IDENTIFIER);
$mainLanguageCode = self::ENG_US;
$contentCreateStruct = $contentService->newContentCreateStruct($contentType, $mainLanguageCode);
$contentCreateStruct->setField('title', 'Test title');

$contentDraft = $contentService->createContent(
$contentCreateStruct,
[
$locationService->newLocationCreateStruct(2),
]
);
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo());

// Creating a draft in ger-DE language with the only field updated being 'title'
$gerDraft = $contentService->createContentDraft($publishedContent->contentInfo);

$contentUpdateStruct = new ContentUpdateStruct([
'initialLanguageCode' => self::GER_DE,
'fields' => $contentDraft->getFields(),
]);

$contentUpdateStruct->setField('title', 'Folder GER', self::GER_DE);
$gerContent = $contentService->updateContent($gerDraft->getVersionInfo(), $contentUpdateStruct);

// Updating non-translatable field in eng-US language (allowed) and publishing it
$engContent = $contentService->createContentDraft($publishedContent->contentInfo);

$contentUpdateStruct = new ContentUpdateStruct([
'initialLanguageCode' => self::ENG_US,
'fields' => $contentDraft->getFields(),
]);

$expectedBodyValue = 'Non-translatable value';
$contentUpdateStruct->setField('title', 'Title v2', self::ENG_US);
$contentUpdateStruct->setField('body', $expectedBodyValue, self::ENG_US);

$engContent = $contentService->updateContent($engContent->getVersionInfo(), $contentUpdateStruct);
$contentService->publishVersion($engContent->getVersionInfo());

// Publishing ger-DE draft with the empty non-translatable field
$contentService->publishVersion($gerContent->getVersionInfo());

// Loading main content
$mainPublishedContent = $contentService->loadContent($engContent->id);
$bodyFieldValue = $mainPublishedContent->getField('body')->getValue();

self::assertSame($expectedBodyValue, $bodyFieldValue->text);
}

private function createNonTranslatableContentType(): void
{
$permissionResolver = self::getPermissionResolver();
$contentTypeService = self::getContentTypeService();

$typeCreate = $contentTypeService->newContentTypeCreateStruct(self::CONTENT_TYPE_IDENTIFIER);

$typeCreate->mainLanguageCode = 'eng-GB';
$typeCreate->remoteId = '1234567890abcdef';
$typeCreate->urlAliasSchema = '<title>';
$typeCreate->nameSchema = '<title>';
$typeCreate->names = [
'eng-GB' => 'Non-translatable content type',
];
$typeCreate->descriptions = [
'eng-GB' => '',
];
$typeCreate->creatorId = $permissionResolver->getCurrentUserReference()->getUserId();
$typeCreate->creationDate = new DateTime();

$fieldDefinitionPosition = 1;
$typeCreate->addFieldDefinition(
$this->buildFieldDefinitionCreateStructForNonTranslatableContentType(
$fieldDefinitionPosition,
'title',
['eng-GB' => 'Title'],
true,
true,
'default title'
)
);

$typeCreate->addFieldDefinition(
$this->buildFieldDefinitionCreateStructForNonTranslatableContentType(
++$fieldDefinitionPosition,
'body',
['eng-GB' => 'Body'],
false,
false
)
);

$contentTypeDraft = $contentTypeService->createContentType(
$typeCreate,
[$contentTypeService->loadContentTypeGroupByIdentifier('Media')],
);
$contentTypeService->publishContentTypeDraft($contentTypeDraft);
}

/**
* @param array<string, string> $names
*/
private function buildFieldDefinitionCreateStructForNonTranslatableContentType(
int $position,
string $fieldIdentifier,
array $names,
bool $isTranslatable,
bool $isRequired,
?string $defaultValue = null
): FieldDefinitionCreateStruct {
$contentTypeService = self::getContentTypeService();

$fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct(
$fieldIdentifier,
self::TEXT_LINE_FIELD_TYPE_IDENTIFIER
);

$fieldDefinitionCreateStruct->names = $names;
$fieldDefinitionCreateStruct->descriptions = $names;
$fieldDefinitionCreateStruct->fieldGroup = 'content';
$fieldDefinitionCreateStruct->position = $position;
$fieldDefinitionCreateStruct->isTranslatable = $isTranslatable;
$fieldDefinitionCreateStruct->isRequired = $isRequired;
$fieldDefinitionCreateStruct->isInfoCollector = false;
$fieldDefinitionCreateStruct->validatorConfiguration = [
'StringLengthValidator' => [
'minStringLength' => 0,
'maxStringLength' => 0,
],
];
$fieldDefinitionCreateStruct->fieldSettings = [];
$fieldDefinitionCreateStruct->isSearchable = true;
$fieldDefinitionCreateStruct->defaultValue = $defaultValue;

return $fieldDefinitionCreateStruct;
}
}

0 comments on commit 20bebf9

Please sign in to comment.