diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 8f4d5df2ff6..e8ae8ee5218 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2332,5 +2332,10 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2018051703.06); } + if ($oldversion < 2018051703.08) { + upgrade_delete_orphaned_file_records(); + upgrade_main_savepoint(true, 2018051703.08); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index fa7dcf74699..e7e162fb78a 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -539,3 +539,30 @@ function upgrade_fix_serialized_objects($serializeddata) { } return [$updated, $serializeddata]; } + +/** + * Deletes file records which have their repository deleted. + * + */ +function upgrade_delete_orphaned_file_records() { + global $DB; + + $sql = "SELECT f.id, f.contextid, f.component, f.filearea, f.itemid, fr.id AS referencefileid + FROM {files} f + JOIN {files_reference} fr ON f.referencefileid = fr.id + LEFT JOIN {repository_instances} ri ON fr.repositoryid = ri.id + WHERE ri.id IS NULL"; + + $deletedfiles = $DB->get_recordset_sql($sql); + + $deletedfileids = array(); + + $fs = get_file_storage(); + foreach ($deletedfiles as $deletedfile) { + $fs->delete_area_files($deletedfile->contextid, $deletedfile->component, $deletedfile->filearea, $deletedfile->itemid); + $deletedfileids[] = $deletedfile->referencefileid; + } + $deletedfiles->close(); + + $DB->delete_records_list('files_reference', 'id', $deletedfileids); +} diff --git a/lib/tests/upgradelib_test.php b/lib/tests/upgradelib_test.php index 0dc466e1623..4d2fe22a2b3 100644 --- a/lib/tests/upgradelib_test.php +++ b/lib/tests/upgradelib_test.php @@ -920,4 +920,89 @@ class core_upgradelib_testcase extends advanced_testcase { $record = $DB->get_record('block_instances', ['id' => $entryid]); $this->assertEquals($expected, $record->configdata); } + + /** + * Check that orphaned files are deleted. + */ + public function test_upgrade_delete_orphaned_file_records() { + global $DB, $CFG; + require_once($CFG->dirroot . '/repository/lib.php'); + + $this->resetAfterTest(); + // Create user. + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $this->setUser($user); + $usercontext = context_user::instance($user->id); + $syscontext = context_system::instance(); + + $fs = get_file_storage(); + + $userrepository = array(); + $newstoredfile = array(); + $repositorypluginname = array('user', 'areafiles'); + + // Create two repositories with one file in each. + foreach ($repositorypluginname as $key => $value) { + // Override repository permission. + $capability = 'repository/' . $value . ':view'; + $guestroleid = $DB->get_field('role', 'id', array('shortname' => 'guest')); + assign_capability($capability, CAP_ALLOW, $guestroleid, $syscontext->id, true); + + $args = array(); + $args['type'] = $value; + $repos = repository::get_instances($args); + $userrepository[$key] = reset($repos); + + $this->assertInstanceOf('repository', $userrepository[$key]); + + $component = 'user'; + $filearea = 'private'; + $itemid = $key; + $filepath = '/'; + $filename = 'userfile.txt'; + $filerecord = array( + 'contextid' => $usercontext->id, + 'component' => $component, + 'filearea' => $filearea, + 'itemid' => $itemid, + 'filepath' => $filepath, + 'filename' => $filename, + ); + + $content = 'Test content'; + $originalfile = $fs->create_file_from_string($filerecord, $content); + $this->assertInstanceOf('stored_file', $originalfile); + + $newfilerecord = array( + 'contextid' => $syscontext->id, + 'component' => 'core', + 'filearea' => 'phpunit', + 'itemid' => $key, + 'filepath' => $filepath, + 'filename' => $filename, + ); + $ref = $fs->pack_reference($filerecord); + $newstoredfile[$key] = $fs->create_file_from_reference($newfilerecord, $userrepository[$key]->id, $ref); + + // Look for references by repository ID. + $files = $fs->get_external_files($userrepository[$key]->id); + $file = reset($files); + $this->assertEquals($file, $newstoredfile[$key]); + } + + // Make one file orphaned by deleting first repository. + $DB->delete_records('repository_instances', array('id' => $userrepository[0]->id)); + $DB->delete_records('repository_instance_config', array('instanceid' => $userrepository[0]->id)); + + upgrade_delete_orphaned_file_records(); + + $files = $fs->get_external_files($userrepository[0]->id); + $file = reset($files); + $this->assertFalse($file); + + $files = $fs->get_external_files($userrepository[1]->id); + $file = reset($files); + $this->assertEquals($file, $newstoredfile[1]); + } } diff --git a/version.php b/version.php index 0b9a9a2f5b6..47d6bff0fb0 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2018051703.07; // 20180517 = branching date YYYYMMDD - do not modify! +$version = 2018051703.08; // 20180517 = branching date YYYYMMDD - do not modify! // RR = release increments - 00 in DEV branches. // .XX = incremental changes.