Exemple #1
0
 /**
  * Destorys the backup controller and the loaded stage.
  */
 public function destroy()
 {
     if ($this->controller) {
         $this->controller->destroy();
     }
     unset($this->stage);
 }
Exemple #2
0
 /**
  * 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';
 }
Exemple #3
0
 /**
  * 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();
 }
 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;
 }
 /**
  * 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();
 }
$PAGE->set_url(new moodle_url('/course/modduplicate.php', array('cmid' => $cm->id, 'courseid' => $course->id)));
$PAGE->set_pagelayout('incourse');
$output = $PAGE->get_renderer('core', 'backup');
$a = new stdClass();
$a->modtype = get_string('modulename', $cm->modname);
$a->modname = format_string($cm->name);
if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
    $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
    print_error('duplicatenosupport', 'error', $url, $a);
}
// backup the activity
$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
$backupid = $bc->get_backupid();
$backupbasepath = $bc->get_plan()->get_basepath();
$bc->execute_plan();
$bc->destroy();
// restore the backup immediately
$rc = new restore_controller($backupid, $courseid, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
if (!$rc->execute_precheck()) {
    $precheckresults = $rc->get_precheck_results();
    if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
        if (empty($CFG->keeptempdirectoriesonbackup)) {
            fulldelete($backupbasepath);
        }
        echo $output->header();
        echo $output->precheck_notices($precheckresults);
        $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
        echo $output->continue_button($url);
        echo $output->footer();
        die;
    }
Exemple #7
0
 /**
  * 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);
 }
Exemple #8
0
 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;
 }
Exemple #9
0
/**
 * 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;
}
Exemple #10
0
 /**
  * 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();
 }
Exemple #11
0
 /**
  * test backup_structure_step class
  */
 function test_backup_structure_step()
 {
     global $CFG;
     $file = $CFG->tempdir . '/test/test_backup_structure_step.txt';
     // Remove the test dir and any content
     @remove_dir(dirname($file));
     // Recreate test dir
     if (!check_dir_exists(dirname($file), true, true)) {
         throw new moodle_exception('error_creating_temp_dir', 'error', dirname($file));
     }
     // We need one (non interactive) controller for instatiating plan
     $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
     // We need one plan
     $bp = new backup_plan($bc);
     // We need one task with mocked basepath
     $bt = new mock_backup_task_basepath('taskname');
     $bp->add_task($bt);
     // Instantiate backup_structure_step (and add it to task)
     $bs = new mock_backup_structure_step('steptest', basename($file), $bt);
     // Execute backup_structure_step
     $bs->execute();
     // Test file has been created
     $this->assertTrue(file_exists($file));
     // Some simple tests with contents
     $contents = file_get_contents($file);
     $this->assertTrue(strpos($contents, '<?xml version="1.0"') !== false);
     $this->assertTrue(strpos($contents, '<test id="1">') !== false);
     $this->assertTrue(strpos($contents, '<field1>value1</field1>') !== false);
     $this->assertTrue(strpos($contents, '<field2>value2</field2>') !== false);
     $this->assertTrue(strpos($contents, '</test>') !== false);
     $bc->destroy();
     unlink($file);
     // delete file
     // Remove the test dir and any content
     @remove_dir(dirname($file));
 }
Exemple #12
0
 function backup_template($courseid_from,$settings,$config,$admin) {
    $bc = new backup_controller(backup::TYPE_1COURSE, $courseid_from, backup::FORMAT_MOODLE,
                                backup::INTERACTIVE_YES, backup::MODE_IMPORT,$admin->id);
    $backupid = $bc->get_backupid();
    $bc->get_plan()->get_setting('users')->set_status(backup_setting::LOCKED_BY_CONFIG);
    
    foreach ($settings as $setting => $configsetting) {
        if ($bc->get_plan()->setting_exists($setting)) {
            $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting});
        }
    }
 
    $bc->finish_ui();
    $bc->execute_plan();
    $bc->destroy();
    unset($bc);
 
    return $backupid;
}
Exemple #13
0
 public function test_mod_jclic_duplicate()
 {
     global $USER, $DB, $CFG;
     require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php';
     require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php';
     $this->resetAfterTest(true);
     $this->setUser(2);
     // Admin user.
     // Create a course with some availability data set.
     $generator = $this->getDataGenerator();
     $course = $generator->create_course(array('format' => 'topics', 'numsections' => 3, 'enablecompletion' => COMPLETION_ENABLED), array('createsections' => true));
     $jclic = $generator->create_module('jclic', array('course' => $course->id));
     $cmid = $jclic->cmid;
     // Do backup.
     $bc = new backup_controller(backup::TYPE_1ACTIVITY, $cmid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
     $backupid = $bc->get_backupid();
     $bc->execute_plan();
     $bc->destroy();
     // Do restore.
     $rc = new restore_controller($backupid, $course->id, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
     $this->assertTrue($rc->execute_precheck());
     $rc->execute_plan();
     // Find cmid.
     $tasks = $rc->get_plan()->get_tasks();
     $cmcontext = context_module::instance($cmid);
     $newcmid = 0;
     foreach ($tasks as $task) {
         if (is_subclass_of($task, 'restore_activity_task')) {
             if ($task->get_old_contextid() == $cmcontext->id) {
                 $newcmid = $task->get_moduleid();
                 break;
             }
         }
     }
     $rc->destroy();
     if (!$newcmid) {
         throw new coding_exception('Unexpected: failure to find restored cmid');
     }
     // Check settings in new course.
     $newjclic = new jclic(context_module::instance($newcmid), false, false);
     $newjclicmodule = $newjclic->get_instance();
     $this->assertNotEmpty($newjclic);
     $jclic = new jclic(context_module::instance($jclic->cmid), false, false);
     $jclicmodule = $jclic->get_instance();
     $fields = (array) $jclicmodule;
     unset($fields['id']);
     unset($fields['course']);
     foreach ($fields as $key => $unused) {
         $this->assertEquals($newjclicmodule->{$key}, $jclicmodule->{$key}, "Failed on field {$key}");
     }
     // Avoid errors...
     ini_set('max_execution_time', 0);
 }
Exemple #14
0
 /**
  * Tests the restore_controller.
  */
 public function test_restore_controller_is_executing()
 {
     global $CFG;
     // Make a backup.
     check_dir_exists($CFG->tempdir . '/backup');
     $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
     $backupid = $bc->get_backupid();
     $bc->execute_plan();
     $bc->destroy();
     // The progress class will get called during restore, so we can use that
     // to check the executing flag is true.
     $progress = new core_backup_progress_restore_is_executing();
     // Set up restore.
     $rc = new restore_controller($backupid, $this->courseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid, backup::TARGET_EXISTING_ADDING);
     $this->assertTrue($rc->execute_precheck());
     // Check restore is NOT executing.
     $this->assertFalse(restore_controller::is_executing());
     // Execute restore.
     $rc->set_progress($progress);
     $rc->execute_plan();
     // Check restore is NOT executing afterward either.
     $this->assertFalse(restore_controller::is_executing());
     $rc->destroy();
     // During restore, check that executing was true.
     $this->assertTrue(count($progress->executing) > 0);
     $alltrue = true;
     foreach ($progress->executing as $executing) {
         if (!$executing) {
             $alltrue = false;
             break;
         }
     }
     $this->assertTrue($alltrue);
 }
Exemple #15
0
 /**
  * Get the restore content tempdir.
  *
  * The tempdir is the sub directory in which the backup has been extracted.
  *
  * This caches the result for better performance, but $CFG->keeptempdirectoriesonbackup
  * needs to be enabled, otherwise the cache is ignored.
  *
  * @param string $backupfile path to a backup file.
  * @param string $shortname shortname of a course.
  * @param array $errors will be populated with errors found.
  * @return string|false false when the backup couldn't retrieved.
  */
 public static function get_restore_content_dir($backupfile = null, $shortname = null, &$errors = array())
 {
     global $CFG, $DB, $USER;
     $cachekey = null;
     if (!empty($backupfile)) {
         $backupfile = realpath($backupfile);
         if (empty($backupfile) || !is_readable($backupfile)) {
             $errors['cannotreadbackupfile'] = new lang_string('cannotreadbackupfile', 'tool_uploadcourse');
             return false;
         }
         $cachekey = 'backup_path:' . $backupfile;
     } else {
         if (!empty($shortname) || is_numeric($shortname)) {
             $cachekey = 'backup_sn:' . $shortname;
         }
     }
     if (empty($cachekey)) {
         return false;
     }
     // If $CFG->keeptempdirectoriesonbackup is not set to true, any restore happening would
     // automatically delete the backup directory... causing the cache to return an unexisting directory.
     $usecache = !empty($CFG->keeptempdirectoriesonbackup);
     if ($usecache) {
         $cache = cache::make('tool_uploadcourse', 'helper');
     }
     // If we don't use the cache, or if we do and not set, or the directory doesn't exist any more.
     if (!$usecache || (($backupid = $cache->get($cachekey)) === false || !is_dir("{$CFG->tempdir}/backup/{$backupid}"))) {
         // Use null instead of false because it would consider that the cache key has not been set.
         $backupid = null;
         if (!empty($backupfile)) {
             // Extracting the backup file.
             $packer = get_file_packer('application/vnd.moodle.backup');
             $backupid = restore_controller::get_tempdir_name(SITEID, $USER->id);
             $path = "{$CFG->tempdir}/backup/{$backupid}/";
             $result = $packer->extract_to_pathname($backupfile, $path);
             if (!$result) {
                 $errors['invalidbackupfile'] = new lang_string('invalidbackupfile', 'tool_uploadcourse');
             }
         } else {
             if (!empty($shortname) || is_numeric($shortname)) {
                 // Creating restore from an existing course.
                 $courseid = $DB->get_field('course', 'id', array('shortname' => $shortname), IGNORE_MISSING);
                 if (!empty($courseid)) {
                     $bc = new backup_controller(backup::TYPE_1COURSE, $courseid, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
                     $bc->execute_plan();
                     $backupid = $bc->get_backupid();
                     $bc->destroy();
                 } else {
                     $errors['coursetorestorefromdoesnotexist'] = new lang_string('coursetorestorefromdoesnotexist', 'tool_uploadcourse');
                 }
             }
         }
         if ($usecache) {
             $cache->set($cachekey, $backupid);
         }
     }
     if ($backupid === null) {
         $backupid = false;
     }
     return $backupid;
 }
 /**
  *
  */
 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 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;
}
Exemple #18
0
 /**
  * Duplicate a course
  *
  * @param int $courseid
  * @param string $fullname Duplicated course fullname
  * @param string $shortname Duplicated course shortname
  * @param int $categoryid Duplicated course parent category id
  * @param int $visible Duplicated course availability
  * @param array $options List of backup options
  * @return array New course info
  * @since Moodle 2.3
  */
 public static function duplicate_course($courseid, $fullname, $shortname, $categoryid, $visible, $options)
 {
     global $CFG, $USER, $DB;
     require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php';
     require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php';
     // Parameter validation.
     $params = self::validate_parameters(self::duplicate_course_parameters(), array('courseid' => $courseid, 'fullname' => $fullname, 'shortname' => $shortname, 'categoryid' => $categoryid, 'visible' => $visible, 'options' => $options));
     // Context validation.
     if (!($course = $DB->get_record('course', array('id' => $params['courseid'])))) {
         throw new moodle_exception('invalidcourseid', 'error', '', $params['courseid']);
     }
     // Category where duplicated course is going to be created.
     $categorycontext = context_coursecat::instance($params['categoryid']);
     self::validate_context($categorycontext);
     // Course to be duplicated.
     $coursecontext = context_course::instance($course->id);
     self::validate_context($coursecontext);
     $backupdefaults = array('activities' => 1, 'blocks' => 1, 'filters' => 1, 'users' => 0, 'role_assignments' => 0, 'user_files' => 0, 'comments' => 0, 'completion_information' => 0, 'logs' => 0, 'histories' => 0);
     $backupsettings = array();
     // Check for backup and restore options.
     if (!empty($params['options'])) {
         foreach ($params['options'] as $option) {
             // Strict check for a correct value (allways 1 or 0, true or false).
             $value = clean_param($option['value'], PARAM_INT);
             if ($value !== 0 and $value !== 1) {
                 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
             }
             if (!isset($backupdefaults[$option['name']])) {
                 throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']);
             }
             $backupsettings[$option['name']] = $value;
         }
     }
     // Capability checking.
     // The backup controller check for this currently, this may be redundant.
     require_capability('moodle/course:create', $categorycontext);
     require_capability('moodle/restore:restorecourse', $categorycontext);
     require_capability('moodle/backup:backupcourse', $coursecontext);
     if (!empty($backupsettings['users'])) {
         require_capability('moodle/backup:userinfo', $coursecontext);
         require_capability('moodle/restore:userinfo', $categorycontext);
     }
     // Check if the shortname is used.
     if ($foundcourses = $DB->get_records('course', array('shortname' => $shortname))) {
         foreach ($foundcourses as $foundcourse) {
             $foundcoursenames[] = $foundcourse->fullname;
         }
         $foundcoursenamestring = implode(',', $foundcoursenames);
         throw new moodle_exception('shortnametaken', '', '', $foundcoursenamestring);
     }
     // Backup the course.
     $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id);
     foreach ($backupsettings as $name => $value) {
         $bc->get_plan()->get_setting($name)->set_value($value);
     }
     $backupid = $bc->get_backupid();
     $backupbasepath = $bc->get_plan()->get_basepath();
     $bc->execute_plan();
     $results = $bc->get_results();
     $file = $results['backup_destination'];
     $bc->destroy();
     // Restore the backup immediately.
     // Check if we need to unzip the file because the backup temp dir does not contains backup files.
     if (!file_exists($backupbasepath . "/moodle_backup.xml")) {
         $file->extract_to_pathname(get_file_packer(), $backupbasepath);
     }
     // Create new course.
     $newcourseid = restore_dbops::create_new_course($params['fullname'], $params['shortname'], $params['categoryid']);
     $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $USER->id, backup::TARGET_NEW_COURSE);
     foreach ($backupsettings as $name => $value) {
         $setting = $rc->get_plan()->get_setting($name);
         if ($setting->get_status() == backup_setting::NOT_LOCKED) {
             $setting->set_value($value);
         }
     }
     if (!$rc->execute_precheck()) {
         $precheckresults = $rc->get_precheck_results();
         if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
             if (empty($CFG->keeptempdirectoriesonbackup)) {
                 fulldelete($backupbasepath);
             }
             $errorinfo = '';
             foreach ($precheckresults['errors'] as $error) {
                 $errorinfo .= $error;
             }
             if (array_key_exists('warnings', $precheckresults)) {
                 foreach ($precheckresults['warnings'] as $warning) {
                     $errorinfo .= $warning;
                 }
             }
             throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo);
         }
     }
     $rc->execute_plan();
     $rc->destroy();
     $course = $DB->get_record('course', array('id' => $newcourseid), '*', MUST_EXIST);
     $course->fullname = $params['fullname'];
     $course->shortname = $params['shortname'];
     $course->visible = $params['visible'];
     // Set shortname and fullname back.
     $DB->update_record('course', $course);
     if (empty($CFG->keeptempdirectoriesonbackup)) {
         fulldelete($backupbasepath);
     }
     // Delete the course backup file created by this WebService. Originally located in the course backups area.
     $file->delete();
     return array('id' => $course->id, 'shortname' => $course->shortname);
 }
 /**
  * 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;
 }
 /**
  * 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;
 }
 /**
  * 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;
 }
Exemple #22
0
 /**
  * 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();
 }
 /**
  * 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;
 }
 /**
  * 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;
 }
Exemple #25
0
/**
 * 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;
}
Exemple #26
0
 /**
  * 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;
 }
Exemple #27
0
    /**
     * 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;
    }
 /**
  * Backs a course up and restores it.
  *
  * @param stdClass $srccourse Course object to backup
  * @param stdClass $dstcourse Course object to restore into
  * @return int ID of newly restored course
  */
 protected function backup_and_restore($srccourse, $dstcourse = null)
 {
     global $USER, $CFG;
     // Turn off file logging, otherwise it can't delete the file (Windows).
     $CFG->backup_file_logger_level = backup::LOG_NONE;
     // Do backup with default settings. MODE_IMPORT means it will just
     // create the directory and not zip it.
     $bc = new backup_controller(backup::TYPE_1COURSE, $srccourse->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
     $backupid = $bc->get_backupid();
     $bc->execute_plan();
     $bc->destroy();
     // Do restore to new course with default settings.
     if ($dstcourse !== null) {
         $newcourseid = $dstcourse->id;
     } else {
         $newcourseid = restore_dbops::create_new_course($srccourse->fullname, $srccourse->shortname . '_2', $srccourse->category);
     }
     $rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, backup::TARGET_NEW_COURSE);
     $this->assertTrue($rc->execute_precheck());
     $rc->execute_plan();
     $rc->destroy();
     return $newcourseid;
 }
 /**
  * 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));
 }