Loading src/EntityClone/Content/ContentEntityCloneBase.php +45 −9 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ use Drupal\Component\Datetime\TimeInterface; use Drupal\content_moderation\Entity\ContentModerationState; use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityChangedInterface; use Drupal\Core\Entity\EntityHandlerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\TranslatableInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldConfigInterface; use Drupal\Core\Field\FieldItemListInterface; Loading Loading @@ -106,15 +108,7 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter } $this->setClonedEntityLabel($entity, $cloned_entity); // For now, check that the cloned entity has a 'setCreatedTime' method, and // if so, try to call it. This condition can be replaced with a more-robust // check whether $cloned_entity is an instance of // Drupal\Core\Entity\EntityCreatedInterface once // https://www.drupal.org/project/drupal/issues/2833378 lands. if (method_exists($cloned_entity, 'setCreatedTime')) { $cloned_entity->setCreatedTime($this->timeService->getRequestTime()); } $this->setCreatedAndChangedDates($cloned_entity); if ($this->hasTranslatableModerationState($cloned_entity)) { // If we are using moderation state, ensure that each translation gets Loading Loading @@ -326,4 +320,46 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter return !empty($entity->getTranslationLanguages(FALSE)); } /** * Resets the created and changed dates on the cloned entity. * * Since we don't want the cloned entity to have the old dates (as a new * entity is being created), we need to reset the created and changed dates * for those entities that support it. * * @param \Drupal\Core\Entity\EntityInterface $entity * The cloned entity. * @param bool $is_translation * Whether we are recursing over a translation. */ protected function setCreatedAndChangedDates(EntityInterface $entity, bool $is_translation = FALSE) { $created_time = $this->timeService->getRequestTime(); // For now, check that the cloned entity has a 'setCreatedTime' method, and // if so, try to call it. This condition can be replaced with a more-robust // check whether $cloned_entity is an instance of // Drupal\Core\Entity\EntityCreatedInterface once // https://www.drupal.org/project/drupal/issues/2833378 lands. if (method_exists($entity, 'setCreatedTime')) { $entity->setCreatedTime($created_time); } // If the entity has a changed time field, we should update it to the // created time we set above as it cannot possibly be before. if ($entity instanceof EntityChangedInterface) { $entity->setChangedTime($created_time); } if ($is_translation) { return; } if ($entity instanceof TranslatableInterface) { foreach ($entity->getTranslationLanguages(FALSE) as $language) { $translation = $entity->getTranslation($language->getId()); $this->setCreatedAndChangedDates($translation, TRUE); } } } } tests/src/Functional/EntityCloneContentCreatedDate.phpdeleted 100644 → 0 +0 −81 Original line number Diff line number Diff line <?php namespace Drupal\Tests\entity_clone\Functional; use Drupal\Core\Url; use Drupal\node\Entity\Node; use Drupal\Tests\node\Functional\NodeTestBase; /** * Test whether cloning an entity also clones its created date. * * @group entity_clone */ class EntityCloneContentCreatedDate extends NodeTestBase { /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; /** * {@inheritdoc} */ public static $modules = ['entity_clone', 'node']; /** * The user that we will use to execute the functional test. * * @var \Drupal\user\UserInterface */ protected $sutUser; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->sutUser = $this->drupalCreateUser([ 'bypass node access', 'administer nodes', 'clone node entity', ]); } /** * Test that an entity's created date is not cloned. */ public function testCreatedDateIsNotCloned() { // Log in. $this->drupalLogin($this->sutUser); // Create the original node. $originalNodeCreatedDate = new \DateTimeImmutable('1 year 1 month 1 day ago'); $originalNode = $this->drupalCreateNode([ 'created' => $originalNodeCreatedDate->getTimestamp(), ]); $this->assertEquals($originalNodeCreatedDate->getTimestamp(), $originalNode->getCreatedTime()); // Clone the node. $this->drupalGet(Url::fromRoute('entity.node.clone_form', [ 'node' => $originalNode->id(), ])->toString()); $this->getSession()->getPage()->pressButton('Clone'); // Find the cloned node. $originalNodeClones = \Drupal::entityTypeManager() ->getStorage('node') ->loadByProperties([ 'title' => sprintf('%s - Cloned', $originalNode->label()), ]); $this->assertGreaterThanOrEqual(1, count($originalNodeClones)); $clonedNode = reset($originalNodeClones); // Validate the cloned node's created time is more recent than the original // node. $this->assertNotEquals($originalNode->getCreatedTime(), $clonedNode->getCreatedTime()); $this->assertGreaterThanOrEqual($originalNode->getCreatedTime(), $clonedNode->getCreatedTime()); } } tests/src/Functional/EntityCloneContentTest.php +69 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ namespace Drupal\Tests\entity_clone\Functional; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\Node; use Drupal\Tests\field\Traits\EntityReferenceTestTrait; use Drupal\Tests\node\Functional\NodeTestBase; Loading @@ -20,7 +22,7 @@ class EntityCloneContentTest extends NodeTestBase { * * @var array */ public static $modules = ['entity_clone', 'block', 'node', 'datetime', 'taxonomy']; public static $modules = ['entity_clone', 'block', 'node', 'datetime', 'taxonomy', 'content_translation', 'language']; /** * Theme to enable by default Loading Loading @@ -54,6 +56,9 @@ class EntityCloneContentTest extends NodeTestBase { $this->adminUser = $this->drupalCreateUser($this->permissions); $this->drupalLogin($this->adminUser); ConfigurableLanguage::createFromLangcode('fr')->save(); \Drupal::service('content_translation.manager')->setEnabled('node', 'page', TRUE); } /** Loading @@ -78,6 +83,9 @@ class EntityCloneContentTest extends NodeTestBase { $this->assertInstanceOf(Node::class, $node, 'Test node cloned found in database.'); } /** * Test content reference config entity. */ public function testContentReferenceConfigEntity() { $this->createEntityReferenceField('node', 'page', 'config_field_reference', 'Config field reference', 'taxonomy_vocabulary'); Loading @@ -93,4 +101,64 @@ class EntityCloneContentTest extends NodeTestBase { $this->assertSession()->elementNotExists('css', '#edit-recursive-nodepageconfig-field-reference'); } /** * Test the cloned entity's created and changed dates. * * For entities that support these kinds of dates, both are reset to the * current time. */ public function testCreatedAndChangedDate() { // Create the original node. $original_node_creation_date = new \DateTimeImmutable('1 year 1 month 1 day ago'); $translation_creation_date = new \DateTimeImmutable('1 month 1 day ago'); $original_node = Node::create([ 'type' => 'page', 'title' => 'Test', 'created' => $original_node_creation_date->getTimestamp(), 'changed' => $original_node_creation_date->getTimestamp(), ]); $original_node->addTranslation('fr', $original_node->toArray()); // The translation was created and updated later. $translation = $original_node->getTranslation('fr'); $translation->setCreatedTime($translation_creation_date->getTimestamp()); $translation->setChangedTime($translation_creation_date->getTimestamp()); $original_node->save(); $original_node = Node::load($original_node->id()); $this->assertEquals($original_node_creation_date->getTimestamp(), $original_node->getCreatedTime()); $this->assertEquals($original_node_creation_date->getTimestamp(), $original_node->getChangedTime()); $this->assertEquals($translation_creation_date->getTimestamp(), $original_node->getTranslation('fr')->getCreatedTime()); $this->assertEquals($translation_creation_date->getTimestamp(), $original_node->getTranslation('fr')->getChangedTime()); // Clone the node. $this->drupalPostForm('entity_clone/node/' . $original_node->id(), [], t('Clone')); // Find the cloned node. $nodes = \Drupal::entityTypeManager() ->getStorage('node') ->loadByProperties([ 'title' => sprintf('%s - Cloned', $original_node->label()), ]); $this->assertGreaterThanOrEqual(1, count($nodes)); /** @var \Drupal\node\NodeInterface $cloned_node */ $cloned_node = reset($nodes); // Validate the cloned node's created time is more recent than the original // node. $this->assertNotEquals($original_node->getCreatedTime(), $cloned_node->getCreatedTime()); $this->assertGreaterThanOrEqual($original_node->getCreatedTime(), $cloned_node->getCreatedTime()); // Assert the changed time is equal to the newly created time since we // cannot have a changed date before it. $this->assertEquals($cloned_node->getCreatedTime(), $cloned_node->getChangedTime()); // Validate the translation created and updated dates. $this->assertTrue($cloned_node->hasTranslation('fr')); $translation = $cloned_node->getTranslation('fr'); // The created and updated times should be the same between the original // and the translation as both should be reset. $this->assertEquals($cloned_node->getCreatedTime(), $translation->getCreatedTime()); $this->assertEquals($cloned_node->getChangedTime(), $translation->getChangedTime()); } } Loading
src/EntityClone/Content/ContentEntityCloneBase.php +45 −9 Original line number Diff line number Diff line Loading @@ -6,11 +6,13 @@ use Drupal\Component\Datetime\TimeInterface; use Drupal\content_moderation\Entity\ContentModerationState; use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityChangedInterface; use Drupal\Core\Entity\EntityHandlerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\TranslatableInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldConfigInterface; use Drupal\Core\Field\FieldItemListInterface; Loading Loading @@ -106,15 +108,7 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter } $this->setClonedEntityLabel($entity, $cloned_entity); // For now, check that the cloned entity has a 'setCreatedTime' method, and // if so, try to call it. This condition can be replaced with a more-robust // check whether $cloned_entity is an instance of // Drupal\Core\Entity\EntityCreatedInterface once // https://www.drupal.org/project/drupal/issues/2833378 lands. if (method_exists($cloned_entity, 'setCreatedTime')) { $cloned_entity->setCreatedTime($this->timeService->getRequestTime()); } $this->setCreatedAndChangedDates($cloned_entity); if ($this->hasTranslatableModerationState($cloned_entity)) { // If we are using moderation state, ensure that each translation gets Loading Loading @@ -326,4 +320,46 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter return !empty($entity->getTranslationLanguages(FALSE)); } /** * Resets the created and changed dates on the cloned entity. * * Since we don't want the cloned entity to have the old dates (as a new * entity is being created), we need to reset the created and changed dates * for those entities that support it. * * @param \Drupal\Core\Entity\EntityInterface $entity * The cloned entity. * @param bool $is_translation * Whether we are recursing over a translation. */ protected function setCreatedAndChangedDates(EntityInterface $entity, bool $is_translation = FALSE) { $created_time = $this->timeService->getRequestTime(); // For now, check that the cloned entity has a 'setCreatedTime' method, and // if so, try to call it. This condition can be replaced with a more-robust // check whether $cloned_entity is an instance of // Drupal\Core\Entity\EntityCreatedInterface once // https://www.drupal.org/project/drupal/issues/2833378 lands. if (method_exists($entity, 'setCreatedTime')) { $entity->setCreatedTime($created_time); } // If the entity has a changed time field, we should update it to the // created time we set above as it cannot possibly be before. if ($entity instanceof EntityChangedInterface) { $entity->setChangedTime($created_time); } if ($is_translation) { return; } if ($entity instanceof TranslatableInterface) { foreach ($entity->getTranslationLanguages(FALSE) as $language) { $translation = $entity->getTranslation($language->getId()); $this->setCreatedAndChangedDates($translation, TRUE); } } } }
tests/src/Functional/EntityCloneContentCreatedDate.phpdeleted 100644 → 0 +0 −81 Original line number Diff line number Diff line <?php namespace Drupal\Tests\entity_clone\Functional; use Drupal\Core\Url; use Drupal\node\Entity\Node; use Drupal\Tests\node\Functional\NodeTestBase; /** * Test whether cloning an entity also clones its created date. * * @group entity_clone */ class EntityCloneContentCreatedDate extends NodeTestBase { /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; /** * {@inheritdoc} */ public static $modules = ['entity_clone', 'node']; /** * The user that we will use to execute the functional test. * * @var \Drupal\user\UserInterface */ protected $sutUser; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->sutUser = $this->drupalCreateUser([ 'bypass node access', 'administer nodes', 'clone node entity', ]); } /** * Test that an entity's created date is not cloned. */ public function testCreatedDateIsNotCloned() { // Log in. $this->drupalLogin($this->sutUser); // Create the original node. $originalNodeCreatedDate = new \DateTimeImmutable('1 year 1 month 1 day ago'); $originalNode = $this->drupalCreateNode([ 'created' => $originalNodeCreatedDate->getTimestamp(), ]); $this->assertEquals($originalNodeCreatedDate->getTimestamp(), $originalNode->getCreatedTime()); // Clone the node. $this->drupalGet(Url::fromRoute('entity.node.clone_form', [ 'node' => $originalNode->id(), ])->toString()); $this->getSession()->getPage()->pressButton('Clone'); // Find the cloned node. $originalNodeClones = \Drupal::entityTypeManager() ->getStorage('node') ->loadByProperties([ 'title' => sprintf('%s - Cloned', $originalNode->label()), ]); $this->assertGreaterThanOrEqual(1, count($originalNodeClones)); $clonedNode = reset($originalNodeClones); // Validate the cloned node's created time is more recent than the original // node. $this->assertNotEquals($originalNode->getCreatedTime(), $clonedNode->getCreatedTime()); $this->assertGreaterThanOrEqual($originalNode->getCreatedTime(), $clonedNode->getCreatedTime()); } }
tests/src/Functional/EntityCloneContentTest.php +69 −1 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ namespace Drupal\Tests\entity_clone\Functional; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\Node; use Drupal\Tests\field\Traits\EntityReferenceTestTrait; use Drupal\Tests\node\Functional\NodeTestBase; Loading @@ -20,7 +22,7 @@ class EntityCloneContentTest extends NodeTestBase { * * @var array */ public static $modules = ['entity_clone', 'block', 'node', 'datetime', 'taxonomy']; public static $modules = ['entity_clone', 'block', 'node', 'datetime', 'taxonomy', 'content_translation', 'language']; /** * Theme to enable by default Loading Loading @@ -54,6 +56,9 @@ class EntityCloneContentTest extends NodeTestBase { $this->adminUser = $this->drupalCreateUser($this->permissions); $this->drupalLogin($this->adminUser); ConfigurableLanguage::createFromLangcode('fr')->save(); \Drupal::service('content_translation.manager')->setEnabled('node', 'page', TRUE); } /** Loading @@ -78,6 +83,9 @@ class EntityCloneContentTest extends NodeTestBase { $this->assertInstanceOf(Node::class, $node, 'Test node cloned found in database.'); } /** * Test content reference config entity. */ public function testContentReferenceConfigEntity() { $this->createEntityReferenceField('node', 'page', 'config_field_reference', 'Config field reference', 'taxonomy_vocabulary'); Loading @@ -93,4 +101,64 @@ class EntityCloneContentTest extends NodeTestBase { $this->assertSession()->elementNotExists('css', '#edit-recursive-nodepageconfig-field-reference'); } /** * Test the cloned entity's created and changed dates. * * For entities that support these kinds of dates, both are reset to the * current time. */ public function testCreatedAndChangedDate() { // Create the original node. $original_node_creation_date = new \DateTimeImmutable('1 year 1 month 1 day ago'); $translation_creation_date = new \DateTimeImmutable('1 month 1 day ago'); $original_node = Node::create([ 'type' => 'page', 'title' => 'Test', 'created' => $original_node_creation_date->getTimestamp(), 'changed' => $original_node_creation_date->getTimestamp(), ]); $original_node->addTranslation('fr', $original_node->toArray()); // The translation was created and updated later. $translation = $original_node->getTranslation('fr'); $translation->setCreatedTime($translation_creation_date->getTimestamp()); $translation->setChangedTime($translation_creation_date->getTimestamp()); $original_node->save(); $original_node = Node::load($original_node->id()); $this->assertEquals($original_node_creation_date->getTimestamp(), $original_node->getCreatedTime()); $this->assertEquals($original_node_creation_date->getTimestamp(), $original_node->getChangedTime()); $this->assertEquals($translation_creation_date->getTimestamp(), $original_node->getTranslation('fr')->getCreatedTime()); $this->assertEquals($translation_creation_date->getTimestamp(), $original_node->getTranslation('fr')->getChangedTime()); // Clone the node. $this->drupalPostForm('entity_clone/node/' . $original_node->id(), [], t('Clone')); // Find the cloned node. $nodes = \Drupal::entityTypeManager() ->getStorage('node') ->loadByProperties([ 'title' => sprintf('%s - Cloned', $original_node->label()), ]); $this->assertGreaterThanOrEqual(1, count($nodes)); /** @var \Drupal\node\NodeInterface $cloned_node */ $cloned_node = reset($nodes); // Validate the cloned node's created time is more recent than the original // node. $this->assertNotEquals($original_node->getCreatedTime(), $cloned_node->getCreatedTime()); $this->assertGreaterThanOrEqual($original_node->getCreatedTime(), $cloned_node->getCreatedTime()); // Assert the changed time is equal to the newly created time since we // cannot have a changed date before it. $this->assertEquals($cloned_node->getCreatedTime(), $cloned_node->getChangedTime()); // Validate the translation created and updated dates. $this->assertTrue($cloned_node->hasTranslation('fr')); $translation = $cloned_node->getTranslation('fr'); // The created and updated times should be the same between the original // and the translation as both should be reset. $this->assertEquals($cloned_node->getCreatedTime(), $translation->getCreatedTime()); $this->assertEquals($cloned_node->getChangedTime(), $translation->getChangedTime()); } }