mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 00:46:50 +02:00
MDL-37761 Backup: Do not include files in backups when importing courses/activities
When we import courses or duplicate activities, the deduplicating nature of the file_storage system means that we don't actually have to include the real files in the backup - just their metadata from the file table. The restoration process needs to know not to expect files from the backup phase so a flag is set in moodle_backup metadata.
This commit is contained in:
parent
f1454424b5
commit
b1850c12c1
8 changed files with 168 additions and 46 deletions
|
@ -409,6 +409,19 @@ abstract class backup_controller_dbops extends backup_dbops {
|
|||
return (int)(bool)$count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the backupid, determine whether this backup should include
|
||||
* files from the moodle file storage system.
|
||||
*
|
||||
* @param string $backupid The ID of the backup.
|
||||
* @return int Indicates whether files should be included in backups.
|
||||
*/
|
||||
public static function backup_includes_files($backupid) {
|
||||
// Load controller
|
||||
$bc = self::load_controller($backupid);
|
||||
return $bc->get_include_files();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the backupid, detect if the backup contains references to external contents
|
||||
*
|
||||
|
|
|
@ -823,6 +823,9 @@ abstract class restore_dbops {
|
|||
public static function send_files_to_pool($basepath, $restoreid, $component, $filearea, $oldcontextid, $dfltuserid, $itemname = null, $olditemid = null, $forcenewcontextid = null, $skipparentitemidctxmatch = false) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$backupinfo = backup_general_helper::get_backup_information(basename($basepath));
|
||||
$includesfiles = $backupinfo->include_files;
|
||||
|
||||
$results = array();
|
||||
|
||||
if ($forcenewcontextid) {
|
||||
|
@ -900,43 +903,68 @@ abstract class restore_dbops {
|
|||
continue;
|
||||
}
|
||||
|
||||
// The file record to restore.
|
||||
$file_record = array(
|
||||
'contextid' => $newcontextid,
|
||||
'component' => $component,
|
||||
'filearea' => $filearea,
|
||||
'itemid' => $rec->newitemid,
|
||||
'filepath' => $file->filepath,
|
||||
'filename' => $file->filename,
|
||||
'timecreated' => $file->timecreated,
|
||||
'timemodified'=> $file->timemodified,
|
||||
'userid' => $mappeduserid,
|
||||
'author' => $file->author,
|
||||
'license' => $file->license,
|
||||
'sortorder' => $file->sortorder
|
||||
);
|
||||
|
||||
if (empty($file->repositoryid)) {
|
||||
// this is a regular file, it must be present in the backup pool
|
||||
$backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
|
||||
|
||||
// The file is not found in the backup.
|
||||
if (!file_exists($backuppath)) {
|
||||
$result = new stdClass();
|
||||
$result->code = 'file_missing_in_backup';
|
||||
$result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
|
||||
$result->level = backup::LOG_WARNING;
|
||||
$results[] = $result;
|
||||
continue;
|
||||
}
|
||||
|
||||
// create the file in the filepool if it does not exist yet
|
||||
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
|
||||
|
||||
// If no license found, use default.
|
||||
if ($file->license == null){
|
||||
$file->license = $CFG->sitedefaultlicense;
|
||||
// Some file types do not include the files as they should already be
|
||||
// present. We still need to create entries into the files table.
|
||||
if ($includesfiles) {
|
||||
// The file is not found in the backup.
|
||||
if (!file_exists($backuppath)) {
|
||||
$result = new stdClass();
|
||||
$result->code = 'file_missing_in_backup';
|
||||
$result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
|
||||
$result->level = backup::LOG_WARNING;
|
||||
$results[] = $result;
|
||||
continue;
|
||||
}
|
||||
|
||||
$file_record = array(
|
||||
'contextid' => $newcontextid,
|
||||
'component' => $component,
|
||||
'filearea' => $filearea,
|
||||
'itemid' => $rec->newitemid,
|
||||
'filepath' => $file->filepath,
|
||||
'filename' => $file->filename,
|
||||
'timecreated' => $file->timecreated,
|
||||
'timemodified'=> $file->timemodified,
|
||||
'userid' => $mappeduserid,
|
||||
'author' => $file->author,
|
||||
'license' => $file->license,
|
||||
'sortorder' => $file->sortorder
|
||||
);
|
||||
$fs->create_file_from_pathname($file_record, $backuppath);
|
||||
// create the file in the filepool if it does not exist yet
|
||||
if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
|
||||
|
||||
// If no license found, use default.
|
||||
if ($file->license == null){
|
||||
$file->license = $CFG->sitedefaultlicense;
|
||||
}
|
||||
|
||||
$fs->create_file_from_pathname($file_record, $backuppath);
|
||||
}
|
||||
} else {
|
||||
// This backup does not include the files - they should be available in moodle filestorage already.
|
||||
|
||||
// Even if a file has been deleted since the backup was made, the file metadata will remain in the
|
||||
// files table, and the file will not be moved to the trashdir.
|
||||
// Files are not cleared from the files table by cron until several days after deletion.
|
||||
if ($foundfiles = $DB->get_records('files', array('contenthash' => $file->contenthash))) {
|
||||
// Only grab one of the foundfiles - the file content should be the same for all entries.
|
||||
$foundfile = reset($foundfiles);
|
||||
$fs->create_file_from_storedfile($file_record, $foundfile->id);
|
||||
} else {
|
||||
// A matching existing file record was not found in the database.
|
||||
$result = new stdClass();
|
||||
$result->code = 'file_missing_in_backup';
|
||||
$result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
|
||||
$result->level = backup::LOG_WARNING;
|
||||
$results[] = $result;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// store the the new contextid and the new itemid in case we need to remap
|
||||
|
@ -954,20 +982,7 @@ abstract class restore_dbops {
|
|||
// oldfile holds the raw information stored in MBZ (including reference-related info)
|
||||
$info->oldfile = $file;
|
||||
// newfile holds the info for the new file_record with the context, user and itemid mapped
|
||||
$info->newfile = (object)array(
|
||||
'contextid' => $newcontextid,
|
||||
'component' => $component,
|
||||
'filearea' => $filearea,
|
||||
'itemid' => $rec->newitemid,
|
||||
'filepath' => $file->filepath,
|
||||
'filename' => $file->filename,
|
||||
'timecreated' => $file->timecreated,
|
||||
'timemodified'=> $file->timemodified,
|
||||
'userid' => $mappeduserid,
|
||||
'author' => $file->author,
|
||||
'license' => $file->license,
|
||||
'sortorder' => $file->sortorder
|
||||
);
|
||||
$info->newfile = (object) $file_record;
|
||||
|
||||
restore_dbops::set_backup_ids_record($restoreid, 'file_aliases_queue', $file->id, 0, null, $info);
|
||||
}
|
||||
|
|
|
@ -148,6 +148,30 @@ class backup_dbops_testcase extends advanced_testcase {
|
|||
backup_controller_dbops::drop_backup_ids_temp_table('testingid');
|
||||
$this->assertFalse($dbman->table_exists('backup_ids_temp'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check backup_includes_files
|
||||
*/
|
||||
function test_backup_controller_dbops_includes_files() {
|
||||
global $DB;
|
||||
|
||||
$dbman = $DB->get_manager(); // Going to use some database_manager services for testing
|
||||
|
||||
// A MODE_GENERAL controller - this should include files
|
||||
$bc = new mock_backup_controller4dbops(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
|
||||
backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
|
||||
$this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 1);
|
||||
|
||||
// A MODE_IMPORT controller - should not include files
|
||||
$bc = new mock_backup_controller4dbops(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
|
||||
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
|
||||
$this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
|
||||
|
||||
// A MODE_SAMESITE controller - should not include files
|
||||
$bc = new mock_backup_controller4dbops(backup::TYPE_1COURSE, $this->moduleid, backup::FORMAT_MOODLE,
|
||||
backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid);
|
||||
$this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
|
||||
}
|
||||
}
|
||||
|
||||
class mock_backup_controller4dbops extends backup_controller {
|
||||
|
|
|
@ -61,6 +61,11 @@ class backup_file_manager {
|
|||
public static function copy_file_moodle2backup($backupid, $filerecorid) {
|
||||
global $DB;
|
||||
|
||||
if (!backup_controller_dbops::backup_includes_files($backupid)) {
|
||||
// Only include the files if required by the controller.
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalise param
|
||||
if (!is_object($filerecorid)) {
|
||||
$filerecorid = $DB->get_record('files', array('id' => $filerecorid));
|
||||
|
|
|
@ -155,6 +155,12 @@ abstract class backup_general_helper extends backup_helper {
|
|||
} else {
|
||||
$info->include_file_references_to_external_content = 0;
|
||||
}
|
||||
// include_files is a new setting in 2.6.
|
||||
if (isset($infoarr['include_files'])) {
|
||||
$info->include_files = $infoarr['include_files'];
|
||||
} else {
|
||||
$info->include_files = 1;
|
||||
}
|
||||
$info->type = $infoarr['details']['detail'][0]['type'];
|
||||
$info->format = $infoarr['details']['detail'][0]['format'];
|
||||
$info->mode = $infoarr['details']['detail'][0]['mode'];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue