Exemplo n.º 1
0
/**
 * Clear a course out completely, deleting all content but don't delete the course itself.
 *
 * This function does not verify any permissions.
 *
 * Please note this function also deletes all user enrolments,
 * enrolment instances and role assignments by default.
 *
 * $options:
 *  - 'keep_roles_and_enrolments' - false by default
 *  - 'keep_groups_and_groupings' - false by default
 *
 * @param int $courseid The id of the course that is being deleted
 * @param bool $showfeedback Whether to display notifications of each action the function performs.
 * @param array $options extra options
 * @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 remove_course_contents($courseid, $showfeedback = true, array $options = null)
{
    global $CFG, $DB, $OUTPUT;
    require_once $CFG->libdir . '/badgeslib.php';
    require_once $CFG->libdir . '/completionlib.php';
    require_once $CFG->libdir . '/questionlib.php';
    require_once $CFG->libdir . '/gradelib.php';
    require_once $CFG->dirroot . '/group/lib.php';
    require_once $CFG->dirroot . '/comment/lib.php';
    require_once $CFG->dirroot . '/rating/lib.php';
    require_once $CFG->dirroot . '/notes/lib.php';
    // Handle course badges.
    badges_handle_course_deletion($courseid);
    // NOTE: these concatenated strings are suboptimal, but it is just extra info...
    $strdeleted = get_string('deleted') . ' - ';
    // Some crazy wishlist of stuff we should skip during purging of course content.
    $options = (array) $options;
    $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
    $coursecontext = context_course::instance($courseid);
    $fs = get_file_storage();
    // Delete course completion information, this has to be done before grades and enrols.
    $cc = new completion_info($course);
    $cc->clear_criteria();
    if ($showfeedback) {
        echo $OUTPUT->notification($strdeleted . get_string('completion', 'completion'), 'notifysuccess');
    }
    // Remove all data from gradebook - this needs to be done before course modules
    // because while deleting this information, the system may need to reference
    // the course modules that own the grades.
    remove_course_grades($courseid, $showfeedback);
    remove_grade_letters($coursecontext, $showfeedback);
    // Delete course blocks in any all child contexts,
    // they may depend on modules so delete them first.
    $childcontexts = $coursecontext->get_child_contexts();
    // Returns all subcontexts since 2.2.
    foreach ($childcontexts as $childcontext) {
        blocks_delete_all_for_context($childcontext->id);
    }
    unset($childcontexts);
    blocks_delete_all_for_context($coursecontext->id);
    if ($showfeedback) {
        echo $OUTPUT->notification($strdeleted . get_string('type_block_plural', 'plugin'), 'notifysuccess');
    }
    // Get the list of all modules that are properly installed.
    $allmodules = $DB->get_records_menu('modules', array(), '', 'name, id');
    // Delete every instance of every module,
    // this has to be done before deleting of course level stuff.
    $locations = core_component::get_plugin_list('mod');
    foreach ($locations as $modname => $moddir) {
        if ($modname === 'NEWMODULE') {
            continue;
        }
        if (array_key_exists($modname, $allmodules)) {
            $sql = "SELECT cm.*, m.id AS modinstance, m.name, '{$modname}' AS modname\n              FROM {" . $modname . "} m\n                   LEFT JOIN {course_modules} cm ON cm.instance = m.id AND cm.module = :moduleid\n             WHERE m.course = :courseid";
            $instances = $DB->get_records_sql($sql, array('courseid' => $course->id, 'modulename' => $modname, 'moduleid' => $allmodules[$modname]));
            include_once "{$moddir}/lib.php";
            // Shows php warning only if plugin defective.
            $moddelete = $modname . '_delete_instance';
            // Delete everything connected to an instance.
            $moddeletecourse = $modname . '_delete_course';
            // Delete other stray stuff (uncommon).
            if ($instances) {
                foreach ($instances as $cm) {
                    if ($cm->id) {
                        // Delete activity context questions and question categories.
                        question_delete_activity($cm, $showfeedback);
                        // Notify the competency subsystem.
                        \core_competency\api::hook_course_module_deleted($cm);
                    }
                    if (function_exists($moddelete)) {
                        // This purges all module data in related tables, extra user prefs, settings, etc.
                        $moddelete($cm->modinstance);
                    } else {
                        // NOTE: we should not allow installation of modules with missing delete support!
                        debugging("Defective module '{$modname}' detected when deleting course contents: missing function {$moddelete}()!");
                        $DB->delete_records($modname, array('id' => $cm->modinstance));
                    }
                    if ($cm->id) {
                        // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition.
                        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
                        $DB->delete_records('course_modules', array('id' => $cm->id));
                    }
                }
            }
            if (function_exists($moddeletecourse)) {
                // Execute optional course cleanup callback. Deprecated since Moodle 3.2. TODO MDL-53297 remove in 3.6.
                debugging("Callback delete_course is deprecated. Function {$moddeletecourse} should be converted " . 'to observer of event \\core\\event\\course_content_deleted', DEBUG_DEVELOPER);
                $moddeletecourse($course, $showfeedback);
            }
            if ($instances and $showfeedback) {
                echo $OUTPUT->notification($strdeleted . get_string('pluginname', $modname), 'notifysuccess');
            }
        } else {
            // Ooops, this module is not properly installed, force-delete it in the next block.
        }
    }
    // We have tried to delete everything the nice way - now let's force-delete any remaining module data.
    // Remove all data from availability and completion tables that is associated
    // with course-modules belonging to this course. Note this is done even if the
    // features are not enabled now, in case they were enabled previously.
    $DB->delete_records_select('course_modules_completion', 'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)', array($courseid));
    // Remove course-module data that has not been removed in modules' _delete_instance callbacks.
    $cms = $DB->get_records('course_modules', array('course' => $course->id));
    $allmodulesbyid = array_flip($allmodules);
    foreach ($cms as $cm) {
        if (array_key_exists($cm->module, $allmodulesbyid)) {
            try {
                $DB->delete_records($allmodulesbyid[$cm->module], array('id' => $cm->instance));
            } catch (Exception $e) {
                // Ignore weird or missing table problems.
            }
        }
        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
        $DB->delete_records('course_modules', array('id' => $cm->id));
    }
    if ($showfeedback) {
        echo $OUTPUT->notification($strdeleted . get_string('type_mod_plural', 'plugin'), 'notifysuccess');
    }
    // Cleanup the rest of plugins. Deprecated since Moodle 3.2. TODO MDL-53297 remove in 3.6.
    $cleanuplugintypes = array('report', 'coursereport', 'format');
    $callbacks = get_plugins_with_function('delete_course', 'lib.php');
    foreach ($cleanuplugintypes as $type) {
        if (!empty($callbacks[$type])) {
            foreach ($callbacks[$type] as $pluginfunction) {
                debugging("Callback delete_course is deprecated. Function {$pluginfunction} should be converted " . 'to observer of event \\core\\event\\course_content_deleted', DEBUG_DEVELOPER);
                $pluginfunction($course->id, $showfeedback);
            }
            if ($showfeedback) {
                echo $OUTPUT->notification($strdeleted . get_string('type_' . $type . '_plural', 'plugin'), 'notifysuccess');
            }
        }
    }
    // Delete questions and question categories.
    question_delete_course($course, $showfeedback);
    if ($showfeedback) {
        echo $OUTPUT->notification($strdeleted . get_string('questions', 'question'), 'notifysuccess');
    }
    // Make sure there are no subcontexts left - all valid blocks and modules should be already gone.
    $childcontexts = $coursecontext->get_child_contexts();
    // Returns all subcontexts since 2.2.
    foreach ($childcontexts as $childcontext) {
        $childcontext->delete();
    }
    unset($childcontexts);
    // Remove all roles and enrolments by default.
    if (empty($options['keep_roles_and_enrolments'])) {
        // This hack is used in restore when deleting contents of existing course.
        role_unassign_all(array('contextid' => $coursecontext->id, 'component' => ''), true);
        enrol_course_delete($course);
        if ($showfeedback) {
            echo $OUTPUT->notification($strdeleted . get_string('type_enrol_plural', 'plugin'), 'notifysuccess');
        }
    }
    // Delete any groups, removing members and grouping/course links first.
    if (empty($options['keep_groups_and_groupings'])) {
        groups_delete_groupings($course->id, $showfeedback);
        groups_delete_groups($course->id, $showfeedback);
    }
    // Filters be gone!
    filter_delete_all_for_context($coursecontext->id);
    // Notes, you shall not pass!
    note_delete_all($course->id);
    // Die comments!
    comment::delete_comments($coursecontext->id);
    // Ratings are history too.
    $delopt = new stdclass();
    $delopt->contextid = $coursecontext->id;
    $rm = new rating_manager();
    $rm->delete_ratings($delopt);
    // Delete course tags.
    core_tag_tag::remove_all_item_tags('core', 'course', $course->id);
    // Notify the competency subsystem.
    \core_competency\api::hook_course_deleted($course);
    // Delete calendar events.
    $DB->delete_records('event', array('courseid' => $course->id));
    $fs->delete_area_files($coursecontext->id, 'calendar');
    // Delete all related records in other core tables that may have a courseid
    // This array stores the tables that need to be cleared, as
    // table_name => column_name that contains the course id.
    $tablestoclear = array('backup_courses' => 'courseid', 'user_lastaccess' => 'courseid');
    foreach ($tablestoclear as $table => $col) {
        $DB->delete_records($table, array($col => $course->id));
    }
    // Delete all course backup files.
    $fs->delete_area_files($coursecontext->id, 'backup');
    // Cleanup course record - remove links to deleted stuff.
    $oldcourse = new stdClass();
    $oldcourse->id = $course->id;
    $oldcourse->summary = '';
    $oldcourse->cacherev = 0;
    $oldcourse->legacyfiles = 0;
    if (!empty($options['keep_groups_and_groupings'])) {
        $oldcourse->defaultgroupingid = 0;
    }
    $DB->update_record('course', $oldcourse);
    // Delete course sections.
    $DB->delete_records('course_sections', array('course' => $course->id));
    // Delete legacy, section and any other course files.
    $fs->delete_area_files($coursecontext->id, 'course');
    // Files from summary and section.
    // Delete all remaining stuff linked to context such as files, comments, ratings, etc.
    if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) {
        // Easy, do not delete the context itself...
        $coursecontext->delete_content();
    } else {
        // Hack alert!!!!
        // We can not drop all context stuff because it would bork enrolments and roles,
        // there might be also files used by enrol plugins...
    }
    // Delete legacy files - just in case some files are still left there after conversion to new file api,
    // also some non-standard unsupported plugins may try to store something there.
    fulldelete($CFG->dataroot . '/' . $course->id);
    // Delete from cache to reduce the cache size especially makes sense in case of bulk course deletion.
    $cachemodinfo = cache::make('core', 'coursemodinfo');
    $cachemodinfo->delete($courseid);
    // Trigger a course content deleted event.
    $event = \core\event\course_content_deleted::create(array('objectid' => $course->id, 'context' => $coursecontext, 'other' => array('shortname' => $course->shortname, 'fullname' => $course->fullname, 'options' => $options)));
    $event->add_record_snapshot('course', $course);
    $event->trigger();
    return true;
}
Exemplo n.º 2
0
Arquivo: lib.php Projeto: dg711/moodle
/**
 * Implements callback to reset course
 *
 * @param stdClass $data
 * @return boolean|array
 */
function wiki_reset_userdata($data)
{
    global $CFG, $DB;
    require_once $CFG->dirroot . '/mod/wiki/pagelib.php';
    require_once $CFG->dirroot . "/mod/wiki/locallib.php";
    $componentstr = get_string('modulenameplural', 'wiki');
    $status = array();
    //get the wiki(s) in this course.
    if (!($wikis = $DB->get_records('wiki', array('course' => $data->courseid)))) {
        return false;
    }
    if (empty($data->reset_wiki_comments) && empty($data->reset_wiki_tags) && empty($data->reset_wiki_pages)) {
        return $status;
    }
    foreach ($wikis as $wiki) {
        if (!($cm = get_coursemodule_from_instance('wiki', $wiki->id, $data->courseid))) {
            continue;
        }
        $context = context_module::instance($cm->id);
        // Remove tags or all pages.
        if (!empty($data->reset_wiki_pages) || !empty($data->reset_wiki_tags)) {
            // Get subwiki information.
            $subwikis = wiki_get_subwikis($wiki->id);
            foreach ($subwikis as $subwiki) {
                // Get existing pages.
                if ($pages = wiki_get_page_list($subwiki->id)) {
                    // If the wiki page isn't selected then we are only removing tags.
                    if (empty($data->reset_wiki_pages)) {
                        // Go through each page and delete the tags.
                        foreach ($pages as $page) {
                            core_tag_tag::remove_all_item_tags('mod_wiki', 'wiki_pages', $page->id);
                        }
                    } else {
                        // Otherwise we are removing pages and tags.
                        wiki_delete_pages($context, $pages, $subwiki->id);
                    }
                }
                if (!empty($data->reset_wiki_pages)) {
                    // Delete any subwikis.
                    $DB->delete_records('wiki_subwikis', array('id' => $subwiki->id), IGNORE_MISSING);
                    // Delete any attached files.
                    $fs = get_file_storage();
                    $fs->delete_area_files($context->id, 'mod_wiki', 'attachments');
                }
            }
            if (!empty($data->reset_wiki_pages)) {
                $status[] = array('component' => $componentstr, 'item' => get_string('deleteallpages', 'wiki'), 'error' => false);
            }
            if (!empty($data->reset_wiki_tags)) {
                $status[] = array('component' => $componentstr, 'item' => get_string('tagsdeleted', 'wiki'), 'error' => false);
            }
        }
        // Remove all comments.
        if (!empty($data->reset_wiki_comments) || !empty($data->reset_wiki_pages)) {
            $DB->delete_records_select('comments', "contextid = ? AND commentarea='wiki_page'", array($context->id));
            if (!empty($data->reset_wiki_comments)) {
                $status[] = array('component' => $componentstr, 'item' => get_string('deleteallcomments'), 'error' => false);
            }
        }
    }
    return $status;
}
Exemplo n.º 3
0
/**
 * This function will handle the whole deletion process of a module. This includes calling
 * the modules delete_instance function, deleting files, events, grades, conditional data,
 * the data in the course_module and course_sections table and adding a module deletion
 * event to the DB.
 *
 * @param int $cmid the course module id
 * @since Moodle 2.5
 */
function course_delete_module($cmid)
{
    global $CFG, $DB;
    require_once $CFG->libdir . '/gradelib.php';
    require_once $CFG->libdir . '/questionlib.php';
    require_once $CFG->dirroot . '/blog/lib.php';
    require_once $CFG->dirroot . '/calendar/lib.php';
    // Get the course module.
    if (!($cm = $DB->get_record('course_modules', array('id' => $cmid)))) {
        return true;
    }
    // Get the module context.
    $modcontext = context_module::instance($cm->id);
    // Get the course module name.
    $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module), MUST_EXIST);
    // Get the file location of the delete_instance function for this module.
    $modlib = "{$CFG->dirroot}/mod/{$modulename}/lib.php";
    // Include the file required to call the delete_instance function for this module.
    if (file_exists($modlib)) {
        require_once $modlib;
    } else {
        throw new moodle_exception('cannotdeletemodulemissinglib', '', '', null, "Cannot delete this module as the file mod/{$modulename}/lib.php is missing.");
    }
    $deleteinstancefunction = $modulename . '_delete_instance';
    // Ensure the delete_instance function exists for this module.
    if (!function_exists($deleteinstancefunction)) {
        throw new moodle_exception('cannotdeletemodulemissingfunc', '', '', null, "Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/{$modulename}/lib.php.");
    }
    // Delete activity context questions and question categories.
    question_delete_activity($cm);
    // Call the delete_instance function, if it returns false throw an exception.
    if (!$deleteinstancefunction($cm->instance)) {
        throw new moodle_exception('cannotdeletemoduleinstance', '', '', null, "Cannot delete the module {$modulename} (instance).");
    }
    // Remove all module files in case modules forget to do that.
    $fs = get_file_storage();
    $fs->delete_area_files($modcontext->id);
    // Delete events from calendar.
    if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename))) {
        foreach ($events as $event) {
            $calendarevent = calendar_event::load($event->id);
            $calendarevent->delete();
        }
    }
    // Delete grade items, outcome items and grades attached to modules.
    if ($grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $modulename, 'iteminstance' => $cm->instance, 'courseid' => $cm->course))) {
        foreach ($grade_items as $grade_item) {
            $grade_item->delete('moddelete');
        }
    }
    // Delete completion and availability data; it is better to do this even if the
    // features are not turned on, in case they were turned on previously (these will be
    // very quick on an empty table).
    $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
    $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id, 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
    // Delete all tag instances associated with the instance of this module.
    core_tag_tag::delete_instances('mod_' . $modulename, null, $modcontext->id);
    core_tag_tag::remove_all_item_tags('core', 'course_modules', $cm->id);
    // Delete the context.
    context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
    // Delete the module from the course_modules table.
    $DB->delete_records('course_modules', array('id' => $cm->id));
    // Delete module from that section.
    if (!delete_mod_from_section($cm->id, $cm->section)) {
        throw new moodle_exception('cannotdeletemodulefromsection', '', '', null, "Cannot delete the module {$modulename} (instance) from section.");
    }
    // Trigger event for course module delete action.
    $event = \core\event\course_module_deleted::create(array('courseid' => $cm->course, 'context' => $modcontext, 'objectid' => $cm->id, 'other' => array('modulename' => $modulename, 'instanceid' => $cm->instance)));
    $event->add_record_snapshot('course_modules', $cm);
    $event->trigger();
    rebuild_course_cache($cm->course, true);
}
Exemplo n.º 4
0
 /**
  * Deletes this entry from the database. Access control checks must be done by calling code.
  *
  * @return void
  */
 public function delete()
 {
     global $DB;
     $this->delete_attachments();
     $this->remove_associations();
     // Get record to pass onto the event.
     $record = $DB->get_record('post', array('id' => $this->id));
     $DB->delete_records('post', array('id' => $this->id));
     core_tag_tag::remove_all_item_tags('core', 'post', $this->id);
     $event = \core\event\blog_entry_deleted::create(array('objectid' => $this->id, 'relateduserid' => $this->userid));
     $event->add_record_snapshot("post", $record);
     $event->set_blog_entry($this);
     $event->trigger();
 }
Exemplo n.º 5
0
/**
 * Course tagging function used only during the deletion of a course (called by lib/moodlelib.php) to clean up associated tags
 *
 * @package core_tag
 * @deprecated since 3.0
 * @param   int      $courseid     the course we wish to delete tag instances from
 * @param   bool     $showfeedback if we should output a notification of the delete to the end user
 */
function coursetag_delete_course_tags($courseid, $showfeedback = false)
{
    debugging('Function coursetag_delete_course_tags() is deprecated. Use core_tag_tag::remove_all_item_tags().', DEBUG_DEVELOPER);
    global $OUTPUT;
    core_tag_tag::remove_all_item_tags('core', 'course', $courseid);
    if ($showfeedback) {
        echo $OUTPUT->notification(get_string('deletedcoursetags', 'tag'), 'notifysuccess');
    }
}
Exemplo n.º 6
0
/**
 * Delete pages and all related data
 *
 * @param mixed $context context in which page needs to be deleted.
 * @param mixed $pageids id's of pages to be deleted
 * @param int $subwikiid id of the subwiki for which all pages should be deleted
 */
function wiki_delete_pages($context, $pageids = null, $subwikiid = null)
{
    global $DB, $CFG;
    if (!empty($pageids) && is_int($pageids)) {
        $pageids = array($pageids);
    } else {
        if (!empty($subwikiid)) {
            $pageids = wiki_get_page_list($subwikiid);
        }
    }
    //If there is no pageid then return as we can't delete anything.
    if (empty($pageids)) {
        return;
    }
    /// Delete page and all it's relevent data
    foreach ($pageids as $pageid) {
        if (is_object($pageid)) {
            $pageid = $pageid->id;
        }
        //Delete page comments
        $comments = wiki_get_comments($context->id, $pageid);
        foreach ($comments as $commentid => $commentvalue) {
            wiki_delete_comment($commentid, $context, $pageid);
        }
        //Delete page tags
        core_tag_tag::remove_all_item_tags('mod_wiki', 'wiki_pages', $pageid);
        //Delete Synonym
        wiki_delete_synonym($subwikiid, $pageid);
        //Delete all page versions
        wiki_delete_page_versions(array($pageid => array(0)), $context);
        //Delete all page locks
        wiki_delete_locks($pageid);
        //Delete all page links
        wiki_delete_links(null, $pageid);
        $params = array('id' => $pageid);
        // Get page before deleting.
        $page = $DB->get_record('wiki_pages', $params);
        //Delete page
        $DB->delete_records('wiki_pages', $params);
        // Trigger page_deleted event.
        $event = \mod_wiki\event\page_deleted::create(array('context' => $context, 'objectid' => $pageid, 'other' => array('subwikiid' => $subwikiid)));
        $event->add_record_snapshot('wiki_pages', $page);
        $event->trigger();
    }
}
Exemplo n.º 7
0
/**
 * Deletes question and all associated data from the database
 *
 * It will not delete a question if it is used by an activity module
 * @param object $question  The question being deleted
 */
function question_delete_question($questionid)
{
    global $DB;
    $question = $DB->get_record_sql('
            SELECT q.*, qc.contextid
            FROM {question} q
            JOIN {question_categories} qc ON qc.id = q.category
            WHERE q.id = ?', array($questionid));
    if (!$question) {
        // In some situations, for example if this was a child of a
        // Cloze question that was previously deleted, the question may already
        // have gone. In this case, just do nothing.
        return;
    }
    // Do not delete a question if it is used by an activity module
    if (questions_in_use(array($questionid))) {
        return;
    }
    $dm = new question_engine_data_mapper();
    $dm->delete_previews($questionid);
    // delete questiontype-specific data
    question_bank::get_qtype($question->qtype, false)->delete_question($questionid, $question->contextid);
    // Delete all tag instances.
    core_tag_tag::remove_all_item_tags('core_question', 'question', $question->id);
    // Now recursively delete all child questions
    if ($children = $DB->get_records('question', array('parent' => $questionid), '', 'id, qtype')) {
        foreach ($children as $child) {
            if ($child->id != $questionid) {
                question_delete_question($child->id);
            }
        }
    }
    // Finally delete the question record itself
    $DB->delete_records('question', array('id' => $questionid));
    question_bank::notify_question_edited($questionid);
}
Exemplo n.º 8
0
 /**
  * Test the tag deleted event
  */
 public function test_tag_deleted()
 {
     global $DB;
     $this->setAdminUser();
     // Create a course and a user.
     $course = $this->getDataGenerator()->create_course();
     $user = $this->getDataGenerator()->create_user();
     // Create tag we are going to delete.
     $tag = $this->getDataGenerator()->create_tag();
     // Trigger and capture the event for deleting a tag.
     $sink = $this->redirectEvents();
     tag_delete($tag->id);
     $this->assertDebuggingCalled();
     $events = $sink->get_events();
     $event = reset($events);
     // Check that the tag was deleted and the event data is valid.
     $this->assertEquals(0, $DB->count_records('tag'));
     $this->assertInstanceOf('\\core\\event\\tag_deleted', $event);
     $this->assertEquals(context_system::instance(), $event->get_context());
     // Create two tags we are going to delete to ensure passing multiple tags work.
     $tag = $this->getDataGenerator()->create_tag();
     $tag2 = $this->getDataGenerator()->create_tag();
     // Trigger and capture the events for deleting multiple tags.
     $sink = $this->redirectEvents();
     tag_delete(array($tag->id, $tag2->id));
     $this->assertDebuggingCalled();
     $events = $sink->get_events();
     // Check that the tags were deleted and the events data is valid.
     $this->assertEquals(0, $DB->count_records('tag'));
     foreach ($events as $event) {
         $this->assertInstanceOf('\\core\\event\\tag_deleted', $event);
         $this->assertEquals(context_system::instance(), $event->get_context());
     }
     // Add a tag instance to a course.
     core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'cat', $user->id);
     // Trigger and capture the event for deleting a personal tag for a user for a course.
     $sink = $this->redirectEvents();
     core_tag_tag::remove_item_tag('core', 'course', $course->id, 'cat', $user->id);
     $events = $sink->get_events();
     $event = $events[1];
     // Check that the tag was deleted and the event data is valid.
     $this->assertEquals(0, $DB->count_records('tag'));
     $this->assertInstanceOf('\\core\\event\\tag_deleted', $event);
     $this->assertEquals(context_system::instance(), $event->get_context());
     // Add the tag instance to the course again as it was deleted.
     core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'dog', $user->id);
     // Trigger and capture the event for deleting all tags in a course.
     $sink = $this->redirectEvents();
     core_tag_tag::remove_all_item_tags('core', 'course', $course->id);
     $events = $sink->get_events();
     $event = $events[1];
     // Check that the tag was deleted and the event data is valid.
     $this->assertEquals(0, $DB->count_records('tag'));
     $this->assertInstanceOf('\\core\\event\\tag_deleted', $event);
     $this->assertEquals(context_system::instance(), $event->get_context());
     // Add multiple tag instances now and check that it still works.
     core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), array('fish', 'hamster'), $user->id);
     // Trigger and capture the event for deleting all tags in a course.
     $sink = $this->redirectEvents();
     core_tag_tag::remove_all_item_tags('core', 'course', $course->id);
     $events = $sink->get_events();
     $events = array($events[1], $events[3]);
     // Check that the tags were deleted and the events data is valid.
     $this->assertEquals(0, $DB->count_records('tag'));
     foreach ($events as $event) {
         $this->assertInstanceOf('\\core\\event\\tag_deleted', $event);
         $this->assertEquals(context_system::instance(), $event->get_context());
     }
 }
Exemplo n.º 9
0
/**
 * This function will handle the whole deletion process of a module. This includes calling
 * the modules delete_instance function, deleting files, events, grades, conditional data,
 * the data in the course_module and course_sections table and adding a module deletion
 * event to the DB.
 *
 * @param int $cmid the course module id
 * @param bool $async whether or not to try to delete the module using an adhoc task. Async also depends on a plugin hook.
 * @throws moodle_exception
 * @since Moodle 2.5
 */
function course_delete_module($cmid, $async = false)
{
    // Check the 'course_module_background_deletion_recommended' hook first.
    // Only use asynchronous deletion if at least one plugin returns true and if async deletion has been requested.
    // Both are checked because plugins should not be allowed to dictate the deletion behaviour, only support/decline it.
    // It's up to plugins to handle things like whether or not they are enabled.
    if ($async && ($pluginsfunction = get_plugins_with_function('course_module_background_deletion_recommended'))) {
        foreach ($pluginsfunction as $plugintype => $plugins) {
            foreach ($plugins as $pluginfunction) {
                if ($pluginfunction()) {
                    return course_module_flag_for_async_deletion($cmid);
                }
            }
        }
    }
    global $CFG, $DB;
    require_once $CFG->libdir . '/gradelib.php';
    require_once $CFG->libdir . '/questionlib.php';
    require_once $CFG->dirroot . '/blog/lib.php';
    require_once $CFG->dirroot . '/calendar/lib.php';
    // Get the course module.
    if (!($cm = $DB->get_record('course_modules', array('id' => $cmid)))) {
        return true;
    }
    // Get the module context.
    $modcontext = context_module::instance($cm->id);
    // Get the course module name.
    $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module), MUST_EXIST);
    // Get the file location of the delete_instance function for this module.
    $modlib = "{$CFG->dirroot}/mod/{$modulename}/lib.php";
    // Include the file required to call the delete_instance function for this module.
    if (file_exists($modlib)) {
        require_once $modlib;
    } else {
        throw new moodle_exception('cannotdeletemodulemissinglib', '', '', null, "Cannot delete this module as the file mod/{$modulename}/lib.php is missing.");
    }
    $deleteinstancefunction = $modulename . '_delete_instance';
    // Ensure the delete_instance function exists for this module.
    if (!function_exists($deleteinstancefunction)) {
        throw new moodle_exception('cannotdeletemodulemissingfunc', '', '', null, "Cannot delete this module as the function {$modulename}_delete_instance is missing in mod/{$modulename}/lib.php.");
    }
    // Allow plugins to use this course module before we completely delete it.
    if ($pluginsfunction = get_plugins_with_function('pre_course_module_delete')) {
        foreach ($pluginsfunction as $plugintype => $plugins) {
            foreach ($plugins as $pluginfunction) {
                $pluginfunction($cm);
            }
        }
    }
    // Delete activity context questions and question categories.
    question_delete_activity($cm);
    // Call the delete_instance function, if it returns false throw an exception.
    if (!$deleteinstancefunction($cm->instance)) {
        throw new moodle_exception('cannotdeletemoduleinstance', '', '', null, "Cannot delete the module {$modulename} (instance).");
    }
    // Remove all module files in case modules forget to do that.
    $fs = get_file_storage();
    $fs->delete_area_files($modcontext->id);
    // Delete events from calendar.
    if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename))) {
        foreach ($events as $event) {
            $calendarevent = calendar_event::load($event->id);
            $calendarevent->delete();
        }
    }
    // Delete grade items, outcome items and grades attached to modules.
    if ($grade_items = grade_item::fetch_all(array('itemtype' => 'mod', 'itemmodule' => $modulename, 'iteminstance' => $cm->instance, 'courseid' => $cm->course))) {
        foreach ($grade_items as $grade_item) {
            $grade_item->delete('moddelete');
        }
    }
    // Delete completion and availability data; it is better to do this even if the
    // features are not turned on, in case they were turned on previously (these will be
    // very quick on an empty table).
    $DB->delete_records('course_modules_completion', array('coursemoduleid' => $cm->id));
    $DB->delete_records('course_completion_criteria', array('moduleinstance' => $cm->id, 'course' => $cm->course, 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY));
    // Delete all tag instances associated with the instance of this module.
    core_tag_tag::delete_instances('mod_' . $modulename, null, $modcontext->id);
    core_tag_tag::remove_all_item_tags('core', 'course_modules', $cm->id);
    // Notify the competency subsystem.
    \core_competency\api::hook_course_module_deleted($cm);
    // Delete the context.
    context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
    // Delete the module from the course_modules table.
    $DB->delete_records('course_modules', array('id' => $cm->id));
    // Delete module from that section.
    if (!delete_mod_from_section($cm->id, $cm->section)) {
        throw new moodle_exception('cannotdeletemodulefromsection', '', '', null, "Cannot delete the module {$modulename} (instance) from section.");
    }
    // Trigger event for course module delete action.
    $event = \core\event\course_module_deleted::create(array('courseid' => $cm->course, 'context' => $modcontext, 'objectid' => $cm->id, 'other' => array('modulename' => $modulename, 'instanceid' => $cm->instance)));
    $event->add_record_snapshot('course_modules', $cm);
    $event->trigger();
    rebuild_course_cache($cm->course, true);
}