예제 #1
0
 protected function define_execution() {
     global $CFG;
     restore_controller_dbops::drop_restore_temp_tables($this->get_restoreid()); // Drop ids temp table
     backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60));              // Delete > 4 hours temp dirs
     if (empty($CFG->keeptempdirectoriesonbackup)) { // Conditionally
         backup_helper::delete_backup_dir($this->task->get_tempdir()); // Empty restore dir
     }
 }
예제 #2
0
 protected function define_execution()
 {
     global $CFG;
     restore_controller_dbops::drop_restore_temp_tables($this->get_restoreid());
     // Drop ids temp table
     $progress = $this->task->get_progress();
     $progress->start_progress('Deleting backup dir');
     backup_helper::delete_old_backup_dirs(strtotime('-1 week'), $progress);
     // Delete > 1 week old temp dirs.
     if (empty($CFG->keeptempdirectoriesonbackup)) {
         // Conditionally
         backup_helper::delete_backup_dir($this->task->get_tempdir(), $progress);
         // Empty restore dir
     }
     $progress->end_progress();
 }
예제 #3
0
 /**
  * Return one array containing all the tasks that have been included
  * in the restore process. Note that these tasks aren't built (they
  * haven't steps nor ids data available)
  */
 public static function get_included_tasks($restoreid)
 {
     $rc = restore_controller_dbops::load_controller($restoreid);
     $tasks = $rc->get_plan()->get_tasks();
     $includedtasks = array();
     foreach ($tasks as $key => $task) {
         // Calculate if the task is being included
         $included = false;
         // blocks, based in blocks setting and parent activity/course
         if ($task instanceof restore_block_task) {
             if (!$task->get_setting_value('blocks')) {
                 // Blocks not included, continue
                 continue;
             }
             $parent = basename(dirname(dirname($task->get_taskbasepath())));
             if ($parent == 'course') {
                 // Parent is course, always included if present
                 $included = true;
             } else {
                 // Look for activity_included setting
                 $included = $task->get_setting_value($parent . '_included');
             }
             // ativities, based on included setting
         } else {
             if ($task instanceof restore_activity_task) {
                 $included = $task->get_setting_value('included');
                 // sections, based on included setting
             } else {
                 if ($task instanceof restore_section_task) {
                     $included = $task->get_setting_value('included');
                     // course always included if present
                 } else {
                     if ($task instanceof restore_course_task) {
                         $included = true;
                     }
                 }
             }
         }
         // If included, add it
         if ($included) {
             $includedtasks[] = $task;
         }
     }
     return $includedtasks;
 }
 /**
  * Entry point for all the prechecks to be performed before restore
  *
  * Returns empty array or warnings/errors array
  */
 public static function execute_prechecks(restore_controller $controller, $droptemptablesafter = false)
 {
     global $CFG;
     $errors = array();
     $warnings = array();
     // Some handy vars to be used along the prechecks
     $samesite = $controller->is_samesite();
     $restoreusers = $controller->get_plan()->get_setting('users')->get_value();
     $hasmnetusers = (int) $controller->get_info()->mnet_remoteusers;
     $restoreid = $controller->get_restoreid();
     $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());
     // Check we are restoring one backup >= $min20version (very first ok ever)
     $min20version = 2010072300;
     if ($controller->get_info()->backup_version < $min20version) {
         $message = new stdclass();
         $message->backup = $controller->get_info()->backup_version;
         $message->min = $min20version;
         $errors[] = get_string('errorminbackup20version', 'backup', $message);
     }
     // Compare Moodle's versions
     if ($CFG->version < $controller->get_info()->moodle_version) {
         $message = new stdclass();
         $message->serverversion = $CFG->version;
         $message->serverrelease = $CFG->release;
         $message->backupversion = $controller->get_info()->moodle_version;
         $message->backuprelease = $controller->get_info()->moodle_release;
         $warnings[] = get_string('noticenewerbackup', '', $message);
     }
     // Error if restoring over frontpage
     // TODO: Review the whole restore process in order to transform this into one warning (see 1.9)
     if ($controller->get_courseid() == SITEID) {
         $errors[] = get_string('errorrestorefrontpage', 'backup');
     }
     // If restoring to different site and restoring users and backup has mnet users warn/error
     if (!$samesite && $restoreusers && $hasmnetusers) {
         // User is admin (can create users at sysctx), warn
         if (has_capability('moodle/user:create', context_system::instance(), $controller->get_userid())) {
             $warnings[] = get_string('mnetrestore_extusers_admin', 'admin');
             // User not admin
         } else {
             $errors[] = get_string('mnetrestore_extusers_noadmin', 'admin');
         }
     }
     // 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) {
         // 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';
         // 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';
     restore_dbops::load_roles_to_tempids($restoreid, $file);
     // Load needed roles to temp_ids
     if ($problems = restore_dbops::precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings)) {
         $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';
     restore_dbops::load_categories_and_questions_to_tempids($restoreid, $file);
     if ($problems = restore_dbops::precheck_categories_and_questions($restoreid, $courseid, $userid, $samesite)) {
         $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.
     $results = array();
     if (!empty($errors)) {
         $results['errors'] = $errors;
     }
     if (!empty($warnings)) {
         $results['warnings'] = $warnings;
     }
     // Warnings/errors detected or want to do so explicitly, drop temp tables
     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;
 }
예제 #5
0
 public static function load_controller($restoreid)
 {
     // Load controller from persistent storage
     // TODO: flag the controller as available. Operations on it can continue
     $controller = restore_controller_dbops::load_controller($restoreid);
     $controller->log('loading controller from db', backup::LOG_DEBUG);
     return $controller;
 }
예제 #6
0
 /**
  * Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected.
  *
  * Note that those private implementations are tested here by using the public
  * backup_ids API and later performing low-level tests.
  */
 public function test_backup_ids_cached()
 {
     global $DB;
     $dbman = $DB->get_manager();
     // We are going to use database_manager services.
     $this->resetAfterTest(true);
     // Playing with temp tables, better reset once finished.
     // Some variables and objects for testing.
     $restoreid = 'testrestoreid';
     $mapping = new stdClass();
     $mapping->itemname = 'user';
     $mapping->itemid = 1;
     $mapping->newitemid = 2;
     $mapping->parentitemid = 3;
     $mapping->info = 'info';
     // Create the backup_ids temp tables used by restore.
     restore_controller_dbops::create_restore_temp_tables($restoreid);
     // Send one mapping using the public api with defaults.
     restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
     // Get that mapping and verify everything is returned as expected.
     $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
     $this->assertSame($mapping->itemname, $result->itemname);
     $this->assertSame($mapping->itemid, $result->itemid);
     $this->assertSame(0, $result->newitemid);
     $this->assertSame(null, $result->parentitemid);
     $this->assertSame(null, $result->info);
     // Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated.
     $dbman->drop_table(new xmldb_table('backup_ids_temp'));
     $dbman->drop_table(new xmldb_table('backup_files_temp'));
     // Verify the mapping continues returning the same info,
     // now from cache (the table does not exist).
     $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
     $this->assertSame($mapping->itemname, $result->itemname);
     $this->assertSame($mapping->itemid, $result->itemid);
     $this->assertSame(0, $result->newitemid);
     $this->assertSame(null, $result->parentitemid);
     $this->assertSame(null, $result->info);
     // Recreate the temp table, just to drop it using the restore API in
     // order to check that, then, the cache becomes invalid for the same request.
     restore_controller_dbops::create_restore_temp_tables($restoreid);
     restore_controller_dbops::drop_restore_temp_tables($restoreid);
     // No cached info anymore, so the mapping request will arrive to
     // DB leading to error (temp table does not exist).
     try {
         $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
         $this->fail('Expecting an exception, none occurred');
     } catch (Exception $e) {
         $this->assertTrue($e instanceof dml_exception);
         $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
     }
     // Create the backup_ids temp tables once more.
     restore_controller_dbops::create_restore_temp_tables($restoreid);
     // Send one mapping using the public api with complete values.
     restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid, $mapping->newitemid, $mapping->parentitemid, $mapping->info);
     // Get that mapping and verify everything is returned as expected.
     $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
     $this->assertSame($mapping->itemname, $result->itemname);
     $this->assertSame($mapping->itemid, $result->itemid);
     $this->assertSame($mapping->newitemid, $result->newitemid);
     $this->assertSame($mapping->parentitemid, $result->parentitemid);
     $this->assertSame($mapping->info, $result->info);
     // Finally, drop the temp tables properly and get the DB error again (memory caches empty).
     restore_controller_dbops::drop_restore_temp_tables($restoreid);
     try {
         $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
         $this->fail('Expecting an exception, none occurred');
     } catch (Exception $e) {
         $this->assertTrue($e instanceof dml_exception);
         $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
     }
 }
 /**
  * Check all the included users, deciding the action to perform
  * 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\progress\base $progress Progress reporter
  */
 public static function precheck_included_users($restoreid, $courseid, $userid, $samesite, \core\progress\base $progress)
 {
     global $CFG, $DB;
     // To return any problem found
     $problems = array();
     // We are going to map mnethostid, so load all the available ones
     $mnethosts = $DB->get_records('mnet_host', array(), 'wwwroot', 'wwwroot, id');
     // Calculate the context we are going to use for capability checking
     $context = context_course::instance($courseid);
     // When conflicting users are detected we may need original site info.
     $restoreinfo = restore_controller_dbops::load_controller($restoreid)->get_info();
     // Calculate if we have perms to create users, by checking:
     // to 'moodle/restore:createuser' and 'moodle/restore:userinfo'
     // and also observe $CFG->disableusercreationonrestore
     $cancreateuser = false;
     if (has_capability('moodle/restore:createuser', $context, $userid) and has_capability('moodle/restore:userinfo', $context, $userid) and empty($CFG->disableusercreationonrestore)) {
         // Can create users
         $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', $conditions, '', 'itemid, info');
     foreach ($rs as $recuser) {
         $user = (object) backup_controller_dbops::decode_backup_temp_info($recuser->info);
         // Find the correct mnethostid for user before performing any further check
         if (empty($user->mnethosturl) || $user->mnethosturl === $CFG->wwwroot) {
             $user->mnethostid = $CFG->mnet_localhost_id;
         } else {
             // fast url-to-id lookups
             if (isset($mnethosts[$user->mnethosturl])) {
                 $user->mnethostid = $mnethosts[$user->mnethosturl]->id;
             } else {
                 $user->mnethostid = $CFG->mnet_localhost_id;
             }
         }
         // Now, precheck that user and, based on returned results, annotate action/problem
         $usercheck = self::precheck_user($user, $samesite, $restoreinfo->original_site_identifier_hash);
         if (is_object($usercheck)) {
             // No problem, we have found one user in DB to be mapped to
             // Annotate it, for later process. Set newitemid to mapping user->id
             self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, $usercheck->id);
         } else {
             if ($usercheck === false) {
                 // Found conflict, report it as problem
                 if (!get_config('backup', 'import_general_duplicate_admin_allowed')) {
                     $problems[] = get_string('restoreuserconflict', '', $user->username);
                 } else {
                     if ($user->username == 'admin') {
                         if (!$cancreateuser) {
                             $problems[] = get_string('restorecannotcreateuser', '', $user->username);
                         }
                         if ($user->mnethostid != $CFG->mnet_localhost_id) {
                             $problems[] = get_string('restoremnethostidmismatch', '', $user->username);
                         }
                         if (!$problems) {
                             // Duplicate admin allowed, append original site idenfitier to username.
                             $user->username .= '_' . $restoreinfo->original_site_identifier_hash;
                             self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, 0, null, (array) $user);
                         }
                     }
                 }
             } else {
                 if ($usercheck === true) {
                     // User needs to be created, check if we are able
                     if ($cancreateuser) {
                         // Can create user, set newitemid to 0 so will be created later
                         self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, 0, null, (array) $user);
                     } else {
                         // Cannot create user, report it as problem
                         $problems[] = get_string('restorecannotcreateuser', '', $user->username);
                     }
                 } 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;
 }
 /**
  * Entry point for all the prechecks to be performed before restore
  *
  * Returns empty array or warnings/errors array
  */
 public static function execute_prechecks($controller, $droptemptablesafter = false)
 {
     global $CFG;
     $errors = array();
     $warnings = array();
     // Some handy vars to be used along the prechecks
     $samesite = $controller->is_samesite();
     $restoreusers = $controller->get_plan()->get_setting('users')->get_value();
     $hasmnetusers = (int) $controller->get_info()->mnet_remoteusers;
     $restoreid = $controller->get_restoreid();
     $courseid = $controller->get_courseid();
     $userid = $controller->get_userid();
     $rolemappings = $controller->get_info()->role_mappings;
     // Load all the included tasks to look for inforef.xml files
     $inforeffiles = array();
     $tasks = restore_dbops::get_included_tasks($restoreid);
     foreach ($tasks as $task) {
         // Add the inforef.xml file if exists
         $inforefpath = $task->get_taskbasepath() . '/inforef.xml';
         if (file_exists($inforefpath)) {
             $inforeffiles[] = $inforefpath;
         }
     }
     // Create temp tables
     restore_controller_dbops::create_restore_temp_tables($controller->get_restoreid());
     // Check we are restoring one backup >= $min20version (very first ok ever)
     $min20version = 2010072300;
     if ($controller->get_info()->backup_version < $min20version) {
         $message = new stdclass();
         $message->backup = $controller->get_info()->backup_version;
         $message->min = $min20version;
         $errors[] = get_string('errorminbackup20version', 'backup', $message);
     }
     // Compare Moodle's versions
     if ($CFG->version < $controller->get_info()->moodle_version) {
         $message = new stdclass();
         $message->serverversion = $CFG->version;
         $message->serverrelease = $CFG->release;
         $message->backupversion = $controller->get_info()->moodle_version;
         $message->backuprelease = $controller->get_info()->moodle_release;
         $warnings[] = get_string('noticenewerbackup', '', $message);
     }
     // Error if restoring over frontpage
     // TODO: Review the whole restore process in order to transform this into one warning (see 1.9)
     if ($controller->get_courseid() == SITEID) {
         $errors[] = get_string('errorrestorefrontpage', 'backup');
     }
     // If restoring to different site and restoring users and backup has mnet users warn/error
     if (!$samesite && $restoreusers && $hasmnetusers) {
         // User is admin (can create users at sysctx), warn
         if (has_capability('moodle/user:create', context_system::instance(), $controller->get_userid())) {
             $warnings[] = get_string('mnetrestore_extusers_admin', 'admin');
             // User not admin
         } else {
             $errors[] = get_string('mnetrestore_extusers_noadmin', 'admin');
         }
     }
     // Load all the inforef files, we are going to need them
     foreach ($inforeffiles as $inforeffile) {
         restore_dbops::load_inforef_to_tempids($restoreid, $inforeffile);
         // Load each inforef file to temp_ids
     }
     // 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)) {
             $errors = array_merge($errors, $problems);
         }
     }
     // Note: restore won't create roles at all. Only mapping/skip!
     $file = $controller->get_plan()->get_basepath() . '/roles.xml';
     restore_dbops::load_roles_to_tempids($restoreid, $file);
     // Load needed roles to temp_ids
     if ($problems = restore_dbops::precheck_included_roles($restoreid, $courseid, $userid, $samesite, $rolemappings)) {
         $errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
         $warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
     }
     // Check we are able to restore and the categories and questions
     $file = $controller->get_plan()->get_basepath() . '/questions.xml';
     restore_dbops::load_categories_and_questions_to_tempids($restoreid, $file);
     if ($problems = restore_dbops::precheck_categories_and_questions($restoreid, $courseid, $userid, $samesite)) {
         $errors = array_key_exists('errors', $problems) ? array_merge($errors, $problems['errors']) : $errors;
         $warnings = array_key_exists('warnings', $problems) ? array_merge($warnings, $problems['warnings']) : $warnings;
     }
     // Prepare results and return
     $results = array();
     if (!empty($errors)) {
         $results['errors'] = $errors;
     }
     if (!empty($warnings)) {
         $results['warnings'] = $warnings;
     }
     // Warnings/errors detected or want to do so explicitly, drop temp tables
     if (!empty($results) || $droptemptablesafter) {
         restore_controller_dbops::drop_restore_temp_tables($controller->get_restoreid());
     }
     return $results;
 }