/** * Send one backup controller to DB * * @param backup_controller $controller controller to send to DB * @param string $checksum hash of the controller to be checked * @param bool $includeobj to decide if the object itself must be updated (true) or no (false) * @param bool $cleanobj to decide if the object itself must be cleaned (true) or no (false) * @return int id of the controller record in the DB * @throws backup_controller_exception|backup_dbops_exception */ public static function save_controller($controller, $checksum, $includeobj = true, $cleanobj = false) { global $DB; // Check we are going to save one backup_controller if (!$controller instanceof backup_controller) { throw new backup_controller_exception('backup_controller_expected'); } // Check checksum is ok. Only if we are including object info. Sounds silly but it isn't ;-). if ($includeobj and !$controller->is_checksum_correct($checksum)) { throw new backup_dbops_exception('backup_controller_dbops_saving_checksum_mismatch'); } // Cannot request to $includeobj and $cleanobj at the same time. if ($includeobj and $cleanobj) { throw new backup_dbops_exception('backup_controller_dbops_saving_cannot_include_and_delete'); } // Get all the columns $rec = new stdclass(); $rec->backupid = $controller->get_backupid(); $rec->operation = $controller->get_operation(); $rec->type = $controller->get_type(); $rec->itemid = $controller->get_id(); $rec->format = $controller->get_format(); $rec->interactive = $controller->get_interactive(); $rec->purpose = $controller->get_mode(); $rec->userid = $controller->get_userid(); $rec->status = $controller->get_status(); $rec->execution = $controller->get_execution(); $rec->executiontime = $controller->get_executiontime(); $rec->checksum = $checksum; // Serialize information if ($includeobj) { $rec->controller = base64_encode(serialize($controller)); } else { if ($cleanobj) { $rec->controller = ''; } } // Send it to DB if ($recexists = $DB->get_record('backup_controllers', array('backupid' => $rec->backupid))) { $rec->id = $recexists->id; $rec->timemodified = time(); $DB->update_record('backup_controllers', $rec); } else { $rec->timecreated = time(); $rec->timemodified = 0; $rec->id = $DB->insert_record('backup_controllers', $rec); } return $rec->id; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * Get the restore content tempdir. * * The tempdir is the sub directory in which the backup has been extracted. * * This caches the result for better performance, but $CFG->keeptempdirectoriesonbackup * needs to be enabled, otherwise the cache is ignored. * * @param string $backupfile path to a backup file. * @param string $shortname shortname of a course. * @param array $errors will be populated with errors found. * @return string|false false when the backup couldn't retrieved. */ public static function get_restore_content_dir($backupfile = null, $shortname = null, &$errors = array()) { global $CFG, $DB, $USER; $cachekey = null; if (!empty($backupfile)) { $backupfile = realpath($backupfile); if (empty($backupfile) || !is_readable($backupfile)) { $errors['cannotreadbackupfile'] = new lang_string('cannotreadbackupfile', 'tool_uploadcourse'); return false; } $cachekey = 'backup_path:' . $backupfile; } else { if (!empty($shortname) || is_numeric($shortname)) { $cachekey = 'backup_sn:' . $shortname; } } if (empty($cachekey)) { return false; } // If $CFG->keeptempdirectoriesonbackup is not set to true, any restore happening would // automatically delete the backup directory... causing the cache to return an unexisting directory. $usecache = !empty($CFG->keeptempdirectoriesonbackup); if ($usecache) { $cache = cache::make('tool_uploadcourse', 'helper'); } // If we don't use the cache, or if we do and not set, or the directory doesn't exist any more. if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir("{$CFG->tempdir}/backup/{$backupid}"))) { // Use null instead of false because it would consider that the cache key has not been set. $backupid = null; if (!empty($backupfile)) { // Extracting the backup file. $packer = get_file_packer('application/vnd.moodle.backup'); $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id); $path = "{$CFG->tempdir}/backup/{$backupid}/"; $result = $packer->extract_to_pathname($backupfile, $path); if (!$result) { $errors['invalidbackupfile'] = new lang_string('invalidbackupfile', 'tool_uploadcourse'); } } else { if (!empty($shortname) || is_numeric($shortname)) { // Creating restore from an existing course. $courseid = $DB->get_field('course', 'id', array('shortname' => $shortname), IGNORE_MISSING); if (!empty($courseid)) { $bc = new backup_controller(backup::TYPE_1COURSE, $courseid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id); $bc->execute_plan(); $backupid = $bc->get_backupid(); $bc->destroy(); } else { $errors['coursetorestorefromdoesnotexist'] = new lang_string('coursetorestorefromdoesnotexist', 'tool_uploadcourse'); } } } if ($usecache) { $cache->set($cachekey, $backupid); } } if ($backupid === null) { $backupid = false; } return $backupid; }
/** * C'est une copie de la commande moosh * https://github.com/tmuras/moosh/blob/master/Moosh/Command/Moodle23/Course/CourseBackup.php * @param $shortname * @param $savepath */ public static function execute($shortname, $savepath) { global $CFG, $DB, $USER; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; error_reporting(E_ALL); ini_set('display_errors', true); //check if course id exists $course = $DB->get_record('course', array('shortname' => $shortname), '*', MUST_EXIST); $shortname = str_replace(' ', '_', $course->shortname); $filename = $savepath . '/backup_' . str_replace('/', '_', $shortname) . '_' . date('Y.m.d') . '.mbz'; //check if destination file does not exist and can be created if (file_exists($filename)) { cli_error("File '{$filename}' already exists, I will not over-write it."); } $bc = new \backup_controller(\backup::TYPE_1COURSE, $course->id, \backup::FORMAT_MOODLE, \backup::INTERACTIVE_YES, \backup::MODE_GENERAL, 2); // 2 correspond à user admin! $tasks = $bc->get_plan()->get_tasks(); foreach ($tasks as &$task) { if ($task instanceof \backup_root_task) { $setting = $task->get_setting('users'); $setting->set_value('0'); $setting = $task->get_setting('anonymize'); $setting->set_value('1'); $setting = $task->get_setting('role_assignments'); $setting->set_value('1'); $setting = $task->get_setting('filters'); $setting->set_value('1'); $setting = $task->get_setting('comments'); $setting->set_value('0'); $setting = $task->get_setting('logs'); $setting->set_value('0'); $setting = $task->get_setting('grade_histories'); $setting->set_value('0'); } } $bc->set_status(\backup::STATUS_AWAITING); $bc->execute_plan(); $result = $bc->get_results(); if (isset($result['backup_destination']) && $result['backup_destination']) { $file = $result['backup_destination']; /** @var $file stored_file */ if (!$file->copy_content_to($filename)) { cli_error("Problems copying final backup to '" . $filename . "'"); } else { printf("%s\n", $filename); } } else { echo $bc->get_backupid(); } }
/** * Duplicate a course * * @param int $courseid * @param string $fullname Duplicated course fullname * @param string $shortname Duplicated course shortname * @param int $categoryid Duplicated course parent category id * @param int $visible Duplicated course availability * @param array $options List of backup options * @return array New course info * @since Moodle 2.3 */ public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible, $options) { 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::duplicate_course_parameters(), array('courseid' => $courseid, 'fullname' => $fullname, 'shortname' => $shortname, 'categoryid' => $categoryid, 'visible' => $visible, 'options' => $options)); // Context validation. if (!($course = $DB->get_record('course', array('id' => $params['courseid'])))) { throw new moodle_exception('invalidcourseid', 'error', '', $params['courseid']); } // Category where duplicated course is going to be created. $categorycontext = context_coursecat::instance($params['categoryid']); self::validate_context($categorycontext); // Course to be duplicated. $coursecontext = context_course::instance($course->id); self::validate_context($coursecontext); $backupdefaults = array('activities' => 1, 'blocks' => 1, 'filters' => 1, 'users' => 0, 'role_assignments' => 0, 'user_files' => 0, 'comments' => 0, 'completion_information' => 0, 'logs' => 0, 'histories' => 0); $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. // The backup controller check for this currently, this may be redundant. require_capability('moodle/course:create', $categorycontext); require_capability('moodle/restore:restorecourse', $categorycontext); require_capability('moodle/backup:backupcourse', $coursecontext); if (!empty($backupsettings['users'])) { require_capability('moodle/backup:userinfo', $coursecontext); require_capability('moodle/restore:userinfo', $categorycontext); } // Check if the shortname is used. if ($foundcourses = $DB->get_records('course', array('shortname' => $shortname))) { foreach ($foundcourses as $foundcourse) { $foundcoursenames[] = $foundcourse->fullname; } $foundcoursenamestring = implode(',', $foundcoursenames); throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring); } // Backup the course. $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $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(); $results = $bc->get_results(); $file = $results['backup_destination']; $bc->destroy(); // Restore the backup immediately. // Check if we need to unzip the file because the backup temp dir does not contains backup files. if (!file_exists($backupbasepath . "/moodle_backup.xml")) { $file->extract_to_pathname(get_file_packer(), $backupbasepath); } // Create new course. $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']); $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE); foreach ($backupsettings as $name => $value) { $setting = $rc->get_plan()->get_setting($name); if ($setting->get_status() == backup_setting::NOT_LOCKED) { $setting->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); } } $rc->execute_plan(); $rc->destroy(); $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST); $course->fullname = $params['fullname']; $course->shortname = $params['shortname']; $course->visible = $params['visible']; // Set shortname and fullname back. $DB->update_record('course', $course); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } // Delete the course backup file created by this WebService. Originally located in the course backups area. $file->delete(); return array('id' => $course->id, 'shortname' => $course->shortname); }
require_once '../../config.php'; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once 'lib.php'; // Login require_login(); // Backup $course_module_to_backup = $course->id; // Set this to one existing choice cmid in your dev site $user_doing_the_backup = $USER->id; // Set this to the id of your admin accouun $bc = new backup_controller(backup::TYPE_1COURSE, $course_module_to_backup, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $user_doing_the_backup); $bc->execute_plan(); require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; // Restore // Transaction. $transaction = $DB->start_delegated_transaction(); // Create new course. $folder = $bc->get_backupid(); $userdoingrestore = $USER->id; // e.g. 2 == admin $courseid = restore_dbops::create_new_course('', '', $categoryid); // Restore backup into course. $controller = new restore_controller($folder, $courseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $userdoingrestore, backup::TARGET_NEW_COURSE); $controller->execute_precheck(); $controller->execute_plan(); // Commit. $transaction->allow_commit(); ?>
function backup_template($courseid_from,$settings,$config,$admin) { $bc = new backup_controller(backup::TYPE_1COURSE, $courseid_from, backup::FORMAT_MOODLE, backup::INTERACTIVE_YES, backup::MODE_IMPORT,$admin->id); $backupid = $bc->get_backupid(); $bc->get_plan()->get_setting('users')->set_status(backup_setting::LOCKED_BY_CONFIG); foreach ($settings as $setting => $configsetting) { if ($bc->get_plan()->setting_exists($setting)) { $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting}); } } $bc->finish_ui(); $bc->execute_plan(); $bc->destroy(); unset($bc); return $backupid; }
public function test_mod_jclic_duplicate() { 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)); $cmid = $jclic->cmid; // 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'); } // Check settings in new course. $newjclic = new jclic(context_module::instance($newcmid), 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); }
/** * Tests the restore_controller. */ public function test_restore_controller_is_executing() { global $CFG; // Make a backup. check_dir_exists($CFG->tempdir . '/backup'); $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid); $backupid = $bc->get_backupid(); $bc->execute_plan(); $bc->destroy(); // The progress class will get called during restore, so we can use that // to check the executing flag is true. $progress = new core_backup_progress_restore_is_executing(); // Set up restore. $rc = new restore_controller($backupid, $this->courseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid, backup::TARGET_EXISTING_ADDING); $this->assertTrue($rc->execute_precheck()); // Check restore is NOT executing. $this->assertFalse(restore_controller::is_executing()); // Execute restore. $rc->set_progress($progress); $rc->execute_plan(); // Check restore is NOT executing afterward either. $this->assertFalse(restore_controller::is_executing()); $rc->destroy(); // During restore, check that executing was true. $this->assertTrue(count($progress->executing) > 0); $alltrue = true; foreach ($progress->executing as $executing) { if (!$executing) { $alltrue = false; break; } } $this->assertTrue($alltrue); }
require_capability('moodle/restore:restoretargetimport', $context); $PAGE->set_title(get_string('duplicate')); $PAGE->set_heading($course->fullname); $PAGE->set_url(new moodle_url('/course/modduplicate.php', array('cmid' => $cm->id, 'courseid' => $course->id))); $PAGE->set_pagelayout('incourse'); $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);
/** * Takes the original course object as an argument, backs up the * course and returns a backupid which can be used for restoring the course. * * @param object $course Original course object * @param array $activityids all cm ids to backup * @param array $activityids all section ids to backup * @throws coding_exception Moodle coding_exception * @retun string backupid Backupid */ private function backup($course, $activityids, $sectionids) { global $CFG, $DB, $USER; // MODE_IMPORT option is required to prevent it from zipping up the // result at the end. Unfortunately this breaks the system in some // other ways (see other comments). // NOTE: We cannot use MODE_SAMESITE here because that option will // zip the result and anyway, MODE_IMPORT already turns off the files. $bc = new \backup_controller(\backup::TYPE_1COURSE, $course->id, \backup::FORMAT_MOODLE, \backup::INTERACTIVE_NO, \backup::MODE_IMPORT, $USER->id); // Setup custom logger that prints dots. $logger = new logger(\backup::LOG_INFO, false, true); self::$currentlogger = $logger; $logger->potential_dot(); $bc->get_logger()->set_next($logger); $bc->set_progress(new progress()); foreach ($bc->get_plan()->get_tasks() as $taskindex => $task) { $settings = $task->get_settings(); foreach ($settings as $settingindex => $setting) { $setting->set_status(\backup_setting::NOT_LOCKED); // Modify the values of the intial backup settings. if ($taskindex == 0) { if (isset($this->backupsettings[$setting->get_name()])) { $setting->set_value($this->backupsettings[$setting->get_name()]); } } else { list($name, $id, $type) = $this->parse_backup_course_module($setting->get_name()); // Include user data on glossary if the role 'contributingstudent' // does not have the capability mod/glossary:write on that glossary. // This is so that we include course-team glossary data but not // glossaries that are written by students. if ($name === 'glossary' && $type === 'userinfo') { // Find the contributing student role id. If there // isn't one, use the student role id, for dev servers etc. $roles = $DB->get_records_select('role', "shortname IN ('contributingstudent', 'student')", null, 'shortname', 'id'); if (!$roles) { continue; } $roleid = reset($roles)->id; // Get the list of roles which have the capability in the context // of the glossary. $context = \context_module::instance($id); list($allowed, $forbidden) = get_roles_with_cap_in_context($context, 'mod/glossary:write'); // If student has this capability then the user data is false. if (!empty($allowed[$roleid]) && empty($forbidden[$roleid])) { $setting->set_value(false); } else { $setting->set_value(true); } } // Ignone any sections not in subpage. if ($name === 'section' && $type === 'included' && !in_array($id, $sectionids)) { $setting->set_value(false); } // Ignone any activities not in subpage. if ($name !== 'section' && $type === 'included' && !in_array($id, $activityids)) { $setting->set_value(false); } } } } $logger->potential_dot(); $bc->save_controller(); $backupid = $bc->get_backupid(); $this->backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); return $backupid; }
/** * 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; }
/** * Returns welcome message. * @param string $sourcecourse * @param string $destinationcourse * @param bool $excludequestionbank * @return string welcome message. */ public static function copy_content($sourcecourse = '', $destinationcourse = '', $excludequestionbank = false) { global $DB; try { $courseid = $destinationcourse; $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); // The id of the course we are importing FROM (will only be set if past first stage. $importcourseid = $sourcecourse; // The target method for the restore (adding or deleting). $restoretarget = backup::TARGET_EXISTING_DELETING; $importcourse = $DB->get_record('course', array('id' => $importcourseid), '*', MUST_EXIST); $bc = new backup_controller(backup::TYPE_1COURSE, $importcourse->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, 2); $bc->get_plan()->get_setting('users')->set_status(backup_setting::LOCKED_BY_CONFIG); $settings = $bc->get_plan()->get_settings(); // If excludequestionbank is set, do not backup question bank. if ($excludequestionbank && $bc->get_plan()->setting_exists('questionbank')) { $setting = $bc->get_plan()->get_setting('questionbank'); $setting->set_value(0); } // For the initial stage we want to hide all locked settings and if there are. // No visible settings move to the next stage. foreach ($settings as $setting) { if ($setting->get_status() !== backup_setting::NOT_LOCKED) { $setting->set_visibility(backup_setting::HIDDEN); } } $backupid = $bc->get_backupid(); $bc->execute_plan(); restore_dbops::delete_course_content($course->id); $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2, $restoretarget); $rc->execute_precheck(); // If excludequestionbank is set, do not restore quizzes. if ($excludequestionbank) { $settings = $rc->get_plan()->get_settings(); foreach ($settings as $setting) { if (preg_match('/(quiz)(_)(\\d+)(_)(included)/', $setting->get_name())) { $rc->get_plan()->get_setting($setting->get_name())->set_value(0); } } } $rc->execute_plan(); $results = $rc->get_results(); if (isset($results["file_missing_in_backup"])) { if (extension_loaded('newrelic')) { newrelic_notice_error($results["file_missing_in_backup"], new Exception('Missing file in backup')); } } $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $importcourse->id = $course->id; $importcourse->modinfo = $course->modinfo; $importcourse->idnumber = $course->idnumber; $importcourse->fullname = $course->fullname; $importcourse->shortname = $course->shortname; $importcourse->startdate = $course->startdate; $importcourse->sectioncache = null; $sourceformat = $DB->get_field('course', 'format', array('id' => $importcourseid), MUST_EXIST); if (!($coursenumsections = $DB->get_field('course_format_options', 'value', array('courseid' => $importcourseid, 'name' => 'numsections', 'format' => $sourceformat)))) { $coursenumsections = 10; } if ($courseformatconfig = $DB->get_record('course_format_options', array('courseid' => $course->id, 'name' => 'numsections', 'format' => $sourceformat))) { $courseformatconfig->value = $coursenumsections; $DB->update_record('course_format_options', $courseformatconfig); } else { $courseformatconfig = new stdClass(); $courseformatconfig->courseid = $course->id; $courseformatconfig->name = 'numsections'; $courseformatconfig->value = $coursenumsections; $courseformatconfig->format = $sourceformat; $courseformatconfig->sectionid = 0; $DB->insert_record('course_format_options', $courseformatconfig); } $DB->update_record('course', $importcourse); return 'success'; } catch (Exception $e) { if (extension_loaded('newrelic')) { newrelic_notice_error($e->getMessage(), $e); } return 'Exception ' . $e->getMessage(); } }