Commit 3bc8b665 authored by catch's avatar catch
Browse files

Issue #2544790 by hchonov, amateescu, tstoeckler:...

Issue #2544790 by hchonov, amateescu, tstoeckler: ContentEntityBase::setNewRevision(FALSE) is broken if ::setNewRevision(TRUE) was called previously
parent 1b8dea09
......@@ -156,6 +156,22 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
*/
protected $loadedRevisionId;
/**
* The revision translation affected entity key.
*
* @var string
*/
protected $revisionTranslationAffectedKey;
/**
* Whether the revision translation affected flag has been enforced.
*
* An array, keyed by the translation language code.
*
* @var bool[]
*/
protected $enforceRevisionTranslationAffected = [];
/**
* {@inheritdoc}
*/
......@@ -164,6 +180,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans
$this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId;
$this->langcodeKey = $this->getEntityType()->getKey('langcode');
$this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode');
$this->revisionTranslationAffectedKey = $this->getEntityType()->getKey('revision_translation_affected');
foreach ($values as $key => $value) {
// If the key matches an existing property set the value to the property
......@@ -269,15 +286,11 @@ public function setNewRevision($value = TRUE) {
// When saving a new revision, set any existing revision ID to NULL so as
// to ensure that a new revision will actually be created.
$this->set($this->getEntityType()->getKey('revision'), NULL);
// Make sure that the flag tracking which translations are affected by the
// current revision is reset.
foreach ($this->translations as $langcode => $data) {
// But skip removed translations.
if ($this->hasTranslation($langcode)) {
$this->getTranslation($langcode)->setRevisionTranslationAffected(NULL);
}
}
}
elseif (!$value && $this->newRevision) {
// If ::setNewRevision(FALSE) is called after ::setNewRevision(TRUE) we
// have to restore the loaded revision ID.
$this->set($this->getEntityType()->getKey('revision'), $this->getLoadedRevisionId());
}
$this->newRevision = $value;
......@@ -322,21 +335,34 @@ public function isDefaultRevision($new_value = NULL) {
* {@inheritdoc}
*/
public function isRevisionTranslationAffected() {
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
return $this->hasField($field_name) ? $this->get($field_name)->value : TRUE;
return $this->hasField($this->revisionTranslationAffectedKey) ? $this->get($this->revisionTranslationAffectedKey)->value : TRUE;
}
/**
* {@inheritdoc}
*/
public function setRevisionTranslationAffected($affected) {
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
if ($this->hasField($field_name)) {
$this->set($field_name, $affected);
if ($this->hasField($this->revisionTranslationAffectedKey)) {
$this->set($this->revisionTranslationAffectedKey, $affected);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function isRevisionTranslationAffectedEnforced() {
return !empty($this->enforceRevisionTranslationAffected[$this->activeLangcode]);
}
/**
* {@inheritdoc}
*/
public function setRevisionTranslationAffectedEnforced($enforced) {
$this->enforceRevisionTranslationAffected[$this->activeLangcode] = $enforced;
return $this;
}
/**
* {@inheritdoc}
*/
......@@ -401,6 +427,12 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
}
}
$this->translations = array_diff_key($this->translations, $removed);
// Reset the new revision flag.
$this->newRevision = FALSE;
// Reset the enforcement of the revision translation affected flag.
$this->enforceRevisionTranslationAffected = [];
}
/**
......@@ -748,6 +780,12 @@ public function onChange($name) {
throw new \LogicException($message);
}
break;
case $this->revisionTranslationAffectedKey:
// If the revision translation affected flag is being set then enforce
// its value.
$this->setRevisionTranslationAffectedEnforced(TRUE);
break;
}
}
......@@ -831,6 +869,7 @@ protected function initializeTranslation($langcode) {
$translation->typedData = NULL;
$translation->loadedRevisionId = &$this->loadedRevisionId;
$translation->isDefaultRevision = &$this->isDefaultRevision;
$translation->enforceRevisionTranslationAffected = &$this->enforceRevisionTranslationAffected;
return $translation;
}
......@@ -1098,7 +1137,8 @@ public function __clone() {
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys,
// translatableEntityKeys, values and isDefaultRevision.
// translatableEntityKeys, values, isDefaultRevision and
// enforceRevisionTranslationAffected.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
......@@ -1123,6 +1163,9 @@ public function __clone() {
$default_revision = $this->isDefaultRevision;
$this->isDefaultRevision = &$default_revision;
$is_revision_translation_affected_enforced = $this->enforceRevisionTranslationAffected;
$this->enforceRevisionTranslationAffected = &$is_revision_translation_affected_enforced;
foreach ($this->fields as $name => $fields_by_langcode) {
$this->fields[$name] = [];
// Untranslatable fields may have multiple references for the same field
......
......@@ -33,6 +33,12 @@ public function hasTranslationChanges();
/**
* Marks the current revision translation as affected.
*
* Setting the revision translation affected flag through the setter or
* through the field directly will always enforce it, which will be used by
* the entity storage to determine if the flag should be recomputed or the set
* value should be used instead.
* @see \Drupal\Core\Entity\ContentEntityStorageBase::populateAffectedRevisionTranslations()
*
* @param bool|null $affected
* The flag value. A NULL value can be specified to reset the current value
* and make sure a new value will be computed by the system.
......@@ -50,6 +56,35 @@ public function setRevisionTranslationAffected($affected);
*/
public function isRevisionTranslationAffected();
/**
* Checks if the revision translation affected flag value has been enforced.
*
* @return bool
* TRUE if revision translation affected flag is enforced, FALSE otherwise.
*
* @internal
*/
public function isRevisionTranslationAffectedEnforced();
/**
* Enforces the revision translation affected flag value.
*
* Note that this method call will not have any influence on the storage if
* the value of the revision translation affected flag is NULL which is used
* as an indication for the storage to recompute the flag.
* @see \Drupal\Core\Entity\ContentEntityInterface::setRevisionTranslationAffected()
*
* @param bool $enforced
* If TRUE, the value of the revision translation affected flag will be
* enforced so that on entity save the entity storage will not recompute it.
* Otherwise the storage will recompute it.
*
* @return $this
*
* @internal
*/
public function setRevisionTranslationAffectedEnforced($enforced);
/**
* Gets the loaded Revision ID of the entity.
*
......
......@@ -587,10 +587,17 @@ protected function populateAffectedRevisionTranslations(ContentEntityInterface $
$languages = $entity->getTranslationLanguages();
foreach ($languages as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
// Avoid populating the value if it was already manually set.
$affected = $translation->isRevisionTranslationAffected();
if (!isset($affected) && $translation->hasTranslationChanges()) {
$translation->setRevisionTranslationAffected(TRUE);
$current_affected = $translation->isRevisionTranslationAffected();
if (!isset($current_affected) || ($entity->isNewRevision() && !$translation->isRevisionTranslationAffectedEnforced())) {
// When setting the revision translation affected flag we have to
// explicitly set it to not be enforced. By default it will be
// enforced automatically when being set, which allows us to determine
// if the flag has been already set outside the storage in which case
// we should not recompute it.
// @see \Drupal\Core\Entity\ContentEntityBase::setRevisionTranslationAffected().
$new_affected = $translation->hasTranslationChanges() ? TRUE : NULL;
$translation->setRevisionTranslationAffected($new_affected);
$translation->setRevisionTranslationAffectedEnforced(FALSE);
}
}
}
......
......@@ -297,7 +297,7 @@ public function testEntityPropertiesModifications() {
// Retrieve the entity properties.
$reflection = new \ReflectionClass($entity);
$properties = $reflection->getProperties(~\ReflectionProperty::IS_STATIC);
$translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds'];
$translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'revisionTranslationAffectedKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds'];
foreach ($properties as $property) {
// Modify each entity property on the clone and assert that the change is
......
......@@ -157,4 +157,33 @@ public function testDefaultRevision() {
$this->assertFalse($translation->isDefaultRevision());
}
/**
* @covers \Drupal\Core\Entity\RevisionableInterface::setNewRevision
*/
public function testSetNewRevision() {
$user = $this->createUser();
// All revisionable entity variations have to have the same results.
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_REVISABLE) as $entity_type) {
$this->installEntitySchema($entity_type);
$entity = entity_create($entity_type, [
'name' => 'foo',
'user_id' => $user->id(),
]);
$entity->save();
$entity_id = $entity->id();
$entity_rev_id = $entity->getRevisionId();
$entity = entity_load($entity_type, $entity_id, TRUE);
$entity->setNewRevision(TRUE);
$entity->setNewRevision(FALSE);
$entity->save();
$entity = entity_load($entity_type, $entity_id, TRUE);
$this->assertEquals($entity_rev_id, $entity->getRevisionId(), 'A new entity revision was not created.');
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment