summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2018-01-26 14:18:10 (GMT)
committerAlex Pott2018-01-26 14:18:10 (GMT)
commit3d5592257d80324792c86caa25401a3e5f6a122a (patch)
treee32afc47fb16d9cbf4eb33ff812d0d1f8f8853d8
parentc8dbe3a93657625a9d3e1798092023804b3cb64e (diff)
Issue #2670730 by chr.fritsch, robpowell, alexpott, tstoeckler, Berdir, bojanz, dawehner: Provide a delete action for each content entity type
-rw-r--r--core/core.link_relation_types.yml3
-rw-r--r--core/core.services.yml5
-rw-r--r--core/lib/Drupal/Core/Action/Plugin/Action/DeleteAction.php99
-rw-r--r--core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php21
-rw-r--r--core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityDeleteActionDeriver.php40
-rw-r--r--core/lib/Drupal/Core/Entity/EntityDeleteMultipleAccessCheck.php86
-rw-r--r--core/lib/Drupal/Core/Entity/Form/DeleteMultipleForm.php323
-rw-r--r--core/lib/Drupal/Core/Entity/Routing/AdminHtmlRouteProvider.php10
-rw-r--r--core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php24
-rw-r--r--core/modules/action/tests/src/Functional/BulkFormTest.php2
-rw-r--r--core/modules/comment/comment.routing.yml12
-rw-r--r--core/modules/comment/config/install/system.action.comment_delete_action.yml2
-rw-r--r--core/modules/comment/config/schema/comment.schema.yml2
-rw-r--r--core/modules/comment/src/Entity/Comment.php1
-rw-r--r--core/modules/comment/src/Form/CommentAdminOverview.php6
-rw-r--r--core/modules/comment/src/Form/ConfirmDeleteMultiple.php170
-rw-r--r--core/modules/comment/src/Plugin/Action/DeleteComment.php90
-rw-r--r--core/modules/node/config/install/system.action.node_delete_action.yml2
-rw-r--r--core/modules/node/config/schema/node.schema.yml2
-rw-r--r--core/modules/node/node.routing.yml11
-rw-r--r--core/modules/node/src/Entity/Node.php4
-rw-r--r--core/modules/node/src/Form/DeleteMultiple.php177
-rw-r--r--core/modules/node/src/Plugin/Action/DeleteNode.php91
-rw-r--r--core/modules/node/tests/src/Functional/Views/BulkFormTest.php10
-rw-r--r--core/modules/system/system.post_update.php19
-rw-r--r--core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php4
-rw-r--r--core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php4
-rw-r--r--core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php29
-rw-r--r--core/tests/Drupal/FunctionalTests/Entity/DeleteMultipleFormTest.php157
-rw-r--r--core/tests/Drupal/KernelTests/Core/Action/DeleteActionTest.php85
-rw-r--r--core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php2
-rw-r--r--core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php2
-rw-r--r--core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php2
33 files changed, 988 insertions, 509 deletions
diff --git a/core/core.link_relation_types.yml b/core/core.link_relation_types.yml
index d4ffce3..21cdd64 100644
--- a/core/core.link_relation_types.yml
+++ b/core/core.link_relation_types.yml
@@ -9,6 +9,9 @@ add-page:
delete-form:
uri: https://drupal.org/link-relations/delete-form
description: A form where a resource of this type can be deleted.
+delete-multiple-form:
+ uri: https://drupal.org/link-relations/delete-multiple-form
+ description: A form where multiple resources of this type can be deleted.
revision:
uri: https://drupal.org/link-relations/revision
description: A particular version of this resource.
diff --git a/core/core.services.yml b/core/core.services.yml
index 0fc93c1..9e82af7 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1132,6 +1132,11 @@ services:
arguments: ['@entity_type.manager', '@entity_type.bundle.info']
tags:
- { name: access_check, applies_to: _entity_create_any_access }
+ access_check.entity_delete_multiple:
+ class: Drupal\Core\Entity\EntityDeleteMultipleAccessCheck
+ arguments: ['@entity_type.manager', '@tempstore.private', '@request_stack']
+ tags:
+ - { name: access_check, applies_to: _entity_delete_multiple_access }
access_check.theme:
class: Drupal\Core\Theme\ThemeAccessCheck
arguments: ['@theme_handler']
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/DeleteAction.php b/core/lib/Drupal/Core/Action/Plugin/Action/DeleteAction.php
new file mode 100644
index 0000000..fcf76a4
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/DeleteAction.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TempStore\PrivateTempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Redirects to an entity deletion form.
+ *
+ * @Action(
+ * id = "entity:delete_action",
+ * action_label = @Translation("Delete"),
+ * deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityDeleteActionDeriver",
+ * )
+ */
+class DeleteAction extends EntityActionBase {
+
+ /**
+ * The tempstore object.
+ *
+ * @var \Drupal\Core\TempStore\SharedTempStore
+ */
+ protected $tempStore;
+
+ /**
+ * The current user.
+ *
+ * @var \Drupal\Core\Session\AccountInterface
+ */
+ protected $currentUser;
+
+ /**
+ * Constructs a new DeleteAction object.
+ *
+ * @param array $configuration
+ * A configuration array containing information about the plugin instance.
+ * @param string $plugin_id
+ * The plugin ID for the plugin instance.
+ * @param mixed $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
+ * The tempstore factory.
+ * @param \Drupal\Core\Session\AccountInterface $current_user
+ * Current user.
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
+ $this->currentUser = $current_user;
+ $this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
+
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('entity_type.manager'),
+ $container->get('tempstore.private'),
+ $container->get('current_user')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function executeMultiple(array $entities) {
+ /** @var \Drupal\Core\Entity\EntityInterface[] $entities */
+ $selection = [];
+ foreach ($entities as $entity) {
+ $langcode = $entity->language()->getId();
+ $selection[$entity->id()][$langcode] = $langcode;
+ }
+ $this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $selection);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function execute($object = NULL) {
+ $this->executeMultiple([$object]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+ return $object->access('delete', $account, $return_as_object);
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php
index 29159c2..7a410d6 100644
--- a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php
@@ -6,6 +6,8 @@ use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -13,6 +15,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
abstract class EntityActionDeriverBase extends DeriverBase implements ContainerDeriverInterface {
+ use StringTranslationTrait;
+
/**
* The entity type manager.
*
@@ -21,20 +25,33 @@ abstract class EntityActionDeriverBase extends DeriverBase implements ContainerD
protected $entityTypeManager;
/**
+ * The string translation service.
+ *
+ * @var \Drupal\Core\StringTranslation\TranslationInterface
+ */
+ protected $stringTranslation;
+
+ /**
* Constructs a new EntityActionDeriverBase object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
+ * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+ * The string translation service.
*/
- public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
$this->entityTypeManager = $entity_type_manager;
+ $this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
- return new static($container->get('entity_type.manager'));
+ return new static(
+ $container->get('entity_type.manager'),
+ $container->get('string_translation')
+ );
}
/**
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityDeleteActionDeriver.php b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityDeleteActionDeriver.php
new file mode 100644
index 0000000..4be4588
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityDeleteActionDeriver.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action\Derivative;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides an action deriver that finds entity types with delete form.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\DeleteAction
+ */
+class EntityDeleteActionDeriver extends EntityActionDeriverBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDerivativeDefinitions($base_plugin_definition) {
+ if (empty($this->derivatives)) {
+ $definitions = [];
+ foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
+ $definition = $base_plugin_definition;
+ $definition['type'] = $entity_type_id;
+ $definition['label'] = $this->t('Delete @entity_type', ['@entity_type' => $entity_type->getSingularLabel()]);
+ $definition['confirm_form_route_name'] = 'entity.' . $entity_type->id() . '.delete_multiple_form';
+ $definitions[$entity_type_id] = $definition;
+ }
+ $this->derivatives = $definitions;
+ }
+
+ return $this->derivatives;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function isApplicable(EntityTypeInterface $entity_type) {
+ return $entity_type->hasLinkTemplate('delete-multiple-form');
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityDeleteMultipleAccessCheck.php b/core/lib/Drupal/Core/Entity/EntityDeleteMultipleAccessCheck.php
new file mode 100644
index 0000000..470c1b0
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityDeleteMultipleAccessCheck.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TempStore\PrivateTempStoreFactory;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Checks if the current user has delete access to the items of the tempstore.
+ */
+class EntityDeleteMultipleAccessCheck implements AccessInterface {
+
+ /**
+ * The entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * The tempstore service.
+ *
+ * @var \Drupal\Core\TempStore\PrivateTempStoreFactory
+ */
+ protected $tempStore;
+
+ /**
+ * Request stack service.
+ *
+ * @var \Symfony\Component\HttpFoundation\RequestStack
+ */
+ protected $requestStack;
+
+ /**
+ * Constructs a new EntityDeleteMultipleAccessCheck.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
+ * The tempstore service.
+ * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+ * The request stack service.
+ */
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, RequestStack $request_stack) {
+ $this->entityTypeManager = $entity_type_manager;
+ $this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
+ $this->requestStack = $request_stack;
+ }
+
+ /**
+ * Checks if the user has delete access for at least one item of the store.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * Run access checks for this account.
+ * @param string $entity_type_id
+ * Entity type ID.
+ *
+ * @return \Drupal\Core\Access\AccessResult
+ * Allowed or forbidden, neutral if tempstore is empty.
+ */
+ public function access(AccountInterface $account, $entity_type_id) {
+ if (!$this->requestStack->getCurrentRequest()->getSession()) {
+ return AccessResult::neutral();
+ }
+ $selection = $this->tempStore->get($account->id() . ':' . $entity_type_id);
+ if (empty($selection) || !is_array($selection)) {
+ return AccessResult::neutral();
+ }
+
+ $entities = $this->entityTypeManager->getStorage($entity_type_id)->loadMultiple(array_keys($selection));
+ foreach ($entities as $entity) {
+ // As long as the user has access to delete one entity allow access to the
+ // delete form. Access will be checked again in
+ // Drupal\Core\Entity\Form\DeleteMultipleForm::submit() in case it has
+ // changed in the meantime.
+ if ($entity->access('delete', $account)) {
+ return AccessResult::allowed();
+ }
+ }
+ return AccessResult::forbidden();
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Form/DeleteMultipleForm.php b/core/lib/Drupal/Core/Entity/Form/DeleteMultipleForm.php
new file mode 100644
index 0000000..aea3291
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Form/DeleteMultipleForm.php
@@ -0,0 +1,323 @@
+<?php
+
+namespace Drupal\Core\Entity\Form;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\BaseFormIdInterface;
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TempStore\PrivateTempStoreFactory;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides an entities deletion confirmation form.
+ */
+class DeleteMultipleForm extends ConfirmFormBase implements BaseFormIdInterface {
+
+ /**
+ * The current user.
+ *
+ * @var \Drupal\Core\Session\AccountInterface
+ */
+ protected $currentUser;
+
+ /**
+ * The entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * The tempstore.
+ *
+ * @var \Drupal\Core\TempStore\SharedTempStore
+ */
+ protected $tempStore;
+
+ /**
+ * The messenger service.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
+ /**
+ * The entity type ID.
+ *
+ * @var string
+ */
+ protected $entityTypeId;
+
+ /**
+ * The selection, in the entity_id => langcodes format.
+ *
+ * @var array
+ */
+ protected $selection = [];
+
+ /**
+ * The entity type definition.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeInterface
+ */
+ protected $entityType;
+
+ /**
+ * Constructs a new DeleteMultiple object.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $current_user
+ * The current user.
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
+ * The tempstore factory.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger service.
+ */
+ public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, MessengerInterface $messenger) {
+ $this->currentUser = $current_user;
+ $this->entityTypeManager = $entity_type_manager;
+ $this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
+ $this->messenger = $messenger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('current_user'),
+ $container->get('entity_type.manager'),
+ $container->get('tempstore.private'),
+ $container->get('messenger')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBaseFormId() {
+ return 'entity_delete_multiple_confirm_form';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ // Get entity type ID from the route because ::buildForm has not yet been
+ // called.
+ $entity_type_id = $this->getRouteMatch()->getParameter('entity_type_id');
+ return $entity_type_id . '_delete_multiple_confirm_form';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQuestion() {
+ return $this->formatPlural(count($this->selection), 'Are you sure you want to delete this @item?', 'Are you sure you want to delete these @items?', [
+ '@item' => $this->entityType->getSingularLabel(),
+ '@items' => $this->entityType->getPluralLabel(),
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCancelUrl() {
+ if ($this->entityType->hasLinkTemplate('collection')) {
+ return new Url('entity.' . $this->entityTypeId . '.collection');
+ }
+ else {
+ return new Url('<front>');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfirmText() {
+ return $this->t('Delete');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
+ $this->entityTypeId = $entity_type_id;
+ $this->entityType = $this->entityTypeManager->getDefinition($this->entityTypeId);
+ $this->selection = $this->tempStore->get($this->currentUser->id() . ':' . $entity_type_id);
+ if (empty($this->entityTypeId) || empty($this->selection)) {
+ return new RedirectResponse($this->getCancelUrl()
+ ->setAbsolute()
+ ->toString());
+ }
+
+ $items = [];
+ $entities = $this->entityTypeManager->getStorage($entity_type_id)->loadMultiple(array_keys($this->selection));
+ foreach ($this->selection as $id => $selected_langcodes) {
+ $entity = $entities[$id];
+ foreach ($selected_langcodes as $langcode) {
+ $key = $id . ':' . $langcode;
+ if ($entity instanceof TranslatableInterface) {
+ $entity = $entity->getTranslation($langcode);
+ $default_key = $id . ':' . $entity->getUntranslated()->language()->getId();
+
+ // Build a nested list of translations that will be deleted if the
+ // entity has multiple translations.
+ $entity_languages = $entity->getTranslationLanguages();
+ if (count($entity_languages) > 1 && $entity->isDefaultTranslation()) {
+ $names = [];
+ foreach ($entity_languages as $translation_langcode => $language) {
+ $names[] = $language->getName();
+ unset($items[$id . ':' . $translation_langcode]);
+ }
+ $items[$default_key] = [
+ 'label' => [
+ '#markup' => $this->t('@label (Original translation) - <em>The following @entity_type translations will be deleted:</em>',
+ [
+ '@label' => $entity->label(),
+ '@entity_type' => $this->entityType->getSingularLabel(),
+ ]),
+ ],
+ 'deleted_translations' => [
+ '#theme' => 'item_list',
+ '#items' => $names,
+ ],
+ ];
+ }
+ elseif (!isset($items[$default_key])) {
+ $items[$key] = $entity->label();
+ }
+ }
+ elseif (!isset($items[$key])) {
+ $items[$key] = $entity->label();
+ }
+ }
+ }
+
+ $form['entities'] = [
+ '#theme' => 'item_list',
+ '#items' => $items,
+ ];
+ $form = parent::buildForm($form, $form_state);
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $total_count = 0;
+ $delete_entities = [];
+ $delete_translations = [];
+ $inaccessible_entities = [];
+ $storage = $this->entityTypeManager->getStorage($this->entityTypeId);
+
+ $entities = $storage->loadMultiple(array_keys($this->selection));
+ foreach ($this->selection as $id => $selected_langcodes) {
+ $entity = $entities[$id];
+ if (!$entity->access('delete', $this->currentUser)) {
+ $inaccessible_entities[] = $entity;
+ continue;
+ }
+ foreach ($selected_langcodes as $langcode) {
+ if ($entity instanceof TranslatableInterface) {
+ $entity = $entity->getTranslation($langcode);
+ // If the entity is the default translation then deleting it will
+ // delete all the translations.
+ if ($entity->isDefaultTranslation()) {
+ $delete_entities[$id] = $entity;
+ // If there are translations already marked for deletion then remove
+ // them as they will be deleted anyway.
+ unset($delete_translations[$id]);
+ // Update the total count. Since a single delete will delete all
+ // translations, we need to add the number of translations to the
+ // count.
+ $total_count += count($entity->getTranslationLanguages());
+ }
+ // Add the translation to the list of translations to be deleted
+ // unless the default translation is being deleted.
+ elseif (!isset($delete_entities[$id])) {
+ $delete_translations[$id][] = $entity;
+ }
+ }
+ elseif (!isset($delete_entities[$id])) {
+ $delete_entities[$id] = $entity;
+ $total_count++;
+ }
+ }
+ }
+
+ if ($delete_entities) {
+ $storage->delete($delete_entities);
+ foreach ($delete_entities as $entity) {
+ $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label has been deleted.', [
+ '@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
+ '%label' => $entity->label(),
+ ]);
+ }
+ }
+
+ if ($delete_translations) {
+ /** @var \Drupal\Core\Entity\TranslatableInterface[][] $delete_translations */
+ foreach ($delete_translations as $id => $translations) {
+ $entity = $entities[$id]->getUntranslated();
+ foreach ($translations as $translation) {
+ $entity->removeTranslation($translation->language()->getId());
+ }
+ $entity->save();
+ foreach ($translations as $translation) {
+ $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label @language translation has been deleted.', [
+ '@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
+ '%label' => $entity->label(),
+ '@language' => $translation->language()->getName(),
+ ]);
+ }
+ $total_count += count($translations);
+ }
+ }
+
+ if ($total_count) {
+ $this->messenger->addStatus($this->getDeletedMessage($total_count));
+ }
+ if ($inaccessible_entities) {
+ $this->messenger->addWarning($this->getInaccessibleMessage(count($inaccessible_entities)));
+ }
+ $this->tempStore->delete($this->currentUser->id());
+ $form_state->setRedirectUrl($this->getCancelUrl());
+ }
+
+ /**
+ * Returns the message to show the user after an item was deleted.
+ *
+ * @param int $count
+ * Count of deleted translations.
+ *
+ * @return \Drupal\Core\StringTranslation\TranslatableMarkup
+ * The item deleted message.
+ */
+ protected function getDeletedMessage($count) {
+ return $this->formatPlural($count, 'Deleted @count item.', 'Deleted @count items.');
+ }
+
+ /**
+ * Returns the message to show the user when an item has not been deleted.
+ *
+ * @param int $count
+ * Count of deleted translations.
+ *
+ * @return \Drupal\Core\StringTranslation\TranslatableMarkup
+ * The item inaccessible message.
+ */
+ protected function getInaccessibleMessage($count) {
+ return $this->formatPlural($count, "@count item has not been deleted because you do not have the necessary permissions.", "@count items have not been deleted because you do not have the necessary permissions.");
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Routing/AdminHtmlRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/AdminHtmlRouteProvider.php
index 1abee11..b04fc3e 100644
--- a/core/lib/Drupal/Core/Entity/Routing/AdminHtmlRouteProvider.php
+++ b/core/lib/Drupal/Core/Entity/Routing/AdminHtmlRouteProvider.php
@@ -54,4 +54,14 @@ class AdminHtmlRouteProvider extends DefaultHtmlRouteProvider {
}
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDeleteMultipleFormRoute(EntityTypeInterface $entity_type) {
+ if ($route = parent::getDeleteMultipleFormRoute($entity_type)) {
+ $route->setOption('_admin_route', TRUE);
+ return $route;
+ }
+ }
+
}
diff --git a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
index fb40fd5..0a99b33 100644
--- a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
+++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
@@ -24,6 +24,7 @@ use Symfony\Component\Routing\RouteCollection;
* - edit-form
* - delete-form
* - collection
+ * - delete-multiple-form
*
* @see \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider.
*/
@@ -98,6 +99,10 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHa
$collection->add("entity.{$entity_type_id}.collection", $collection_route);
}
+ if ($delete_multiple_route = $this->getDeleteMultipleFormRoute($entity_type)) {
+ $collection->add('entity.' . $entity_type->id() . '.delete_multiple_form', $delete_multiple_route);
+ }
+
return $collection;
}
@@ -350,4 +355,23 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHa
return $field_storage_definitions[$entity_type->getKey('id')]->getType();
}
+ /**
+ * Returns the delete multiple form route.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ * The entity type.
+ *
+ * @return \Symfony\Component\Routing\Route|null
+ * The generated route, if available.
+ */
+ protected function getDeleteMultipleFormRoute(EntityTypeInterface $entity_type) {
+ if ($entity_type->hasLinkTemplate('delete-multiple-form') && $entity_type->hasHandlerClass('form', 'delete-multiple-confirm')) {
+ $route = new Route($entity_type->getLinkTemplate('delete-multiple-form'));
+ $route->setDefault('_form', $entity_type->getFormClass('delete-multiple-confirm'));
+ $route->setDefault('entity_type_id', $entity_type->id());
+ $route->setRequirement('_entity_delete_multiple_access', $entity_type->id());
+ return $route;
+ }
+ }
+
}
diff --git a/core/modules/action/tests/src/Functional/BulkFormTest.php b/core/modules/action/tests/src/Functional/BulkFormTest.php
index 439ffe4..ec6ca48 100644
--- a/core/modules/action/tests/src/Functional/BulkFormTest.php
+++ b/core/modules/action/tests/src/Functional/BulkFormTest.php
@@ -148,7 +148,7 @@ class BulkFormTest extends BrowserTestBase {
$errors = $this->xpath('//div[contains(@class, "messages--status")]');
$this->assertFalse($errors, 'No action message shown.');
$this->drupalPostForm(NULL, [], t('Delete'));
- $this->assertText(t('Deleted 5 posts.'));
+ $this->assertText(t('Deleted 5 content items.'));
// Check if we got redirected to the original page.
$this->assertUrl('test_bulk_form');
}
diff --git a/core/modules/comment/comment.routing.yml b/core/modules/comment/comment.routing.yml
index 80ee86c..6daeace 100644
--- a/core/modules/comment/comment.routing.yml
+++ b/core/modules/comment/comment.routing.yml
@@ -59,8 +59,18 @@ comment.multiple_delete_confirm:
defaults:
_title: 'Delete'
_form: '\Drupal\comment\Form\ConfirmDeleteMultiple'
+ entity_type_id: 'comment'
requirements:
- _permission: 'administer comments'
+ _entity_delete_multiple_access: 'comment'
+
+entity.comment.delete_multiple_form:
+ path: '/admin/content/comment/delete'
+ defaults:
+ _title: 'Delete'
+ _form: '\Drupal\comment\Form\ConfirmDeleteMultiple'
+ entity_type_id: 'comment'
+ requirements:
+ _entity_delete_multiple_access: 'comment'
comment.reply:
path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
diff --git a/core/modules/comment/config/install/system.action.comment_delete_action.yml b/core/modules/comment/config/install/system.action.comment_delete_action.yml
index 035299e..08f7966 100644
--- a/core/modules/comment/config/install/system.action.comment_delete_action.yml
+++ b/core/modules/comment/config/install/system.action.comment_delete_action.yml
@@ -6,5 +6,5 @@ dependencies:
id: comment_delete_action
label: 'Delete comment'
type: comment
-plugin: comment_delete_action
+plugin: entity:delete_action:comment
configuration: { }
diff --git a/core/modules/comment/config/schema/comment.schema.yml b/core/modules/comment/config/schema/comment.schema.yml
index c29088b..0f64318 100644
--- a/core/modules/comment/config/schema/comment.schema.yml
+++ b/core/modules/comment/config/schema/comment.schema.yml
@@ -44,6 +44,8 @@ action.configuration.comment_unpublish_action:
type: action_configuration_default
label: 'Unpublish comment configuration'
+# @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2934349
action.configuration.comment_delete_action:
type: action_configuration_default
label: 'Delete comment configuration'
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 233c752..5f5a611 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -56,6 +56,7 @@ use Drupal\user\UserInterface;
* links = {
* "canonical" = "/comment/{comment}",
* "delete-form" = "/comment/{comment}/delete",
+ * "delete-multiple-form" = "/admin/content/comment/delete",
* "edit-form" = "/comment/{comment}/edit",
* "create" = "/comment",
* },
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index 4fe7291..8a93ecd 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -290,9 +290,9 @@ class CommentAdminOverview extends FormBase {
$info[$comment->id()][$langcode] = $langcode;
}
$this->tempStoreFactory
- ->get('comment_multiple_delete_confirm')
- ->set($this->currentUser()->id(), $info);
- $form_state->setRedirect('comment.multiple_delete_confirm');
+ ->get('entity_delete_multiple_confirm')
+ ->set($this->currentUser()->id() . ':comment', $info);
+ $form_state->setRedirect('entity.comment.delete_multiple_form');
}
}
diff --git a/core/modules/comment/src/Form/ConfirmDeleteMultiple.php b/core/modules/comment/src/Form/ConfirmDeleteMultiple.php
index 7044393..825b0af 100644
--- a/core/modules/comment/src/Form/ConfirmDeleteMultiple.php
+++ b/core/modules/comment/src/Form/ConfirmDeleteMultiple.php
@@ -2,76 +2,21 @@
namespace Drupal\comment\Form;
-use Drupal\comment\CommentStorageInterface;
-use Drupal\Core\TempStore\PrivateTempStoreFactory;
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\Form\DeleteMultipleForm as EntityDeleteMultipleForm;
use Drupal\Core\Url;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the comment multiple delete confirmation form.
*
* @internal
*/
-class ConfirmDeleteMultiple extends ConfirmFormBase {
-
- /**
- * The tempstore factory.
- *
- * @var \Drupal\Core\TempStore\PrivateTempStoreFactory
- */
- protected $tempStoreFactory;
-
- /**
- * The comment storage.
- *
- * @var \Drupal\comment\CommentStorageInterface
- */
- protected $commentStorage;
-
- /**
- * An array of comments to be deleted.
- *
- * @var string[][]
- */
- protected $commentInfo;
-
- /**
- * Creates an new ConfirmDeleteMultiple form.
- *
- * @param \Drupal\comment\CommentStorageInterface $comment_storage
- * The comment storage.
- * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
- * The tempstore factory.
- */
- public function __construct(CommentStorageInterface $comment_storage, PrivateTempStoreFactory $temp_store_factory) {
- $this->commentStorage = $comment_storage;
- $this->tempStoreFactory = $temp_store_factory;
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container) {
- return new static(
- $container->get('entity.manager')->getStorage('comment'),
- $container->get('tempstore.private')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormId() {
- return 'comment_multiple_delete_confirm';
- }
+class ConfirmDeleteMultiple extends EntityDeleteMultipleForm {
/**
* {@inheritdoc}
*/
public function getQuestion() {
- return $this->formatPlural(count($this->commentInfo), 'Are you sure you want to delete this comment and all its children?', 'Are you sure you want to delete these comments and all their children?');
+ return $this->formatPlural(count($this->selection), 'Are you sure you want to delete this comment and all its children?', 'Are you sure you want to delete these comments and all their children?');
}
/**
@@ -84,116 +29,15 @@ class ConfirmDeleteMultiple extends ConfirmFormBase {
/**
* {@inheritdoc}
*/
- public function getConfirmText() {
- return $this->t('Delete');
+ protected function getDeletedMessage($count) {
+ return $this->formatPlural($count, 'Deleted @count comment.', 'Deleted @count comments.');
}
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state) {
- $this->commentInfo = $this->tempStoreFactory->get('comment_multiple_delete_confirm')->get($this->currentUser()->id());
- if (empty($this->commentInfo)) {
- return $this->redirect('comment.admin');
- }
- /** @var \Drupal\comment\CommentInterface[] $comments */
- $comments = $this->commentStorage->loadMultiple(array_keys($this->commentInfo));
-
- $items = [];
- foreach ($this->commentInfo as $id => $langcodes) {
- foreach ($langcodes as $langcode) {
- $comment = $comments[$id]->getTranslation($langcode);
- $key = $id . ':' . $langcode;
- $default_key = $id . ':' . $comment->getUntranslated()->language()->getId();
-
- // If we have a translated entity we build a nested list of translations
- // that will be deleted.
- $languages = $comment->getTranslationLanguages();
- if (count($languages) > 1 && $comment->isDefaultTranslation()) {
- $names = [];
- foreach ($languages as $translation_langcode => $language) {
- $names[] = $language->getName();
- unset($items[$id . ':' . $translation_langcode]);
- }
- $items[$default_key] = [
- 'label' => [
- '#markup' => $this->t('@label (Original translation) - <em>The following comment translations will be deleted:</em>', ['@label' => $comment->label()]),
- ],
- 'deleted_translations' => [
- '#theme' => 'item_list',
- '#items' => $names,
- ],
- ];
- }
- elseif (!isset($items[$default_key])) {
- $items[$key] = $comment->label();
- }
- }
- }
-
- $form['comments'] = [
- '#theme' => 'item_list',
- '#items' => $items,
- ];
-
- return parent::buildForm($form, $form_state);
- }
-
- /**
- * {@inheritdoc}
- */
- public function submitForm(array &$form, FormStateInterface $form_state) {
- if ($form_state->getValue('confirm') && !empty($this->commentInfo)) {
- $total_count = 0;
- $delete_comments = [];
- /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
- $delete_translations = [];
- /** @var \Drupal\comment\CommentInterface[] $comments */
- $comments = $this->commentStorage->loadMultiple(array_keys($this->commentInfo));
-
- foreach ($this->commentInfo as $id => $langcodes) {
- foreach ($langcodes as $langcode) {
- $comment = $comments[$id]->getTranslation($langcode);
- if ($comment->isDefaultTranslation()) {
- $delete_comments[$id] = $comment;
- unset($delete_translations[$id]);
- $total_count += count($comment->getTranslationLanguages());
- }
- elseif (!isset($delete_comments[$id])) {
- $delete_translations[$id][] = $comment;
- }
- }
- }
-
- if ($delete_comments) {
- $this->commentStorage->delete($delete_comments);
- $this->logger('content')->notice('Deleted @count comments.', ['@count' => count($delete_comments)]);
- }
-
- if ($delete_translations) {
- $count = 0;
- foreach ($delete_translations as $id => $translations) {
- $comment = $comments[$id]->getUntranslated();
- foreach ($translations as $translation) {
- $comment->removeTranslation($translation->language()->getId());
- }
- $comment->save();
- $count += count($translations);
- }
- if ($count) {
- $total_count += $count;
- $this->logger('content')->notice('Deleted @count comment translations.', ['@count' => $count]);
- }
- }
-
- if ($total_count) {
- drupal_set_message($this->formatPlural($total_count, 'Deleted 1 comment.', 'Deleted @count comments.'));
- }
-
- $this->tempStoreFactory->get('comment_multiple_delete_confirm')->delete($this->currentUser()->id());
- }
-
- $form_state->setRedirectUrl($this->getCancelUrl());
+ protected function getInaccessibleMessage($count) {
+ return $this->formatPlural($count, "@count comment has not been deleted because you do not have the necessary permissions.", "@count comments have not been deleted because you do not have the necessary permissions.");
}
}
diff --git a/core/modules/comment/src/Plugin/Action/DeleteComment.php b/core/modules/comment/src/Plugin/Action/DeleteComment.php
index 5c6941d..ef6116c 100644
--- a/core/modules/comment/src/Plugin/Action/DeleteComment.php
+++ b/core/modules/comment/src/Plugin/Action/DeleteComment.php
@@ -2,97 +2,33 @@
namespace Drupal\comment\Plugin\Action;
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Action\Plugin\Action\DeleteAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Deletes a comment.
*
+ * @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\DeleteAction
+ * @see https://www.drupal.org/node/2934349
+ *
* @Action(
* id = "comment_delete_action",
- * label = @Translation("Delete comment"),
- * type = "comment",
- * confirm_form_route_name = "comment.multiple_delete_confirm"
+ * label = @Translation("Delete comment")
* )
*/
-class DeleteComment extends ActionBase implements ContainerFactoryPluginInterface {
-
- /**
- * The tempstore object.
- *
- * @var \Drupal\Core\TempStore\PrivateTempStore
- */
- protected $tempStore;
-
- /**
- * The current user.
- *
- * @var \Drupal\Core\Session\AccountInterface
- */
- protected $currentUser;
-
- /**
- * Constructs a new DeleteComment object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin ID for the plugin instance.
- * @param array $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
- * The tempstore factory.
- * @param \Drupal\Core\Session\AccountInterface $current_user
- * The current user.
- */
- public function __construct(array $configuration, $plugin_id, array $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
- $this->currentUser = $current_user;
- $this->tempStore = $temp_store_factory->get('comment_multiple_delete_confirm');
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('tempstore.private'),
- $container->get('current_user')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function executeMultiple(array $entities) {
- $info = [];
- /** @var \Drupal\comment\CommentInterface $comment */
- foreach ($entities as $comment) {
- $langcode = $comment->language()->getId();
- $info[$comment->id()][$langcode] = $langcode;
- }
- $this->tempStore->set($this->currentUser->id(), $info);
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($entity = NULL) {
- $this->executeMultiple([$entity]);
- }
+class DeleteComment extends DeleteAction {
/**
* {@inheritdoc}
*/
- public function access($comment, AccountInterface $account = NULL, $return_as_object = FALSE) {
- /** @var \Drupal\comment\CommentInterface $comment */
- return $comment->access('delete', $account, $return_as_object);
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $temp_store_factory, $current_user);
+ @trigger_error(__NAMESPACE__ . '\DeleteComment is deprecated in Drupal 8.6.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead. See https://www.drupal.org/node/2934349.', E_USER_DEPRECATED);
}
}
diff --git a/core/modules/node/config/install/system.action.node_delete_action.yml b/core/modules/node/config/install/system.action.node_delete_action.yml
index 97662dc..da2d886 100644
--- a/core/modules/node/config/install/system.action.node_delete_action.yml
+++ b/core/modules/node/config/install/system.action.node_delete_action.yml
@@ -6,5 +6,5 @@ dependencies:
id: node_delete_action
label: 'Delete content'
type: node
-plugin: node_delete_action
+plugin: entity:delete_action:node
configuration: { }
diff --git a/core/modules/node/config/schema/node.schema.yml b/core/modules/node/config/schema/node.schema.yml
index 50b3f3e..b6fb7bc 100644
--- a/core/modules/node/config/schema/node.schema.yml
+++ b/core/modules/node/config/schema/node.schema.yml
@@ -80,6 +80,8 @@ action.configuration.node_save_action:
type: action_configuration_default
label: 'Save content configuration'
+# @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2934349
action.configuration.node_delete_action:
type: action_configuration_default
label: 'Delete content configuration'
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index a016e3a..d592bd1 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -2,8 +2,17 @@ node.multiple_delete_confirm:
path: '/admin/content/node/delete'
defaults:
_form: '\Drupal\node\Form\DeleteMultiple'
+ entity_type_id: 'node'
requirements:
- _permission: 'administer nodes'
+ _entity_delete_multiple_access: 'node'
+
+entity.node.delete_multiple_form:
+ path: '/admin/content/node/delete'
+ defaults:
+ _form: '\Drupal\node\Form\DeleteMultiple'
+ entity_type_id: 'node'
+ requirements:
+ _entity_delete_multiple_access: 'node'
node.add_page:
path: '/node/add'
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 368ce1b..ccee95b 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -33,7 +33,8 @@ use Drupal\user\UserInterface;
* "form" = {
* "default" = "Drupal\node\NodeForm",
* "delete" = "Drupal\node\Form\NodeDeleteForm",
- * "edit" = "Drupal\node\NodeForm"
+ * "edit" = "Drupal\node\NodeForm",
+ * "delete-multiple-confirm" = "Drupal\node\Form\DeleteMultiple"
* },
* "route_provider" = {
* "html" = "Drupal\node\Entity\NodeRouteProvider",
@@ -71,6 +72,7 @@ use Drupal\user\UserInterface;
* links = {
* "canonical" = "/node/{node}",
* "delete-form" = "/node/{node}/delete",
+ * "delete-multiple-form" = "/admin/content/node/delete",
* "edit-form" = "/node/{node}/edit",
* "version-history" = "/node/{node}/revisions",
* "revision" = "/node/{node}/revisions/{node_revision}/view",
diff --git a/core/modules/node/src/Form/DeleteMultiple.php b/core/modules/node/src/Form/DeleteMultiple.php
index ef2c89f..a36de5f 100644
--- a/core/modules/node/src/Form/DeleteMultiple.php
+++ b/core/modules/node/src/Form/DeleteMultiple.php
@@ -2,78 +2,15 @@
namespace Drupal\node\Form;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\Form\DeleteMultipleForm as EntityDeleteMultipleForm;
use Drupal\Core\Url;
-use Drupal\Core\TempStore\PrivateTempStoreFactory;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides a node deletion confirmation form.
*
* @internal
*/
-class DeleteMultiple extends ConfirmFormBase {
-
- /**
- * The array of nodes to delete.
- *
- * @var string[][]
- */
- protected $nodeInfo = [];
-
- /**
- * The tempstore factory.
- *
- * @var \Drupal\Core\TempStore\PrivateTempStoreFactory
- */
- protected $tempStoreFactory;
-
- /**
- * The node storage.
- *
- * @var \Drupal\Core\Entity\EntityStorageInterface
- */
- protected $manager;
-
- /**
- * Constructs a DeleteMultiple form object.
- *
- * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
- * The tempstore factory.
- * @param \Drupal\Core\Entity\EntityManagerInterface $manager
- * The entity manager.
- */
- public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityManagerInterface $manager) {
- $this->tempStoreFactory = $temp_store_factory;
- $this->storage = $manager->getStorage('node');
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container) {
- return new static(
- $container->get('tempstore.private'),
- $container->get('entity.manager')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormId() {
- return 'node_multiple_delete_confirm';
- }
-
- /**
- * {@inheritdoc}
- */
- public function getQuestion() {
- return $this->formatPlural(count($this->nodeInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
- }
+class DeleteMultiple extends EntityDeleteMultipleForm {
/**
* {@inheritdoc}
@@ -85,117 +22,15 @@ class DeleteMultiple extends ConfirmFormBase {
/**
* {@inheritdoc}
*/
- public function getConfirmText() {
- return t('Delete');
+ protected function getDeletedMessage($count) {
+ return $this->formatPlural($count, 'Deleted @count content item.', 'Deleted @count content items.');
}
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state) {
- $this->nodeInfo = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
- if (empty($this->nodeInfo)) {
- return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
- }
- /** @var \Drupal\node\NodeInterface[] $nodes */
- $nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
-
- $items = [];
- foreach ($this->nodeInfo as $id => $langcodes) {
- foreach ($langcodes as $langcode) {
- $node = $nodes[$id]->getTranslation($langcode);
- $key = $id . ':' . $langcode;
- $default_key = $id . ':' . $node->getUntranslated()->language()->getId();
-
- // If we have a translated entity we build a nested list of translations
- // that will be deleted.
- $languages = $node->getTranslationLanguages();
- if (count($languages) > 1 && $node->isDefaultTranslation()) {
- $names = [];
- foreach ($languages as $translation_langcode => $language) {
- $names[] = $language->getName();
- unset($items[$id . ':' . $translation_langcode]);
- }
- $items[$default_key] = [
- 'label' => [
- '#markup' => $this->t('@label (Original translation) - <em>The following content translations will be deleted:</em>', ['@label' => $node->label()]),
- ],
- 'deleted_translations' => [
- '#theme' => 'item_list',
- '#items' => $names,
- ],
- ];
- }
- elseif (!isset($items[$default_key])) {
- $items[$key] = $node->label();
- }
- }
- }
-
- $form['nodes'] = [
- '#theme' => 'item_list',
- '#items' => $items,
- ];
- $form = parent::buildForm($form, $form_state);
-
- return $form;
- }
-
- /**
- * {@inheritdoc}
- */
- public function submitForm(array &$form, FormStateInterface $form_state) {
- if ($form_state->getValue('confirm') && !empty($this->nodeInfo)) {
- $total_count = 0;
- $delete_nodes = [];
- /** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
- $delete_translations = [];
- /** @var \Drupal\node\NodeInterface[] $nodes */
- $nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
-
- foreach ($this->nodeInfo as $id => $langcodes) {
- foreach ($langcodes as $langcode) {
- $node = $nodes[$id]->getTranslation($langcode);
- if ($node->isDefaultTranslation()) {
- $delete_nodes[$id] = $node;
- unset($delete_translations[$id]);
- $total_count += count($node->getTranslationLanguages());
- }
- elseif (!isset($delete_nodes[$id])) {
- $delete_translations[$id][] = $node;
- }
- }
- }
-
- if ($delete_nodes) {
- $this->storage->delete($delete_nodes);
- $this->logger('content')->notice('Deleted @count posts.', ['@count' => count($delete_nodes)]);
- }
-
- if ($delete_translations) {
- $count = 0;
- foreach ($delete_translations as $id => $translations) {
- $node = $nodes[$id]->getUntranslated();
- foreach ($translations as $translation) {
- $node->removeTranslation($translation->language()->getId());
- }
- $node->save();
- $count += count($translations);
- }
- if ($count) {
- $total_count += $count;
- $this->logger('content')->notice('Deleted @count content translations.', ['@count' => $count]);
- }
- }
-
- if ($total_count) {
- drupal_set_message($this->formatPlural($total_count, 'Deleted 1 post.', 'Deleted @count posts.'));
- }
-
- $this->tempStoreFactory->get('node_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
- }
-
- $form_state->setRedirect('system.admin_content');
+ protected function getInaccessibleMessage($count) {
+ return $this->formatPlural($count, "@count content item has not been deleted because you do not have the necessary permissions.", "@count content items have not been deleted because you do not have the necessary permissions.");
}
}
diff --git a/core/modules/node/src/Plugin/Action/DeleteNode.php b/core/modules/node/src/Plugin/Action/DeleteNode.php
index 19f8a7a..64fbe12 100644
--- a/core/modules/node/src/Plugin/Action/DeleteNode.php
+++ b/core/modules/node/src/Plugin/Action/DeleteNode.php
@@ -2,98 +2,33 @@
namespace Drupal\node\Plugin\Action;
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Action\Plugin\Action\DeleteAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Redirects to a node deletion form.
*
+ * @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\DeleteAction
+ * @see https://www.drupal.org/node/2934349
+ *
* @Action(
* id = "node_delete_action",
- * label = @Translation("Delete content"),
- * type = "node",
- * confirm_form_route_name = "node.multiple_delete_confirm"
+ * label = @Translation("Delete content")
* )
*/
-class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface {
-
- /**
- * The tempstore object.
- *
- * @var \Drupal\Core\TempStore\SharedTempStore
- */
- protected $tempStore;
-
- /**
- * The current user.
- *
- * @var \Drupal\Core\Session\AccountInterface
- */
- protected $currentUser;
-
- /**
- * Constructs a new DeleteNode object.
- *
- * @param array $configuration
- * A configuration array containing information about the plugin instance.
- * @param string $plugin_id
- * The plugin ID for the plugin instance.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
- * The tempstore factory.
- * @param \Drupal\Core\Session\AccountInterface $current_user
- * Current user.
- */
- public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
- $this->currentUser = $current_user;
- $this->tempStore = $temp_store_factory->get('node_multiple_delete_confirm');
-
- parent::__construct($configuration, $plugin_id, $plugin_definition);
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $configuration,
- $plugin_id,
- $plugin_definition,
- $container->get('tempstore.private'),
- $container->get('current_user')
- );
- }
-
- /**
- * {@inheritdoc}
- */
- public function executeMultiple(array $entities) {
- $info = [];
- /** @var \Drupal\node\NodeInterface $node */
- foreach ($entities as $node) {
- $langcode = $node->language()->getId();
- $info[$node->id()][$langcode] = $langcode;
- }
- $this->tempStore->set($this->currentUser->id(), $info);
- }
-
- /**
- * {@inheritdoc}
- */
- public function execute($object = NULL) {
- $this->executeMultiple([$object]);
- }
+class DeleteNode extends DeleteAction {
/**
* {@inheritdoc}
*/
- public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
- /** @var \Drupal\node\NodeInterface $object */
- return $object->access('delete', $account, $return_as_object);
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $temp_store_factory, $current_user);
+ @trigger_error(__NAMESPACE__ . '\DeleteNode is deprecated in Drupal 8.6.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead. See https://www.drupal.org/node/2934349.', E_USER_DEPRECATED);
}
}
diff --git a/core/modules/node/tests/src/Functional/Views/BulkFormTest.php b/core/modules/node/tests/src/Functional/Views/BulkFormTest.php
index 319890b..894a460 100644
--- a/core/modules/node/tests/src/Functional/Views/BulkFormTest.php
+++ b/core/modules/node/tests/src/Functional/Views/BulkFormTest.php
@@ -249,15 +249,15 @@ class BulkFormTest extends NodeTestBase {
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$label = $this->loadNode(1)->label();
- $this->assertText("$label (Original translation) - The following content translations will be deleted:");
+ $this->assertText("$label (Original translation) - The following content item translations will be deleted:");
$label = $this->loadNode(2)->label();
- $this->assertText("$label (Original translation) - The following content translations will be deleted:");
+ $this->assertText("$label (Original translation) - The following content item translations will be deleted:");
$label = $this->loadNode(3)->getTranslation('en')->label();
$this->assertText($label);
- $this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
+ $this->assertNoText("$label (Original translation) - The following content item translations will be deleted:");
$label = $this->loadNode(4)->label();
$this->assertText($label);
- $this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
+ $this->assertNoText("$label (Original translation) - The following content item translations will be deleted:");
$this->drupalPostForm(NULL, [], t('Delete'));
@@ -273,7 +273,7 @@ class BulkFormTest extends NodeTestBase {
$node = $this->loadNode(5);
$this->assertTrue($node, '5: Node has not been deleted');
- $this->assertText('Deleted 8 posts.');
+ $this->assertText('Deleted 8 content items.');
}
/**
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index da13077..66fa24a 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -111,3 +111,22 @@ function system_post_update_change_action_plugins() {
}
}
}
+
+/**
+ * Change plugin IDs of delete actions.
+ */
+function system_post_update_change_delete_action_plugins() {
+ $old_new_action_id_map = [
+ 'comment_delete_action' => 'entity:delete_action:comment',
+ 'node_delete_action' => 'entity:delete_action:node',
+ ];
+
+ /** @var \Drupal\system\Entity\Action[] $actions */
+ $actions = \Drupal::entityTypeManager()->getStorage('action')->loadMultiple();
+ foreach ($actions as $action) {
+ if (isset($old_new_action_id_map[$action->getPlugin()->getPluginId()])) {
+ $action->setPlugin($old_new_action_id_map[$action->getPlugin()->getPluginId()]);
+ $action->save();
+ }
+ }
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
index 862958e..8fa69a3 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevPub.php
@@ -17,7 +17,8 @@ use Drupal\Core\Entity\EntityTypeInterface;
* "access" = "Drupal\entity_test\EntityTestAccessControlHandler",
* "form" = {
* "default" = "Drupal\entity_test\EntityTestForm",
- * "delete" = "Drupal\entity_test\EntityTestDeleteForm"
+ * "delete" = "Drupal\entity_test\EntityTestDeleteForm",
+ * "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm"
* },
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
* "views_data" = "Drupal\views\EntityViewsData",
@@ -45,6 +46,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
* "add-form" = "/entity_test_mulrevpub/add",
* "canonical" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}",
* "delete-form" = "/entity_test/delete/entity_test_mulrevpub/{entity_test_mulrevpub}",
+ * "delete-multiple-form" = "/entity_test/delete",
* "edit-form" = "/entity_test_mulrevpub/manage/{entity_test_mulrevpub}/edit",
* "revision" = "/entity_test_mulrevpub/{entity_test_mulrevpub}/revision/{entity_test_mulrevpub_revision}/view",
* }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index 5aa3ae1..6fd4904 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -16,7 +16,8 @@ use Drupal\Core\Field\BaseFieldDefinition;
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
* "form" = {
* "default" = "Drupal\entity_test\EntityTestForm",
- * "delete" = "Drupal\entity_test\EntityTestDeleteForm"
+ * "delete" = "Drupal\entity_test\EntityTestDeleteForm",
+ * "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm"
* },
* "view_builder" = "Drupal\entity_test\EntityTestViewBuilder",
* "translation" = "Drupal\content_translation\ContentTranslationHandler",
@@ -41,6 +42,7 @@ use Drupal\Core\Field\BaseFieldDefinition;
* "add-form" = "/entity_test_rev/add",
* "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
* "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}",
+ * "delete-multiple-form" = "/entity_test_rev/delete_multiple",
* "edit-form" = "/entity_test_rev/manage/{entity_test_rev}/edit",
* "revision" = "/entity_test_rev/{entity_test_rev}/revision/{entity_test_rev_revision}/view",
* }
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
index 4094d00..e3bdcf6 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
@@ -53,4 +53,33 @@ class UpdateActionsWithEntityPluginsTest extends UpdatePathTestBase {
}
}
+ /**
+ * Tests upgrading comment and node delete actions to generic entity ones.
+ *
+ * @see system_post_update_change_delete_action_plugins()
+ */
+ public function testUpdateDeleteActionsWithEntityPlugins() {
+ // comment_delete_actions is not part of the dump files.
+ $array = [
+ 'node_delete_action' => ['node_delete_action', 'entity:delete_action:node'],
+ ];
+
+ foreach ($array as $key => list($before, $after)) {
+ /** @var \Drupal\system\Entity\Action $action */
+ $action = Action::load($key);
+ $this->assertSame($before, $action->getPlugin()->getPluginId());
+ }
+
+ $this->runUpdates();
+
+ foreach ($array as $key => list($before, $after)) {
+ /** @var \Drupal\system\Entity\Action $action */
+ $action = Action::load($key);
+ $this->assertSame($after, $action->getPlugin()->getPluginId());
+
+ // Check that the type the action is based on will be a module dependency.
+ $this->assertArraySubset(['module' => [$action->getPluginDefinition()['type']]], $action->getDependencies());
+ }
+ }
+
}
diff --git a/core/tests/Drupal/FunctionalTests/Entity/DeleteMultipleFormTest.php b/core/tests/Drupal/FunctionalTests/Entity/DeleteMultipleFormTest.php
new file mode 100644
index 0000000..45f88d6
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Entity/DeleteMultipleFormTest.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace Drupal\FunctionalTests\Entity;
+
+use Drupal\entity_test\Entity\EntityTestBundle;
+use Drupal\entity_test\Entity\EntityTestMulRevPub;
+use Drupal\entity_test\Entity\EntityTestRev;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the delete multiple confirmation form.
+ *
+ * @group Entity
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ */
+class DeleteMultipleFormTest extends BrowserTestBase {
+
+ /**
+ * The current user.
+ *
+ * @var \Drupal\Core\Session\AccountInterface
+ */
+ protected $account;
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = ['entity_test', 'user', 'language'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ EntityTestBundle::create([
+ 'id' => 'default',
+ 'label' => 'Default',
+ ])->save();
+ $this->account = $this->drupalCreateUser(['administer entity_test content']);
+ $this->drupalLogin($this->account);
+ }
+
+ /**
+ * Tests the delete form for translatable entities.
+ */
+ public function testTranslatableEntities() {
+ ConfigurableLanguage::create(['id' => 'es'])->save();
+ ConfigurableLanguage::create(['id' => 'fr'])->save();
+
+ $selection = [];
+
+ $entity1 = EntityTestMulRevPub::create(['type' => 'default', 'name' => 'entity1']);
+ $entity1->addTranslation('es', ['name' => 'entity1 spanish']);
+ $entity1->addTranslation('fr', ['name' => 'entity1 french']);
+ $entity1->save();
+ $selection[$entity1->id()]['en'] = 'en';
+
+ $entity2 = EntityTestMulRevPub::create(['type' => 'default', 'name' => 'entity2']);
+ $entity2->addTranslation('es', ['name' => 'entity2 spanish']);
+ $entity2->addTranslation('fr', ['name' => 'entity2 french']);
+ $entity2->save();
+ $selection[$entity2->id()]['es'] = 'es';
+ $selection[$entity2->id()]['fr'] = 'fr';
+
+ $entity3 = EntityTestMulRevPub::create(['type' => 'default', 'name' => 'entity3']);
+ $entity3->addTranslation('es', ['name' => 'entity3 spanish']);
+ $entity3->addTranslation('fr', ['name' => 'entity3 french']);
+ $entity3->save();
+ $selection[$entity3->id()]['fr'] = 'fr';
+
+ // This entity will be inaccessible because of
+ // Drupal\entity_test\EntityTestAccessControlHandler.
+ $entity4 = EntityTestMulRevPub::create(['type' => 'default', 'name' => 'forbid_access']);
+ $entity4->save();
+ $selection[$entity4->id()]['en'] = 'en';
+
+ // Add the selection to the tempstore just like DeleteAction would.
+ $tempstore = \Drupal::service('tempstore.private')->get('entity_delete_multiple_confirm');
+ $tempstore->set($this->account->id() . ':entity_test_mulrevpub', $selection);
+
+ $this->drupalGet('/entity_test/delete');
+ $assert = $this->assertSession();
+ $assert->statusCodeEquals(200);
+ $assert->elementTextContains('css', '.page-title', 'Are you sure you want to delete these test entity - revisions, data table, and published interface entities?');
+ $list_selector = '#entity-test-mulrevpub-delete-multiple-confirm-form > div.item-list > ul';
+ $assert->elementTextContains('css', $list_selector, 'entity1 (Original translation) - The following test entity - revisions, data table, and published interface translations will be deleted:');
+ $assert->elementTextContains('css', $list_selector, 'entity2 spanish');
+ $assert->elementTextContains('css', $list_selector, 'entity2 french');
+ $assert->elementTextNotContains('css', $list_selector, 'entity3 spanish');
+ $assert->elementTextContains('css', $list_selector, 'entity3 french');
+ $delete_button = $this->getSession()->getPage()->findButton('Delete');
+ $delete_button->click();
+ $assert = $this->assertSession();
+ $assert->addressEquals('/user/' . $this->account->id());
+ $assert->responseContains('Deleted 6 items.');
+ $assert->responseContains('1 item has not been deleted because you do not have the necessary permissions.');
+
+ \Drupal::entityTypeManager()->getStorage('entity_test_mulrevpub')->resetCache();
+ $remaining_entities = EntityTestMulRevPub::loadMultiple([$entity1->id(), $entity2->id(), $entity3->id(), $entity4->id()]);
+ $this->assertCount(3, $remaining_entities);
+ }
+
+ /**
+ * Tests the delete form for untranslatable entities.
+ */
+ public function testUntranslatableEntities() {
+ $selection = [];
+
+ $entity1 = EntityTestRev::create(['type' => 'default', 'name' => 'entity1']);
+ $entity1->save();
+ $selection[$entity1->id()]['en'] = 'en';
+
+ $entity2 = EntityTestRev::create(['type' => 'default', 'name' => 'entity2']);
+ $entity2->save();
+ $selection[$entity2->id()]['en'] = 'en';
+
+ // This entity will be inaccessible because of
+ // Drupal\entity_test\EntityTestAccessControlHandler.
+ $entity3 = EntityTestRev::create(['type' => 'default', 'name' => 'forbid_access']);
+ $entity3->save();
+ $selection[$entity3->id()]['en'] = 'en';
+
+ // This entity will be inaccessible because of
+ // Drupal\entity_test\EntityTestAccessControlHandler.
+ $entity4 = EntityTestRev::create(['type' => 'default', 'name' => 'forbid_access']);
+ $entity4->save();
+ $selection[$entity4->id()]['en'] = 'en';
+
+ // Add the selection to the tempstore just like DeleteAction would.
+ $tempstore = \Drupal::service('tempstore.private')->get('entity_delete_multiple_confirm');
+ $tempstore->set($this->account->id() . ':entity_test_rev', $selection);
+
+ $this->drupalGet('/entity_test_rev/delete_multiple');
+ $assert = $this->assertSession();
+ $assert->statusCodeEquals(200);
+ $assert->elementTextContains('css', '.page-title', 'Are you sure you want to delete these test entity - revisions entities?');
+ $list_selector = '#entity-test-rev-delete-multiple-confirm-form > div.item-list > ul';
+ $assert->elementTextContains('css', $list_selector, 'entity1');
+ $assert->elementTextContains('css', $list_selector, 'entity2');
+ $delete_button = $this->getSession()->getPage()->findButton('Delete');
+ $delete_button->click();
+ $assert = $this->assertSession();
+ $assert->addressEquals('/user/' . $this->account->id());
+ $assert->responseContains('Deleted 2 items.');
+ $assert->responseContains('2 items have not been deleted because you do not have the necessary permissions.');
+
+ \Drupal::entityTypeManager()->getStorage('entity_test_mulrevpub')->resetCache();
+ $remaining_entities = EntityTestRev::loadMultiple([$entity1->id(), $entity2->id(), $entity3->id(), $entity4->id()]);
+ $this->assertCount(2, $remaining_entities);
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Action/DeleteActionTest.php b/core/tests/Drupal/KernelTests/Core/Action/DeleteActionTest.php
new file mode 100644
index 0000000..e5a2bd9
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Action/DeleteActionTest.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Action;
+
+use Drupal\Core\Action\Plugin\Action\Derivative\EntityDeleteActionDeriver;
+use Drupal\entity_test\Entity\EntityTestMulRevPub;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\system\Entity\Action;
+use Drupal\user\Entity\User;
+
+/**
+ * @group Action
+ */
+class DeleteActionTest extends KernelTestBase {
+
+ protected $testUser;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['system', 'entity_test', 'user'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installEntitySchema('entity_test_mulrevpub');
+ $this->installEntitySchema('user');
+ $this->installSchema('system', ['sequences', 'key_value_expire']);
+
+ $this->testUser = User::create([
+ 'name' => 'foobar',
+ 'mail' => 'foobar@example.com',
+ ]);
+ $this->testUser->save();
+ \Drupal::service('current_user')->setAccount($this->testUser);
+ }
+
+ /**
+ * @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityDeleteActionDeriver::getDerivativeDefinitions
+ */
+ public function testGetDerivativeDefinitions() {
+ $deriver = new EntityDeleteActionDeriver(\Drupal::entityTypeManager(), \Drupal::translation());
+ $this->assertEquals([
+ 'entity_test_mulrevpub' => [
+ 'type' => 'entity_test_mulrevpub',
+ 'label' => 'Delete test entity - revisions, data table, and published interface',
+ 'action_label' => 'Delete',
+ 'confirm_form_route_name' => 'entity.entity_test_mulrevpub.delete_multiple_form',
+ ],
+ 'entity_test_rev' => [
+ 'type' => 'entity_test_rev',
+ 'label' => 'Delete test entity - revisions',
+ 'action_label' => 'Delete',
+ 'confirm_form_route_name' => 'entity.entity_test_rev.delete_multiple_form',
+ ],
+ ], $deriver->getDerivativeDefinitions([
+ 'action_label' => 'Delete',
+ ]));
+ }
+
+ /**
+ * @covers \Drupal\Core\Action\Plugin\Action\DeleteAction::execute
+ */
+ public function testDeleteAction() {
+ $entity = EntityTestMulRevPub::create(['name' => 'test']);
+ $entity->save();
+
+ $action = Action::create([
+ 'id' => 'entity_delete_action',
+ 'plugin' => 'entity:delete_action:entity_test_mulrevpub',
+ ]);
+ $action->save();
+
+ $action->execute([$entity]);
+ $this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
+
+ /** @var \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store */
+ $temp_store = \Drupal::service('tempstore.private');
+ $store_entries = $temp_store->get('entity_delete_multiple_confirm')->get($this->testUser->id() . ':entity_test_mulrevpub');
+ $this->assertArraySubset([$this->testUser->id() => ['en' => 'en']], $store_entries);
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php b/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php
index 04bf1c0..d381de1 100644
--- a/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php
@@ -29,7 +29,7 @@ class PublishActionTest extends KernelTestBase {
* @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver::getDerivativeDefinitions
*/
public function testGetDerivativeDefinitions() {
- $deriver = new EntityPublishedActionDeriver(\Drupal::entityTypeManager());
+ $deriver = new EntityPublishedActionDeriver(\Drupal::entityTypeManager(), \Drupal::translation());
$this->assertArraySubset([
'entity_test_mulrevpub' => [
'type' => 'entity_test_mulrevpub',
diff --git a/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php b/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php
index 118f542..ed9b287 100644
--- a/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php
@@ -29,7 +29,7 @@ class SaveActionTest extends KernelTestBase {
* @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver::getDerivativeDefinitions
*/
public function testGetDerivativeDefinitions() {
- $deriver = new EntityChangedActionDeriver(\Drupal::entityTypeManager());
+ $deriver = new EntityChangedActionDeriver(\Drupal::entityTypeManager(), \Drupal::translation());
$this->assertArraySubset([
'entity_test_mul_changed' => [
'type' => 'entity_test_mul_changed',
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index f5aa40a..e71d7df 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -325,6 +325,8 @@ trait DeprecationListenerTrait {
'The "session_handler.write_check" service relies on the deprecated "Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler" class. It should either be deprecated or its implementation upgraded.',
'Not setting the strict option of the Choice constraint to true is deprecated since Symfony 3.4 and will throw an exception in 4.0.',
'Using the Yaml::PARSE_KEYS_AS_STRINGS flag is deprecated since Symfony 3.4 as it will be removed in 4.0. Quote your keys when they are evaluable instead.',
+ 'Drupal\node\Plugin\Action\DeleteNode is deprecated in Drupal 8.6.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead. See https://www.drupal.org/node/2934349.',
+ 'Drupal\comment\Plugin\Action\DeleteComment is deprecated in Drupal 8.6.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead. See https://www.drupal.org/node/2934349.',
];
}