function xmldb_enrol_delayedcohort_uninstall() { global $CFG, $DB; $cohort = enrol_get_plugin('delayedcohort'); $rs = $DB->get_recordset('enrol', array('enrol' => 'delayedcohort')); foreach ($rs as $instance) { $cohort->delete_instance($instance); } $rs->close(); role_unassign_all(array('component' => 'enrol_delayedcohort')); return true; }
function xmldb_enrol_metaplus_uninstall() { global $CFG, $DB; $meta = enrol_get_plugin('metaplus'); $rs = $DB->get_recordset('enrol', array('enrol' => 'metaplus')); foreach ($rs as $instance) { $meta->delete_instance($instance); } $rs->close(); role_unassign_all(array('component' => 'enrol_metaplus')); return true; }
/** * Forces synchronisation of all enrolments with external database. * * @return void */ public function sync_enrolments() { global $CFG, $DB; // we do not create courses here intentionally because it requires full sync and is slow if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) { return; } // we may need a lot of memory here @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); $extdb = $this->db_init(); // second step is to sync instances and users $table = $this->get_config('remoteenroltable'); $coursefield = strtolower($this->get_config('remotecoursefield')); $userfield = strtolower($this->get_config('remoteuserfield')); $rolefield = strtolower($this->get_config('remoterolefield')); $localrolefield = $this->get_config('localrolefield'); $localuserfield = $this->get_config('localuserfield'); $localcoursefiled = $this->get_config('localcoursefield'); $unenrolaction = $this->get_config('unenrolaction'); $defaultrole = $this->get_config('defaultrole'); // create roles mapping $allroles = get_all_roles(); if (!isset($allroles[$defaultrole])) { $defaultrole = 0; } $roles = array(); foreach ($allroles as $role) { $roles[$role->{$localrolefield}] = $role->id; } // first find all existing courses with enrol instance $sql = "SELECT c.id, c.visible, c.{$localcoursefiled} AS mapping, e.id AS enrolid\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')"; $existing = array(); $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates foreach ($rs as $course) { if (empty($course->mapping)) { continue; } $existing[$course->mapping] = $course; } $rs->close(); // add necessary enrol instances that are not present yet; $sql = $this->db_get_sql($table, array(), array($coursefield), true); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { $sql = "SELECT c.id, c.visible\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')\n WHERE c.{$localcoursefiled} = :mapping"; $params = array(); while ($mapping = $rs->FetchRow()) { $mapping = reset($mapping); $mapping = $this->db_decode($mapping); if (!empty($mapping) and !isset($existing[$mapping])) { $params['mapping'] = $mapping; if ($course = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE)) { $new = new stdClass(); $new->id = $course->id; $new->visible = $course->visible; $new->mapping = $mapping; $new->enrolid = $this->add_instance($course); $existing[$mapping] = $new; } } } } $rs->Close(); } else { debugging('Error while communicating with external enrolment database'); $extdb->Close(); return; } // sync enrolments $ignorehidden = $this->get_config('ignorehiddencourses'); $fields = array($userfield); if ($rolefield) { $fields[] = $rolefield; } foreach ($existing as $course) { if ($ignorehidden and !$course->visible) { continue; } if (!($instance = $DB->get_record('enrol', array('id' => $course->enrolid)))) { continue; //weird } $context = get_context_instance(CONTEXT_COURSE, $course->id); // get current list of enrolled users with their roles $current_roles = array(); $current_status = array(); $user_mapping = array(); $sql = "SELECT u.{$localuserfield} AS mapping, u.id, ue.status, ue.userid, ra.roleid\n FROM {user} u\n JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)\n JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')\n WHERE u.deleted = 0"; $params = array('enrolid' => $instance->id); if ($localuserfield === 'username') { $sql .= " AND u.mnethostid = :mnethostid"; $params['mnethostid'] = $CFG->mnet_localhost_id; } $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { $current_roles[$ue->userid][$ue->roleid] = $ue->roleid; $current_status[$ue->userid] = $ue->status; $user_mapping[$ue->mapping] = $ue->userid; } $rs->close(); // get list of users that need to be enrolled and their roles $requested_roles = array(); $sql = $this->db_get_sql($table, array($coursefield => $course->mapping), $fields); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { if ($localuserfield === 'username') { $usersearch = array('mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 0); } while ($fields = $rs->FetchRow()) { $fields = array_change_key_case($fields, CASE_LOWER); if (empty($fields[$userfield])) { //user identification is mandatory! } $mapping = $fields[$userfield]; if (!isset($user_mapping[$mapping])) { $usersearch[$localuserfield] = $mapping; if (!($user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE))) { // user does not exist or was deleted continue; } $user_mapping[$mapping] = $user->id; $userid = $user->id; } else { $userid = $user_mapping[$mapping]; } if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) { if (!$defaultrole) { // role is mandatory continue; } $roleid = $defaultrole; } else { $roleid = $roles[$fields[$rolefield]]; } $requested_roles[$userid][$roleid] = $roleid; } } $rs->Close(); } else { debugging('Error while communicating with external enrolment database'); $extdb->Close(); return; } unset($user_mapping); // enrol all users and sync roles foreach ($requested_roles as $userid => $roles) { foreach ($roles as $roleid) { if (empty($current_roles[$userid])) { $this->enrol_user($instance, $userid, $roleid); $current_roles[$userid][$roleid] = $roleid; $current_status[$userid] = ENROL_USER_ACTIVE; } } // unassign removed roles foreach ($current_roles[$userid] as $cr) { if (empty($roles[$cr])) { role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id); unset($current_roles[$userid][$cr]); } } // reenable enrolment when previously disable enrolment refreshed if ($current_status[$userid] == ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid' => $instance->id, 'userid' => $userid)); } } // deal with enrolments removed from external table if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { // unenrol if (!empty($requested_roles)) { // we might get some error or connection problem, better not unenrol everybody foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } $this->unenrol_user($instance, $userid); } } } else { if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) { // keep - only adding enrolments } else { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { // disable foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } if ($status != ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid' => $instance->id, 'userid' => $userid)); } if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { role_unassign_all(array('contextid' => $context->id, 'userid' => $userid, 'component' => 'enrol_database', 'itemid' => $instance->id)); } } } } } } // close db connection $extdb->Close(); }
/** * Sync all meta course links. * * @param progress_trace $trace * @param int $courseid one course, empty mean all * @return int 0 means ok, 1 means error, 2 means plugin disabled */ public function sync(progress_trace $trace, $courseid = null) { global $DB; if (!enrol_is_enabled('manual')) { $trace->finished(); return 2; } // Unfortunately this may take a long time, execution can be interrupted safely here. @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); $trace->output('Verifying manual enrolment expiration...'); $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE); $coursesql = ""; if ($courseid) { $coursesql = "AND e.courseid = :courseid"; $params['courseid'] = $courseid; } // Deal with expired accounts. $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); if ($action == ENROL_EXT_REMOVED_UNENROL) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); $this->unenrol_user($instance, $ue->userid); $trace->output("unenrolling expired user {$ue->userid} from course {$instance->courseid}", 1); } $rs->close(); unset($instances); } else { if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n AND ue.status = :useractive\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); $trace->output("suspending expired user {$ue->userid} in course {$instance->courseid}", 1); } $rs->close(); unset($instances); } else { // ENROL_EXT_REMOVED_KEEP means no changes. } } $trace->output('...manual enrolment updates finished.'); $trace->finished(); return 0; }
/** * Forces synchronisation of all enrolments with external database. * * @param bool $verbose * @param null|int $onecourse limit sync to one course only (used primarily in restore) * @return int 0 means success, 1 db connect failure, 2 db read failure */ public function sync_enrolments($verbose = false, $onecourse = null) { global $CFG, $DB; // We do not create courses here intentionally because it requires full sync and is slow. if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) { if ($verbose) { mtrace('User enrolment synchronisation skipped.'); } return 0; } if ($verbose) { mtrace('Starting user enrolment synchronisation...'); } if (!($extdb = $this->db_init())) { mtrace('Error while communicating with external enrolment database'); return 1; } // We may need a lot of memory here. @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); $table = $this->get_config('remoteenroltable'); $coursefield = trim($this->get_config('remotecoursefield')); $userfield = trim($this->get_config('remoteuserfield')); $rolefield = trim($this->get_config('remoterolefield')); // Lowercased versions - necessary because we normalise the resultset with array_change_key_case(). $coursefield_l = strtolower($coursefield); $userfield_l = strtolower($userfield); $rolefield_l = strtolower($rolefield); $localrolefield = $this->get_config('localrolefield'); $localuserfield = $this->get_config('localuserfield'); $localcoursefield = $this->get_config('localcoursefield'); $unenrolaction = $this->get_config('unenrolaction'); $defaultrole = $this->get_config('defaultrole'); // Create roles mapping. $allroles = get_all_roles(); if (!isset($allroles[$defaultrole])) { $defaultrole = 0; } $roles = array(); foreach ($allroles as $role) { $roles[$role->{$localrolefield}] = $role->id; } if ($onecourse) { $sql = "SELECT c.id, c.visible, c.{$localcoursefield} AS mapping, c.shortname, e.id AS enrolid\n FROM {course} c\n LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')\n WHERE c.id = :id"; if (!($course = $DB->get_record_sql($sql, array('id' => $onecourse)))) { // Course does not exist, nothing to sync. return 0; } if (empty($course->mapping)) { // We can not map to this course, sorry. return 0; } if (empty($course->enrolid)) { $course->enrolid = $this->add_instance($course); } $existing = array($course->mapping => $course); // Feel free to unenrol everybody, no safety tricks here. $preventfullunenrol = false; // Course being restored are always hidden, we have to ignore the setting here. $ignorehidden = false; } else { // Get a list of courses to be synced that are in external table. $externalcourses = array(); $sql = $this->db_get_sql($table, array(), array($coursefield), true); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { while ($mapping = $rs->FetchRow()) { $mapping = reset($mapping); $mapping = $this->db_decode($mapping); if (empty($mapping)) { // invalid mapping continue; } $externalcourses[$mapping] = true; } } $rs->Close(); } else { mtrace('Error reading data from the external enrolment table'); $extdb->Close(); return 2; } $preventfullunenrol = empty($externalcourses); if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) { mtrace(' Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.'); } // First find all existing courses with enrol instance. $existing = array(); $sql = "SELECT c.id, c.visible, c.{$localcoursefield} AS mapping, e.id AS enrolid, c.shortname\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')"; $rs = $DB->get_recordset_sql($sql); // Watch out for idnumber duplicates. foreach ($rs as $course) { if (empty($course->mapping)) { continue; } $existing[$course->mapping] = $course; unset($externalcourses[$course->mapping]); } $rs->close(); // Add necessary enrol instances that are not present yet. $params = array(); $localnotempty = ""; if ($localcoursefield !== 'id') { $localnotempty = "AND c.{$localcoursefield} <> :lcfe"; $params['lcfe'] = $DB->sql_empty(); } $sql = "SELECT c.id, c.visible, c.{$localcoursefield} AS mapping, c.shortname\n FROM {course} c\n LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')\n WHERE e.id IS NULL {$localnotempty}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $course) { if (empty($course->mapping)) { continue; } if (!isset($externalcourses[$course->mapping])) { // Course not synced or duplicate. continue; } $course->enrolid = $this->add_instance($course); $existing[$course->mapping] = $course; unset($externalcourses[$course->mapping]); } $rs->close(); // Print list of missing courses. if ($verbose and $externalcourses) { $list = implode(', ', array_keys($externalcourses)); mtrace(" error: following courses do not exist - {$list}"); unset($list); } // Free memory. unset($externalcourses); $ignorehidden = $this->get_config('ignorehiddencourses'); } // Sync user enrolments. $sqlfields = array($userfield); if ($rolefield) { $sqlfields[] = $rolefield; } foreach ($existing as $course) { if ($ignorehidden and !$course->visible) { continue; } if (!($instance = $DB->get_record('enrol', array('id' => $course->enrolid)))) { continue; // Weird! } $context = context_course::instance($course->id); // Get current list of enrolled users with their roles. $current_roles = array(); $current_status = array(); $user_mapping = array(); $sql = "SELECT u.{$localuserfield} AS mapping, u.id, ue.status, ue.userid, ra.roleid\n FROM {user} u\n JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)\n JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')\n WHERE u.deleted = 0"; $params = array('enrolid' => $instance->id); if ($localuserfield === 'username') { $sql .= " AND u.mnethostid = :mnethostid"; $params['mnethostid'] = $CFG->mnet_localhost_id; } $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { $current_roles[$ue->userid][$ue->roleid] = $ue->roleid; $current_status[$ue->userid] = $ue->status; $user_mapping[$ue->mapping] = $ue->userid; } $rs->close(); // Get list of users that need to be enrolled and their roles. $requested_roles = array(); $sql = $this->db_get_sql($table, array($coursefield => $course->mapping), $sqlfields); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { $usersearch = array('deleted' => 0); if ($localuserfield === 'username') { $usersearch['mnethostid'] = $CFG->mnet_localhost_id; } while ($fields = $rs->FetchRow()) { $fields = array_change_key_case($fields, CASE_LOWER); if (empty($fields[$userfield_l])) { if ($verbose) { mtrace(" error: skipping user without mandatory {$localuserfield} in course '{$course->mapping}'"); } continue; } $mapping = $fields[$userfield_l]; if (!isset($user_mapping[$mapping])) { $usersearch[$localuserfield] = $mapping; if (!($user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE))) { if ($verbose) { mtrace(" error: skipping unknown user {$localuserfield} '{$mapping}' in course '{$course->mapping}'"); } continue; } $user_mapping[$mapping] = $user->id; $userid = $user->id; } else { $userid = $user_mapping[$mapping]; } if (empty($fields[$rolefield_l]) or !isset($roles[$fields[$rolefield_l]])) { if (!$defaultrole) { if ($verbose) { mtrace(" error: skipping user '{$userid}' in course '{$course->mapping}' - missing course and default role"); } continue; } $roleid = $defaultrole; } else { $roleid = $roles[$fields[$rolefield_l]]; } $requested_roles[$userid][$roleid] = $roleid; } } $rs->Close(); } else { mtrace(" error: skipping course '{$course->mapping}' - could not match with external database"); continue; } unset($user_mapping); // Enrol all users and sync roles. foreach ($requested_roles as $userid => $userroles) { foreach ($userroles as $roleid) { if (empty($current_roles[$userid])) { $this->enrol_user($instance, $userid, $roleid, 0, 0, ENROL_USER_ACTIVE); $current_roles[$userid][$roleid] = $roleid; $current_status[$userid] = ENROL_USER_ACTIVE; if ($verbose) { mtrace(" enrolling: {$userid} ==> {$course->shortname} as " . $allroles[$roleid]->shortname); } } } // Assign extra roles. foreach ($userroles as $roleid) { if (empty($current_roles[$userid][$roleid])) { role_assign($roleid, $userid, $context->id, 'enrol_database', $instance->id); $current_roles[$userid][$roleid] = $roleid; if ($verbose) { mtrace(" assigning roles: {$userid} ==> {$course->shortname} as " . $allroles[$roleid]->shortname); } } } // Unassign removed roles. foreach ($current_roles[$userid] as $cr) { if (empty($userroles[$cr])) { role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id); unset($current_roles[$userid][$cr]); if ($verbose) { mtrace(" unsassigning roles: {$userid} ==> {$course->shortname}"); } } } // Reenable enrolment when previously disable enrolment refreshed. if ($current_status[$userid] == ENROL_USER_SUSPENDED) { $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE); if ($verbose) { mtrace(" unsuspending: {$userid} ==> {$course->shortname}"); } } } // Deal with enrolments removed from external table. if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { if (!$preventfullunenrol) { // Unenrol. foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } $this->unenrol_user($instance, $userid); if ($verbose) { mtrace(" unenrolling: {$userid} ==> {$course->shortname}"); } } } } else { if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) { // Keep - only adding enrolments. } else { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { // Suspend enrolments. foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } if ($status != ENROL_USER_SUSPENDED) { $this->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED); if ($verbose) { mtrace(" suspending: {$userid} ==> {$course->shortname}"); } } if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { role_unassign_all(array('contextid' => $context->id, 'userid' => $userid, 'component' => 'enrol_database', 'itemid' => $instance->id)); if ($verbose) { mtrace(" unsassigning all roles: {$userid} ==> {$course->shortname}"); } } } } } } } // Close db connection. $extdb->Close(); if ($verbose) { mtrace('...user enrolment synchronisation finished.'); } return 0; }
/** * This function will empty a course of user data. * It will retain the activities and the structure of the course. * * @param object $data an object containing all the settings including courseid (without magic quotes) * @return array status array of array component, item, error */ function reset_course_userdata($data) { global $CFG, $DB; require_once $CFG->libdir . '/gradelib.php'; require_once $CFG->libdir . '/completionlib.php'; require_once $CFG->dirroot . '/group/lib.php'; $data->courseid = $data->id; $context = context_course::instance($data->courseid); $eventparams = array('context' => $context, 'courseid' => $data->id, 'other' => array('reset_options' => (array) $data)); $event = \core\event\course_reset_started::create($eventparams); $event->trigger(); // Calculate the time shift of dates. if (!empty($data->reset_start_date)) { // Time part of course startdate should be zero. $data->timeshift = $data->reset_start_date - usergetmidnight($data->reset_start_date_old); } else { $data->timeshift = 0; } // Result array: component, item, error. $status = array(); // Start the resetting. $componentstr = get_string('general'); // Move the course start time. if (!empty($data->reset_start_date) and $data->timeshift) { // Change course start data. $DB->set_field('course', 'startdate', $data->reset_start_date, array('id' => $data->courseid)); // Update all course and group events - do not move activity events. $updatesql = "UPDATE {event}\n SET timestart = timestart + ?\n WHERE courseid=? AND instance=0"; $DB->execute($updatesql, array($data->timeshift, $data->courseid)); // Update any date activity restrictions. if ($CFG->enableavailability) { \availability_date\condition::update_all_dates($data->courseid, $data->timeshift); } $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => false); } if (!empty($data->reset_end_date)) { // If the user set a end date value respect it. $DB->set_field('course', 'enddate', $data->reset_end_date, array('id' => $data->courseid)); } else { if ($data->timeshift > 0 && $data->reset_end_date_old) { // If there is a time shift apply it to the end date as well. $enddate = $data->reset_end_date_old + $data->timeshift; $DB->set_field('course', 'enddate', $enddate, array('id' => $data->courseid)); } } if (!empty($data->reset_events)) { $DB->delete_records('event', array('courseid' => $data->courseid)); $status[] = array('component' => $componentstr, 'item' => get_string('deleteevents', 'calendar'), 'error' => false); } if (!empty($data->reset_notes)) { require_once $CFG->dirroot . '/notes/lib.php'; note_delete_all($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('deletenotes', 'notes'), 'error' => false); } if (!empty($data->delete_blog_associations)) { require_once $CFG->dirroot . '/blog/lib.php'; blog_remove_associations_for_course($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('deleteblogassociations', 'blog'), 'error' => false); } if (!empty($data->reset_completion)) { // Delete course and activity completion information. $course = $DB->get_record('course', array('id' => $data->courseid)); $cc = new completion_info($course); $cc->delete_all_completion_data(); $status[] = array('component' => $componentstr, 'item' => get_string('deletecompletiondata', 'completion'), 'error' => false); } if (!empty($data->reset_competency_ratings)) { \core_competency\api::hook_course_reset_competency_ratings($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('deletecompetencyratings', 'core_competency'), 'error' => false); } $componentstr = get_string('roles'); if (!empty($data->reset_roles_overrides)) { $children = $context->get_child_contexts(); foreach ($children as $child) { $DB->delete_records('role_capabilities', array('contextid' => $child->id)); } $DB->delete_records('role_capabilities', array('contextid' => $context->id)); // Force refresh for logged in users. $context->mark_dirty(); $status[] = array('component' => $componentstr, 'item' => get_string('deletecourseoverrides', 'role'), 'error' => false); } if (!empty($data->reset_roles_local)) { $children = $context->get_child_contexts(); foreach ($children as $child) { role_unassign_all(array('contextid' => $child->id)); } // Force refresh for logged in users. $context->mark_dirty(); $status[] = array('component' => $componentstr, 'item' => get_string('deletelocalroles', 'role'), 'error' => false); } // First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc. $data->unenrolled = array(); if (!empty($data->unenrol_users)) { $plugins = enrol_get_plugins(true); $instances = enrol_get_instances($data->courseid, true); foreach ($instances as $key => $instance) { if (!isset($plugins[$instance->enrol])) { unset($instances[$key]); continue; } } foreach ($data->unenrol_users as $withroleid) { if ($withroleid) { $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)\n JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.roleid = :roleid AND ra.userid = ue.userid)"; $params = array('courseid' => $data->courseid, 'roleid' => $withroleid, 'courselevel' => CONTEXT_COURSE); } else { // Without any role assigned at course context. $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)\n LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = ue.userid)\n WHERE ra.id IS null"; $params = array('courseid' => $data->courseid, 'courselevel' => CONTEXT_COURSE); } $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { 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); $data->unenrolled[$ue->userid] = $ue->userid; } $rs->close(); } } if (!empty($data->unenrolled)) { $status[] = array('component' => $componentstr, 'item' => get_string('unenrol', 'enrol') . ' (' . count($data->unenrolled) . ')', 'error' => false); } $componentstr = get_string('groups'); // Remove all group members. if (!empty($data->reset_groups_members)) { groups_delete_group_members($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removegroupsmembers', 'group'), 'error' => false); } // Remove all groups. if (!empty($data->reset_groups_remove)) { groups_delete_groups($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('deleteallgroups', 'group'), 'error' => false); } // Remove all grouping members. if (!empty($data->reset_groupings_members)) { groups_delete_groupings_groups($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('removegroupingsmembers', 'group'), 'error' => false); } // Remove all groupings. if (!empty($data->reset_groupings_remove)) { groups_delete_groupings($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('deleteallgroupings', 'group'), 'error' => false); } // Look in every instance of every module for data to delete. $unsupportedmods = array(); if ($allmods = $DB->get_records('modules')) { foreach ($allmods as $mod) { $modname = $mod->name; $modfile = $CFG->dirroot . '/mod/' . $modname . '/lib.php'; $moddeleteuserdata = $modname . '_reset_userdata'; // Function to delete user data. if (file_exists($modfile)) { if (!$DB->count_records($modname, array('course' => $data->courseid))) { continue; // Skip mods with no instances. } include_once $modfile; if (function_exists($moddeleteuserdata)) { $modstatus = $moddeleteuserdata($data); if (is_array($modstatus)) { $status = array_merge($status, $modstatus); } else { debugging('Module ' . $modname . ' returned incorrect staus - must be an array!'); } } else { $unsupportedmods[] = $mod; } } else { debugging('Missing lib.php in ' . $modname . ' module!'); } } } // Mention unsupported mods. if (!empty($unsupportedmods)) { foreach ($unsupportedmods as $mod) { $status[] = array('component' => get_string('modulenameplural', $mod->name), 'item' => '', 'error' => get_string('resetnotimplemented')); } } $componentstr = get_string('gradebook', 'grades'); // Reset gradebook,. if (!empty($data->reset_gradebook_items)) { remove_course_grades($data->courseid, false); grade_grab_course_grades($data->courseid); grade_regrade_final_grades($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removeallcourseitems', 'grades'), 'error' => false); } else { if (!empty($data->reset_gradebook_grades)) { grade_course_reset($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removeallcoursegrades', 'grades'), 'error' => false); } } // Reset comments. if (!empty($data->reset_comments)) { require_once $CFG->dirroot . '/comment/lib.php'; comment::reset_course_page_comments($context); } $event = \core\event\course_reset_ended::create($eventparams); $event->trigger(); return $status; }
/** * Forces synchronisation of all enrolments with LDAP server. * It creates courses if the plugin is configured to do so. * * @param progress_trace $trace * @param int|null $onecourse limit sync to one course->id, null if all courses * @return void */ public function sync_enrolments(progress_trace $trace, $onecourse = null) { global $CFG, $DB; if (!$this->ldap_connect($trace)) { $trace->finished(); return; } $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version')); // we may need a lot of memory here core_php_time_limit::raise(); raise_memory_limit(MEMORY_HUGE); $oneidnumber = null; if ($onecourse) { if (!($course = $DB->get_record('course', array('id' => $onecourse), 'id,' . $this->enrol_localcoursefield))) { // Course does not exist, nothing to do. $trace->output("Requested course {$onecourse} does not exist, no sync performed."); $trace->finished(); return; } if (empty($course->{$this->enrol_localcoursefield})) { $trace->output("Requested course {$onecourse} does not have {$this->enrol_localcoursefield}, no sync performed."); $trace->finished(); return; } $oneidnumber = ldap_filter_addslashes(core_text::convert($course->idnumber, 'utf-8', $this->get_config('ldapencoding'))); } // Get enrolments for each type of role. $roles = get_all_roles(); $enrolments = array(); foreach ($roles as $role) { // Get all contexts $ldap_contexts = explode(';', $this->config->{'contexts_role' . $role->id}); // Get all the fields we will want for the potential course creation // as they are light. Don't get membership -- potentially a lot of data. $ldap_fields_wanted = array('dn', $this->config->course_idnumber); if (!empty($this->config->course_fullname)) { array_push($ldap_fields_wanted, $this->config->course_fullname); } if (!empty($this->config->course_shortname)) { array_push($ldap_fields_wanted, $this->config->course_shortname); } if (!empty($this->config->course_summary)) { array_push($ldap_fields_wanted, $this->config->course_summary); } array_push($ldap_fields_wanted, $this->config->{'memberattribute_role' . $role->id}); // Define the search pattern $ldap_search_pattern = $this->config->objectclass; if ($oneidnumber !== null) { $ldap_search_pattern = "(&{$ldap_search_pattern}({$this->config->course_idnumber}={$oneidnumber}))"; } $ldap_cookie = ''; foreach ($ldap_contexts as $ldap_context) { $ldap_context = trim($ldap_context); if (empty($ldap_context)) { continue; // Next; } $flat_records = array(); do { if ($ldap_pagedresults) { ldap_control_paged_result($this->ldapconnection, $this->config->pagesize, true, $ldap_cookie); } if ($this->config->course_search_sub) { // Use ldap_search to find first user from subtree $ldap_result = @ldap_search($this->ldapconnection, $ldap_context, $ldap_search_pattern, $ldap_fields_wanted); } else { // Search only in this context $ldap_result = @ldap_list($this->ldapconnection, $ldap_context, $ldap_search_pattern, $ldap_fields_wanted); } if (!$ldap_result) { continue; // Next } if ($ldap_pagedresults) { ldap_control_paged_result_response($this->ldapconnection, $ldap_result, $ldap_cookie); } // Check and push results $records = ldap_get_entries($this->ldapconnection, $ldap_result); // LDAP libraries return an odd array, really. fix it: for ($c = 0; $c < $records['count']; $c++) { array_push($flat_records, $records[$c]); } // Free some mem unset($records); } while ($ldap_pagedresults && !empty($ldap_cookie)); // If LDAP paged results were used, the current connection must be completely // closed and a new one created, to work without paged results from here on. if ($ldap_pagedresults) { $this->ldap_close(); $this->ldap_connect($trace); } if (count($flat_records)) { $ignorehidden = $this->get_config('ignorehiddencourses'); foreach ($flat_records as $course) { $course = array_change_key_case($course, CASE_LOWER); $idnumber = $course[$this->config->course_idnumber][0]; $trace->output(get_string('synccourserole', 'enrol_ldap', array('idnumber' => $idnumber, 'role_shortname' => $role->shortname))); // Does the course exist in moodle already? $course_obj = $DB->get_record('course', array($this->enrol_localcoursefield => $idnumber)); if (empty($course_obj)) { // Course doesn't exist if ($this->get_config('autocreate')) { // Autocreate $trace->output(get_string('createcourseextid', 'enrol_ldap', array('courseextid' => $idnumber))); if (!($newcourseid = $this->create_course($course, $trace))) { continue; } $course_obj = $DB->get_record('course', array('id' => $newcourseid)); } else { $trace->output(get_string('createnotcourseextid', 'enrol_ldap', array('courseextid' => $idnumber))); continue; // Next; skip this one! } } else { // Check if course needs update & update as needed. $this->update_course($course_obj, $course, $trace); } // Enrol & unenrol // Pull the ldap membership into a nice array // this is an odd array -- mix of hash and array -- $ldapmembers = array(); if (array_key_exists('memberattribute_role' . $role->id, $this->config) && !empty($this->config->{'memberattribute_role' . $role->id}) && !empty($course[$this->config->{'memberattribute_role' . $role->id}])) { // May have no membership! $ldapmembers = $course[$this->config->{'memberattribute_role' . $role->id}]; unset($ldapmembers['count']); // Remove oddity ;) // If we have enabled nested groups, we need to expand // the groups to get the real user list. We need to do // this before dealing with 'memberattribute_isdn'. if ($this->config->nested_groups) { $users = array(); foreach ($ldapmembers as $ldapmember) { $grpusers = $this->ldap_explode_group($ldapmember, $this->config->{'memberattribute_role' . $role->id}); $users = array_merge($users, $grpusers); } $ldapmembers = array_unique($users); // There might be duplicates. } // Deal with the case where the member attribute holds distinguished names, // but only if the user attribute is not a distinguished name itself. if ($this->config->memberattribute_isdn && $this->config->idnumber_attribute !== 'dn' && $this->config->idnumber_attribute !== 'distinguishedname') { // We need to retrieve the idnumber for all the users in $ldapmembers, // as the idnumber does not match their dn and we get dn's from membership. $memberidnumbers = array(); foreach ($ldapmembers as $ldapmember) { $result = ldap_read($this->ldapconnection, $ldapmember, '(objectClass=*)', array($this->config->idnumber_attribute)); $entry = ldap_first_entry($this->ldapconnection, $result); $values = ldap_get_values($this->ldapconnection, $entry, $this->config->idnumber_attribute); array_push($memberidnumbers, $values[0]); } $ldapmembers = $memberidnumbers; } } // Prune old ldap enrolments // hopefully they'll fit in the max buffer size for the RDBMS $sql = "SELECT u.id as userid, u.username, ue.status,\n ra.contextid, ra.itemid as instanceid\n FROM {user} u\n JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.component = 'enrol_ldap' AND ra.roleid = :roleid)\n JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = ra.itemid)\n JOIN {enrol} e ON (e.id = ue.enrolid)\n WHERE u.deleted = 0 AND e.courseid = :courseid "; $params = array('roleid' => $role->id, 'courseid' => $course_obj->id); $context = context_course::instance($course_obj->id); if (!empty($ldapmembers)) { list($ldapml, $params2) = $DB->get_in_or_equal($ldapmembers, SQL_PARAMS_NAMED, 'm', false); $sql .= "AND u.idnumber {$ldapml}"; $params = array_merge($params, $params2); unset($params2); } else { $shortname = format_string($course_obj->shortname, true, array('context' => $context)); $trace->output(get_string('emptyenrolment', 'enrol_ldap', array('role_shortname' => $role->shortname, 'course_shortname' => $shortname))); } $todelete = $DB->get_records_sql($sql, $params); if (!empty($todelete)) { $transaction = $DB->start_delegated_transaction(); foreach ($todelete as $row) { $instance = $DB->get_record('enrol', array('id' => $row->instanceid)); switch ($this->get_config('unenrolaction')) { case ENROL_EXT_REMOVED_UNENROL: $this->unenrol_user($instance, $row->userid); $trace->output(get_string('extremovedunenrol', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); break; case ENROL_EXT_REMOVED_KEEP: // Keep - only adding enrolments break; case ENROL_EXT_REMOVED_SUSPEND: if ($row->status != ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid' => $instance->id, 'userid' => $row->userid)); $trace->output(get_string('extremovedsuspend', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } break; case ENROL_EXT_REMOVED_SUSPENDNOROLES: if ($row->status != ENROL_USER_SUSPENDED) { $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid' => $instance->id, 'userid' => $row->userid)); } role_unassign_all(array('contextid' => $row->contextid, 'userid' => $row->userid, 'component' => 'enrol_ldap', 'itemid' => $instance->id)); $trace->output(get_string('extremovedsuspendnoroles', 'enrol_ldap', array('user_username' => $row->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); break; } } $transaction->allow_commit(); } // Insert current enrolments // bad we can't do INSERT IGNORE with postgres... // Add necessary enrol instance if not present yet; $sql = "SELECT c.id, c.visible, e.id as enrolid\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'ldap')\n WHERE c.id = :courseid"; $params = array('courseid' => $course_obj->id); if (!($course_instance = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE))) { $course_instance = new stdClass(); $course_instance->id = $course_obj->id; $course_instance->visible = $course_obj->visible; $course_instance->enrolid = $this->add_instance($course_instance); } if (!($instance = $DB->get_record('enrol', array('id' => $course_instance->enrolid)))) { continue; // Weird; skip this one. } if ($ignorehidden && !$course_instance->visible) { continue; } $transaction = $DB->start_delegated_transaction(); foreach ($ldapmembers as $ldapmember) { $sql = 'SELECT id,username,1 FROM {user} WHERE idnumber = ? AND deleted = 0'; $member = $DB->get_record_sql($sql, array($ldapmember)); if (empty($member) || empty($member->id)) { $trace->output(get_string('couldnotfinduser', 'enrol_ldap', $ldapmember)); continue; } $sql = "SELECT ue.status\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'ldap')\n WHERE e.courseid = :courseid AND ue.userid = :userid"; $params = array('courseid' => $course_obj->id, 'userid' => $member->id); $userenrolment = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE); if (empty($userenrolment)) { $this->enrol_user($instance, $member->id, $role->id); // Make sure we set the enrolment status to active. If the user wasn't // previously enrolled to the course, enrol_user() sets it. But if we // configured the plugin to suspend the user enrolments _AND_ remove // the role assignments on external unenrol, then enrol_user() doesn't // set it back to active on external re-enrolment. So set it // unconditionally to cover both cases. $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid' => $instance->id, 'userid' => $member->id)); $trace->output(get_string('enroluser', 'enrol_ldap', array('user_username' => $member->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } else { if (!$DB->record_exists('role_assignments', array('roleid' => $role->id, 'userid' => $member->id, 'contextid' => $context->id, 'component' => 'enrol_ldap', 'itemid' => $instance->id))) { // This happens when reviving users or when user has multiple roles in one course. $context = context_course::instance($course_obj->id); role_assign($role->id, $member->id, $context->id, 'enrol_ldap', $instance->id); $trace->output("Assign role to user '{$member->username}' in course '{$course_obj->shortname} ({$course_obj->id})'"); } if ($userenrolment->status == ENROL_USER_SUSPENDED) { // Reenable enrolment that was previously disabled. Enrolment refreshed $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid' => $instance->id, 'userid' => $member->id)); $trace->output(get_string('enroluserenable', 'enrol_ldap', array('user_username' => $member->username, 'course_shortname' => $course_obj->shortname, 'course_id' => $course_obj->id))); } } } $transaction->allow_commit(); } } } } @$this->ldap_close(); $trace->finished(); }
/** * This function will empty a course of user data. * It will retain the activities and the structure of the course. * * @param object $data an object containing all the settings including courseid (without magic quotes) * @return array status array of array component, item, error */ function reset_course_userdata($data) { global $CFG, $USER, $DB; require_once $CFG->libdir . '/gradelib.php'; require_once $CFG->libdir . '/completionlib.php'; require_once $CFG->dirroot . '/group/lib.php'; $data->courseid = $data->id; $context = get_context_instance(CONTEXT_COURSE, $data->courseid); // calculate the time shift of dates if (!empty($data->reset_start_date)) { // time part of course startdate should be zero $data->timeshift = $data->reset_start_date - usergetmidnight($data->reset_start_date_old); } else { $data->timeshift = 0; } // result array: component, item, error $status = array(); // start the resetting $componentstr = get_string('general'); // move the course start time if (!empty($data->reset_start_date) and $data->timeshift) { // change course start data $DB->set_field('course', 'startdate', $data->reset_start_date, array('id' => $data->courseid)); // update all course and group events - do not move activity events $updatesql = "UPDATE {event}\n SET timestart = timestart + ?\n WHERE courseid=? AND instance=0"; $DB->execute($updatesql, array($data->timeshift, $data->courseid)); $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => false); } if (!empty($data->reset_logs)) { $DB->delete_records('log', array('course' => $data->courseid)); $status[] = array('component' => $componentstr, 'item' => get_string('deletelogs'), 'error' => false); } if (!empty($data->reset_events)) { $DB->delete_records('event', array('courseid' => $data->courseid)); $status[] = array('component' => $componentstr, 'item' => get_string('deleteevents', 'calendar'), 'error' => false); } if (!empty($data->reset_notes)) { require_once $CFG->dirroot . '/notes/lib.php'; note_delete_all($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('deletenotes', 'notes'), 'error' => false); } if (!empty($data->delete_blog_associations)) { require_once $CFG->dirroot . '/blog/lib.php'; blog_remove_associations_for_course($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('deleteblogassociations', 'blog'), 'error' => false); } if (!empty($data->reset_course_completion)) { // Delete course completion information $course = $DB->get_record('course', array('id' => $data->courseid)); $cc = new completion_info($course); $cc->delete_course_completion_data(); $status[] = array('component' => $componentstr, 'item' => get_string('deletecoursecompletiondata', 'completion'), 'error' => false); } $componentstr = get_string('roles'); if (!empty($data->reset_roles_overrides)) { $children = get_child_contexts($context); foreach ($children as $child) { $DB->delete_records('role_capabilities', array('contextid' => $child->id)); } $DB->delete_records('role_capabilities', array('contextid' => $context->id)); //force refresh for logged in users mark_context_dirty($context->path); $status[] = array('component' => $componentstr, 'item' => get_string('deletecourseoverrides', 'role'), 'error' => false); } if (!empty($data->reset_roles_local)) { $children = get_child_contexts($context); foreach ($children as $child) { role_unassign_all(array('contextid' => $child->id)); } //force refresh for logged in users mark_context_dirty($context->path); $status[] = array('component' => $componentstr, 'item' => get_string('deletelocalroles', 'role'), 'error' => false); } // First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc. $data->unenrolled = array(); if (!empty($data->unenrol_users)) { $plugins = enrol_get_plugins(true); $instances = enrol_get_instances($data->courseid, true); foreach ($instances as $key => $instance) { if (!isset($plugins[$instance->enrol])) { unset($instances[$key]); continue; } if (!$plugins[$instance->enrol]->allow_unenrol($instance)) { unset($instances[$key]); } } $sqlempty = $DB->sql_empty(); foreach ($data->unenrol_users as $withroleid) { $sql = "SELECT DISTINCT ue.userid, ue.enrolid\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)\n JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.roleid = :roleid AND ra.userid = ue.userid)"; $params = array('courseid' => $data->courseid, 'roleid' => $withroleid, 'courselevel' => CONTEXT_COURSE); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (!isset($instances[$ue->enrolid])) { continue; } $plugins[$instances[$ue->enrolid]->enrol]->unenrol_user($instances[$ue->enrolid], $ue->userid); $data->unenrolled[$ue->userid] = $ue->userid; } } } if (!empty($data->unenrolled)) { $status[] = array('component' => $componentstr, 'item' => get_string('unenrol', 'enrol') . ' (' . count($data->unenrolled) . ')', 'error' => false); } $componentstr = get_string('groups'); // remove all group members if (!empty($data->reset_groups_members)) { groups_delete_group_members($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removegroupsmembers', 'group'), 'error' => false); } // remove all groups if (!empty($data->reset_groups_remove)) { groups_delete_groups($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('deleteallgroups', 'group'), 'error' => false); } // remove all grouping members if (!empty($data->reset_groupings_members)) { groups_delete_groupings_groups($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('removegroupingsmembers', 'group'), 'error' => false); } // remove all groupings if (!empty($data->reset_groupings_remove)) { groups_delete_groupings($data->courseid, false); $status[] = array('component' => $componentstr, 'item' => get_string('deleteallgroupings', 'group'), 'error' => false); } // Look in every instance of every module for data to delete $unsupported_mods = array(); if ($allmods = $DB->get_records('modules')) { foreach ($allmods as $mod) { $modname = $mod->name; if (!$DB->count_records($modname, array('course' => $data->courseid))) { continue; // skip mods with no instances } $modfile = $CFG->dirroot . '/mod/' . $modname . '/lib.php'; $moddeleteuserdata = $modname . '_reset_userdata'; // Function to delete user data if (file_exists($modfile)) { include_once $modfile; if (function_exists($moddeleteuserdata)) { $modstatus = $moddeleteuserdata($data); if (is_array($modstatus)) { $status = array_merge($status, $modstatus); } else { debugging('Module ' . $modname . ' returned incorrect staus - must be an array!'); } } else { $unsupported_mods[] = $mod; } } else { debugging('Missing lib.php in ' . $modname . ' module!'); } } } // mention unsupported mods if (!empty($unsupported_mods)) { foreach ($unsupported_mods as $mod) { $status[] = array('component' => get_string('modulenameplural', $mod->name), 'item' => '', 'error' => get_string('resetnotimplemented')); } } $componentstr = get_string('gradebook', 'grades'); // reset gradebook if (!empty($data->reset_gradebook_items)) { remove_course_grades($data->courseid, false); grade_grab_course_grades($data->courseid); grade_regrade_final_grades($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removeallcourseitems', 'grades'), 'error' => false); } else { if (!empty($data->reset_gradebook_grades)) { grade_course_reset($data->courseid); $status[] = array('component' => $componentstr, 'item' => get_string('removeallcoursegrades', 'grades'), 'error' => false); } } // reset comments if (!empty($data->reset_comments)) { require_once $CFG->dirroot . '/comment/lib.php'; comment::reset_course_page_comments($context); } return $status; }
/** * Deal with users that are not supposed to be enrolled via this instance * @static * @param stdClass $instance * @param stdClass $ue * @param context_course $context * @param enrol_meta $plugin * @return void */ protected static function user_not_supposed_to_be_here($instance, $ue, context_course $context, $plugin) { if (!$ue) { // Not enrolled yet - simple! return; } $userid = $ue->userid; $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES); if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { // Purges grades, group membership, preferences, etc. - admins were warned! $plugin->unenrol_user($instance, $userid); } else { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) { if ($ue->status != ENROL_USER_SUSPENDED) { $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED); } } else { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { if ($ue->status != ENROL_USER_SUSPENDED) { $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED); } role_unassign_all(array('userid' => $userid, 'contextid' => $context->id, 'component' => 'enrol_meta', 'itemid' => $instance->id)); } else { debugging('Unknown unenrol action ' . $unenrolaction); } } } }
/** * A small functional test of accesslib functions and classes. */ public function test_everything_in_accesslib() { global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE; // First of all finalize the session, we must not carry over any of this mess to the next page via SESSION!!! session_get_instance()->write_close(); // hack - this is going to take very long time set_time_limit(3600); // Get rid of current user that would not work anyway, // do NOT even think about using $this->switch_global_user_id()! if (!isset($this->accesslibprevuser)) { $this->accesslibprevuser = clone $USER; $USER = new stdClass(); $USER->id = 0; } // Backup $SITE global if (!isset($this->accesslibprevsite)) { $this->accesslibprevsite = $SITE; } // Switch DB, if it somehow fails or you specified wrong unittest prefix it will nuke real data, you have been warned! $this->switch_to_test_db(); // Let's switch the CFG $prevcfg = clone $CFG; $this->switch_to_test_cfg(); $CFG->config_php_settings = $prevcfg->config_php_settings; $CFG->noemailever = true; $CFG->admin = $prevcfg->admin; $CFG->lang = 'en'; $CFG->tempdir = $prevcfg->tempdir; $CFG->cachedir = $prevcfg->cachedir; $CFG->directorypermissions = $prevcfg->directorypermissions; $CFG->filepermissions = $prevcfg->filepermissions; // Reset all caches accesslib_clear_all_caches_for_unit_testing(); // Add some core tables - this is known to break constantly because we keep adding dependencies... $tablenames = array('config', 'config_plugins', 'modules', 'course', 'course_modules', 'course_sections', 'course_categories', 'mnet_host', 'mnet_application', 'capabilities', 'context', 'context_temp', 'role', 'role_capabilities', 'role_allow_switch', 'license', 'my_pages', 'block', 'block_instances', 'block_positions', 'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels', 'enrol', 'user_enrolments', 'filter_active', 'filter_config', 'comments', 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'rating', 'files', 'role_names', 'user_preferences'); $this->create_test_tables($tablenames, 'lib'); // Create all core default records and default settings require_once "{$CFG->libdir}/db/install.php"; xmldb_main_install(); // installs the capabilities too // Fake mod_page install $tablenames = array('page'); $this->create_test_tables($tablenames, 'mod/page'); $module = new stdClass(); require $CFG->dirroot . '/mod/page/version.php'; $module->name = 'page'; $pagemoduleid = $DB->insert_record('modules', $module); update_capabilities('mod_page'); // Fake block_online_users install $plugin = new stdClass(); $plugin->version = NULL; $plugin->cron = 0; include $CFG->dirroot . '/blocks/online_users/version.php'; $plugin->name = 'online_users'; $onlineusersblockid = $DB->insert_record('block', $plugin); update_capabilities('block_online_users'); // Finish roles setup set_config('defaultfrontpageroleid', $DB->get_field('role', 'id', array('archetype' => 'frontpage'))); set_config('defaultuserroleid', $DB->get_field('role', 'id', array('archetype' => 'user'))); set_config('notloggedinroleid', $DB->get_field('role', 'id', array('archetype' => 'guest'))); set_config('rolesactive', 1); // Init manual enrol set_config('status', ENROL_INSTANCE_ENABLED, 'enrol_manual'); set_config('defaultperiod', 0, 'enrol_manual'); $manualenrol = enrol_get_plugin('manual'); // Fill the site with some real data $testcategories = array(); $testcourses = array(); $testpages = array(); $testblocks = array(); $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id'); $systemcontext = context_system::instance(); $frontpagecontext = context_course::instance(SITEID); // Add block to system context $bi = new stdClass(); $bi->blockname = 'online_users'; $bi->parentcontextid = $systemcontext->id; $bi->showinsubcontexts = 1; $bi->pagetypepattern = ''; $bi->subpagepattern = ''; $bi->defaultregion = ''; $bi->defaultweight = 0; $bi->configdata = ''; $biid = $DB->insert_record('block_instances', $bi); context_block::instance($biid); $testblocks[] = $biid; // Some users $testusers = array(); for ($i = 0; $i < 20; $i++) { $user = new stdClass(); $user->auth = 'manual'; $user->firstname = 'user' . $i; $user->lastname = 'user' . $i; $user->username = '******' . $i; $user->password = '******'; $user->email = "user{$i}@example.com"; $user->confirmed = 1; $user->mnethostid = $CFG->mnet_localhost_id; $user->lang = $CFG->lang; $user->maildisplay = 1; $user->timemodified = time(); $user->deleted = 0; $user->lastip = '0.0.0.0'; $userid = $DB->insert_record('user', $user); $testusers[$i] = $userid; $usercontext = context_user::instance($userid); // Add block to user profile $bi->parentcontextid = $usercontext->id; $biid = $DB->insert_record('block_instances', $bi); context_block::instance($biid); $testblocks[] = $biid; } // Deleted user - should be ignored everywhere, can not have context $user = new stdClass(); $user->auth = 'manual'; $user->firstname = ''; $user->lastname = ''; $user->username = '******'; $user->password = ''; $user->email = ''; $user->confirmed = 1; $user->mnethostid = $CFG->mnet_localhost_id; $user->lang = $CFG->lang; $user->maildisplay = 1; $user->timemodified = time(); $user->deleted = 1; $user->lastip = '0.0.0.0'; $DB->insert_record('user', $user); unset($user); // Add block to frontpage $bi->parentcontextid = $frontpagecontext->id; $biid = $DB->insert_record('block_instances', $bi); $frontpageblockcontext = context_block::instance($biid); $testblocks[] = $biid; // Add a resource to frontpage $page = new stdClass(); $page->course = $SITE->id; $page->intro = '...'; $page->introformat = FORMAT_HTML; $page->content = '...'; $page->contentformat = FORMAT_HTML; $pageid = $DB->insert_record('page', $page); $testpages[] = $pageid; $cm = new stdClass(); $cm->course = $SITE->id; $cm->module = $pagemoduleid; $cm->section = 1; $cm->instance = $pageid; $cmid = $DB->insert_record('course_modules', $cm); $frontpagepagecontext = context_module::instance($cmid); // Add block to frontpage resource $bi->parentcontextid = $frontpagepagecontext->id; $biid = $DB->insert_record('block_instances', $bi); $frontpagepageblockcontext = context_block::instance($biid); $testblocks[] = $biid; // Some nested course categories with courses require_once "{$CFG->dirroot}/course/lib.php"; $path = ''; $parentcat = 0; for ($i = 0; $i < 5; $i++) { $cat = new stdClass(); $cat->name = 'category' . $i; $cat->parent = $parentcat; $cat->depth = $i + 1; $cat->sortorder = MAX_COURSES_IN_CATEGORY * ($i + 2); $cat->timemodified = time(); $catid = $DB->insert_record('course_categories', $cat); $path = $path . '/' . $catid; $DB->set_field('course_categories', 'path', $path, array('id' => $catid)); $parentcat = $catid; $testcategories[] = $catid; $catcontext = context_coursecat::instance($catid); if ($i >= 4) { continue; } // Add resource to each category $bi->parentcontextid = $catcontext->id; $biid = $DB->insert_record('block_instances', $bi); context_block::instance($biid); // Add a few courses to each category for ($j = 0; $j < 6; $j++) { $course = new stdClass(); $course->fullname = 'course' . $j; $course->shortname = 'c' . $j; $course->summary = 'bah bah bah'; $course->newsitems = 0; $course->numsections = 1; $course->category = $catid; $course->format = 'topics'; $course->timecreated = time(); $course->visible = 1; $course->timemodified = $course->timecreated; $courseid = $DB->insert_record('course', $course); $section = new stdClass(); $section->course = $courseid; $section->section = 0; $section->summaryformat = FORMAT_HTML; $DB->insert_record('course_sections', $section); $section->section = 1; $DB->insert_record('course_sections', $section); $testcourses[] = $courseid; $coursecontext = context_course::instance($courseid); if ($j >= 5) { continue; } // Add manual enrol instance $manualenrol->add_default_instance($DB->get_record('course', array('id' => $courseid))); // Add block to each course $bi->parentcontextid = $coursecontext->id; $biid = $DB->insert_record('block_instances', $bi); context_block::instance($biid); $testblocks[] = $biid; // Add a resource to each course $page->course = $courseid; $pageid = $DB->insert_record('page', $page); $testpages[] = $pageid; $cm->course = $courseid; $cm->instance = $pageid; $cm->id = $DB->insert_record('course_modules', $cm); $modcontext = context_module::instance($cm->id); // Add block to each module $bi->parentcontextid = $modcontext->id; $biid = $DB->insert_record('block_instances', $bi); context_block::instance($biid); $testblocks[] = $biid; } } // Make sure all contexts were created properly $count = 1; //system $count += $DB->count_records('user', array('deleted' => 0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEqual($DB->count_records('context'), $count); $this->assertEqual($DB->count_records('context', array('depth' => 0)), 0); $this->assertEqual($DB->count_records('context', array('path' => NULL)), 0); // ====== context_helper::get_level_name() ================================ $levels = context_helper::get_all_levels(); foreach ($levels as $level => $classname) { $name = context_helper::get_level_name($level); $this->assertFalse(empty($name)); } // ======= context::instance_by_id(), context_xxx::instance(); $context = context::instance_by_id($frontpagecontext->id); $this->assertidentical($context->contextlevel, CONTEXT_COURSE); $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING)); try { context::instance_by_id(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $this->assertTrue(context_system::instance() instanceof context_system); $this->assertTrue(context_coursecat::instance($testcategories[0]) instanceof context_coursecat); $this->assertTrue(context_course::instance($testcourses[0]) instanceof context_course); $this->assertTrue(context_module::instance($testpages[0]) instanceof context_module); $this->assertTrue(context_block::instance($testblocks[0]) instanceof context_block); $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_course::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_module::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_block::instance(-1, IGNORE_MISSING)); try { context_coursecat::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_course::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_module::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_block::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } // ======= $context->get_url(), $context->get_context_name(), $context->get_capabilities() ========= $testcontexts = array(); $testcontexts[CONTEXT_SYSTEM] = context_system::instance(); $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]); $testcontexts[CONTEXT_COURSE] = context_course::instance($testcourses[0]); $testcontexts[CONTEXT_MODULE] = context_module::instance($testpages[0]); $testcontexts[CONTEXT_BLOCK] = context_block::instance($testblocks[0]); foreach ($testcontexts as $context) { $name = $context->get_context_name(true, true); $this->assertFalse(empty($name)); $this->assertTrue($context->get_url() instanceof moodle_url); $caps = $context->get_capabilities(); $this->assertTrue(is_array($caps)); foreach ($caps as $cap) { $cap = (array) $cap; $this->assertIdentical(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask')); } } unset($testcontexts); // ===== $context->get_course_context() ========================================= $this->assertFalse($systemcontext->get_course_context(false)); try { $systemcontext->get_course_context(); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $context = context_coursecat::instance($testcategories[0]); $this->assertFalse($context->get_course_context(false)); try { $context->get_course_context(); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $this->assertIdentical($frontpagecontext->get_course_context(true), $frontpagecontext); $this->assertIdentical($frontpagepagecontext->get_course_context(true), $frontpagecontext); $this->assertIdentical($frontpagepageblockcontext->get_course_context(true), $frontpagecontext); // ======= $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() ======= $userid = reset($testusers); $usercontext = context_user::instance($userid); $this->assertIdentical($usercontext->get_parent_context(), $systemcontext); $this->assertIdentical($usercontext->get_parent_contexts(), array($systemcontext->id => $systemcontext)); $this->assertIdentical($usercontext->get_parent_contexts(true), array($usercontext->id => $usercontext, $systemcontext->id => $systemcontext)); $this->assertIdentical($systemcontext->get_parent_contexts(), array()); $this->assertIdentical($systemcontext->get_parent_contexts(true), array($systemcontext->id => $systemcontext)); $this->assertIdentical($systemcontext->get_parent_context_ids(), array()); $this->assertIdentical($systemcontext->get_parent_context_ids(true), array($systemcontext->id)); $this->assertIdentical($frontpagecontext->get_parent_context(), $systemcontext); $this->assertIdentical($frontpagecontext->get_parent_contexts(), array($systemcontext->id => $systemcontext)); $this->assertIdentical($frontpagecontext->get_parent_contexts(true), array($frontpagecontext->id => $frontpagecontext, $systemcontext->id => $systemcontext)); $this->assertIdentical($frontpagecontext->get_parent_context_ids(), array($systemcontext->id)); $this->assertEqual($frontpagecontext->get_parent_context_ids(true), array($frontpagecontext->id, $systemcontext->id)); $this->assertIdentical($systemcontext->get_parent_context(), false); $frontpagecontext = context_course::instance($SITE->id); $parent = $systemcontext; foreach ($testcategories as $catid) { $catcontext = context_coursecat::instance($catid); $this->assertIdentical($catcontext->get_parent_context(), $parent); $parent = $catcontext; } $this->assertIdentical($frontpagepagecontext->get_parent_context(), $frontpagecontext); $this->assertIdentical($frontpageblockcontext->get_parent_context(), $frontpagecontext); $this->assertIdentical($frontpagepageblockcontext->get_parent_context(), $frontpagepagecontext); // ====== $context->get_child_contexts() ================================ $children = $systemcontext->get_child_contexts(); $this->assertEqual(count($children) + 1, $DB->count_records('context')); $context = context_coursecat::instance($testcategories[3]); $children = $context->get_child_contexts(); $countcats = 0; $countcourses = 0; $countblocks = 0; foreach ($children as $child) { if ($child->contextlevel == CONTEXT_COURSECAT) { $countcats++; } if ($child->contextlevel == CONTEXT_COURSE) { $countcourses++; } if ($child->contextlevel == CONTEXT_BLOCK) { $countblocks++; } } $this->assertEqual(count($children), 8); $this->assertEqual($countcats, 1); $this->assertEqual($countcourses, 6); $this->assertEqual($countblocks, 1); $context = context_course::instance($testcourses[2]); $children = $context->get_child_contexts(); $this->assertEqual(count($children), 3); $context = context_module::instance($testpages[3]); $children = $context->get_child_contexts(); $this->assertEqual(count($children), 1); $context = context_block::instance($testblocks[1]); $children = $context->get_child_contexts(); $this->assertEqual(count($children), 0); unset($children); unset($countcats); unset($countcourses); unset($countblocks); // ======= context_helper::reset_caches() ============================ context_helper::reset_caches(); $this->assertEqual(context_inspection::test_context_cache_size(), 0); context_course::instance($SITE->id); $this->assertEqual(context_inspection::test_context_cache_size(), 1); // ======= context preloading ======================================== context_helper::reset_caches(); $sql = "SELECT " . context_helper::get_preload_record_columns_sql('c') . "\n FROM {context} c\n WHERE c.contextlevel <> " . CONTEXT_SYSTEM; $records = $DB->get_records_sql($sql); $firstrecord = reset($records); $columns = context_helper::get_preload_record_columns('c'); $firstrecord = (array) $firstrecord; $this->assertIdentical(array_keys($firstrecord), array_values($columns)); context_helper::reset_caches(); foreach ($records as $record) { context_helper::preload_from_record($record); $this->assertIdentical($record, new stdClass()); } $this->assertEqual(context_inspection::test_context_cache_size(), count($records)); unset($records); unset($columns); context_helper::reset_caches(); context_helper::preload_course($SITE->id); $this->assertEqual(context_inspection::test_context_cache_size(), 4); // ====== assign_capability(), unassign_capability() ==================== $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertFalse($rc); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id); $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertEqual($rc->permission, CAP_ALLOW); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id); $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertEqual($rc->permission, CAP_ALLOW); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true); $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertEqual($rc->permission, CAP_PREVENT); assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext); $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertFalse($rc); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext); unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true); $rc = $DB->get_record('role_capabilities', array('contextid' => $frontpagecontext->id, 'roleid' => $allroles['teacher'], 'capability' => 'moodle/site:accessallgroups')); $this->assertFalse($rc); unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true); unset($rc); accesslib_clear_all_caches(false); // must be done after assign_capability() // ======= role_assign(), role_unassign(), role_unassign_all() ============== $context = context_course::instance($testcourses[1]); $this->assertEqual($DB->count_records('role_assignments', array('contextid' => $context->id)), 0); role_assign($allroles['teacher'], $testusers[1], $context->id); role_assign($allroles['teacher'], $testusers[2], $context->id); role_assign($allroles['manager'], $testusers[1], $context->id); $this->assertEqual($DB->count_records('role_assignments', array('contextid' => $context->id)), 3); role_unassign($allroles['teacher'], $testusers[1], $context->id); $this->assertEqual($DB->count_records('role_assignments', array('contextid' => $context->id)), 2); role_unassign_all(array('contextid' => $context->id)); $this->assertEqual($DB->count_records('role_assignments', array('contextid' => $context->id)), 0); unset($context); accesslib_clear_all_caches(false); // just in case // ====== has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends ======================== $adminid = get_admin()->id; $guestid = $CFG->siteguest; // Enrol some users into some courses $course1 = $DB->get_record('course', array('id' => $testcourses[22]), '*', MUST_EXIST); $course2 = $DB->get_record('course', array('id' => $testcourses[7]), '*', MUST_EXIST); $cms = $DB->get_records('course_modules', array('course' => $course1->id), 'id'); $cm1 = reset($cms); $blocks = $DB->get_records('block_instances', array('parentcontextid' => context_module::instance($cm1->id)->id), 'id'); $block1 = reset($blocks); $instance1 = $DB->get_record('enrol', array('enrol' => 'manual', 'courseid' => $course1->id)); $instance2 = $DB->get_record('enrol', array('enrol' => 'manual', 'courseid' => $course2->id)); for ($i = 0; $i < 9; $i++) { $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']); } $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']); $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']); for ($i = 10; $i < 15; $i++) { $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']); } $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']); // Add tons of role assignments - the more the better role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2])); role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1])); role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id)); role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id)); role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id)); role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id)); role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id)); role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id)); role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id)); role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id)); // Add tons of overrides - the more the better assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true); assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true); assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true); assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true); assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true); assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true); assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true); assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true); assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true); assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true); accesslib_clear_all_caches(false); // must be done after assign_capability() // Extra tests for guests and not-logged-in users because they can not be verified by cross checking // with get_users_by_capability() where they are ignored $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid)); $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid)); $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid)); $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid)); $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0)); $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0)); $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0)); $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0)); $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11])); $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11])); $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11])); $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11])); $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9])); $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9])); $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9])); // Test the list of enrolled users $coursecontext = context_course::instance($course1->id); $enrolled = get_enrolled_users($coursecontext); $this->assertEqual(count($enrolled), 10); for ($i = 0; $i < 10; $i++) { $this->assertTrue(isset($enrolled[$testusers[$i]])); } $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update'); $this->assertEqual(count($enrolled), 1); $this->assertTrue(isset($enrolled[$testusers[9]])); unset($enrolled); // role switching $userid = $testusers[9]; $USER = $DB->get_record('user', array('id' => $userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); $this->assertFalse(is_role_switched($course1->id)); role_switch($allroles['student'], $coursecontext); $this->assertTrue(is_role_switched($course1->id)); $this->assertEqual($USER->access['rsw'][$coursecontext->path], $allroles['student']); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); role_switch(0, $coursecontext); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); $userid = $adminid; $USER = $DB->get_record('user', array('id' => $userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $blockcontext = context_block::instance($block1->id); $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); role_switch($allroles['student'], $coursecontext); $this->assertEqual($USER->access['rsw'][$coursecontext->path], $allroles['student']); $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); load_all_capabilities(); $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); // temp course role for enrol $DB->delete_records('cache_flags', array()); // this prevents problem with dirty contexts immediately resetting the temp role - this is a known problem... $userid = $testusers[5]; $roleid = $allroles['editingteacher']; $USER = $DB->get_record('user', array('id' => $userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid])); load_temp_course_role($coursecontext, $roleid); $this->assertEqual($USER->access['ra'][$coursecontext->path][$roleid], $roleid); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); remove_temp_course_roles($coursecontext); $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); load_temp_course_role($coursecontext, $roleid); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); $USER = new stdClass(); $USER->id = 0; // Now cross check has_capability() with get_users_by_capability(), each using different code paths, // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong, // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users) $contexts = $DB->get_records('context', array(), 'id'); $contexts = array_values($contexts); $capabilities = $DB->get_records('capabilities', array(), 'id'); $capabilities = array_values($capabilities); $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']); // Random time! srand(666); foreach ($testusers as $userid) { // no guest or deleted // each user gets 0-20 random roles $rcount = rand(0, 10); for ($j = 0; $j < $rcount; $j++) { $roleid = $roles[rand(0, count($roles) - 1)]; $contextid = $contexts[rand(0, count($contexts) - 1)]->id; role_assign($roleid, $userid, $contextid); } } $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT); for ($j = 0; $j < 10000; $j++) { $roleid = $roles[rand(0, count($roles) - 1)]; $contextid = $contexts[rand(0, count($contexts) - 1)]->id; $permission = $permissions[rand(0, count($permissions) - 1)]; $capname = $capabilities[rand(0, count($capabilities) - 1)]->name; assign_capability($capname, $permission, $roleid, $contextid, true); } unset($permissions); unset($roles); unset($contexts); unset($users); unset($capabilities); accesslib_clear_all_caches(false); // must be done after assign_capability() // Test time - let's set up some real user, just in case the logic for USER affects the others... $USER = $DB->get_record('user', array('id' => $testusers[3])); load_all_capabilities(); $contexts = $DB->get_records('context', array(), 'id'); $users = $DB->get_records('user', array(), 'id', 'id'); $capabilities = $DB->get_records('capabilities', array(), 'id'); $users[0] = null; // not-logged-in user $users[-1] = null; // non-existent user foreach ($contexts as $crecord) { $context = context::instance_by_id($crecord->id); if ($coursecontext = $context->get_course_context(false)) { $enrolled = get_enrolled_users($context); } else { $enrolled = array(); } foreach ($capabilities as $cap) { $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username'); if ($enrolled) { $enrolledwithcap = get_enrolled_users($context, $cap->name); } else { $enrolledwithcap = array(); } foreach ($users as $userid => $unused) { if ($userid == 0 or isguestuser($userid)) { if ($userid == 0) { $CFG->forcelogin = true; $this->assertFalse(has_capability($cap->name, $context, $userid)); unset($CFG->forcelogin); } if ($cap->captype === 'write' or $cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) { $this->assertFalse(has_capability($cap->name, $context, $userid)); } $this->assertFalse(isset($allowed[$userid])); } else { if (is_siteadmin($userid)) { $this->assertTrue(has_capability($cap->name, $context, $userid, true)); } $hascap = has_capability($cap->name, $context, $userid, false); $this->assertIdentical($hascap, isset($allowed[$userid]), "Capability result mismatch user:{$userid}, context:{$context->id}, {$cap->name}, hascap: " . (int) $hascap . " "); if (isset($enrolled[$userid])) { $this->assertIdentical(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:{$userid}, context:{$context->id}, {$cap->name}, hascap: " . (int) $hascap . " "); } } } } } // Back to nobody $USER = new stdClass(); $USER->id = 0; unset($contexts); unset($users); unset($capabilities); // Now let's do all the remaining tests that break our carefully prepared fake site // ======= $context->mark_dirty() ======================================= $DB->delete_records('cache_flags', array()); accesslib_clear_all_caches(false); $systemcontext->mark_dirty(); $dirty = get_cache_flags('accesslib/dirtycontexts', time() - 2); $this->assertTrue(isset($dirty[$systemcontext->path])); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path])); // ======= $context->reload_if_dirty(); ================================= $DB->delete_records('cache_flags', array()); accesslib_clear_all_caches(false); load_all_capabilities(); $context = context_course::instance($testcourses[2]); $page = $DB->get_record('page', array('course' => $testcourses[2])); $pagecontext = context_module::instance($page->id); $context->mark_dirty(); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); $USER->access['test'] = true; $context->reload_if_dirty(); $this->assertFalse(isset($USER->access['test'])); $context->mark_dirty(); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); $USER->access['test'] = true; $pagecontext->reload_if_dirty(); $this->assertFalse(isset($USER->access['test'])); // ======= context_helper::build_all_paths() ============================ $oldcontexts = $DB->get_records('context', array(), 'id'); $DB->set_field_select('context', 'path', NULL, "contextlevel <> " . CONTEXT_SYSTEM); $DB->set_field_select('context', 'depth', 0, "contextlevel <> " . CONTEXT_SYSTEM); context_helper::build_all_paths(); $newcontexts = $DB->get_records('context', array(), 'id'); $this->assertIdentical($oldcontexts, $newcontexts); unset($oldcontexts); unset($newcontexts); // ======= $context->reset_paths() ====================================== $context = context_course::instance($testcourses[2]); $children = $context->get_child_contexts(); $context->reset_paths(false); $this->assertIdentical($DB->get_field('context', 'path', array('id' => $context->id)), NULL); $this->assertEqual($DB->get_field('context', 'depth', array('id' => $context->id)), 0); foreach ($children as $child) { $this->assertIdentical($DB->get_field('context', 'path', array('id' => $child->id)), NULL); $this->assertEqual($DB->get_field('context', 'depth', array('id' => $child->id)), 0); } $this->assertEqual(count($children) + 1, $DB->count_records('context', array('depth' => 0))); $this->assertEqual(count($children) + 1, $DB->count_records('context', array('path' => NULL))); $context = context_course::instance($testcourses[2]); $context->reset_paths(true); $context = context_course::instance($testcourses[2]); $this->assertEqual($DB->get_field('context', 'path', array('id' => $context->id)), $context->path); $this->assertEqual($DB->get_field('context', 'depth', array('id' => $context->id)), $context->depth); $this->assertEqual(0, $DB->count_records('context', array('depth' => 0))); $this->assertEqual(0, $DB->count_records('context', array('path' => NULL))); // ====== $context->update_moved(); ====================================== accesslib_clear_all_caches(false); $DB->delete_records('cache_flags', array()); $course = $DB->get_record('course', array('id' => $testcourses[0])); $context = context_course::instance($course->id); $oldpath = $context->path; $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $categorycontext = context_coursecat::instance($miscid); $course->category = $miscid; $DB->update_record('course', $course); $context->update_moved($categorycontext); $context = context_course::instance($course->id); $this->assertIdentical($context->get_parent_context(), $categorycontext); $dirty = get_cache_flags('accesslib/dirtycontexts', time() - 2); $this->assertTrue(isset($dirty[$oldpath])); $this->assertTrue(isset($dirty[$context->path])); // ====== $context->delete_content() ===================================== context_helper::reset_caches(); $context = context_module::instance($testpages[3]); $this->assertTrue($DB->record_exists('context', array('id' => $context->id))); $this->assertEqual(1, $DB->count_records('block_instances', array('parentcontextid' => $context->id))); $context->delete_content(); $this->assertTrue($DB->record_exists('context', array('id' => $context->id))); $this->assertEqual(0, $DB->count_records('block_instances', array('parentcontextid' => $context->id))); // ====== $context->delete() ============================= context_helper::reset_caches(); $context = context_module::instance($testpages[4]); $this->assertTrue($DB->record_exists('context', array('id' => $context->id))); $this->assertEqual(1, $DB->count_records('block_instances', array('parentcontextid' => $context->id))); $bi = $DB->get_record('block_instances', array('parentcontextid' => $context->id)); $bicontext = context_block::instance($bi->id); $DB->delete_records('cache_flags', array()); $context->delete(); // should delete also linked blocks $dirty = get_cache_flags('accesslib/dirtycontexts', time() - 2); $this->assertTrue(isset($dirty[$context->path])); $this->assertFalse($DB->record_exists('context', array('id' => $context->id))); $this->assertFalse($DB->record_exists('context', array('id' => $bicontext->id))); $this->assertFalse($DB->record_exists('context', array('contextlevel' => CONTEXT_MODULE, 'instanceid' => $testpages[4]))); $this->assertFalse($DB->record_exists('context', array('contextlevel' => CONTEXT_BLOCK, 'instanceid' => $bi->id))); $this->assertEqual(0, $DB->count_records('block_instances', array('parentcontextid' => $context->id))); context_module::instance($testpages[4]); // ====== context_helper::delete_instance() ============================= context_helper::reset_caches(); $lastcourse = array_pop($testcourses); $this->assertTrue($DB->record_exists('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $lastcourse))); $coursecontext = context_course::instance($lastcourse); $this->assertEqual(context_inspection::test_context_cache_size(), 1); $this->assertFalse($coursecontext->instanceid == CONTEXT_COURSE); $DB->delete_records('cache_flags', array()); context_helper::delete_instance(CONTEXT_COURSE, $lastcourse); $dirty = get_cache_flags('accesslib/dirtycontexts', time() - 2); $this->assertTrue(isset($dirty[$coursecontext->path])); $this->assertEqual(context_inspection::test_context_cache_size(), 0); $this->assertFalse($DB->record_exists('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $lastcourse))); context_course::instance($lastcourse); // ======= context_helper::create_instances() ========================== $prevcount = $DB->count_records('context'); $DB->delete_records('context', array('contextlevel' => CONTEXT_BLOCK)); context_helper::create_instances(null, true); $this->assertIdentical($DB->count_records('context'), $prevcount); $this->assertEqual($DB->count_records('context', array('depth' => 0)), 0); $this->assertEqual($DB->count_records('context', array('path' => NULL)), 0); $DB->delete_records('context', array('contextlevel' => CONTEXT_BLOCK)); $DB->delete_records('block_instances', array()); $prevcount = $DB->count_records('context'); $DB->delete_records_select('context', 'contextlevel <> ' . CONTEXT_SYSTEM); context_helper::create_instances(null, true); $this->assertIdentical($DB->count_records('context'), $prevcount); $this->assertEqual($DB->count_records('context', array('depth' => 0)), 0); $this->assertEqual($DB->count_records('context', array('path' => NULL)), 0); // ======= context_helper::cleanup_instances() ========================== $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); $DB->delete_records('course', array('id' => $lastcourse)); $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); $DB->delete_records('course_categories', array('id' => $lastcategory)); $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); $DB->delete_records('user', array('id' => $lastuser)); $DB->delete_records('block_instances', array('parentcontextid' => $frontpagepagecontext->id)); $DB->delete_records('course_modules', array('id' => $frontpagepagecontext->instanceid)); context_helper::cleanup_instances(); $count = 1; //system $count += $DB->count_records('user', array('deleted' => 0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEqual($DB->count_records('context'), $count); // ======= context cache size restrictions ============================== $testusers = array(); for ($i = 0; $i < CONTEXT_CACHE_MAX_SIZE + 100; $i++) { $user = new stdClass(); $user->auth = 'manual'; $user->firstname = 'xuser' . $i; $user->lastname = 'xuser' . $i; $user->username = '******' . $i; $user->password = '******'; $user->email = "xuser{$i}@example.com"; $user->confirmed = 1; $user->mnethostid = $CFG->mnet_localhost_id; $user->lang = $CFG->lang; $user->maildisplay = 1; $user->timemodified = time(); $user->lastip = '0.0.0.0'; $userid = $DB->insert_record('user', $user); $testusers[$i] = $userid; } context_helper::create_instances(null, true); context_helper::reset_caches(); for ($i = 0; $i < CONTEXT_CACHE_MAX_SIZE + 100; $i++) { context_user::instance($testusers[$i]); if ($i == CONTEXT_CACHE_MAX_SIZE - 1) { $this->assertEqual(context_inspection::test_context_cache_size(), CONTEXT_CACHE_MAX_SIZE); } else { if ($i == CONTEXT_CACHE_MAX_SIZE) { // once the limit is reached roughly 1/3 of records should be removed from cache $this->assertEqual(context_inspection::test_context_cache_size(), (int) (CONTEXT_CACHE_MAX_SIZE * (2 / 3) + 102)); } } } // We keep the first 100 cached $prevsize = context_inspection::test_context_cache_size(); for ($i = 0; $i < 100; $i++) { context_user::instance($testusers[$i]); $this->assertEqual(context_inspection::test_context_cache_size(), $prevsize); } context_user::instance($testusers[102]); $this->assertEqual(context_inspection::test_context_cache_size(), $prevsize + 1); unset($testusers); // ================================================================= // ======= basic test of legacy functions ========================== // ================================================================= // note: watch out, the fake site might be pretty borked already $this->assertIdentical(get_system_context(), context_system::instance()); foreach ($DB->get_records('context') as $contextid => $record) { $context = context::instance_by_id($contextid); $this->assertIdentical(get_context_instance_by_id($contextid), $context); $this->assertIdentical(get_context_instance($record->contextlevel, $record->instanceid), $context); $this->assertIdentical(get_parent_contexts($context), $context->get_parent_context_ids()); if ($context->id == SYSCONTEXTID) { $this->assertIdentical(get_parent_contextid($context), false); } else { $this->assertIdentical(get_parent_contextid($context), $context->get_parent_context()->id); } } $children = get_child_contexts($systemcontext); $this->assertEqual(count($children), $DB->count_records('context') - 1); unset($children); $DB->delete_records('context', array('contextlevel' => CONTEXT_BLOCK)); create_contexts(); $this->assertFalse($DB->record_exists('context', array('contextlevel' => CONTEXT_BLOCK))); $DB->set_field('context', 'depth', 0, array('contextlevel' => CONTEXT_BLOCK)); build_context_path(); $this->assertFalse($DB->record_exists('context', array('depth' => 0))); $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); $DB->delete_records('course', array('id' => $lastcourse)); $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); $DB->delete_records('course_categories', array('id' => $lastcategory)); $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); $DB->delete_records('user', array('id' => $lastuser)); $DB->delete_records('block_instances', array('parentcontextid' => $frontpagepagecontext->id)); $DB->delete_records('course_modules', array('id' => $frontpagepagecontext->instanceid)); cleanup_contexts(); $count = 1; //system $count += $DB->count_records('user', array('deleted' => 0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEqual($DB->count_records('context'), $count); context_helper::reset_caches(); preload_course_contexts($SITE->id); $this->assertEqual(context_inspection::test_context_cache_size(), 1); context_helper::reset_caches(); list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx'); $sql = "SELECT c.id {$select} FROM {course_categories} c {$join}"; $records = $DB->get_records_sql($sql); foreach ($records as $record) { context_instance_preload($record); $record = (array) $record; $this->assertEqual(1, count($record)); // only id left } $this->assertEqual(count($records), context_inspection::test_context_cache_size()); accesslib_clear_all_caches(true); $DB->delete_records('cache_flags', array()); mark_context_dirty($systemcontext->path); $dirty = get_cache_flags('accesslib/dirtycontexts', time() - 2); $this->assertTrue(isset($dirty[$systemcontext->path])); accesslib_clear_all_caches(false); $DB->delete_records('cache_flags', array()); $course = $DB->get_record('course', array('id' => $testcourses[2])); $context = get_context_instance(CONTEXT_COURSE, $course->id); $oldpath = $context->path; $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $categorycontext = context_coursecat::instance($miscid); $course->category = $miscid; $DB->update_record('course', $course); context_moved($context, $categorycontext); $context = get_context_instance(CONTEXT_COURSE, $course->id); $this->assertIdentical($context->get_parent_context(), $categorycontext); $this->assertTrue($DB->record_exists('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $testcourses[2]))); delete_context(CONTEXT_COURSE, $testcourses[2]); $this->assertFalse($DB->record_exists('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $testcourses[2]))); $name = get_contextlevel_name(CONTEXT_COURSE); $this->assertFalse(empty($name)); $context = get_context_instance(CONTEXT_COURSE, $testcourses[2]); $name = print_context_name($context); $this->assertFalse(empty($name)); $url = get_context_url($coursecontext); $this->assertFalse($url instanceof modole_url); $page = $DB->get_record('page', array('id' => $testpages[7])); $context = get_context_instance(CONTEXT_MODULE, $page->id); $coursecontext = get_course_context($context); $this->assertEqual($coursecontext->contextlevel, CONTEXT_COURSE); $this->assertEqual(get_courseid_from_context($context), $page->course); $caps = fetch_context_capabilities($systemcontext); $this->assertTrue(is_array($caps)); unset($caps); }
/** * Pre-uninstall hook. * * This is intended for disabling of plugin, some DB table purging, etc. * * NOTE: to be called from uninstall_plugin() only. * @private */ public function uninstall_cleanup() { global $DB, $CFG; // NOTE: this is a bit brute force way - it will not trigger events and hooks properly. // Nuke all role assignments. role_unassign_all(array('component' => 'enrol_' . $this->name)); // Purge participants. $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($this->name)); // Purge enrol instances. $DB->delete_records('enrol', array('enrol' => $this->name)); // Tweak enrol settings. if (!empty($CFG->enrol_plugins_enabled)) { $enabledenrols = explode(',', $CFG->enrol_plugins_enabled); $enabledenrols = array_unique($enabledenrols); $enabledenrols = array_flip($enabledenrols); unset($enabledenrols[$this->name]); $enabledenrols = array_flip($enabledenrols); if (is_array($enabledenrols)) { set_config('enrol_plugins_enabled', implode(',', $enabledenrols)); } } parent::uninstall_cleanup(); }
/** * Called for all enabled enrol plugins that returned true from is_cron_required(). * @return void */ public function cron() { global $CFG; // purge all roles if cohort sync disabled, those can be recreated later here in cron if (!enrol_is_enabled('cohort')) { role_unassign_all(array('component'=>'cohort_enrol')); return; } require_once("$CFG->dirroot/enrol/cohort/locallib.php"); enrol_cohort_sync(); }
// If its a company created course then assign the editor role to the user. if ($DB->record_exists('company_created_courses', array('companyid' => $companyid, 'courseid' => $companycourse->courseid))) { company_user::enrol($user, array($companycourse->courseid), $companycourse->companyid, $companycourseeditorrole->id); } else { company_user::enrol($user, array($companycourse->courseid), $companycourse->companyid, $companycoursenoneditorrole->id); } } } } } else { $departmentmanagerrole = $DB->get_record('role', array('shortname' => 'companydepartmentmanager')); $companyinstructorrole = $DB->get_record('role', array('shortname' => 'instructor')); $companymanagerrole = $DB->get_record('role', array('shortname' => 'companymanager')); role_unassign_all(array('roleid' => $departmentmanagerrole->id, 'userid' => $userid, 'contextid' => $systemcontext->id), false, false); role_unassign_all(array('roleid' => $companyinstructorrole->id, 'userid' => $userid, 'contextid' => $systemcontext->id), false, false); role_unassign_all(array('roleid' => $companymanagerrole->id, 'userid' => $userid, 'contextid' => $systemcontext->id), false, false); } } } } // End of code $usercreated = false; } $usercontext = context_user::instance($usernew->id); // Update preferences. useredit_update_user_preference($usernew); // Update tags. /* if (!empty($CFG->usetags)) { useredit_update_interests($usernew, $usernew->interests); } */ // Update user picture.
/** * Sync all ilios course links. * @param progress_trace $trace * @param int $courseid one course, empty mean all * @return int 0 means ok, 1 means error, 2 means plugin disabled */ function enrol_ilios_sync(progress_trace $trace, $courseid = NULL) { global $CFG, $DB; require_once "{$CFG->dirroot}/group/lib.php"; // Purge all roles if ilios sync disabled, those can be recreated later here by cron or CLI. if (!enrol_is_enabled('ilios')) { $trace->output('Ilios enrolment sync plugin is disabled, unassigning all plugin roles and stopping.'); role_unassign_all(array('component' => 'enrol_ilios')); return 2; } // Unfortunately this may take a long time, this script can be interrupted without problems. @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); $trace->output('Starting user enrolment synchronisation...'); $allroles = get_all_roles(); $iliosusers = array(); // cache $plugin = enrol_get_plugin('ilios'); $http = $plugin->get_http_client(); $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL); $moodleusersyncfield = 'idnumber'; $iliosusersyncfield = 'campusId'; // Iterate through all not enrolled yet users. $onecourse = $courseid ? "AND e.courseid = :courseid" : ""; $sql = "SELECT *\n FROM {enrol} e\n WHERE e.enrol = 'ilios' {$onecourse}"; $params = array(); $params['courseid'] = $courseid; $params['suspended'] = ENROL_USER_SUSPENDED; $instances = $DB->get_recordset_sql($sql, $params); foreach ($instances as $instance) { $synctype = $instance->customchar1; $syncid = $instance->customint1; $groups = $http->get($synctype . 's', array("id" => $syncid)); if (!empty($groups) && is_array($groups)) { $group = $groups[0]; if (!empty($group->users)) { $users = $http->getbyids('users', $group->users); $userids = array(); foreach ($users as $user) { if (!isset($iliosusers[$user->id])) { $iliosusers[$user->id] = null; if (!empty($user->{$iliosusersyncfield})) { $urec = $DB->get_record('user', array("{$moodleusersyncfield}" => $user->{$iliosusersyncfield})); if (!empty($urec)) { $iliosusers[$user->id] = array('id' => $urec->id, 'syncfield' => $urec->{$moodleusersyncfield}); } } } if ($iliosusers[$user->id] === null) { if (!empty($user->{$iliosusersyncfield})) { $trace->output("skipping: Cannot find {$iliosusersyncfield} " . $user->{$iliosusersyncfield} . " that matches Moodle user field {$moodleusersyncfield}.", 1); } else { $trace->output("skipping: Ilios user " . $user->id . " does not have a {$iliosusersyncfield} field.", 1); } } else { $userids[] = $userid = $iliosusers[$user->id]['id']; $ue = $DB->get_record('user_enrolments', array('enrolid' => $instance->id, 'userid' => $userid)); if (!empty($ue) && isset($ue->status)) { if ($ue->status == ENROL_USER_SUSPENDED) { $plugin->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE); $trace->output("unsuspending: userid {$userid} ==> courseid " . $instance->courseid . " via Ilios {$synctype} {$syncid}", 1); } } else { $plugin->enrol_user($instance, $userid); $trace->output("enrolling: userid {$userid} ==> courseid " . $instance->courseid . " via Ilios {$synctype} {$syncid}", 1); } } } // Unenrol as necessary. if (!empty($userids)) { $sql = "SELECT ue.*\n FROM {user_enrolments} ue\n WHERE ue.enrolid = {$instance->id}\n AND ue.userid NOT IN ( " . implode(",", $userids) . " )"; $rs = $DB->get_recordset_sql($sql); foreach ($rs as $ue) { if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { // Remove enrolment together with group membership, grades, preferences, etc. $plugin->unenrol_user($instance, $ue->userid); $trace->output("unenrolling: {$ue->userid} ==> " . $instance->courseid . " via Ilios {$synctype} {$syncid}", 1); } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES // Just disable and ignore any changes. if ($ue->status != ENROL_USER_SUSPENDED) { $plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); $context = context_course::instance($instance->courseid); role_unassign_all(array('userid' => $ue->userid, 'contextid' => $context->id, 'component' => 'enrol_ilios', 'itemid' => $instance->id)); $trace->output("suspending and unsassigning all roles: userid " . $ue->userid . " ==> courseid " . $instance->courseid, 1); } } } $rs->close(); } } } } $instances->close(); unset($iliosusers); // Now assign all necessary roles to enrolled users - skip suspended instances and users. $onecourse = $courseid ? "AND e.courseid = :courseid" : ""; $sql = "SELECT e.roleid, ue.userid, c.id AS contextid, e.id AS itemid, e.courseid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'ilios' AND e.status = :statusenabled {$onecourse})\n JOIN {role} r ON (r.id = e.roleid)\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :coursecontext)\n JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0)\n LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = ue.userid AND ra.itemid = e.id AND ra.component = 'enrol_ilios' AND e.roleid = ra.roleid)\n WHERE ue.status = :useractive AND ra.id IS NULL"; $params = array(); $params['statusenabled'] = ENROL_INSTANCE_ENABLED; $params['useractive'] = ENROL_USER_ACTIVE; $params['coursecontext'] = CONTEXT_COURSE; $params['courseid'] = $courseid; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ra) { role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_ilios', $ra->itemid); $trace->output("assigning role: {$ra->userid} ==> {$ra->courseid} as " . $allroles[$ra->roleid]->shortname, 1); } $rs->close(); // Remove unwanted roles - sync role can not be changed, we only remove role when unenrolled. $onecourse = $courseid ? "AND e.courseid = :courseid" : ""; $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid\n FROM {role_assignments} ra\n JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :coursecontext)\n JOIN {enrol} e ON (e.id = ra.itemid AND e.enrol = 'ilios' {$onecourse})\n LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :useractive)\n WHERE ra.component = 'enrol_ilios' AND (ue.id IS NULL OR e.status <> :statusenabled)"; $params = array(); $params['statusenabled'] = ENROL_INSTANCE_ENABLED; $params['useractive'] = ENROL_USER_ACTIVE; $params['coursecontext'] = CONTEXT_COURSE; $params['courseid'] = $courseid; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ra) { role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_ilios', $ra->itemid); $trace->output("unassigning role: {$ra->userid} ==> {$ra->courseid} as " . $allroles[$ra->roleid]->shortname, 1); } $rs->close(); // Finally sync groups. $onecourse = $courseid ? "AND e.courseid = :courseid" : ""; // Remove invalid. $sql = "SELECT gm.*, e.courseid, g.name AS groupname\n FROM {groups_members} gm\n JOIN {groups} g ON (g.id = gm.groupid)\n JOIN {enrol} e ON (e.enrol = 'ilios' AND e.courseid = g.courseid {$onecourse})\n JOIN {user_enrolments} ue ON (ue.userid = gm.userid AND ue.enrolid = e.id)\n WHERE gm.component='enrol_ilios' AND gm.itemid = e.id AND g.id <> e.customint6"; $params = array(); $params['courseid'] = $courseid; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $gm) { groups_remove_member($gm->groupid, $gm->userid); $trace->output("removing user from group: {$gm->userid} ==> {$gm->courseid} - {$gm->groupname}", 1); } $rs->close(); // Add missing. $sql = "SELECT ue.*, g.id AS groupid, e.courseid, g.name AS groupname\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'ilios' {$onecourse})\n JOIN {groups} g ON (g.courseid = e.courseid AND g.id = e.customint6)\n JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0)\n LEFT JOIN {groups_members} gm ON (gm.groupid = g.id AND gm.userid = ue.userid)\n WHERE gm.id IS NULL"; $params = array(); $params['courseid'] = $courseid; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { groups_add_member($ue->groupid, $ue->userid, 'enrol_ilios', $ue->enrolid); $trace->output("adding user to group: {$ue->userid} ==> {$ue->courseid} - {$ue->groupname}", 1); } $rs->close(); $trace->output('...user enrolment synchronisation finished.'); return 0; }
/** * Triggered via course_deleted event. * * @param \core\event\course_deleted $event * @return bool true on success */ public static function course_deleted(\core\event\course_deleted $event) { global $DB; if (!enrol_is_enabled('meta')) { // This is slow, let enrol_meta_sync() deal with disabled plugin. return true; } // Does anything want to sync with this parent? if (!($enrols = $DB->get_records('enrol', array('customint1' => $event->objectid, 'enrol' => 'meta'), 'courseid ASC, id ASC'))) { return true; } $plugin = enrol_get_plugin('meta'); $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES); if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { // Simple, just delete this instance which purges all enrolments, // admins were warned that this is risky setting! foreach ($enrols as $enrol) { $plugin->delete_instance($enrol); } return true; } foreach ($enrols as $enrol) { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { // This makes all enrolments suspended very quickly. $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED); } if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { $context = context_course::instance($enrol->courseid); role_unassign_all(array('contextid' => $context->id, 'component' => 'enrol_meta', 'itemid' => $enrol->id)); } } return true; }
/** * Process user enrolment line. * * @param progress_trace $trace * @param string $action * @param int $roleid * @param stdClass $user * @param stdClass $course * @param int $timestart * @param int $timeend * @param bool $buffer_if_future */ protected function process_records(progress_trace $trace, $action, $roleid, $user, $course, $timestart, $timeend, $buffer_if_future = true) { global $CFG, $DB; // Check if timestart is for future processing. if ($timestart > time() and $buffer_if_future) { // Populate into enrol_flatfile table as a future role to be assigned by cron. // Note: since 2.0 future enrolments do not cause problems if you disable guest access. $future_en = new stdClass(); $future_en->action = $action; $future_en->roleid = $roleid; $future_en->userid = $user->id; $future_en->courseid = $course->id; $future_en->timestart = $timestart; $future_en->timeend = $timeend; $future_en->timemodified = time(); $DB->insert_record('enrol_flatfile', $future_en); $trace->output("User {$user->id} will be enrolled later into course {$course->id} using role {$roleid} ({$timestart}, {$timeend})", 1); return; } $context = context_course::instance($course->id); if ($action === 'add') { // Clear the buffer just in case there were some future enrolments. $DB->delete_records('enrol_flatfile', array('userid' => $user->id, 'courseid' => $course->id, 'roleid' => $roleid)); $instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'flatfile')); if (empty($instance)) { // Only add an enrol instance to the course if non-existent. $enrolid = $this->add_instance($course); $instance = $DB->get_record('enrol', array('id' => $enrolid)); } $notify = false; if ($ue = $DB->get_record('user_enrolments', array('enrolid' => $instance->id, 'userid' => $user->id))) { // Update only. $this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE, $timestart, $timeend); if (!$DB->record_exists('role_assignments', array('contextid' => $context->id, 'roleid' => $roleid, 'userid' => $user->id, 'component' => 'enrol_flatfile', 'itemid' => $instance->id))) { role_assign($roleid, $user->id, $context->id, 'enrol_flatfile', $instance->id); } $trace->output("User {$user->id} enrolment updated in course {$course->id} using role {$roleid} ({$timestart}, {$timeend})", 1); } else { // Enrol the user with this plugin instance. $this->enrol_user($instance, $user->id, $roleid, $timestart, $timeend); $trace->output("User {$user->id} enrolled in course {$course->id} using role {$roleid} ({$timestart}, {$timeend})", 1); $notify = true; } if ($notify and $this->get_config('mailstudents')) { $oldforcelang = force_current_language($user->lang); // Send welcome notification to enrolled users. $a = new stdClass(); $a->coursename = format_string($course->fullname, true, array('context' => $context)); $a->profileurl = "{$CFG->wwwroot}/user/view.php?id={$user->id}&course={$course->id}"; $subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context))); $eventdata = new stdClass(); $eventdata->modulename = 'moodle'; $eventdata->component = 'enrol_flatfile'; $eventdata->name = 'flatfile_enrolment'; $eventdata->userfrom = $this->get_enroller($course->id); $eventdata->userto = $user; $eventdata->subject = $subject; $eventdata->fullmessage = get_string('welcometocoursetext', '', $a); $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = ''; $eventdata->smallmessage = ''; if (message_send($eventdata)) { $trace->output("Notified enrolled user", 1); } else { $trace->output("Failed to notify enrolled user", 1); } force_current_language($oldforcelang); } if ($notify and $this->get_config('mailteachers', 0)) { // Notify person responsible for enrolments. $enroller = $this->get_enroller($course->id); $oldforcelang = force_current_language($enroller->lang); $a = new stdClass(); $a->course = format_string($course->fullname, true, array('context' => $context)); $a->user = fullname($user); $subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context))); $eventdata = new stdClass(); $eventdata->modulename = 'moodle'; $eventdata->component = 'enrol_flatfile'; $eventdata->name = 'flatfile_enrolment'; $eventdata->userfrom = get_admin(); $eventdata->userto = $enroller; $eventdata->subject = $subject; $eventdata->fullmessage = get_string('enrolmentnewuser', 'enrol', $a); $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = ''; $eventdata->smallmessage = ''; if (message_send($eventdata)) { $trace->output("Notified enroller {$eventdata->userto->id}", 1); } else { $trace->output("Failed to notify enroller {$eventdata->userto->id}", 1); } force_current_language($oldforcelang); } return; } else { if ($action === 'del') { // Clear the buffer just in case there were some future enrolments. $DB->delete_records('enrol_flatfile', array('userid' => $user->id, 'courseid' => $course->id, 'roleid' => $roleid)); $action = $this->get_config('unenrolaction'); if ($action == ENROL_EXT_REMOVED_KEEP) { $trace->output("del action is ignored", 1); return; } // Loops through all enrolment methods, try to unenrol if roleid somehow matches. $instances = $DB->get_records('enrol', array('courseid' => $course->id)); $unenrolled = false; foreach ($instances as $instance) { if (!($ue = $DB->get_record('user_enrolments', array('enrolid' => $instance->id, 'userid' => $user->id)))) { continue; } if ($instance->enrol === 'flatfile') { $plugin = $this; } else { if (!enrol_is_enabled($instance->enrol)) { continue; } if (!($plugin = enrol_get_plugin($instance->enrol))) { continue; } if (!$plugin->allow_unenrol_user($instance, $ue)) { continue; } } // For some reason the del action includes a role name, this complicates everything. $componentroles = array(); $manualroles = array(); $ras = $DB->get_records('role_assignments', array('userid' => $user->id, 'contextid' => $context->id)); foreach ($ras as $ra) { if ($ra->component === '') { $manualroles[$ra->roleid] = $ra->roleid; } else { if ($ra->component === 'enrol_' . $instance->enrol and $ra->itemid == $instance->id) { $componentroles[$ra->roleid] = $ra->roleid; } } } if ($componentroles and !isset($componentroles[$roleid])) { // Do not unenrol using this method, user has some other protected role! continue; } else { if (empty($ras)) { // If user does not have any roles then let's just suspend as many methods as possible. } else { if (!$plugin->roles_protected()) { if (!$componentroles and $manualroles and !isset($manualroles[$roleid])) { // Most likely we want to keep users enrolled because they have some other course roles. continue; } } } } if ($action == ENROL_EXT_REMOVED_UNENROL) { $unenrolled = true; if (!$plugin->roles_protected()) { role_unassign_all(array('contextid' => $context->id, 'userid' => $user->id, 'roleid' => $roleid, 'component' => '', 'itemid' => 0), true); } $plugin->unenrol_user($instance, $user->id); $trace->output("User {$user->id} was unenrolled from course {$course->id} (enrol_{$instance->enrol})", 1); } else { if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { if ($plugin->allow_manage($instance)) { if ($ue->status == ENROL_USER_ACTIVE) { $unenrolled = true; $plugin->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED); if (!$plugin->roles_protected()) { role_unassign_all(array('contextid' => $context->id, 'userid' => $user->id, 'component' => 'enrol_' . $instance->enrol, 'itemid' => $instance->id), true); role_unassign_all(array('contextid' => $context->id, 'userid' => $user->id, 'roleid' => $roleid, 'component' => '', 'itemid' => 0), true); } $trace->output("User {$user->id} enrolment was suspended in course {$course->id} (enrol_{$instance->enrol})", 1); } } } } } if (!$unenrolled) { if (0 == $DB->count_records('role_assignments', array('userid' => $user->id, 'contextid' => $context->id))) { role_unassign_all(array('contextid' => $context->id, 'userid' => $user->id, 'component' => '', 'itemid' => 0), true); } $trace->output("User {$user->id} (with role {$roleid}) not unenrolled from course {$course->id}", 1); } return; } } }
/** * Unenrol user from course, * the last unenrolment removes all remaining roles. * * @param stdClass $instance * @param int $userid * @return void */ public function unenrol_user(stdClass $instance, $userid) { global $CFG, $USER, $DB; require_once "{$CFG->dirroot}/group/lib.php"; $name = $this->get_name(); $courseid = $instance->courseid; if ($instance->enrol !== $name) { throw new coding_exception('invalid enrol instance!'); } $context = context_course::instance($instance->courseid, MUST_EXIST); if (!($ue = $DB->get_record('user_enrolments', array('enrolid' => $instance->id, 'userid' => $userid)))) { // weird, user not enrolled return; } // Remove all users groups linked to this enrolment instance. if ($gms = $DB->get_records('groups_members', array('userid' => $userid, 'component' => 'enrol_' . $name, 'itemid' => $instance->id))) { foreach ($gms as $gm) { groups_remove_member($gm->groupid, $gm->userid); } } role_unassign_all(array('userid' => $userid, 'contextid' => $context->id, 'component' => 'enrol_' . $name, 'itemid' => $instance->id)); $DB->delete_records('user_enrolments', array('id' => $ue->id)); // add extra info and trigger event $ue->courseid = $courseid; $ue->enrol = $name; $sql = "SELECT 'x'\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid)\n WHERE ue.userid = :userid AND e.courseid = :courseid"; if ($DB->record_exists_sql($sql, array('userid' => $userid, 'courseid' => $courseid))) { $ue->lastenrol = false; events_trigger('user_unenrolled', $ue); // user still has some enrolments, no big cleanup yet } else { // the big cleanup IS necessary! require_once "{$CFG->libdir}/gradelib.php"; // remove all remaining roles role_unassign_all(array('userid' => $userid, 'contextid' => $context->id), true, false); //clean up ALL invisible user data from course if this is the last enrolment - groups, grades, etc. groups_delete_group_members($courseid, $userid); grade_user_unenrol($courseid, $userid); $DB->delete_records('user_lastaccess', array('userid' => $userid, 'courseid' => $courseid)); $ue->lastenrol = true; // means user not enrolled any more events_trigger('user_unenrolled', $ue); } // reset all enrol caches $context->mark_dirty(); // reset current user enrolment caching if ($userid == $USER->id) { if (isset($USER->enrol['enrolled'][$courseid])) { unset($USER->enrol['enrolled'][$courseid]); } if (isset($USER->enrol['tempguest'][$courseid])) { unset($USER->enrol['tempguest'][$courseid]); remove_temp_course_roles($context); } } }
/** * Called for all enabled enrol plugins that returned true from is_cron_required(). * @return void */ public function cron() { global $DB, $CFG; // purge all roles if jwc sync disabled, those can be recreated later here in cron if (!enrol_is_enabled('jwc')) { role_unassign_all(array('component' => 'jwc_enrol')); return; } // 管理员可以设定清除所有选课 if ($this->get_config('unenrolall')) { $instances = $DB->get_records('enrol', array('enrol' => 'jwc')); foreach ($instances as $instance) { //first unenrol all users $participants = $DB->get_recordset('user_enrolments', array('enrolid' => $instance->id)); foreach ($participants as $participant) { $this->unenrol_user($instance, $participant->userid); } $participants->close(); // now clean up all remainders that were not removed correctly $DB->delete_records('role_assignments', array('itemid' => $instance->id, 'component' => 'jwc')); $DB->delete_records('user_enrolments', array('enrolid' => $instance->id)); } $this->set_config('unenrolall', 0); return; } // 暂时禁用cron。将来通过是否有数据更改来决定cron同步周期 // require_once("$CFG->dirroot/enrol/jwc/locallib.php"); // enrol_jwc_sync(); }
/** * Sync all meta course links. * * @param int $courseid one course, empty mean all * @param bool $verbose verbose CLI output * @return int 0 means ok, 1 means error, 2 means plugin disabled */ public function sync($courseid = null, $verbose = false) { global $DB; if (!enrol_is_enabled('self')) { return 2; } // Unfortunately this may take a long time, execution can be interrupted safely here. @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); if ($verbose) { mtrace('Verifying self-enrolments...'); } $params = array('now' => time(), 'useractive' => ENROL_USER_ACTIVE, 'courselevel' => CONTEXT_COURSE); $coursesql = ""; if ($courseid) { $coursesql = "AND e.courseid = :courseid"; $params['courseid'] = $courseid; } // Note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set) // and that user accessed course at least once too (=== user_lastaccess record exists). // First deal with users that did not log in for a really long time - they do not have user_lastaccess records. $sql = "SELECT e.*, ue.userid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n JOIN {user} u ON u.id = ue.userid\n WHERE :now - u.lastaccess > e.customint2\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $instance) { $userid = $instance->userid; unset($instance->userid); $this->unenrol_user($instance, $userid); if ($verbose) { $days = $instance->customint2 / 60 * 60 * 24; mtrace(" unenrolling user {$userid} from course {$instance->courseid} as they have did not log in for at least {$days} days"); } } $rs->close(); // Now unenrol from course user did not visit for a long time. $sql = "SELECT e.*, ue.userid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)\n JOIN {user_lastaccess} ul ON (ul.userid = ue.userid AND ul.courseid = e.courseid)\n WHERE :now - ul.timeaccess > e.customint2\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $instance) { $userid = $instance->userid; unset($instance->userid); $this->unenrol_user($instance, $userid); if ($verbose) { $days = $instance->customint2 / 60 * 60 * 24; mtrace(" unenrolling user {$userid} from course {$instance->courseid} as they have did not access course for at least {$days} days"); } } $rs->close(); // Deal with expired accounts. $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); if ($action == ENROL_EXT_REMOVED_UNENROL) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; if ($instance->roleid) { role_unassign($instance->roleid, $ue->userid, $ue->contextid, '', 0); } $this->unenrol_user($instance, $ue->userid); if ($verbose) { mtrace(" unenrolling expired user {$ue->userid} from course {$instance->courseid}"); } } $rs->close(); unset($instances); } else { if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self')\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n AND ue.status = :useractive\n {$coursesql}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; if (1 == $DB->count_records('role_assignments', array('userid' => $ue->userid, 'contextid' => $ue->contextid))) { role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); } else { if ($instance->roleid) { role_unassign($instance->roleid, $ue->userid, $ue->contextid, '', 0); } } $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); if ($verbose) { mtrace(" suspending expired user {$ue->userid} in course {$instance->courseid}"); } } $rs->close(); unset($instances); } else { // ENROL_EXT_REMOVED_KEEP means no changes. } } if ($verbose) { mtrace('...user self-enrolment updates finished.'); } return 0; }
} // Try and make the manage instances node on the navigation active. $courseadmin = $PAGE->settingsnav->get('courseadmin'); if ($courseadmin && $courseadmin->get('users') && $courseadmin->get('users')->get('manageinstances')) { $courseadmin->get('users')->get('manageinstances')->make_active(); } $mform = new enrol_cohort_edit_form(null, array($instance, $enrol, $course)); if ($mform->is_cancelled()) { redirect($returnurl); } else { if ($data = $mform->get_data()) { if ($data->id) { // NOTE: no cohort changes here!!! if ($data->roleid != $instance->roleid) { // The sync script can only add roles, for perf reasons it does not modify them. role_unassign_all(array('contextid' => $context->id, 'roleid' => $instance->roleid, 'component' => 'enrol_cohort', 'itemid' => $instance->id)); } $instance->name = $data->name; $instance->status = $data->status; $instance->roleid = $data->roleid; $instance->customint2 = $data->customint2; $instance->timemodified = time(); // Create a new group for the cohort if requested. if ($data->customint2 == COHORT_CREATE_GROUP) { require_capability('moodle/course:managegroups', $context); $groupid = enrol_cohort_create_new_group($course->id, $data->customint1); $instance->customint2 = $groupid; } $DB->update_record('enrol', $instance); \core\event\enrol_instance_updated::create_from_record($instance)->trigger(); } else {
/** * A small functional test of accesslib functions and classes. * @return void */ public function test_everything_in_accesslib() { global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE; $this->resetAfterTest(true); $generator = $this->getDataGenerator(); // Fill the site with some real data $testcategories = array(); $testcourses = array(); $testpages = array(); $testblocks = array(); $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id'); $systemcontext = context_system::instance(); $frontpagecontext = context_course::instance(SITEID); // Add block to system context $bi = $generator->create_block('online_users'); context_block::instance($bi->id); $testblocks[] = $bi->id; // Some users $testusers = array(); for($i=0; $i<20; $i++) { $user = $generator->create_user(); $testusers[$i] = $user->id; $usercontext = context_user::instance($user->id); // Add block to user profile $bi = $generator->create_block('online_users', array('parentcontextid'=>$usercontext->id)); $testblocks[] = $bi->id; } // Deleted user - should be ignored everywhere, can not have context $generator->create_user(array('deleted'=>1)); // Add block to frontpage $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagecontext->id)); $frontpageblockcontext = context_block::instance($bi->id); $testblocks[] = $bi->id; // Add a resource to frontpage $page = $generator->create_module('page', array('course'=>$SITE->id)); $testpages[] = $page->id; $frontpagepagecontext = context_module::instance($page->cmid); // Add block to frontpage resource $bi = $generator->create_block('online_users', array('parentcontextid'=>$frontpagepagecontext->id)); $frontpagepageblockcontext = context_block::instance($bi->id); $testblocks[] = $bi->id; // Some nested course categories with courses $manualenrol = enrol_get_plugin('manual'); $parentcat = 0; for($i=0; $i<5; $i++) { $cat = $generator->create_category(array('parent'=>$parentcat)); $testcategories[] = $cat->id; $catcontext = context_coursecat::instance($cat->id); $parentcat = $cat->id; if ($i >=4) { continue; } // Add resource to each category $bi = $generator->create_block('online_users', array('parentcontextid'=>$catcontext->id)); context_block::instance($bi->id); // Add a few courses to each category for($j=0; $j<6; $j++) { $course = $generator->create_course(array('category'=>$cat->id)); $testcourses[] = $course->id; $coursecontext = context_course::instance($course->id); if ($j >= 5) { continue; } // Add manual enrol instance $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$course->id))); // Add block to each course $bi = $generator->create_block('online_users', array('parentcontextid'=>$coursecontext->id)); $testblocks[] = $bi->id; // Add a resource to each course $page = $generator->create_module('page', array('course'=>$course->id)); $testpages[] = $page->id; $modcontext = context_module::instance($page->cmid); // Add block to each module $bi = $generator->create_block('online_users', array('parentcontextid'=>$modcontext->id)); $testblocks[] = $bi->id; } } // Make sure all contexts were created properly $count = 1; //system $count += $DB->count_records('user', array('deleted'=>0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEquals($DB->count_records('context'), $count); $this->assertEquals($DB->count_records('context', array('depth'=>0)), 0); $this->assertEquals($DB->count_records('context', array('path'=>NULL)), 0); // ====== context_helper::get_level_name() ================================ $levels = context_helper::get_all_levels(); foreach ($levels as $level=>$classname) { $name = context_helper::get_level_name($level); $this->assertFalse(empty($name)); } // ======= context::instance_by_id(), context_xxx::instance(); $context = context::instance_by_id($frontpagecontext->id); $this->assertSame($context->contextlevel, CONTEXT_COURSE); $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING)); try { context::instance_by_id(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $this->assertTrue(context_system::instance() instanceof context_system); $this->assertTrue(context_coursecat::instance($testcategories[0]) instanceof context_coursecat); $this->assertTrue(context_course::instance($testcourses[0]) instanceof context_course); $this->assertTrue(context_module::instance($testpages[0]) instanceof context_module); $this->assertTrue(context_block::instance($testblocks[0]) instanceof context_block); $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_course::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_module::instance(-1, IGNORE_MISSING)); $this->assertFalse(context_block::instance(-1, IGNORE_MISSING)); try { context_coursecat::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_course::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_module::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } try { context_block::instance(-1); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } // ======= $context->get_url(), $context->get_context_name(), $context->get_capabilities() ========= $testcontexts = array(); $testcontexts[CONTEXT_SYSTEM] = context_system::instance(); $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]); $testcontexts[CONTEXT_COURSE] = context_course::instance($testcourses[0]); $testcontexts[CONTEXT_MODULE] = context_module::instance($testpages[0]); $testcontexts[CONTEXT_BLOCK] = context_block::instance($testblocks[0]); foreach ($testcontexts as $context) { $name = $context->get_context_name(true, true); $this->assertFalse(empty($name)); $this->assertTrue($context->get_url() instanceof moodle_url); $caps = $context->get_capabilities(); $this->assertTrue(is_array($caps)); foreach ($caps as $cap) { $cap = (array)$cap; $this->assertSame(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask')); } } unset($testcontexts); // ===== $context->get_course_context() ========================================= $this->assertFalse($systemcontext->get_course_context(false)); try { $systemcontext->get_course_context(); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $context = context_coursecat::instance($testcategories[0]); $this->assertFalse($context->get_course_context(false)); try { $context->get_course_context(); $this->fail('exception expected'); } catch (Exception $e) { $this->assertTrue(true); } $this->assertSame($frontpagecontext->get_course_context(true), $frontpagecontext); $this->assertSame($frontpagepagecontext->get_course_context(true), $frontpagecontext); $this->assertSame($frontpagepageblockcontext->get_course_context(true), $frontpagecontext); // ======= $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() ======= $userid = reset($testusers); $usercontext = context_user::instance($userid); $this->assertSame($usercontext->get_parent_context(), $systemcontext); $this->assertSame($usercontext->get_parent_contexts(), array($systemcontext->id=>$systemcontext)); $this->assertSame($usercontext->get_parent_contexts(true), array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext)); $this->assertSame($systemcontext->get_parent_contexts(), array()); $this->assertSame($systemcontext->get_parent_contexts(true), array($systemcontext->id=>$systemcontext)); $this->assertSame($systemcontext->get_parent_context_ids(), array()); $this->assertSame($systemcontext->get_parent_context_ids(true), array($systemcontext->id)); $this->assertSame($frontpagecontext->get_parent_context(), $systemcontext); $this->assertSame($frontpagecontext->get_parent_contexts(), array($systemcontext->id=>$systemcontext)); $this->assertSame($frontpagecontext->get_parent_contexts(true), array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext)); $this->assertSame($frontpagecontext->get_parent_context_ids(), array($systemcontext->id)); $this->assertEquals($frontpagecontext->get_parent_context_ids(true), array($frontpagecontext->id, $systemcontext->id)); $this->assertSame($systemcontext->get_parent_context(), false); $frontpagecontext = context_course::instance($SITE->id); $parent = $systemcontext; foreach ($testcategories as $catid) { $catcontext = context_coursecat::instance($catid); $this->assertSame($catcontext->get_parent_context(), $parent); $parent = $catcontext; } $this->assertSame($frontpagepagecontext->get_parent_context(), $frontpagecontext); $this->assertSame($frontpageblockcontext->get_parent_context(), $frontpagecontext); $this->assertSame($frontpagepageblockcontext->get_parent_context(), $frontpagepagecontext); // ====== $context->get_child_contexts() ================================ $CFG->debug = 0; $children = $systemcontext->get_child_contexts(); $CFG->debug = DEBUG_DEVELOPER; $this->assertEquals(count($children)+1, $DB->count_records('context')); $context = context_coursecat::instance($testcategories[3]); $children = $context->get_child_contexts(); $countcats = 0; $countcourses = 0; $countblocks = 0; foreach ($children as $child) { if ($child->contextlevel == CONTEXT_COURSECAT) { $countcats++; } if ($child->contextlevel == CONTEXT_COURSE) { $countcourses++; } if ($child->contextlevel == CONTEXT_BLOCK) { $countblocks++; } } $this->assertEquals(count($children), 8); $this->assertEquals($countcats, 1); $this->assertEquals($countcourses, 6); $this->assertEquals($countblocks, 1); $context = context_course::instance($testcourses[2]); $children = $context->get_child_contexts(); $this->assertEquals(count($children), 7); // depends on number of default blocks $context = context_module::instance($testpages[3]); $children = $context->get_child_contexts(); $this->assertEquals(count($children), 1); $context = context_block::instance($testblocks[1]); $children = $context->get_child_contexts(); $this->assertEquals(count($children), 0); unset($children); unset($countcats); unset($countcourses); unset($countblocks); // ======= context_helper::reset_caches() ============================ context_helper::reset_caches(); $this->assertEquals(context_inspection::test_context_cache_size(), 0); context_course::instance($SITE->id); $this->assertEquals(context_inspection::test_context_cache_size(), 1); // ======= context preloading ======================================== context_helper::reset_caches(); $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')." FROM {context} c WHERE c.contextlevel <> ".CONTEXT_SYSTEM; $records = $DB->get_records_sql($sql); $firstrecord = reset($records); $columns = context_helper::get_preload_record_columns('c'); $firstrecord = (array)$firstrecord; $this->assertSame(array_keys($firstrecord), array_values($columns)); context_helper::reset_caches(); foreach ($records as $record) { context_helper::preload_from_record($record); $this->assertEquals($record, new stdClass()); } $this->assertEquals(context_inspection::test_context_cache_size(), count($records)); unset($records); unset($columns); context_helper::reset_caches(); context_helper::preload_course($SITE->id); $this->assertEquals(7, context_inspection::test_context_cache_size()); // depends on number of default blocks // ====== assign_capability(), unassign_capability() ==================== $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertFalse($rc); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id); $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertEquals($rc->permission, CAP_ALLOW); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id); $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertEquals($rc->permission, CAP_ALLOW); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true); $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertEquals($rc->permission, CAP_PREVENT); assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext); $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertFalse($rc); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext); unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true); $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); $this->assertFalse($rc); unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true); unset($rc); accesslib_clear_all_caches(false); // must be done after assign_capability() // ======= role_assign(), role_unassign(), role_unassign_all() ============== $context = context_course::instance($testcourses[1]); $this->assertEquals($DB->count_records('role_assignments', array('contextid'=>$context->id)), 0); role_assign($allroles['teacher'], $testusers[1], $context->id); role_assign($allroles['teacher'], $testusers[2], $context->id); role_assign($allroles['manager'], $testusers[1], $context->id); $this->assertEquals($DB->count_records('role_assignments', array('contextid'=>$context->id)), 3); role_unassign($allroles['teacher'], $testusers[1], $context->id); $this->assertEquals($DB->count_records('role_assignments', array('contextid'=>$context->id)), 2); role_unassign_all(array('contextid'=>$context->id)); $this->assertEquals($DB->count_records('role_assignments', array('contextid'=>$context->id)), 0); unset($context); accesslib_clear_all_caches(false); // just in case // ====== has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends ======================== $adminid = get_admin()->id; $guestid = $CFG->siteguest; // Enrol some users into some courses $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST); $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST); $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id'); $cm1 = reset($cms); $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id'); $block1 = reset($blocks); $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id)); $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id)); for($i=0; $i<9; $i++) { $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']); } $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']); $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']); for($i=10; $i<15; $i++) { $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']); } $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']); // Add tons of role assignments - the more the better role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2])); role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1])); role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id)); role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id)); role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id)); role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id)); role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id)); role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id)); role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id)); role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id)); // Add tons of overrides - the more the better assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true); assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true); assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true); assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true); assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true); assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true); assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true); assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true); assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true); assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true); assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true); accesslib_clear_all_caches(false); // must be done after assign_capability() // Extra tests for guests and not-logged-in users because they can not be verified by cross checking // with get_users_by_capability() where they are ignored $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid)); $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid)); $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid)); $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid)); $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0)); $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0)); $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0)); $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0)); $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11])); $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11])); $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11])); $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11])); $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9])); $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9])); $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9])); // Test the list of enrolled users $coursecontext = context_course::instance($course1->id); $enrolled = get_enrolled_users($coursecontext); $this->assertEquals(count($enrolled), 10); for($i=0; $i<10; $i++) { $this->assertTrue(isset($enrolled[$testusers[$i]])); } $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update'); $this->assertEquals(count($enrolled), 1); $this->assertTrue(isset($enrolled[$testusers[9]])); unset($enrolled); // role switching $userid = $testusers[9]; $USER = $DB->get_record('user', array('id'=>$userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); $this->assertFalse(is_role_switched($course1->id)); role_switch($allroles['student'], $coursecontext); $this->assertTrue(is_role_switched($course1->id)); $this->assertEquals($USER->access['rsw'][$coursecontext->path], $allroles['student']); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); role_switch(0, $coursecontext); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); $userid = $adminid; $USER = $DB->get_record('user', array('id'=>$userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $blockcontext = context_block::instance($block1->id); $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); role_switch($allroles['student'], $coursecontext); $this->assertEquals($USER->access['rsw'][$coursecontext->path], $allroles['student']); $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); load_all_capabilities(); $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); // temp course role for enrol $DB->delete_records('cache_flags', array()); // this prevents problem with dirty contexts immediately resetting the temp role - this is a known problem... $userid = $testusers[5]; $roleid = $allroles['editingteacher']; $USER = $DB->get_record('user', array('id'=>$userid)); load_all_capabilities(); $coursecontext = context_course::instance($course1->id); $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid])); load_temp_course_role($coursecontext, $roleid); $this->assertEquals($USER->access['ra'][$coursecontext->path][$roleid], $roleid); $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); remove_temp_course_roles($coursecontext); $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); load_temp_course_role($coursecontext, $roleid); reload_all_capabilities(); $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); $USER = new stdClass(); $USER->id = 0; // Now cross check has_capability() with get_users_by_capability(), each using different code paths, // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong, // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users) $contexts = $DB->get_records('context', array(), 'id'); $contexts = array_values($contexts); $capabilities = $DB->get_records('capabilities', array(), 'id'); $capabilities = array_values($capabilities); $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']); $userids = array_values($testusers); $userids[] = get_admin()->id; if (!PHPUNIT_LONGTEST) { $contexts = array_slice($contexts, 0, 10); $capabilities = array_slice($capabilities, 0, 5); $userids = array_slice($userids, 0, 5); } // Random time! //srand(666); foreach($userids as $userid) { // no guest or deleted // each user gets 0-10 random roles $rcount = rand(0, 10); for($j=0; $j<$rcount; $j++) { $roleid = $roles[rand(0, count($roles)-1)]; $contextid = $contexts[rand(0, count($contexts)-1)]->id; role_assign($roleid, $userid, $contextid); } } $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT); $maxoverrides = count($contexts)*10; for($j=0; $j<$maxoverrides; $j++) { $roleid = $roles[rand(0, count($roles)-1)]; $contextid = $contexts[rand(0, count($contexts)-1)]->id; $permission = $permissions[rand(0,count($permissions)-1)]; $capname = $capabilities[rand(0, count($capabilities)-1)]->name; assign_capability($capname, $permission, $roleid, $contextid, true); } unset($permissions); unset($roles); accesslib_clear_all_caches(false); // must be done after assign_capability() // Test time - let's set up some real user, just in case the logic for USER affects the others... $USER = $DB->get_record('user', array('id'=>$testusers[3])); load_all_capabilities(); $userids[] = $CFG->siteguest; $userids[] = 0; // not-logged-in user $userids[] = -1; // non-existent user foreach ($contexts as $crecord) { $context = context::instance_by_id($crecord->id); if ($coursecontext = $context->get_course_context(false)) { $enrolled = get_enrolled_users($context); } else { $enrolled = array(); } foreach ($capabilities as $cap) { $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username'); if ($enrolled) { $enrolledwithcap = get_enrolled_users($context, $cap->name); } else { $enrolledwithcap = array(); } foreach ($userids as $userid) { if ($userid == 0 or isguestuser($userid)) { if ($userid == 0) { $CFG->forcelogin = true; $this->assertFalse(has_capability($cap->name, $context, $userid)); unset($CFG->forcelogin); } if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) { $this->assertFalse(has_capability($cap->name, $context, $userid)); } $this->assertFalse(isset($allowed[$userid])); } else { if (is_siteadmin($userid)) { $this->assertTrue(has_capability($cap->name, $context, $userid, true)); } $hascap = has_capability($cap->name, $context, $userid, false); $this->assertSame($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); if (isset($enrolled[$userid])) { $this->assertSame(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); } } } } } // Back to nobody $USER = new stdClass(); $USER->id = 0; unset($contexts); unset($userids); unset($capabilities); // Now let's do all the remaining tests that break our carefully prepared fake site // ======= $context->mark_dirty() ======================================= $DB->delete_records('cache_flags', array()); accesslib_clear_all_caches(false); $systemcontext->mark_dirty(); $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); $this->assertTrue(isset($dirty[$systemcontext->path])); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path])); // ======= $context->reload_if_dirty(); ================================= $DB->delete_records('cache_flags', array()); accesslib_clear_all_caches(false); load_all_capabilities(); $context = context_course::instance($testcourses[2]); $page = $DB->get_record('page', array('course'=>$testcourses[2])); $pagecontext = context_module::instance($page->id); $context->mark_dirty(); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); $USER->access['test'] = true; $context->reload_if_dirty(); $this->assertFalse(isset($USER->access['test'])); $context->mark_dirty(); $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); $USER->access['test'] = true; $pagecontext->reload_if_dirty(); $this->assertFalse(isset($USER->access['test'])); // ======= context_helper::build_all_paths() ============================ $oldcontexts = $DB->get_records('context', array(), 'id'); $DB->set_field_select('context', 'path', NULL, "contextlevel <> ".CONTEXT_SYSTEM); $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM); context_helper::build_all_paths(); $newcontexts = $DB->get_records('context', array(), 'id'); $this->assertEquals($oldcontexts, $newcontexts); unset($oldcontexts); unset($newcontexts); // ======= $context->reset_paths() ====================================== $context = context_course::instance($testcourses[2]); $children = $context->get_child_contexts(); $context->reset_paths(false); $this->assertSame($DB->get_field('context', 'path', array('id'=>$context->id)), NULL); $this->assertEquals($DB->get_field('context', 'depth', array('id'=>$context->id)), 0); foreach ($children as $child) { $this->assertSame($DB->get_field('context', 'path', array('id'=>$child->id)), NULL); $this->assertEquals($DB->get_field('context', 'depth', array('id'=>$child->id)), 0); } $this->assertEquals(count($children)+1, $DB->count_records('context', array('depth'=>0))); $this->assertEquals(count($children)+1, $DB->count_records('context', array('path'=>NULL))); $context = context_course::instance($testcourses[2]); $context->reset_paths(true); $context = context_course::instance($testcourses[2]); $this->assertEquals($DB->get_field('context', 'path', array('id'=>$context->id)), $context->path); $this->assertEquals($DB->get_field('context', 'depth', array('id'=>$context->id)), $context->depth); $this->assertEquals(0, $DB->count_records('context', array('depth'=>0))); $this->assertEquals(0, $DB->count_records('context', array('path'=>NULL))); // ====== $context->update_moved(); ====================================== accesslib_clear_all_caches(false); $DB->delete_records('cache_flags', array()); $course = $DB->get_record('course', array('id'=>$testcourses[0])); $context = context_course::instance($course->id); $oldpath = $context->path; $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $categorycontext = context_coursecat::instance($miscid); $course->category = $miscid; $DB->update_record('course', $course); $context->update_moved($categorycontext); $context = context_course::instance($course->id); $this->assertEquals($context->get_parent_context(), $categorycontext); $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); $this->assertTrue(isset($dirty[$oldpath])); $this->assertTrue(isset($dirty[$context->path])); // ====== $context->delete_content() ===================================== context_helper::reset_caches(); $context = context_module::instance($testpages[3]); $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); $context->delete_content(); $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); // ====== $context->delete() ============================= context_helper::reset_caches(); $context = context_module::instance($testpages[4]); $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); $this->assertEquals(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id)); $bicontext = context_block::instance($bi->id); $DB->delete_records('cache_flags', array()); $context->delete(); // should delete also linked blocks $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); $this->assertTrue(isset($dirty[$context->path])); $this->assertFalse($DB->record_exists('context', array('id'=>$context->id))); $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id))); $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4]))); $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id))); $this->assertEquals(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); context_module::instance($testpages[4]); // ====== context_helper::delete_instance() ============================= context_helper::reset_caches(); $lastcourse = array_pop($testcourses); $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); $coursecontext = context_course::instance($lastcourse); $this->assertEquals(context_inspection::test_context_cache_size(), 1); $this->assertFalse($coursecontext->instanceid == CONTEXT_COURSE); $DB->delete_records('cache_flags', array()); context_helper::delete_instance(CONTEXT_COURSE, $lastcourse); $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); $this->assertTrue(isset($dirty[$coursecontext->path])); $this->assertEquals(context_inspection::test_context_cache_size(), 0); $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); context_course::instance($lastcourse); // ======= context_helper::create_instances() ========================== $prevcount = $DB->count_records('context'); $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); context_helper::create_instances(null, true); $this->assertSame($DB->count_records('context'), $prevcount); $this->assertEquals($DB->count_records('context', array('depth'=>0)), 0); $this->assertEquals($DB->count_records('context', array('path'=>NULL)), 0); $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); $DB->delete_records('block_instances', array()); $prevcount = $DB->count_records('context'); $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM); context_helper::create_instances(null, true); $this->assertSame($DB->count_records('context'), $prevcount); $this->assertEquals($DB->count_records('context', array('depth'=>0)), 0); $this->assertEquals($DB->count_records('context', array('path'=>NULL)), 0); // ======= context_helper::cleanup_instances() ========================== $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); $DB->delete_records('course', array('id'=>$lastcourse)); $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); $DB->delete_records('course_categories', array('id'=>$lastcategory)); $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); $DB->delete_records('user', array('id'=>$lastuser)); $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id)); $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid)); context_helper::cleanup_instances(); $count = 1; //system $count += $DB->count_records('user', array('deleted'=>0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEquals($DB->count_records('context'), $count); // ======= context cache size restrictions ============================== $testusers= array(); for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) { $user = $generator->create_user(); $testusers[$i] = $user->id; } context_helper::create_instances(null, true); context_helper::reset_caches(); for ($i=0; $i<CONTEXT_CACHE_MAX_SIZE + 100; $i++) { context_user::instance($testusers[$i]); if ($i == CONTEXT_CACHE_MAX_SIZE - 1) { $this->assertEquals(context_inspection::test_context_cache_size(), CONTEXT_CACHE_MAX_SIZE); } else if ($i == CONTEXT_CACHE_MAX_SIZE) { // once the limit is reached roughly 1/3 of records should be removed from cache $this->assertEquals(context_inspection::test_context_cache_size(), (int)(CONTEXT_CACHE_MAX_SIZE * (2/3) +102)); } } // We keep the first 100 cached $prevsize = context_inspection::test_context_cache_size(); for ($i=0; $i<100; $i++) { context_user::instance($testusers[$i]); $this->assertEquals(context_inspection::test_context_cache_size(), $prevsize); } context_user::instance($testusers[102]); $this->assertEquals(context_inspection::test_context_cache_size(), $prevsize+1); unset($testusers); // ================================================================= // ======= basic test of legacy functions ========================== // ================================================================= // note: watch out, the fake site might be pretty borked already $this->assertSame(get_system_context(), context_system::instance()); foreach ($DB->get_records('context') as $contextid=>$record) { $context = context::instance_by_id($contextid); $this->assertSame(get_context_instance_by_id($contextid), $context); $this->assertSame(get_context_instance($record->contextlevel, $record->instanceid), $context); $this->assertSame(get_parent_contexts($context), $context->get_parent_context_ids()); if ($context->id == SYSCONTEXTID) { $this->assertSame(get_parent_contextid($context), false); } else { $this->assertSame(get_parent_contextid($context), $context->get_parent_context()->id); } } $CFG->debug = 0; $children = get_child_contexts($systemcontext); $CFG->debug = DEBUG_DEVELOPER; $this->assertEquals(count($children), $DB->count_records('context')-1); unset($children); $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); create_contexts(); $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK))); $DB->set_field('context', 'depth', 0, array('contextlevel'=>CONTEXT_BLOCK)); build_context_path(); $this->assertFalse($DB->record_exists('context', array('depth'=>0))); $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); $DB->delete_records('course', array('id'=>$lastcourse)); $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); $DB->delete_records('course_categories', array('id'=>$lastcategory)); $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); $DB->delete_records('user', array('id'=>$lastuser)); $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id)); $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid)); cleanup_contexts(); $count = 1; //system $count += $DB->count_records('user', array('deleted'=>0)); $count += $DB->count_records('course_categories'); $count += $DB->count_records('course'); $count += $DB->count_records('course_modules'); $count += $DB->count_records('block_instances'); $this->assertEquals($DB->count_records('context'), $count); context_helper::reset_caches(); preload_course_contexts($SITE->id); $this->assertEquals(context_inspection::test_context_cache_size(), 1); context_helper::reset_caches(); list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx'); $sql = "SELECT c.id $select FROM {course_categories} c $join"; $records = $DB->get_records_sql($sql); foreach ($records as $record) { context_instance_preload($record); $record = (array)$record; $this->assertEquals(1, count($record)); // only id left } $this->assertEquals(count($records), context_inspection::test_context_cache_size()); accesslib_clear_all_caches(true); $DB->delete_records('cache_flags', array()); mark_context_dirty($systemcontext->path); $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); $this->assertTrue(isset($dirty[$systemcontext->path])); accesslib_clear_all_caches(false); $DB->delete_records('cache_flags', array()); $course = $DB->get_record('course', array('id'=>$testcourses[2])); $context = get_context_instance(CONTEXT_COURSE, $course->id); $oldpath = $context->path; $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); $categorycontext = context_coursecat::instance($miscid); $course->category = $miscid; $DB->update_record('course', $course); context_moved($context, $categorycontext); $context = get_context_instance(CONTEXT_COURSE, $course->id); $this->assertEquals($context->get_parent_context(), $categorycontext); $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2]))); delete_context(CONTEXT_COURSE, $testcourses[2]); $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2]))); $name = get_contextlevel_name(CONTEXT_COURSE); $this->assertFalse(empty($name)); $context = get_context_instance(CONTEXT_COURSE, $testcourses[2]); $name = print_context_name($context); $this->assertFalse(empty($name)); $url = get_context_url($coursecontext); $this->assertFalse($url instanceof modole_url); $page = $DB->get_record('page', array('id'=>$testpages[7])); $context = get_context_instance(CONTEXT_MODULE, $page->id); $coursecontext = get_course_context($context); $this->assertEquals($coursecontext->contextlevel, CONTEXT_COURSE); $this->assertEquals(get_courseid_from_context($context), $page->course); $caps = fetch_context_capabilities($systemcontext); $this->assertTrue(is_array($caps)); unset($caps); }
function xmldb_enrol_database_install() { global $CFG, $DB; // migrate old config settings first if (isset($CFG->enrol_dbtype)) { set_config('dbtype', $CFG->enrol_dbtype, 'enrol_database'); unset_config('enrol_dbtype'); } if (isset($CFG->enrol_dbhost)) { set_config('dbhost', $CFG->enrol_dbhost, 'enrol_database'); unset_config('enrol_dbhost'); } if (isset($CFG->enrol_dbuser)) { set_config('dbuser', $CFG->enrol_dbuser, 'enrol_database'); unset_config('enrol_dbuser'); } if (isset($CFG->enrol_dbpass)) { set_config('dbpass', $CFG->enrol_dbpass, 'enrol_database'); unset_config('enrol_dbpass'); } if (isset($CFG->enrol_dbname)) { set_config('dbname', $CFG->enrol_dbname, 'enrol_database'); unset_config('enrol_dbname'); } if (isset($CFG->enrol_dbtable)) { set_config('remoteenroltable', $CFG->enrol_dbtable, 'enrol_database'); unset_config('enrol_dbtable'); } if (isset($CFG->enrol_localcoursefield)) { set_config('localcoursefield', $CFG->enrol_localcoursefield, 'enrol_database'); unset_config('enrol_localcoursefield'); } if (isset($CFG->enrol_localuserfield)) { set_config('localuserfield', $CFG->enrol_localuserfield, 'enrol_database'); unset_config('enrol_localuserfield'); } if (isset($CFG->enrol_localrolefield)) { set_config('localrolefield', $CFG->enrol_localrolefield, 'enrol_database'); unset_config('enrol_localrolefield'); } if (isset($CFG->enrol_remotecoursefield)) { set_config('remotecoursefield', $CFG->enrol_remotecoursefield, 'enrol_database'); unset_config('enrol_remotecoursefield'); } if (isset($CFG->enrol_remoteuserfield)) { set_config('remoteuserfield', $CFG->enrol_remoteuserfield, 'enrol_database'); unset_config('enrol_remoteuserfield'); } if (isset($CFG->enrol_remoterolefield)) { set_config('remoterolefield', $CFG->enrol_remoterolefield, 'enrol_database'); unset_config('enrol_remoterolefield'); } if (isset($CFG->enrol_db_defaultcourseroleid)) { set_config('defaultrole', $CFG->enrol_db_defaultcourseroleid, 'enrol_database'); unset_config('enrol_db_defaultcourseroleid'); } unset_config('enrol_db_autocreate'); // replaced by new coruse temple sync if (isset($CFG->enrol_db_category)) { set_config('defaultcategory', $CFG->enrol_db_category, 'enrol_database'); unset_config('enrol_db_category'); } if (isset($CFG->enrol_db_template)) { set_config('templatecourse', $CFG->enrol_db_template, 'enrol_database'); unset_config('enrol_db_template'); } if (isset($CFG->enrol_db_ignorehiddencourse)) { set_config('ignorehiddencourses', $CFG->enrol_db_ignorehiddencourse, 'enrol_database'); unset_config('enrol_db_ignorehiddencourse'); } // just make sure there are no leftovers after disabled plugin if (!$DB->record_exists('enrol', array('enrol' => 'database'))) { role_unassign_all(array('component' => 'enrol_database')); return; } }
/** * Forces synchronisation of all enrolments with external database. * * @param bool $verbose * @return int 0 means success, 1 db connect failure, 2 db read failure */ public function sync_enrolments($verbose = false) { global $CFG, $DB; // we do not create courses here intentionally because it requires full sync and is slow if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) { if ($verbose) { mtrace('User enrolment synchronisation skipped.'); } return 0; } if ($verbose) { mtrace('Starting user enrolment synchronisation...'); } if (!($extdb = $this->db_init())) { mtrace('Error while communicating with external enrolment database'); return 1; } // we may need a lot of memory here @set_time_limit(0); raise_memory_limit(MEMORY_HUGE); // second step is to sync instances and users $table = $this->get_config('remoteenroltable'); $coursefield = strtolower($this->get_config('remotecoursefield')); $userfield = strtolower($this->get_config('remoteuserfield')); $rolefield = strtolower($this->get_config('remoterolefield')); $localrolefield = $this->get_config('localrolefield'); $localuserfield = $this->get_config('localuserfield'); $localcoursefield = $this->get_config('localcoursefield'); $unenrolaction = $this->get_config('unenrolaction'); $defaultrole = $this->get_config('defaultrole'); // create roles mapping $allroles = get_all_roles(); if (!isset($allroles[$defaultrole])) { $defaultrole = 0; } $roles = array(); foreach ($allroles as $role) { $roles[$role->{$localrolefield}] = $role->id; } // get a list of courses to be synced that are in external table $externalcourses = array(); $sql = $this->db_get_sql($table, array(), array($coursefield), true); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { while ($mapping = $rs->FetchRow()) { $mapping = reset($mapping); $mapping = $this->db_decode($mapping); if (empty($mapping)) { // invalid mapping continue; } $externalcourses[$mapping] = true; } } $rs->Close(); } else { mtrace('Error reading data from the external enrolment table'); $extdb->Close(); return 2; } $preventfullunenrol = empty($externalcourses); if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) { mtrace(' Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.'); } // first find all existing courses with enrol instance $existing = array(); $sql = "SELECT c.id, c.visible, c.{$localcoursefield} AS mapping, e.id AS enrolid, c.shortname\n FROM {course} c\n JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')"; $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates foreach ($rs as $course) { if (empty($course->mapping)) { continue; } $existing[$course->mapping] = $course; } $rs->close(); // add necessary enrol instances that are not present yet $params = array(); $localnotempty = ""; if ($localcoursefield !== 'id') { $localnotempty = "AND c.{$localcoursefield} <> :lcfe"; $params['lcfe'] = $DB->sql_empty(); } $sql = "SELECT c.id, c.visible, c.{$localcoursefield} AS mapping, c.shortname\n FROM {course} c\n LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')\n WHERE e.id IS NULL {$localnotempty}"; $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $course) { if (empty($course->mapping)) { continue; } if (!isset($externalcourses[$course->mapping])) { // course not synced continue; } if (isset($existing[$course->mapping])) { // some duplicate, sorry continue; } $course->enrolid = $this->add_instance($course); $existing[$course->mapping] = $course; } $rs->close(); // free memory unset($externalcourses); // sync enrolments $ignorehidden = $this->get_config('ignorehiddencourses'); $sqlfields = array($userfield); if ($rolefield) { $sqlfields[] = $rolefield; } foreach ($existing as $course) { if ($ignorehidden and !$course->visible) { continue; } if (!($instance = $DB->get_record('enrol', array('id' => $course->enrolid)))) { continue; //weird } $context = get_context_instance(CONTEXT_COURSE, $course->id); // get current list of enrolled users with their roles $current_roles = array(); $current_status = array(); $user_mapping = array(); $sql = "SELECT u.{$localuserfield} AS mapping, u.id, ue.status, ue.userid, ra.roleid\n FROM {user} u\n JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)\n JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')\n WHERE u.deleted = 0"; $params = array('enrolid' => $instance->id); if ($localuserfield === 'username') { $sql .= " AND u.mnethostid = :mnethostid"; $params['mnethostid'] = $CFG->mnet_localhost_id; } $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { $current_roles[$ue->userid][$ue->roleid] = $ue->roleid; $current_status[$ue->userid] = $ue->status; $user_mapping[$ue->mapping] = $ue->userid; } $rs->close(); // get list of users that need to be enrolled and their roles $requested_roles = array(); $sql = $this->db_get_sql($table, array($coursefield => $course->mapping), $sqlfields); if ($rs = $extdb->Execute($sql)) { if (!$rs->EOF) { if ($localuserfield === 'username') { $usersearch = array('mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 0); } while ($fields = $rs->FetchRow()) { $fields = array_change_key_case($fields, CASE_LOWER); if (empty($fields[$userfield])) { //user identification is mandatory! } $mapping = $fields[$userfield]; if (!isset($user_mapping[$mapping])) { $usersearch[$localuserfield] = $mapping; if (!($user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE))) { // user does not exist or was deleted continue; } $user_mapping[$mapping] = $user->id; $userid = $user->id; } else { $userid = $user_mapping[$mapping]; } if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) { if (!$defaultrole) { // role is mandatory continue; } $roleid = $defaultrole; } else { $roleid = $roles[$fields[$rolefield]]; } $requested_roles[$userid][$roleid] = $roleid; } } $rs->Close(); } else { mtrace('Error while communicating with external enrolment database'); $extdb->Close(); return; } unset($user_mapping); // enrol all users and sync roles foreach ($requested_roles as $userid => $userroles) { foreach ($userroles as $roleid) { if (empty($current_roles[$userid])) { $this->enrol_user($instance, $userid, $roleid, 0, 0, ENROL_USER_ACTIVE); $current_roles[$userid][$roleid] = $roleid; $current_status[$userid] = ENROL_USER_ACTIVE; if ($verbose) { mtrace(" enrolling: {$userid} ==> {$course->shortname} as " . $allroles[$roleid]->shortname); } } } // assign extra roles foreach ($userroles as $roleid) { if (empty($current_roles[$userid][$roleid])) { role_assign($roleid, $userid, $context->id, 'enrol_database', $instance->id); $current_roles[$userid][$roleid] = $roleid; if ($verbose) { mtrace(" assigning roles: {$userid} ==> {$course->shortname} as " . $allroles[$roleid]->shortname); } } } // unassign removed roles foreach ($current_roles[$userid] as $cr) { if (empty($userroles[$cr])) { role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id); unset($current_roles[$userid][$cr]); if ($verbose) { mtrace(" unsassigning roles: {$userid} ==> {$course->shortname}"); } } } // reenable enrolment when previously disable enrolment refreshed if ($current_status[$userid] == ENROL_USER_SUSPENDED) { $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE); if ($verbose) { mtrace(" unsuspending: {$userid} ==> {$course->shortname}"); } } } // deal with enrolments removed from external table if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) { if (!$preventfullunenrol) { // unenrol foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } $this->unenrol_user($instance, $userid); if ($verbose) { mtrace(" unenrolling: {$userid} ==> {$course->shortname}"); } } } } else { if ($unenrolaction == ENROL_EXT_REMOVED_KEEP) { // keep - only adding enrolments } else { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { // disable foreach ($current_status as $userid => $status) { if (isset($requested_roles[$userid])) { continue; } if ($status != ENROL_USER_SUSPENDED) { $this->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED); if ($verbose) { mtrace(" suspending: {$userid} ==> {$course->shortname}"); } } if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { role_unassign_all(array('contextid' => $context->id, 'userid' => $userid, 'component' => 'enrol_database', 'itemid' => $instance->id)); if ($verbose) { mtrace(" unsassigning all roles: {$userid} ==> {$course->shortname}"); } } } } } } } // close db connection $extdb->Close(); if ($verbose) { mtrace('...user enrolment synchronisation finished.'); } return 0; }
public function test_sync_full() { global $DB; $this->resetAfterTest(); // Setup a few courses and categories. $studentrole = $DB->get_record('role', array('shortname' => 'student')); $this->assertNotEmpty($studentrole); $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); $this->assertNotEmpty($teacherrole); $managerrole = $DB->get_record('role', array('shortname' => 'manager')); $this->assertNotEmpty($managerrole); $cat1 = $this->getDataGenerator()->create_category(); $cat2 = $this->getDataGenerator()->create_category(); $cat3 = $this->getDataGenerator()->create_category(array('parent' => $cat2->id)); $course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id)); $course2 = $this->getDataGenerator()->create_course(array('category' => $cat2->id)); $course3 = $this->getDataGenerator()->create_course(array('category' => $cat3->id)); $course4 = $this->getDataGenerator()->create_course(array('category' => $cat3->id)); $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $user4 = $this->getDataGenerator()->create_user(); $this->enable_role_sync($studentrole->id); $this->enable_role_sync($teacherrole->id); $this->enable_plugin(); $this->assertEquals(0, $DB->count_records('role_assignments', array())); role_assign($managerrole->id, $user1->id, context_coursecat::instance($cat1->id)); role_assign($managerrole->id, $user3->id, context_course::instance($course1->id)); role_assign($managerrole->id, $user3->id, context_course::instance($course2->id)); $this->assertEquals(0, $DB->count_records('user_enrolments', array())); $result = enrol_category_sync_full(); $this->assertSame(0, $result); $this->disable_plugin(); role_assign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id)); $this->enable_plugin(); $result = enrol_category_sync_full(); $this->assertSame(0, $result); $this->assertEquals(3, $DB->count_records('user_enrolments', array())); $this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id)); $this->assertTrue(is_enrolled(context_course::instance($course3->id), $user1->id)); $this->assertTrue(is_enrolled(context_course::instance($course4->id), $user1->id)); $this->disable_plugin(); role_unassign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id)->id); role_assign($studentrole->id, $user2->id, context_coursecat::instance($cat1->id)); role_assign($teacherrole->id, $user4->id, context_coursecat::instance($cat1->id)); role_assign($teacherrole->id, $user3->id, context_coursecat::instance($cat2->id)); role_assign($managerrole->id, $user3->id, context_course::instance($course3->id)); $this->enable_plugin(); $result = enrol_category_sync_full(); $this->assertSame(0, $result); $this->assertEquals(5, $DB->count_records('user_enrolments', array())); $this->assertTrue(is_enrolled(context_course::instance($course1->id), $user2->id)); $this->assertTrue(is_enrolled(context_course::instance($course1->id), $user4->id)); $this->assertTrue(is_enrolled(context_course::instance($course2->id), $user3->id)); $this->assertTrue(is_enrolled(context_course::instance($course3->id), $user3->id)); $this->assertTrue(is_enrolled(context_course::instance($course4->id), $user3->id)); // Cleanup everything. $this->assertNotEmpty($DB->count_records('role_assignments', array())); $this->assertNotEmpty($DB->count_records('user_enrolments', array())); $this->disable_plugin(); role_unassign_all(array('roleid' => $studentrole->id)); role_unassign_all(array('roleid' => $managerrole->id)); role_unassign_all(array('roleid' => $teacherrole->id)); $result = enrol_category_sync_full(); $this->assertSame(2, $result); $this->assertEquals(0, $DB->count_records('role_assignments', array())); $this->assertNotEmpty($DB->count_records('user_enrolments', array())); $this->disable_role_sync($studentrole->id); $this->disable_role_sync($teacherrole->id); $this->enable_plugin(); $result = enrol_category_sync_full(); $this->assertSame(0, $result); $this->assertEquals(0, $DB->count_records('role_assignments', array())); $this->assertEquals(0, $DB->count_records('user_enrolments', array())); $this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'category'))); }
/** * Triggered via course_deleted event. * @static * @param stdClass $course * @return bool success */ public static function course_deleted($course) { global $DB; // NOTE: do not test if plugin enabled, we want to keep disabling instances with invalid course links // does anything want to sync with this parent? if (!($enrols = $DB->get_records('enrol', array('customint1' => $course->id, 'enrol' => 'meta'), 'courseid ASC, id ASC'))) { return true; } $plugin = enrol_get_plugin('meta'); // hack the DB info for all courses first foreach ($enrols as $enrol) { $enrol->customint1 = 0; $enrol->status = ENROL_INSTANCE_DISABLED; $DB->update_record('enrol', $enrol); $context = context_course::instance($enrol->courseid); role_unassign_all(array('contextid' => $context->id, 'component' => 'enrol_meta', 'itemid' => $enrol->id)); } // now trigger sync for each instance and purge caches foreach ($enrols as $enrol) { $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED); } return true; }
/** * Event processor - cohort deleted. * @param \core\event\cohort_deleted $event * @return bool */ public static function deleted(\core\event\cohort_deleted $event) { global $DB; // Does anything want to sync with this cohort? if (!($instances = $DB->get_records('enrol', array('customint1' => $event->objectid, 'enrol' => 'cohort'), 'id ASC'))) { return true; } $plugin = enrol_get_plugin('cohort'); $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL); foreach ($instances as $instance) { if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) { $context = context_course::instance($instance->courseid); role_unassign_all(array('contextid' => $context->id, 'component' => 'enrol_cohort', 'itemid' => $instance->id)); $plugin->update_status($instance, ENROL_INSTANCE_DISABLED); } else { $plugin->delete_instance($instance); } } return true; }
/** * Automatically clean-up all plugin data and remove the plugin DB tables * * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc. * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc. * @uses global $OUTPUT to produce notices and other messages * @return void */ function uninstall_plugin($type, $name) { global $CFG, $DB, $OUTPUT; // recursively uninstall all module subplugins first if ($type === 'mod') { if (file_exists("{$CFG->dirroot}/mod/{$name}/db/subplugins.php")) { $subplugins = array(); include "{$CFG->dirroot}/mod/{$name}/db/subplugins.php"; foreach ($subplugins as $subplugintype => $dir) { $instances = get_plugin_list($subplugintype); foreach ($instances as $subpluginname => $notusedpluginpath) { uninstall_plugin($subplugintype, $subpluginname); } } } } $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum' if ($type === 'mod') { $pluginname = $name; // eg. 'forum' if (get_string_manager()->string_exists('modulename', $component)) { $strpluginname = get_string('modulename', $component); } else { $strpluginname = $component; } } else { $pluginname = $component; if (get_string_manager()->string_exists('pluginname', $component)) { $strpluginname = get_string('pluginname', $component); } else { $strpluginname = $component; } } echo $OUTPUT->heading($pluginname); $plugindirectory = get_plugin_directory($type, $name); $uninstalllib = $plugindirectory . '/db/uninstall.php'; if (file_exists($uninstalllib)) { require_once $uninstalllib; $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()' if (function_exists($uninstallfunction)) { if (!$uninstallfunction()) { echo $OUTPUT->notification('Encountered a problem running uninstall function for ' . $pluginname); } } } if ($type === 'mod') { // perform cleanup tasks specific for activity modules if (!($module = $DB->get_record('modules', array('name' => $name)))) { print_error('moduledoesnotexist', 'error'); } // delete all the relevant instances from all course sections if ($coursemods = $DB->get_records('course_modules', array('module' => $module->id))) { foreach ($coursemods as $coursemod) { if (!delete_mod_from_section($coursemod->id, $coursemod->section)) { echo $OUTPUT->notification("Could not delete the {$strpluginname} with id = {$coursemod->id} from section {$coursemod->section}"); } } } // clear course.modinfo for courses that used this module $sql = "UPDATE {course}\n SET modinfo=''\n WHERE id IN (SELECT DISTINCT course\n FROM {course_modules}\n WHERE module=?)"; $DB->execute($sql, array($module->id)); // delete all the course module records $DB->delete_records('course_modules', array('module' => $module->id)); // delete module contexts if ($coursemods) { foreach ($coursemods as $coursemod) { if (!delete_context(CONTEXT_MODULE, $coursemod->id)) { echo $OUTPUT->notification("Could not delete the context for {$strpluginname} with id = {$coursemod->id}"); } } } // delete the module entry itself $DB->delete_records('modules', array('name' => $module->name)); // cleanup the gradebook require_once $CFG->libdir . '/gradelib.php'; grade_uninstalled_module($module->name); // Perform any custom uninstall tasks if (file_exists($CFG->dirroot . '/mod/' . $module->name . '/lib.php')) { require_once $CFG->dirroot . '/mod/' . $module->name . '/lib.php'; $uninstallfunction = $module->name . '_uninstall'; if (function_exists($uninstallfunction)) { debugging("{$uninstallfunction}() has been deprecated. Use the plugin's db/uninstall.php instead", DEBUG_DEVELOPER); if (!$uninstallfunction()) { echo $OUTPUT->notification('Encountered a problem running uninstall function for ' . $module->name . '!'); } } } } else { if ($type === 'enrol') { // NOTE: this is a bit brute force way - it will not trigger events and hooks properly // nuke all role assignments role_unassign_all(array('component' => $component)); // purge participants $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($name)); // purge enrol instances $DB->delete_records('enrol', array('enrol' => $name)); // tweak enrol settings if (!empty($CFG->enrol_plugins_enabled)) { $enabledenrols = explode(',', $CFG->enrol_plugins_enabled); $enabledenrols = array_unique($enabledenrols); $enabledenrols = array_flip($enabledenrols); unset($enabledenrols[$name]); $enabledenrols = array_flip($enabledenrols); if (is_array($enabledenrols)) { set_config('enrol_plugins_enabled', implode(',', $enabledenrols)); } } } else { if ($type === 'block') { if ($block = $DB->get_record('block', array('name' => $name))) { // Inform block it's about to be deleted if (file_exists("{$CFG->dirroot}/blocks/{$block->name}/block_{$block->name}.php")) { $blockobject = block_instance($block->name); if ($blockobject) { $blockobject->before_delete(); //only if we can create instance, block might have been already removed } } // First delete instances and related contexts $instances = $DB->get_records('block_instances', array('blockname' => $block->name)); foreach ($instances as $instance) { blocks_delete_instance($instance); } // Delete block $DB->delete_records('block', array('id' => $block->id)); } } } } // perform clean-up task common for all the plugin/subplugin types // delete calendar events $DB->delete_records('event', array('modulename' => $pluginname)); // delete all the logs $DB->delete_records('log', array('module' => $pluginname)); // delete log_display information $DB->delete_records('log_display', array('component' => $component)); // delete the module configuration records unset_all_config_for_plugin($pluginname); // delete message provider message_provider_uninstall($component); // delete message processor if ($type === 'message') { message_processor_uninstall($name); } // delete the plugin tables $xmldbfilepath = $plugindirectory . '/db/install.xml'; drop_plugin_tables($component, $xmldbfilepath, false); if ($type === 'mod' or $type === 'block') { // non-frankenstyle table prefixes drop_plugin_tables($name, $xmldbfilepath, false); } // delete the capabilities that were defined by this module capabilities_cleanup($component); // remove event handlers and dequeue pending events events_uninstall($component); echo $OUTPUT->notification(get_string('success'), 'notifysuccess'); }
/** * Unenrol user from course, * the last unenrolment removes all remaining roles. * * @param stdClass $instance * @param int $userid * @return void */ public function unenrol_user(stdClass $instance, $userid) { global $CFG, $USER, $DB; $name = $this->get_name(); $courseid = $instance->courseid; if ($instance->enrol !== $name) { throw new coding_exception('invalid enrol instance!'); } $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST); if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) { // weird, user not enrolled return; } role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_'.$name, 'itemid'=>$instance->id)); $DB->delete_records('user_enrolments', array('id'=>$ue->id)); // add extra info and trigger event $ue->courseid = $courseid; $ue->enrol = $name; $sql = "SELECT 'x' FROM {user_enrolments} ue JOIN {enrol} e ON (e.id = ue.enrolid) WHERE ue.userid = :userid AND e.courseid = :courseid"; if ($DB->record_exists_sql($sql, array('userid'=>$userid, 'courseid'=>$courseid))) { $ue->lastenrol = false; events_trigger('user_unenrolled', $ue); // user still has some enrolments, no big cleanup yet } else { // the big cleanup IS necessary! require_once("$CFG->dirroot/group/lib.php"); require_once("$CFG->libdir/gradelib.php"); // remove all remaining roles role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id), true, false); //clean up ALL invisible user data from course if this is the last enrolment - groups, grades, etc. groups_delete_group_members($courseid, $userid); grade_user_unenrol($courseid, $userid); $DB->delete_records('user_lastaccess', array('userid'=>$userid, 'courseid'=>$courseid)); $ue->lastenrol = true; // means user not enrolled any more events_trigger('user_unenrolled', $ue); } // reset primitive require_login() caching if ($userid == $USER->id) { if (isset($USER->enrol['enrolled'][$courseid])) { unset($USER->enrol['enrolled'][$courseid]); } if (isset($USER->enrol['tempguest'][$courseid])) { unset($USER->enrol['tempguest'][$courseid]); $USER->access = remove_temp_roles($context, $USER->access); } } }
/** * Removes multiple role assignments, parameters may contain: * 'roleid', 'userid', 'contextid', 'component', 'enrolid'. * * @param array $params role assignment parameters * @param bool $subcontexts unassign in subcontexts too * @param bool $includemanual include manual role assignments too * @return void */ function role_unassign_all(array $params, $subcontexts = false, $includemanual = false) { global $USER, $CFG, $DB; if (!$params) { throw new coding_exception('Missing parameters in role_unsassign_all() call'); } $allowed = array('roleid', 'userid', 'contextid', 'component', 'itemid'); foreach ($params as $key => $value) { if (!in_array($key, $allowed)) { throw new coding_exception('Unknown role_unsassign_all() parameter key', 'key:' . $key); } } if (isset($params['component']) and $params['component'] !== '' and strpos($params['component'], '_') === false) { throw new coding_exception('Invalid component paramter in role_unsassign_all() call', 'component:' . $params['component']); } if ($includemanual) { if (!isset($params['component']) or $params['component'] === '') { throw new coding_exception('include manual parameter requires component parameter in role_unsassign_all() call'); } } if ($subcontexts) { if (empty($params['contextid'])) { throw new coding_exception('subcontexts paramtere requires component parameter in role_unsassign_all() call'); } } $ras = $DB->get_records('role_assignments', $params); foreach ($ras as $ra) { $DB->delete_records('role_assignments', array('id' => $ra->id)); if ($context = context::instance_by_id($ra->contextid, IGNORE_MISSING)) { // this is a bit expensive but necessary $context->mark_dirty(); /// If the user is the current user, then do full reload of capabilities too. if (!empty($USER->id) && $USER->id == $ra->userid) { reload_all_capabilities(); } } events_trigger('role_unassigned', $ra); } unset($ras); // process subcontexts if ($subcontexts and $context = context::instance_by_id($params['contextid'], IGNORE_MISSING)) { if ($params['contextid'] instanceof context) { $context = $params['contextid']; } else { $context = context::instance_by_id($params['contextid'], IGNORE_MISSING); } if ($context) { $contexts = $context->get_child_contexts(); $mparams = $params; foreach ($contexts as $context) { $mparams['contextid'] = $context->id; $ras = $DB->get_records('role_assignments', $mparams); foreach ($ras as $ra) { $DB->delete_records('role_assignments', array('id' => $ra->id)); // this is a bit expensive but necessary $context->mark_dirty(); /// If the user is the current user, then do full reload of capabilities too. if (!empty($USER->id) && $USER->id == $ra->userid) { reload_all_capabilities(); } events_trigger('role_unassigned', $ra); } } } } // do this once more for all manual role assignments if ($includemanual) { $params['component'] = ''; role_unassign_all($params, $subcontexts, false); } }
/** * Do any enrolments need expiration processing. * * Plugins that want to call this functionality must implement 'expiredaction' config setting. * * @param progress_trace $trace * @param int $courseid one course, empty mean all * @return bool true if any data processed, false if not */ public function process_expirations(progress_trace $trace, $courseid = null) { global $DB; $name = $this->get_name(); if (!enrol_is_enabled($name)) { $trace->finished(); return false; } $processed = false; $params = array(); $coursesql = ""; if ($courseid) { $coursesql = "AND e.courseid = :courseid"; } // Deal with expired accounts. $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); if ($action == ENROL_EXT_REMOVED_UNENROL) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :enrol)\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now {$coursesql}"; $params = array('now' => time(), 'courselevel' => CONTEXT_COURSE, 'enrol' => $name, 'courseid' => $courseid); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (!$processed) { $trace->output("Starting processing of enrol_{$name} expirations..."); $processed = true; } if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; if (!$this->roles_protected()) { // Let's just guess what extra roles are supposed to be removed. if ($instance->roleid) { role_unassign($instance->roleid, $ue->userid, $ue->contextid); } } // The unenrol cleans up all subcontexts if this is the only course enrolment for this user. $this->unenrol_user($instance, $ue->userid); $trace->output("Unenrolling expired user {$ue->userid} from course {$instance->courseid}", 1); } $rs->close(); unset($instances); } else { if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES or $action == ENROL_EXT_REMOVED_SUSPEND) { $instances = array(); $sql = "SELECT ue.*, e.courseid, c.id AS contextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = :enrol)\n JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)\n WHERE ue.timeend > 0 AND ue.timeend < :now\n AND ue.status = :useractive {$coursesql}"; $params = array('now' => time(), 'courselevel' => CONTEXT_COURSE, 'useractive' => ENROL_USER_ACTIVE, 'enrol' => $name, 'courseid' => $courseid); $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $ue) { if (!$processed) { $trace->output("Starting processing of enrol_{$name} expirations..."); $processed = true; } if (empty($instances[$ue->enrolid])) { $instances[$ue->enrolid] = $DB->get_record('enrol', array('id' => $ue->enrolid)); } $instance = $instances[$ue->enrolid]; if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { if (!$this->roles_protected()) { // Let's just guess what roles should be removed. $count = $DB->count_records('role_assignments', array('userid' => $ue->userid, 'contextid' => $ue->contextid)); if ($count == 1) { role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0)); } else { if ($count > 1 and $instance->roleid) { role_unassign($instance->roleid, $ue->userid, $ue->contextid, '', 0); } } } // In any case remove all roles that belong to this instance and user. role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => 'enrol_' . $name, 'itemid' => $instance->id), true); // Final cleanup of subcontexts if there are no more course roles. if (0 == $DB->count_records('role_assignments', array('userid' => $ue->userid, 'contextid' => $ue->contextid))) { role_unassign_all(array('userid' => $ue->userid, 'contextid' => $ue->contextid, 'component' => '', 'itemid' => 0), true); } } $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); $trace->output("Suspending expired user {$ue->userid} in course {$instance->courseid}", 1); } $rs->close(); unset($instances); } else { // ENROL_EXT_REMOVED_KEEP means no changes. } } if ($processed) { $trace->output("...finished processing of enrol_{$name} expirations"); } else { $trace->output("No expired enrol_{$name} enrolments detected"); } $trace->finished(); return $processed; }