diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 1d69be2..ad6bd80 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -149,6 +149,13 @@ protected $validationRequired = FALSE; /** + * The original revision ID before the new revision was set. + * + * @var int + */ + protected $originalRevisionId; + + /** * {@inheritdoc} */ public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = array()) { @@ -219,6 +226,11 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans } } } + if ($this->getEntityType()->isRevisionable()) { + // Store the original revision ID the entity has been loaded with to + // keep it safe from changes. + $this->originalRevisionId = $this->getRevisionId(); + } } /** @@ -252,6 +264,12 @@ public function setNewRevision($value = TRUE) { throw new \LogicException("Entity type {$this->getEntityTypeId()} does not support revisions."); } + // If there is an revision ID set it as the original revision ID. Even when + // a new revision isn't created the original ID should be set. + if ($this->getRevisionId()) { + $this->originalRevisionId = $this->getRevisionId(); + } + if ($value && !$this->newRevision) { // When saving a new revision, set any existing revision ID to NULL so as // to ensure that a new revision will actually be created. @@ -273,6 +291,13 @@ public function setNewRevision($value = TRUE) { /** * {@inheritdoc} */ + public function getOriginalRevisionId() { + return $this->originalRevisionId; + } + + /** + * {@inheritdoc} + */ public function isNewRevision() { return $this->newRevision || ($this->getEntityType()->hasKey('revision') && !$this->getRevisionId()); } @@ -1025,6 +1050,7 @@ public function createDuplicate() { // Check whether the entity type supports revisions and initialize it if so. if ($entity_type->isRevisionable()) { $duplicate->{$entity_type->getKey('revision')}->value = NULL; + $duplicate->originalRevisionId = NULL; } return $duplicate; diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php index 0c3f04b..684aaa7 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php @@ -53,4 +53,13 @@ public function setRevisionTranslationAffected($affected); */ public function isRevisionTranslationAffected(); + /** + * Gets the original revision identifier of the entity. + * + * @return int + * The original revision identifier of the entity, or NULL if the entity + * does not have a revision identifier. + */ + public function getOriginalRevisionId(); + } diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index f8f56eb..717bdb9 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Access\AccessResult; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -413,6 +414,15 @@ function entity_test_entity_test_insert($entity) { } /** + * Implements hook_entity_update(). + */ +function entity_test_entity_update(EntityInterface $entity) { + if ($entity instanceof ContentEntityInterface) { + \Drupal::state()->set('entity_test.originalRevisionId', $entity->getOriginalRevisionId()); + } +} + +/** * Implements hook_entity_field_access(). * * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess() diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityOriginalRevisionTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityOriginalRevisionTest.php new file mode 100644 index 0000000..33fc951 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityOriginalRevisionTest.php @@ -0,0 +1,85 @@ +installEntitySchema('entity_test_mulrev'); + + } + + /** + * Test getOriginalRevisionId() returns the correct ID throughout the process. + */ + public function testOriginalRevisionId() { + // Create a basic EntityTestMulRev entity and save it. + $entity = EntityTestMulRev::create(); + $entity->save(); + + // Load the created entity and create a new revision. + $loaded = EntityTestMulRev::load($entity->id()); + $loaded->setNewRevision(TRUE); + + // Before saving, the original revision ID should be the same as the created + // entity, not the same as the loaded entity (which do not have a revision + // id yet). + $this->assertEquals($entity->getRevisionId(), $loaded->getOriginalRevisionId()); + $this->assertNotEquals($loaded->getRevisionId(), $loaded->getOriginalRevisionId()); + $this->assertSame(NULL, $loaded->getRevisionId()); + + $loaded->save(); + + // In entity_test_entity_update() the original revision id was stored in + // state. This should be the same as we had before calling $loaded->save(). + /** @var \Drupal\Core\Entity\ContentEntityInterface $loaded_original */ + $originalRevisionId = \Drupal::state()->get('entity_test.originalRevisionId'); + $this->assertEquals($entity->getRevisionId(), $originalRevisionId); + $this->assertNotEquals($loaded->getRevisionId(), $originalRevisionId); + + // The revision ID and original revision ID should be different for the two + // versions of the entity, but the same for a saved entity. + $this->assertNotEquals($loaded->getRevisionId(), $entity->getRevisionId()); + $this->assertNotEquals($loaded->getOriginalRevisionId(), $entity->getOriginalRevisionId()); + $this->assertEquals($entity->getRevisionId(), $entity->getOriginalRevisionId()); + $this->assertEquals($loaded->getRevisionId(), $loaded->getOriginalRevisionId()); + + // Make a change to the loaded entity. + $loaded->set('name', 'dublin'); + + // The revision id and original revision id should still be the same. + $this->assertEquals($loaded->getRevisionId(), $loaded->getOriginalRevisionId()); + + $loaded->save(); + + // After saving, the original revision id set in entity_test_entity_update() + // and returned from the entity should be the same and the entity's revision + // id because a new revision wasn't created, the existing revision was + // updated. + $originalRevisionId2 = \Drupal::state()->get('entity_test.originalRevisionId'); + $this->assertEquals($loaded->getRevisionId(), $originalRevisionId2); + $this->assertEquals($loaded->getRevisionId(), $loaded->getOriginalRevisionId()); + + $duplicate = $loaded->createDuplicate(); + $this->assertSame(NULL, $duplicate->getOriginalRevisionId()); + } +} diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php index cb73533..bc5965b 100644 --- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php @@ -215,7 +215,7 @@ public function testIsNewRevision() { ->method('hasKey') ->with('revision') ->will($this->returnValue(TRUE)); - $this->entityType->expects($this->at(5)) + $this->entityType->expects($this->at(6)) ->method('getKey') ->with('revision') ->will($this->returnValue('revision_id'));