diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php index 45c8aa7cec53c1814415d7cb134ce7d7030b13f6..ca4e887a1b3fd193006cd2eb657a8fceacf18f8f 100644 --- a/core/modules/file/src/Element/ManagedFile.php +++ b/core/modules/file/src/Element/ManagedFile.php @@ -398,15 +398,23 @@ public static function preRenderManagedFile($element) { * Render API callback: Validates the managed_file element. */ public static function validateManagedFile(&$element, FormStateInterface $form_state, &$complete_form) { - // If referencing an existing file, only allow if there are existing - // references. This prevents unmanaged files from being deleted if this - // item were to be deleted. $clicked_button = end($form_state->getTriggeringElement()['#parents']); if ($clicked_button != 'remove_button' && !empty($element['fids']['#value'])) { $fids = $element['fids']['#value']; foreach ($fids as $fid) { if ($file = File::load($fid)) { - if ($file->isPermanent()) { + // If referencing an existing file, only allow if there are existing + // references. This prevents unmanaged files from being deleted if + // this item were to be deleted. When files that are no longer in use + // are automatically marked as temporary (now disabled by default), + // it is not safe to reference a permanent file without usage. Adding + // a usage and then later on removing it again would delete the file, + // but it is unknown if and where it is currently referenced. However, + // when files are not marked temporary (and then removed) + // automatically, it is safe to add and remove usages, as it would + // simply return to the current state. + // @see https://www.drupal.org/node/2891902 + if ($file->isPermanent() && \Drupal::config('file.settings')->get('make_unused_managed_files_temporary')) { $references = static::fileUsage()->listUsage($file); if (empty($references)) { // We expect the field name placeholder value to be wrapped in t() diff --git a/core/modules/file/src/Tests/FileManagedFileElementTest.php b/core/modules/file/src/Tests/FileManagedFileElementTest.php index 8abff25e45ff09347c2fabcfe439f3ed8711a52e..1ff00b90d917adf6502692be4cb291072f5011f6 100644 --- a/core/modules/file/src/Tests/FileManagedFileElementTest.php +++ b/core/modules/file/src/Tests/FileManagedFileElementTest.php @@ -194,4 +194,55 @@ public function testFileRemovedFromDisk() { $file->delete(); } + /** + * Verify that unused permanent files can be used. + */ + public function testUnusedPermanentFileValidation() { + + // Create a permanent file without usages. + $file = $this->getTestFile('image'); + $file->setPermanent(); + $file->save(); + + // By default, unused files are no longer marked temporary, and it must be + // allowed to reference an unused file. + $this->drupalGet('file/test/1/0/1/' . $file->id()); + $this->drupalPostForm(NULL, [], 'Save'); + $this->assertNoText('The file used in the Managed file & butter field may not be referenced.'); + $this->assertText('The file ids are ' . $file->id()); + + // Enable marking unused files as tempory, unused permanent files must not + // be referenced now. + $this->config('file.settings') + ->set('make_unused_managed_files_temporary', TRUE) + ->save(); + $this->drupalGet('file/test/1/0/1/' . $file->id()); + $this->drupalPostForm(NULL, [], 'Save'); + $this->assertText('The file used in the Managed file & butter field may not be referenced.'); + $this->assertNoText('The file ids are ' . $file->id()); + + // Make the file temporary, now using it is allowed. + $file->setTemporary(); + $file->save(); + + $this->drupalGet('file/test/1/0/1/' . $file->id()); + $this->drupalPostForm(NULL, [], 'Save'); + $this->assertNoText('The file used in the Managed file & butter field may not be referenced.'); + $this->assertText('The file ids are ' . $file->id()); + + // Make the file permanent again and add a usage from itself, referencing is + // still allowed. + $file->setPermanent(); + $file->save(); + + /** @var \Drupal\file\FileUsage\FileUsageInterface $file_usage */ + $file_usage = \Drupal::service('file.usage'); + $file_usage->add($file, 'file', 'file', $file->id()); + + $this->drupalGet('file/test/1/0/1/' . $file->id()); + $this->drupalPostForm(NULL, [], 'Save'); + $this->assertNoText('The file used in the Managed file & butter field may not be referenced.'); + $this->assertText('The file ids are ' . $file->id()); + } + }