Unverified Commit cf354c04 authored by Daniel Sipos's avatar Daniel Sipos Committed by Daniel Sipos
Browse files

Issue #3216900 by Upchuk, guilhermevp, Fernly, n.ghunaim, joevagyok, sinn:...

Issue #3216900 by Upchuk, guilhermevp, Fernly, n.ghunaim, joevagyok, sinn: Change cloned entity's last-modified date to the date it was cloned
parent 76cfa872
Loading
Loading
Loading
Loading
+45 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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);
      }
    }
  }

}
+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());
  }

}
+69 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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);
  }

  /**
@@ -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');

@@ -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());
   }

}