mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
MDL-35773 Backup: API should have option to not backup files
Allow both UI and automated backups to be created without including files. Instead include only file references. This is essentially implementing "SAMESITE" to backup files instead of only for import and export functionality. A new backup setting to include files (defaults to yes) has been included. The restore process will also look for and attempt to restore files from the trashdir as part of restoring backups. Additionally to support this process the ammount of time files are kept in trashdir before they are cleaned up via cron is also adjustable via admin setting.
This commit is contained in:
parent
f622ee97e3
commit
d7e4481e98
17 changed files with 190 additions and 18 deletions
|
@ -150,6 +150,13 @@ if (!async_helper::is_async_pending($id, 'course', 'backup')) {
|
|||
$loghtml = '';
|
||||
if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
|
||||
|
||||
// Before we perform the backup check settings to see if user
|
||||
// or setting defaults are set to exclude files from the backup.
|
||||
if ($backup->get_setting_value('files') == 0) {
|
||||
$bc->set_mode(backup::MODE_SAMESITE);
|
||||
$renderer->set_samesite_notification();
|
||||
}
|
||||
|
||||
if ($backupmode != backup::MODE_ASYNC) {
|
||||
// Synchronous backup handling.
|
||||
|
||||
|
@ -180,6 +187,7 @@ if (!async_helper::is_async_pending($id, 'course', 'backup')) {
|
|||
// Hide the progress display and first backup step bar (the 'finished' step will show next).
|
||||
echo html_writer::end_div();
|
||||
echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
|
||||
|
||||
} else {
|
||||
// Async backup handling.
|
||||
$backup->get_controller()->finish_ui();
|
||||
|
@ -203,6 +211,8 @@ if (!async_helper::is_async_pending($id, 'course', 'backup')) {
|
|||
'restoreurl' => $restoreurl->out(),
|
||||
'headingident' => 'backup'
|
||||
);
|
||||
|
||||
echo $renderer->set_samesite_notification();
|
||||
echo $renderer->render_from_template('core/async_backup_status', $progresssetup);
|
||||
}
|
||||
|
||||
|
|
|
@ -185,6 +185,20 @@ class backup_controller extends base_controller {
|
|||
backup_check::check_security($this, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode (purpose) of the backup.
|
||||
*
|
||||
* @param int $mode The mode to set.
|
||||
*/
|
||||
public function set_mode($mode) {
|
||||
$this->mode = $mode;
|
||||
$this->set_include_files(); // Need to check if files are included as mode may have changed.
|
||||
$this->save_controller();
|
||||
$tbc = self::load_controller($this->backupid);
|
||||
$this->logger = $tbc->logger; // Wakeup loggers.
|
||||
$tbc->plan->destroy(); // Clean plan controller structures, keeping logger alive.
|
||||
}
|
||||
|
||||
public function set_status($status) {
|
||||
// Note: never save_controller() with the object info after STATUS_EXECUTING or the whole controller,
|
||||
// containing all the steps will be sent to DB. 100% (monster) useless.
|
||||
|
@ -410,6 +424,13 @@ class backup_controller extends base_controller {
|
|||
$includefiles = false;
|
||||
}
|
||||
|
||||
// If backup is automated and we have set auto backup config to exclude
|
||||
// files then set them to be excluded here.
|
||||
$backupautofiles = (bool)get_config('backup', 'backup_auto_files');
|
||||
if ($this->get_mode() === backup::MODE_AUTOMATED && !$backupautofiles) {
|
||||
$includefiles = false;
|
||||
}
|
||||
|
||||
$this->includefiles = (int) $includefiles;
|
||||
$this->log("setting file inclusion to {$this->includefiles}", backup::LOG_DEBUG);
|
||||
return $this->includefiles;
|
||||
|
|
|
@ -110,6 +110,12 @@ class backup_root_task extends backup_task {
|
|||
$this->add_setting($blocks);
|
||||
$this->converter_deps($blocks, $converters);
|
||||
|
||||
// Define files.
|
||||
$files = new backup_generic_setting('files', base_setting::IS_BOOLEAN, true);
|
||||
$files->set_ui(new backup_setting_ui_checkbox($files, get_string('rootsettingfiles', 'backup')));
|
||||
$this->add_setting($files);
|
||||
$this->converter_deps($files, $converters);
|
||||
|
||||
// Define filters
|
||||
$filters = new backup_generic_setting('filters', base_setting::IS_BOOLEAN, true);
|
||||
$filters->set_ui(new backup_setting_ui_checkbox($filters, get_string('rootsettingfilters', 'backup')));
|
||||
|
|
|
@ -201,7 +201,8 @@ abstract class backup_plan_dbops extends backup_dbops {
|
|||
* @param bool $useidonly only use the ID in the file name
|
||||
* @return string The filename to use
|
||||
*/
|
||||
public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidonly = false) {
|
||||
public static function get_default_backup_filename($format, $type, $id, $users, $anonymised,
|
||||
$useidonly = false, $files = true) {
|
||||
global $DB;
|
||||
|
||||
// Calculate backup word
|
||||
|
@ -251,6 +252,11 @@ abstract class backup_plan_dbops extends backup_dbops {
|
|||
$info = '-an';
|
||||
}
|
||||
|
||||
// Indicate if backup doesn't contain files.
|
||||
if (!$files) {
|
||||
$info .= '-nf';
|
||||
}
|
||||
|
||||
return $backupword . '-' . $format . '-' . $type . '-' .
|
||||
$name . '-' . $date . $info . '.mbz';
|
||||
}
|
||||
|
|
|
@ -1054,17 +1054,25 @@ abstract class restore_dbops {
|
|||
// 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)) {
|
||||
|
||||
// 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.
|
||||
// Even if a file has been deleted since the backup was made, the file metadata may remain in the
|
||||
// files table, and the file will not yet have been moved to the trashdir. e.g. a draft file version.
|
||||
// Try to recover from file table first.
|
||||
if ($foundfiles = $DB->get_records('files', array('contenthash' => $file->contenthash), '', '*', 0, 1)) {
|
||||
// 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.
|
||||
$results[] = self::get_missing_file_result($file);
|
||||
continue;
|
||||
// Finally try to restore the file from trash.
|
||||
$filesytem = $fs->get_file_system();
|
||||
$restorefile = $file;
|
||||
$restorefile->contextid = $newcontextid;
|
||||
$storedfile = new stored_file($fs, $restorefile);
|
||||
$trashrecovery = $filesytem->recover_file($storedfile, true);
|
||||
if (!$trashrecovery) {
|
||||
// A matching file was not found.
|
||||
$results[] = self::get_missing_file_result($file);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -415,8 +415,9 @@ abstract class backup_cron_automated_helper {
|
|||
$id = $bc->get_id();
|
||||
$users = $bc->get_plan()->get_setting('users')->get_value();
|
||||
$anonymised = $bc->get_plan()->get_setting('anonymize')->get_value();
|
||||
$incfiles = (bool)$config->backup_auto_files;
|
||||
$bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type,
|
||||
$id, $users, $anonymised));
|
||||
$id, $users, $anonymised, false, $incfiles));
|
||||
|
||||
$bc->set_status(backup::STATUS_AWAITING);
|
||||
|
||||
|
|
|
@ -287,7 +287,15 @@ abstract class backup_helper {
|
|||
$config = get_config('backup');
|
||||
$dir = $config->backup_auto_destination;
|
||||
if ($config->backup_auto_storage == 1 and $dir and is_dir($dir) and is_writable($dir)) {
|
||||
$filedest = $dir.'/'.backup_plan_dbops::get_default_backup_filename($format, $backuptype, $courseid, $hasusers, $isannon, !$config->backup_shortname);
|
||||
$filedest = $dir.'/'
|
||||
.backup_plan_dbops::get_default_backup_filename(
|
||||
$format,
|
||||
$backuptype,
|
||||
$courseid,
|
||||
$hasusers,
|
||||
$isannon,
|
||||
!$config->backup_shortname,
|
||||
(bool)$config->backup_auto_files);
|
||||
// first try to move the file, if it is not possible copy and delete instead
|
||||
if (@rename($filepath, $filedest)) {
|
||||
return null;
|
||||
|
|
|
@ -155,7 +155,9 @@ class backup_ui_stage_initial extends backup_ui_stage {
|
|||
$this->ui->get_type(),
|
||||
$this->ui->get_controller_id(),
|
||||
$this->ui->get_setting_value('users'),
|
||||
$this->ui->get_setting_value('anonymize')
|
||||
$this->ui->get_setting_value('anonymize'),
|
||||
false,
|
||||
(bool)$this->ui->get_setting_value('files')
|
||||
);
|
||||
$setting->set_value($filename);
|
||||
}
|
||||
|
@ -457,7 +459,16 @@ class backup_ui_stage_confirmation extends backup_ui_stage {
|
|||
$id = $this->ui->get_controller_id();
|
||||
$users = $this->ui->get_setting_value('users');
|
||||
$anonymised = $this->ui->get_setting_value('anonymize');
|
||||
$setting->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
|
||||
$files = (bool)$this->ui->get_setting_value('files');
|
||||
$filename = backup_plan_dbops::get_default_backup_filename(
|
||||
$format,
|
||||
$type,
|
||||
$id,
|
||||
$users,
|
||||
$anonymised,
|
||||
false,
|
||||
$files);
|
||||
$setting->set_value($filename);
|
||||
}
|
||||
$form->add_setting($setting, $task);
|
||||
break;
|
||||
|
@ -628,6 +639,7 @@ class backup_ui_stage_complete extends backup_ui_stage_final {
|
|||
if (!empty($this->results['missing_files_in_pool'])) {
|
||||
$output .= $renderer->notification(get_string('missingfilesinpool', 'backup'), 'notifyproblem');
|
||||
}
|
||||
$output .= $renderer->get_samesite_notification();
|
||||
$output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
|
||||
$output .= $renderer->continue_button($restorerul);
|
||||
$output .= $renderer->box_end();
|
||||
|
|
|
@ -43,6 +43,13 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
|
|||
*/
|
||||
class core_backup_renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Same site notification display.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $samesitenotification = '';
|
||||
|
||||
/**
|
||||
* Renderers a progress bar for the backup or restore given the items that make it up.
|
||||
*
|
||||
|
@ -80,6 +87,22 @@ class core_backup_renderer extends plugin_renderer_base {
|
|||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the same site backup notification.
|
||||
*
|
||||
*/
|
||||
public function set_samesite_notification() {
|
||||
$this->samesitenotification = $this->output->notification(get_string('samesitenotification', 'backup'), 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the same site backup notification.
|
||||
*
|
||||
*/
|
||||
public function get_samesite_notification() {
|
||||
return $this->samesitenotification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a dependency notification
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue