diff -u b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php --- b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityChangedInterface.php @@ -22,7 +22,7 @@ interface EntityChangedInterface { /** - * Gets the timestamp of the last entity change for current translation. + * Gets the timestamp of the last entity change for the current translation. * * @return int * The timestamp of the last entity save operation. diff -u b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php --- b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/ChangedItem.php @@ -31,42 +31,36 @@ public function preSave() { parent::preSave(); - // @todo Knowing if the current translation was modified or not is generally - // useful. There's a follow-up issue to reduce the nesting here and to - // offer an accessor for this information. See - // https://www.drupal.org/node/2453153 - $entity = $this->getEntity(); - if ($entity->isNew()){ - // Allow the changed timestamp to be set manually on entity creation, - // for example when migration is running. - if (!$this->value) { - $this->value = REQUEST_TIME; - } + // Set the timestamp to request time if it is not set. + if (!$this->value) { + $this->value = REQUEST_TIME; } else { - // Don't allow an illegal value. - if (!$this->value) { - $this->value = REQUEST_TIME; - } - else { + // On an existing entity the changed timestamp will only be set to request + // time automatically if at least one other field value of the entity has + // changed. This detection doesn't run on new entities and will be turned + // off if the changed timestamp is set manually before save, for example + // during migrations or using + // \Drupal\content_translation\ContentTranslationMetadataWrapperInterface::setChangedTime(). + // @todo Knowing if the current translation was modified or not is + // generally useful. There's a follow-up issue to reduce the nesting + // here and to offer an accessor for this information. See + // https://www.drupal.org/node/2453153 + $entity = $this->getEntity(); + if (!$entity->isNew()) { $field_name = $this->getFieldDefinition()->getName(); - // We have to clone the original because getTranslation() will modify - // the state of original. + // Clone $entity->original to avoid modifying it when calling + // getTranslation(). $original = clone $entity->original; - if ($this->getFieldDefinition()->isTranslatable()) { + $translatable = $this->getFieldDefinition()->isTranslatable(); + if ($translatable) { $original = $original->getTranslation($entity->language()->getId()); } - // If the timestamp has not been set explicitly auto detect a - // modification of the current translation and set the timestamp if - // needed. An example of setting the timestamp explicitly is - // \Drupal\content_translation\ContentTranslationMetadataWrapperInterface::setChangedTime(). - if ($this->value == $original->{$field_name}->value) { - foreach ($entity->getFieldDefinitions() as $other_field_definition) { - $other_field_name = $other_field_definition->getName(); - if ($other_field_name != $field_name && $other_field_definition->isTranslatable()) { + if ($this->value == $original->get($field_name)->value) { + foreach ($entity->getFieldDefinitions() as $other_field_name => $other_field_definition) { + if ($other_field_name != $field_name && !$other_field_definition->isComputed() && (!$translatable || $other_field_definition->isTranslatable())) { $items = $entity->get($other_field_name)->filterEmptyItems(); - $original_items = $original->get($other_field_name) - ->filterEmptyItems(); + $original_items = $original->get($other_field_name)->filterEmptyItems(); if (!$items->equals($original_items)) { $this->value = REQUEST_TIME; break; diff -u b/core/modules/node/node.module b/core/modules/node/node.module --- b/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -743,7 +743,7 @@ * @param $nid * The ID of a node. * @param string $langcode - * (optional) The language to get the last changed time for. If omitted the + * (optional) The language to get the last changed time for. If omitted, the * last changed time across all translations will be returned. * * @return string diff -u b/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php b/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php --- b/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php +++ b/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php @@ -58,7 +58,8 @@ // We can't assert equality here because the created time is set to the // request time, while instances of ChangedTestItem use the current - // timestamp every time. + // timestamp every time. Therefor we check if the changed timestamp is + // between the created time and now. $this->assertTrue( ($entity->getChangedTime() >= $entity->get('created')->value) && (($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME), @@ -83,7 +84,7 @@ $this->assertTrue( $german->getChangedTime() > $entity->getChangedTime(), - 'Changed time of German translation is newer then the original language.' + 'Changed time of the German translation is newer then the original language.' ); $this->assertEqual( @@ -144,9 +145,9 @@ 'Changed time of the German translation did not change.' ); - // Current state: changed time of original language (en) is newer than the - // changed time of the German translation. Now test that entity queries work - // as expected. + // At this point the changed time of the original language (en) is newer + // than the changed time of the German translation. Now test that entity + // queries work as expected. $storage = $this->entityManager->getStorage('entity_test_mul_changed'); $query = $storage->getQuery(); @@ -162,7 +163,7 @@ $this->assertEqual( reset($ids), $entity->id(), - 'Entity query can access changed time of original language.' + 'Entity query can access changed time of original language by setting the original language as condition.' ); $query = $storage->getQuery(); @@ -194,7 +195,7 @@ $this->assertEqual( reset($ids), $entity->id(), - 'Entity query can access changed time of German translation.' + 'Entity query can access changed time of the German translation.' ); $query = $storage->getQuery(); @@ -202,7 +203,7 @@ $this->assertEqual( reset($ids), $entity->id(), - 'Entity query can access changed time of German translation.' + 'Entity query can access changed time of the German translation.' ); $query = $storage->getQuery(); @@ -315,7 +316,7 @@ $this->assertTrue( $german->getChangedTime() > $entity->getChangedTime(), - 'Changed time of German translation is newer then the original language.' + 'Changed time of the German translation is newer then the original language.' ); $this->assertEqual( @@ -350,7 +351,7 @@ $this->assertTrue( $german->getChangedTime() > $changed_de, - 'Changed time of German translation did change.' + 'Changed time of the German translation did change.' ); $this->assertEqual(