Browse Source

Merge pull request #792 from qadan/delete-confirm

Delete confirm
pull/794/head
Alan Stanley 4 years ago committed by GitHub
parent
commit
3f58dfd583
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      islandora.routing.yml
  2. 182
      src/Form/ConfirmDeleteMediaAndFile.php
  3. 111
      src/Plugin/Action/DeleteMediaAndFile.php
  4. 48
      tests/src/Functional/DeleteMediaTest.php

8
islandora.routing.yml

@ -57,3 +57,11 @@ islandora.media_source_put_to_node:
_custom_access: '\Drupal\islandora\Controller\MediaSourceController::putToNodeAccess'
options:
_auth: ['basic_auth', 'cookie', 'jwt_auth']
islandora.confirm_delete_media_and_file:
path: '/media/delete_with_files'
defaults:
_form: 'Drupal\islandora\Form\ConfirmDeleteMediaAndFile'
requirements:
_permission: 'administer media+delete any media'

182
src/Form/ConfirmDeleteMediaAndFile.php

@ -0,0 +1,182 @@
<?php
namespace Drupal\islandora\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Form\DeleteMultipleForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\islandora\MediaSource\MediaSourceService;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Confirmation form for the 'Delete media and file(s)' action.
*/
class ConfirmDeleteMediaAndFile extends DeleteMultipleForm {
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSourceService;
/**
* Logger.
*
* @var Psr\Log\LoggerInterface
*/
protected $logger;
/**
* List of media targeted.
*
* @var array
*/
protected $selection = [];
/**
* {@inheritdoc}
*/
public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, MessengerInterface $messenger, MediaSourceService $media_source_service, LoggerInterface $logger) {
$this->currentUser = $current_user;
$this->entityTypeManager = $entity_type_manager;
$this->tempStore = $temp_store_factory->get('media_and_file_delete_confirm');
$this->messenger = $messenger;
$this->mediaSourceService = $media_source_service;
$this->logger = $logger;
}
/**
* {@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'),
$container->get('islandora.media_source_service'),
$container->get('logger.channel.islandora'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'media_and_file_delete_confirm_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->selection),
'Are you sure you want to delete this media and associated files?',
'Are you sure you want to delete these media and associated files?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.media.collection');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
return parent::buildForm($form, $form_state, 'media');
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Similar to parent::submitForm(), but let's blend in the related files and
// optimize based on the fact that we know we're working with media.
$total_count = 0;
$delete_media = [];
$delete_media_translations = [];
$delete_files = [];
$inaccessible_entities = [];
$media_storage = $this->entityTypeManager->getStorage('media');
$file_storage = $this->entityTypeManager->getStorage('file');
$media = $media_storage->loadMultiple(array_keys($this->selection));
foreach ($this->selection as $id => $selected_langcodes) {
$entity = $media[$id];
if (!$entity->access('delete', $this->currentUser)) {
$inaccessible_entities[] = $entity;
continue;
}
// Check for files.
$source_field = $this->mediaSourceService->getSourceFieldName($entity->bundle());
foreach ($entity->get($source_field)->referencedEntities() as $file) {
if (!$file->access('delete', $this->currentUser)) {
$inaccessible_entities[] = $file;
continue;
}
$delete_files[$file->id()] = $file;
$total_count++;
}
foreach ($selected_langcodes as $langcode) {
// We're only working with media, which are translatable.
$entity = $entity->getTranslation($langcode);
if ($entity->isDefaultTranslation()) {
$delete_media[$id] = $entity;
unset($delete_media_translations[$id]);
$total_count += count($entity->getTranslationLanguages());
}
elseif (!isset($delete_media[$id])) {
$delete_media_translations[$id][] = $entity;
}
}
}
if ($delete_media) {
$media_storage->delete($delete_media);
foreach ($delete_media as $entity) {
$this->logger->notice('The media %label has been deleted.', [
'%label' => $entity->label(),
]);
}
}
if ($delete_files) {
$file_storage->delete($delete_files);
foreach ($delete_files as $entity) {
$this->logger->notice('The file %label has been deleted.', [
'%label' => $entity->label(),
]);
}
}
if ($delete_media_translations) {
foreach ($delete_media_translations as $id => $translations) {
$entity = $media[$id];
foreach ($translations as $translation) {
$entity->removeTranslation($translation->language()->getId());
}
$entity->save();
foreach ($translations as $translation) {
$this->logger->notice('The media %label @language translation has been deleted', [
'%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());
}
}

111
src/Plugin/Action/DeleteMediaAndFile.php

@ -2,13 +2,10 @@
namespace Drupal\islandora\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Action\Plugin\Action\DeleteAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
/**
* Deletes a media and its source file.
@ -16,105 +13,35 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Action(
* id = "delete_media_and_file",
* label = @Translation("Delete media and file"),
* type = "media"
* type = "media",
* confirm_form_route_name = "islandora.confirm_delete_media_and_file"
* )
*/
class DeleteMediaAndFile extends ActionBase implements ContainerFactoryPluginInterface {
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSourceService;
/**
* Database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Logger.
*
* @var Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructor.
*
* @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\islandora\MediaSource\MediaSourceService $media_source_service
* Media source service.
* @param \Drupal\Core\Database\Connection $connection
* Database connection.
* @param Psr\Log\LoggerInterface $logger
* Logger.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MediaSourceService $media_source_service,
Connection $connection,
LoggerInterface $logger
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->mediaSourceService = $media_source_service;
$this->connection = $connection;
$this->logger = $logger;
}
class DeleteMediaAndFile extends DeleteAction {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('islandora.media_source_service'),
$container->get('database'),
$container->get('logger.channel.islandora')
);
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('media_and_file_delete_confirm');
$this->entityTypeManager = $entity_type_manager;
$this->configuration = $configuration;
$this->pluginId = $plugin_id;
$this->pluginDefinition = $plugin_definition;
}
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
if (!$entity) {
return;
}
$transaction = $this->connection->startTransaction();
public function executeMultiple(array $entities) {
try {
// Delete all the source files and then the media.
$source_field = $this->mediaSourceService->getSourceFieldName($entity->bundle());
foreach ($entity->get($source_field)->referencedEntities() as $file) {
$file->delete();
}
$entity->delete();
$selection = [];
foreach ($entities as $entity) {
$langcode = $entity->language()->getId();
$selection[$entity->id()][$langcode] = $langcode;
}
catch (\Exception $e) {
$transaction->rollBack();
$this->logger->error("Cannot delete media and its files. Rolling back transaction: @msg", ["@msg" => $e->getMessage()]);
}
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
return $object->access('delete', $account, $return_as_object);
$this->tempStore->set("{$this->currentUser->id()}:media", $selection);
}
}

48
tests/src/Functional/DeleteMediaTest.php

@ -2,6 +2,8 @@
namespace Drupal\Tests\islandora\Functional;
use Drupal\views\Views;
/**
* Tests the DeleteMedia and DeleteMediaAndFile actions.
*
@ -9,6 +11,18 @@ namespace Drupal\Tests\islandora\Functional;
*/
class DeleteMediaTest extends IslandoraFunctionalTestBase {
/**
* Modules to be enabled.
*
* @var array
*/
public static $modules = [
'media_test_views',
'context_ui',
'field_ui',
'islandora',
];
/**
* Media.
*
@ -23,6 +37,13 @@ class DeleteMediaTest extends IslandoraFunctionalTestBase {
*/
protected $file;
/**
* User account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* {@inheritdoc}
*/
@ -30,9 +51,9 @@ class DeleteMediaTest extends IslandoraFunctionalTestBase {
parent::setUp();
// Create a test user.
$account = $this->createUser(['create media']);
$this->account = $this->createUser(['create media', 'delete any media']);
list($this->file, $this->media) = $this->makeMediaAndFile($account);
list($this->file, $this->media) = $this->makeMediaAndFile($this->account);
}
/**
@ -41,12 +62,31 @@ class DeleteMediaTest extends IslandoraFunctionalTestBase {
* @covers \Drupal\islandora\Plugin\Action\DeleteMediaAndFile::execute
*/
public function testDeleteMediaAndFile() {
$action = $this->container->get('entity_type.manager')->getStorage('action')->load('delete_media_and_file');
$this->drupalLogin($this->account);
$session = $this->getSession();
$page = $session->getPage();
$mid = $this->media->id();
$fid = $this->file->id();
$action->execute([$this->media]);
// Ensure the media is in the test view.
$view = Views::getView('test_media_bulk_form');
$view->execute();
$this->assertSame($view->total_rows, 1);
$this->drupalGet('test-media-bulk-form');
// Check that the option exists.
$this->assertSession()->optionExists('action', 'delete_media_and_file');
// Run the bulk action.
$page->checkField('media_bulk_form[0]');
$page->selectFieldOption('action', 'delete_media_and_file');
$page->pressButton('Apply to selected items');
$this->assertSession()->pageTextContains('Are you sure you want to delete this media and associated files?');
$page->pressButton('Delete');
// Should assert that a media and file were deleted.
$this->assertSession()->pageTextContains('Deleted 2 items.');
// Attempt to reload the entities.
// Both media and file should be gone.

Loading…
Cancel
Save