public function validate_class_prerequisites() { // check prerequisites $pmclass = $this->pmclass; // get all the curricula that the user is in $curricula = $this->users->get_programassignments(); foreach ($curricula as $curriculum) { $curcrs = new curriculumcourse(); $curcrs->courseid = $pmclass->courseid; $curcrs->curriculumid = $curriculum->curriculumid; if (!$curcrs->prerequisites_satisfied($this->userid)) { // prerequisites not satisfied throw new unsatisfied_prerequisites_exception($this); /* $status = new Object(); $status->message = get_string('unsatisfiedprereqs', self::LANG_FILE); $status->code = 'unsatisfiedprereqs'; //error_log('student.class::add() - student missing prereqs!'); return $status; */ } else { return true; } } }
/** * Assign a class to a track, this function also creates * and assigns the class to the curriculum default track * * @return TODO: add meaningful return value */ function save() { //add() if (empty($this->courseid)) { $this->courseid = $this->_db->get_field(pmclass::TABLE, 'courseid', array('id' => $this->classid)); } if ((empty($this->trackid) or empty($this->classid) or empty($this->courseid)) and empty(elis::$config->local_elisprogram->userdefinedtrack)) { cm_error('trackid and classid have not been properly initialized'); return false; } else { if ((empty($this->courseid) or empty($this->classid)) and elis::$config->local_elisprogram->userdefinedtrack) { cm_error('courseid has not been properly initialized'); } } if (!isset($this->id) && $this->is_class_assigned_to_track()) { //trying to re-add an existing association return; } // Determine whether class is required $curcrsobj = new curriculumcourse(array('curriculumid' => $this->track->curid, 'courseid' => $this->courseid)); // TBV: was $this->classid // insert assignment record parent::save(); //updated for ELIS2 from $this->data_insert_record() if ($this->autoenrol && $this->is_autoenrollable()) { // autoenrol all users in the track // ELIS-7582 @set_time_limit(0); $users = usertrack::get_users($this->trackid); foreach ($users as $user) { // ELIS-3460: Must check pre-requisites ... if (!$curcrsobj->prerequisites_satisfied($user->userid)) { //error_log("/local/elisprogram/lib/data/track.class.php:trackassignment::save(); pre-requisites NOT satisfied for course: {$this->courseid}, curriculum: {$this->track->curid}"); continue; } $now = time(); $stu_record = new object(); $stu_record->userid = $user->userid; $stu_record->user_idnumber = $user->idnumber; $stu_record->classid = $this->classid; $stu_record->enrolmenttime = $now; $enrolment = new student($stu_record); // check enrolment limits try { $enrolment->save(); } catch (pmclass_enrolment_limit_validation_exception $e) { // autoenrol into waitlist $wait_record = new object(); $wait_record->userid = $user->userid; $wait_record->classid = $this->classid; $wait_record->enrolmenttime = $now; $wait_record->timecreated = $now; $wait_record->position = 0; $wait_list = new waitlist($wait_record); $wait_list->save(); } catch (Exception $e) { $param = array('message' => $e->getMessage()); echo cm_error(get_string('record_not_created_reason', 'local_elisprogram', $param)); } } } events_trigger('pm_track_class_associated', $this); }
/** * Perform all necessary tasks to add a student enrolment to the system. * * @param array $checks what checks to perform before adding enrolling the * user. e.g. array('prereq' => 1, 'waitlist' => 1) will check that * prerequisites are satisfied, and that the class is not full * @param boolean $notify whether or not notifications should be sent if a * check fails */ function add($checks = array(), $notify = false) { global $CURMAN, $CFG, $USER; $status = true; if ($CURMAN->db->record_exists(STUTABLE, 'userid', $this->userid, 'classid', $this->classid)) { // already enrolled -- pretend we succeeded return true; } // check that the student can be enrolled first if (!empty($checks['prereq'])) { // check prerequisites $cmclass = new cmclass($this->classid); // get all the curricula that the user is in $curricula = curriculumstudent::get_curricula($this->userid); foreach ($curricula as $curriculum) { $curcrs = new curriculumcourse(); $curcrs->courseid = $cmclass->courseid; $curcrs->curriculumid = $curriculum->curid; if (!$curcrs->prerequisites_satisfied($this->userid)) { // prerequisites not satisfied if ($notify) { $data = new stdClass(); $data->userid = $this->userid; $data->classid = $this->classid; //$data->trackid = $trackid; events_trigger('crlm_prereq_unsatisfied', $data); } $status = new Object(); $status->message = get_string('unsatisfiedprereqs', 'block_curr_admin'); $status->code = 'unsatisfiedprereqs'; return $status; } } } if (!empty($checks['waitlist'])) { // check class enrolment limit $cmclass = new cmclass($this->classid); $limit = $cmclass->maxstudents; if (!empty($limit) && $limit <= student::count_enroled($this->classid)) { // class is full // put student on wait list $wait_list = new waitlist($this); $wait_list->timecreated = time(); $wait_list->position = 0; $wait_list->add(); if ($notify) { $subject = get_string('user_waitlisted', 'block_curr_admin'); $a = new object(); $a->user = $this->user->idnumber; $a->cmclass = $cmclass->idnumber; $message = get_string('user_waitlisted_msg', 'block_curr_admin', $a); $from = $user = get_admin(); notification::notify($message, $user, $from); email_to_user($user, $from, $subject, $message); } $status = new Object(); $status->message = get_string('user_waitlisted', 'block_curr_admin'); $status->code = 'user_waitlisted'; return $status; } } //set end time based on class duration $studentclass = new cmclass($this->classid); if (empty($this->endtime)) { if (isset($studentclass->duration) && $studentclass->duration) { $this->endtime = $this->enrolmenttime + $studentclass->duration; } else { // no class duration -> no end time $this->endtime = 0; } } $status = $this->data_insert_record(); // TBD: we should check this! /// Get the Moodle user ID or create a new account for this user. if (!($muserid = cm_get_moodleuserid($this->userid))) { $user = new user($this->userid); if (!($muserid = $user->synchronize_moodle_user(true, true))) { $status = new Object(); $status->message = get_string('errorsynchronizeuser', 'block_curr_admin'); $muserid = false; } } /// Enrol them into the Moodle class. if ($moodlecourseid = moodle_get_course($this->classid)) { if ($mcourse = get_record('course', 'id', $moodlecourseid)) { $enrol = $mcourse->enrol; if (!$enrol) { $enrol = $CFG->enrol; } if ($CURMAN->config->restrict_to_elis_enrolment_plugin && $enrol != 'elis') { $status = new Object(); $status->message = get_string('error_not_using_elis_enrolment', 'block_curr_admin'); return $status; } $timestart = $this->enrolmenttime; $timeend = $this->endtime; if ($role = get_default_course_role($mcourse)) { $context = get_context_instance(CONTEXT_COURSE, $mcourse->id); if (!empty($muserid)) { if (!role_assign($role->id, $muserid, 0, $context->id, $timestart, $timeend, 0, 'manual')) { $status = new Object(); $status->message = get_string('errorroleassign', 'block_curr_admin'); } } } } } else { if (!empty($muserid)) { $sturole = $CURMAN->config->enrolment_role_sync_student_role; // ELIS-2776: must still trigger events for notifications $ra = new stdClass(); $ra->roleid = !empty($sturole) ? $sturole : get_field('role', 'id', 'shortname', 'student'); $ra->contextid = context_level_base::get_custom_context_level('class', 'block_curr_admin'); // TBD $ra->userid = $muserid; $ra->component = ''; // TBD: 'enrol_elis' $ra->itemid = $this->classid; // TBD $ra->timemodified = time(); $ra->modifierid = empty($USER->id) ? 0 : $USER->id; events_trigger('role_assigned', $ra); } } return $status; }
/** * Enrols a user in a track. * * @param int $userid The user id * @param int $trackid The track id */ public static function enrol($userid, $trackid) { global $DB; // make sure we don't double-enrol if ($DB->record_exists(self::TABLE, array('userid' => $userid, 'trackid' => $trackid))) { return false; } $record = new usertrack(); $record->userid = $userid; $record->trackid = $trackid; $record->save(); $user = new user($userid); $track = new track($trackid); if (!$DB->record_exists(curriculumstudent::TABLE, array('userid' => $userid, 'curriculumid' => $track->curid))) { $curstu = new curriculumstudent(); $curstu->userid = $userid; $curstu->curriculumid = $track->curid; $curstu->completed = 0; $curstu->credits = 0; $curstu->locked = 0; $curstu->save(); } events_trigger('track_assigned', $record); /** * Get autoenrollable classes in the track. Classes are autoenrollable * if: * - the autoenrol flag is set * - it is the only class in that course slot for the track */ $sql = 'SELECT classid, courseid ' . 'FROM {' . trackassignment::TABLE . '} ' . 'WHERE trackid = ? ' . 'GROUP BY courseid ' . 'HAVING COUNT(*) = 1 AND MAX(autoenrol) = 1'; $params = array($trackid); $classes = $DB->get_recordset_sql($sql, $params); foreach ($classes as $class) { // ELIS-3460: check pre-requisites ... $curcrs = new curriculumcourse(array('courseid' => $class->courseid, 'curriculumid' => $track->curid)); if (!$curcrs->prerequisites_satisfied($userid)) { //error_log("/local/elisprogram/lib/data/usertrack.class.php::enrol({$userid}); pre-requisites NOT satisfied for course: {$class->courseid}, curriculum: {$track->curid}"); continue; } $now = time(); // enrol user in each autoenrolable class $stu_record = new object(); $stu_record->userid = $userid; $stu_record->classid = $class->classid; $stu_record->enrolmenttime = $now; $enrolment = new student($stu_record); // catch enrolment limits try { $status = $enrolment->save(); } catch (pmclass_enrolment_limit_validation_exception $e) { // autoenrol into waitlist $wait_record = new object(); $wait_record->userid = $userid; $wait_record->classid = $class->classid; $wait_record->enrolmenttime = $now; $wait_record->timecreated = $now; $wait_record->position = 0; $wait_list = new waitlist($wait_record); $wait_list->save(); $status = true; } catch (Exception $e) { $param = array('message' => $e->getMessage()); echo cm_error(get_string('record_not_created_reason', 'local_elisprogram', $param)); } } unset($classes); return true; }
/** * Check whether a student should be auto-enrolled in more class. * * This function gets called when a student completes a class, and checks * whether that class was a prerequisite for other courses in a track, * and whether the student should now be enrolled in the other classes. * * Strategy: * - find tracks that the student is in, and get their curricula * - find the course that the class is associated with * - see if the course is a prerequisite in any of the curricula * - if yes, see if there are any autoenrollable classes in the tracks that * the student can be autoenrolled in */ public static function check_autoenrol_after_course_completion($enrolment) { global $CURMAN; $userid = $enrolment->userid; $courseid = $enrolment->cmclass->courseid; // this query will give us all the classes that had course $courseid as // a prerequisite, that are autoenrollable in tracks that user $userid // is in $sql = "SELECT trkcls.*, trk.curid, cls.courseid\n FROM {$CURMAN->db->prefix_table('crlm_course_prerequisite')} prereq\n INNER JOIN {$CURMAN->db->prefix_table('crlm_curriculum_course')} curcrs\n ON curcrs.id = prereq.curriculumcourseid\n INNER JOIN {$CURMAN->db->prefix_table('crlm_class')} cls\n ON curcrs.courseid = cls.courseid\n INNER JOIN {$CURMAN->db->prefix_table('crlm_track_class')} trkcls\n ON trkcls.classid = cls.id AND trkcls.autoenrol = 1\n INNER JOIN {$CURMAN->db->prefix_table('crlm_track')} trk\n ON trk.id = trkcls.trackid AND trk.curid = curcrs.curriculumid\n INNER JOIN {$CURMAN->db->prefix_table('crlm_user_track')} usrtrk\n ON usrtrk.trackid = trk.id\n WHERE prereq.courseid = {$courseid} AND usrtrk.userid = {$userid}"; $recs = $CURMAN->db->get_records_sql($sql); // now we just need to loop through them, and enrol them $recs = $recs ? $recs : array(); foreach ($recs as $trkcls) { $curcrs = new curriculumcourse(); $curcrs->courseid = $trkcls->courseid; $curcrs->curriculumid = $trkcls->curid; if ($curcrs->prerequisites_satisfied($userid)) { // no unsatisfied prereqs -- enrol the student $newenrolment = new student(); $newenrolment->userid = $userid; $newenrolment->classid = $trkcls->classid; $newenrolment->enrolmenttime = time(); $newenrolment->add(array('waitlist' => 1)); } } return true; }
/** * Updates resulting enrolments that are auto-created after users are * assigned to user sets (specifically user-track assignments, user-program * assignments, and class enrolments in a track's default class) * * Note: This is essentially equivalent to cluster_assigned_handler but * runs a fixed number of queries for scalability reasons * * @param int $userid A specific PM user id to filter on for * consideration, or all users if zero * @param int $clusterid A specific cluster / user set id to filter * on for consideration, or all users if zero */ static function update_enrolments($userid = 0, $clusterid = 0) { global $DB; require_once elispm::lib('data/usermoodle.class.php'); // error_log("/local/elisprogram/lib/data/clusterassignment.class.php::update_enrolments({$userid}, {$clusterid})"); // ELIS-7582 @set_time_limit(0); // convert provided parameters to SQL conditions $extraconditions = array(); $extraparams = array(); if (!empty($userid)) { $users = array($userid); $extraconditions[] = 'u.id = ?'; $extraparams[] = $userid; } else { $users = clusterassignment::find(new field_filter('clusterid', $clusterid)); } if (!empty($clusterid)) { $extraconditions[] = 'clu.clusterid = ?'; $extraparams[] = $clusterid; } $extrawhere = ''; if (!empty($extraconditions)) { $extrawhere = ' AND ' . implode(' AND ', $extraconditions); } //use the current time as the time created and modified for curriculum //assignments $timenow = time(); //assign to curricula based on user-cluster and cluster-curriculum //associations $sql = "INSERT INTO {" . curriculumstudent::TABLE . "}\n (userid, curriculumid, timecreated, timemodified)\n SELECT DISTINCT u.id, clucur.curriculumid, {$timenow}, {$timenow}\n FROM {" . clusterassignment::TABLE . "} clu\n JOIN {" . user::TABLE . "} u ON u.id = clu.userid\n JOIN {" . clustercurriculum::TABLE . "} clucur\n ON clucur.clusterid = clu.clusterid\n LEFT JOIN {" . curriculumstudent::TABLE . "} ca\n ON ca.userid = u.id\n AND ca.curriculumid = clucur.curriculumid\n WHERE ca.curriculumid IS NULL\n AND clucur.autoenrol = 1\n {$extrawhere}"; $DB->execute($sql, $extraparams); //assign to curricula based on user-cluster and cluster-track //associations (assigning a user to a track auto-assigns them to //the track's curriculum, track assignment happens below) $sql = "INSERT INTO {" . curriculumstudent::TABLE . "}\n (userid, curriculumid, timecreated, timemodified)\n SELECT DISTINCT u.id, trk.curid, {$timenow}, {$timenow}\n FROM {" . clusterassignment::TABLE . "} clu\n JOIN {" . user::TABLE . "} u\n ON u.id = clu.userid\n JOIN {" . clustertrack::TABLE . "} clutrk\n ON clutrk.clusterid = clu.clusterid\n JOIN {" . track::TABLE . "} trk\n ON clutrk.trackid = trk.id\n LEFT JOIN {" . curriculumstudent::TABLE . "} ca\n ON ca.userid = u.id\n AND ca.curriculumid = trk.curid\n WHERE ca.curriculumid IS NULL\n AND clutrk.autoenrol = 1\n {$extrawhere}"; $DB->execute($sql, $extraparams); //this represents the tracks that users will be assigned to //based on user-cluster and cluster-track associations //(actual assignment happens below) $exists = "EXISTS (SELECT DISTINCT u.id, clutrk.trackid\n FROM {" . clusterassignment::TABLE . "} clu\n JOIN {" . user::TABLE . "} u\n ON u.id = clu.userid\n JOIN {" . clustertrack::TABLE . "} clutrk\n ON clutrk.clusterid = clu.clusterid\n LEFT JOIN {" . usertrack::TABLE . "} ta\n ON ta.userid = u.id\n AND ta.trackid = clutrk.trackid\n WHERE ta.trackid IS NULL\n AND clutrk.autoenrol = 1\n AND outerta.trackid = clutrk.trackid\n\t {$extrawhere})"; /** * Get autoenrollable classes in the track. Classes are autoenrollable * if: * - the autoenrol flag is set * - it is the only class in that course slot for the track */ // group the classes from the same course together // only select the ones that are the only class for that course in // the given track, and if the autoenrol flag is set $sql = "SELECT outerta.classid, outerta.courseid, trk.curid\n FROM {" . trackassignment::TABLE . "} outerta\n JOIN {" . track::TABLE . "} trk ON trk.id = outerta.trackid\n WHERE {$exists}\n GROUP BY courseid\n HAVING COUNT(*) = 1 AND MAX(autoenrol) = 1"; //go through and assign user(s) to the autoenollable classes $classes = $DB->get_records_sql($sql, $extraparams); if (!empty($classes)) { foreach ($users as $user) { $userid = is_object($user) ? $user->userid : $user; foreach ($classes as $class) { // check pre-requisites $curcrs = new curriculumcourse(array('courseid' => $class->courseid, 'curriculumid' => $class->curid)); if (!$curcrs->prerequisites_satisfied($userid)) { continue; } $now = time(); // enrol user in each autoenrolable class $stu_record = new object(); $stu_record->userid = $userid; $stu_record->classid = $class->classid; $stu_record->enrolmenttime = $now; $enrolment = new student($stu_record); // catch enrolment limits try { $enrolment->save(); } catch (pmclass_enrolment_limit_validation_exception $e) { // autoenrol into waitlist $wait_record = new object(); $wait_record->userid = $userid; $wait_record->classid = $class->classid; $wait_record->enrolmenttime = $now; $wait_record->timecreated = $now; $wait_record->position = 0; $wait_list = new waitlist($wait_record); $wait_list->save(); } catch (Exception $e) { $param = array('message' => $e->getMessage()); if (in_cron()) { mtrace(get_string('record_not_created_reason', 'local_elisprogram', $param)); } else { echo cm_error(get_string('record_not_created_reason', 'local_elisprogram', $param)); } } } } } //assign to tracks based on user-cluster and cluster-track //associations $sql = "INSERT INTO {" . usertrack::TABLE . "}\n (userid, trackid)\n SELECT DISTINCT u.id, clutrk.trackid\n FROM {" . clusterassignment::TABLE . "} clu\n JOIN {" . user::TABLE . "} u\n ON u.id = clu.userid\n JOIN {" . clustertrack::TABLE . "} clutrk\n ON clutrk.clusterid = clu.clusterid\n LEFT JOIN {" . usertrack::TABLE . "} ta\n ON ta.userid = u.id\n AND ta.trackid = clutrk.trackid\n WHERE ta.trackid IS NULL\n AND clutrk.autoenrol = 1\n {$extrawhere}"; $DB->execute($sql, $extraparams); //update site-level "cluster groups" //TODO: make sure all "cluster groups" scenarios are handled here, and look at //performance in more detal if (!empty($userid) && file_exists(elispm::file('plugins/usetgroups/lib.php'))) { require_once elispm::file('plugins/usetgroups/lib.php'); //need the Moodle user id $mdluserid = $DB->get_field(usermoodle::TABLE, 'muserid', array('cuserid' => $userid)); if ($mdluserid) { //find all assignments for this user $assignments = $DB->get_recordset(clusterassignment::TABLE, array('userid' => $userid)); foreach ($assignments as $assignment) { //update site-level group assignments userset_groups_update_site_course($assignment->clusterid, true, $mdluserid); } } //update course-level group assignment userset_groups_update_groups(array('mdlusr.cuserid' => $userid)); } }