diff --git a/core/modules/media_library/src/Form/FileUploadForm.php b/core/modules/media_library/src/Form/FileUploadForm.php index cfc85651e03d13fe6ccbb070f79b32c7e5ba5883..81367821c25fca550eeb5afe4102ee75213e7729 100644 --- a/core/modules/media_library/src/Form/FileUploadForm.php +++ b/core/modules/media_library/src/Form/FileUploadForm.php @@ -14,6 +14,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\Url; use Drupal\file\FileInterface; +use Drupal\file\FileUsage\FileUsageInterface; use Drupal\file\Plugin\Field\FieldType\FileFieldItemList; use Drupal\file\Plugin\Field\FieldType\FileItem; use Drupal\media\MediaInterface; @@ -53,6 +54,13 @@ class FileUploadForm extends AddFormBase { */ protected $fileSystem; + /** + * The file usage service. + * + * @var \Drupal\file\FileUsage\FileUsageInterface + */ + protected $fileUsage; + /** * Constructs a new FileUploadForm. * @@ -68,12 +76,19 @@ class FileUploadForm extends AddFormBase { * The file system service. * @param \Drupal\media_library\OpenerResolverInterface $opener_resolver * The opener resolver. + * @param \Drupal\file\FileUsage\FileUsageInterface $file_usage + * The file usage service. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer, FileSystemInterface $file_system, OpenerResolverInterface $opener_resolver = NULL) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, ElementInfoManagerInterface $element_info, RendererInterface $renderer, FileSystemInterface $file_system, OpenerResolverInterface $opener_resolver = NULL, FileUsageInterface $file_usage = NULL) { parent::__construct($entity_type_manager, $library_ui_builder, $opener_resolver); $this->elementInfo = $element_info; $this->renderer = $renderer; $this->fileSystem = $file_system; + if (!$file_usage) { + @trigger_error('Calling FileUploadForm::__construct() without the `file.usage` service is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Pass the `file.usage` service to the constructor. See https://www.drupal.org/node/3075165', E_USER_DEPRECATED); + $file_usage = \Drupal::service('file.usage'); + } + $this->fileUsage = $file_usage; } /** @@ -86,7 +101,8 @@ public static function create(ContainerInterface $container) { $container->get('element_info'), $container->get('renderer'), $container->get('file_system'), - $container->get('media_library.opener_resolver') + $container->get('media_library.opener_resolver'), + $container->get('file.usage') ); } @@ -290,4 +306,29 @@ protected function prepareMediaEntityForSave(MediaInterface $media) { $file->save(); } + /** + * Submit handler for the remove button. + * + * @param array $form + * The form render array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public function removeButtonSubmit(array $form, FormStateInterface $form_state) { + // Retrieve the delta of the media item from the parents of the remove + // button. + $triggering_element = $form_state->getTriggeringElement(); + $delta = array_slice($triggering_element['#array_parents'], -2, 1)[0]; + + /** @var \Drupal\media\MediaInterface $removed_media */ + $removed_media = $form_state->get(['media', $delta]); + + $file = $removed_media->get($this->getSourceFieldName($removed_media->bundle->entity))->entity; + if ($file instanceof FileInterface && empty($this->fileUsage->listUsage($file))) { + $file->delete(); + } + + parent::removeButtonSubmit($form, $form_state); + } + } diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php index 180c6fa2097f972832b2e597b7be3760b2b13e4f..8e559d257a421f8ca0473e3e11d8a6a6a4829774 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php @@ -1321,7 +1321,7 @@ public function testWidgetUpload() { // Create a list of new files to upload. $filenames = []; $remote_paths = []; - foreach (range(1, 3) as $i) { + foreach (range(1, 4) as $i) { $path = $file_system->copy($png_image->uri, 'public://'); $filenames[] = $file_system->basename($path); $remote_paths[] = $driver->uploadFileAndGetRemoteFilePath($file_system->realpath($path)); @@ -1336,19 +1336,50 @@ public function testWidgetUpload() { $assert_session->fieldValueEquals('media[0][fields][name][0][value]', $filenames[0]); $assert_session->fieldValueEquals('media[1][fields][name][0][value]', $filenames[1]); $assert_session->fieldValueEquals('media[2][fields][name][0][value]', $filenames[2]); + $assert_session->fieldValueEquals('media[3][fields][name][0][value]', $filenames[3]); // Assert the pre-selected items are shown. - $selection_area = $assert_session->elementExists('css', '.media-library-add-form__selected-media'); + $this->assertNotEmpty($selection_area = $assert_session->waitForElement('css', '.media-library-add-form__selected-media')); $assert_session->elementExists('css', 'summary', $selection_area)->click(); $assert_session->checkboxChecked("Select $existing_media_name", $selection_area); - // Set alt texts for items 1 and 2, leave the alt text empty for item 3 to - // assert the field validation does not stop users from removing items. + // Set alt texts for items 1 and 2, leave the alt text empty for items 3 + // and 4 to assert the field validation does not stop users from removing + // items. $page->fillField('media[0][fields][field_media_test_image][0][alt]', $filenames[0]); $page->fillField('media[1][fields][field_media_test_image][0][alt]', $filenames[1]); + // Assert the file is available in the file storage. + $files = $file_storage->loadByProperties(['filename' => $filenames[1]]); + $this->assertCount(1, $files); + $file_1_uri = reset($files)->getFileUri(); // Remove the second file and assert the focus is shifted to the container // of the next media item and field values are still correct. $page->pressButton('media-1-remove-button'); $this->assertJsCondition('jQuery(".media-library-add-form__media[data-media-library-added-delta=2]").is(":focus")'); $assert_session->pageTextContains('The media item ' . $filenames[1] . ' has been removed.'); + // Assert the file was deleted. + $this->assertEmpty($file_storage->loadByProperties(['filename' => $filenames[1]])); + $this->assertFileNotExists($file_1_uri); + + // When a file is already in usage, it should not be deleted. To test, + // let's add a usage for $filenames[3] (now in the third position). + $files = $file_storage->loadByProperties(['filename' => $filenames[3]]); + $this->assertCount(1, $files); + $target_file = reset($files); + Media::create([ + 'bundle' => 'type_three', + 'name' => 'Disturbing', + 'field_media_test_image' => [ + ['target_id' => $target_file->id()], + ], + ])->save(); + // Remove $filenames[3] (now in the third position) and assert the focus is + // shifted to the container of the previous media item and field values are + // still correct. + $page->pressButton('media-3-remove-button'); + $this->assertTrue($assert_session->waitForText('The media item ' . $filenames[3] . ' has been removed.')); + // Assert the file was not deleted, due to being in use elsewhere. + $this->assertNotEmpty($file_storage->loadByProperties(['filename' => $filenames[3]])); + $this->assertFileExists($target_file->getFileUri()); + // The second media item should be removed (this has the delta 1 since we // start counting from 0). $assert_session->elementNotExists('css', '.media-library-add-form__media[data-media-library-added-delta=1]');