/** * Function responsible for executing the tasks of any plan */ public function execute() { if ($this->controller->get_status() != backup::STATUS_AWAITING) { throw new restore_controller_exception('restore_not_executable_awaiting_required', $this->controller->get_status()); } $this->controller->set_status(backup::STATUS_EXECUTING); parent::execute(); $this->controller->set_status(backup::STATUS_FINISHED_OK); }
/** * Gets the value for the requested setting * * @param string $name * @return mixed */ public function get_setting_value($name, $default = false) { try { return $this->controller->get_plan()->get_setting($name)->get_value(); } catch (Exception $e) { debugging('Failed to find the setting: ' . $name, DEBUG_DEVELOPER); return $default; } }
/** * Function responsible for executing the tasks of any plan */ public function execute() { if ($this->controller->get_status() != backup::STATUS_AWAITING) { throw new restore_controller_exception('restore_not_executable_awaiting_required', $this->controller->get_status()); } $this->controller->set_status(backup::STATUS_EXECUTING); parent::execute(); $this->controller->set_status(backup::STATUS_FINISHED_OK); events_trigger('course_restored', (object) array('courseid' => $this->get_courseid(), 'userid' => $this->get_userid(), 'type' => $this->controller->get_type(), 'target' => $this->controller->get_target(), 'mode' => $this->controller->get_mode(), 'operation' => $this->controller->get_operation(), 'samesite' => $this->controller->is_samesite())); }
/** * Function responsible for executing the tasks of any plan */ public function execute() { if ($this->controller->get_status() != backup::STATUS_AWAITING) { throw new restore_controller_exception('restore_not_executable_awaiting_required', $this->controller->get_status()); } $this->controller->set_status(backup::STATUS_EXECUTING); parent::execute(); $this->controller->set_status(backup::STATUS_FINISHED_OK); // Trigger a course restored event. $event = \core\event\course_restored::create(array('objectid' => $this->get_courseid(), 'userid' => $this->get_userid(), 'context' => context_course::instance($this->get_courseid()), 'other' => array('type' => $this->controller->get_type(), 'target' => $this->controller->get_target(), 'mode' => $this->controller->get_mode(), 'operation' => $this->controller->get_operation(), 'samesite' => $this->controller->is_samesite()))); $event->trigger(); }
/** * Function responsible for executing the tasks of any plan */ public function execute() { if ($this->controller->get_status() != backup::STATUS_AWAITING) { throw new restore_controller_exception('restore_not_executable_awaiting_required', $this->controller->get_status()); } $this->controller->set_status(backup::STATUS_EXECUTING); parent::execute(); $this->controller->set_status(backup::STATUS_FINISHED_OK); // Check if we are restoring a course. if ($this->controller->get_type() === backup::TYPE_1COURSE) { // Check to see if we are on the same site to pass original course info. $issamesite = $this->controller->is_samesite(); $otherarray = array('type' => $this->controller->get_type(), 'target' => $this->controller->get_target(), 'mode' => $this->controller->get_mode(), 'operation' => $this->controller->get_operation(), 'samesite' => $issamesite); if ($this->controller->is_samesite()) { $otherarray['originalcourseid'] = $this->controller->get_info()->original_course_id; } // Trigger a course restored event. $event = \core\event\course_restored::create(array('objectid' => $this->get_courseid(), 'userid' => $this->get_userid(), 'context' => context_course::instance($this->get_courseid()), 'other' => $otherarray)); $event->trigger(); } }
/** * 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; }
/** * Duplicates a single activity within a course. * * This is based on the code from course/modduplicate.php, but reduced for * simplicity. * * @param stdClass $course Course object * @param int $cmid Activity to duplicate * @return int ID of new activity */ protected function duplicate($course, $cmid) { global $USER; // Do backup. $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cmid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $bc->execute_plan(); $bc->destroy(); // Do restore. $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); $this->assertTrue($rc->execute_precheck()); $rc->execute_plan(); // Find cmid. $tasks = $rc->get_plan()->get_tasks(); $cmcontext = context_module::instance($cmid); $newcmid = 0; foreach ($tasks as $task) { if (is_subclass_of($task, 'restore_activity_task')) { if ($task->get_old_contextid() == $cmcontext->id) { $newcmid = $task->get_moduleid(); break; } } } $rc->destroy(); if (!$newcmid) { throw new coding_exception('Unexpected: failure to find restored cmid'); } return $newcmid; }
/** * Constructor for the rollover backup controller class * * @param string $tempdir Directory under dataroot/temp/backup awaiting restore * @param int $courseid Course id where restore is going to happen */ public function __construct($tempdir, $courseid) { global $USER; parent::__construct($tempdir, $courseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE); }
/** * Used to notify the completion system (if necessary) that a user's grade * has changed, and clear up a possible score cache. * * @param bool $deleted True if grade was actually deleted */ protected function notify_changed($deleted) { global $CFG; // Condition code may cache the grades for conditional availability of // modules or sections. (This code should use a hook for communication // with plugin, but hooks are not implemented at time of writing.) if (!empty($CFG->enableavailability) && class_exists('\\availability_grade\\callbacks')) { \availability_grade\callbacks::grade_changed($this->userid); } require_once $CFG->libdir . '/completionlib.php'; // Bail out immediately if completion is not enabled for site (saves loading // grade item & requiring the restore stuff). if (!completion_info::is_enabled_for_site()) { return; } // Ignore during restore, as completion data will be updated anyway and // doing it now will result in incorrect dates (it will say they got the // grade completion now, instead of the correct time). if (class_exists('restore_controller', false) && restore_controller::is_executing()) { return; } // Load information about grade item $this->load_grade_item(); // Only course-modules have completion data if ($this->grade_item->itemtype != 'mod') { return; } // Use $COURSE if available otherwise get it via item fields $course = get_course($this->grade_item->courseid, false); // Bail out if completion is not enabled for course $completion = new completion_info($course); if (!$completion->is_enabled()) { return; } // Get course-module $cm = get_coursemodule_from_instance($this->grade_item->itemmodule, $this->grade_item->iteminstance, $this->grade_item->courseid); // If the course-module doesn't exist, display a warning... if (!$cm) { // ...unless the grade is being deleted in which case it's likely // that the course-module was just deleted too, so that's okay. if (!$deleted) { debugging("Couldn't find course-module for module '" . $this->grade_item->itemmodule . "', instance '" . $this->grade_item->iteminstance . "', course '" . $this->grade_item->courseid . "'"); } return; } // Pass information on to completion system $completion->inform_grade_changed($cm, $this->grade_item, $this, $deleted); }
/** * Backs a course up and restores it. * * @param stdClass $srccourse Course object to backup * @param stdClass $dstcourse Course object to restore into * @return int ID of newly restored course */ protected function backup_and_restore($srccourse, $dstcourse = null) { global $USER, $CFG; // Turn off file logging, otherwise it can't delete the file (Windows). $CFG->backup_file_logger_level = backup::LOG_NONE; // Do backup with default settings. MODE_IMPORT means it will just // create the directory and not zip it. $bc = new backup_controller(backup::TYPE_1COURSE, $srccourse->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $bc->execute_plan(); $bc->destroy(); // Do restore to new course with default settings. if ($dstcourse !== null) { $newcourseid = $dstcourse->id; } else { $newcourseid = restore_dbops::create_new_course($srccourse->fullname, $srccourse->shortname . '_2', $srccourse->category); } $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_NEW_COURSE); $this->assertTrue($rc->execute_precheck()); $rc->execute_plan(); $rc->destroy(); return $newcourseid; }
/** * Imports a course * * @param int $importfrom The id of the course we are importing from * @param int $importto The id of the course we are importing to * @param bool $deletecontent Whether to delete the course we are importing to content * @param array $options List of backup options * @return null * @since Moodle 2.4 */ public static function import_course($importfrom, $importto, $deletecontent = 0, $options = array()) { global $CFG, $USER, $DB; require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php'); require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); // Parameter validation. $params = self::validate_parameters( self::import_course_parameters(), array( 'importfrom' => $importfrom, 'importto' => $importto, 'deletecontent' => $deletecontent, 'options' => $options ) ); if ($params['deletecontent'] !== 0 and $params['deletecontent'] !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $params['deletecontent']); } // Context validation. if (! ($importfrom = $DB->get_record('course', array('id'=>$params['importfrom'])))) { throw new moodle_exception('invalidcourseid', 'error'); } if (! ($importto = $DB->get_record('course', array('id'=>$params['importto'])))) { throw new moodle_exception('invalidcourseid', 'error'); } $importfromcontext = context_course::instance($importfrom->id); self::validate_context($importfromcontext); $importtocontext = context_course::instance($importto->id); self::validate_context($importtocontext); $backupdefaults = array( 'activities' => 1, 'blocks' => 1, 'filters' => 1 ); $backupsettings = array(); // Check for backup and restore options. if (!empty($params['options'])) { foreach ($params['options'] as $option) { // Strict check for a correct value (allways 1 or 0, true or false). $value = clean_param($option['value'], PARAM_INT); if ($value !== 0 and $value !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } if (!isset($backupdefaults[$option['name']])) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } $backupsettings[$option['name']] = $value; } } // Capability checking. require_capability('moodle/backup:backuptargetimport', $importfromcontext); require_capability('moodle/restore:restoretargetimport', $importtocontext); $bc = new backup_controller(backup::TYPE_1COURSE, $importfrom->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); foreach ($backupsettings as $name => $value) { $bc->get_plan()->get_setting($name)->set_value($value); } $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // Restore the backup immediately. // Check if we must delete the contents of the destination course. if ($params['deletecontent']) { $restoretarget = backup::TARGET_EXISTING_DELETING; } else { $restoretarget = backup::TARGET_EXISTING_ADDING; } $rc = new restore_controller($backupid, $importto->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, $restoretarget); foreach ($backupsettings as $name => $value) { $rc->get_plan()->get_setting($name)->set_value($value); } if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } $errorinfo = ''; foreach ($precheckresults['errors'] as $error) { $errorinfo .= $error; } if (array_key_exists('warnings', $precheckresults)) { foreach ($precheckresults['warnings'] as $warning) { $errorinfo .= $warning; } } throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); } } else { if ($restoretarget == backup::TARGET_EXISTING_DELETING) { restore_dbops::delete_course_content($importto->id); } } $rc->execute_plan(); $rc->destroy(); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } return null; }
/** * Takes backupid and original course object as arguments and returns a new courseid. * * @param string $backupid The backupid * @param object $newcourse Target course object * @throws coding_exception Moodle coding exception */ private function restore($backupid, $newcourse) { global $CFG, $DB, $USER; // Call restore. $rc = new \restore_controller($backupid, $newcourse->id, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id, \backup::TARGET_EXISTING_ADDING); // Setup custom logger that prints dots. $logger = new logger(\backup::LOG_INFO, false, true); self::$currentlogger = $logger; $logger->potential_dot(); $rc->get_logger()->set_next($logger); $rc->set_progress(new progress()); foreach ($rc->get_plan()->get_tasks() as $taskindex => $task) { $settings = $task->get_settings(); foreach ($settings as $settingindex => $setting) { // Set userinfo true for activity, since we controlled it // more accurately (i.e. true only for glossary) in backup. if (preg_match('/^glossary_([0-9])*_userinfo$$/', $setting->get_name())) { $setting->set_value(true); } if ($taskindex == 0 && isset($this->backupsettings[$setting->get_name()])) { $setting->set_value($this->backupsettings[$setting->get_name()]); } } } if (!$rc->execute_precheck()) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($this->backupbasepath); } $results = print_r($rc->get_precheck_results(), true); print \html_writer::tag('pre', s($results)); throw new \coding_exception('Restore precheck error.'); } $logger->potential_dot(); $rc->execute_plan(); $rc->destroy(); // Delete backup file. if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($this->backupbasepath); } }
/** * Duplicate a module on the course. * * @param object $course The course * @param object $cm The course module to duplicate * @throws moodle_exception if the plugin doesn't support duplication * @return Object containing: * - fullcontent: The HTML markup for the created CM * - cmid: The CMID of the newly created CM * - redirect: Whether to trigger a redirect following this change */ function mod_duplicate_activity($course, $cm, $sr = null) { global $CFG, $USER, $PAGE, $DB; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; require_once $CFG->libdir . '/filelib.php'; $a = new stdClass(); $a->modtype = get_string('modulename', $cm->modname); $a->modname = format_string($cm->name); if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) { throw new moodle_exception('duplicatenosupport', 'error'); } // backup the activity $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // restore the backup immediately $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); $cmcontext = context_module::instance($cm->id); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } } } $rc->execute_plan(); // now a bit hacky part follows - we try to get the cmid of the newly // restored copy of the module $newcmid = null; $tasks = $rc->get_plan()->get_tasks(); foreach ($tasks as $task) { error_log("Looking at a task"); if (is_subclass_of($task, 'restore_activity_task')) { error_log("Looking at a restore_activity_task task"); if ($task->get_old_contextid() == $cmcontext->id) { error_log("Contexts match"); $newcmid = $task->get_moduleid(); break; } } } // if we know the cmid of the new course module, let us move it // right below the original one. otherwise it will stay at the // end of the section if ($newcmid) { $info = get_fast_modinfo($course); $newcm = $info->get_cm($newcmid); $section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course)); moveto_module($newcm, $section, $cm); moveto_module($cm, $section, $newcm); // Trigger course module created event. We can trigger the event only if we know the newcmid. $event = \core\event\course_module_created::create_from_cm($newcm); $event->trigger(); } rebuild_course_cache($cm->course); $rc->destroy(); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } $resp = new stdClass(); if ($newcm) { $courserenderer = $PAGE->get_renderer('core', 'course'); $completioninfo = new completion_info($course); $modulehtml = $courserenderer->course_section_cm($course, $completioninfo, $newcm, null, array()); $resp->fullcontent = $courserenderer->course_section_cm_list_item($course, $completioninfo, $newcm, $sr); $resp->cmid = $newcm->id; } else { // Trigger a redirect $resp->redirect = true; } return $resp; }
/** * Api to duplicate a module. * * @param object $course course object. * @param object $cm course module object to be duplicated. * @since Moodle 2.8 * * @throws Exception * @throws coding_exception * @throws moodle_exception * @throws restore_controller_exception * * @return cm_info|null cminfo object if we sucessfully duplicated the mod and found the new cm. */ function duplicate_module($course, $cm) { global $CFG, $DB, $USER; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; require_once $CFG->libdir . '/filelib.php'; $a = new stdClass(); $a->modtype = get_string('modulename', $cm->modname); $a->modname = format_string($cm->name); if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) { throw new moodle_exception('duplicatenosupport', 'error', '', $a); } // Backup the activity. $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // Restore the backup immediately. $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); $cmcontext = context_module::instance($cm->id); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } } } $rc->execute_plan(); // Now a bit hacky part follows - we try to get the cmid of the newly // restored copy of the module. $newcmid = null; $tasks = $rc->get_plan()->get_tasks(); foreach ($tasks as $task) { if (is_subclass_of($task, 'restore_activity_task')) { if ($task->get_old_contextid() == $cmcontext->id) { $newcmid = $task->get_moduleid(); break; } } } // If we know the cmid of the new course module, let us move it // right below the original one. otherwise it will stay at the // end of the section. if ($newcmid) { $info = get_fast_modinfo($course); $newcm = $info->get_cm($newcmid); $section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course)); moveto_module($newcm, $section, $cm); moveto_module($cm, $section, $newcm); // Update calendar events with the duplicated module. $refresheventsfunction = $newcm->modname . '_refresh_events'; if (function_exists($refresheventsfunction)) { call_user_func($refresheventsfunction, $newcm->course); } // Trigger course module created event. We can trigger the event only if we know the newcmid. $event = \core\event\course_module_created::create_from_cm($newcm); $event->trigger(); } rebuild_course_cache($cm->course); $rc->destroy(); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } return isset($newcm) ? $newcm : null; }
/** * Proceed with the import of the course. * * @return void */ public function proceed() { global $CFG, $USER; if (!$this->prepared) { throw new coding_exception('The course has not been prepared.'); } else { if ($this->has_errors()) { throw new moodle_exception('Cannot proceed, errors were detected.'); } else { if ($this->processstarted) { throw new coding_exception('The process has already been started.'); } } } $this->processstarted = true; if ($this->do === self::DO_DELETE) { if ($this->delete()) { $this->status('coursedeleted', new lang_string('coursedeleted', 'tool_uploadcourse')); } else { $this->error('errorwhiledeletingcourse', new lang_string('errorwhiledeletingcourse', 'tool_uploadcourse')); } return true; } else { if ($this->do === self::DO_CREATE) { $course = create_course((object) $this->data); $this->id = $course->id; $this->status('coursecreated', new lang_string('coursecreated', 'tool_uploadcourse')); } else { if ($this->do === self::DO_UPDATE) { $course = (object) $this->data; update_course($course); $this->id = $course->id; $this->status('courseupdated', new lang_string('courseupdated', 'tool_uploadcourse')); } else { // Strangely the outcome has not been defined, or is unknown! throw new coding_exception('Unknown outcome!'); } } } // Restore a course. if (!empty($this->restoredata)) { $rc = new restore_controller($this->restoredata, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); // Check if the format conversion must happen first. if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { $rc->convert(); } if ($rc->execute_precheck()) { $rc->execute_plan(); $this->status('courserestored', new lang_string('courserestored', 'tool_uploadcourse')); } else { $this->error('errorwhilerestoringcourse', new lang_string('errorwhilerestoringthecourse', 'tool_uploadcourse')); } $rc->destroy(); } // Proceed with enrolment data. $this->process_enrolment_data($course); // Reset the course. if ($this->importoptions['reset'] || $this->options['reset']) { if ($this->do === self::DO_UPDATE && $this->can_reset()) { $this->reset($course); $this->status('coursereset', new lang_string('coursereset', 'tool_uploadcourse')); } } // Mark context as dirty. $context = context_course::instance($course->id); $context->mark_dirty(); }
$restore_url = new moodle_url('/backup/restore.php', array('contextid' => $contextid, 'filename' => $filename)); redirect($restore_url); } else { redirect($url, get_string('filenotfound', 'error')); } die; } $PAGE->set_url($url); $PAGE->set_context($context); $PAGE->set_title(get_string('course') . ': ' . $course->fullname); $PAGE->set_heading($heading); $PAGE->set_pagelayout('admin'); $form = new course_restore_form(null, array('contextid' => $contextid)); $data = $form->get_data(); if ($data && has_capability('moodle/restore:uploadfile', $context)) { $filename = restore_controller::get_tempdir_name($course->id, $USER->id); $pathname = $tmpdir . '/' . $filename; $form->save_file('backupfile', $pathname); $restore_url = new moodle_url('/backup/restore.php', array('contextid' => $contextid, 'filename' => $filename)); redirect($restore_url); die; } echo $OUTPUT->header(); // require uploadfile cap to use file picker if (has_capability('moodle/restore:uploadfile', $context)) { echo $OUTPUT->heading(get_string('importfile', 'backup')); echo $OUTPUT->container_start(); $form->display(); echo $OUTPUT->container_end(); } if ($context->contextlevel == CONTEXT_MODULE) {
/** * Duplicates a Moodle module in an existing course * @param int $cmid Course module id * @param int $courseid Course id * @return int New course module id */ function local_ltiprovider_duplicate_module($cmid, $courseid, $newidnumber, $lticontext) { global $CFG, $DB, $USER; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; require_once $CFG->libdir . '/filelib.php'; if (empty($USER)) { // Emulate session. cron_setup_user(); } $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $cm = get_coursemodule_from_id('', $cmid, 0, true, MUST_EXIST); $cmcontext = context_module::instance($cm->id); $context = context_course::instance($course->id); if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) { $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)); print_error('duplicatenosupport', 'error', $url, $a); } // backup the activity $admin = get_admin(); $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // restore the backup immediately $rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id, backup::TARGET_CURRENT_ADDING); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } print_r($precheckresults); die; } } $rc->execute_plan(); $newcmid = null; $tasks = $rc->get_plan()->get_tasks(); foreach ($tasks as $task) { if (is_subclass_of($task, 'restore_activity_task')) { if ($task->get_old_contextid() == $cmcontext->id) { $newcmid = $task->get_moduleid(); break; } } } $rc->destroy(); if ($module = $DB->get_record('course_modules', array('id' => $newcmid))) { $module->idnumber = $newidnumber; $DB->update_record('course_modules', $module); } $newtoolid = 0; if ($tools = $DB->get_records('local_ltiprovider', array('contextid' => $cmcontext->id))) { $newcmcontext = context_module::instance($newcmid); foreach ($tools as $tool) { $tool->courseid = $course->id; $tool->contextid = $newcmcontext->id; $newtoolid = $DB->insert_record('local_ltiprovider', $tool); } } if (!$newtoolid) { $tool = local_ltiprovider_create_tool($course->id, $newcmcontext->id, $lticontext); } if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } return $newcmid; }
/** * */ public function apply_preset($userpreset, $torestorer = true) { global $DB, $CFG, $USER; $df = mod_dataform_dataform::instance($this->_dataformid); // Extract the backup file to the temp folder. $folder = 'tmp-' . $df->context->id . '-' . time(); $backuptempdir = make_temp_directory("backup/{$folder}"); $fs = get_file_storage(); $file = $fs->get_file_by_id($userpreset); $zipper = get_file_packer($file->get_mimetype()); $file->extract_to_pathname($zipper, $backuptempdir); require_once "{$CFG->dirroot}/backup/util/includes/restore_includes.php"; // Required preparation due to restorer assumption that this should be a new activity // Anonymous users cleanup. $DB->delete_records_select('user', $DB->sql_like('firstname', '?'), array('%anonfirstname%')); // Grading area removal. $DB->delete_records('grading_areas', array('contextid' => $df->context->id)); $transaction = $DB->start_delegated_transaction(); $rc = new restore_controller($folder, $df->course->id, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_CURRENT_ADDING); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backuptempdir); } } } // Get the dataform restore activity task. $tasks = $rc->get_plan()->get_tasks(); $dataformtask = null; foreach ($tasks as &$task) { if ($task instanceof restore_dataform_activity_task) { $dataformtask =& $task; break; } } if ($dataformtask) { $dataformtask->set_activityid($df->id); $dataformtask->set_moduleid($df->cm->id); $dataformtask->set_contextid($df->context->id); if ($torestorer) { $dataformtask->set_ownerid($USER->id); } $rc->set_status(backup::STATUS_AWAITING); $rc->execute_plan(); $transaction->allow_commit(); // Rc cleanup. $rc->destroy(); // Anonymous users cleanup. $DB->delete_records_select('user', $DB->sql_like('firstname', '?'), array('%anonfirstname%')); return true; } else { $rc->destroy(); } return false; }
list($context, $course, $cm) = get_context_info_array($contextid); navigation_node::override_active_url(new moodle_url('/backup/restorefile.php', array('contextid' => $contextid))); $PAGE->set_url(new moodle_url('/backup/restore.php', array('contextid' => $contextid))); $PAGE->set_context($context); $PAGE->set_pagelayout('standard'); require_login($course, null, $cm); require_capability('moodle/restore:restorecourse', $context); if ($stage & restore_ui::STAGE_CONFIRM + restore_ui::STAGE_DESTINATION) { $restore = restore_ui::engage_independent_stage($stage, $contextid); } else { $restoreid = optional_param('restore', false, PARAM_ALPHANUM); $rc = restore_ui::load_controller($restoreid); if (!$rc) { $restore = restore_ui::engage_independent_stage($stage / 2, $contextid); if ($restore->process()) { $rc = new restore_controller($restore->get_filepath(), $restore->get_course_id(), backup::INTERACTIVE_YES, backup::MODE_GENERAL, $USER->id, $restore->get_target()); } } if ($rc) { // check if the format conversion must happen first if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { $rc->convert(); } $restore = new restore_ui($rc, array('contextid' => $context->id)); } } $outcome = $restore->process(); if (!$restore->is_independent()) { if ($restore->get_stage() == restore_ui::STAGE_PROCESS && !$restore->requires_substage()) { try { $restore->execute();
/** * Extracts the file. * * @param string|stored_file $source Archive file to extract * @return bool */ protected function extract_file_to_dir($source) { global $CFG, $USER; $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id); $fb = get_file_packer('application/vnd.moodle.backup'); $result = $fb->extract_to_pathname($source, $CFG->tempdir . '/backup/' . $this->filepath . '/', null, $this); // If any progress happened, end it. if ($this->startedprogress) { $this->get_progress_reporter()->end_progress(); } return $result; }
/** * Test that triggering a course_restored event works as expected. */ public function test_course_restored_event() { global $CFG; // Get the necessary files to perform backup and restore. require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; $this->resetAfterTest(); // Set to admin user. $this->setAdminUser(); // The user id is going to be 2 since we are the admin user. $userid = 2; // Create a course. $course = $this->getDataGenerator()->create_course(); // Create backup file and save it to the backup location. $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; $fp = get_file_packer('application/vnd.moodle.backup'); $filepath = $CFG->dataroot . '/temp/backup/test-restore-course-event'; $file->extract_to_pathname($fp, $filepath); $bc->destroy(); unset($bc); // Now we want to catch the restore course event. $sink = $this->redirectEvents(); // Now restore the course to trigger the event. $rc = new restore_controller('test-restore-course-event', $course->id, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid, backup::TARGET_NEW_COURSE); $rc->execute_precheck(); $rc->execute_plan(); // Capture the event. $events = $sink->get_events(); $sink->close(); // Validate the event. $event = array_pop($events); $this->assertInstanceOf('\\core\\event\\course_restored', $event); $this->assertEquals('course', $event->objecttable); $this->assertEquals($rc->get_courseid(), $event->objectid); $this->assertEquals(context_course::instance($rc->get_courseid())->id, $event->contextid); $this->assertEquals('course_restored', $event->get_legacy_eventname()); $legacydata = (object) array('courseid' => $rc->get_courseid(), 'userid' => $rc->get_userid(), 'type' => $rc->get_type(), 'target' => $rc->get_target(), 'mode' => $rc->get_mode(), 'operation' => $rc->get_operation(), 'samesite' => $rc->is_samesite()); $url = new moodle_url('/course/view.php', array('id' => $event->objectid)); $this->assertEquals($url, $event->get_url()); $this->assertEventLegacyData($legacydata, $event); $this->assertEventContextNotUsed($event); // Destroy the resource controller since we are done using it. $rc->destroy(); unset($rc); }
/** * Duplicates a single dataform within a course. * * This is based on the code from course/modduplicate.php, but reduced for * simplicity. * * @param stdClass $course Course object * @param int $cmid Dataform to duplicate * @return stdClass The new dataform instance with cmid */ public function duplicate_instance($course, $cmid) { global $DB, $USER; // Do backup. $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cmid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // Do restore. $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } } } $rc->execute_plan(); // Find cmid. $tasks = $rc->get_plan()->get_tasks(); $cmcontext = context_module::instance($cmid); $newcmid = 0; $newactivityid = 0; foreach ($tasks as $task) { if (is_subclass_of($task, 'restore_activity_task')) { if ($task->get_old_contextid() == $cmcontext->id) { $newcmid = $task->get_moduleid(); $newactivityid = $task->get_activityid(); break; } } } $rc->destroy(); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } if (!$newcmid) { throw new coding_exception('Unexpected: failure to find restored cmid'); } if (!($instance = $DB->get_record('dataform', array('id' => $newactivityid)))) { throw new coding_exception('Unexpected: failure to find restored activityid'); } $instance->cmid = $newcmid; // Clear the time limit, otherwise phpunit complains. set_time_limit(0); return $instance; }
<?php define('CLI_SCRIPT', true); chdir('/var/www/ae.ket.org'); require_once 'config.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; // Transaction $transaction = $DB->start_delegated_transaction(); // Create new course $folder = '9706ff11ee909cc426e66fff74926faa'; // as found in: $CFG->dataroot . '/temp/backup/' $categoryid = 1; // e.g. 1 == Miscellaneous $user_doing_the_restore = 4; // e.g. 2 == admin $courseid = restore_dbops::create_new_course('Z', 'Z', $categoryid); // Restore backup into course $controller = new restore_controller($folder, $courseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $user_doing_the_restore, backup::TARGET_NEW_COURSE); $controller->execute_precheck(); $controller->execute_plan(); // Commit $transaction->allow_commit();
/** * Restore a course. * * @param int $backupid The backup ID. * @param int $courseid The course ID to restore in, or 0. * @param int $userid The ID of the user performing the restore. * @return stdClass The updated course object. */ protected function restore_course($backupid, $courseid, $userid) { global $DB; $target = backup::TARGET_CURRENT_ADDING; if (!$courseid) { $target = backup::TARGET_NEW_COURSE; $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $courseid = restore_dbops::create_new_course('Tmp', 'tmp', $categoryid); } $rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid, $target); $target == backup::TARGET_NEW_COURSE ?: $rc->get_plan()->get_setting('overwrite_conf')->set_value(true); $rc->execute_precheck(); $rc->execute_plan(); $course = $DB->get_record('course', array('id' => $rc->get_courseid())); $rc->destroy(); unset($rc); return $course; }
/** * Returns the origin of current request. * * Note: constants are not required because we need to use these values in logging and reports. * * @return string 'web', 'ws', 'cli', 'restore', etc. */ protected function magic_get_requestorigin() { if (class_exists('restore_controller', false) && restore_controller::is_executing()) { return 'restore'; } if (WS_SERVER) { return 'ws'; } if (CLI_SCRIPT) { return 'cli'; } return 'web'; }
/** * Loads the restore controller if we are tracking one * @param string|bool $restoreid * @return string */ public static final function load_controller($restoreid = false) { // Get the restore id optional param. if ($restoreid) { try { // Try to load the controller with it. // If it fails at this point it is likely because this is the first load. $controller = restore_controller::load_controller($restoreid); return $controller; } catch (Exception $e) { return false; } } return $restoreid; }
$output = $PAGE->get_renderer('core', 'backup'); $a = new stdClass(); $a->modtype = get_string('modulename', $cm->modname); $a->modname = format_string($cm->name); if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) { $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)); print_error('duplicatenosupport', 'error', $url, $a); } // backup the activity $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // restore the backup immediately $rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } echo $output->header(); echo $output->precheck_notices($precheckresults); $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)); echo $output->continue_button($url); echo $output->footer(); die; } } $rc->execute_plan();
protected function extract_file_to_dir() { global $CFG, $USER; $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id); $fb = get_file_packer(); return $fb->extract_to_pathname("{$CFG->tempdir}/backup/" . $this->filename, "{$CFG->tempdir}/backup/{$this->filepath}/"); }
/** * This function tests that the functions responsible for moving questions to * different contexts also updates the tag instances associated with the questions. */ public function test_altering_tag_instance_context() { global $CFG, $DB; // Set to admin user. $this->setAdminUser(); // Create two course categories - we are going to delete one of these later and will expect // all the questions belonging to the course in the deleted category to be moved. $coursecat1 = $this->getDataGenerator()->create_category(); $coursecat2 = $this->getDataGenerator()->create_category(); // Create a couple of categories and questions. $context1 = context_coursecat::instance($coursecat1->id); $context2 = context_coursecat::instance($coursecat2->id); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $questioncat1 = $questiongenerator->create_question_category(array('contextid' => $context1->id)); $questioncat2 = $questiongenerator->create_question_category(array('contextid' => $context2->id)); $question1 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat1->id)); $question2 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat1->id)); $question3 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat2->id)); $question4 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat2->id)); // Now lets tag these questions. core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $context1, array('tag 1', 'tag 2')); core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $context1, array('tag 3', 'tag 4')); core_tag_tag::set_item_tags('core_question', 'question', $question3->id, $context2, array('tag 5', 'tag 6')); core_tag_tag::set_item_tags('core_question', 'question', $question4->id, $context2, array('tag 7', 'tag 8')); // Test moving the questions to another category. question_move_questions_to_category(array($question1->id, $question2->id), $questioncat2->id); // Test that all tag_instances belong to one context. $this->assertEquals(8, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat2->contextid))); // Test moving them back. question_move_questions_to_category(array($question1->id, $question2->id), $questioncat1->id); // Test that all tag_instances are now reset to how they were initially. $this->assertEquals(4, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat1->contextid))); $this->assertEquals(4, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat2->contextid))); // Now test moving a whole question category to another context. question_move_category_to_context($questioncat1->id, $questioncat1->contextid, $questioncat2->contextid); // Test that all tag_instances belong to one context. $this->assertEquals(8, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat2->contextid))); // Now test moving them back. question_move_category_to_context($questioncat1->id, $questioncat2->contextid, context_coursecat::instance($coursecat1->id)->id); // Test that all tag_instances are now reset to how they were initially. $this->assertEquals(4, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat1->contextid))); $this->assertEquals(4, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat2->contextid))); // Now we want to test deleting the course category and moving the questions to another category. question_delete_course_category($coursecat1, $coursecat2, false); // Test that all tag_instances belong to one context. $this->assertEquals(8, $DB->count_records('tag_instance', array('component' => 'core_question', 'contextid' => $questioncat2->contextid))); // Create a course. $course = $this->getDataGenerator()->create_course(); // Create some question categories and questions in this course. $coursecontext = context_course::instance($course->id); $questioncat = $questiongenerator->create_question_category(array('contextid' => $coursecontext->id)); $question1 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat->id)); $question2 = $questiongenerator->create_question('shortanswer', null, array('category' => $questioncat->id)); // Add some tags to these questions. core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, array('tag 1', 'tag 2')); core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, array('tag 1', 'tag 2')); // Create a course that we are going to restore the other course to. $course2 = $this->getDataGenerator()->create_course(); // Create backup file and save it to the backup location. $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; $fp = get_file_packer('application/vnd.moodle.backup'); $filepath = $CFG->dataroot . '/temp/backup/test-restore-course'; $file->extract_to_pathname($fp, $filepath); $bc->destroy(); // Now restore the course. $rc = new restore_controller('test-restore-course', $course2->id, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2, backup::TARGET_NEW_COURSE); $rc->execute_precheck(); $rc->execute_plan(); // Get the created question category. $restoredcategory = $DB->get_record('question_categories', array('contextid' => context_course::instance($course2->id)->id), '*', MUST_EXIST); // Check that there are two questions in the restored to course's context. $this->assertEquals(2, $DB->count_records('question', array('category' => $restoredcategory->id))); $rc->destroy(); }
if ($backup->get_stage() == backup_ui::STAGE_FINAL) { // First execute the backup $backup->execute(); $backup->destroy(); unset($backup); // Check whether the backup directory still exists. If missing, something // went really wrong in backup, throw error. Note that backup::MODE_IMPORT // backups don't store resulting files ever $tempdestination = $CFG->tempdir . '/backup/' . $backupid; if (!file_exists($tempdestination) || !is_dir($tempdestination)) { print_error('unknownbackupexporterror'); // shouldn't happen ever } // 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); // Convert the backup if required.... it should NEVER happed if ($rc->get_status() == backup::STATUS_REQUIRE_CONV) { $rc->convert(); } // Mark the UI finished. $rc->finish_ui(); // Execute prechecks $warnings = false; if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults)) { if (!empty($precheckresults['errors'])) { // If errors are found, terminate the import. fulldelete($tempdestination); echo $OUTPUT->header();