From b9e4af5922daf1f3c205e9960067cd7a9a30afe5 Mon Sep 17 00:00:00 2001
From: Colan Schwartz <colan@58704.no-reply.drupal.org>
Date: Fri, 20 Oct 2017 14:43:20 -0400
Subject: [PATCH] Issue #2706639 by vpeltot, michaellander, ruloweb, dpolant,
 colan, weekbeforenext, pogfra: Added support for cloning sub-entities.

---
 entity_clone.info.yml                              |   1 +
 entity_clone.links.menu.yml                        |   5 +
 entity_clone.links.task.yml                        |   7 +-
 entity_clone.module                                |  48 ++++--
 entity_clone.routing.yml                           |   9 +
 entity_clone.services.yml                          |   3 +
 src/EntityClone/Config/ConfigEntityCloneBase.php   |   2 +-
 .../Config/ConfigEntityCloneFormBase.php           |  10 +-
 .../Config/ConfigWithFieldEntityClone.php          |  14 +-
 src/EntityClone/Config/FieldConfigEntityClone.php  |   2 +-
 src/EntityClone/Content/ContentEntityCloneBase.php | 135 ++++++++++++++-
 .../Content/ContentEntityCloneFormBase.php         | 181 +++++++++++++++++++++
 src/EntityClone/Content/FileEntityClone.php        |   2 +-
 .../Content/TaxonomyTermEntityClone.php            |  26 +++
 src/EntityClone/Content/UserEntityClone.php        |   2 +-
 src/EntityClone/EntityCloneFormInterface.php       |   2 +-
 src/EntityClone/EntityCloneInterface.php           |   6 +-
 src/EntityClonePermissions.php                     |   1 -
 src/EntityCloneSettingsManager.php                 | 149 +++++++++++++++++
 src/Form/EntityCloneForm.php                       |  28 +++-
 src/Form/EntityCloneSettingsForm.php               | 125 ++++++++++++++
 src/Plugin/Derivative/DynamicLocalTasks.php        |   4 +-
 src/Tests/EntityCloneActionTest.php                |  15 +-
 src/Tests/EntityCloneBlockTest.php                 |  15 +-
 src/Tests/EntityCloneCommentTest.php               |  22 ++-
 src/Tests/EntityCloneContentRecursiveTest.php      | 102 ++++++++++++
 src/Tests/EntityCloneContentTest.php               |  12 +-
 src/Tests/EntityCloneCustomBlockTest.php           |  12 +-
 src/Tests/EntityCloneDateFormatTest.php            |  15 +-
 src/Tests/EntityCloneEntityFormModeTest.php        |  15 +-
 src/Tests/EntityCloneEntityViewModeTest.php        |  15 +-
 src/Tests/EntityCloneFileTest.php                  |  19 +--
 src/Tests/EntityCloneFilterFormatTest.php          |  15 +-
 src/Tests/EntityCloneImageStyleTest.php            |  15 +-
 src/Tests/EntityCloneLanguageTest.php              |  17 +-
 src/Tests/EntityCloneMenuTest.php                  |  15 +-
 src/Tests/EntityCloneResponsiveImageStyleTest.php  |  15 +-
 src/Tests/EntityCloneRoleTest.php                  |  15 +-
 src/Tests/EntityCloneSearchPageTest.php            |  15 +-
 src/Tests/EntityCloneShortcutSetTest.php           |  15 +-
 src/Tests/EntityCloneUserTest.php                  |  15 +-
 src/Tests/EntityCloneViewTest.php                  |  15 +-
 42 files changed, 952 insertions(+), 204 deletions(-)
 create mode 100644 entity_clone.links.menu.yml
 create mode 100644 entity_clone.routing.yml
 create mode 100644 src/EntityClone/Content/ContentEntityCloneFormBase.php
 create mode 100644 src/EntityClone/Content/TaxonomyTermEntityClone.php
 create mode 100644 src/EntityCloneSettingsManager.php
 create mode 100644 src/Form/EntityCloneSettingsForm.php
 create mode 100644 src/Tests/EntityCloneContentRecursiveTest.php

diff --git a/entity_clone.info.yml b/entity_clone.info.yml
index cafcab7..b624edd 100644
--- a/entity_clone.info.yml
+++ b/entity_clone.info.yml
@@ -2,3 +2,4 @@ name: Entity Clone
 description: Add a clone action for all entities
 core:  "8.x"
 type: module
+configure: entity_clone.settings
diff --git a/entity_clone.links.menu.yml b/entity_clone.links.menu.yml
new file mode 100644
index 0000000..f013a12
--- /dev/null
+++ b/entity_clone.links.menu.yml
@@ -0,0 +1,5 @@
+entity_clone.settings:
+  title: 'Entity clone settings'
+  description: 'Entity clone settings.'
+  route_name: entity_clone.settings
+  parent: 'system.admin_config_content'
diff --git a/entity_clone.links.task.yml b/entity_clone.links.task.yml
index dbb40ee..427f575 100644
--- a/entity_clone.links.task.yml
+++ b/entity_clone.links.task.yml
@@ -1,3 +1,8 @@
 entity_clone.clone:
   deriver: 'Drupal\entity_clone\Plugin\Derivative\DynamicLocalTasks'
-  weight: 100
\ No newline at end of file
+  weight: 100
+entity_clone.settings:
+  title: 'Entity clone settings'
+  route_name: entity_clone.settings
+  base_route: entity_clone.settings
+  weight: 0
diff --git a/entity_clone.module b/entity_clone.module
index 81faab0..eb699cb 100644
--- a/entity_clone.module
+++ b/entity_clone.module
@@ -9,6 +9,15 @@ use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
+use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneBase;
+use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase;
+use Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone;
+use Drupal\entity_clone\EntityClone\Config\FieldConfigEntityClone;
+use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneBase;
+use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneFormBase;
+use Drupal\entity_clone\EntityClone\Content\FileEntityClone;
+use Drupal\entity_clone\EntityClone\Content\TaxonomyTermEntityClone;
+use Drupal\entity_clone\EntityClone\Content\UserEntityClone;
 
 /**
  * Implements hook_help().
@@ -29,35 +38,41 @@ function entity_clone_help($route_name, RouteMatchInterface $route_match) {
 }
 
 /**
- * Implements hook_entity_info_alter().
+ * Implements hook_entity_type_build().
  */
 function entity_clone_entity_type_build(array &$entity_types) {
   $specific_handler = [
     'file' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Content\FileEntityClone',
+      'entity_clone' => FileEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
     ],
     'user' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Content\UserEntityClone',
+      'entity_clone' => UserEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
     ],
     'field_config' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Config\FieldConfigEntityClone',
-      'entity_clone_form' => '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase',
+      'entity_clone' => FieldConfigEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
     ],
     'node_type' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone',
-      'entity_clone_form' => '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase',
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
     ],
     'comment_type' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone',
-      'entity_clone_form' => '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase',
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
     ],
     'block_content_type' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone',
-      'entity_clone_form' => '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase',
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
     ],
     'contact_form' => [
-      'entity_clone' => '\Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone',
-      'entity_clone_form' => '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase',
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'taxonomy_term' => [
+      'entity_clone' => TaxonomyTermEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
     ],
   ];
 
@@ -70,11 +85,12 @@ function entity_clone_entity_type_build(array &$entity_types) {
       }
     }
     elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ContentEntityTypeInterface) {
-      $entity_type->setHandlerClass('entity_clone', '\Drupal\entity_clone\EntityClone\Content\ContentEntityCloneBase');
+      $entity_type->setHandlerClass('entity_clone', ContentEntityCloneBase::class);
+      $entity_type->setHandlerClass('entity_clone_form', ContentEntityCloneFormBase::class);
     }
     elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ConfigEntityTypeInterface) {
-      $entity_type->setHandlerClass('entity_clone', '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneBase');
-      $entity_type->setHandlerClass('entity_clone_form', '\Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase');
+      $entity_type->setHandlerClass('entity_clone', ConfigEntityCloneBase::class);
+      $entity_type->setHandlerClass('entity_clone_form', ConfigEntityCloneFormBase::class);
     }
   }
 }
diff --git a/entity_clone.routing.yml b/entity_clone.routing.yml
new file mode 100644
index 0000000..b89a94b
--- /dev/null
+++ b/entity_clone.routing.yml
@@ -0,0 +1,9 @@
+entity_clone.settings:
+  path: '/admin/config/system/entity-clone'
+  defaults:
+    _form: 'Drupal\entity_clone\Form\EntityCloneSettingsForm'
+    _title: 'Entity clone settings'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _permission: 'administer entity clone'
diff --git a/entity_clone.services.yml b/entity_clone.services.yml
index deb11ef..20cf5db 100644
--- a/entity_clone.services.yml
+++ b/entity_clone.services.yml
@@ -1,4 +1,7 @@
 services:
+  entity_clone.settings.manager:
+    class: Drupal\entity_clone\EntityCloneSettingsManager
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@config.factory']
   entity_clone.route_subscriber:
     class: Drupal\entity_clone\Routing\RouteSubscriber
     arguments: ['@entity_type.manager']
diff --git a/src/EntityClone/Config/ConfigEntityCloneBase.php b/src/EntityClone/Config/ConfigEntityCloneBase.php
index ecf5116..c0c3349 100644
--- a/src/EntityClone/Config/ConfigEntityCloneBase.php
+++ b/src/EntityClone/Config/ConfigEntityCloneBase.php
@@ -54,7 +54,7 @@ class ConfigEntityCloneBase implements EntityHandlerInterface, EntityCloneInterf
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []) {
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
     /** @var \Drupal\core\Config\Entity\ConfigEntityInterface $cloned_entity */
     $id_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('id');
     $label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label');
diff --git a/src/EntityClone/Config/ConfigEntityCloneFormBase.php b/src/EntityClone/Config/ConfigEntityCloneFormBase.php
index d40ddd5..8d2f125 100644
--- a/src/EntityClone/Config/ConfigEntityCloneFormBase.php
+++ b/src/EntityClone/Config/ConfigEntityCloneFormBase.php
@@ -60,20 +60,20 @@ class ConfigEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFo
     $form = [];
 
     if ($this->entityTypeManager->getDefinition($entity->getEntityTypeId())->getKey('label')) {
-      $form['label'] = array(
+      $form['label'] = [
         '#type' => 'textfield',
         '#title' => $this->translationManager->translate('New Label'),
         '#maxlength' => 255,
         '#required' => TRUE,
-      );
+      ];
     }
 
-    $form['id'] = array(
+    $form['id'] = [
       '#type' => 'machine_name',
       '#title' => $this->translationManager->translate('New Id'),
       '#maxlength' => 255,
       '#required' => TRUE,
-    );
+    ];
 
     // If entity must have a prefix
     // (e.g. entity_form_mode, entity_view_mode, ...).
@@ -93,7 +93,7 @@ class ConfigEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFo
   /**
    * {@inheritdoc}
    */
-  public function getNewValues(FormStateInterface $form_state) {
+  public function getValues(FormStateInterface $form_state) {
     // If entity must have a prefix
     // (e.g. entity_form_mode, entity_view_mode, ...).
     $field_prefix = '';
diff --git a/src/EntityClone/Config/ConfigWithFieldEntityClone.php b/src/EntityClone/Config/ConfigWithFieldEntityClone.php
index 6b66e9a..7cdb093 100644
--- a/src/EntityClone/Config/ConfigWithFieldEntityClone.php
+++ b/src/EntityClone/Config/ConfigWithFieldEntityClone.php
@@ -13,7 +13,7 @@ class ConfigWithFieldEntityClone extends ConfigEntityCloneBase {
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []) {
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
     $cloned_entity = parent::cloneEntity($entity, $cloned_entity, $properties);
     $bundle_of = $cloned_entity->getEntityType()->getBundleOf();
     if ($bundle_of) {
@@ -40,16 +40,16 @@ class ConfigWithFieldEntityClone extends ConfigEntityCloneBase {
    *
    * @param string $entity_id
    *   The base entity ID.
-   * @param $cloned_entity_id
+   * @param string $cloned_entity_id
    *   The cloned entity ID.
-   * @param $bundle_of
+   * @param string $bundle_of
    *   The bundle of the cloned entity.
    */
   protected function cloneFields($entity_id, $cloned_entity_id, $bundle_of) {
     /** @var \Drupal\Core\Entity\EntityFieldManager $field_manager */
     $field_manager = \Drupal::service('entity_field.manager');
     $fields = $field_manager->getFieldDefinitions($bundle_of, $entity_id);
-    foreach ($fields as $field_id => $field_definition) {
+    foreach ($fields as $field_definition) {
       if ($field_definition instanceof FieldConfigInterface) {
         if ($this->entityTypeManager->hasHandler($this->entityTypeManager->getDefinition($field_definition->getEntityTypeId())
           ->id(), 'entity_clone')
@@ -77,14 +77,14 @@ class ConfigWithFieldEntityClone extends ConfigEntityCloneBase {
    *   The type of display (view or form).
    * @param string $entity_id
    *   The base entity ID.
-   * @param $cloned_entity_id
+   * @param string $cloned_entity_id
    *   The cloned entity ID.
    * @param array $view_displays
    *   All view available display for this type.
-   * @param $bundle_of
+   * @param string $bundle_of
    *   The bundle of the cloned entity.
    */
-  protected function cloneDisplays($type, $entity_id, $cloned_entity_id, $view_displays, $bundle_of) {
+  protected function cloneDisplays($type, $entity_id, $cloned_entity_id, array $view_displays, $bundle_of) {
     foreach ($view_displays as $view_display_id => $view_display) {
       /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
       $display = $this->entityTypeManager->getStorage('entity_' . $type . '_display')->load($bundle_of . '.' . $entity_id . '.' . $view_display_id);
diff --git a/src/EntityClone/Config/FieldConfigEntityClone.php b/src/EntityClone/Config/FieldConfigEntityClone.php
index a4fb270..b16d1ed 100644
--- a/src/EntityClone/Config/FieldConfigEntityClone.php
+++ b/src/EntityClone/Config/FieldConfigEntityClone.php
@@ -12,7 +12,7 @@ class FieldConfigEntityClone extends ConfigEntityCloneBase {
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $field_config, EntityInterface $cloned_field_config, $properties = []) {
+  public function cloneEntity(EntityInterface $field_config, EntityInterface $cloned_field_config, array $properties = []) {
     /** @var \Drupal\field\Entity\FieldConfig $field_config */
     /** @var \Drupal\field\Entity\FieldConfig $cloned_field_config */
     /** @var \Drupal\field\Entity\FieldStorageConfig $cloned_field_storage */
diff --git a/src/EntityClone/Content/ContentEntityCloneBase.php b/src/EntityClone/Content/ContentEntityCloneBase.php
index 6712f6b..98c3ba6 100644
--- a/src/EntityClone/Content/ContentEntityCloneBase.php
+++ b/src/EntityClone/Content/ContentEntityCloneBase.php
@@ -6,6 +6,10 @@ 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\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldConfigInterface;
+use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\entity_clone\EntityClone\EntityCloneInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -54,15 +58,136 @@ class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInter
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []) {
-    /** @var \Drupal\core\Entity\ContentEntityInterface $cloned_entity */
-    if ($label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label')) {
-      $cloned_entity->set($label_key, $entity->label() . ' - Cloned');
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    // Clone referenced entities.
+    if ($cloned_entity instanceof FieldableEntityInterface && $entity instanceof FieldableEntityInterface) {
+      foreach ($cloned_entity->getFieldDefinitions() as $field_id => $field_definition) {
+        if ($this->fieldIsClonable($field_definition)) {
+          $field = $entity->get($field_id);
+          /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
+          if ($field->count() > 0) {
+            $cloned_entity->set($field_id, $this->cloneReferencedEntities($field, $field_definition, $properties));
+          }
+        }
+      }
     }
 
+    $this->setClonedEntityLabel($entity, $cloned_entity);
     $cloned_entity->save();
-
     return $cloned_entity;
   }
 
+  /**
+   * Determines if a field is clonable.
+   *
+   * @param Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition.
+   *
+   * @return bool
+   *   TRUE if th field is clonable; FALSE otherwise.
+   */
+  protected function fieldIsClonable(FieldDefinitionInterface $field_definition) {
+    $clonable_field_types = [
+      'entity_reference',
+      'entity_reference_revisions',
+    ];
+
+    $type_is_clonable = in_array($field_definition->getType(), $clonable_field_types);
+    if (($field_definition instanceof FieldConfigInterface) && $type_is_clonable) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Sets the cloned entity's label.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $original_entity
+   *   The original entity.
+   * @param \Drupal\Core\Entity\EntityInterface $cloned_entity
+   *   The entity cloned from the original.
+   */
+  protected function setClonedEntityLabel(EntityInterface $original_entity, EntityInterface $cloned_entity) {
+    $label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label');
+    if ($label_key) {
+      $cloned_entity->set($label_key, $original_entity->label() . ' - Cloned');
+    }
+  }
+
+  /**
+   * Clone referenced entities.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $field
+   *   The field item.
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param array $properties
+   *   All new properties to replace old.
+   *
+   * @return array
+   *   Referenced entities.
+   */
+  protected function cloneReferencedEntities(FieldItemListInterface $field, FieldConfigInterface $field_definition, array $properties) {
+    $referenced_entities = [];
+    foreach ($field as $value) {
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */
+      $referenced_entity = $value->get('entity')->getTarget()->getValue();
+      if (isset($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['clone'])
+        && $properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['clone']) {
+
+        $cloned_reference = $referenced_entity->createDuplicate();
+        /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $entity_clone_handler */
+        $entity_clone_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone');
+        $child_properties = $this->getChildProperties($properties, $field_definition, $referenced_entity);
+        $entity_clone_handler->cloneEntity($referenced_entity, $cloned_reference, $child_properties);
+
+        $referenced_entities[] = $this->formatTargetIdsAsArray($cloned_reference->id(), $cloned_reference->getRevisionId());
+      }
+      else {
+        $referenced_entities[] = $this->formatTargetIdsAsArray($referenced_entity->id(), $referenced_entity->getRevisionId());
+      }
+    }
+    return $referenced_entities;
+  }
+
+  /**
+   * Fetches the properties of a child entity.
+   *
+   * @param array $properties
+   *   Properties of the clone operation.
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param \Drupal\Core\Entity\EntityInterface $referenced_entity
+   *   The field's target entity.
+   */
+  protected function getChildProperties(array $properties, FieldConfigInterface $field_definition, EntityInterface $referenced_entity) {
+    $child_properties = [];
+    if (isset($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['children'])) {
+      $child_properties = $properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['children'];
+    }
+    return $child_properties;
+  }
+
+  /**
+   * Formats the target and revision IDs as an array.
+   *
+   * @param int $target_id
+   *   The target ID.
+   * @param int $target_revision_id
+   *   The target revision ID.
+   *
+   * @return array
+   *   The IDs are returned in this form:
+   *     [
+   *       'target_id' => THE_PROVIDED_TARGET_ID,
+   *       'target_revision_id' => THE_PROVIDED_TARGET_REVISION_ID,
+   *     ]
+   */
+  protected function formatTargetIdsAsArray($target_id, $target_revision_id) {
+    return [
+      'target_id' => $target_id,
+      'target_revision_id' => $target_revision_id,
+    ];
+  }
+
 }
diff --git a/src/EntityClone/Content/ContentEntityCloneFormBase.php b/src/EntityClone/Content/ContentEntityCloneFormBase.php
new file mode 100644
index 0000000..04027ed
--- /dev/null
+++ b/src/EntityClone/Content/ContentEntityCloneFormBase.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManager;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldConfigInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Drupal\entity_clone\EntityClone\EntityCloneFormInterface;
+use Drupal\entity_clone\EntityCloneSettingsManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ContentEntityCloneFormBase.
+ */
+class ContentEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFormInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translationManager;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManager
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity clone settings manager service.
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager
+   */
+  protected $entityCloneSettingsManager;
+
+  /**
+   * Constructs a new ContentEntityCloneFormBase.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $translation_manager
+   *   The string translation manager.
+   * @param \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager
+   *   The entity clone settings manager.
+   */
+  public function __construct(EntityTypeManager $entity_type_manager, TranslationManager $translation_manager, EntityCloneSettingsManager $entity_clone_settings_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->translationManager = $translation_manager;
+    $this->entityCloneSettingsManager = $entity_clone_settings_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation'),
+      $container->get('entity_clone.settings.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(EntityInterface $entity) {
+    $form = [];
+
+    if ($entity instanceof FieldableEntityInterface) {
+      foreach ($entity->getFieldDefinitions() as $field_id => $field_definition) {
+        if ($field_definition instanceof FieldConfigInterface && in_array($field_definition->getType(), ['entity_reference', 'entity_reference_revisions'])) {
+          $field = $entity->get($field_id);
+          /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
+          if ($field->count() > 0) {
+            $form['recursive'] = $this->getRecursiveFormElement($field_definition, $field_id, $field);
+          }
+        }
+      }
+    }
+
+    return $form;
+  }
+
+  /**
+   * Get the recursive form element.
+   *
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param string $field_id
+   *   The field ID.
+   * @param \Drupal\Core\Field\FieldItemListInterface $field
+   *   The field item.
+   *
+   * @return array
+   *   The form element for a recursive clone.
+   */
+  protected function getRecursiveFormElement(FieldConfigInterface $field_definition, $field_id, FieldItemListInterface $field) {
+    $form_element = [
+      '#tree' => TRUE,
+    ];
+
+    $form_element[$field_definition->id()] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Entities referenced by field <em>@label (@field_id)</em>.', [
+        '@label' => $field_definition->label(),
+        '@field_id' => $field_id,
+      ]),
+      '#access' => !$this->entityCloneSettingsManager->getHiddenValue($field_definition->getFieldStorageDefinition()->getSetting('target_type')),
+    ];
+
+    foreach ($field as $value) {
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */
+      $referenced_entity = $value->get('entity')->getTarget()->getValue();
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Clone entity <strong>ID:</strong> <em>@entity_id</em>, <strong>Type:</strong> <em>@entity_type - @bundle</em>, <strong>Label:</strong> <em>@entity_label</em>', [
+          '@entity_id' => $referenced_entity->id(),
+          '@entity_type' => $referenced_entity->getEntityTypeId(),
+          '@bundle' => $referenced_entity->bundle(),
+          '@entity_label' => $referenced_entity->label(),
+        ]),
+        '#default_value' => $this->entityCloneSettingsManager->getDefaultValue($referenced_entity->getEntityTypeId()),
+        '#attributes' => [
+          'disabled' => $this->entityCloneSettingsManager->getDisableValue($referenced_entity->getEntityTypeId()),
+        ],
+      ];
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['target_entity_type_id'] = [
+        '#type' => 'hidden',
+        '#value' => $referenced_entity->getEntityTypeId(),
+      ];
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['target_bundle'] = [
+        '#type' => 'hidden',
+        '#value' => $referenced_entity->bundle(),
+      ];
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['children'] = $this->getChildren($referenced_entity);
+    }
+
+    return $form_element;
+  }
+
+  /**
+   * Fetches clonable children from a field.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $referenced_entity
+   *   The field item list.
+   *
+   * @return array
+   *   The list of children.
+   */
+  protected function getChildren(ContentEntityInterface $referenced_entity) {
+    /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */
+    if ($this->entityTypeManager->hasHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form')) {
+      $entity_clone_form_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form');
+      return $entity_clone_form_handler->formElement($referenced_entity);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValues(FormStateInterface $form_state) {
+    return $form_state->getValues();
+  }
+
+}
diff --git a/src/EntityClone/Content/FileEntityClone.php b/src/EntityClone/Content/FileEntityClone.php
index b15924e..fb552c4 100644
--- a/src/EntityClone/Content/FileEntityClone.php
+++ b/src/EntityClone/Content/FileEntityClone.php
@@ -12,7 +12,7 @@ class FileEntityClone extends ContentEntityCloneBase {
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []) {
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
     /** @var \Drupal\file\FileInterface $cloned_entity */
     $cloned_file = file_copy($cloned_entity, $cloned_entity->getFileUri(), FILE_EXISTS_RENAME);
     return parent::cloneEntity($entity, $cloned_file, $properties);
diff --git a/src/EntityClone/Content/TaxonomyTermEntityClone.php b/src/EntityClone/Content/TaxonomyTermEntityClone.php
new file mode 100644
index 0000000..15388c6
--- /dev/null
+++ b/src/EntityClone/Content/TaxonomyTermEntityClone.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Class TaxonomyTermEntityClone.
+ */
+class TaxonomyTermEntityClone extends ContentEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    /** @var \Drupal\core\Entity\ContentEntityInterface $cloned_entity */
+
+    // Enforce a parent if the cloned term doesn't have a parent.
+    // (First level of a taxonomy tree).
+    if (!isset($cloned_entity->parent->target_id)) {
+      $cloned_entity->set('parent', 0);
+    }
+    return parent::cloneEntity($entity, $cloned_entity, $properties);
+  }
+
+}
diff --git a/src/EntityClone/Content/UserEntityClone.php b/src/EntityClone/Content/UserEntityClone.php
index 25c9979..68e64b4 100644
--- a/src/EntityClone/Content/UserEntityClone.php
+++ b/src/EntityClone/Content/UserEntityClone.php
@@ -12,7 +12,7 @@ class UserEntityClone extends ContentEntityCloneBase {
   /**
    * {@inheritdoc}
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []) {
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
     /** @var \Drupal\user\UserInterface $cloned_entity */
     $cloned_entity->set('name', $cloned_entity->getAccountName() . '_cloned');
     return parent::cloneEntity($entity, $cloned_entity, $properties);
diff --git a/src/EntityClone/EntityCloneFormInterface.php b/src/EntityClone/EntityCloneFormInterface.php
index 2ac5622..a30c217 100644
--- a/src/EntityClone/EntityCloneFormInterface.php
+++ b/src/EntityClone/EntityCloneFormInterface.php
@@ -30,6 +30,6 @@ interface EntityCloneFormInterface {
    * @return array
    *   An array containing all new values.
    */
-  public function getNewValues(FormStateInterface $form_state);
+  public function getValues(FormStateInterface $form_state);
 
 }
diff --git a/src/EntityClone/EntityCloneInterface.php b/src/EntityClone/EntityCloneInterface.php
index c8e0949..ed02023 100644
--- a/src/EntityClone/EntityCloneInterface.php
+++ b/src/EntityClone/EntityCloneInterface.php
@@ -19,9 +19,9 @@ interface EntityCloneInterface {
    * @param array $properties
    *   All new properties to replace old.
    *
-   * @return \Drupal\Core\Entity\EntityInterface The new saved entity.
-   * The new saved entity.
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The new saved entity.
    */
-  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, $properties = []);
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []);
 
 }
diff --git a/src/EntityClonePermissions.php b/src/EntityClonePermissions.php
index 3057d57..1a74df4 100644
--- a/src/EntityClonePermissions.php
+++ b/src/EntityClonePermissions.php
@@ -26,7 +26,6 @@ class EntityClonePermissions implements ContainerInjectionInterface {
    */
   protected $translationManager;
 
-
   /**
    * Constructs a new EntityClonePermissions instance.
    *
diff --git a/src/EntityCloneSettingsManager.php b/src/EntityCloneSettingsManager.php
new file mode 100644
index 0000000..f578ebb
--- /dev/null
+++ b/src/EntityCloneSettingsManager.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\entity_clone;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+
+/**
+ * Manage entity clone configuration.
+ */
+class EntityCloneSettingsManager {
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity type bundle info service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * The immutable entity clone settings configuration entity.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig
+   */
+  protected $config;
+
+  /**
+   * The editable entity clone settings configuration entity.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $editableConfig;
+
+  /**
+   * EntityCloneSettingsManager constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   *   The entity type bundle info service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, ConfigFactoryInterface $config_factory) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    $this->config = $config_factory->get('entity_clone.settings');
+    $this->editableConfig = $config_factory->getEditable('entity_clone.settings');
+  }
+
+  /**
+   * Get all content entity types.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityTypeInterface[]
+   *   An array containing all content entity types.
+   */
+  public function getContentEntityTypes($allowed_only = FALSE) {
+    $definitions = $this->entityTypeManager->getDefinitions();
+    $ret = [];
+    foreach ($definitions as $machine => $type) {
+      if ($type instanceof ContentEntityTypeInterface) {
+        $ret[$machine] = $type;
+      }
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Set the entity clone settings.
+   *
+   * @param array $settings
+   *   The settings from the form.
+   */
+  public function setFormSettings(array $settings) {
+    if (isset($settings['table'])) {
+      array_walk_recursive($settings['table'], function (&$item) {
+        if ($item == '1') {
+          $item = TRUE;
+        }
+        else {
+          $item = FALSE;
+        }
+      });
+      $this->editableConfig->set('form_settings', $settings['table'])->save();
+    }
+  }
+
+  /**
+   * Get the checkbox default value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The default value.
+   */
+  public function getDefaultValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['default_value'])) {
+      return $form_settings[$entity_type_id]['default_value'];
+    }
+    return FALSE;
+  }
+
+  /**
+   * Get the checkbox disable value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The disable value.
+   */
+  public function getDisableValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['disable'])) {
+      return $form_settings[$entity_type_id]['disable'];
+    }
+    return FALSE;
+  }
+
+  /**
+   * Get the checkbox hidden value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The hidden value.
+   */
+  public function getHiddenValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['hidden'])) {
+      return $form_settings[$entity_type_id]['hidden'];
+    }
+    return FALSE;
+  }
+
+}
diff --git a/src/Form/EntityCloneForm.php b/src/Form/EntityCloneForm.php
index 8ca215f..6884880 100644
--- a/src/Form/EntityCloneForm.php
+++ b/src/Form/EntityCloneForm.php
@@ -102,11 +102,26 @@ class EntityCloneForm extends FormBase {
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
     if ($this->entity && $this->entityTypeDefinition->hasHandlerClass('entity_clone')) {
-      $form['information'] = [
-        '#markup' => $this->stringTranslationManager->translate('<p>Do you want clone the <em>@entity_type</em> entity named <em>@title</em>?</p>', [
-          '@entity_type' => $this->entity->getEntityType()->getLabel(),
-          '@title' => $this->entity->label(),
-        ]),
+
+      $form['description'] = [
+        '#markup' => t("
+          <p>Specify the child entities (the entities referenced by this entity) that should also be cloned as part of
+          the cloning process.  If they're not included, these fields' referenced entities will be the same as in the
+          original.  In other words, fields in both the original entity and the cloned entity will refer to the same
+          referenced entity.  Examples:</p>
+
+          <p>If you have a Paragraph field in your entity, and you choose not to clone it here, deleting the original
+          or cloned entity will also delete the Paragraph field from the other one.  So you probably want to clone
+          Paragraph fields.</p>
+
+          <p>However, if you have a User reference field, you probably don't want to clone it here because a new User
+          will be created for referencing by the clone.</p>
+
+          <p>Some options may be disabled here, preventing you from changing them, as set by your administrator.  Some
+          options may also be missing, hidden by your administrator, forcing you to clone with the default settings.
+          It's possible that there are no options here for you at all, or none need to be set, in which case you may
+          simply hit the <em>Clone</em> button.</p>
+        "),
       ];
 
       /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */
@@ -139,6 +154,7 @@ class EntityCloneForm extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+
     /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $entity_clone_handler */
     $entity_clone_handler = $this->entityTypeManager->getHandler($this->entityTypeDefinition->id(), 'entity_clone');
     if ($this->entityTypeManager->hasHandler($this->entityTypeDefinition->id(), 'entity_clone_form')) {
@@ -147,7 +163,7 @@ class EntityCloneForm extends FormBase {
 
     $properties = [];
     if (isset($entity_clone_form_handler) && $entity_clone_form_handler) {
-      $properties = $entity_clone_form_handler->getNewValues($form_state);
+      $properties = $entity_clone_form_handler->getValues($form_state);
     }
 
     $duplicate = $this->entity->createDuplicate();
diff --git a/src/Form/EntityCloneSettingsForm.php b/src/Form/EntityCloneSettingsForm.php
new file mode 100644
index 0000000..9dde82b
--- /dev/null
+++ b/src/Form/EntityCloneSettingsForm.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\entity_clone\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_clone\EntityCloneSettingsManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provide the settings form for entity clone.
+ */
+class EntityCloneSettingsForm extends ConfigFormBase implements ContainerInjectionInterface {
+
+  /**
+   * The entity clone settings manager.
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager
+   */
+  protected $entityCloneSettingsManager;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, EntityCloneSettingsManager $entity_clone_settings_manager) {
+    parent::__construct($config_factory);
+    $this->entityCloneSettingsManager = $entity_clone_settings_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('entity_clone.settings.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEditableConfigNames() {
+    return ['entity_clone.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'entity_clone_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['#tree'] = TRUE;
+
+    $form['form_settings'] = [
+      '#tree' => TRUE,
+      '#type' => 'fieldset',
+      '#title' => $this->t('Clone form settings'),
+      '#description' => $this->t("
+        For each type of child entity (the entity that's referenced by the entity being
+        cloned), please set your cloning preferences. This will affect the clone form presented to users when they
+        clone entities. Default behaviour for whether or not the child entities should be cloned is specified in
+        the left-most column.  To prevent users from altering behaviour for each type when they're actually cloning
+        (but still allowing them to see what will happen), use the middle column. The right-most column can be used
+        to hide the form options from users completely. This will run the clone operation with the defaults set here
+        (in the left-most column). See the clone form (by cloning an entity) for more information.
+      "),
+      '#open' => TRUE,
+      '#collapsible' => FALSE,
+    ];
+
+    $form['form_settings']['table'] = [
+      '#type' => 'table',
+      '#header' => [
+        'label' => $this->t('Label'),
+        'default_value' => $this->t('Checkboxes default value'),
+        'disable'  => $this->t('Disable checkboxes'),
+        'hidden' => $this->t('Hide checkboxes'),
+      ],
+    ];
+
+    foreach ($this->entityCloneSettingsManager->getContentEntityTypes() as $type_id => $type) {
+      $form['form_settings']['table'][$type_id] = [
+        'label' => [
+          '#type' => 'label',
+          '#title' => $this->t('@type', [
+            '@type' => $type->getLabel(),
+          ]),
+        ],
+        'default_value' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getDefaultValue($type_id),
+        ],
+        'disable' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getDisableValue($type_id),
+        ],
+        'hidden' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getHiddenValue($type_id),
+        ],
+      ];
+    }
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entityCloneSettingsManager->setFormSettings($form_state->getValue('form_settings'));
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/src/Plugin/Derivative/DynamicLocalTasks.php b/src/Plugin/Derivative/DynamicLocalTasks.php
index 44e1689..5dd716b 100644
--- a/src/Plugin/Derivative/DynamicLocalTasks.php
+++ b/src/Plugin/Derivative/DynamicLocalTasks.php
@@ -62,12 +62,12 @@ class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface
       $has_canonical_path = $entity_type->hasLinkTemplate('canonical');
 
       if ($has_clone_path) {
-        $this->derivatives["$entity_type_id.clone_tab"] = array(
+        $this->derivatives["$entity_type_id.clone_tab"] = [
           'route_name' => "entity.$entity_type_id.clone_form",
           'title' => $this->translationManager->translate('Clone'),
           'base_route' => "entity.$entity_type_id." . ($has_canonical_path ? "canonical" : "edit_form"),
           'weight' => 100,
-        );
+        ];
       }
     }
 
diff --git a/src/Tests/EntityCloneActionTest.php b/src/Tests/EntityCloneActionTest.php
index 5c97890..f530531 100644
--- a/src/Tests/EntityCloneActionTest.php
+++ b/src/Tests/EntityCloneActionTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneActionTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\Component\Utility\Crypt;
@@ -31,11 +26,13 @@ class EntityCloneActionTest extends WebTestBase {
    */
   protected $permissions = [
     'administer actions',
-    'clone action entity'
+    'clone action entity',
   ];
 
   /**
-   * An administrative user with permission to configure actions settings.
+   * An administrative user.
+   *
+   * With permission to configure actions settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -51,6 +48,9 @@ class EntityCloneActionTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone an action entity.
+   */
   public function testActionEntityClone() {
     foreach (\Drupal::service('plugin.manager.action')->getDefinitions() as $id => $definition) {
       if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface') && $definition['label'] == 'Send email') {
@@ -91,4 +91,3 @@ class EntityCloneActionTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneBlockTest.php b/src/Tests/EntityCloneBlockTest.php
index 07295d5..291226f 100644
--- a/src/Tests/EntityCloneBlockTest.php
+++ b/src/Tests/EntityCloneBlockTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneBlockTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\block\Entity\Block;
@@ -31,11 +26,13 @@ class EntityCloneBlockTest extends WebTestBase {
    */
   protected $permissions = [
     'administer blocks',
-    'clone block entity'
+    'clone block entity',
   ];
 
   /**
-   * An administrative user with permission to configure blocks settings.
+   * An administrative user.
+   *
+   * With permission to configure blocks settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -51,6 +48,9 @@ class EntityCloneBlockTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a block entity.
+   */
   public function testBlockEntityClone() {
     $config = \Drupal::configFactory();
     $block = Block::create([
@@ -79,4 +79,3 @@ class EntityCloneBlockTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneCommentTest.php b/src/Tests/EntityCloneCommentTest.php
index 33ce635..f7c5995 100644
--- a/src/Tests/EntityCloneCommentTest.php
+++ b/src/Tests/EntityCloneCommentTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneCommentTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\comment\Tests\CommentTestBase;
@@ -24,7 +19,15 @@ class EntityCloneCommentTest extends CommentTestBase {
    *
    * @var array
    */
-  public static $modules = ['entity_clone', 'block', 'comment', 'node', 'history', 'field_ui', 'datetime'];
+  public static $modules = [
+    'entity_clone',
+    'block',
+    'comment',
+    'node',
+    'history',
+    'field_ui',
+    'datetime',
+  ];
 
   /**
    * Permissions to grant admin user.
@@ -42,7 +45,7 @@ class EntityCloneCommentTest extends CommentTestBase {
     'access comments',
     'access user profiles',
     'access content',
-    'clone comment entity'
+    'clone comment entity',
   ];
 
   /**
@@ -55,6 +58,9 @@ class EntityCloneCommentTest extends CommentTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a comment entity.
+   */
   public function testCommentEntityClone() {
     $subject = 'Test comment for clone';
     $body = $this->randomMachineName();
@@ -72,4 +78,4 @@ class EntityCloneCommentTest extends CommentTestBase {
     $this->assertTrue($comments, 'Test comment cloned found in database.');
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/Tests/EntityCloneContentRecursiveTest.php b/src/Tests/EntityCloneContentRecursiveTest.php
new file mode 100644
index 0000000..f883ac6
--- /dev/null
+++ b/src/Tests/EntityCloneContentRecursiveTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\node\Entity\Node;
+use Drupal\node\Tests\NodeTestBase;
+use Drupal\taxonomy\Entity\Term;
+
+/**
+ * Create a content and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneContentRecursiveTest extends NodeTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block', 'node', 'datetime'];
+
+  /**
+   * Profile to install.
+   *
+   * @var string
+   */
+  protected $profile = 'standard';
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'bypass node access',
+    'administer nodes',
+    'clone node entity',
+  ];
+
+  /**
+   * A user with permission to bypass content access checks.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Test clone a content entity with another entities attached.
+   */
+  public function testContentEntityClone() {
+
+    $term_title = $this->randomMachineName(8);
+    $term = Term::create([
+      'vid' => 'tags',
+      'name' => $term_title,
+    ]);
+    $term->save();
+
+    $node_title = $this->randomMachineName(8);
+    $node = Node::create([
+      'type' => 'article',
+      'title' => $node_title,
+      'field_tags' => [
+        'target_id' => $term->id(),
+      ],
+    ]);
+    $node->save();
+
+    $this->drupalPostForm('entity_clone/node/' . $node->id(), [
+      'recursive[node.article.field_tags][references][' . $term->id() . '][clone]' => 1,
+    ], t('Clone'));
+
+    $nodes = \Drupal::entityTypeManager()
+      ->getStorage('node')
+      ->loadByProperties([
+        'title' => $node_title . ' - Cloned',
+      ]);
+    $node = reset($nodes);
+    $this->assertTrue($node, 'Test node cloned found in database.');
+
+    $terms = \Drupal::entityTypeManager()
+      ->getStorage('taxonomy_term')
+      ->loadByProperties([
+        'name' => $term_title . ' - Cloned',
+      ]);
+    $term = reset($terms);
+    $this->assertTrue($term, 'Test term referenced by node cloned too found in database.');
+
+  }
+
+}
diff --git a/src/Tests/EntityCloneContentTest.php b/src/Tests/EntityCloneContentTest.php
index 0e751fe..93982d4 100644
--- a/src/Tests/EntityCloneContentTest.php
+++ b/src/Tests/EntityCloneContentTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneContentTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\node\Entity\Node;
@@ -32,7 +27,7 @@ class EntityCloneContentTest extends NodeTestBase {
   protected $permissions = [
     'bypass node access',
     'administer nodes',
-    'clone node entity'
+    'clone node entity',
   ];
 
   /**
@@ -52,6 +47,9 @@ class EntityCloneContentTest extends NodeTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a content entity.
+   */
   public function testContentEntityClone() {
     $node_title = $this->randomMachineName(8);
     $node = Node::create([
@@ -71,4 +69,4 @@ class EntityCloneContentTest extends NodeTestBase {
     $this->assertTrue($node, 'Test node cloned found in database.');
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/Tests/EntityCloneCustomBlockTest.php b/src/Tests/EntityCloneCustomBlockTest.php
index 6da9265..abce81d 100644
--- a/src/Tests/EntityCloneCustomBlockTest.php
+++ b/src/Tests/EntityCloneCustomBlockTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneCustomBlockTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\block_content\Tests\BlockContentTestBase;
@@ -41,9 +36,12 @@ class EntityCloneCustomBlockTest extends BlockContentTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a custom block entity.
+   */
   public function testCustomBlockEntityClone() {
 
-    $edit = array();
+    $edit = [];
     $edit['info[0][value]'] = 'Test block ready to clone';
     $edit['body[0][value]'] = $this->randomMachineName(16);
     $this->drupalPostForm('block/add/basic', $edit, t('Save'));
@@ -68,4 +66,4 @@ class EntityCloneCustomBlockTest extends BlockContentTestBase {
     $this->assertTrue($block, 'Test Block cloned found in database.');
   }
 
-}
\ No newline at end of file
+}
diff --git a/src/Tests/EntityCloneDateFormatTest.php b/src/Tests/EntityCloneDateFormatTest.php
index 40a7442..1269e31 100644
--- a/src/Tests/EntityCloneDateFormatTest.php
+++ b/src/Tests/EntityCloneDateFormatTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneDateFormatTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneDateFormatTest extends WebTestBase {
    */
   protected $permissions = [
     'clone date_format entity',
-    'administer site configuration'
+    'administer site configuration',
   ];
 
   /**
-   * An administrative user with permission to configure date formats settings.
+   * An administrative user.
+   *
+   * With permission to configure date formats settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneDateFormatTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a date format entity.
+   */
   public function testDateFormatEntityClone() {
     $edit = [
       'label' => 'Test date format for clone',
@@ -81,4 +81,3 @@ class EntityCloneDateFormatTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneEntityFormModeTest.php b/src/Tests/EntityCloneEntityFormModeTest.php
index 68235fe..c0e6549 100644
--- a/src/Tests/EntityCloneEntityFormModeTest.php
+++ b/src/Tests/EntityCloneEntityFormModeTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneEntityFormModeTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -29,11 +24,13 @@ class EntityCloneEntityFormModeTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone entity_form_mode entity'
+    'clone entity_form_mode entity',
   ];
 
   /**
-   * An administrative user with permission to configure entity form modes settings.
+   * An administrative user.
+   *
+   * With permission to configure entity form modes settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -49,6 +46,9 @@ class EntityCloneEntityFormModeTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a form mode entity.
+   */
   public function testEntityFormModeEntityClone() {
     $entity_form_modes = \Drupal::entityTypeManager()
       ->getStorage('entity_form_mode')
@@ -73,4 +73,3 @@ class EntityCloneEntityFormModeTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneEntityViewModeTest.php b/src/Tests/EntityCloneEntityViewModeTest.php
index 7876807..0cb4f88 100644
--- a/src/Tests/EntityCloneEntityViewModeTest.php
+++ b/src/Tests/EntityCloneEntityViewModeTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneEntityViewModeTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -29,11 +24,13 @@ class EntityCloneEntityViewModeTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone entity_view_mode entity'
+    'clone entity_view_mode entity',
   ];
 
   /**
-   * An administrative user with permission to configure entity view modes settings.
+   * An administrative user.
+   *
+   * With permission to configure entity view modes settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -49,6 +46,9 @@ class EntityCloneEntityViewModeTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a view mode entity.
+   */
   public function testEntityViewModeEntityClone() {
     $entity_view_modes = \Drupal::entityTypeManager()
       ->getStorage('entity_view_mode')
@@ -73,4 +73,3 @@ class EntityCloneEntityViewModeTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneFileTest.php b/src/Tests/EntityCloneFileTest.php
index d477aa0..e6bf5e6 100644
--- a/src/Tests/EntityCloneFileTest.php
+++ b/src/Tests/EntityCloneFileTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneFileTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\file\Entity\File;
@@ -30,11 +25,13 @@ class EntityCloneFileTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone file entity'
+    'clone file entity',
   ];
 
   /**
-   * An administrative user with permission to configure files settings.
+   * An administrative user.
+   *
+   * With permission to configure files settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,15 +47,18 @@ class EntityCloneFileTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a file entity.
+   */
   public function testFileEntityClone() {
     /** @var \Drupal\file\FileInterface $file */
-    $file = File::create(array(
+    $file = File::create([
       'uid' => 1,
       'filename' => 'druplicon.txt',
       'uri' => 'public://druplicon.txt',
       'filemime' => 'text/plain',
       'status' => FILE_STATUS_PERMANENT,
-    ));
+    ]);
     file_put_contents($file->getFileUri(), 'hello world');
     $file->save();
 
@@ -76,4 +76,3 @@ class EntityCloneFileTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneFilterFormatTest.php b/src/Tests/EntityCloneFilterFormatTest.php
index c458e23..dcb253a 100644
--- a/src/Tests/EntityCloneFilterFormatTest.php
+++ b/src/Tests/EntityCloneFilterFormatTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneFilterFormatTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneFilterFormatTest extends WebTestBase {
    */
   protected $permissions = [
     'clone filter_format entity',
-    'administer filters'
+    'administer filters',
   ];
 
   /**
-   * An administrative user with permission to configure filter formats settings.
+   * An administrative user.
+   *
+   * With permission to configure filters settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneFilterFormatTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a filter format entity.
+   */
   public function testFilterFormatEntityClone() {
     $edit = [
       'name' => 'Test filter format for clone',
@@ -80,4 +80,3 @@ class EntityCloneFilterFormatTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneImageStyleTest.php b/src/Tests/EntityCloneImageStyleTest.php
index 648e227..75e0dde 100644
--- a/src/Tests/EntityCloneImageStyleTest.php
+++ b/src/Tests/EntityCloneImageStyleTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneImageStyleTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneImageStyleTest extends WebTestBase {
    */
   protected $permissions = [
     'clone image_style entity',
-    'administer image styles'
+    'administer image styles',
   ];
 
   /**
-   * An administrative user with permission to configure image styles settings.
+   * An administrative user.
+   *
+   * With permission to configure image styles settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneImageStyleTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone an image style entity.
+   */
   public function testImageStyleEntityClone() {
     $edit = [
       'label' => 'Test image style for clone',
@@ -80,4 +80,3 @@ class EntityCloneImageStyleTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneLanguageTest.php b/src/Tests/EntityCloneLanguageTest.php
index bffba19..0ba35fa 100644
--- a/src/Tests/EntityCloneLanguageTest.php
+++ b/src/Tests/EntityCloneLanguageTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneLanguageTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneLanguageTest extends WebTestBase {
    */
   protected $permissions = [
     'administer languages',
-    'clone configurable_language entity'
+    'clone configurable_language entity',
   ];
 
   /**
-   * An administrative user with permission to configure languages settings.
+   * An administrative user.
+   *
+   * With permission to configure languages settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,9 +47,12 @@ class EntityCloneLanguageTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a language entity.
+   */
   public function testLanguageEntityClone() {
     $edit = [
-      'predefined_langcode' => 'fr'
+      'predefined_langcode' => 'fr',
     ];
     $this->drupalPostForm("/admin/config/regional/language/add", $edit, t('Add language'));
 
@@ -79,4 +79,3 @@ class EntityCloneLanguageTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneMenuTest.php b/src/Tests/EntityCloneMenuTest.php
index 81c393d..d822dcc 100644
--- a/src/Tests/EntityCloneMenuTest.php
+++ b/src/Tests/EntityCloneMenuTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneMenuTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneMenuTest extends WebTestBase {
    */
   protected $permissions = [
     'clone menu entity',
-    'administer menu'
+    'administer menu',
   ];
 
   /**
-   * An administrative user with permission to configure menus settings.
+   * An administrative user.
+   *
+   * With permission to configure menus settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneMenuTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a menu entity.
+   */
   public function testMenuEntityClone() {
 
     $menus = \Drupal::entityTypeManager()
@@ -75,4 +75,3 @@ class EntityCloneMenuTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneResponsiveImageStyleTest.php b/src/Tests/EntityCloneResponsiveImageStyleTest.php
index d6385fa..74f9ce8 100644
--- a/src/Tests/EntityCloneResponsiveImageStyleTest.php
+++ b/src/Tests/EntityCloneResponsiveImageStyleTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneResponsiveImageStyleTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneResponsiveImageStyleTest extends WebTestBase {
    */
   protected $permissions = [
     'clone responsive_image_style entity',
-    'administer responsive images'
+    'administer responsive images',
   ];
 
   /**
-   * An administrative user with permission to configure image styles settings.
+   * An administrative user.
+   *
+   * With permission to configure image styles settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneResponsiveImageStyleTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a responsive image style entity.
+   */
   public function testResponsiveImageStyleEntityClone() {
     $edit = [
       'label' => 'Test responsive image style for clone',
@@ -82,4 +82,3 @@ class EntityCloneResponsiveImageStyleTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneRoleTest.php b/src/Tests/EntityCloneRoleTest.php
index b551305..dcc0206 100644
--- a/src/Tests/EntityCloneRoleTest.php
+++ b/src/Tests/EntityCloneRoleTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneRoleTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneRoleTest extends WebTestBase {
    */
   protected $permissions = [
     'administer permissions',
-    'clone user_role entity'
+    'clone user_role entity',
   ];
 
   /**
-   * An administrative user with permission to configure roles settings.
+   * An administrative user.
+   *
+   * With permission to configure roles settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneRoleTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a role entity.
+   */
   public function testRoleEntityClone() {
     $edit = [
       'label' => 'Test role for clone',
@@ -80,4 +80,3 @@ class EntityCloneRoleTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneSearchPageTest.php b/src/Tests/EntityCloneSearchPageTest.php
index 24cee53..0cb6b42 100644
--- a/src/Tests/EntityCloneSearchPageTest.php
+++ b/src/Tests/EntityCloneSearchPageTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneSearchPageTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -30,11 +25,13 @@ class EntityCloneSearchPageTest extends WebTestBase {
    */
   protected $permissions = [
     'administer search',
-    'clone search_page entity'
+    'clone search_page entity',
   ];
 
   /**
-   * An administrative user with permission to configure search pages settings.
+   * An administrative user.
+   *
+   * With permission to configure search pages settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -50,6 +47,9 @@ class EntityCloneSearchPageTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a search page entity.
+   */
   public function testSearchPageEntityClone() {
     $edit = [
       'label' => 'Test search page for clone',
@@ -81,4 +81,3 @@ class EntityCloneSearchPageTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneShortcutSetTest.php b/src/Tests/EntityCloneShortcutSetTest.php
index 46c8c0e..c5ba517 100644
--- a/src/Tests/EntityCloneShortcutSetTest.php
+++ b/src/Tests/EntityCloneShortcutSetTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneShortcutSetTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -29,11 +24,13 @@ class EntityCloneShortcutSetTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone shortcut_set entity'
+    'clone shortcut_set entity',
   ];
 
   /**
-   * An administrative user with permission to configure shortcuts settings.
+   * An administrative user.
+   *
+   * With permission to configure shortcuts settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -49,6 +46,9 @@ class EntityCloneShortcutSetTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a shortcut entity.
+   */
   public function testShortcutSetEntityClone() {
     $edit = [
       'id' => 'test_shortcut_set_cloned',
@@ -66,4 +66,3 @@ class EntityCloneShortcutSetTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneUserTest.php b/src/Tests/EntityCloneUserTest.php
index 1de2d15..ab52a5e 100644
--- a/src/Tests/EntityCloneUserTest.php
+++ b/src/Tests/EntityCloneUserTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneUserTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -29,11 +24,13 @@ class EntityCloneUserTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone user entity'
+    'clone user entity',
   ];
 
   /**
-   * An administrative user with permission to configure users settings.
+   * An administrative user.
+   *
+   * With permission to configure users settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -49,6 +46,9 @@ class EntityCloneUserTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a user entity.
+   */
   public function testUserEntityClone() {
     $this->drupalPostForm('entity_clone/user/' . $this->adminUser->id(), [], t('Clone'));
 
@@ -62,4 +62,3 @@ class EntityCloneUserTest extends WebTestBase {
   }
 
 }
-
diff --git a/src/Tests/EntityCloneViewTest.php b/src/Tests/EntityCloneViewTest.php
index 900ad05..5ccbf35 100644
--- a/src/Tests/EntityCloneViewTest.php
+++ b/src/Tests/EntityCloneViewTest.php
@@ -1,10 +1,5 @@
 <?php
 
-/**
- * @file
- * Definition of Drupal\entity_clone\Tests\EntityCloneViewTest.
- */
-
 namespace Drupal\entity_clone\Tests;
 
 use Drupal\simpletest\WebTestBase;
@@ -29,11 +24,13 @@ class EntityCloneViewTest extends WebTestBase {
    * @var array
    */
   protected $permissions = [
-    'clone view entity'
+    'clone view entity',
   ];
 
   /**
-   * An administrative user with permission to configure views settings.
+   * An administrative user.
+   *
+   * With permission to configure views settings.
    *
    * @var \Drupal\user\UserInterface
    */
@@ -49,6 +46,9 @@ class EntityCloneViewTest extends WebTestBase {
     $this->drupalLogin($this->adminUser);
   }
 
+  /**
+   * Test clone a view entity.
+   */
   public function testViewEntityClone() {
     $edit = [
       'id' => 'test_view_cloned',
@@ -66,4 +66,3 @@ class EntityCloneViewTest extends WebTestBase {
   }
 
 }
-
-- 
2.11.0

