diff --git a/core/modules/content_translation/src/FieldTranslationSynchronizer.php b/core/modules/content_translation/src/FieldTranslationSynchronizer.php index e900c5086c..3f3595ecbd 100644 --- a/core/modules/content_translation/src/FieldTranslationSynchronizer.php +++ b/core/modules/content_translation/src/FieldTranslationSynchronizer.php @@ -92,18 +92,38 @@ public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode return; } - if ($entity->isDefaultTranslationAffectedOnly()) { - // If changes to untranslatable fields are configured to affect only the - // default translation, we need to skip synchronization in pending - // revisions, otherwise multiple translations would be affected. - if (!$entity->isDefaultRevision()) { - return; + if ($entity->isNewRevision()) { + if ($entity->isDefaultTranslationAffectedOnly()) { + // If changes to untranslatable fields are configured to affect only the + // default translation, we need to skip synchronization in pending + // revisions, otherwise multiple translations would be affected. + if (!$entity->isDefaultRevision()) { + return; + } + // When this mode is enabled, changes to synchronized properties are + // allowed only in the default translation, thus we need to make sure this + // is always used as source for the synchronization process. + else { + $sync_langcode = $entity->getUntranslated()->language()->getId(); + } } - // When this mode is enabled, changes to synchronized properties are - // allowed only in the default translation, thus we need to make sure this - // is always used as source for the synchronization process. - else { - $sync_langcode = $entity->getUntranslated()->language()->getId(); + elseif ($entity->isDefaultRevision()) { + // If a new default revision is being saved, but a newer default + // revision was created meanwhile, use any other translation as source + // for synchronization, since that will have been merged from the + // default revision. In this case the actual language does not matter as + // synchronized properties are the same for all the translations in the + // default revision. + /** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */ + $default_revision = $this->entityManager + ->getStorage($entity->getEntityTypeId()) + ->load($entity->id()); + if ($default_revision->getLoadedRevisionId() !== $entity->getLoadedRevisionId()) { + $other_langcodes = array_diff_key($default_revision->getTranslationLanguages(), [$sync_langcode => FALSE]); + if ($other_langcodes) { + $sync_langcode = key($other_langcodes); + } + } } } diff --git a/core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncValidationTest.php b/core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncRevisionTest.php similarity index 86% rename from core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncValidationTest.php rename to core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncRevisionTest.php index 4c75e62785..c1d1e272fb 100644 --- a/core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncValidationTest.php +++ b/core/modules/content_translation/tests/src/Kernel/ContentTranslationFieldSyncRevisionTest.php @@ -15,13 +15,11 @@ use Drupal\user\Entity\User; /** - * Tests the field synchronization validation logic. + * Tests the field synchronization logic when revisions are involved. * * @group content_translation - * - * @coversDefaultClass \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator */ -class ContentTranslationFieldSyncValidationTest extends EntityKernelTestBase { +class ContentTranslationFieldSyncRevisionTest extends EntityKernelTestBase { use TestFileCreationTrait; @@ -114,14 +112,16 @@ protected function setUp() { } /** - * Checks that pending revision validation works as expected. + * Checks that field synchronization works as expected with revisions. * - * @covers ::__construct - * @covers ::create - * @covers ::validate - * @covers ::hasSynchronizedPropertyChanges + * @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::create + * @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::validate + * @covers \Drupal\content_translation\Plugin\Validation\Constraint\ContentTranslationSynchronizedFieldsConstraintValidator::hasSynchronizedPropertyChanges + * @covers \Drupal\content_translation\FieldTranslationSynchronizer::getFieldSynchronizedProperties + * @covers \Drupal\content_translation\FieldTranslationSynchronizer::synchronizeFields + * @covers \Drupal\content_translation\FieldTranslationSynchronizer::synchronizeItems */ - public function testPendingRevisionValidation() { + public function testFieldSynchronizationAndValidation() { // Test that when untranslatable field widgets are displayed, synchronized // field properties can be changed only in default revisions. $this->setUntranslatableFieldWidgetsDisplay(TRUE); @@ -332,6 +332,41 @@ public function testPendingRevisionValidation() { $en_revision->getTranslation('it')->get($this->fieldName)->alt = 'Alt 3 IT'; $violations = $en_revision->validate(); $this->assertViolations($violations); + + // Test that when saving a new default revision starting from a pending + // revision, outdated synchronized properties do not override more recent + // ones. + $this->setUntranslatableFieldWidgetsDisplay(TRUE); + $entity = $this->saveNewEntity(); + $entity_id = $entity->id(); + $this->assertLatestRevisionFieldValues($entity_id, [23, 1, 1, 'Alt 1 EN']); + + $it_translation = $entity->addTranslation('it', $entity->toArray()); + /** @var \Drupal\Core\Entity\ContentEntityInterface $it_revision */ + $it_revision = $this->createRevision($it_translation, FALSE); + $metadata = $this->contentTranslationManager->getTranslationMetadata($it_revision); + $metadata->setSource('en'); + $it_revision->get($this->fieldName)->alt = 'Alt 2 IT'; + $violations = $it_revision->validate(); + $this->assertEmpty($violations); + $this->storage->save($it_revision); + $this->assertLatestRevisionFieldValues($entity_id, [24, 1, 1, 'Alt 1 EN', 'Alt 2 IT']); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $en_revision */ + $en_revision = $this->createRevision($entity); + $en_revision->get($this->fieldName)->target_id = 3; + $en_revision->get($this->fieldName)->alt = 'Alt 3 EN'; + $violations = $en_revision->validate(); + $this->assertEmpty($violations); + $this->storage->save($en_revision); + $this->assertLatestRevisionFieldValues($entity_id, [25, 3, 3, 'Alt 3 EN', 'Alt 2 IT']); + + $it_revision = $this->createRevision($it_revision); + $it_revision->get($this->fieldName)->alt = 'Alt 4 IT'; + $violations = $it_revision->validate(); + $this->assertEmpty($violations); + $this->storage->save($it_revision); + $this->assertLatestRevisionFieldValues($entity_id, [26, 3, 3, 'Alt 3 EN', 'Alt 4 IT']); } /**