diff --git a/composer.lock b/composer.lock index 38fb2fbb4d62c4103d60cfd04fc6cfb2252ce7d6..9390dd2fce09dfcc5be44e574bd5ea5682d826c6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "80a5fefd459bde7852dfd2301ce78a0e", + "content-hash": "b50b24db351327a76dc297fa85641300", "packages": [ { "name": "alchemy/zippy", @@ -4456,17 +4456,17 @@ }, { "name": "drupal/entity_clone", - "version": "1.0.0-beta1", + "version": "1.0.0-beta3", "source": { "type": "git", "url": "https://git.drupalcode.org/project/entity_clone.git", - "reference": "8.x-1.0-beta1" + "reference": "8.x-1.0-beta3" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta1.zip", - "reference": "8.x-1.0-beta1", - "shasum": "d0ace20bbe1672fbe6d0cdd6da8f5b80cd156643" + "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta3.zip", + "reference": "8.x-1.0-beta3", + "shasum": "011ca7b8e2af77594f2d9df41954d5a950b0958c" }, "require": { "drupal/core": "~8.0" @@ -4477,8 +4477,8 @@ "dev-1.x": "1.x-dev" }, "drupal": { - "version": "8.x-1.0-beta1", - "datestamp": "1546583284", + "version": "8.x-1.0-beta3", + "datestamp": "1551868984", "security-coverage": { "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." @@ -4498,7 +4498,7 @@ "description": "Add a clone action for all entities", "homepage": "https://www.drupal.org/project/entity_clone", "support": { - "source": "http://cgit.drupalcode.org/entity_clone" + "source": "https://git.drupalcode.org/project/entity_clone" } }, { @@ -5471,6 +5471,9 @@ "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." } + }, + "patches_applied": { + "2712951": "https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch" } }, "notification-url": "https://packages.drupal.org/8/downloads", diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 7508cad86ed1dba316f08775ea266007e4eafec4..9ca848dc7dea5779c81f3fbf949395c44bc08107 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -4589,18 +4589,18 @@ }, { "name": "drupal/entity_clone", - "version": "1.0.0-beta1", - "version_normalized": "1.0.0.0-beta1", + "version": "1.0.0-beta3", + "version_normalized": "1.0.0.0-beta3", "source": { "type": "git", "url": "https://git.drupalcode.org/project/entity_clone.git", - "reference": "8.x-1.0-beta1" + "reference": "8.x-1.0-beta3" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta1.zip", - "reference": "8.x-1.0-beta1", - "shasum": "d0ace20bbe1672fbe6d0cdd6da8f5b80cd156643" + "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta3.zip", + "reference": "8.x-1.0-beta3", + "shasum": "011ca7b8e2af77594f2d9df41954d5a950b0958c" }, "require": { "drupal/core": "~8.0" @@ -4611,8 +4611,8 @@ "dev-1.x": "1.x-dev" }, "drupal": { - "version": "8.x-1.0-beta1", - "datestamp": "1546583284", + "version": "8.x-1.0-beta3", + "datestamp": "1551868984", "security-coverage": { "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." @@ -4633,7 +4633,7 @@ "description": "Add a clone action for all entities", "homepage": "https://www.drupal.org/project/entity_clone", "support": { - "source": "http://cgit.drupalcode.org/entity_clone" + "source": "https://git.drupalcode.org/project/entity_clone" } }, { diff --git a/web/modules/entity_clone/README.txt b/web/modules/entity_clone/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..8f1ed3c7aceee8f8ec135a23a11ee692b8d59f72 --- /dev/null +++ b/web/modules/entity_clone/README.txt @@ -0,0 +1,14 @@ +README.txt +========== + +What is this module? +-------------------- + +This module add a new entity operation which allows to clone many +of the entities (config & content) provided by the Drupal core. + +How does it work? +----------------- + +The module add a new operation on each entity in all manage lists. +Just click on the clone button to process the clone operation. diff --git a/web/modules/entity_clone/entity_clone.info.yml b/web/modules/entity_clone/entity_clone.info.yml index 4f70fcb5ee80bce7d4600dd58d029cd100ef0c70..828e2f8e9a02acb29ad6d2864dcffd4f140d3b1d 100644 --- a/web/modules/entity_clone/entity_clone.info.yml +++ b/web/modules/entity_clone/entity_clone.info.yml @@ -4,8 +4,8 @@ description: Add a clone action for all entities type: module configure: entity_clone.settings -# Information added by Drupal.org packaging script on 2019-01-04 -version: '8.x-1.0-beta1' +# Information added by Drupal.org packaging script on 2019-03-06 +version: '8.x-1.0-beta3' core: '8.x' project: 'entity_clone' -datestamp: 1546583287 +datestamp: 1551869001 diff --git a/web/modules/entity_clone/entity_clone.module b/web/modules/entity_clone/entity_clone.module index 7b6a3a919abde6c18d4dad747ae684c5aa4066ef..a27058fe3a4faf9df14c0a538128f6fe87e8683b 100644 --- a/web/modules/entity_clone/entity_clone.module +++ b/web/modules/entity_clone/entity_clone.module @@ -5,10 +5,12 @@ * Contains entity_clone.module. */ +use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneBase; use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase; use Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone; @@ -44,54 +46,52 @@ function entity_clone_entity_type_build(array &$entity_types) { $specific_handler = [ 'file' => [ 'entity_clone' => FileEntityClone::class, - 'entity_clone_form' => ContentEntityCloneFormBase::class, ], 'user' => [ 'entity_clone' => UserEntityClone::class, - 'entity_clone_form' => ContentEntityCloneFormBase::class, ], 'field_config' => [ 'entity_clone' => FieldConfigEntityClone::class, - 'entity_clone_form' => ConfigEntityCloneFormBase::class, ], 'node_type' => [ 'entity_clone' => ConfigWithFieldEntityClone::class, - 'entity_clone_form' => ConfigEntityCloneFormBase::class, ], 'comment_type' => [ 'entity_clone' => ConfigWithFieldEntityClone::class, - 'entity_clone_form' => ConfigEntityCloneFormBase::class, ], 'block_content_type' => [ 'entity_clone' => ConfigWithFieldEntityClone::class, - 'entity_clone_form' => ConfigEntityCloneFormBase::class, ], 'contact_form' => [ 'entity_clone' => ConfigWithFieldEntityClone::class, - 'entity_clone_form' => ConfigEntityCloneFormBase::class, ], 'taxonomy_term' => [ 'entity_clone' => TaxonomyTermEntityClone::class, - 'entity_clone_form' => ContentEntityCloneFormBase::class, ], ]; /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ - foreach ($entity_types as &$entity_type) { - if (isset($specific_handler[$entity_type->id()])) { - $entity_type->setHandlerClass('entity_clone', $specific_handler[$entity_type->id()]['entity_clone']); - if (isset($specific_handler[$entity_type->id()]['entity_clone_form'])) { - $entity_type->setHandlerClass('entity_clone_form', $specific_handler[$entity_type->id()]['entity_clone_form']); + foreach ($entity_types as $entity_type_id => $entity_type) { + $has_entity_clone_handler = $entity_type->getHandlerClass('entity_clone'); + if (!$has_entity_clone_handler) { + if ($entity_type instanceof ContentEntityTypeInterface) { + $entity_type->setHandlerClass('entity_clone', ContentEntityCloneBase::class); + $entity_type->setHandlerClass('entity_clone_form', ContentEntityCloneFormBase::class); + } + elseif ($entity_type instanceof ConfigEntityTypeInterface) { + $entity_type->setHandlerClass('entity_clone', ConfigEntityCloneBase::class); + $entity_type->setHandlerClass('entity_clone_form', ConfigEntityCloneFormBase::class); } } - elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ContentEntityTypeInterface) { - $entity_type->setHandlerClass('entity_clone', ContentEntityCloneBase::class); - $entity_type->setHandlerClass('entity_clone_form', ContentEntityCloneFormBase::class); + + if (isset($specific_handler[$entity_type->id()]['entity_clone'])) { + $entity_type->setHandlerClass('entity_clone', $specific_handler[$entity_type->id()]['entity_clone']); } - elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ConfigEntityTypeInterface) { - $entity_type->setHandlerClass('entity_clone', ConfigEntityCloneBase::class); - $entity_type->setHandlerClass('entity_clone_form', ConfigEntityCloneFormBase::class); + if (isset($specific_handler[$entity_type->id()]['entity_clone_form'])) { + $entity_type->setHandlerClass('entity_clone_form', $specific_handler[$entity_type->id()]['entity_clone_form']); } + + $entity_type->setLinkTemplate('clone-form', "/entity_clone/$entity_type_id/{{$entity_type_id}}"); } } @@ -108,7 +108,7 @@ function entity_clone_entity_type_build(array &$entity_types) { * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations() */ function entity_clone_entity_operation(EntityInterface $entity) { - if ($entity->hasLinkTemplate('clone-form')) { + if ($entity->hasLinkTemplate('clone-form') && $entity->access('clone')) { return [ 'clone' => [ 'title' => t('Clone'), @@ -122,11 +122,11 @@ function entity_clone_entity_operation(EntityInterface $entity) { } /** - * Implements hook_entity_type_alter(). + * Implements hook_entity_access(). */ -function entity_clone_entity_type_alter(array &$entity_types) { - /** @var \Drupal\Core\Entity\EntityType[] $entity_types */ - foreach ($entity_types as $entity_type_id => $entity_type) { - $entity_type->setLinkTemplate('clone-form', "/entity_clone/$entity_type_id/{{$entity_type_id}}"); +function entity_clone_entity_access(EntityInterface $entity, $operation, AccountInterface $account) { + if ($operation === 'clone') { + return AccessResult::allowedIfHasPermission($account, 'clone ' . $entity->getEntityTypeId() . ' entity'); } + return AccessResult::neutral(); } diff --git a/web/modules/entity_clone/entity_clone.permissions.yml b/web/modules/entity_clone/entity_clone.permissions.yml index 6e259f5f594a6b1543e836dbc1cb38fc6c964773..4069893036e65dbf124983813722edc6d93b12b8 100644 --- a/web/modules/entity_clone/entity_clone.permissions.yml +++ b/web/modules/entity_clone/entity_clone.permissions.yml @@ -1,2 +1,5 @@ +administer entity clone: + title: 'Administer entity clone' + permission_callbacks: - Drupal\entity_clone\EntityClonePermissions::permissions diff --git a/web/modules/entity_clone/entity_clone.post_update.php b/web/modules/entity_clone/entity_clone.post_update.php index 67349bb564ebca59939235e9c9f0f17ca95df699..08f25a5a6fc74d835ab338d65d2a05a2abf3fdab 100644 --- a/web/modules/entity_clone/entity_clone.post_update.php +++ b/web/modules/entity_clone/entity_clone.post_update.php @@ -8,17 +8,20 @@ /** * Populates new entity_clone form settings. */ -function entity_clone_post_update_populate_form_settings() { - /** @var \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager */ - $entity_clone_settings_manager = \Drupal::service('entity_clone.settings.manager'); - $form_settings = []; - foreach (array_keys($entity_clone_settings_manager->getContentEntityTypes()) as $entity_type_id) { - $form_settings[$entity_type_id] = [ - 'default_value' => FALSE, - 'disable' => FALSE, - 'hidden' => FALSE, - ]; - } +function entity_clone_post_update_populate_form_settings3() { + $form_settings = \Drupal::configFactory()->get('entity_clone.settings')->get('form_settings'); + if (!$form_settings) { + /** @var \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager */ + $entity_clone_settings_manager = \Drupal::service('entity_clone.settings.manager'); + $form_settings = []; + foreach (array_keys($entity_clone_settings_manager->getContentEntityTypes()) as $entity_type_id) { + $form_settings[$entity_type_id] = [ + 'default_value' => FALSE, + 'disable' => FALSE, + 'hidden' => FALSE, + ]; + } - \Drupal::configFactory()->getEditable('entity_clone.settings')->set('form_settings', $form_settings)->save(); + \Drupal::configFactory()->getEditable('entity_clone.settings')->set('form_settings', $form_settings)->save(); + } } diff --git a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php index b3b7de1faef6ec638597f47fa7e6cb42600da4f7..dea3e7bdf8de2de4c067d1ed4ad0d6ad45b8629f 100644 --- a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php +++ b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php @@ -58,15 +58,17 @@ public static function createInstance(ContainerInterface $container, EntityTypeI /** * {@inheritdoc} */ - public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) { + public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = [], array &$already_cloned = []) { // Clone referenced entities. + $cloned_entity->save(); + $already_cloned[$entity->getEntityTypeId()][$entity->id()] = $cloned_entity; 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)); + $cloned_entity->set($field_id, $this->cloneReferencedEntities($field, $field_definition, $properties, $already_cloned)); } } } @@ -109,7 +111,7 @@ protected function fieldIsClonable(FieldDefinitionInterface $field_definition) { */ protected function setClonedEntityLabel(EntityInterface $original_entity, EntityInterface $cloned_entity) { $label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label'); - if ($label_key) { + if ($label_key && $cloned_entity->hasField($label_key)) { $cloned_entity->set($label_key, $original_entity->label() . ' - Cloned'); } } @@ -123,29 +125,37 @@ protected function setClonedEntityLabel(EntityInterface $original_entity, Entity * The field definition. * @param array $properties * All new properties to replace old. + * @param array $already_cloned + * List of all already cloned entities, used for circular references. * * @return array * Referenced entities. */ - protected function cloneReferencedEntities(FieldItemListInterface $field, FieldConfigInterface $field_definition, array $properties) { + protected function cloneReferencedEntities(FieldItemListInterface $field, FieldConfigInterface $field_definition, array $properties, array &$already_cloned) { $referenced_entities = []; foreach ($field as $value) { - // Check if we're not dealing with an entity that has been deleted in the meantime + // Check if we're not dealing with an entity + // that has been deleted in the meantime. if (!$referenced_entity = $value->get('entity')->getTarget()) { continue; } /** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */ $referenced_entity = $value->get('entity')->getTarget()->getValue(); - if (!empty($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['clone'])) { + $child_properties = $this->getChildProperties($properties, $field_definition, $referenced_entity); + if (!empty($child_properties['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); + $entity_clone_handler->cloneEntity($referenced_entity, $cloned_reference, $child_properties['children'], $already_cloned); $referenced_entities[] = $cloned_reference; } + elseif (!empty($child_properties['is_circular'])) { + if (!empty($already_cloned[$referenced_entity->getEntityTypeId()][$referenced_entity->id()])) { + $referenced_entities[] = $already_cloned[$referenced_entity->getEntityTypeId()][$referenced_entity->id()]; + } + } else { $referenced_entities[] = $referenced_entity; } @@ -168,8 +178,11 @@ protected function cloneReferencedEntities(FieldItemListInterface $field, FieldC */ 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']; + if (isset($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()])) { + $child_properties = $properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]; + } + if (!isset($child_properties['children'])) { + $child_properties['children'] = []; } return $child_properties; } diff --git a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php index f4e5be8353dc4931c3a7dc174b33728faf9f9b55..1a830993014a291dae718f7c196bebf625452a53 100644 --- a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php +++ b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php @@ -11,7 +11,6 @@ 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; @@ -22,8 +21,6 @@ */ class ContentEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFormInterface { - use StringTranslationTrait; - /** * The entity type manager. * @@ -50,19 +47,23 @@ class ContentEntityCloneFormBase implements EntityHandlerInterface, EntityCloneF * * @var \Drupal\Core\Entity\ContentEntityInterface[] */ - protected $discovered_entities = []; + protected $discoveredEntities = []; /** * Constructs a new ContentEntityCloneFormBase. * - * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $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(EntityTypeManagerInterface $entity_type_manager, TranslationManager $translation_manager, EntityCloneSettingsManager $entity_clone_settings_manager) { + public function __construct( + EntityTypeManagerInterface $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; @@ -82,18 +83,19 @@ public static function createInstance(ContainerInterface $container, EntityTypeI /** * {@inheritdoc} */ - public function formElement(EntityInterface $entity, $parent = TRUE) { + public function formElement(EntityInterface $entity, $parent = TRUE, &$discovered_entities = []) { $form = [ - 'recursive' => [] + 'recursive' => [], ]; if ($entity instanceof FieldableEntityInterface) { + $discovered_entities[$entity->getEntityTypeId()][$entity->id()] = $entity; foreach ($entity->getFieldDefinitions() as $field_id => $field_definition) { if ($field_definition instanceof FieldConfigInterface && in_array($field_definition->getType(), ['entity_reference', 'entity_reference_revisions'], TRUE)) { $field = $entity->get($field_id); /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */ if ($field->count() > 0) { - $form['recursive'] = array_merge($form['recursive'], $this->getRecursiveFormElement($field_definition, $field_id, $field)); + $form['recursive'] = array_merge($form['recursive'], $this->getRecursiveFormElement($field_definition, $field_id, $field, $discovered_entities)); } } } @@ -101,25 +103,8 @@ public function formElement(EntityInterface $entity, $parent = TRUE) { if ($parent) { $form = array_merge([ 'description' => [ - '#markup' => $this->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> - "), - '#access' => $this->descriptionShouldBeShown($form), + '#markup' => $this->getFormDescription($form, $entity), + '#access' => TRUE, ], ], $form); } @@ -137,11 +122,13 @@ public function formElement(EntityInterface $entity, $parent = TRUE) { * The field ID. * @param \Drupal\Core\Field\FieldItemListInterface $field * The field item. + * @param array $discovered_entities + * List of all entities already discovered. * * @return array * The form element for a recursive clone. */ - protected function getRecursiveFormElement(FieldConfigInterface $field_definition, $field_id, FieldItemListInterface $field) { + protected function getRecursiveFormElement(FieldConfigInterface $field_definition, $field_id, FieldItemListInterface $field, array &$discovered_entities) { $form_element = [ '#tree' => TRUE, ]; @@ -149,7 +136,7 @@ protected function getRecursiveFormElement(FieldConfigInterface $field_definitio $fieldset_access = !$this->entityCloneSettingsManager->getHiddenValue($field_definition->getFieldStorageDefinition()->getSetting('target_type')); $form_element[$field_definition->id()] = [ '#type' => 'fieldset', - '#title' => $this->t('Entities referenced by field <em>@label (@field_id)</em>.', [ + '#title' => $this->translationManager->translate('Entities referenced by field <em>@label (@field_id)</em>.', [ '@label' => $field_definition->label(), '@field_id' => $field_id, ]), @@ -158,43 +145,56 @@ protected function getRecursiveFormElement(FieldConfigInterface $field_definitio ]; foreach ($field as $value) { - // Check if we're not dealing with an entity that has been deleted in the meantime + // Check if we're not dealing with an entity + // That has been deleted in the meantime. if (!$referenced_entity = $value->get('entity')->getTarget()) { continue; } /** @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()), - '#access' => $referenced_entity->access('view label'), - ]; + if (isset($discovered_entities[$referenced_entity->getEntityTypeId()]) && array_key_exists($referenced_entity->id(), $discovered_entities[$referenced_entity->getEntityTypeId()])) { + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['is_circular'] = [ + '#type' => 'hidden', + '#value' => TRUE, + ]; + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['circular'] = [ + '#type' => 'item', + '#markup' => $this->translationManager->translate('Circular reference detected'), + ]; + } + else { + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone'] = [ + '#type' => 'checkbox', + '#title' => $this->translationManager->translate('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()), + '#access' => $referenced_entity->access('view label'), + ]; if ($this->entityCloneSettingsManager->getDisableValue($referenced_entity->getEntityTypeId())) { $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#attributes'] = [ 'disabled' => TRUE, + ]; + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#value'] = $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#default_value']; + } + + $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()]['clone']['#value'] = $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#default_value']; - } - $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(), - ]; - if ($referenced_entity instanceOf ContentEntityInterface) { - $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['children'] = $this->getChildren($referenced_entity); + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['target_bundle'] = [ + '#type' => 'hidden', + '#value' => $referenced_entity->bundle(), + ]; + if ($referenced_entity instanceof ContentEntityInterface) { + $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['children'] = $this->getChildren($referenced_entity, $discovered_entities); + } } } @@ -206,26 +206,23 @@ protected function getRecursiveFormElement(FieldConfigInterface $field_definitio * * @param \Drupal\Core\Entity\ContentEntityInterface $referenced_entity * The field item list. + * @param array $discovered_entities + * List of all entities already discovered. * * @return array * The list of children. */ - protected function getChildren(ContentEntityInterface $referenced_entity) { - // Use memoization to prevent circular references. - if (array_key_exists($referenced_entity->id(), $this->discovered_entities)) { - return; - } - + protected function getChildren(ContentEntityInterface $referenced_entity, array &$discovered_entities) { /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */ if ($this->entityTypeManager->hasHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form')) { // Record that we've found this entity. - $this->discovered_entities[$referenced_entity->id()] = $referenced_entity; + $discovered_entities[$referenced_entity->getEntityTypeId()][$referenced_entity->id()] = $referenced_entity; $entity_clone_form_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form'); - return $entity_clone_form_handler->formElement($referenced_entity, FALSE); + return $entity_clone_form_handler->formElement($referenced_entity, FALSE, $discovered_entities); } - return; + return []; } /** @@ -236,34 +233,60 @@ public function getValues(FormStateInterface $form_state) { } /** - * Checks if description should be shown. + * Get the clone form confirmation page description. * - * If there are no recursive elements visible, the description should be - * hidden. + * If there are no recursive elements visible, the default description should + * be shown. * * @param array $form - * Form. + * The clone form. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. * - * @return bool - * TRUE if description should be shown. + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * Description to be shown */ - protected function descriptionShouldBeShown(array $form) { - $show_description = TRUE; - if (!isset($form['recursive'])) { - $show_description = FALSE; + protected function getFormDescription(array $form, EntityInterface $entity) { + $has_recursive = FALSE; + $elements_visible = FALSE; + + if (isset($form['recursive'])) { + $has_recursive = TRUE; } - $visible = FALSE; - array_walk_recursive($form['recursive'], function ($item, $key) use (&$visible) { + array_walk_recursive($form['recursive'], function ($item, $key) use (&$elements_visible) { if ($key === '#description_should_be_shown') { - $visible = ($visible || $item); + $elements_visible = ($elements_visible || $item); } }); - if (!$visible) { - $show_description = FALSE; + if ($has_recursive && $elements_visible) { + return $this->translationManager->translate(" + <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> + "); } - return $show_description; + else { + return $this->translationManager->translate("<p>Do you want to clone the <em>@type</em> entity named <em>@title</em></p>", [ + "@type" => $entity->getEntityType()->getLabel(), + "@title" => $entity->label(), + ]); + } + } } diff --git a/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php index fb552c46312f8a8facc47fe6f857bd068d7710fc..b1a6fca480342ee64f30784713726ff46cce282e 100644 --- a/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php +++ b/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php @@ -12,7 +12,7 @@ class FileEntityClone extends ContentEntityCloneBase { /** * {@inheritdoc} */ - public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) { + public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = [], array &$already_cloned = []) { /** @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/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php index 15388c666fe74bc02bdea30e7e11c81014abeab8..3e4da6a36d6e7c75dc22f2a71ede502a7d93c275 100644 --- a/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php +++ b/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php @@ -12,7 +12,7 @@ class TaxonomyTermEntityClone extends ContentEntityCloneBase { /** * {@inheritdoc} */ - public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) { + public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = [], array &$already_cloned = []) { /** @var \Drupal\core\Entity\ContentEntityInterface $cloned_entity */ // Enforce a parent if the cloned term doesn't have a parent. diff --git a/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php index 68e64b49b3598f75450d89753e5204bc33b94676..844deca3ed0c5cb35ab7404a7a98a9f15c94a601 100644 --- a/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php +++ b/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php @@ -12,7 +12,7 @@ class UserEntityClone extends ContentEntityCloneBase { /** * {@inheritdoc} */ - public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) { + public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = [], array &$already_cloned = []) { /** @var \Drupal\user\UserInterface $cloned_entity */ $cloned_entity->set('name', $cloned_entity->getAccountName() . '_cloned'); return parent::cloneEntity($entity, $cloned_entity, $properties); diff --git a/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php b/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php index 55346c1f89b08f56fd077076502f97a9fa30f379..c1656a71ff5d8d47c997d7731613f2f040eb49b8 100644 --- a/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php +++ b/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php @@ -15,7 +15,6 @@ interface EntityCloneFormInterface { * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. - * * @param bool $parent * Is the parent form element. * diff --git a/web/modules/entity_clone/src/EntityClonePermissions.php b/web/modules/entity_clone/src/EntityClonePermissions.php index 3057d57d9a7f942bff4cfebe88ec20f9a8fd86a1..1a74df43ddf032115eb36c8b0da18955a98e6e75 100644 --- a/web/modules/entity_clone/src/EntityClonePermissions.php +++ b/web/modules/entity_clone/src/EntityClonePermissions.php @@ -26,7 +26,6 @@ class EntityClonePermissions implements ContainerInjectionInterface { */ protected $translationManager; - /** * Constructs a new EntityClonePermissions instance. * diff --git a/web/modules/entity_clone/src/Form/EntityCloneForm.php b/web/modules/entity_clone/src/Form/EntityCloneForm.php index 8e7e658ca654d0ff242b7dc8c64d9649d53bebe3..f97adba7828dd3bf52595768c00455cdf974826d 100644 --- a/web/modules/entity_clone/src/Form/EntityCloneForm.php +++ b/web/modules/entity_clone/src/Form/EntityCloneForm.php @@ -6,7 +6,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; +use Drupal\Core\Messenger\Messenger; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslationManager; use Drupal\entity_clone\Event\EntityCloneEvent; @@ -54,6 +54,13 @@ class EntityCloneForm extends FormBase { */ protected $eventDispatcher; + /** + * The messenger service. + * + * @var \Drupal\Core\Messenger\Messenger + */ + protected $messenger; + /** * Constructs a new Entity Clone form. * @@ -65,13 +72,16 @@ class EntityCloneForm extends FormBase { * The string translation manager. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher * The event dispatcher service. + * @param \Drupal\Core\Messenger\Messenger $messenger + * The messenger service. * * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match, TranslationManager $string_translation, EventDispatcherInterface $eventDispatcher) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match, TranslationManager $string_translation, EventDispatcherInterface $eventDispatcher, Messenger $messenger) { $this->entityTypeManager = $entity_type_manager; $this->stringTranslationManager = $string_translation; $this->eventDispatcher = $eventDispatcher; + $this->messenger = $messenger; $parameter_name = $route_match->getRouteObject()->getOption('_entity_clone_entity_type_id'); $this->entity = $route_match->getParameter($parameter_name); @@ -87,7 +97,8 @@ public static function create(ContainerInterface $container) { $container->get('entity_type.manager'), $container->get('current_route_match'), $container->get('string_translation'), - $container->get('event_dispatcher') + $container->get('event_dispatcher'), + $container->get('messenger') ); } @@ -112,12 +123,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['clone'] = [ '#type' => 'submit', - '#value' => 'Clone', + '#value' => $this->stringTranslationManager->translate('Clone'), ]; $form['abort'] = [ '#type' => 'submit', - '#value' => 'Abort', + '#value' => $this->stringTranslationManager->translate('Abort'), '#submit' => ['::cancelForm'], ]; } @@ -151,7 +162,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $cloned_entity = $entity_clone_handler->cloneEntity($this->entity, $duplicate, $properties); $this->eventDispatcher->dispatch(EntityCloneEvents::POST_CLONE, new EntityCloneEvent($this->entity, $duplicate, $properties)); - drupal_set_message($this->stringTranslationManager->translate('The entity <em>@entity (@entity_id)</em> of type <em>@type</em> was cloned', [ + $this->messenger->addMessage($this->stringTranslationManager->translate('The entity <em>@entity (@entity_id)</em> of type <em>@type</em> was cloned', [ '@entity' => $this->entity->label(), '@entity_id' => $this->entity->id(), '@type' => $this->entity->getEntityTypeId(), diff --git a/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php b/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php index 44e1689a263ef7f31307695207856b3398fc96d6..5dd716bb979deef598c5e5f8ed8418e50d73da23 100644 --- a/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php +++ b/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php @@ -62,12 +62,12 @@ public function getDerivativeDefinitions($base_plugin_definition) { $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/web/modules/entity_clone/src/Routing/RouteSubscriber.php b/web/modules/entity_clone/src/Routing/RouteSubscriber.php index f3bf092f73d2837e765e04055d26f031368b256f..5c892ee5151925ee033df17c3a63877223ee64e7 100644 --- a/web/modules/entity_clone/src/Routing/RouteSubscriber.php +++ b/web/modules/entity_clone/src/Routing/RouteSubscriber.php @@ -61,7 +61,7 @@ protected function getEntityCloneRoute(EntityTypeInterface $entity_type) { '_title' => 'Clone ' . $entity_type->getLabel(), ]) ->addRequirements([ - '_permission' => 'clone ' . $entity_type->id() . ' entity', + '_entity_access' => $entity_type_id . '.clone', ]) ->setOption('_entity_clone_entity_type_id', $entity_type_id) ->setOption('_admin_route', TRUE) @@ -69,6 +69,16 @@ protected function getEntityCloneRoute(EntityTypeInterface $entity_type) { $entity_type_id => ['type' => 'entity:' . $entity_type_id], ]); + // Special case for menu link content. + // Menu link content does not work properly with custom operation. + // This case must be removed when issue #3016038 + // (https://www.drupal.org/project/drupal/issues/3016038) was closed. + if ($entity_type_id === 'menu_link_content') { + $route->setRequirements([ + '_permission' => 'clone ' . $entity_type_id . ' entity', + ]); + } + return $route; } } diff --git a/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php b/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php index f60728753444b52e2c06f9ab28a8e8a0141e4297..841de729e94ac0a19baba15a0290cddb8b4688ee 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php +++ b/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php @@ -1,10 +1,5 @@ <?php -/** - * @file - * Definition of Drupal\entity_clone\Tests\EntityCloneActionTest. - */ - namespace Drupal\entity_clone\Tests; use Drupal\simpletest\WebTestBase; @@ -30,7 +25,7 @@ class EntityCloneActionTest extends WebTestBase { */ protected $permissions = [ 'administer actions', - 'clone action entity' + 'clone action entity', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test action entity clone. + */ 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') { @@ -90,4 +88,3 @@ public function testActionEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php b/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php index 07295d543147fba4f51f21dcc94e7daf0fba7013..6bccabba55799acff48cc6c6ca33838de8655f57 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php +++ b/web/modules/entity_clone/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,7 +26,7 @@ class EntityCloneBlockTest extends WebTestBase { */ protected $permissions = [ 'administer blocks', - 'clone block entity' + 'clone block entity', ]; /** @@ -51,6 +46,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test block entity clone. + */ public function testBlockEntityClone() { $config = \Drupal::configFactory(); $block = Block::create([ @@ -79,4 +77,3 @@ public function testBlockEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php b/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php index 33ce635a046fce4cce8017ace10876f658cd3c66..f9599340527b9e7e558ce0b63347465db575809a 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php +++ b/web/modules/entity_clone/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 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test comment entity clone. + */ public function testCommentEntityClone() { $subject = 'Test comment for clone'; $body = $this->randomMachineName(); @@ -72,4 +78,4 @@ public function testCommentEntityClone() { $this->assertTrue($comments, 'Test comment cloned found in database.'); } -} \ No newline at end of file +} diff --git a/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveCircularTest.php b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveCircularTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b959587870f04851274fa18e114bce19049f1d82 --- /dev/null +++ b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveCircularTest.php @@ -0,0 +1,143 @@ +<?php + +namespace Drupal\entity_clone\Tests; + +use Drupal\node\Entity\Node; +use Drupal\node\Tests\NodeTestBase; +use Drupal\Tests\field\Traits\EntityReferenceTestTrait; + +/** + * Create a content and test a clone. + * + * @group entity_clone + */ +class EntityCloneContentRecursiveCircularTest extends NodeTestBase { + + use EntityReferenceTestTrait; + + /** + * 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->drupalCreateContentType([ + 'type' => 'test_content_type', + 'name' => 'Test content type', + 'display_submitted' => FALSE, + ]); + + $this->createEntityReferenceField('node', 'test_content_type', 'test_field_reference', 'Test field reference', 'node'); + + $this->adminUser = $this->drupalCreateUser($this->permissions); + $this->drupalLogin($this->adminUser); + } + + /** + * Test clone a content entity with another entities attached. + */ + public function testContentEntityClone() { + + $node1_title = $this->randomMachineName(8); + $node1 = Node::create([ + 'type' => 'test_content_type', + 'title' => $node1_title, + ]); + $node1->save(); + + $node2_title = $this->randomMachineName(8); + $node2 = Node::create([ + 'type' => 'test_content_type', + 'title' => $node2_title, + 'test_field_reference' => $node1, + ]); + $node2->save(); + + $node1->set('test_field_reference', $node2->id()); + $node1->save(); + + $settings = [ + 'node' => [ + 'default_value' => 1, + 'disable' => 0, + 'hidden' => 0, + ], + ]; + \Drupal::service('config.factory')->getEditable('entity_clone.settings')->set('form_settings', $settings)->save(); + + $this->drupalPostForm('entity_clone/node/' . $node1->id(), [], t('Clone')); + + $nodes = \Drupal::entityTypeManager() + ->getStorage('node') + ->loadByProperties([ + 'title' => $node1_title . ' - Cloned', + ]); + /** @var \Drupal\node\Entity\Node $node1cloned */ + $node1cloned = reset($nodes); + $this->assertTrue($node1cloned, 'Node 1 cloned found in database.'); + + $nodes = \Drupal::entityTypeManager() + ->getStorage('node') + ->loadByProperties([ + 'title' => $node2_title . ' - Cloned', + ]); + /** @var \Drupal\node\Entity\Node $node2cloned */ + $node2cloned = reset($nodes); + $this->assertTrue($node2cloned, 'Node 2 cloned found in database.'); + + $reference = $node2cloned->get('test_field_reference')->first()->get('entity')->getTarget()->getValue(); + $this->assertEqual($node1cloned->id(), $reference->id(), "Node 1 reference, from circular reference, is correctly referenced."); + + $node1cloned->delete(); + $node2cloned->delete(); + + $nodes = \Drupal::entityTypeManager() + ->getStorage('node') + ->loadByProperties([ + 'title' => $node1_title, + ]); + $node = reset($nodes); + $this->assertTrue($node, 'Test original node 1 found in database.'); + + $nodes = \Drupal::entityTypeManager() + ->getStorage('node') + ->loadByProperties([ + 'title' => $node2_title, + ]); + $node = reset($nodes); + $this->assertTrue($node, 'Test original node 2 found in database.'); + } + +} diff --git a/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php index 589bfcfb8a472a72bcf9cab30a89e94b47d3943e..3b0ee1d0581ac6b34a61450177a9eef40a47279a 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php +++ b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php @@ -82,7 +82,7 @@ public function testContentEntityClone() { 'default_value' => 1, 'disable' => 0, 'hidden' => 0, - ] + ], ]; \Drupal::service('config.factory')->getEditable('entity_clone.settings')->set('form_settings', $settings)->save(); diff --git a/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php b/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php index 0e751fe2b52fd96f0936df51f810ecc5f7acbe55..bc399cead430483ba66b8807e3a9013307b29d13 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php +++ b/web/modules/entity_clone/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 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test content entity clone. + */ public function testContentEntityClone() { $node_title = $this->randomMachineName(8); $node = Node::create([ @@ -71,4 +69,4 @@ public function testContentEntityClone() { $this->assertTrue($node, 'Test node cloned found in database.'); } -} \ No newline at end of file +} diff --git a/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php b/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php index 6da92651e28bd0327fa3145940b6c15711d77c01..752aeaa30620cbed8caeddc05c626bbde0f910ea 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php +++ b/web/modules/entity_clone/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 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test custom block entity clone. + */ 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 @@ public function testCustomBlockEntityClone() { $this->assertTrue($block, 'Test Block cloned found in database.'); } -} \ No newline at end of file +} diff --git a/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php b/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php index 40a7442128d9be337c44ea97bf7a76c0daca278a..43eb314a461fdf2e9abcb1335a620cb99e76925b 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneDateFormatTest extends WebTestBase { */ protected $permissions = [ 'clone date_format entity', - 'administer site configuration' + 'administer site configuration', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test date format entity clone. + */ public function testDateFormatEntityClone() { $edit = [ 'label' => 'Test date format for clone', @@ -81,4 +79,3 @@ public function testDateFormatEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php b/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php index 68235fe2acfebb4eef0d251b54c0feb42bc802e9..39e857cfcf64cf9399001a756bf03048d9c55dfe 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php +++ b/web/modules/entity_clone/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 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test entity form mode entity clone. + */ public function testEntityFormModeEntityClone() { $entity_form_modes = \Drupal::entityTypeManager() ->getStorage('entity_form_mode') @@ -73,4 +73,3 @@ public function testEntityFormModeEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php b/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php index 78768071a410c20e6d9dc43df4eb886defa139d9..dab6dc9d0d1af1b0bc25a95b3a415bdf202d0691 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php +++ b/web/modules/entity_clone/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 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test entity view mode entity clone. + */ public function testEntityViewModeEntityClone() { $entity_view_modes = \Drupal::entityTypeManager() ->getStorage('entity_view_mode') @@ -73,4 +73,3 @@ public function testEntityViewModeEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php b/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php index d477aa0f9fa7b16ee4f259a4ec158bc7683f5814..3955599c284f91af81cce1c849d436239353e27a 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneFileTest extends WebTestBase { * @var array */ protected $permissions = [ - 'clone file entity' + 'clone file entity', ]; /** @@ -50,15 +45,18 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test file entity clone. + */ 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 +74,3 @@ public function testFileEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php b/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php index c458e2334a2160f84957ed096e8acfca716c9a48..d0d417fdf515f90836218e88e835c4f53d0f3b0f 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php +++ b/web/modules/entity_clone/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 filter formats settings. * * @var \Drupal\user\UserInterface */ @@ -50,6 +47,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test filter format entity clone. + */ public function testFilterFormatEntityClone() { $edit = [ 'name' => 'Test filter format for clone', @@ -80,4 +80,3 @@ public function testFilterFormatEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php index 648e22778470a62641f87c85cbff9a2c73a994db..75698cc637c55764faea9f97026aaf5c29750aef 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneImageStyleTest extends WebTestBase { */ protected $permissions = [ 'clone image_style entity', - 'administer image styles' + 'administer image styles', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test image style entity clone. + */ public function testImageStyleEntityClone() { $edit = [ 'label' => 'Test image style for clone', @@ -80,4 +78,3 @@ public function testImageStyleEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php b/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php index bffba1912764765428f4702b4b6df22ff2cd6935..635ac0ec57554e11b8d8310acf31915797454139 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneLanguageTest extends WebTestBase { */ protected $permissions = [ 'administer languages', - 'clone configurable_language entity' + 'clone configurable_language entity', ]; /** @@ -50,9 +45,12 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test language entity clone. + */ public function testLanguageEntityClone() { $edit = [ - 'predefined_langcode' => 'fr' + 'predefined_langcode' => 'fr', ]; $this->drupalPostForm("/admin/config/regional/language/add", $edit, t('Add language')); @@ -79,4 +77,3 @@ public function testLanguageEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php b/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php index 81c393df06b58bbf0255ab99e9281674b0f8607c..6f4c1a20551ec17544a8f35262bfebeb9ea610fc 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneMenuTest extends WebTestBase { */ protected $permissions = [ 'clone menu entity', - 'administer menu' + 'administer menu', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test menu entity clone. + */ public function testMenuEntityClone() { $menus = \Drupal::entityTypeManager() @@ -75,4 +73,3 @@ public function testMenuEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php index d6385fa89d8347375de3a473ce7729ea574401db..6780b8e07296c2644718421d9b1c33c984213626 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneResponsiveImageStyleTest extends WebTestBase { */ protected $permissions = [ 'clone responsive_image_style entity', - 'administer responsive images' + 'administer responsive images', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test responsive image style entity clone. + */ public function testResponsiveImageStyleEntityClone() { $edit = [ 'label' => 'Test responsive image style for clone', @@ -82,4 +80,3 @@ public function testResponsiveImageStyleEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php index b551305754e669e18424d2f2ab2dacc0e5f92f0c..f411c846c61fb5f02d1c039b4dd03dfc386cd943 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneRoleTest extends WebTestBase { */ protected $permissions = [ 'administer permissions', - 'clone user_role entity' + 'clone user_role entity', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test role entity clone. + */ public function testRoleEntityClone() { $edit = [ 'label' => 'Test role for clone', @@ -80,4 +78,3 @@ public function testRoleEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php b/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php index 24cee534f17e5e7a2a26fb150e3b8ce5e41ae610..5103598d382df5ebf341548f64c222e16a5fa0f5 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php +++ b/web/modules/entity_clone/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,7 +25,7 @@ class EntityCloneSearchPageTest extends WebTestBase { */ protected $permissions = [ 'administer search', - 'clone search_page entity' + 'clone search_page entity', ]; /** @@ -50,6 +45,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test search page entity clone. + */ public function testSearchPageEntityClone() { $edit = [ 'label' => 'Test search page for clone', @@ -81,4 +79,3 @@ public function testSearchPageEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php b/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php index 46c8c0e4a2ed23a55dcb5073b217a1b13cc79ccd..988e9b038ef102191baffefc03a8cbe50fa37962 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php +++ b/web/modules/entity_clone/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,7 +24,7 @@ class EntityCloneShortcutSetTest extends WebTestBase { * @var array */ protected $permissions = [ - 'clone shortcut_set entity' + 'clone shortcut_set entity', ]; /** @@ -49,6 +44,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test shortcut set entity clone. + */ public function testShortcutSetEntityClone() { $edit = [ 'id' => 'test_shortcut_set_cloned', @@ -66,4 +64,3 @@ public function testShortcutSetEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php b/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php index 1de2d157d32571d5aef9987ebddea8a5361eeef5..fa3a6bce5a3ba83fce957cd89510fb1d9dd97455 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php +++ b/web/modules/entity_clone/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,7 +24,7 @@ class EntityCloneUserTest extends WebTestBase { * @var array */ protected $permissions = [ - 'clone user entity' + 'clone user entity', ]; /** @@ -49,6 +44,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test user entity clone. + */ public function testUserEntityClone() { $this->drupalPostForm('entity_clone/user/' . $this->adminUser->id(), [], t('Clone')); @@ -62,4 +60,3 @@ public function testUserEntityClone() { } } - diff --git a/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php b/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php index 900ad05c8882dbc44d338479315b2c830d6ee6a3..a2ffbb4fd310e3c6ddd30bd2ddfb29af5c3c51f4 100644 --- a/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php +++ b/web/modules/entity_clone/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,7 +24,7 @@ class EntityCloneViewTest extends WebTestBase { * @var array */ protected $permissions = [ - 'clone view entity' + 'clone view entity', ]; /** @@ -49,6 +44,9 @@ protected function setUp() { $this->drupalLogin($this->adminUser); } + /** + * Test view entity clone. + */ public function testViewEntityClone() { $edit = [ 'id' => 'test_view_cloned', @@ -66,4 +64,3 @@ public function testViewEntityClone() { } } -