// Prepare the restore controller. We don't need a UI here as we will just use what
// ever the restore has (the user has just chosen).
$rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_YES, backup::MODE_IMPORT, $USER->id, $restoretarget);
+
+ // Start a progress section for the restore, which will consist of 2 steps
+ // (the precheck and then the actual restore).
+ $progress->start_progress('Restore process', 2);
$rc->set_progress($progress);
// Convert the backup if required.... it should NEVER happed
if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) {
// Delete the temp directory now
fulldelete($tempdestination);
+ // End restore section of progress tracking (restore/precheck).
+ $progress->end_progress();
+
// All progress complete. Hide progress area.
$progress->end_progress();
echo html_writer::end_div();
// Get all the included tasks
$tasks = restore_dbops::get_included_tasks($this->get_restoreid());
+ $progress = $this->task->get_progress();
+ $progress->start_progress($this->get_name(), count($tasks));
foreach ($tasks as $task) {
// Load the inforef.xml file if exists
$inforefpath = $task->get_taskbasepath() . '/inforef.xml';
if (file_exists($inforefpath)) {
- restore_dbops::load_inforef_to_tempids($this->get_restoreid(), $inforefpath); // Load each inforef file to temp_ids
+ // Load each inforef file to temp_ids.
+ restore_dbops::load_inforef_to_tempids($this->get_restoreid(), $inforefpath, $progress);
}
}
+ $progress->end_progress();
}
}
return;
}
$file = $this->get_basepath() . '/users.xml';
- restore_dbops::load_users_to_tempids($this->get_restoreid(), $file); // Load needed users to temp_ids
+ // Load needed users to temp_ids.
+ restore_dbops::load_users_to_tempids($this->get_restoreid(), $file, $this->task->get_progress());
}
}
if (!$this->task->get_setting_value('users')) { // No userinfo being restored, nothing to do
return;
}
- restore_dbops::process_included_users($this->get_restoreid(), $this->task->get_courseid(), $this->task->get_userid(), $this->task->is_samesite());
+ restore_dbops::process_included_users($this->get_restoreid(), $this->task->get_courseid(),
+ $this->task->get_userid(), $this->task->is_samesite(), $this->task->get_progress());
}
}
// Prepare a progress bar which can display optionally during long-running
// operations while setting up the UI.
-$slowprogress = new core_backup_display_progress_if_slow();
+$slowprogress = new core_backup_display_progress_if_slow(get_string('preparingui', 'backup'));
// Depending on the code branch above, $restore may be a restore_ui or it may
// be a restore_ui_independent_stage. Either way, this function exists.
$restore->set_progress_reporter($slowprogress);
}
if (!$restore->is_independent()) {
+ // Use a temporary (disappearing) progress bar to show the precheck progress if any.
+ $precheckprogress = new core_backup_display_progress_if_slow(get_string('preparingdata', 'backup'));
+ $restore->get_controller()->set_progress($precheckprogress);
if ($restore->get_stage() == restore_ui::STAGE_PROCESS && !$restore->requires_substage()) {
try {
- // Display an extra progress bar so that we can show the progress first.
+ // Div used to hide the 'progress' step once the page gets onto 'finished'.
echo html_writer::start_div('', array('id' => 'executionprogress'));
+ // Show the current restore state (header with bolded item).
echo $renderer->progress_bar($restore->get_progress_bar());
- $restore->get_controller()->set_progress(new core_backup_display_progress());
+ // Start displaying the actual progress bar percentage.
+ $restore->get_controller()->set_progress(new core_backup_display_progress(true));
+ // Do actual restore.
$restore->execute();
+ // Hide this section because we are now going to make the page show 'finished'.
echo html_writer::end_div();
echo html_writer::script('document.getElementById("executionprogress").style.display = "none";');
} catch(Exception $e) {
/**
* Load one inforef.xml file to backup_ids table for future reference
+ *
+ * @param string $restoreid Restore id
+ * @param string $inforeffile File path
+ * @param core_backup_progress $progress Progress tracker
*/
- public static function load_inforef_to_tempids($restoreid, $inforeffile) {
+ public static function load_inforef_to_tempids($restoreid, $inforeffile,
+ core_backup_progress $progress = null) {
if (!file_exists($inforeffile)) { // Shouldn't happen ever, but...
throw new backup_helper_exception('missing_inforef_xml_file', $inforeffile);
}
+
+ // Set up progress tracking (indeterminate).
+ if (!$progress) {
+ $progress = new core_backup_null_progress();
+ }
+ $progress->start_progress('Loading inforef.xml file');
+
// Let's parse, custom processor will do its work, sending info to DB
$xmlparser = new progressive_parser();
$xmlparser->set_file($inforeffile);
$xmlprocessor = new restore_inforef_parser_processor($restoreid);
$xmlparser->set_processor($xmlprocessor);
+ $xmlparser->set_progress($progress);
$xmlparser->process();
+
+ // Finish progress
+ $progress->end_progress();
}
/**
/**
* Load the needed users.xml file to backup_ids table for future reference
+ *
+ * @param string $restoreid Restore id
+ * @param string $usersfile File path
+ * @param core_backup_progress $progress Progress tracker
*/
- public static function load_users_to_tempids($restoreid, $usersfile) {
+ public static function load_users_to_tempids($restoreid, $usersfile,
+ core_backup_progress $progress = null) {
if (!file_exists($usersfile)) { // Shouldn't happen ever, but...
throw new backup_helper_exception('missing_users_xml_file', $usersfile);
}
+
+ // Set up progress tracking (indeterminate).
+ if (!$progress) {
+ $progress = new core_backup_null_progress();
+ }
+ $progress->start_progress('Loading users into temporary table');
+
// Let's parse, custom processor will do its work, sending info to DB
$xmlparser = new progressive_parser();
$xmlparser->set_file($usersfile);
$xmlprocessor = new restore_users_parser_processor($restoreid);
$xmlparser->set_processor($xmlprocessor);
+ $xmlparser->set_progress($progress);
$xmlparser->process();
+
+ // Finish progress.
+ $progress->end_progress();
}
/**
* for each one (mapping / creation) and returning one array
* of problems in case something is wrong (lack of permissions,
* conficts)
+ *
+ * @param string $restoreid Restore id
+ * @param int $courseid Course id
+ * @param int $userid User id
+ * @param bool $samesite True if restore is to same site
+ * @param core_backup_progress $progress Progress reporter
*/
- public static function precheck_included_users($restoreid, $courseid, $userid, $samesite) {
+ public static function precheck_included_users($restoreid, $courseid, $userid, $samesite,
+ core_backup_progress $progress) {
global $CFG, $DB;
// To return any problem found
$cancreateuser = true;
}
+ // Prepare for reporting progress.
+ $conditions = array('backupid' => $restoreid, 'itemname' => 'user');
+ $max = $DB->count_records('backup_ids_temp', $conditions);
+ $done = 0;
+ $progress->start_progress('Checking users', $max);
+
// Iterate over all the included users
- $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $restoreid, 'itemname' => 'user'), '', 'itemid, info');
+ $rs = $DB->get_recordset('backup_ids_temp', $conditions, '', 'itemid, info');
foreach ($rs as $recuser) {
$user = (object)backup_controller_dbops::decode_backup_temp_info($recuser->info);
} else { // Shouldn't arrive here ever, something is for sure wrong. Exception
throw new restore_dbops_exception('restore_error_processing_user', $user->username);
}
+ $done++;
+ $progress->progress($done);
}
$rs->close();
+ $progress->end_progress();
return $problems;
}
*
* Just wrap over precheck_included_users(), returning
* exception if any problem is found
+ *
+ * @param string $restoreid Restore id
+ * @param int $courseid Course id
+ * @param int $userid User id
+ * @param bool $samesite True if restore is to same site
+ * @param core_backup_progress $progress Optional progress tracker
*/
- public static function process_included_users($restoreid, $courseid, $userid, $samesite) {
+ public static function process_included_users($restoreid, $courseid, $userid, $samesite,
+ core_backup_progress $progress = null) {
global $DB;
// Just let precheck_included_users() to do all the hard work
- $problems = self::precheck_included_users($restoreid, $courseid, $userid, $samesite);
+ $problems = self::precheck_included_users($restoreid, $courseid, $userid, $samesite, $progress);
// With problems, throw exception, shouldn't happen if prechecks were originally
// executed, so be radical here.
*
* Returns empty array or warnings/errors array
*/
- public static function execute_prechecks($controller, $droptemptablesafter = false) {
+ public static function execute_prechecks(restore_controller $controller, $droptemptablesafter = false) {
global $CFG;
$errors = array();
$courseid = $controller->get_courseid();
$userid = $controller->get_userid();
$rolemappings = $controller->get_info()->role_mappings;
+ $progress = $controller->get_progress();
+
+ // Start tracking progress. There are currently 8 major steps, corresponding
+ // to $majorstep++ lines in this code; we keep track of the total so as to
+ // verify that it's still correct. If you add a major step, you need to change
+ // the total here.
+ $majorstep = 1;
+ $majorsteps = 8;
+ $progress->start_progress('Carrying out pre-restore checks', $majorsteps);
+
// Load all the included tasks to look for inforef.xml files
$inforeffiles = array();
$tasks = restore_dbops::get_included_tasks($restoreid);
+ $progress->start_progress('Listing inforef files', count($tasks));
+ $minorstep = 1;
foreach ($tasks as $task) {
// Add the inforef.xml file if exists
$inforefpath = $task->get_taskbasepath() . '/inforef.xml';
if (file_exists($inforefpath)) {
$inforeffiles[] = $inforefpath;
}
+ $progress->progress($minorstep++);
}
+ $progress->end_progress();
+ $progress->progress($majorstep++);
// Create temp tables
restore_controller_dbops::create_restore_temp_tables($controller->get_restoreid());
}
// Load all the inforef files, we are going to need them
+ $progress->start_progress('Loading temporary IDs', count($inforeffiles));
+ $minorstep = 1;
foreach ($inforeffiles as $inforeffile) {
- restore_dbops::load_inforef_to_tempids($restoreid, $inforeffile); // Load each inforef file to temp_ids
+ // Load each inforef file to temp_ids.
+ restore_dbops::load_inforef_to_tempids($restoreid, $inforeffile, $progress);
+ $progress->progress($minorstep++);
}
+ $progress->end_progress();
+ $progress->progress($majorstep++);
// If restoring users, check we are able to create all them
if ($restoreusers) {
$file = $controller->get_plan()->get_basepath() . '/users.xml';
- restore_dbops::load_users_to_tempids($restoreid, $file); // Load needed users to temp_ids
- if ($problems = restore_dbops::precheck_included_users($restoreid, $courseid, $userid, $samesite)) {
+ // Load needed users to temp_ids.
+ restore_dbops::load_users_to_tempids($restoreid, $file, $progress);
+ $progress->progress($majorstep++);
+ if ($problems = restore_dbops::precheck_included_users($restoreid, $courseid, $userid, $samesite, $progress)) {
$errors = array_merge($errors, $problems);
}
+ } else {
+ // To ensure consistent number of steps in progress tracking,
+ // mark progress even though we didn't do anything.
+ $progress->progress($majorstep++);
}
+ $progress->progress($majorstep++);
// Note: restore won't create roles at all. Only mapping/skip!
$file = $controller->get_plan()->get_basepath() . '/roles.xml';
$errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
$warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
}
+ $progress->progress($majorstep++);
// Check we are able to restore and the categories and questions
$file = $controller->get_plan()->get_basepath() . '/questions.xml';
$errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
$warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
}
+ $progress->progress($majorstep++);
- // Prepare results and return
+ // Prepare results.
$results = array();
if (!empty($errors)) {
$results['errors'] = $errors;
if (!empty($results) || $droptemptablesafter) {
restore_controller_dbops::drop_restore_temp_tables($controller->get_restoreid());
}
+
+ // Finish progress and check we got the initial number of steps right.
+ $progress->progress($majorstep++);
+ if ($majorstep != $majorsteps) {
+ throw new coding_exception('Progress step count wrong: ' . $majorstep);
+ }
+ $progress->end_progress();
+
return $results;
}
}
$xmlprocessor->add_path($element->get_path(), $element->is_grouped());
}
+ // Set up progress tracking.
+ $progress = $this->get_task()->get_progress();
+ $progress->start_progress($this->get_name(), core_backup_progress::INDETERMINATE);
+ $xmlparser->set_progress($progress);
+
// And process it, dispatch to target methods in step will start automatically
$xmlparser->process();
+ $progress->end_progress();
// Have finished, launch the after_execute method of all the processing objects
$this->launch_after_execute_methods();
*/
protected $id;
+ /**
+ * @var string Text to display in heading if bar appears
+ */
+ protected $heading;
+
/**
* @var int Time at which the progress bar should display (if it isn't yet)
*/
* Constructs the progress reporter. This will not output HTML just yet,
* until the required delay time expires.
*
+ * @param string $heading Text to display above bar (if it appears); '' for none
* @param int $delay Delay time (default 5 seconds)
*/
- public function __construct($delay = self::DEFAULT_DISPLAY_DELAY) {
+ public function __construct($heading, $delay = self::DEFAULT_DISPLAY_DELAY) {
// Set start time based on delay.
$this->starttime = time() + $delay;
+ $this->heading = $heading;
parent::__construct(false);
}
/**
- * Adds a div around the parent display so it can be hidden.
+ * Starts displaying the progress bar, with optional heading and a special
+ * div so it can be hidden later.
*
* @see core_backup_display_progress::start_html()
*/
public function start_html() {
+ global $OUTPUT;
$this->id = 'core_backup_display_progress_if_slow' . self::$nextid;
self::$nextid++;
- echo html_writer::start_div('', array('id' => $this->id));
+
+ // Containing div includes a CSS class so that it can be themed if required,
+ // and an id so it can be automatically hidden at end.
+ echo html_writer::start_div('core_backup_display_progress_if_slow',
+ array('id' => $this->id));
+
+ // Display optional heading.
+ if ($this->heading !== '') {
+ echo $OUTPUT->heading($this->heading, 3);
+ }
+
+ // Use base class to display progress bar.
parent::start_html();
}
protected $prevlevel; // level of the previous tag processed - to detect pushing places
protected $currtag; // name/value/attributes of the tag being processed
+ /**
+ * @var core_backup_progress Progress tracker called for each action
+ */
+ protected $progress;
+
public function __construct($case_folding = false) {
$this->xml_parser = xml_parser_create('UTF-8');
xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, $case_folding);
$this->processor = $processor;
}
+ /**
+ * Sets the progress tracker for the parser. If set, the tracker will be
+ * called to report indeterminate progress for each chunk of XML.
+ *
+ * The caller should have already called start_progress on the progress tracker.
+ *
+ * @param core_backup_progress $progress Progress tracker
+ */
+ public function set_progress(core_backup_progress $progress) {
+ $this->progress = $progress;
+ }
+
/*
* Process the XML, delegating found chunks to the @progressive_parser_processor
*/
protected function publish($data) {
$this->processor->receive_chunk($data);
+ if (!empty($this->progress)) {
+ // Report indeterminate progress.
+ $this->progress->progress();
+ }
}
/**
$string['norestoreoptions'] = 'There are no categories or existing courses you can restore to.';
$string['originalwwwroot'] = 'URL of backup';
$string['previousstage'] = 'Previous';
+$string['preparingui'] = 'Preparing to display page';
+$string['preparingdata'] = 'Preparing data';
$string['qcategory2coursefallback'] = 'The questions category "{$a->name}", originally at system/course category context in backup file, will be created at course context by restore';
$string['qcategorycannotberestored'] = 'The questions category "{$a->name}" cannot be created by restore';
$string['question2coursefallback'] = 'The questions category "{$a->name}", originally at system/course category context in backup file, will be created at course context by restore';