diff -u b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php --- b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityChangedConstraintValidator.php @@ -18,27 +18,19 @@ /** @var \Drupal\Core\Entity\EntityInterface $entity */ if (!$entity->isNew()) { $saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id()); - // A change to any other translation must add a violation to the current - // translation because there might be untranslatable shared fields. - // - // In the entity form API we put the changed timestamp of the entity in - // a form hidden value and on form submit we set it on the entity which - // will be loaded from the storage if the form is not yet cached. - // Setting the initial changed timestamp on the entity loaded from the - // storage is used as a prevention from saving a form built with a - // previous version of the entity and thus reverting changes by other - // users. However the entity might have been saved in the background in - // a way that has updated the changed timestamps of all entity - // translations and simply comparing the latest changed timestamps - // across all translations is not a sufficient check in this case. - // Instead we have to compare directly the changed timestamps of the - // current entity translation first and after that the changed - // timestamps across all translations. + // It is not allowed that there is an entity translation with an older + // changed timestamp than the one of the corresponding saved entity + // translation. Comparing only the highest changed timestamps across all + // translations is not a sufficient check as by doing this we will not + // be able to detect the case where only one entity translation is + // obsolete. if ($saved_entity) { - $langcode = $entity->language()->getId(); - if (($saved_entity->hasTranslation($langcode) && ($saved_entity->getTranslation($langcode)->getChangedTime() > $entity->getChangedTime())) || - ($saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations())) { - $this->context->addViolation($constraint->message); + $common_translation_languages = array_intersect_key($entity->getTranslationLanguages(), $saved_entity->getTranslationLanguages()); + foreach (array_keys($common_translation_languages) as $langcode) { + if ($saved_entity->getTranslation($langcode)->getChangedTime() > $entity->getTranslation($langcode)->getChangedTime()) { + $this->context->addViolation($constraint->message); + break; + } } } }