/** * 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'; }
/** * test backup_plan class */ function test_backup_plan() { // We need one (non interactive) controller for instantiating plan $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid); // Instantiate one backup plan $bp = new backup_plan($bc); $this->assertTrue($bp instanceof backup_plan); $this->assertEquals($bp->get_name(), 'backup_plan'); // Calculate checksum and check it $checksum = $bp->calculate_checksum(); $this->assertTrue($bp->is_checksum_correct($checksum)); $bc->destroy(); }
/** * 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(); } }
/** * Gets the value for the requested setting * * @param string $name * @return mixed */ public function get_setting_value($name, $default = false) { try { return $this->controller->get_plan()->get_setting($name)->get_value(); } catch (Exception $e) { debugging('Failed to find the setting: ' . $name, DEBUG_DEVELOPER); return $default; } }
/** * 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; }
public function save_controller($includeobj = true, $cleanobj = false) { parent::save_controller($includeobj, $cleanobj); }
/** * 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; }
/** * Change standard behavior so the checksum is also stored and not onlt calculated */ public function calculate_checksum() { $this->checksum = parent::calculate_checksum(); return $this->checksum; }
$search = new import_course_search(array('url' => $url)); // show the course selector echo $OUTPUT->header(); echo $renderer->import_course_selector($url, $search); echo $OUTPUT->footer(); die; } // Load the course +context to import from $importcourse = $DB->get_record('course', array('id' => $importcourseid), '*', MUST_EXIST); $importcontext = context_course::instance($importcourseid); // Make sure the user can backup from that course require_capability('moodle/backup:backuptargetimport', $importcontext); // Attempt to load the existing backup controller (backupid will be false if there isn't one) $backupid = optional_param('backup', false, PARAM_ALPHANUM); if (!($bc = backup_ui::load_controller($backupid))) { $bc = new backup_controller(backup::TYPE_1COURSE, $importcourse->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_YES, backup::MODE_IMPORT, $USER->id); $bc->get_plan()->get_setting('users')->set_status(backup_setting::LOCKED_BY_CONFIG); $settings = $bc->get_plan()->get_settings(); // For the initial stage we want to hide all locked settings and if there are // no visible settings move to the next stage $visiblesettings = false; foreach ($settings as $setting) { if ($setting->get_status() !== backup_setting::NOT_LOCKED) { $setting->set_visibility(backup_setting::HIDDEN); } else { $visiblesettings = true; } } import_ui::skip_current_stage(!$visiblesettings); } // Prepare the import UI
public function save_controller() { parent::save_controller(); }
<?php define('CLI_SCRIPT', 1); chdir('/var/www/ae.ket.org'); require_once 'config.php'; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; $course_module_to_backup = 1341; // Set this to one existing choice cmid in your dev site $user_doing_the_backup = 4; // Set this to the id of your admin accouun $bc = new backup_controller(backup::TYPE_1ACTIVITY, $course_module_to_backup, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $user_doing_the_backup); $bc->execute_plan();
/** * Given one backupid and the (FS) final generated file, perform its final storage * into Moodle file storage. For stored files it returns the complete file_info object * * Note: the $filepath is deleted if the backup file is created successfully * * If you specify the progress monitor, this will start a new progress section * to track progress in processing (in case this task takes a long time). * * @param int $backupid * @param string $filepath zip file containing the backup * @param \core\progress\base $progress Optional progress monitor * @return stored_file if created, null otherwise * * @throws moodle_exception in case of any problems */ public static function store_backup_file($backupid, $filepath, \core\progress\base $progress = null) { global $CFG; // First of all, get some information from the backup_controller to help us decide list($dinfo, $cinfo, $sinfo) = backup_controller_dbops::get_moodle_backup_information($backupid, $progress); // Extract useful information to decide $hasusers = (bool) $sinfo['users']->value; // Backup has users $isannon = (bool) $sinfo['anonymize']->value; // Backup is anonymised $filename = $sinfo['filename']->value; // Backup filename $backupmode = $dinfo[0]->mode; // Backup mode backup::MODE_GENERAL/IMPORT/HUB $backuptype = $dinfo[0]->type; // Backup type backup::TYPE_1ACTIVITY/SECTION/COURSE $userid = $dinfo[0]->userid; // User->id executing the backup $id = $dinfo[0]->id; // Id of activity/section/course (depends of type) $courseid = $dinfo[0]->courseid; // Id of the course $format = $dinfo[0]->format; // Type of backup file // Quick hack. If for any reason, filename is blank, fix it here. // TODO: This hack will be out once MDL-22142 - P26 gets fixed if (empty($filename)) { $filename = backup_plan_dbops::get_default_backup_filename('moodle2', $backuptype, $id, $hasusers, $isannon); } // Backups of type IMPORT aren't stored ever if ($backupmode == backup::MODE_IMPORT) { return null; } if (!is_readable($filepath)) { // we have a problem if zip file does not exist throw new coding_exception('backup_helper::store_backup_file() expects valid $filepath parameter'); } // Calculate file storage options of id being backup $ctxid = 0; $filearea = ''; $component = ''; $itemid = 0; switch ($backuptype) { case backup::TYPE_1ACTIVITY: $ctxid = context_module::instance($id)->id; $component = 'backup'; $filearea = 'activity'; $itemid = 0; break; case backup::TYPE_1SECTION: $ctxid = context_course::instance($courseid)->id; $component = 'backup'; $filearea = 'section'; $itemid = $id; break; case backup::TYPE_1COURSE: $ctxid = context_course::instance($courseid)->id; $component = 'backup'; $filearea = 'course'; $itemid = 0; break; } if ($backupmode == backup::MODE_AUTOMATED) { // Automated backups have there own special area! $filearea = 'automated'; // If we're keeping the backup only in a chosen path, just move it there now // this saves copying from filepool to here later and filling trashdir. $config = get_config('backup'); $dir = $config->backup_auto_destination; if ($config->backup_auto_storage == 1 and $dir and is_dir($dir) and is_writable($dir)) { $filedest = $dir . '/' . backup_plan_dbops::get_default_backup_filename($format, $backuptype, $courseid, $hasusers, $isannon, !$config->backup_shortname); // first try to move the file, if it is not possible copy and delete instead if (@rename($filepath, $filedest)) { return null; } umask($CFG->umaskpermissions); if (copy($filepath, $filedest)) { @chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot unlink($filepath); return null; } else { $bc = backup_controller::load_controller($backupid); $bc->log('Attempt to copy backup file to the specified directory using filesystem failed - ', backup::LOG_WARNING, $dir); $bc->destroy(); } // bad luck, try to deal with the file the old way - keep backup in file area if we can not copy to ext system } } // Backups of type HUB (by definition never have user info) // are sent to user's "user_tohub" file area. The upload process // will be responsible for cleaning that filearea once finished if ($backupmode == backup::MODE_HUB) { $ctxid = context_user::instance($userid)->id; $component = 'user'; $filearea = 'tohub'; $itemid = 0; } // Backups without user info or with the anonymise functionality // enabled are sent to user's "user_backup" // file area. Maintenance of such area is responsibility of // the user via corresponding file manager frontend if ($backupmode == backup::MODE_GENERAL && (!$hasusers || $isannon)) { $ctxid = context_user::instance($userid)->id; $component = 'user'; $filearea = 'backup'; $itemid = 0; } // Let's send the file to file storage, everything already defined $fs = get_file_storage(); $fr = array('contextid' => $ctxid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'filepath' => '/', 'filename' => $filename, 'userid' => $userid, 'timecreated' => time(), 'timemodified' => time()); // If file already exists, delete if before // creating it again. This is BC behaviour - copy() // overwrites by default if ($fs->file_exists($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename'])) { $pathnamehash = $fs->get_pathname_hash($fr['contextid'], $fr['component'], $fr['filearea'], $fr['itemid'], $fr['filepath'], $fr['filename']); $sf = $fs->get_file_by_hash($pathnamehash); $sf->delete(); } $file = $fs->create_file_from_pathname($fr, $filepath); unlink($filepath); return $file; }
/** * Sets the controller settings default values from the admin config. * * @param backup_controller $controller * @param array $settings a map from admin config names to setting names (Config name => Setting name) * @param boolean $uselocks whether "locked" admin settings should be honoured */ private static function apply_admin_config_defaults(backup_controller $controller, array $settings, $uselocks) { $plan = $controller->get_plan(); foreach ($settings as $config => $settingname) { $value = get_config('backup', $config); if ($value === false) { // Ignore this because the config has not been set. get_config // returns false if a setting doesn't exist, '0' is returned when // the configuration is set to false. $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG); continue; } $locked = $uselocks && get_config('backup', $config . '_locked') == true; if ($plan->setting_exists($settingname)) { $setting = $plan->get_setting($settingname); if ($setting->get_value() != $value || 1 == 1) { $setting->set_value($value); if ($locked) { $setting->set_status(base_setting::LOCKED_BY_CONFIG); } } } else { $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG); } } }
/** * 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; }
/** * 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; }
/** * Duplicates a Moodle module in an existing course * @param int $cmid Course module id * @param int $courseid Course id * @return int New course module id */ function local_ltiprovider_duplicate_module($cmid, $courseid, $newidnumber, $lticontext) { global $CFG, $DB, $USER; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; require_once $CFG->libdir . '/filelib.php'; if (empty($USER)) { // Emulate session. cron_setup_user(); } $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $cm = get_coursemodule_from_id('', $cmid, 0, true, MUST_EXIST); $cmcontext = context_module::instance($cm->id); $context = context_course::instance($course->id); if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) { $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn)); print_error('duplicatenosupport', 'error', $url, $a); } // backup the activity $admin = get_admin(); $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id); $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $bc->destroy(); // restore the backup immediately $rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $admin->id, backup::TARGET_CURRENT_ADDING); if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } print_r($precheckresults); die; } } $rc->execute_plan(); $newcmid = null; $tasks = $rc->get_plan()->get_tasks(); foreach ($tasks as $task) { if (is_subclass_of($task, 'restore_activity_task')) { if ($task->get_old_contextid() == $cmcontext->id) { $newcmid = $task->get_moduleid(); break; } } } $rc->destroy(); if ($module = $DB->get_record('course_modules', array('id' => $newcmid))) { $module->idnumber = $newidnumber; $DB->update_record('course_modules', $module); } $newtoolid = 0; if ($tools = $DB->get_records('local_ltiprovider', array('contextid' => $cmcontext->id))) { $newcmcontext = context_module::instance($newcmid); foreach ($tools as $tool) { $tool->courseid = $course->id; $tool->contextid = $newcmcontext->id; $newtoolid = $DB->insert_record('local_ltiprovider', $tool); } } if (!$newtoolid) { $tool = local_ltiprovider_create_tool($course->id, $newcmcontext->id, $lticontext); } if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } return $newcmid; }
/** * */ public function 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(); $bc->destroy(); $fs = get_file_storage(); if ($users and !$anon) { $contextid = $df->context->id; $files = $fs->get_area_files($contextid, 'backup', 'activity', 0, 'timemodified', false); } else { $usercontext = context_user::instance($USER->id); $contextid = $usercontext->id; $files = $fs->get_area_files($contextid, 'user', 'backup', 0, 'timemodified', false); } if (!empty($files)) { $coursecontext = context_course::instance($df->course->id); foreach ($files as $file) { if ($file->get_contextid() != $contextid) { continue; } $preset = new object(); $preset->contextid = $coursecontext->id; $preset->component = 'mod_dataform'; $preset->filearea = self::PRESET_COURSEAREA; $preset->filepath = '/'; $preset->filename = clean_filename(str_replace(' ', '_', $df->name) . '-dataform-preset-' . gmdate("Ymd_Hi") . '-' . str_replace(' ', '-', get_string("preset{$userdata}", 'dataform')) . '.mbz'); $fs->create_file_from_storedfile($preset, $file); $file->delete(); return true; } } return false; }
/** * 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; }
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; }
require_capability('moodle/backup:backuptargetimport', $context); 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));
/** * 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; }
/** * 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(); }
/** * 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); }
/** * 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; }
/** * 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; }
/** * Sets the controller settings default values from the backup config. * * @param backup_controller $controller */ private static function apply_general_config_defaults(backup_controller $controller) { $settings = array('backup_general_users' => 'users', 'backup_general_anonymize' => 'anonymize', 'backup_general_role_assignments' => 'role_assignments', 'backup_general_activities' => 'activities', 'backup_general_blocks' => 'blocks', 'backup_general_filters' => 'filters', 'backup_general_comments' => 'comments', 'backup_general_badges' => 'badges', 'backup_general_userscompletion' => 'userscompletion', 'backup_general_logs' => 'logs', 'backup_general_histories' => 'grade_histories', 'backup_general_questionbank' => 'questionbank'); $plan = $controller->get_plan(); foreach ($settings as $config => $settingname) { $value = get_config('backup', $config); if ($value === false) { // Ignore this because the config has not been set. get_config // returns false if a setting doesn't exist, '0' is returned when // the configuration is set to false. $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG); continue; } $locked = get_config('backup', $config . '_locked') == true; if ($plan->setting_exists($settingname)) { $setting = $plan->get_setting($settingname); if ($setting->get_value() != $value || 1 == 1) { $setting->set_value($value); if ($locked) { $setting->set_status(base_setting::LOCKED_BY_CONFIG); } } } else { $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG); } } }
/** * 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; }
/** * Sets the controller settings default values from the backup config. * * @param backup_controller $controller */ private static function apply_general_config_defaults(backup_controller $controller) { $settings = array('backup_general_users' => 'users', 'backup_general_anonymize' => 'anonymize', 'backup_general_role_assignments' => 'role_assignments', 'backup_general_activities' => 'activities', 'backup_general_blocks' => 'blocks', 'backup_general_filters' => 'filters', 'backup_general_comments' => 'comments', 'backup_general_badges' => 'badges', 'backup_general_userscompletion' => 'userscompletion', 'backup_general_logs' => 'logs', 'backup_general_histories' => 'grade_histories'); $plan = $controller->get_plan(); foreach ($settings as $config => $settingname) { $value = get_config('backup', $config); $locked = get_config('backup', $config . '_locked') == true; if ($plan->setting_exists($settingname)) { $setting = $plan->get_setting($settingname); if ($setting->get_value() != $value || 1 == 1) { $setting->set_value($value); if ($locked) { $setting->set_status(base_setting::LOCKED_BY_CONFIG); } } } } }
/** * 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; }
$hubname = optional_param('hubname', '', PARAM_TEXT); //some permissions and parameters checking $course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST); require_login($course); if (!has_capability('moodle/course:publish', get_context_instance(CONTEXT_COURSE, $id)) or !confirm_sesskey()) { throw new moodle_exception('nopermission'); } //page settings $PAGE->set_url('/course/publish/backup.php'); $PAGE->set_pagelayout('course'); $PAGE->set_title(get_string('course') . ': ' . $course->fullname); $PAGE->set_heading($course->fullname); //BEGIN backup processing $backupid = optional_param('backup', false, PARAM_ALPHANUM); if (!($bc = backup_ui::load_controller($backupid))) { $bc = new backup_controller(backup::TYPE_1COURSE, $id, backup::FORMAT_MOODLE, backup::INTERACTIVE_YES, backup::MODE_HUB, $USER->id); } $backup = new backup_ui($bc, array('id' => $id, 'hubcourseid' => $hubcourseid, 'huburl' => $huburl, 'hubname' => $hubname)); $backup->process(); if ($backup->get_stage() == backup_ui::STAGE_FINAL) { $backup->execute(); } else { $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')); }