Issue #2801777 by Berdir, Wim Leers, Pol, alexpott, dawehner, Jo Fitzgerald, Munavijayalakshmi, poornima.n, ifrik, Bojhan, catch: Prevent drupal from deleting temporary files
parent
e0ea6c67e5
commit
e3ceb190af
|
|
@ -3,4 +3,4 @@ description:
|
||||||
length: 128
|
length: 128
|
||||||
icon:
|
icon:
|
||||||
directory: 'core/modules/file/icons'
|
directory: 'core/modules/file/icons'
|
||||||
|
make_unused_managed_files_temporary: false
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ file.settings:
|
||||||
directory:
|
directory:
|
||||||
type: path
|
type: path
|
||||||
label: 'Directory'
|
label: 'Directory'
|
||||||
|
make_unused_managed_files_temporary:
|
||||||
|
type: boolean
|
||||||
|
label: 'Controls if unused files should be marked temporary'
|
||||||
|
|
||||||
field.storage_settings.file:
|
field.storage_settings.file:
|
||||||
type: base_entity_reference_field_settings
|
type: base_entity_reference_field_settings
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,15 @@ function file_requirements($phase) {
|
||||||
|
|
||||||
return $requirements;
|
return $requirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent unused files from being deleted.
|
||||||
|
*/
|
||||||
|
function file_update_8300() {
|
||||||
|
// Disable deletion of unused permanent files.
|
||||||
|
\Drupal::configFactory()->getEditable('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', FALSE)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
return t('Files that have no remaining usages are no longer deleted by default.');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
services:
|
services:
|
||||||
file.usage:
|
file.usage:
|
||||||
class: Drupal\file\FileUsage\DatabaseFileUsageBackend
|
class: Drupal\file\FileUsage\DatabaseFileUsageBackend
|
||||||
arguments: ['@database']
|
arguments: ['@database', 'file_usage', '@config.factory']
|
||||||
tags:
|
tags:
|
||||||
- { name: backend_overridable }
|
- { name: backend_overridable }
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Drupal\file\FileUsage;
|
namespace Drupal\file\FileUsage;
|
||||||
|
|
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
use Drupal\Core\Database\Connection;
|
use Drupal\Core\Database\Connection;
|
||||||
use Drupal\file\FileInterface;
|
use Drupal\file\FileInterface;
|
||||||
|
|
||||||
|
|
@ -32,8 +33,11 @@ class DatabaseFileUsageBackend extends FileUsageBase {
|
||||||
* information.
|
* information.
|
||||||
* @param string $table
|
* @param string $table
|
||||||
* (optional) The table to store file usage info. Defaults to 'file_usage'.
|
* (optional) The table to store file usage info. Defaults to 'file_usage'.
|
||||||
|
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||||
|
* (optional) The config factory.
|
||||||
*/
|
*/
|
||||||
public function __construct(Connection $connection, $table = 'file_usage') {
|
public function __construct(Connection $connection, $table = 'file_usage', ConfigFactoryInterface $config_factory = NULL) {
|
||||||
|
parent::__construct($config_factory);
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
|
|
||||||
$this->tableName = $table;
|
$this->tableName = $table;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Drupal\file\FileUsage;
|
namespace Drupal\file\FileUsage;
|
||||||
|
|
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
use Drupal\file\FileInterface;
|
use Drupal\file\FileInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -9,6 +10,27 @@ use Drupal\file\FileInterface;
|
||||||
*/
|
*/
|
||||||
abstract class FileUsageBase implements FileUsageInterface {
|
abstract class FileUsageBase implements FileUsageInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config factory.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||||
|
*/
|
||||||
|
protected $configFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a FileUsageBase object.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||||
|
* (optional) The config factory. Defaults to NULL and will use
|
||||||
|
* \Drupal::configFactory() instead.
|
||||||
|
*
|
||||||
|
* @deprecated The $config_factory parameter will become required in Drupal
|
||||||
|
* 9.0.0.
|
||||||
|
*/
|
||||||
|
public function __construct(ConfigFactoryInterface $config_factory = NULL) {
|
||||||
|
$this->configFactory = $config_factory ?: \Drupal::configFactory();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,6 +46,10 @@ abstract class FileUsageBase implements FileUsageInterface {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function delete(FileInterface $file, $module, $type = NULL, $id = NULL, $count = 1) {
|
public function delete(FileInterface $file, $module, $type = NULL, $id = NULL, $count = 1) {
|
||||||
|
// Do not actually mark files as temporary when the behavior is disabled.
|
||||||
|
if (!$this->configFactory->get('file.settings')->get('make_unused_managed_files_temporary')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// If there are no more remaining usages of this file, mark it as temporary,
|
// If there are no more remaining usages of this file, mark it as temporary,
|
||||||
// which result in a delete through system_cron().
|
// which result in a delete through system_cron().
|
||||||
$usage = \Drupal::service('file.usage')->listUsage($file);
|
$usage = \Drupal::service('file.usage')->listUsage($file);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@ class FileFieldRevisionTest extends FileFieldTestBase {
|
||||||
* should be deleted also.
|
* should be deleted also.
|
||||||
*/
|
*/
|
||||||
public function testRevisions() {
|
public function testRevisions() {
|
||||||
|
// This test expects unused managed files to be marked as a temporary file
|
||||||
|
// and then deleted up by file_cron().
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||||
$type_name = 'article';
|
$type_name = 'article';
|
||||||
$field_name = strtolower($this->randomMachineName());
|
$field_name = strtolower($this->randomMachineName());
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ class FileListingTest extends FileFieldTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
// This test expects unused managed files to be marked as a temporary file.
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->adminUser = $this->drupalCreateUser(['access files overview', 'bypass node access']);
|
$this->adminUser = $this->drupalCreateUser(['access files overview', 'bypass node access']);
|
||||||
$this->baseUser = $this->drupalCreateUser();
|
$this->baseUser = $this->drupalCreateUser();
|
||||||
$this->createFileField('file', 'node', 'article', [], ['file_extensions' => 'txt png']);
|
$this->createFileField('file', 'node', 'article', [], ['file_extensions' => 'txt png']);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,11 @@ class FileOnTranslatedEntityTest extends FileFieldTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
// This test expects unused managed files to be marked as temporary a file.
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
|
|
||||||
// Create the "Basic page" node type.
|
// Create the "Basic page" node type.
|
||||||
// @todo Remove the disabling of new revision creation in
|
// @todo Remove the disabling of new revision creation in
|
||||||
// https://www.drupal.org/node/1239558.
|
// https://www.drupal.org/node/1239558.
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ class FilePrivateTest extends FileFieldTestBase {
|
||||||
node_access_test_add_field(NodeType::load('article'));
|
node_access_test_add_field(NodeType::load('article'));
|
||||||
node_access_rebuild();
|
node_access_rebuild();
|
||||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||||
|
// This test expects unused managed files to be marked as a temporary file.
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\file\Tests\Update;
|
||||||
|
|
||||||
|
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the upgrade path for setting the file usage deletion configuration.
|
||||||
|
*
|
||||||
|
* @see https://www.drupal.org/node/2801777
|
||||||
|
*
|
||||||
|
* @group Update
|
||||||
|
*/
|
||||||
|
class FileUsageTemporaryDeletionConfigurationUpdateTest extends UpdatePathTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected static $modules = ['file'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setDatabaseDumpFiles() {
|
||||||
|
$this->databaseDumpFiles = [
|
||||||
|
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that block context mapping is updated properly.
|
||||||
|
*/
|
||||||
|
public function testUpdateHookN() {
|
||||||
|
$this->assertIdentical($this->config('file.settings')->get('make_unused_managed_files_temporary'), NULL);
|
||||||
|
$this->runUpdates();
|
||||||
|
$this->assertIdentical($this->config('file.settings')->get('make_unused_managed_files_temporary'), FALSE);
|
||||||
|
$this->assertResponse('200');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,11 @@ class DeleteTest extends FileManagedUnitTestBase {
|
||||||
* Tries deleting a file that is in use.
|
* Tries deleting a file that is in use.
|
||||||
*/
|
*/
|
||||||
public function testInUse() {
|
public function testInUse() {
|
||||||
|
// This test expects unused managed files to be marked as a temporary file
|
||||||
|
// and then deleted up by file_cron().
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
$file = $this->createFile();
|
$file = $this->createFile();
|
||||||
$file_usage = $this->container->get('file.usage');
|
$file_usage = $this->container->get('file.usage');
|
||||||
$file_usage->add($file, 'testing', 'test', 1);
|
$file_usage->add($file, 'testing', 'test', 1);
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,34 @@ class UsageTest extends FileManagedUnitTestBase {
|
||||||
$this->assertEqual($usage[2]->count, 2, 'Correct count');
|
$this->assertEqual($usage[2]->count, 2, 'Correct count');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests file usage deletion when files are made temporary.
|
||||||
|
*/
|
||||||
|
public function testRemoveUsageTemporary() {
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
|
$file = $this->doTestRemoveUsage();
|
||||||
|
$this->assertTrue($file->isTemporary());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests file usage deletion when files are made temporary.
|
||||||
|
*/
|
||||||
|
public function testRemoveUsageNonTemporary() {
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', FALSE)
|
||||||
|
->save();
|
||||||
|
$file = $this->doTestRemoveUsage();
|
||||||
|
$this->assertFalse($file->isTemporary());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests \Drupal\file\FileUsage\DatabaseFileUsageBackend::delete().
|
* Tests \Drupal\file\FileUsage\DatabaseFileUsageBackend::delete().
|
||||||
*/
|
*/
|
||||||
public function testRemoveUsage() {
|
public function doTestRemoveUsage() {
|
||||||
$file = $this->createFile();
|
$file = $this->createFile();
|
||||||
|
$file->setPermanent();
|
||||||
$file_usage = $this->container->get('file.usage');
|
$file_usage = $this->container->get('file.usage');
|
||||||
db_insert('file_usage')
|
db_insert('file_usage')
|
||||||
->fields([
|
->fields([
|
||||||
|
|
@ -116,6 +139,7 @@ class UsageTest extends FileManagedUnitTestBase {
|
||||||
->execute()
|
->execute()
|
||||||
->fetchField();
|
->fetchField();
|
||||||
$this->assertIdentical(FALSE, $count, 'Decrementing non-exist record complete.');
|
$this->assertIdentical(FALSE, $count, 'Decrementing non-exist record complete.');
|
||||||
|
return $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
// This test expects unused managed files to be marked as a temporary file.
|
||||||
|
$this->config('file.settings')->set('make_unused_managed_files_temporary', TRUE)->save();
|
||||||
|
|
||||||
// Create the "Basic page" node type.
|
// Create the "Basic page" node type.
|
||||||
// @todo Remove the disabling of new revision creation in
|
// @todo Remove the disabling of new revision creation in
|
||||||
// https://www.drupal.org/node/1239558.
|
// https://www.drupal.org/node/1239558.
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ class FileSystemForm extends ConfigFormBase {
|
||||||
$period[0] = t('Never');
|
$period[0] = t('Never');
|
||||||
$form['temporary_maximum_age'] = [
|
$form['temporary_maximum_age'] = [
|
||||||
'#type' => 'select',
|
'#type' => 'select',
|
||||||
'#title' => t('Delete orphaned files after'),
|
'#title' => t('Delete temporary files after'),
|
||||||
'#default_value' => $config->get('temporary_maximum_age'),
|
'#default_value' => $config->get('temporary_maximum_age'),
|
||||||
'#options' => $period,
|
'#options' => $period,
|
||||||
'#description' => t('Orphaned files are not referenced from any content but remain in the file system and may appear in administrative listings. <strong>Warning:</strong> If enabled, orphaned files will be permanently deleted and may not be recoverable.'),
|
'#description' => t('Temporary files are not referenced, but are in the file system and therefore may show up in administrative lists. <strong>Warning:</strong> If enabled, temporary files will be permanently deleted and may not be recoverable.'),
|
||||||
];
|
];
|
||||||
|
|
||||||
return parent::buildForm($form, $form_state);
|
return parent::buildForm($form, $form_state);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ class UserPictureTest extends WebTestBase {
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
// This test expects unused managed files to be marked temporary and then
|
||||||
|
// cleaned up by file_cron().
|
||||||
|
$this->config('file.settings')
|
||||||
|
->set('make_unused_managed_files_temporary', TRUE)
|
||||||
|
->save();
|
||||||
|
|
||||||
$this->webUser = $this->drupalCreateUser([
|
$this->webUser = $this->drupalCreateUser([
|
||||||
'access content',
|
'access content',
|
||||||
'access comments',
|
'access comments',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue