/** * Test remove_course_content deletes course contents * TODO Add asserts to verify other data related to course is deleted as well. */ public function test_remove_course_contents() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $user = $this->getDataGenerator()->create_user(); $gen = $this->getDataGenerator()->get_plugin_generator('core_notes'); $note = $gen->create_instance(array('courseid' => $course->id, 'userid' => $user->id)); $this->assertNotEquals(false, note_load($note->id)); remove_course_contents($course->id, false); $this->assertFalse(note_load($note->id)); }
/** * Test that triggering a course_content_deleted event works as expected. */ public function test_course_content_deleted_event() { global $DB; $this->resetAfterTest(); // Create the course. $course = $this->getDataGenerator()->create_course(); // Get the course from the DB. The data generator adds some extra properties, such as // numsections, to the course object which will fail the assertions later on. $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); // Save the course context before we delete the course. $coursecontext = context_course::instance($course->id); // Catch the update event. $sink = $this->redirectEvents(); remove_course_contents($course->id, false); // Capture the event. $events = $sink->get_events(); $sink->close(); // Validate the event. $event = array_pop($events); $this->assertInstanceOf('\\core\\event\\course_content_deleted', $event); $this->assertEquals('course', $event->objecttable); $this->assertEquals($course->id, $event->objectid); $this->assertEquals($coursecontext->id, $event->contextid); $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); $this->assertEquals('course_content_removed', $event->get_legacy_eventname()); // The legacy data also passed the context and options in the course object. $course->context = $coursecontext; $course->options = array(); $this->assertEventLegacyData($course, $event); $this->assertEventContextNotUsed($event); }
/** * Delete a course, including all related data from the database, and any associated files. * * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $DB; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = $DB->get_record('course', array('id' => $courseid)))) { return false; } } $context = context_course::instance($courseid); // Frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } // Allow plugins to use this course before we completely delete it. if ($pluginsfunction = get_plugins_with_function('pre_course_delete')) { foreach ($pluginsfunction as $plugintype => $plugins) { foreach ($plugins as $pluginfunction) { $pluginfunction($course); } } } // Make the course completely empty. remove_course_contents($courseid, $showfeedback); // Delete the course and related context instance. context_helper::delete_instance(CONTEXT_COURSE, $courseid); $DB->delete_records("course", array("id" => $courseid)); $DB->delete_records("course_format_options", array("courseid" => $courseid)); // Reset all course related caches here. if (class_exists('format_base', false)) { format_base::reset_course_cache($courseid); } // Trigger a course deleted event. $event = \core\event\course_deleted::create(array('objectid' => $course->id, 'context' => $context, 'other' => array('shortname' => $course->shortname, 'fullname' => $course->fullname, 'idnumber' => $course->idnumber))); $event->add_record_snapshot('course', $course); $event->trigger(); return true; }
/** * Delete a course, including all related data from the database, * and any associated files from the moodledata folder. * * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $CFG; $result = true; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = get_record('course', 'id', $courseid))) { return false; } } // frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } if (!remove_course_contents($courseid, $showfeedback)) { if ($showfeedback) { notify("An error occurred while deleting some of the course contents."); } $result = false; } if (!delete_records("course", "id", $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main course record."); } $result = false; } /// Delete all roles and overiddes in the course context if (!delete_context(CONTEXT_COURSE, $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main course context."); } $result = false; } if (!fulldelete($CFG->dataroot . '/' . $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the course files."); } $result = false; } if ($result) { //trigger events events_trigger('course_deleted', $course); } return $result; }
function restore_execute(&$restore, $info, $course_header, &$errorstr) { global $CFG, $USER; $status = true; //Checks for the required files/functions to restore every module //and include them if ($allmods = get_records("modules")) { foreach ($allmods as $mod) { $modname = $mod->name; $modfile = "{$CFG->dirroot}/mod/{$modname}/restorelib.php"; //If file exists and we have selected to restore that type of module if (file_exists($modfile) and !empty($restore->mods[$modname]) and $restore->mods[$modname]->restore) { include_once $modfile; } } } if (!defined('RESTORE_SILENTLY')) { //Start the main table echo "<table cellpadding=\"5\">"; echo "<tr><td>"; //Start the main ul echo "<ul>"; } //Location of the xml file $xml_file = $CFG->dataroot . "/temp/backup/" . $restore->backup_unique_code . "/moodle.xml"; //Preprocess the moodle.xml file spliting into smaller chucks (modules, users, logs...) //for optimal parsing later in the restore process. if (!empty($CFG->experimentalsplitrestore)) { if (!defined('RESTORE_SILENTLY')) { echo '<li>' . get_string('preprocessingbackupfile') . '</li>'; } //First of all, split moodle.xml into handy files if (!restore_split_xml($xml_file, $restore)) { if (!defined('RESTORE_SILENTLY')) { notify("Error proccessing moodle.xml file. Process ended."); } else { $errorstr = "Error proccessing moodle.xml file. Process ended."; } return false; } } //If we've selected to restore into new course //create it (course) //Saving conversion id variables into backup_tables if ($restore->restoreto == RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo '<li>' . get_string('creatingnewcourse') . '</li>'; } $oldidnumber = $course_header->course_idnumber; if (!($status = restore_create_new_course($restore, $course_header))) { if (!defined('RESTORE_SILENTLY')) { notify("Error while creating the new empty course."); } else { $errorstr = "Error while creating the new empty course."; return false; } } //Print course fullname and shortname and category if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<ul>"; echo "<li>" . $course_header->course_fullname . " (" . $course_header->course_shortname . ")" . '</li>'; echo "<li>" . get_string("category") . ": " . $course_header->category->name . '</li>'; if (!empty($oldidnumber)) { echo "<li>" . get_string("nomoreidnumber", "moodle", $oldidnumber) . "</li>"; } echo "</ul>"; //Put the destination course_id } $restore->course_id = $course_header->course_id; } if ($status = restore_open_html($restore, $course_header)) { if (!defined('RESTORE_SILENTLY')) { echo "<li>Creating the Restorelog.html in the course backup folder</li>"; } } } else { $course = get_record("course", "id", $restore->course_id); if ($course) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("usingexistingcourse"); echo "<ul>"; echo "<li>" . get_string("from") . ": " . $course_header->course_fullname . " (" . $course_header->course_shortname . ")" . '</li>'; echo "<li>" . get_string("to") . ": " . format_string($course->fullname) . " (" . format_string($course->shortname) . ")" . '</li>'; if ($restore->deleting) { echo "<li>" . get_string("deletingexistingcoursedata") . '</li>'; } else { echo "<li>" . get_string("addingdatatoexisting") . '</li>'; } echo "</ul></li>"; } //If we have selected to restore deleting, we do it now. if ($restore->deleting) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("deletingolddata") . '</li>'; } $status = remove_course_contents($restore->course_id, false) and delete_dir_contents($CFG->dataroot . "/" . $restore->course_id, "backupdata"); if ($status) { //Now , this situation is equivalent to the "restore to new course" one (we //have a course record and nothing more), so define it as "to new course" $restore->restoreto = RESTORETO_NEW_COURSE; } else { if (!defined('RESTORE_SILENTLY')) { notify("An error occurred while deleting some of the course contents."); } else { $errrostr = "An error occurred while deleting some of the course contents."; return false; } } } } else { if (!defined('RESTORE_SILENTLY')) { notify("Error opening existing course."); $status = false; } else { $errorstr = "Error opening existing course."; return false; } } } //Now create users as needed if ($status and ($restore->users == 0 or $restore->users == 1)) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingusers") . "<br />"; } if (!($status = restore_create_users($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore users."); } else { $errorstr = "Could not restore users."; return false; } } //Now print info about the work done if ($status) { $recs = get_records_sql("select old_id, new_id from {$CFG->prefix}backup_ids\n where backup_code = '{$restore->backup_unique_code}' and\n table_name = 'user'"); //We've records if ($recs) { $new_count = 0; $exists_count = 0; $student_count = 0; $teacher_count = 0; $counter = 0; //Iterate, filling counters foreach ($recs as $rec) { //Get full record, using backup_getids $record = backup_getid($restore->backup_unique_code, "user", $rec->old_id); if (strpos($record->info, "new") !== false) { $new_count++; } if (strpos($record->info, "exists") !== false) { $exists_count++; } if (strpos($record->info, "student") !== false) { $student_count++; } else { if (strpos($record->info, "teacher") !== false) { $teacher_count++; } } //Do some output $counter++; if ($counter % 10 == 0) { if (!defined('RESTORE_SILENTLY')) { echo "."; if ($counter % 200 == 0) { echo "<br />"; } } backup_flush(300); } } if (!defined('RESTORE_SILENTLY')) { //Now print information gathered echo " (" . get_string("new") . ": " . $new_count . ", " . get_string("existing") . ": " . $exists_count . ")"; echo "<ul>"; echo "<li>" . get_string("students") . ": " . $student_count . '</li>'; echo "<li>" . get_string("teachers") . ": " . $teacher_count . '</li>'; echo "</ul>"; } } else { if (!defined('RESTORE_SILENTLY')) { notify("No users were found!"); } // no need to return false here, it's recoverable. } } if (!defined('RESTORE_SILENTLY')) { echo "</li>"; } } //Now create groups as needed if ($status and ($restore->groups == RESTORE_GROUPS_ONLY or $restore->groups == RESTORE_GROUPS_GROUPINGS)) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatinggroups"); } if (!($status = restore_create_groups($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore groups!"); } else { $errorstr = "Could not restore groups!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create groupings as needed if ($status and ($restore->groups == RESTORE_GROUPINGS_ONLY or $restore->groups == RESTORE_GROUPS_GROUPINGS)) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatinggroupings"); } if (!($status = restore_create_groupings($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore groupings!"); } else { $errorstr = "Could not restore groupings!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create groupingsgroups as needed if ($status and $restore->groups == RESTORE_GROUPS_GROUPINGS) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatinggroupingsgroups"); } if (!($status = restore_create_groupings_groups($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore groups in groupings!"); } else { $errorstr = "Could not restore groups in groupings!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create the course_sections and their associated course_modules //we have to do this after groups and groupings are restored, because we need the new groupings id if ($status) { //Into new course if ($restore->restoreto == RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingsections"); } if (!($status = restore_create_sections($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Error creating sections in the existing course."); } else { $errorstr = "Error creating sections in the existing course."; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } //Into existing course } else { if ($restore->restoreto != RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("checkingsections"); } if (!($status = restore_create_sections($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Error creating sections in the existing course."); } else { $errorstr = "Error creating sections in the existing course."; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } //Error } else { if (!defined('RESTORE_SILENTLY')) { notify("Neither a new course or an existing one was specified."); $status = false; } else { $errorstr = "Neither a new course or an existing one was specified."; return false; } } } } //Now create metacourse info if ($status and $restore->metacourse) { //Only to new courses! if ($restore->restoreto == RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingmetacoursedata"); } if (!($status = restore_create_metacourse($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Error creating metacourse in the course."); } else { $errorstr = "Error creating metacourse in the course."; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } } //Now create categories and questions as needed if ($status) { include_once "{$CFG->dirroot}/question/restorelib.php"; if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingcategoriesandquestions"); echo "<ul>"; } if (!($status = restore_create_questions($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore categories and questions!"); } else { $errorstr = "Could not restore categories and questions!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo "</ul></li>"; } } //Now create user_files as needed if ($status and $restore->user_files) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("copyinguserfiles"); } if (!($status = restore_user_files($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore user files!"); } else { $errorstr = "Could not restore user files!"; return false; } } //If all is ok (and we have a counter) if ($status and $status !== true) { //Inform about user dirs created from backup if (!defined('RESTORE_SILENTLY')) { echo "<ul>"; echo "<li>" . get_string("userzones") . ": " . $status; echo "</li></ul>"; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create course files as needed if ($status and $restore->course_files) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("copyingcoursefiles"); } if (!($status = restore_course_files($restore))) { if (empty($status)) { notify("Could not restore course files!"); } else { $errorstr = "Could not restore course files!"; return false; } } //If all is ok (and we have a counter) if ($status and $status !== true) { //Inform about user dirs created from backup if (!defined('RESTORE_SILENTLY')) { echo "<ul>"; echo "<li>" . get_string("filesfolders") . ": " . $status . '</li>'; echo "</ul>"; } } if (!defined('RESTORE_SILENTLY')) { echo "</li>"; } } //Now create site files as needed if ($status and $restore->site_files) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string('copyingsitefiles'); } if (!($status = restore_site_files($restore))) { if (empty($status)) { notify("Could not restore site files!"); } else { $errorstr = "Could not restore site files!"; return false; } } //If all is ok (and we have a counter) if ($status and $status !== true) { //Inform about user dirs created from backup if (!defined('RESTORE_SILENTLY')) { echo "<ul>"; echo "<li>" . get_string("filesfolders") . ": " . $status . '</li>'; echo "</ul>"; } } if (!defined('RESTORE_SILENTLY')) { echo "</li>"; } } //Now create messages as needed if ($status and $restore->messages) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingmessagesinfo"); } if (!($status = restore_create_messages($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore messages!"); } else { $errorstr = "Could not restore messages!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo "</li>"; } } //Now create blogs as needed if ($status and $restore->blogs) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingblogsinfo"); } if (!($status = restore_create_blogs($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore blogs!"); } else { $errorstr = "Could not restore blogs!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo "</li>"; } } //Now create scales as needed if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingscales"); } if (!($status = restore_create_scales($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore custom scales!"); } else { $errorstr = "Could not restore custom scales!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create events as needed if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingevents"); } if (!($status = restore_create_events($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore course events!"); } else { $errorstr = "Could not restore course events!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create course modules as needed if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatingcoursemodules"); } if (!($status = restore_create_modules($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore modules!"); } else { $errorstr = "Could not restore modules!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Bring back the course blocks -- do it AFTER the modules!!! if ($status) { //If we are deleting and bringing into a course or making a new course, same situation if ($restore->restoreto == RESTORETO_CURRENT_DELETING || $restore->restoreto == RESTORETO_EXISTING_DELETING || $restore->restoreto == RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo '<li>' . get_string('creatingblocks'); } $course_header->blockinfo = !empty($course_header->blockinfo) ? $course_header->blockinfo : NULL; if (!($status = restore_create_blocks($restore, $info->backup_block_format, $course_header->blockinfo, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify('Error while creating the course blocks'); } else { $errorstr = "Error while creating the course blocks"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } } if ($status) { //If we are deleting and bringing into a course or making a new course, same situation if ($restore->restoreto == RESTORETO_CURRENT_DELETING || $restore->restoreto == RESTORETO_EXISTING_DELETING || $restore->restoreto == RESTORETO_NEW_COURSE) { if (!defined('RESTORE_SILENTLY')) { echo '<li>' . get_string('courseformatdata'); } if (!($status = restore_set_format_data($restore, $xml_file))) { $error = "Error while setting the course format data"; if (!defined('RESTORE_SILENTLY')) { notify($error); } else { $errorstr = $error; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } } //Now create log entries as needed if ($status and $restore->logs) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatinglogentries"); } if (!($status = restore_create_logs($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore logs!"); } else { $errorstr = "Could not restore logs!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now, if all is OK, adjust the instance field in course_modules !! //this also calculates the final modinfo information so, after this, //code needing it can be used (like role_assignments. MDL-13740) if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("checkinginstances"); } if (!($status = restore_check_instances($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not adjust instances in course_modules!"); } else { $errorstr = "Could not adjust instances in course_modules!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now, if all is OK, adjust activity events if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("refreshingevents"); } if (!($status = restore_refresh_events($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not refresh events for activities!"); } else { $errorstr = "Could not refresh events for activities!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now, if all is OK, adjust inter-activity links if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("decodinginternallinks"); } if (!($status = restore_decode_content_links($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not decode content links!"); } else { $errorstr = "Could not decode content links!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now, with backup files prior to version 2005041100, //convert all the wiki texts in the course to markdown if ($status && $restore->backup_version < 2005041100) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("convertingwikitomarkdown"); } if (!($status = restore_convert_wiki2markdown($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not convert wiki texts to markdown!"); } else { $errorstr = "Could not convert wiki texts to markdown!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Now create gradebook as needed -- AFTER modules and blocks!!! if ($status) { if ($restore->backup_version > 2007090500) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("creatinggradebook"); } if (!($status = restore_create_gradebook($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not restore gradebook!"); } else { $errorstr = "Could not restore gradebook!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } else { // for moodle versions before 1.9, those grades need to be converted to use the new gradebook // this code needs to execute *after* the course_modules are sorted out if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("migratinggrades"); } /// force full refresh of grading data before migration == crete all items first if (!($status = restore_migrate_old_gradebook($restore, $xml_file))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not migrate gradebook!"); } else { $errorstr = "Could not migrade gradebook!"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } /// force full refresh of grading data after all items are created grade_force_full_regrading($restore->course_id); grade_grab_course_grades($restore->course_id); } /******************************************************************************* ************* Restore of Roles and Capabilities happens here ****************** *******************************************************************************/ // try to restore roles even when restore is going to fail - teachers might have // at least some role assigned - this is not correct though $status = restore_create_roles($restore, $xml_file) && $status; $status = restore_roles_settings($restore, $xml_file) && $status; //Now if all is OK, update: // - course modinfo field // - categories table // - add user as teacher if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("checkingcourse"); } //categories table $course = get_record("course", "id", $restore->course_id); fix_course_sortorder(); // Check if the user has course update capability in the newly restored course // there is no need to load his capabilities again, because restore_roles_settings // would have loaded it anyway, if there is any assignments. // fix for MDL-6831 $newcontext = get_context_instance(CONTEXT_COURSE, $restore->course_id); if (!has_capability('moodle/course:manageactivities', $newcontext)) { // fix for MDL-9065, use the new config setting if exists if ($CFG->creatornewroleid) { role_assign($CFG->creatornewroleid, $USER->id, 0, $newcontext->id); } else { if ($legacyteachers = get_roles_with_capability('moodle/legacy:editingteacher', CAP_ALLOW, get_context_instance(CONTEXT_SYSTEM))) { if ($legacyteacher = array_shift($legacyteachers)) { role_assign($legacyteacher->id, $USER->id, 0, $newcontext->id); } } else { notify('Could not find a legacy teacher role. You might need your moodle admin to assign a role with editing privilages to this course.'); } } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } //Cleanup temps (files and db) if ($status) { if (!defined('RESTORE_SILENTLY')) { echo "<li>" . get_string("cleaningtempdata"); } if (!($status = clean_temp_data($restore))) { if (!defined('RESTORE_SILENTLY')) { notify("Could not clean up temporary data from files and database"); } else { $errorstr = "Could not clean up temporary data from files and database"; return false; } } if (!defined('RESTORE_SILENTLY')) { echo '</li>'; } } // this is not a critical check - the result can be ignored if (restore_close_html($restore)) { if (!defined('RESTORE_SILENTLY')) { echo '<li>Closing the Restorelog.html file.</li>'; } } else { if (!defined('RESTORE_SILENTLY')) { notify("Could not close the restorelog.html file"); } } if (!defined('RESTORE_SILENTLY')) { //End the main ul echo "</ul>"; //End the main table echo "</td></tr>"; echo "</table>"; } return $status; }
/** * Delete a course, including all related data from the database, and any associated files. * * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $DB; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = $DB->get_record('course', array('id' => $courseid)))) { return false; } } $context = context_course::instance($courseid); // Frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } // Make the course completely empty. remove_course_contents($courseid, $showfeedback); // Delete the course and related context instance. context_helper::delete_instance(CONTEXT_COURSE, $courseid); $DB->delete_records("course", array("id" => $courseid)); $DB->delete_records("course_format_options", array("courseid" => $courseid)); // Trigger a course deleted event. $event = \core\event\course_deleted::create(array('objectid' => $course->id, 'context' => $context, 'other' => array('shortname' => $course->shortname, 'fullname' => $course->fullname, 'idnumber' => $course->idnumber))); $event->add_record_snapshot('course', $course); $event->trigger(); return true; }
/** * Delete a course, including all related data from the database, * and any associated files. * * @global object * @global object * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $DB; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = $DB->get_record('course', array('id' => $courseid)))) { return false; } } $context = get_context_instance(CONTEXT_COURSE, $courseid); // frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } // make the course completely empty remove_course_contents($courseid, $showfeedback); // delete the course and related context instance delete_context(CONTEXT_COURSE, $courseid); $DB->delete_records("course", array("id" => $courseid)); //trigger events $course->context = $context; // you can not fetch context in the event because it was already deleted events_trigger('course_deleted', $course); return true; }
/** * Deletes all of the content associated with the given course (courseid) * @param int $courseid * @param array $options * @return bool True for success */ public static function delete_course_content($courseid, array $options = null) { return remove_course_contents($courseid, false, $options); }
/** * Delete a course, including all related data from the database, * and any associated files. * * @global object * @global object * @param mixed $courseorid The id of the course or course object to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseorid, $showfeedback = true) { global $DB; if (is_object($courseorid)) { $courseid = $courseorid->id; $course = $courseorid; } else { $courseid = $courseorid; if (!($course = $DB->get_record('course', array('id' => $courseid)))) { return false; } } $context = get_context_instance(CONTEXT_COURSE, $courseid); // frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } // make the course completely empty remove_course_contents($courseid, $showfeedback); // delete the course and related context instance delete_context(CONTEXT_COURSE, $courseid); // We will update the course's timemodified, as it will be passed to the course_deleted event, // which should know about this updated property, as this event is meant to pass the full course record $course->timemodified = time(); $DB->delete_records("course", array("id" => $courseid)); //trigger events $course->context = $context; // you can not fetch context in the event because it was already deleted events_trigger('course_deleted', $course); return true; }
/** * Duplicate a course * * @param int $courseid * @param string $fullname Duplicated course fullname * @param int $newcourse Destination course * @param array $options List of backup options * @return stdClass New course info */ function local_ltiprovider_duplicate_course($courseid, $newcourse, $visible = 1, $options = array(), $useridcreating = null, $context) { global $CFG, $USER, $DB; require_once $CFG->dirroot . '/backup/util/includes/backup_includes.php'; require_once $CFG->dirroot . '/backup/util/includes/restore_includes.php'; if (empty($USER)) { // Emulate session. cron_setup_user(); } // Context validation. if (!($course = $DB->get_record('course', array('id' => $courseid)))) { throw new moodle_exception('invalidcourseid', 'error'); } $removeoptions = array(); $removeoptions['keep_roles_and_enrolments'] = true; $removeoptions['keep_groups_and_groupings'] = true; remove_course_contents($newcourse->id, false, $removeoptions); $backupdefaults = array('activities' => 1, 'blocks' => 1, 'filters' => 1, 'users' => 0, 'role_assignments' => 0, 'comments' => 0, 'userscompletion' => 0, 'logs' => 0, 'grade_histories' => 0); $backupsettings = array(); // Check for backup and restore options. if (!empty($options)) { foreach ($options as $option) { // Strict check for a correct value (allways 1 or 0, true or false). $value = clean_param($option['value'], PARAM_INT); if ($value !== 0 and $value !== 1) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } if (!isset($backupdefaults[$option['name']])) { throw new moodle_exception('invalidextparam', 'webservice', '', $option['name']); } $backupsettings[$option['name']] = $value; } } // Backup the course. $admin = get_admin(); $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $admin->id); foreach ($backupsettings as $name => $value) { $bc->get_plan()->get_setting($name)->set_value($value); } $backupid = $bc->get_backupid(); $backupbasepath = $bc->get_plan()->get_basepath(); $bc->execute_plan(); $results = $bc->get_results(); $file = $results['backup_destination']; $bc->destroy(); // Restore the backup immediately. // Check if we need to unzip the file because the backup temp dir does not contains backup files. if (!file_exists($backupbasepath . "/moodle_backup.xml")) { $file->extract_to_pathname(get_file_packer(), $backupbasepath); } $rc = new restore_controller($backupid, $newcourse->id, backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $admin->id, backup::TARGET_CURRENT_DELETING); foreach ($backupsettings as $name => $value) { $setting = $rc->get_plan()->get_setting($name); if ($setting->get_status() == backup_setting::NOT_LOCKED) { $setting->set_value($value); } } if (!$rc->execute_precheck()) { $precheckresults = $rc->get_precheck_results(); if (is_array($precheckresults) && !empty($precheckresults['errors'])) { if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } $errorinfo = ''; foreach ($precheckresults['errors'] as $error) { $errorinfo .= $error; } if (array_key_exists('warnings', $precheckresults)) { foreach ($precheckresults['warnings'] as $warning) { $errorinfo .= $warning; } } throw new moodle_exception('backupprecheckerrors', 'webservice', '', $errorinfo); } } $rc->execute_plan(); $rc->destroy(); $course = $DB->get_record('course', array('id' => $newcourse->id), '*', MUST_EXIST); $course->visible = $visible; $course->fullname = $newcourse->fullname; $course->shortname = $newcourse->shortname; $course->idnumber = $newcourse->idnumber; // Set shortname and fullname back. $DB->update_record('course', $course); if (empty($CFG->keeptempdirectoriesonbackup)) { fulldelete($backupbasepath); } // Delete the course backup file created by this WebService. Originally located in the course backups area. $file->delete(); // We have to unenroll all the user except the one that create the course. if (get_config('local_ltiprovider', 'duplicatecourseswithoutusers') and $useridcreating) { require_once $CFG->dirroot . '/group/lib.php'; // Previous to unenrol users, we assign some type of activities to the user that created the course. if ($user = $DB->get_record('user', array('id' => $useridcreating))) { if ($databases = $DB->get_records('data', array('course' => $course->id))) { foreach ($databases as $data) { $DB->execute("UPDATE {data_records} SET userid = ? WHERE dataid = ?", array($user->id, $data->id)); } } if ($glossaries = $DB->get_records('glossary', array('course' => $course->id))) { foreach ($glossaries as $glossary) { $DB->execute("UPDATE {glossary_entries} SET userid = ? WHERE glossaryid = ?", array($user->id, $glossary->id)); } } // Same for questions. $newcoursecontextid = context_course::instance($course->id); if ($qcategories = $DB->get_records('question_categories', array('contextid' => $newcoursecontextid->id))) { foreach ($qcategories as $qcategory) { $DB->execute("UPDATE {question} SET createdby = ?, modifiedby = ? WHERE category = ?", array($user->id, $user->id, $qcategory->id)); } } // Enrol the user. if ($tool = $DB->get_record('local_ltiprovider', array('contextid' => $newcoursecontextid->id))) { $roles = explode(',', strtolower($context->info['roles'])); local_ltiprovider_enrol_user($tool, $user, $roles, true); } // Now, we unenrol all the users except the one who created the course. $plugins = enrol_get_plugins(true); $instances = enrol_get_instances($course->id, true); foreach ($instances as $key => $instance) { if (!isset($plugins[$instance->enrol])) { unset($instances[$key]); continue; } } $sql = "SELECT ue.*\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)\n JOIN {context} c ON (c.contextlevel = :courselevel AND c.instanceid = e.courseid)"; $params = array('courseid' => $course->id, 'courselevel' => CONTEXT_COURSE); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if ($ue->userid == $user->id) { continue; } if (!isset($instances[$ue->enrolid])) { continue; } $instance = $instances[$ue->enrolid]; $plugin = $plugins[$instance->enrol]; if (!$plugin->allow_unenrol($instance) and !$plugin->allow_unenrol_user($instance, $ue)) { continue; } $plugin->unenrol_user($instance, $ue->userid); } $rs->close(); groups_delete_group_members($course->id); groups_delete_groups($course->id, false); groups_delete_groupings_groups($course->id, false); groups_delete_groupings($course->id, false); } } return $course; }
/** * Test that triggering a course_content_deleted event works as expected. */ public function test_course_content_deleted_event() { global $DB; $this->resetAfterTest(); // Create the course. $course = $this->getDataGenerator()->create_course(); // Get the course from the DB. The data generator adds some extra properties, such as // numsections, to the course object which will fail the assertions later on. $course = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST); // Save the course context before we delete the course. $coursecontext = context_course::instance($course->id); // Catch the update event. $sink = $this->redirectEvents(); // Call remove_course_contents() which will trigger the course_content_deleted event. // This function prints out data to the screen, which we do not want during a PHPUnit // test, so use ob_start and ob_end_clean to prevent this. ob_start(); remove_course_contents($course->id); ob_end_clean(); // Capture the event. $events = $sink->get_events(); $sink->close(); // Validate the event. $event = $events[0]; $this->assertInstanceOf('\\core\\event\\course_content_deleted', $event); $this->assertEquals('course', $event->objecttable); $this->assertEquals($course->id, $event->objectid); $this->assertEquals($coursecontext->id, $event->contextid); $this->assertEquals($course, $event->get_record_snapshot('course', $course->id)); $this->assertEquals('course_content_removed', $event->get_legacy_eventname()); // The legacy data also passed the context and options in the course object. $course->context = $coursecontext; $course->options = array(); $this->assertEventLegacyData($course, $event); }
/** * Delete a course, including all related data from the database, * and any associated files from the moodledata folder. * * @param int $courseid The id of the course to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseid, $showfeedback = true) { global $CFG; require_once $CFG->libdir . '/gradelib.php'; $result = true; if (!remove_course_contents($courseid, $showfeedback)) { if ($showfeedback) { notify("An error occurred while deleting some of the course contents."); } $result = false; } remove_course_grades($courseid, $showfeedback); if (!delete_records("course", "id", $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main course record."); } $result = false; } if (!delete_records('context', 'contextlevel', CONTEXT_COURSE, 'instanceid', $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main context record."); } $result = false; } if (!fulldelete($CFG->dataroot . '/' . $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the course files."); } $result = false; } return $result; }
/** * Delete a course, including all related data from the database, * and any associated files from the moodledata folder. * * @param int $courseid The id of the course to delete. * @param bool $showfeedback Whether to display notifications of each action the function performs. * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function delete_course($courseid, $showfeedback = true) { global $CFG; require_once $CFG->libdir . '/gradelib.php'; $result = true; // frontpage course can not be deleted!! if ($courseid == SITEID) { return false; } $context = get_context_instance(CONTEXT_COURSE, $courseid); if (!remove_course_contents($courseid, $showfeedback)) { if ($showfeedback) { notify("An error occurred while deleting some of the course contents."); } $result = false; } remove_course_grades($courseid, $showfeedback); remove_grade_letters($context, $showfeedback); if (!delete_records("course", "id", $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main course record."); } $result = false; } /// Delete all roles and overiddes in the course context if (!delete_context(CONTEXT_COURSE, $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the main course context."); } $result = false; } if (!fulldelete($CFG->dataroot . '/' . $courseid)) { if ($showfeedback) { notify("An error occurred while deleting the course files."); } $result = false; } return $result; }
/** * Deletes all of the content associated with the given course (courseid) * @param int $courseid * @return bool True for success */ public static function delete_course_content($courseid) { return remove_course_contents($courseid, false); }