/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target glossary for the block. This must be done here * and not in normal execution steps because the glossary * may be restored after the block. */ public function after_restore() { global $DB; // Get the blockid $blockid = $this->get_blockid(); // Extract block configdata and update it to point to the new glossary if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->glossary)) { if ($glossarymap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary', $config->glossary)) { // Get glossary mapping and replace it in config $config->glossary = $glossarymap->newitemid; } else { if ($this->is_samesite()) { // We are restoring on the same site, check if glossary can be used in the block in this course. $glossaryid = $DB->get_field_sql("SELECT id FROM {glossary} " . "WHERE id = ? AND (course = ? OR globalglossary = 1)", [$config->glossary, $this->get_courseid()]); if (!$glossaryid) { unset($config->glossary); } } else { // The block refers to a glossary not present in the backup file. unset($config->glossary); } } // Unset config variables that are no longer used. unset($config->globalglossary); unset($config->courseid); // Save updated config. $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); } } }
/** * This function, executed after all the tasks in the plan * have been executed, will remove tag collection reference in case block was restored into another site. * Also get mapping of contextid. */ public function after_restore() { global $DB; // Get the blockid. $blockid = $this->get_blockid(); // Extract block configdata and remove tag collection reference if this is another site. Also map contextid. if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); $changed = false; if (!empty($config->tagcoll) && $config->tagcoll > 1 && !$this->is_samesite()) { $config->tagcoll = 0; $changed = true; } if (!empty($config->ctx)) { if ($ctxmap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'context', $config->ctx)) { $config->ctx = $ctxmap->newitemid; } else { $config->ctx = 0; } $changed = true; } if ($changed) { $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); } } }
/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target activity for the block. This must be done here * and not in normal execution so we are sure everything is * at its finale place. */ public function after_restore() { global $DB; // Get the blockid. $blockid = $this->get_blockid(); if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->section_id)) { // Get the mapping and replace it in config. if ($mapping = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'course_section', $config->section_id)) { // Update the parent module id (the id from mdl_quiz etc...) $config->section_id = $mapping->newitemid; // Encode and save the config. $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); // Arrange the replace link in the course_sections summary. $newsection = $DB->get_record('course_sections', array('id' => $mapping->newitemid)); // Update the Side Bar section with the required values to make it work. $reseturl = new moodle_url('/blocks/side_bar/reset.php?cid=' . $newsection->course); $newsection->name = get_string('sidebar', 'block_side_bar'); $newsection->summary = get_string('sectionsummary', 'block_side_bar', (string) html_writer::link($reseturl, $reseturl)); $newsection->summaryformat = FORMAT_HTML; $newsection->visible = true; $DB->update_record('course_sections', $newsection); } } } }
public function after_restore() { global $DB; $course_id = $this->get_courseid(); $records = $DB->get_records("block_lockdownbrowser_sett", array("course" => $course_id)); if ($records === FALSE) { return; } $missing_ids = 0; foreach ($records as $settings) { $old_quizid = $settings->quizid; if (empty($old_quizid)) { continue; } $quizmap = restore_dbops::get_backup_ids_record($this->get_restoreid(), "quiz", $old_quizid); if (empty($quizmap)) { $missing_ids++; continue; } $settings->quizid = $quizmap->newitemid; $DB->update_record('block_lockdownbrowser_sett', $settings); } if ($missing_ids > 0) { $this->get_logger()->process("Failed to restore dependency in block 'lockdownbrowser'. " . "Backup and restore will not work correctly unless you " . "include the dependent 'quiz' modules.", backup::LOG_ERROR); } }
/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target quiz for the block. This must be done here * and not in normal execution steps because the quiz * can be restored after the block. */ public function after_restore() { global $DB; // Get the blockid. $blockid = $this->get_blockid(); // Extract block configdata and update it to point to the new quiz. if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->quizid)) { // Get quiz mapping and replace it in config. if ($quizmap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'quiz', $config->quizid)) { $config->activityparent = 'quiz'; $config->activityparentid = $quizmap->newitemid; // Set the decimal valuue as appropriate. if ($config->gradeformat == 1) { // This block is using percentages, do not display any decimal places. $config->decimalpoints = 0; } else { // Get the decimal value from the corresponding quiz. $config->decimalpoints = $DB->get_field('quiz', 'decimalpoints', array('id' => $config->activityparentid)); } // Get the grade_items record to set the activitygradeitemid. $info = $DB->get_record('grade_items', array('iteminstance' => $config->activityparentid, 'itemmodule' => $config->activityparent)); $config->activitygradeitemid = $info->id; unset($config->quizid); // Save the new configuration and update the record. $DB->set_field('block_instances', 'configdata', base64_encode(serialize($config)), array('id' => $blockid)); $DB->set_field('block_instances', 'blockname', 'activity_results', array('id' => $blockid)); } } } }
protected function process_log($log, $oldctx, $context) { global $DB; $log = (object) $log; $oldid = $log->id; $mailedusers = explode(',', $log->mailto); $validusers = array(); foreach ($mailedusers as $userid) { $validusers[] = $this->get_mappingid('user', $userid); } $log->courseid = $this->get_courseid(); $log->userid = $this->get_mappingid('user', $log->userid); $log->mailto = implode(',', $validusers); $log->time = $this->apply_date_offset($log->time); // TODO: correctly convert alternate ids $log->alternateid = null; $newid = $DB->insert_record('block_quickmail_log', $log); $this->set_mapping('log', $oldid, $newid); foreach (array('log', 'attachment_log') as $filearea) { restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'block_quickmail', $filearea, $oldctx, $log->userid); $sql = 'UPDATE {files} SET itemid = :newid WHERE contextid = :ctxt AND itemid = :oldid'; $params = array('newid' => $newid, 'oldid' => $oldid, 'ctxt' => $context->id); $DB->execute($sql, $params); } }
protected function dispatch_chunk($data) { // Prepare question_category record if ($data['path'] == '/question_categories/question_category') { $info = (object) $data['tags']; $itemname = 'question_category'; $itemid = $info->id; $parentitemid = $info->contextid; $this->lastcatid = $itemid; // Prepare question record } else { if ($data['path'] == '/question_categories/question_category/questions/question') { $info = (object) $data['tags']; $itemname = 'question'; $itemid = $info->id; $parentitemid = $this->lastcatid; // Not question_category nor question, impossible. Throw exception. } else { throw new progressive_parser_exception('restore_questions_parser_processor_unexpected_path', $data['path']); } } // Only load it if needed (exist same question_categoryref itemid in table) if (restore_dbops::get_backup_ids_record($this->restoreid, 'question_categoryref', $this->lastcatid)) { restore_dbops::set_backup_ids_record($this->restoreid, $itemname, $itemid, 0, $parentitemid, $info); } }
protected function dispatch_chunk($data) { // Received one inforef chunck, we are going to store it into backup_ids // table, with name = itemname + "ref" for later use $itemname = basename($data['path']) . 'ref'; $itemid = $data['tags']['id']; restore_dbops::set_backup_ids_record($this->restoreid, $itemname, $itemid); }
protected function dispatch_chunk($data) { // Received one role chunck, we are going to store it into backup_ids // table, with name = role $itemname = 'role'; $itemid = $data['tags']['id']; $info = $data['tags']; // Only load it if needed (exist same roleref itemid in table) if (restore_dbops::get_backup_ids_record($this->restoreid, 'roleref', $itemid)) { restore_dbops::set_backup_ids_record($this->restoreid, $itemname, $itemid, 0, null, $info); } }
protected function dispatch_chunk($data) { // Received one user chunck, we are going to store it into backup_ids // table, with name = user and parentid = contextid for later use $itemname = 'user'; $itemid = $data['tags']['id']; $parentitemid = $data['tags']['contextid']; $info = $data['tags']; // Only load it if needed (exist same userref itemid in table) if (restore_dbops::get_backup_ids_record($this->restoreid, 'userref', $itemid)) { restore_dbops::set_backup_ids_record($this->restoreid, $itemname, $itemid, 0, $parentitemid, $info); } }
/** * Checks if mapped questions are exact valid, or marks them to be created * * @global $DB * @throws moodle_exception */ protected function define_execution() { global $DB; $restoreid = $this->get_restoreid(); $courseid = $this->get_courseid(); $userid = $this->task->get_userid(); $workaround_qtypes = explode(',', get_config('block_sharing_cart', 'workaround_qtypes')); // @see /backup/util/dbops/restore_dbops.class.php#prechek_precheck_qbanks_by_level $contexts = restore_dbops::restore_get_question_banks($restoreid); foreach ($contexts as $contextid => $contextlevel) { $categories = restore_dbops::restore_get_question_categories($restoreid, $contextid); $canadd = false; if ($targetcontext = restore_dbops::restore_find_best_target_context($categories, $courseid, $contextlevel)) { $canadd = has_capability('moodle/question:add', $targetcontext, $userid); } foreach ($categories as $category) { $questions = restore_dbops::restore_get_questions($restoreid, $category->id); foreach ($questions as $question) { if (!in_array($question->qtype, $workaround_qtypes)) { continue; } $mapping = restore_dbops::get_backup_ids_record($restoreid, 'question', $question->id); if ($mapping && $mapping->newitemid && !self::is_question_valid($question->qtype, $mapping->newitemid)) { if (!$canadd) { throw new moodle_exception('questioncannotberestored', 'backup', '', $question); } $catmapping = restore_dbops::get_backup_ids_record($restoreid, 'question_category', $category->id); $matchquestions = $DB->get_records('question', array('category' => $catmapping->newitemid, 'qtype' => $question->qtype, 'stamp' => $question->stamp, 'version' => $question->version)); $newitemid = 0; // to be created if no valid duplicate exists foreach ($matchquestions as $q) { if ($q->id == $mapping->newitemid) { continue; } if (self::is_question_valid($question->qtype, $q->id)) { $newitemid = $q->id; // updates mapping if a valid one found break; } } $this->update_mapping($mapping, $newitemid); } } } } }
public function after_restore() { global $DB; // Find all the items that have a 'moduleid' but are not headings and match them up to the newly-restored activities. $items = $DB->get_records_select('checklist_item', 'checklist = ? AND moduleid > 0 AND itemoptional <> 2', array($this->get_activityid())); foreach ($items as $item) { $moduleid = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'course_module', $item->moduleid); if ($moduleid) { // Match up the moduleid to the restored activity module. $DB->set_field('checklist_item', 'moduleid', $moduleid->newitemid, array('id' => $item->id)); } else { // Does not match up to a restored activity module => delete the item + associated user data. $DB->delete_records('checklist_check', array('item' => $item->id)); $DB->delete_records('checklist_comment', array('itemid' => $item->id)); $DB->delete_records('checklist_item', array('id' => $item->id)); } } }
/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target quiz for the block. This must be done here * and not in normal execution steps because the quiz * can be restored after the block. */ public function after_restore() { global $DB; // Get the blockid $blockid = $this->get_blockid(); // Extract block configdata and update it to point to the new quiz if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->quizid)) { // Get quiz mapping and replace it in config if ($quizmap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'quiz', $config->quizid)) { $config->quizid = $quizmap->newitemid; $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); } } } }
public function test_mod_jclic_backup_restore() { global $USER, $DB, $CFG; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; $this->resetAfterTest(true); $this->setUser(2); // Admin user. // Create a course with some availability data set. $generator = $this->getDataGenerator(); $course = $generator->create_course(array('format' => 'topics', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true)); $jclic = $generator->create_module('jclic', array('course' => $course->id)); // 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, $course->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. $newcourseid = restore_dbops::create_new_course($course->fullname, $course->shortname . '_2', $course->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(); // Check settings in new course. $modinfo = get_fast_modinfo($newcourseid); $jclics = array_values($modinfo->get_instances_of('jclic')); $newjclic = new jclic(context_module::instance($jclics[0]->id), false, false); $newjclicmodule = $newjclic->get_instance(); $this->assertNotEmpty($newjclic); $jclic = new jclic(context_module::instance($jclic->cmid), false, false); $jclicmodule = $jclic->get_instance(); $fields = (array) $jclicmodule; unset($fields['id']); unset($fields['course']); foreach ($fields as $key => $unused) { $this->assertEquals($newjclicmodule->{$key}, $jclicmodule->{$key}, "Failed on field {$key}"); } // Avoid errors... ini_set('max_execution_time', 0); }
/** * Translates the backed up configuration data for the target course modules. * * @global type $DB */ public function after_restore() { global $DB; $prefixes = array('monitor_', 'date_time_', 'action_', 'locked_'); // Get the blockid. $id = $this->get_blockid(); // Get restored course id. $courseid = $this->get_courseid(); if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $id))) { $config = (array) unserialize(base64_decode($configdata)); $newconfig = $config; // Filter module related config information. foreach ($newconfig as $key => $value) { foreach ($prefixes as $prefix) { if (substr($key, 0, strlen($prefix)) === $prefix) { unset($newconfig[$key]); } } } // Translate the old config information to the target course values. foreach ($config as $key => $value) { $matches = array(); preg_match('/monitor_(\\D+)(\\d+)/', $key, $matches); if (!empty($matches)) { $module = $matches[1]; $instance = $matches[2]; // Find the mapped instance ID. if ($newinstance = restore_dbops::get_backup_ids_record($this->get_restoreid(), $module, $instance)) { $newinstanceid = $newinstance->newitemid; // Translate new instance values from old IDs. foreach ($prefixes as $prefix) { if (isset($config["{$prefix}{$module}{$instance}"])) { $newconfig["{$prefix}{$module}{$newinstanceid}"] = $config["{$prefix}{$module}{$instance}"]; } } } } } // Save everything back to DB. $configdata = base64_encode(serialize((object) $newconfig)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $id)); } }
/** * Roll over an existing Moodle course into a new one using a customized backup * and restore routine * * @param int $courseid Id of the course we are copying * @param int $categoryid * @return int|boolean Id of the newly created course */ function course_rollover($courseid, $categoryid = null) { global $DB; if ($course = $DB->get_record('course', array('id' => $courseid))) { //figure out the category if ($categoryid === null) { $categoryid = $course->category; } //make sure the category is valid if (!$DB->record_exists('course_categories', array('id' => $categoryid))) { //invalid category return false; } //perform backup without including user info $controller = new rollover_backup_controller($courseid); $controller->get_plan()->get_setting('users')->set_value(false); $controller->execute_plan(); //get directory name for use in restore $backupid = $controller->get_backupid(); //start a database transaction to make sure restore is atomic, etc $transaction = $DB->start_delegated_transaction(); //create the new course $result = restore_dbops::create_new_course($course->fullname, $course->shortname, $categoryid); //restore the content into it within the current transaction without including user information $controller = new rollover_restore_controller($backupid, $result); $controller->get_plan()->get_setting('users')->set_value(false); $controller->execute_precheck(); $controller->execute_plan(); //make sure the sort order is defined as expected fix_course_sortorder(); try { //try to finalize $transaction->allow_commit(); } catch (dml_transaction_exception $e) { //failure $result = false; } return $result; } else { //invalid course specified return false; } }
/** * Translates the backed up configuration data for the target course modules * * @global type $DB */ public function after_restore() { global $DB; // Get the blockid $id = $this->get_blockid(); //Restored course id $courseid = $this->get_courseid(); if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $id))) { $config = (array) unserialize(base64_decode($configdata)); // Translate the old config information to the target course values foreach ($config as $key => $value) { $matches = array(); preg_match('/monitor_(\\D+)(\\d+)/', $key, $matches); if ($value == 1 && !empty($matches)) { $module = $matches[1]; $instance = $matches[2]; // Find a matching module in the target course if ($cm = get_coursemodule_from_instance($module, $instance)) { // Get new cm and instance $newitem = restore_dbops::get_backup_ids_record($this->get_restoreid(), "course_module", $cm->id); $newcm = get_coursemodule_from_id($module, $newitem->newitemid); $newinstance = $newcm->instance; // Set new config $config["monitor_{$module}{$newinstance}"] = $config["monitor_{$module}{$instance}"]; $config["locked_{$module}{$newinstance}"] = $config["locked_{$module}{$instance}"]; $config["date_time_{$module}{$newinstance}"] = $config["date_time_{$module}{$instance}"]; $config["action_{$module}{$newinstance}"] = $config["action_{$module}{$instance}"]; // Unset old config unset($config["monitor_{$module}{$instance}"]); unset($config["locked_{$module}{$instance}"]); unset($config["date_time_{$module}{$instance}"]); unset($config["action_{$module}{$instance}"]); } } } // Save everything back to DB $configdata = base64_encode(serialize((object) $config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $id)); } }
public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name) { global $DB; if (!$this->roleid) { // If using 'same as activity' option, no need to change it. return false; } $rec = \restore_dbops::get_backup_ids_record($restoreid, 'role', $this->roleid); if (!$rec || !$rec->newitemid) { // If we are on the same course (e.g. duplicate) then we can just // use the existing one. if ($DB->record_exists('roles', array('id' => $this->roleid, 'courseid' => $courseid))) { return false; } // Otherwise it's a warning. $this->roleid = -1; $logger->process('Restored item (' . $name . ') has availability condition on grouping that was not restored', \backup::LOG_WARNING); } else { $this->roleid = (int) $rec->newitemid; } return true; }
/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target activity for the block. This must be done here * and not in normal execution steps because the activity * can be restored after the block. */ public function after_restore() { global $DB; // Get the blockid. $blockid = $this->get_blockid(); if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->activityparentid)) { // Get the mapping and replace it in config. if ($mapping = restore_dbops::get_backup_ids_record($this->get_restoreid(), $config->activityparent, $config->activityparentid)) { // Update the parent module id (the id from mdl_quiz etc...) $config->activityparentid = $mapping->newitemid; // Get the grade_items record to update the activitygradeitemid. $info = $DB->get_record('grade_items', array('iteminstance' => $config->activityparentid, 'itemmodule' => $config->activityparent)); // Update the activitygradeitemid the id from the grade_items table. $config->activitygradeitemid = $info->id; // Encode and save the config. $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); } } } }
/** * This function, executed after all the tasks in the plan * have been executed, will perform the recode of the * target glossary for the block. This must be done here * and not in normal execution steps because the glossary * may be restored after the block. */ public function after_restore() { global $DB; // Get the blockid $blockid = $this->get_blockid(); // Extract block configdata and update it to point to the new glossary if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $blockid))) { $config = unserialize(base64_decode($configdata)); if (!empty($config->glossary)) { // Get glossary mapping and replace it in config if ($glossarymap = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary', $config->glossary)) { $mappedglossary = $DB->get_record('glossary', array('id' => $glossarymap->newitemid), 'id,course,globalglossary', MUST_EXIST); $config->glossary = $mappedglossary->id; $config->courseid = $mappedglossary->course; $config->globalglossary = $mappedglossary->globalglossary; $configdata = base64_encode(serialize($config)); $DB->set_field('block_instances', 'configdata', $configdata, array('id' => $blockid)); } else { // The block refers to a glossary not present in the backup file. $DB->set_field('block_instances', 'configdata', '', array('id' => $blockid)); } } } }
/** * Send the question type specific files to a new context. * * @param text $qtype The qtype name to send. * @param int $oldctxid Old context id. * @param int $newctxid New context id. * @param \core\progress $progress Progress object to use. */ private function send_qtype_files($qtype, $oldctxid, $newctxid, $progress) { if (!isset($this->qtypecomponentscache[$qtype])) { $this->qtypecomponentscache[$qtype] = backup_qtype_plugin::get_components_and_fileareas($qtype); } $components = $this->qtypecomponentscache[$qtype]; foreach ($components as $component => $fileareas) { foreach ($fileareas as $filearea => $mapping) { restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea, $oldctxid, $this->task->get_userid(), $mapping, null, $newctxid, true, $progress); } } }
/** * Add all the existing file, given their component and filearea and one backup_ids itemname to match with */ public function add_related_files($component, $filearea, $mappingitemname, $filesctxid = null, $olditemid = null) { // If the current progress object is set up and ready to receive // indeterminate progress, then use it, otherwise don't. (This check is // just in case this function is ever called from somewhere not within // the execute() method here, which does set up progress like this.) $progress = $this->get_task()->get_progress(); if (!$progress->is_in_progress_section() || $progress->get_current_max() !== \core\progress\base::INDETERMINATE) { $progress = null; } $filesctxid = is_null($filesctxid) ? $this->task->get_old_contextid() : $filesctxid; $results = restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea, $filesctxid, $this->task->get_userid(), $mappingitemname, $olditemid, null, false, $progress); $resultstoadd = array(); foreach ($results as $result) { $this->log($result->message, $result->level); $resultstoadd[$result->code] = true; } $this->task->add_result($resultstoadd); }
/** * 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; }
/** * Processes the destination stage. * @return bool * @throws coding_exception * @throws restore_ui_exception */ public function process() { global $CFG, $DB; if (!file_exists("{$CFG->tempdir}/backup/" . $this->filepath) || !is_dir("{$CFG->tempdir}/backup/" . $this->filepath)) { throw new restore_ui_exception('invalidrestorepath'); } if (optional_param('searchcourses', false, PARAM_BOOL)) { return false; } $this->target = optional_param('target', backup::TARGET_NEW_COURSE, PARAM_INT); $targetid = optional_param('targetid', null, PARAM_INT); if (!is_null($this->target) && !is_null($targetid) && confirm_sesskey()) { if ($this->target == backup::TARGET_NEW_COURSE) { list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'), get_string('restoringcourseshortname', 'backup')); $this->courseid = restore_dbops::create_new_course($fullname, $shortname, $targetid); } else { $this->courseid = $targetid; } return $DB->record_exists('course', array('id' => $this->courseid)); } return false; }
protected function define_execution() { global $DB; // Let's process only created questions $questionsrs = $DB->get_recordset_sql("SELECT bi.itemid, bi.newitemid, bi.parentitemid, q.qtype FROM {backup_ids_temp} bi JOIN {question} q ON q.id = bi.newitemid WHERE bi.backupid = ? AND bi.itemname = 'question_created'", array($this->get_restoreid())); foreach ($questionsrs as $question) { // Get question_category mapping, it contains the target context for the question if (!$qcatmapping = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question_category', $question->parentitemid)) { // Something went really wrong, cannot find the question_category for the question debugging('Error fetching target context for question', DEBUG_DEVELOPER); continue; } // Calculate source and target contexts $oldctxid = $qcatmapping->info->contextid; $newctxid = $qcatmapping->parentitemid; // Add common question files (question and question_answer ones) restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'questiontext', $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true); restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'generalfeedback', $oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true); restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answer', $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true); restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answerfeedback', $oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true); restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint', $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true); // Add qtype dependent files $components = backup_qtype_plugin::get_components_and_fileareas($question->qtype); foreach ($components as $component => $fileareas) { foreach ($fileareas as $filearea => $mapping) { // Use itemid only if mapping is question_created $itemid = ($mapping == 'question_created') ? $question->itemid : null; restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), $component, $filearea, $oldctxid, $this->task->get_userid(), $mapping, $itemid, $newctxid, true); } } } $questionsrs->close(); }
/** * Backs a course up and restores it. * * @param stdClass $course Course object to backup * @param int $newdate If non-zero, specifies custom date for new course * @return int ID of newly restored course */ protected function backup_and_restore($course, $newdate = 0) { 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, $course->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. $newcourseid = restore_dbops::create_new_course($course->fullname, $course->shortname . '_2', $course->category); $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_NEW_COURSE); if ($newdate) { $rc->get_plan()->get_setting('course_startdate')->set_value($newdate); } $this->assertTrue($rc->execute_precheck()); $rc->execute_plan(); $rc->destroy(); return $newcourseid; }
<?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();
public function after_restore() { // Do something at end of restore. global $DB; // Get the blockid. $gameid = $this->get_activityid(); // Extract Game configdata and update it to point to the new glossary. $rec = $DB->get_record_select('game', 'id=' . $gameid, null, 'id,quizid,glossaryid,glossarycategoryid,questioncategoryid,bookid,glossaryid2,glossarycategoryid2'); $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'quiz', $rec->quizid); if ($ret != false) { $rec->quizid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary', $rec->glossaryid); if ($ret != false) { $rec->glossaryid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary_categories', $rec->glossarycategoryid); if ($ret != false) { $rec->glossarycategoryid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question_categories', $rec->questioncategoryid); if ($ret != false) { $rec->questioncategoryid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'book', $rec->bookid); if ($ret != false) { $rec->bookid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary', $rec->glossaryid2); if ($ret != false) { $rec->glossaryid2 = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary_categories', $rec->glossarycategoryid); if ($ret != false) { $rec->glossarycategoryid = $ret->newitemid; } $DB->update_record('game', $rec); // Read game_repetitions. $recs = $DB->get_records_select('game_repetitions', 'gameid=' . $gameid, null, '', 'id,questionid,glossaryentryid'); if ($recs != false) { foreach ($recs as $rec) { $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question', $rec->questionid); if ($ret != false) { $rec->questionid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary_entry', $rec->glossaryentryid); if ($ret != false) { $rec->glossaryentryid = $ret->newitemid; } $DB->update_record('game_repetitions', $rec); } } // Read game_queries. $recs = $DB->get_records_select('game_queries', 'gameid=' . $gameid, null, '', 'id,questionid,glossaryentryid,answerid'); if ($recs != false) { foreach ($recs as $rec) { $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question', $rec->questionid); if ($ret != false) { $rec->questionid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'glossary_entry', $rec->glossaryentryid); if ($ret != false) { $rec->glossaryentryid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'question_answers', $rec->glossaryentryid); if ($ret != false) { $rec->answerid = $ret->newitemid; } $DB->update_record('game_queries', $rec); } } // Read bookquiz. $recs = $DB->get_records_select('game_bookquiz', 'id=' . $gameid, null, '', 'id,lastchapterid'); if ($recs != false) { foreach ($recs as $rec) { $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'book_chapters', $rec->lastchapterid); if ($ret != false) { $rec->lastchapterid = $ret->newitemid; } $DB->update_record('game_bookquiz', $rec); } } // Read bookquiz_chapters. $sql = "SELECT gbc.* " . "FROM {game_bookquiz_chapters} gbc LEFT JOIN {game_attempts} a ON gbc.attemptid = a.id" . " WHERE a.gameid={$gameid}"; $recs = $DB->get_records_sql($sql); if ($recs != false) { foreach ($recs as $rec) { $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'book_chapters', $rec->chapterid); if ($ret != false) { $rec->chapterid = $ret->newitemid; } $DB->update_record('game_bookquiz_chapter', $rec); } } // Read bookquiz_questions. $recs = $DB->get_records_select('game_bookquiz_questions', 'id=' . $gameid, null, '', 'id,chapterid,questioncategoryid'); if ($recs != false) { foreach ($recs as $rec) { $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'book_chapters', $rec->chapterid); if ($ret != false) { $rec->chapterid = $ret->newitemid; } $ret = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'book_chapters', $rec->questioncategoryid); if ($ret != false) { $rec->questioncategoryid = $ret->newitemid; } $DB->update_record('game_bookquiz_questions', $rec); } } }
/** * Restores a given stash stored previously by {@link self::set_stash()} * * @param string $stashname name of the stash * @param int $itemid optional id for multiple infos within the same stashname * @throws moodle1_convert_empty_storage_exception if the info has not been stashed previously * @return mixed stashed data */ public function get_stash($stashname, $itemid = 0) { $record = restore_dbops::get_backup_ids_record($this->get_id(), $stashname, $itemid); if (empty($record)) { throw new moodle1_convert_empty_storage_exception('required_not_stashed_data', array($stashname, $itemid)); } else { return $record->info; } }
/** * 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; }