Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete content node along with its media and file #905

Merged
merged 12 commits into from
Dec 6, 2022
1 change: 1 addition & 0 deletions config/install/islandora.settings.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
broker_url: 'tcp://localhost:61613'
jwt_expiry: '+2 hour'
gemini_url: ''
delete_media_and_files: TRUE
shriram10567 marked this conversation as resolved.
Show resolved Hide resolved
gemini_pseudo_bundles: []
5 changes: 4 additions & 1 deletion config/schema/islandora.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ islandora.settings:
jwt_expiry:
type: string
label: 'How long JWTs should last before expiring.'
delete_media_and_files:
type: boolean
label: 'Node Delete with Media and Files'
upload_form_location:
type: string
label: 'Upload Form Location'
Expand Down Expand Up @@ -166,7 +169,7 @@ condition.plugin.node_had_namespace:
pid_field:
type: ignore
label: 'PID field'

field.formatter.settings.islandora_image:
type: field.formatter.settings.image
label: 'Islandora image field display format settings'
Expand Down
3 changes: 3 additions & 0 deletions css/islandora.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container .islandora-media-items {
margin: 0;
}
5 changes: 5 additions & 0 deletions islandora.libraries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
islandora:
version: VERSION
shriram10567 marked this conversation as resolved.
Show resolved Hide resolved
css:
theme:
css/islandora.css: {}
193 changes: 193 additions & 0 deletions islandora.module
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use Drupal\file\FileInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\serialization\Normalizer\CacheableNormalizerInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\file\Entity\File;

/**
* Implements hook_help().
Expand Down Expand Up @@ -331,6 +333,197 @@ function islandora_form_alter(&$form, FormStateInterface $form_state, $form_id)
}
}
}

$form_object = $form_state->getFormObject();
$utils = \Drupal::service('islandora.utils');
$config = \Drupal::config('islandora.settings')->get('delete_media_and_files');

if ($config == 1 && $form_object instanceof EntityForm) {
$entity = $form_object->getEntity();

if ($entity->getEntityTypeId() == "node" && $utils->isIslandoraType($entity->getEntityTypeId(), $entity->bundle()) && strpos($form['#form_id'], 'delete_form') !== FALSE) {
$medias = $utils->getMedia($form_state->getFormObject()->getEntity());
if (count($medias) != 0) {
$form['delete_associated_content'] = [
'#type' => 'checkbox',
'#title' => t('Delete all associated medias and nodes'),
];

$media_list = [];

foreach ($medias as $media) {
$media_list[] = $media->getName();
}

$form['container'] = [
'#type' => 'container',
'#states' => [
'visible' => [
':input[name="delete_associated_content"]' => ['checked' => TRUE],
],
],
];

$form['container']['media_items'] = [
'#theme' => 'item_list',
'#type' => 'ul',
'#items' => $media_list,
'#attributes' => ['class' => ['islandora-media-items']],
'#wrapper_attributes' => ['class' => ['container']],
'#attached' => [
'library' => [
'islandora/islandora',
],
],
];

$form['actions']['submit']['#submit'][] = 'islandora_object_delete_form_submit';
return $form;
}
}
}

return $form;
}

/**
* Implements a submit handler for the delete form.
*/
function islandora_object_delete_form_submit($form, FormStateInterface $form_state) {

$result = $form_state->getValues('delete_associated_content');
$utils = \Drupal::service('islandora.utils');

if ($result['delete_associated_content'] == 1) {

$node = $form_state->getFormObject()->getEntity();
$medias = $utils->getMedia($node);
$media_list = [];

$entity_field_manager = \Drupal::service('entity_field.manager');
$current_user = \Drupal::currentUser();
$logger = \Drupal::logger('logger.channel.islandora');
$messenger = \Drupal::messenger();

$delete_media = [];
$media_translations = [];
$media_files = [];
$entity_protected_medias = [];
$inaccessible_entities = [];

foreach ($medias as $id => $media) {
$lang = $media->language()->getId();
$selected_langcodes[$lang] = $lang;

if (!$media->access('delete', $current_user)) {
$inaccessible_entities[] = $media;
continue;
}
// Check for files.
$fields = $entity_field_manager->getFieldDefinitions('media', $media->bundle());
foreach ($fields as $field) {
$type = $field->getType();
if ($type == 'file' || $type == 'image') {
$target_id = $media->get($field->getName())->target_id;
$file = File::load($target_id);
if ($file) {
if (!$file->access('delete', $current_user)) {
$inaccessible_entities[] = $file;
continue;
}
$media_files[$id][$file->id()] = $file;
}
}
}

foreach ($selected_langcodes as $langcode) {
// We're only working with media, which are translatable.
$entity = $media->getTranslation($langcode);
if ($entity->isDefaultTranslation()) {
$delete_media[$id] = $entity;
unset($media_translations[$id]);
}
elseif (!isset($delete_media[$id])) {
$media_translations[$id][] = $entity;
}
}
}

if ($delete_media) {
foreach ($delete_media as $id => $media) {
try {
$media->delete();
$media_list[] = $id;
$logger->notice('The media %label has been deleted.', [
'%label' => $media->label(),
]);
}
catch (Exception $e) {
$entity_protected_medias[] = $id;
}
}
}

$delete_files = array_filter($media_files, function ($media) use ($entity_protected_medias) {
return !in_array($media, $entity_protected_medias);
}, ARRAY_FILTER_USE_KEY);

if ($delete_files) {
foreach ($delete_files as $files_array) {
foreach ($files_array as $file) {
$file->delete();
$logger->notice('The file %label has been deleted.', [
'%label' => $file->label(),
]);
}
}
}

$delete_media_translations = array_filter($media_translations, function ($media) use ($entity_protected_medias) {
return !in_array($media, $entity_protected_medias);
}, ARRAY_FILTER_USE_KEY);

if ($delete_media_translations) {
foreach ($delete_media_translations as $id => $translations) {
$media = $medias[$id];
foreach ($translations as $translation) {
$media->removeTranslation($translation->language()->getId());
}
$media->save();
foreach ($translations as $translation) {
$logger->notice('The media %label @language translation has been deleted', [
'%label' => $media->label(),
'@language' => $translation->language()->getName(),
]);
}
}
}

if ($inaccessible_entities) {
$messenger->addWarning("@count items have not been deleted because you do not have the necessary permissions.", [
'@count' => count($inaccessible_entities),
]);
}

$build = [
'heading' => [
'#type' => 'html_tag',
'#tag' => 'div',
'#value' => t("The repository item @node and @media", [
'@node' => $node->getTitle(),
'@media' => \Drupal::translation()->formatPlural(
count($media_list), 'the media with the id @media has been deleted.',
'the medias with the ids @media have been deleted.',
['@media' => implode(", ", $media_list)],
),
]),
],
];

$message = \Drupal::service('renderer')->renderPlain($build);
$messenger->deleteByType('status');
$messenger->addStatus($message);
}
}

/**
Expand Down
16 changes: 16 additions & 0 deletions islandora.post_update.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/**
* @file
* Post updates.
*/

/**
* Set default value for delete_media_and_files field in settings.
*/
function islandora_post_update_delete_media_and_files() {
$config_factory = \Drupal::configFactory();
$config = $config_factory->getEditable('islandora.settings');
$config->set('delete_media_and_files', TRUE);
$config->save(TRUE);
}
10 changes: 10 additions & 0 deletions src/Form/IslandoraSettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class IslandoraSettingsForm extends ConfigFormBase {
'year',
];
const GEMINI_PSEUDO_FIELD = 'field_gemini_uri';
const NODE_DELETE_MEDIA_AND_FILES = 'delete_media_and_files';

/**
* To list the available bundle types.
Expand Down Expand Up @@ -201,6 +202,14 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$fedora_url = NULL;
}

$form[self::NODE_DELETE_MEDIA_AND_FILES] = [
'#type' => 'checkbox',
'#title' => $this->t('Node Delete with Media and Files'),
'#description' => $this->t('Adds a checkbox in the "Delete" tab of islandora objects to delete media and files associated with the object.'
),
'#default_value' => (bool) $config->get(self::NODE_DELETE_MEDIA_AND_FILES),
];

$form[self::FEDORA_URL] = [
'#type' => 'textfield',
'#title' => $this->t('Fedora URL'),
Expand Down Expand Up @@ -351,6 +360,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
->set(self::UPLOAD_FORM_LOCATION, $form_state->getValue(self::UPLOAD_FORM_LOCATION))
->set(self::UPLOAD_FORM_ALLOWED_MIMETYPES, $form_state->getValue(self::UPLOAD_FORM_ALLOWED_MIMETYPES))
->set(self::GEMINI_PSEUDO, $new_pseudo_types)
->set(self::NODE_DELETE_MEDIA_AND_FILES, $form_state->getValue(self::NODE_DELETE_MEDIA_AND_FILES))
->save();

parent::submitForm($form, $form_state);
Expand Down
104 changes: 104 additions & 0 deletions tests/src/Functional/DeleteNodeWithMediaAndFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Drupal\Tests\islandora\Functional;

/**
* Tests the Delete Node with Media.
*
* @group islandora
*/
class DeleteNodeWithMediaAndFile extends IslandoraFunctionalTestBase {

/**
* Tests delete Node and its assoicated media.
*/
public function testDeleteNodeWithMediaAndFile() {
$account = $this->drupalCreateUser([
'delete any media',
'create media',
'view media',
'bypass node access',
'access files overview',
'administer site configuration',
]);
$this->drupalLogin($account);

$assert_session = $this->assertSession();

$testImageMediaType = $this->createMediaType('image', ['id' => 'test_image_media_type']);
$testImageMediaType->save();

$this->createEntityReferenceField('media', $testImageMediaType->id(), 'field_media_of', 'Media Of', 'node', 'default', [], 2);

$node = $this->container->get('entity_type.manager')->getStorage('node')->create([
'type' => 'test_type',
'title' => 'node',
]);
$node->save();

// Make an image for the Media.
$file = $this->container->get('entity_type.manager')->getStorage('file')->create([
'uid' => $account->id(),
'uri' => "public://test.jpeg",
'filename' => "test.jpeg",
'filemime' => "image/jpeg",
'status' => FILE_STATUS_PERMANENT,
]);
$file->save();

$this->drupalGet("node/1/delete");
$assert_session->pageTextNotContains('Delete all associated medias and nodes');

// Make the media, and associate it with the image and node.
$media1 = $this->container->get('entity_type.manager')->getStorage('media')->create([
'bundle' => $testImageMediaType->id(),
'name' => 'Media1',
'field_media_image' =>
[
'target_id' => $file->id(),
'alt' => 'Some Alt',
'title' => 'Some Title',
],
'field_media_of' => ['target_id' => $node->id()],
]);
$media1->save();

$media2 = $this->container->get('entity_type.manager')->getStorage('media')->create([
'bundle' => $testImageMediaType->id(),
'name' => 'Media2',
'field_media_image' =>
[
'target_id' => $file->id(),
'alt' => 'Some Alt',
'title' => 'Some Title',
],
'field_media_of' => ['target_id' => $node->id()],
]);
$media2->save();

$this->drupalGet("admin/config/islandora/core");
$assert_session->pageTextContains('Node Delete with Media and Files');
\Drupal::configFactory()->getEditable('islandora.settings')->set('delete_media_and_files', TRUE)->save();

$delete = ['delete_associated_content' => TRUE];

$this->drupalGet("node/1/delete");
$assert_session->pageTextContains('Media1');
$assert_session->pageTextContains('Media2');
$this->submitForm($delete, 'Delete');

$assert_session->pageTextContains($media1->id());
$assert_session->pageTextContains($media2->id());

$this->drupalGet("media/1/delete");
$assert_session->pageTextContains('Page not found');

$this->drupalGet("media/2/delete");
$assert_session->pageTextContains('Page not found');

$this->drupalGet("/admin/content/files");
$assert_session->pageTextNotContains('test.jpeg');

}

}