/** * Backup a course and return its backup ID. * * @param int $courseid The course ID. * @param int $userid The user doing the backup. * @return string */ protected function backup_course($courseid, $userid = 2) { global $CFG; $packer = get_file_packer('application/vnd.moodle.backup'); $bc = new backup_controller(backup::TYPE_1COURSE, $courseid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $userid); $bc->execute_plan(); $results = $bc->get_results(); $results['backup_destination']->extract_to_pathname($packer, "{$CFG->tempdir}/backup/core_course_testcase"); $bc->destroy(); unset($bc); return 'core_course_testcase'; }
/** * 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 = 1, $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::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'); } // 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, 'comments' => 0, 'userscompletion' => 0, 'logs' => 0, 'grade_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); }
/** * 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); }
/** * Launches a automated backup routine for the given course * * @param stdClass $course * @param int $starttime * @param int $userid * @return bool */ public static function launch_automated_backup($course, $starttime, $userid) { $config = get_config('backup'); $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid); try { $settings = array('users' => 'backup_auto_users', 'role_assignments' => 'backup_auto_role_assignments', 'activities' => 'backup_auto_activities', 'blocks' => 'backup_auto_blocks', 'filters' => 'backup_auto_filters', 'comments' => 'backup_auto_comments', 'completion_information' => 'backup_auto_userscompletion', 'logs' => 'backup_auto_logs', 'histories' => 'backup_auto_histories'); foreach ($settings as $setting => $configsetting) { if ($bc->get_plan()->setting_exists($setting)) { $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting}); } } // Set the default filename $format = $bc->get_format(); $type = $bc->get_type(); $id = $bc->get_id(); $users = $bc->get_plan()->get_setting('users')->get_value(); $anonymised = $bc->get_plan()->get_setting('anonymize')->get_value(); $bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised)); $bc->set_status(backup::STATUS_AWAITING); $outcome = $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; $dir = $config->backup_auto_destination; $storage = (int) $config->backup_auto_storage; if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) { $dir = null; } if (!empty($dir) && $storage !== 0) { $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, true); $outcome = $file->copy_content_to($dir . '/' . $filename); if ($outcome && $storage === 1) { $file->delete(); } } $outcome = true; } catch (backup_exception $e) { $bc->log('backup_auto_failed_on_course', backup::LOG_WARNING, $course->shortname); $outcome = false; } $bc->destroy(); unset($bc); return true; }
public function test_get_restore_content_dir() { global $CFG; $this->resetAfterTest(true); $this->setAdminUser(); $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course((object) array('shortname' => 'Yay')); // Creating backup file. $bc = new backup_controller(backup::TYPE_1COURSE, $c1->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2); $bc->execute_plan(); $result = $bc->get_results(); $this->assertTrue(isset($result['backup_destination'])); $c1backupfile = $result['backup_destination']->copy_content_to_temp(); $bc->destroy(); unset($bc); // File logging is a mess, we can only try to rely on gc to close handles. // Creating backup file. $bc = new backup_controller(backup::TYPE_1COURSE, $c2->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2); $bc->execute_plan(); $result = $bc->get_results(); $this->assertTrue(isset($result['backup_destination'])); $c2backupfile = $result['backup_destination']->copy_content_to_temp(); $bc->destroy(); unset($bc); // File logging is a mess, we can only try to rely on gc to close handles. $oldcfg = isset($CFG->keeptempdirectoriesonbackup) ? $CFG->keeptempdirectoriesonbackup : false; $CFG->keeptempdirectoriesonbackup = true; // Checking restore dir. $dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null); $bcinfo = backup_general_helper::get_backup_information($dir); $this->assertEquals($bcinfo->original_course_id, $c1->id); $this->assertEquals($bcinfo->original_course_fullname, $c1->fullname); // Do it again, it should be the same directory. $dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null); $this->assertEquals($dir, $dir2); // Get the second course. $dir = tool_uploadcourse_helper::get_restore_content_dir($c2backupfile, null); $bcinfo = backup_general_helper::get_backup_information($dir); $this->assertEquals($bcinfo->original_course_id, $c2->id); $this->assertEquals($bcinfo->original_course_fullname, $c2->fullname); // Checking with a shortname. $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname); $bcinfo = backup_general_helper::get_backup_information($dir); $this->assertEquals($bcinfo->original_course_id, $c1->id); $this->assertEquals($bcinfo->original_course_fullname, $c1->fullname); // Do it again, it should be the same directory. $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname); $this->assertEquals($dir, $dir2); // Get the second course. $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c2->shortname); $bcinfo = backup_general_helper::get_backup_information($dir); $this->assertEquals($bcinfo->original_course_id, $c2->id); $this->assertEquals($bcinfo->original_course_fullname, $c2->fullname); // Get a course that does not exist. $errors = array(); $dir = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors); $this->assertFalse($dir); $this->assertArrayHasKey('coursetorestorefromdoesnotexist', $errors); // Trying again without caching. $CFG->keeptempdirectoriesonbackup is required for caching. $CFG->keeptempdirectoriesonbackup = false; // Checking restore dir. $dir = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null); $dir2 = tool_uploadcourse_helper::get_restore_content_dir($c1backupfile, null); $this->assertNotEquals($dir, $dir2); // Checking with a shortname. $dir = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname); $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, $c1->shortname); $this->assertNotEquals($dir, $dir2); // Get a course that does not exist. $errors = array(); $dir = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors); $this->assertFalse($dir); $this->assertArrayHasKey('coursetorestorefromdoesnotexist', $errors); $dir2 = tool_uploadcourse_helper::get_restore_content_dir(null, 'DoesNotExist', $errors); $this->assertEquals($dir, $dir2); $CFG->keeptempdirectoriesonbackup = $oldcfg; }
$backup->save_controller(); } if ($backup->get_stage() !== backup_ui::STAGE_COMPLETE) { $renderer = $PAGE->get_renderer('core', 'backup'); echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('publishcourseon', 'hub', !empty($hubname) ? $hubname : $huburl), 3, 'main'); if ($backup->enforce_changed_dependencies()) { echo $renderer->dependency_notification(get_string('dependenciesenforced', 'backup')); } echo $renderer->progress_bar($backup->get_progress_bar()); echo $backup->display(); echo $OUTPUT->footer(); die; } //$backupfile = $backup->get_stage_results(); $backupfile = $bc->get_results(); $backupfile = $backupfile['backup_destination']; //END backup processing //retrieve the token to call the hub $registrationmanager = new registration_manager(); $registeredhub = $registrationmanager->get_registeredhub($huburl); //display the sending file page echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('sendingcourse', 'hub'), 3, 'main'); $renderer = $PAGE->get_renderer('core', 'publish'); echo $renderer->sendingbackupinfo($backupfile); if (ob_get_level()) { ob_flush(); } flush(); //send backup file to the hub
public function execute() { global $USER; $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->cm_id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id); $bc->execute_plan(); $results = $bc->get_results(); $file = $results["backup_destination"]; $this->contextid = $file->get_contextid(); $this->fileid = $file->get_id(); $bc->destroy(); // $this->debug->trace(); // $this->prepareDir(); // $this->createXml(); // $temp_dir = $this->getTempDir().'/'; // moodle.xml 内を検索し、本体未対応や取りこぼしたリンクを // 独自形式に書き換え、バックアップ一時ディレクトリへコピー // $xml = file_get_contents($temp_dir.'moodle.xml'); // $xml = $this->encodeLinksAndBackupTargets($xml); // file_put_contents($temp_dir.'moodle.xml', $xml); // ユーザ指定のデータおよびファイルをZIPに追加 // foreach ($this->zip_userdata as $filename => $data) { // file_put_contents($temp_dir.$filename, $data); // } // foreach ($this->zip_userfile as $filename => $path) { // copy($path, $temp_dir.$filename); // } // $this->createZip(); // $this->cleanupDir(); // if (defined('BACKUP_SILENTLY')) { // if (empty($this->message_output)) { // if (is_callable('header_remove', FALSE, $header_remove)) { // 可能であれば出力済みのHTTPヘッダを除去し、 // 直接リダイレクトできる状態にする (PHP 5.3.x) // $header_remove(); // } // (進捗以外の)メッセージが出力されていなければ、成功フラグを立てる // $this->execute_succeeded = TRUE; // } // } else { // 非サイレントモードでは成功かどうかの判定が困難なのでHTTPヘッダで簡易判定する // if (!headers_sent()) { // $this->execute_succeeded = TRUE; // } // } $this->execute_succeeded = TRUE; }
/** * Duplicate a course * * @param int $courseid * @param string $fullname Duplicated course fullname * @param int $newcourse Destination course * @param array $options List of backup options * @return stdClass New course info */ function local_ltiprovider_duplicate_course($courseid, $newcourse, $visible = 1, $options = array(), $useridcreating = null, $context) { global $CFG, $USER, $DB; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; if (empty($USER)) { // Emulate session. cron_setup_user(); } // Context validation. if (!($course = $DB->get_record('course', array('id' => $courseid)))) { throw new moodle_exception('invalidcourseid', 'error'); } $removeoptions = array(); $removeoptions['keep_roles_and_enrolments'] = true; $removeoptions['keep_groups_and_groupings'] = true; remove_course_contents($newcourse->id, false, $removeoptions); $backupdefaults = array('activities' => 1, 'blocks' => 1, 'filters' => 1, 'users' => 0, 'role_assignments' => 0, 'comments' => 0, 'userscompletion' => 0, 'logs' => 0, 'grade_histories' => 0); $backupsettings = array(); // Check for backup and restore options. if (!empty($options)) { foreach ($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; } } // Backup the course. $admin = get_admin(); $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $admin->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); } $rc = new restore_controller($backupid, $newcourse->id, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $admin->id, backup::TARGET_CURRENT_DELETING); 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' => $newcourse->id), '*', MUST_EXIST); $course->visible = $visible; $course->fullname = $newcourse->fullname; $course->shortname = $newcourse->shortname; $course->idnumber = $newcourse->idnumber; // 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(); // We have to unenroll all the user except the one that create the course. if (get_config('local_ltiprovider', 'duplicatecourseswithoutusers') and $useridcreating) { require_once $CFG->dirroot . '/group/lib.php'; // Previous to unenrol users, we assign some type of activities to the user that created the course. if ($user = $DB->get_record('user', array('id' => $useridcreating))) { if ($databases = $DB->get_records('data', array('course' => $course->id))) { foreach ($databases as $data) { $DB->execute("UPDATE {data_records} SET userid = ? WHERE dataid = ?", array($user->id, $data->id)); } } if ($glossaries = $DB->get_records('glossary', array('course' => $course->id))) { foreach ($glossaries as $glossary) { $DB->execute("UPDATE {glossary_entries} SET userid = ? WHERE glossaryid = ?", array($user->id, $glossary->id)); } } // Same for questions. $newcoursecontextid = context_course::instance($course->id); if ($qcategories = $DB->get_records('question_categories', array('contextid' => $newcoursecontextid->id))) { foreach ($qcategories as $qcategory) { $DB->execute("UPDATE {question} SET createdby = ?, modifiedby = ? WHERE category = ?", array($user->id, $user->id, $qcategory->id)); } } // Enrol the user. if ($tool = $DB->get_record('local_ltiprovider', array('contextid' => $newcoursecontextid->id))) { $roles = explode(',', strtolower($context->info['roles'])); local_ltiprovider_enrol_user($tool, $user, $roles, true); } // Now, we unenrol all the users except the one who created the course. $plugins = enrol_get_plugins(true); $instances = enrol_get_instances($course->id, true); foreach ($instances as $key => $instance) { if (!isset($plugins[$instance->enrol])) { unset($instances[$key]); continue; } } $sql = "SELECT ue.*\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)\n JOIN {context} c ON (c.contextlevel = :courselevel AND c.instanceid = e.courseid)"; $params = array('courseid' => $course->id, 'courselevel' => CONTEXT_COURSE); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if ($ue->userid == $user->id) { continue; } if (!isset($instances[$ue->enrolid])) { continue; } $instance = $instances[$ue->enrolid]; $plugin = $plugins[$instance->enrol]; if (!$plugin->allow_unenrol($instance) and !$plugin->allow_unenrol_user($instance, $ue)) { continue; } $plugin->unenrol_user($instance, $ue->userid); } $rs->close(); groups_delete_group_members($course->id); groups_delete_groups($course->id, false); groups_delete_groupings_groups($course->id, false); groups_delete_groupings($course->id, false); } } return $course; }
/** * Store a course in the recycle bin. * * @param \stdClass $course Course * @throws \moodle_exception */ public function store_item($course) { global $CFG, $DB; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; // Backup the course. $user = get_admin(); $controller = new \backup_controller(\backup::TYPE_1COURSE, $course->id, \backup::FORMAT_MOODLE, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $user->id); $controller->execute_plan(); // Grab the result. $result = $controller->get_results(); if (!isset($result['backup_destination'])) { throw new \moodle_exception('Failed to backup activity prior to deletion.'); } // Have finished with the controller, let's destroy it, freeing mem and resources. $controller->destroy(); // Grab the filename. $file = $result['backup_destination']; if (!$file->get_contenthash()) { throw new \moodle_exception('Failed to backup activity prior to deletion (invalid file).'); } // Record the activity, get an ID. $item = new \stdClass(); $item->categoryid = $course->category; $item->shortname = $course->shortname; $item->fullname = $course->fullname; $item->timecreated = time(); $binid = $DB->insert_record('tool_recyclebin_category', $item); // Create the location we want to copy this file to. $filerecord = array('contextid' => \context_coursecat::instance($course->category)->id, 'component' => 'tool_recyclebin', 'filearea' => TOOL_RECYCLEBIN_COURSECAT_BIN_FILEAREA, 'itemid' => $binid, 'timemodified' => time()); // Move the file to our own special little place. $fs = get_file_storage(); if (!$fs->create_file_from_storedfile($filerecord, $file)) { // Failed, cleanup first. $DB->delete_records('tool_recyclebin_category', array('id' => $binid)); throw new \moodle_exception("Failed to copy backup file to recyclebin."); } // Delete the old file. $file->delete(); // Fire event. $event = \tool_recyclebin\event\category_bin_item_created::create(array('objectid' => $binid, 'context' => \context_coursecat::instance($course->category))); $event->trigger(); }
} } cli_heading('Performing backup...'); $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_YES, backup::MODE_GENERAL, $admin->id); // Set the default filename. $format = $bc->get_format(); $type = $bc->get_type(); $id = $bc->get_id(); $users = $bc->get_plan()->get_setting('users')->get_value(); $anonymised = $bc->get_plan()->get_setting('anonymize')->get_value(); $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised); $bc->get_plan()->get_setting('filename')->set_value($filename); // Execution. $bc->finish_ui(); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; // May be empty if file already moved to target location. // Do we need to store backup somewhere else? if (!empty($dir)) { if ($file) { mtrace("Writing " . $dir . '/' . $filename); if ($file->copy_content_to($dir . '/' . $filename)) { $file->delete(); mtrace("Backup completed."); } else { mtrace("Destination directory does not exist or is not writable. Leaving the backup in the course backup file area."); } } } else { mtrace("Backup completed, the new file is listed in the backup area of the given course");
/** * Return an array of owners and a list of each course they are teachers of. * * @param object $obj course obj * @param string $folder name of folder (optional) * @return bool of courses that match the search */ protected function archivecourse($obj, $folder = false) { global $CFG; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/controller/backup_controller.class.php'; if (empty($CFG->siteadmins)) { // Should not happen on an ordinary site. return false; } else { $admin = get_admin(); } $coursetobackup = $obj["course"]->id; // Set this to one existing choice cmid in your dev site. $userdoingthebackup = $admin->id; // Set this to the id of your admin account. try { // Prepare path. $matchers = array('/\\s/', '/\\//'); $safeshort = preg_replace($matchers, '-', $obj["course"]->shortname); if (empty($obj["course"]->idnumber)) { $suffix = '-ID-' . $obj["course"]->id; } else { $suffix = '-ID-' . $obj["course"]->id . '-IDNUM-' . $obj["course"]->idnumber; } $archivefile = date("Y-m-d") . "{$suffix}-{$safeshort}.zip"; $archivepath = str_replace(str_split('\\/:*?"<>|'), '', get_config('tool_coursearchiver', 'coursearchiverpath')); // Check for custom folder. if (!empty($folder)) { $folder = str_replace(str_split('\\/:*?"<>|'), '', $folder); } // If no custom folder is given, use the current year. if (empty($folder)) { $folder = date('Y'); } // Final full path of file. $path = $CFG->dataroot . '/' . $archivepath . '/' . $folder; // If the path doesn't exist, make it so! if (!is_dir($path)) { umask(00); // Create the directory for CourseArchival. if (!mkdir($path, $CFG->directorypermissions, true)) { throw new Exception('Archive path could not be created'); } @chmod($path, $dirpermissions); } // Perform Backup. $bc = new backup_controller(backup::TYPE_1COURSE, $coursetobackup, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userdoingthebackup); $bc->execute_plan(); // Execute backup. $results = $bc->get_results(); // Get the file information needed. $config = get_config('backup'); $dir = $config->backup_auto_destination; // The backup file will have already been moved, so I have to find it. if (!empty($dir)) { // Calculate backup filename regex, ignoring the date/time/info parts that can be // variable, depending of languages, formats and automated backup settings. $filename = backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' . $obj["course"]->id . '-'; $regex = '#' . preg_quote($filename, '#') . '.*\\.mbz#'; // Store all the matching files into filename => timemodified array. $files = array(); foreach (scandir($dir) as $file) { // Skip files not matching the naming convention. if (!preg_match($regex, $file, $matches)) { continue; } // Read the information contained in the backup itself. try { $bcinfo = backup_general_helper::get_backup_information_from_mbz($dir . '/' . $file); } catch (backup_helper_exception $e) { throw new Exception('Error: ' . $file . ' does not appear to be a valid backup (' . $e->errorcode . ')'); continue; } // Make sure this backup concerns the course and site we are looking for. if ($bcinfo->format === backup::FORMAT_MOODLE && $bcinfo->type === backup::TYPE_1COURSE && $bcinfo->original_course_id == $obj["course"]->id && backup_general_helper::backup_is_samesite($bcinfo)) { $files[$file] = $bcinfo->backup_date; } } // Sort by values descending (newer to older filemodified). arsort($files); foreach ($files as $filename => $backupdate) { // Make sure the backup is from today. if (date('m/d/Y', $backupdate) == date('m/d/Y')) { rename($dir . '/' . $filename, $path . '/' . $archivefile); } break; // Just the last backup...thanks! } } else { $file = $results['backup_destination']; if (!empty($file)) { $file->copy_content_to($path . '/' . $archivefile); } else { throw new Exception('Backup failed'); } } $bc->destroy(); unset($bc); if (file_exists($path . '/' . $archivefile)) { // Make sure file got moved. // Remove Course. delete_course($obj["course"]->id, false); } else { throw new Exception('Course archive file does not exist'); } } catch (Exception $e) { return false; } return true; }
/** * Store a course module in the recycle bin. * * @param \stdClass $cm Course module * @throws \moodle_exception */ public function store_item($cm) { global $CFG, $DB; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; // Get more information. $modinfo = get_fast_modinfo($cm->course); if (!isset($modinfo->cms[$cm->id])) { return; // Can't continue without the module information. } $cminfo = $modinfo->cms[$cm->id]; // Check backup/restore support. if (!plugin_supports('mod', $cminfo->modname, FEATURE_BACKUP_MOODLE2)) { return; } // Backup the activity. $user = get_admin(); $controller = new \backup_controller(\backup::TYPE_1ACTIVITY, $cm->id, \backup::FORMAT_MOODLE, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $user->id); $controller->execute_plan(); // Grab the result. $result = $controller->get_results(); if (!isset($result['backup_destination'])) { throw new \moodle_exception('Failed to backup activity prior to deletion.'); } // Have finished with the controller, let's destroy it, freeing mem and resources. $controller->destroy(); // Grab the filename. $file = $result['backup_destination']; if (!$file->get_contenthash()) { throw new \moodle_exception('Failed to backup activity prior to deletion (invalid file).'); } // Record the activity, get an ID. $activity = new \stdClass(); $activity->courseid = $cm->course; $activity->section = $cm->section; $activity->module = $cm->module; $activity->name = $cminfo->name; $activity->timecreated = time(); $binid = $DB->insert_record('tool_recyclebin_course', $activity); // Create the location we want to copy this file to. $filerecord = array('contextid' => \context_course::instance($this->_courseid)->id, 'component' => 'tool_recyclebin', 'filearea' => TOOL_RECYCLEBIN_COURSE_BIN_FILEAREA, 'itemid' => $binid, 'timemodified' => time()); // Move the file to our own special little place. $fs = get_file_storage(); if (!$fs->create_file_from_storedfile($filerecord, $file)) { // Failed, cleanup first. $DB->delete_records('tool_recyclebin_course', array('id' => $binid)); throw new \moodle_exception("Failed to copy backup file to recyclebin."); } // Delete the old file. $file->delete(); // Fire event. $event = \tool_recyclebin\event\course_bin_item_created::create(array('objectid' => $binid, 'context' => \context_course::instance($cm->course))); $event->trigger(); }
/** * */ public function create_preset_from_backup($userdata) { global $CFG, $USER, $SESSION; require_once "{$CFG->dirroot}/backup/util/includes/backup_includes.php"; $df = mod_dataform_dataform::instance($this->_dataformid); $users = 0; $anon = 0; switch ($userdata) { case 'dataanon': $anon = 1; case 'data': $users = 1; } // Store preset settings in $SESSION. $SESSION->{"dataform_{$df->cm->id}_preset"} = "{$users} {$anon}"; $bc = new backup_controller(backup::TYPE_1ACTIVITY, $df->cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id); // Clear preset settings from $SESSION. unset($SESSION->{"dataform_{$df->cm->id}_preset"}); // Set users and anon in plan. $bc->get_plan()->get_setting('users')->set_value($users); $bc->get_plan()->get_setting('anonymize')->set_value($anon); $bc->set_status(backup::STATUS_AWAITING); $bc->execute_plan(); $results = $bc->get_results(); $bc->destroy(); unset($bc); if ($file = $results['backup_destination']) { $fs = get_file_storage(); $coursecontext = context_course::instance($df->course->id); $presetname = clean_filename(str_replace(' ', '_', $df->name) . '-dataform-preset-' . gmdate("Ymd_Hi") . '-' . str_replace(' ', '-', get_string("preset{$userdata}", 'dataform')) . '.mbz'); $preset = new \stdClass(); $preset->contextid = $coursecontext->id; $preset->component = 'mod_dataform'; $preset->filearea = self::PRESET_COURSEAREA; $preset->filepath = '/'; $preset->filename = $presetname; $fs->create_file_from_storedfile($preset, $file); $file->delete(); return true; } return false; }
/** * 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(); }
/** * Launches a automated backup routine for the given course * * @param stdClass $course * @param int $starttime * @param int $userid * @return bool */ public static function launch_automated_backup($course, $starttime, $userid) { $outcome = self::BACKUP_STATUS_OK; $config = get_config('backup'); $dir = $config->backup_auto_destination; $storage = (int) $config->backup_auto_storage; $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid); try { $settings = array('users' => 'backup_auto_users', 'role_assignments' => 'backup_auto_role_assignments', 'activities' => 'backup_auto_activities', 'blocks' => 'backup_auto_blocks', 'filters' => 'backup_auto_filters', 'comments' => 'backup_auto_comments', 'badges' => 'backup_auto_badges', 'completion_information' => 'backup_auto_userscompletion', 'logs' => 'backup_auto_logs', 'histories' => 'backup_auto_histories', 'questionbank' => 'backup_auto_questionbank'); foreach ($settings as $setting => $configsetting) { if ($bc->get_plan()->setting_exists($setting)) { if (isset($config->{$configsetting})) { $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting}); } } } // Set the default filename. $format = $bc->get_format(); $type = $bc->get_type(); $id = $bc->get_id(); $users = $bc->get_plan()->get_setting('users')->get_value(); $anonymised = $bc->get_plan()->get_setting('anonymize')->get_value(); $bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised)); $bc->set_status(backup::STATUS_AWAITING); $bc->execute_plan(); $results = $bc->get_results(); $outcome = self::outcome_from_results($results); $file = $results['backup_destination']; // May be empty if file already moved to target location. if (!file_exists($dir) || !is_dir($dir) || !is_writable($dir)) { $dir = null; } // Copy file only if there was no error. if ($file && !empty($dir) && $storage !== 0 && $outcome != self::BACKUP_STATUS_ERROR) { $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname); if (!$file->copy_content_to($dir . '/' . $filename)) { $outcome = self::BACKUP_STATUS_ERROR; } if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) { $file->delete(); } } } catch (moodle_exception $e) { $bc->log('backup_auto_failed_on_course', backup::LOG_ERROR, $course->shortname); // Log error header. $bc->log('Exception: ' . $e->errorcode, backup::LOG_ERROR, $e->a, 1); // Log original exception problem. $bc->log('Debug: ' . $e->debuginfo, backup::LOG_DEBUG, null, 1); // Log original debug information. $outcome = self::BACKUP_STATUS_ERROR; } // Delete the backup file immediately if something went wrong. if ($outcome === self::BACKUP_STATUS_ERROR) { // Delete the file from file area if exists. if (!empty($file)) { $file->delete(); } // Delete file from external storage if exists. if ($storage !== 0 && !empty($filename) && file_exists($dir . '/' . $filename)) { @unlink($dir . '/' . $filename); } } $bc->destroy(); unset($bc); return $outcome; }
/** * Launches a automated backup routine for the given course * * @param stdClass $course * @param int $starttime * @param int $userid * @return bool */ public static function launch_automated_backup($course, $starttime, $userid) { $outcome = self::BACKUP_STATUS_OK; $config = get_config('backup'); $dir = $config->backup_auto_destination; $storage = (int) $config->backup_auto_storage; $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_AUTOMATED, $userid); try { // Set the default filename. $format = $bc->get_format(); $type = $bc->get_type(); $id = $bc->get_id(); $users = $bc->get_plan()->get_setting('users')->get_value(); $anonymised = $bc->get_plan()->get_setting('anonymize')->get_value(); $bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised)); $bc->set_status(backup::STATUS_AWAITING); $bc->execute_plan(); $results = $bc->get_results(); $outcome = self::outcome_from_results($results); $file = $results['backup_destination']; // May be empty if file already moved to target location. if (empty($dir) && $storage !== 0) { // This is intentionally left as a warning instead of an error because of the current behaviour of backup settings. // See MDL-48266 for details. $bc->log('No directory specified for automated backups', backup::LOG_WARNING); $outcome = self::BACKUP_STATUS_WARNING; } else { if ($storage !== 0 && (!file_exists($dir) || !is_dir($dir) || !is_writable($dir))) { // If we need to copy the backup file to an external dir and it is not writable, change status to error. $bc->log('Specified backup directory is not writable - ', backup::LOG_ERROR, $dir); $dir = null; $outcome = self::BACKUP_STATUS_ERROR; } } // Copy file only if there was no error. if ($file && !empty($dir) && $storage !== 0 && $outcome != self::BACKUP_STATUS_ERROR) { $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname); if (!$file->copy_content_to($dir . '/' . $filename)) { $bc->log('Attempt to copy backup file to the specified directory failed - ', backup::LOG_ERROR, $dir); $outcome = self::BACKUP_STATUS_ERROR; } if ($outcome != self::BACKUP_STATUS_ERROR && $storage === 1) { if (!$file->delete()) { $outcome = self::BACKUP_STATUS_WARNING; $bc->log('Attempt to delete the backup file from course automated backup area failed - ', backup::LOG_WARNING, $file->get_filename()); } } } } catch (moodle_exception $e) { $bc->log('backup_auto_failed_on_course', backup::LOG_ERROR, $course->shortname); // Log error header. $bc->log('Exception: ' . $e->errorcode, backup::LOG_ERROR, $e->a, 1); // Log original exception problem. $bc->log('Debug: ' . $e->debuginfo, backup::LOG_DEBUG, null, 1); // Log original debug information. $outcome = self::BACKUP_STATUS_ERROR; } // Delete the backup file immediately if something went wrong. if ($outcome === self::BACKUP_STATUS_ERROR) { // Delete the file from file area if exists. if (!empty($file)) { $file->delete(); } // Delete file from external storage if exists. if ($storage !== 0 && !empty($filename) && file_exists($dir . '/' . $filename)) { @unlink($dir . '/' . $filename); } } $bc->destroy(); unset($bc); return $outcome; }
public function test_backup_restore() { // TODO this test does not check if userids are correctly mapped global $CFG, $DB; core_php_time_limit::raise(); // Set to admin user. $this->setAdminUser(); $gen_mod = new mod_ratingallocate_generated_module($this); $course1 = $gen_mod->course; // Create backup file and save it to the backup location. $bc = new backup_controller(backup::TYPE_1ACTIVITY, $gen_mod->mod_db->cmid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, 2); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; //TODO: Necessary to ensure backward compatibility if (tgz_packer::is_tgz_file($file)) { $fp = get_file_packer('application/x-gzip'); } else { $fp = get_file_packer(); } $filepath = $CFG->dataroot . '/temp/backup/test-restore-course'; $file->extract_to_pathname($fp, $filepath); $bc->destroy(); unset($bc); // Create a course that we are going to restore the other course to. $course2 = $this->getDataGenerator()->create_course(); // 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(); $unset_values = function ($elem1, $elem2, $varname) { $this->assertNotEquals($elem1->{$varname}, $elem2->{$varname}); $result = array($elem1->{$varname}, $elem2->{$varname}); unset($elem1->{$varname}); unset($elem2->{$varname}); return $result; }; $ratingallocate1 = $DB->get_record(this_db\ratingallocate::TABLE, array(this_db\ratingallocate::COURSE => $course1->id)); $ratingallocate2 = $DB->get_record(this_db\ratingallocate::TABLE, array(this_db\ratingallocate::COURSE => $course2->id)); list($rating_id1, $rating_id2) = $unset_values($ratingallocate1, $ratingallocate2, this_db\ratingallocate::ID); $unset_values($ratingallocate1, $ratingallocate2, this_db\ratingallocate::COURSE); $this->assertEquals($ratingallocate1, $ratingallocate2); $choices1 = $DB->get_records(this_db\ratingallocate_choices::TABLE, array(this_db\ratingallocate_choices::RATINGALLOCATEID => $rating_id1), this_db\ratingallocate_choices::TITLE); $choices2 = $DB->get_records(this_db\ratingallocate_choices::TABLE, array(this_db\ratingallocate_choices::RATINGALLOCATEID => $rating_id2), this_db\ratingallocate_choices::TITLE); $this->assertCount(2, $choices1); $this->assertCount(2, array_values($choices2)); $choice2_copy = $choices2; foreach ($choices1 as $choice1) { //work with copies $choice2 = json_decode(json_encode(array_shift($choice2_copy))); $choice1 = json_decode(json_encode($choice1)); list($choiceid1, $choiceid2) = $unset_values($choice1, $choice2, this_db\ratingallocate_choices::ID); $unset_values($choice1, $choice2, this_db\ratingallocate_choices::RATINGALLOCATEID); $this->assertEquals($choice1, $choice2); // compare ratings for this choice $ratings1 = array_values($DB->get_records(this_db\ratingallocate_ratings::TABLE, array(this_db\ratingallocate_ratings::CHOICEID => $choiceid1), this_db\ratingallocate_ratings::USERID)); $ratings2 = array_values($DB->get_records(this_db\ratingallocate_ratings::TABLE, array(this_db\ratingallocate_ratings::CHOICEID => $choiceid2), this_db\ratingallocate_ratings::USERID)); $this->assertEquals(count($ratings1), count($ratings2)); $ratings2_copy = $ratings2; foreach ($ratings1 as $rating1) { $rating2 = json_decode(json_encode(array_shift($ratings2_copy))); $rating1 = json_decode(json_encode($rating1)); $unset_values($rating1, $rating2, this_db\ratingallocate_ratings::CHOICEID); $unset_values($rating1, $rating2, this_db\ratingallocate_ratings::ID); $this->assertEquals($rating1, $rating2); } } // compare allocations $allocations1 = $DB->get_records(this_db\ratingallocate_allocations::TABLE, array(this_db\ratingallocate_allocations::RATINGALLOCATEID => $rating_id1), this_db\ratingallocate_allocations::USERID); $allocations2 = $DB->get_records(this_db\ratingallocate_allocations::TABLE, array(this_db\ratingallocate_allocations::RATINGALLOCATEID => $rating_id2), this_db\ratingallocate_allocations::USERID); // number of allocations is equal //$this->assertCount(count($allocations1), $allocations2); $this->assertCount(count($gen_mod->allocations), $allocations2); // create function that can be used to replace $map_allocation_to_choice_title = function (&$alloc, $choices) { $alloc->{'choice_title'} = $choices[$alloc->{this_db\ratingallocate_allocations::CHOICEID}]->{this_db\ratingallocate_choices::TITLE}; }; // compare allocations in detail! $alloc2 = reset($allocations2); foreach ($allocations1 as &$alloc1) { $map_allocation_to_choice_title($alloc1, $choices1); $map_allocation_to_choice_title($alloc2, $choices2); $unset_values($alloc1, $alloc2, this_db\ratingallocate_allocations::RATINGALLOCATEID); $unset_values($alloc1, $alloc2, this_db\ratingallocate_allocations::CHOICEID); $unset_values($alloc1, $alloc2, this_db\ratingallocate_allocations::ID); $alloc2 = next($allocations2); } $this->assertEquals(array_values($allocations1), array_values($allocations2)); }