MDL-69983 backup: Save async backups in correct area.

This commit is contained in:
Franziska Hübler 2023-06-20 11:22:20 +02:00 committed by David Woloszyn
parent 6628845087
commit 09a97ba0bf
9 changed files with 144 additions and 39 deletions

View File

@ -128,6 +128,7 @@ class core_backup_external extends external_api {
array(
'filename' => new external_value(PARAM_FILE, 'Backup filename', VALUE_REQUIRED, null, NULL_NOT_ALLOWED),
'contextid' => new external_value(PARAM_INT, 'Context id', VALUE_REQUIRED, null, NULL_NOT_ALLOWED),
'backupid' => new external_value(PARAM_ALPHANUMEXT, 'Backup id', VALUE_REQUIRED, null, NULL_NOT_ALLOWED),
)
);
}
@ -138,9 +139,10 @@ class core_backup_external extends external_api {
*
* @param string $filename The file name of the backup file.
* @param int $contextid The context the backup relates to.
* @param string $backupid The backup ID to get the backup settings.
* @since Moodle 3.7
*/
public static function get_async_backup_links_backup($filename, $contextid) {
public static function get_async_backup_links_backup($filename, $contextid, $backupid) {
// Release session lock.
\core\session\manager::write_close();
@ -149,7 +151,8 @@ class core_backup_external extends external_api {
self::get_async_backup_links_backup_parameters(),
array(
'filename' => $filename,
'contextid' => $contextid
'contextid' => $contextid,
'backupid' => $backupid,
)
);
@ -158,10 +161,18 @@ class core_backup_external extends external_api {
self::validate_context($context);
require_capability('moodle/backup:backupcourse', $context);
if ($cm) {
$filearea = 'activity';
} else {
$filearea = 'course';
// Backups without user info or with the anonymise functionality enabled are sent
// to user's "user_backup" file area.
$filearea = 'backup';
// Get useful info to render async status in correct area.
$bc = \backup_controller::load_controller($backupid);
list($hasusers, $isannon) = \async_helper::get_userdata_backup_settings($bc);
if ($hasusers && !$isannon) {
if ($cm) {
$filearea = 'activity';
} else {
$filearea = 'course';
}
}
$results = \async_helper::get_backup_file_info($filename, $filearea, $contextid);

View File

@ -87,7 +87,7 @@ class async_backup_test extends \advanced_testcase {
set_config('buffersize', 0, 'logstore_standard');
get_log_manager(true);
// Start backup process.
// Case 1: Make a course backup without users.
$this->setUser($teacher->id);
// Make the backup controller for an async backup.
@ -132,6 +132,52 @@ class async_backup_test extends \advanced_testcase {
'target' => 'course_backup'], '*', MUST_EXIST);
$otherdata = json_decode($logrec->other);
$this->assertEquals($backupid, $otherdata->backupid);
// Check backup was stored in correct area.
$usercontextid = $DB->get_field('context', 'id', ['contextlevel' => CONTEXT_USER, 'instanceid' => $teacher->id]);
$this->assertEquals(1, $DB->count_records('files', ['contextid' => $usercontextid,
'component' => 'user', 'filearea' => 'backup', 'filename' => 'backup.mbz']));
// Case 2: Make a second backup with users and not anonymised.
$this->setAdminUser();
$bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_YES, backup::MODE_ASYNC, $USER->id);
$bc->get_plan()->get_setting('users')->set_status(\backup_setting::NOT_LOCKED);
$bc->get_plan()->get_setting('users')->set_value(true);
$bc->get_plan()->get_setting('anonymize')->set_value(false);
$bc->finish_ui();
$backupid = $bc->get_backupid();
$bc->destroy();
// Create the adhoc task.
$asynctask = new \core\task\asynchronous_backup_task();
$asynctask->set_blocking(false);
$asynctask->set_custom_data(['backupid' => $backupid]);
\core\task\manager::queue_adhoc_task($asynctask);
// Execute adhoc task.
$now = time();
$task = \core\task\manager::get_next_adhoc_task($now);
$task->execute();
\core\task\manager::adhoc_task_complete($task);
$postbackuprec = $DB->get_record('backup_controllers', ['backupid' => $backupid]);
// Check backup was created successfully.
$this->assertEquals(backup::STATUS_FINISHED_OK, $postbackuprec->status);
$this->assertEquals(1.0, $postbackuprec->progress);
$this->assertEquals($USER->id, $postbackuprec->userid);
// Check that the backupid was logged correctly.
$logrec = $DB->get_record('logstore_standard_log', ['userid' => $postbackuprec->userid,
'target' => 'course_backup'], '*', MUST_EXIST);
$otherdata = json_decode($logrec->other);
$this->assertEquals($backupid, $otherdata->backupid);
// Check backup was stored in correct area.
$coursecontextid = $DB->get_field('context', 'id', ['contextlevel' => CONTEXT_COURSE, 'instanceid' => $course->id]);
$this->assertEquals(1, $DB->count_records('files', ['contextid' => $coursecontextid,
'component' => 'backup', 'filearea' => 'course', 'filename' => 'backup.mbz']));
}
/**

View File

@ -313,39 +313,82 @@ class async_helper {
* Get markup for in progress async backups,
* to use in backup table UI.
*
* @param \core_backup_renderer $renderer The backup renderer object.
* @param string $filearea The filearea to get backup data for.
* @param integer $instanceid The context id to get backup data for.
* @return array $tabledata the rows of table data.
*/
public static function get_async_backups($renderer, $instanceid) {
public static function get_async_backups($filearea, $instanceid) {
global $DB;
$tabledata = array();
$backups = [];
// Get relevant backup ids based on context instance id.
$select = 'itemid = :itemid AND execution = :execution AND status < :status1 AND status > :status2 ' .
$table = 'backup_controllers';
$select = 'execution = :execution AND status < :status1 AND status > :status2 ' .
'AND operation = :operation';
$params = [
'itemid' => $instanceid,
'execution' => backup::EXECUTION_DELAYED,
'status1' => backup::STATUS_FINISHED_ERR,
'status2' => backup::STATUS_NEED_PRECHECK,
'operation' => 'backup',
];
$sort = 'timecreated DESC';
$fields = 'id, backupid, status, timecreated';
$backups = $DB->get_records_select('backup_controllers', $select, $params, 'timecreated DESC', 'id, backupid, timecreated');
foreach ($backups as $backup) {
$bc = \backup_controller::load_controller($backup->backupid); // Get the backup controller.
$filename = $bc->get_plan()->get_setting('filename')->get_value();
$timecreated = $backup->timecreated;
$status = $renderer->get_status_display($bc->get_status(), $bc->get_backupid());
$bc->destroy();
if ($filearea == 'backup') {
// Get relevant backup ids based on user id.
$params['userid'] = $instanceid;
$select = 'userid = :userid AND ' . $select;
$records = $DB->get_records_select($table, $select, $params, $sort, $fields);
foreach ($records as $record) {
$bc = \backup_controller::load_controller($record->backupid);
$tablerow = array($filename, userdate($timecreated), '-', '-', '-', $status);
$tabledata[] = $tablerow;
// Get useful info to render async status in correct area.
list($hasusers, $isannon) = self::get_userdata_backup_settings($bc);
// Backup has users and is not anonymised -> don't show it in users backup file area.
if ($hasusers && !$isannon) {
continue;
}
$record->filename = $bc->get_plan()->get_setting('filename')->get_value();
$bc->destroy();
array_push($backups, $record);
}
} else {
if ($filearea == 'course' || $filearea == 'activity') {
// Get relevant backup ids based on context instance id.
$params['itemid'] = $instanceid;
$select = 'itemid = :itemid AND ' . $select;
$records = $DB->get_records_select($table, $select, $params, $sort, $fields);
foreach ($records as $record) {
$bc = \backup_controller::load_controller($record->backupid);
// Get useful info to render async status in correct area.
list($hasusers, $isannon) = self::get_userdata_backup_settings($bc);
// Backup has no user or is anonymised -> don't show it in course/activity backup file area.
if (!$hasusers || $isannon) {
continue;
}
$record->filename = $bc->get_plan()->get_setting('filename')->get_value();
$bc->destroy();
array_push($backups, $record);
}
}
}
return $tabledata;
return $backups;
}
/**
* Get the user data settings for backups.
*
* @param \backup_controller $backupcontroller The backup controller object.
* @return array Array of user data settings.
*/
public static function get_userdata_backup_settings(\backup_controller $backupcontroller): array {
$hasusers = (bool)$backupcontroller->get_plan()->get_setting('users')->get_value(); // Backup has users.
$isannon = (bool)$backupcontroller->get_plan()->get_setting('anonymize')->get_value(); // Backup is anonymised.
return [$hasusers, $isannon];
}
/**

View File

@ -331,7 +331,7 @@ abstract class backup_helper {
// enabled are sent to user's "user_backup"
// file area. Maintenance of such area is responsibility of
// the user via corresponding file manager frontend
if ($backupmode == backup::MODE_GENERAL && (!$hasusers || $isannon)) {
if (($backupmode == backup::MODE_GENERAL || $backupmode == backup::MODE_ASYNC) && (!$hasusers || $isannon)) {
$ctxid = context_user::instance($userid)->id;
$component = 'user';
$filearea = 'backup';

View File

@ -140,12 +140,11 @@ class async_helper_test extends \advanced_testcase {
unset($bc);
$coursecontext = \context_course::instance($course->id);
$renderer = $PAGE->get_renderer('core', 'backup');
$result = \async_helper::get_async_backups($renderer, $coursecontext->instanceid);
$result = \async_helper::get_async_backups('course', $coursecontext->instanceid);
$this->assertEquals(1, count($result));
$this->assertEquals('backup.mbz', $result[0][0]);
$this->assertEquals('backup.mbz', $result[0]->filename);
}
/**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -100,7 +100,8 @@ define(['jquery', 'core/ajax', 'core/str', 'core/notification', 'core/templates'
methodname: 'core_backup_get_async_backup_links_backup',
args: {
'filename': filename,
'contextid': contextid
'contextid': contextid,
'backupid': backupid
},
}])[0].done(function(response) {
// We have the data now update the UI.

View File

@ -619,10 +619,10 @@ class core_backup_renderer extends plugin_renderer_base {
* @return string
*/
public function render_backup_files_viewer(backup_files_viewer $viewer) {
global $CFG;
$files = $viewer->files;
$async = async_helper::is_async_enabled();
$async = \async_helper::is_async_enabled();
$tablehead = array(
get_string('filename', 'backup'),
@ -638,16 +638,21 @@ class core_backup_renderer extends plugin_renderer_base {
$table->attributes['class'] = 'backup-files-table generaltable';
$table->head = $tablehead;
$table->width = '100%';
$table->data = array();
$table->data = [];
// First add in progress asynchronous backups.
// Only if asynchronous backups are enabled.
// Also only render async status in correct area. Courese OR activity (not both).
if ($async
&& (($viewer->filearea == 'course' && $viewer->currentcontext->contextlevel == CONTEXT_COURSE)
|| ($viewer->filearea == 'activity' && $viewer->currentcontext->contextlevel == CONTEXT_MODULE))
) {
$table->data = \async_helper::get_async_backups($this, $viewer->currentcontext->instanceid);
if ($async) {
$tabledata = [];
$backups = \async_helper::get_async_backups($viewer->filearea, $viewer->filecontext->instanceid);
// For each backup get, new item name, time restore created and progress.
foreach ($backups as $backup) {
$status = $this->get_status_display($backup->status, $backup->backupid);
$timecreated = $backup->timecreated;
$tablerow = [$backup->filename, userdate($timecreated), '-', '-', '-', $status];
$tabledata[] = $tablerow;
}
$table->data = $tabledata;
}
// Add completed backups.