diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 1517b167444324dd3e8b8ae886886caf93bd0903..5cd27c7c61def98eb8c89c914a180598508d4fa6 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -1266,6 +1266,31 @@ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, return array(); } + /** + * Returns an array of field names to skip in ::hasTranslationChanges. + * + * @return array + * An array of field names. + */ + protected function getFieldsToSkipFromTranslationChangesCheck() { + // A list of known revision metadata fields which should be skipped from + // the comparision. + // @todo Replace the hard coded list of revision metadata fields with the + // solution from https://www.drupal.org/node/2615016. + $fields = [ + $this->getEntityType()->getKey('revision'), + 'revision_translation_affected', + 'revision_uid', + 'revision_user', + 'revision_timestamp', + 'revision_created', + 'revision_log', + 'revision_log_message', + ]; + + return $fields; + } + /** * {@inheritdoc} */ @@ -1299,13 +1324,17 @@ public function hasTranslationChanges() { // possible or be meaningless. /** @var \Drupal\Core\Entity\ContentEntityBase $translation */ $translation = $original->getTranslation($this->activeLangcode); + + // The list of fields to skip from the comparision. + $skip_fields = $this->getFieldsToSkipFromTranslationChangesCheck(); + foreach ($this->getFieldDefinitions() as $field_name => $definition) { // @todo Avoid special-casing the following fields. See // https://www.drupal.org/node/2329253. - if ($field_name == 'revision_translation_affected' || $field_name == 'revision_id') { + if (in_array($field_name, $skip_fields, TRUE)) { continue; } - if (!$definition->isComputed() && (!$translated || $definition->isTranslatable())) { + if (!$definition->isComputed()) { $items = $this->get($field_name)->filterEmptyItems(); $original_items = $translation->get($field_name)->filterEmptyItems(); if (!$items->equals($original_items)) { diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php index 8893a8ba851a7336e596d306c1b3d3b9dd9b5ec1..d045152f6a1055299e4f59143fc43ecda7c5ad41 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php @@ -25,9 +25,6 @@ interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, /** * Determines if the current translation of the entity has unsaved changes. * - * If the entity is translatable only translatable fields will be checked for - * changes. - * * @return bool * TRUE if the current translation of the entity has changes. */ diff --git a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php index f298af42e133ef28ffe3b4b4fe47e8ac547a1334..673a83685ac8de3463fea578dee26965ec31fca7 100644 --- a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php +++ b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php @@ -129,14 +129,29 @@ public function testNodeRevisionsTabWithDefaultRevision() { // Create the node. $node = $this->drupalCreateNode(); + $storage = \Drupal::entityTypeManager()->getStorage($node->getEntityTypeId()); + // Create a new revision based on the default revision. + // Revision 2. + $node = $storage->load($node->id()); $node->setNewRevision(TRUE); $node->save(); + + // Revision 3. + $node = $storage->load($node->id()); $node->setNewRevision(TRUE); $node->save(); + + // Revision 4. + // Trigger translation changes in order to show the revision. + $node = $storage->load($node->id()); + $node->setTitle($this->randomString()); $node->isDefaultRevision(FALSE); $node->setNewRevision(TRUE); $node->save(); + + // Revision 5. + $node = $storage->load($node->id()); $node->isDefaultRevision(FALSE); $node->setNewRevision(TRUE); $node->save(); @@ -147,17 +162,15 @@ public function testNodeRevisionsTabWithDefaultRevision() { // Verify that the default revision can be an older revision than the latest // one. - $this->assertLinkByHref('/node/' . $node_id . '/revisions/5/revert'); + // Assert that the revisions with translations changes are shown: 1 and 4. + $this->assertLinkByHref('/node/' . $node_id . '/revisions/1/revert'); $this->assertLinkByHref('/node/' . $node_id . '/revisions/4/revert'); + + // Assert that the revisions without translations changes are filtered out: + // 2, 3 and 5. + $this->assertNoLinkByHref('/node/' . $node_id . '/revisions/2/revert'); $this->assertNoLinkByHref('/node/' . $node_id . '/revisions/3/revert'); - $current_revision_row = $this->xpath("//table[contains(@class, :table_class)]//tbody//tr[3 and contains(@class, :class) and contains(., :text)]", [ - ':table_class' => 'node-revision-table', - ':class' => 'revision-current', - ':text' => 'Current revision', - ]); - $this->assertEqual(count($current_revision_row), 1, 'The default revision can be a revision other than the latest one.'); - $this->assertLinkByHref('/node/' . $node_id . '/revisions/2/revert'); - $this->assertLinkByHref('/node/' . $node_id . '/revisions/1/revert'); + $this->assertNoLinkByHref('/node/' . $node_id . '/revisions/5/revert'); } } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php index 7e38b33883f73a861c9353362f9dfd6ce7775185..63991311a90a6e86accac3a54900032c019c9049 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php @@ -154,7 +154,7 @@ protected function getExpectedNormalizedEntity() { ], 'changed' => [ [ - 'value' => '123456789', + 'value' => (string) $this->entity->getChangedTime(), ], ], 'default_langcode' => [ diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index b2fe6dc42ca6ec4c3271edf16262387916a1b5ce..339af6ba162451dd888a9324540c67f46a809fc0 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -186,13 +186,6 @@ public function setUp() { // Set a default value on the field. $this->entity->set('field_rest_test', ['value' => 'All the faith he had had had had no effect on the outcome of his life.']); - // @todo Remove in this if-test in https://www.drupal.org/node/2808335. - if ($this->entity instanceof EntityChangedInterface) { - $changed = $this->entity->getChangedTime(); - $this->entity->setChangedTime(42); - $this->entity->save(); - $this->entity->setChangedTime($changed); - } $this->entity->save(); } } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php index aa1f608c27de74a24fcfedd5d41e2d1fcd8f85b8..20a90955833eb1b40168c0e44daebec8022c17a3 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Node/NodeResourceTestBase.php @@ -126,7 +126,7 @@ protected function getExpectedNormalizedEntity() { ], 'changed' => [ [ - 'value' => '123456789', + 'value' => (string) $this->entity->getChangedTime(), ], ], 'promote' => [ diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php index b6bde1d2987c38c854683c0a5b64d8e38748d116..94a0a6e265ca4306e005e592b43625518fa45333 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php @@ -108,7 +108,7 @@ protected function getExpectedNormalizedEntity() { ], 'changed' => [ [ - 'value' => '123456789', + 'value' => (string) $this->entity->getChangedTime(), ], ], 'default_langcode' => [ diff --git a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php index fe9b7508152d71728d2aa279d0569285c1af50b1..1e7673478db5400b43228204dfa9133578dbdf21 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/User/UserResourceTestBase.php @@ -104,7 +104,7 @@ protected function getExpectedNormalizedEntity() { ], 'changed' => [ [ - 'value' => '123456789', + 'value' => (string) $this->entity->getChangedTime(), ], ], 'default_langcode' => [ diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php index 5fbc408df833fd963a48743b660c03f21a10e2c4..dd3da09dcec5b173e87672cbf785038da9e9c04a 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php @@ -60,6 +60,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDescription(t('The time that the entity was last edited.')) ->setTranslatable(TRUE); + $fields['not_translatable'] = BaseFieldDefinition::create('string') + ->setLabel(t('Non translatable')) + ->setDescription(t('A non-translatable string field')); + return $fields; } diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php index 0c3a0ebbf23282baad764b56b3729c992a2d7579..7aca19f108ac9ec38d8217e101a5e03dafa129ad 100644 --- a/core/modules/taxonomy/src/Entity/Term.php +++ b/core/modules/taxonomy/src/Entity/Term.php @@ -233,4 +233,17 @@ public function getVocabularyId() { return $this->bundle(); } + /** + * {@inheritdoc} + */ + protected function getFieldsToSkipFromTranslationChangesCheck() { + // @todo the current implementation of the parent field makes it impossible + // for ::hasTranslationChanges() to correctly check the field for changes, + // so it is currently skipped from the comparision and has to be fixed by + // https://www.drupal.org/node/2843060. + $fields = parent::getFieldsToSkipFromTranslationChangesCheck(); + $fields[] = 'parent'; + return $fields; + } + } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php index e8425e5d91f5cfbdceba473fdd32c00eba2c92e9..e9e2ad14252053d028c3184c84f3aea1ff28730a 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php @@ -61,6 +61,7 @@ public function testChanged() { // Create a test entity. $entity = EntityTestMulChanged::create(array( 'name' => $this->randomString(), + 'not_translatable' => $this->randomString(), 'user_id' => $user1->id(), 'language' => 'en', )); @@ -122,6 +123,25 @@ public function testChanged() { 'Changed time of the German translation did not change.' ); + // Update a non-translatable field to make sure that the changed timestamp + // is updated for all translations. + $entity->set('not_translatable', $this->randomString())->save(); + + $this->assertTrue( + $entity->getChangedTime() > $changed_en, + 'Changed time of original language did change.' + ); + + $this->assertTrue( + $german->getChangedTime() > $changed_de, + 'Changed time of the German translation did change.' + ); + + $this->assertEquals($entity->getChangedTime(), $german->getChangedTime(), 'When editing a non-translatable field the updated changed time is equal across all translations.'); + + $changed_en = $entity->getChangedTime(); + $changed_de = $german->getChangedTime(); + $entity->setOwner($user2); $entity->save();