Commit 80f9e581 authored by webchick's avatar webchick

Issue #1399846 by slashrsm, Gábor Hojtsy, Berdir, brantwynn, visabhishek,...

Issue #1399846 by slashrsm, Gábor Hojtsy, Berdir, brantwynn, visabhishek, David_Rothstein, cweagans, martin107, SteffenR, Dave Reid: Make unused file 'cleanup' configurable.
parent acbd5fa8
......@@ -109,8 +109,10 @@
* - filesize: The size of the file in bytes.
* - status: A bitmapped field indicating the status of the file. The first 8
* bits are reserved for Drupal core. The least significant bit indicates
* temporary (0) or permanent (1). Temporary files older than
* DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during cron runs.
* temporary (0) or permanent (1). Temporary files will be removed during
* cron runs if they are older than the configuration value
* "system.file.temporary_maximum_age", and if clean-up is enabled. Permanent
* files will not be removed.
* - timestamp: UNIX timestamp for the date the file was added to the database.
*/
......@@ -142,9 +144,10 @@
/**
* Indicates that the file is permanent and should not be deleted.
*
* Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed
* during cron runs, but permanent files will not be removed during the file
* garbage collection process.
* Temporary files older than the system.file.temporary_maximum_age
* configuration value will be, if clean-up not disabled, removed during cron
* runs, but permanent files will not be removed during the file garbage
* collection process.
*/
const FILE_STATUS_PERMANENT = 1;
......
......@@ -69,7 +69,7 @@ function file_schema() {
'default' => 0,
),
'status' => array(
'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.',
'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than system.file.temporary_maximum_age will be removed during a cron run.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
......
......@@ -694,9 +694,18 @@ function file_file_download($uri, $field_type = 'file') {
* Implements file_cron()
*/
function file_cron() {
$result = \Drupal::entityManager()->getStorage('file')->retrieveTemporaryFiles();
foreach ($result as $row) {
if ($file = file_load($row->fid)) {
$age = \Drupal::config('system.file')->get('temporary_maximum_age');
// Only delete temporary files if older than $age. Note that automatic cleanup
// is disabled if $age set to 0.
if ($age) {
$fids = Drupal::entityQuery('file')
->condition('status', FILE_STATUS_PERMANENT, '<>')
->condition('changed', REQUEST_TIME - $age, '<')
->range(0, 100)
->execute();
$files = file_load_multiple($fids);
foreach ($files as $file) {
$references = \Drupal::service('file.usage')->listUsage($file);
if (empty($references)) {
if (file_exists($file->getFileUri())) {
......
......@@ -61,17 +61,18 @@ function testInUse() {
$this->assertTrue($file->isTemporary(), 'File is temporary.');
file_test_reset();
// Call system_cron() to clean up the file. Make sure the timestamp
// of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Call file_cron() to clean up the file. Make sure the changed timestamp
// of the file is older than the system.file.temporary_maximum_age
// configuration value.
db_update('file_managed')
->fields(array(
'changed' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
'changed' => REQUEST_TIME - ($this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') + 1),
))
->condition('fid', $file->id())
->execute();
\Drupal::service('cron')->run();
// system_cron() loads
// file_cron() loads
$this->assertFileHooksCalled(array('delete'));
$this->assertFalse(file_exists($file->getFileUri()), 'File has been deleted after its last usage was removed.');
$this->assertFalse(file_load($file->id()), 'File was removed from the database.');
......
......@@ -115,11 +115,12 @@ function testRevisions() {
clearstatcache($node_file_r3->getFileUri());
clearstatcache($node_file_r4->getFileUri());
// Call system_cron() to clean up the file. Make sure the timestamp
// of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Call file_cron() to clean up the file. Make sure the changed timestamp
// of the file is older than the system.file.temporary_maximum_age
// configuration value.
db_update('file_managed')
->fields(array(
'changed' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
'changed' => REQUEST_TIME - ($this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') + 1),
))
->condition('fid', $node_file_r3->id())
->execute();
......@@ -130,11 +131,12 @@ function testRevisions() {
// Delete the entire node and check that the original file is deleted.
$this->drupalPostForm('node/' . $nid . '/delete', array(), t('Delete'));
// Call system_cron() to clean up the file. Make sure the timestamp
// of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Call file_cron() to clean up the file. Make sure the changed timestamp
// of the file is older than the system.file.temporary_maximum_age
// configuration value.
db_update('file_managed')
->fields(array(
'changed' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
'changed' => REQUEST_TIME - ($this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') + 1),
))
->condition('fid', $node_file_r1->id())
->execute();
......
......@@ -123,24 +123,24 @@ function testRemoveUsage() {
}
/**
* Ensure that temporary files are removed.
* Create files for all the possible combinations of age and status.
*
* Create files for all the possible combinations of age and status. We are
* using UPDATE statements because using the API would set the timestamp.
* We are using UPDATE statements because using the API would set the
* timestamp.
*/
function testTempFileCleanup() {
// Temporary file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
function createTempFiles() {
// Temporary file that is old.
$temp_old = file_save_data('');
db_update('file_managed')
->fields(array(
'status' => 0,
'changed' => 1,
'changed' => REQUEST_TIME - $this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') - 1,
))
->condition('fid', $temp_old->id())
->execute();
$this->assertTrue(file_exists($temp_old->getFileUri()), 'Old temp file was created correctly.');
// Temporary file that is less than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Temporary file that is new.
$temp_new = file_save_data('');
db_update('file_managed')
->fields(array('status' => 0))
......@@ -148,17 +148,25 @@ function testTempFileCleanup() {
->execute();
$this->assertTrue(file_exists($temp_new->getFileUri()), 'New temp file was created correctly.');
// Permanent file that is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Permanent file that is old.
$perm_old = file_save_data('');
db_update('file_managed')
->fields(array('changed' => 1))
->fields(array('changed' => REQUEST_TIME - $this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') - 1))
->condition('fid', $temp_old->id())
->execute();
$this->assertTrue(file_exists($perm_old->getFileUri()), 'Old permanent file was created correctly.');
// Permanent file that is newer than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Permanent file that is new.
$perm_new = file_save_data('');
$this->assertTrue(file_exists($perm_new->getFileUri()), 'New permanent file was created correctly.');
return array($temp_old, $temp_new, $perm_old, $perm_new);
}
/**
* Ensure that temporary files are removed by default.
*/
function testTempFileCleanupDefault() {
list($temp_old, $temp_new, $perm_old, $perm_new) = $this->createTempFiles();
// Run cron and then ensure that only the old, temp file was deleted.
$this->container->get('cron')->run();
......@@ -167,4 +175,42 @@ function testTempFileCleanup() {
$this->assertTrue(file_exists($perm_old->getFileUri()), 'Old permanent file was correctly ignored.');
$this->assertTrue(file_exists($perm_new->getFileUri()), 'New permanent file was correctly ignored.');
}
/**
* Ensure that temporary files are kept as configured.
*/
function testTempFileNoCleanup() {
list($temp_old, $temp_new, $perm_old, $perm_new) = $this->createTempFiles();
// Set the max age to 0, meaning no temporary files will be deleted.
\Drupal::config('system.file')
->set('temporary_maximum_age', 0)
->save();
// Run cron and then ensure that no file was deleted.
$this->container->get('cron')->run();
$this->assertTrue(file_exists($temp_old->getFileUri()), 'Old temp file was correctly ignored.');
$this->assertTrue(file_exists($temp_new->getFileUri()), 'New temp file was correctly ignored.');
$this->assertTrue(file_exists($perm_old->getFileUri()), 'Old permanent file was correctly ignored.');
$this->assertTrue(file_exists($perm_new->getFileUri()), 'New permanent file was correctly ignored.');
}
/**
* Ensure that temporary files are kept as configured.
*/
function testTempFileCustomCleanup() {
list($temp_old, $temp_new, $perm_old, $perm_new) = $this->createTempFiles();
// Set the max age to older than default.
\Drupal::config('system.file')
->set('temporary_maximum_age', 21600 + 2)
->save();
// Run cron and then ensure that more files were deleted.
$this->container->get('cron')->run();
$this->assertTrue(file_exists($temp_old->getFileUri()), 'Old temp file was correctly ignored.');
$this->assertTrue(file_exists($temp_new->getFileUri()), 'New temp file was correctly ignored.');
$this->assertTrue(file_exists($perm_old->getFileUri()), 'Old permanent file was correctly ignored.');
$this->assertTrue(file_exists($perm_new->getFileUri()), 'New permanent file was correctly ignored.');
}
}
......@@ -355,6 +355,9 @@ system.file:
temporary:
type: string
label: 'Temporary directory'
temporary_maximum_age:
type: integer
label: 'Maximum age for temporary files'
system.image:
type: mapping
......
......@@ -3,3 +3,4 @@ default_scheme: 'public'
path:
private: ''
temporary: ''
temporary_maximum_age: 21600
......@@ -68,6 +68,17 @@ public function buildForm(array $form, array &$form_state) {
);
}
$intervals = array(0, 21600, 43200, 86400, 604800, 2419200, 7776000);
$period = array_combine($intervals, array_map('format_interval', $intervals));
$period[0] = t('Never');
$form['temporary_maximum_age'] = array(
'#type' => 'select',
'#title' => t('Delete orphaned files after'),
'#default_value' => $config->get('temporary_maximum_age'),
'#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.'),
);
return parent::buildForm($form, $form_state);
}
......@@ -77,7 +88,8 @@ public function buildForm(array $form, array &$form_state) {
public function submitForm(array &$form, array &$form_state) {
$config = $this->configFactory->get('system.file')
->set('path.private', $form_state['values']['file_private_path'])
->set('path.temporary', $form_state['values']['file_temporary_path']);
->set('path.temporary', $form_state['values']['file_temporary_path'])
->set('temporary_maximum_age', $form_state['values']['temporary_maximum_age']);
if (isset($form_state['values']['file_default_scheme'])) {
$config->set('default_scheme', $form_state['values']['file_default_scheme']);
......
......@@ -14,11 +14,6 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use GuzzleHttp\Exception\RequestException;
/**
* Maximum age of temporary files in seconds.
*/
const DRUPAL_MAXIMUM_TEMP_FILE_AGE = 21600;
/**
* New users will be set to the default time zone at registration.
*/
......
......@@ -65,11 +65,12 @@ function testCreateDeletePicture() {
$this->drupalPostForm('user/' . $this->web_user->id() . '/edit', $edit, t('Remove'));
$this->drupalPostForm(NULL, array(), t('Save'));
// Call system_cron() to clean up the file. Make sure the timestamp
// of the file is older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
// Call file_cron() to clean up the file. Make sure the timestamp
// of the file is older than the system.file.temporary_maximum_age
// configuration value.
db_update('file_managed')
->fields(array(
'changed' => REQUEST_TIME - (DRUPAL_MAXIMUM_TEMP_FILE_AGE + 1),
'changed' => REQUEST_TIME - ($this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age') + 1),
))
->condition('fid', $file->id())
->execute();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment