function enrol_all_track_users_in_class() { // find all users who are not enrolled in the class // TODO: validate this... $sql = "NOT EXISTS (SELECT 'x'\n FROM {" . student::TABLE . "} s\n WHERE s.classid = ? AND s.userid = {" . usertrack::TABLE . "}.userid)\n AND trackid = ?"; $params = array($this->classid, $this->trackid); $users = $this->_db->get_recordset_select(usertrack::TABLE, $sql, $params, 'userid'); if ($users->valid() === true) { // ELIS-7582 @set_time_limit(0); $timenow = time(); $count = 0; $waitlisted = 0; $prereq = 0; foreach ($users as $user) { // enrol user in track $enrol = new student(); $enrol->classid = $this->classid; $enrol->userid = $user->userid; $enrol->enrolmenttime = $timenow; try { $enrol->save(); $count++; } catch (unsatisfied_prerequisites_exception $ex) { $prereq++; } catch (pmclass_enrolment_limit_validation_exception $ex) { // autoenrol into waitlist $wait_record = new stdClass(); $wait_record->userid = $user->userid; $wait_record->classid = $this->classid; //$wait_record->enrolmenttime = $timenow; $wait_record->timecreated = $timenow; $wait_record->position = 0; $wait_list = new waitlist($wait_record); $wait_list->save(); $waitlisted++; } } print_string('n_users_enrolled', 'local_elisprogram', $count); if ($waitlisted) { print_string('n_users_waitlisted', 'local_elisprogram', $waitlisted); } if ($prereq) { print_string('n_users_unsatisfied_prereq', 'local_elisprogram', $prereq); } } else { print_string('all_users_already_enrolled', 'local_elisprogram'); } unset($users); }
/** * 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; }
/** * Test validation of duplicates * * Note: no exception thrown from waitlist.class.php for dup. */ public function test_waitlistvalidationpreventsduplicates() { global $DB; $this->load_csv_data(); $waitlist = new waitlist(array('classid' => 100, 'userid' => 1, 'position' => 1)); $waitlist->save(); $waitlistentries = $DB->get_records(waitlist::TABLE, array('classid' => 100, 'userid' => 1)); $this->assertEquals(count($waitlistentries), 1); }
function do_savewaitlist() { // action_savewaitlist global $USER, $DB; $classid = cm_get_param('id', 0, PARAM_INT); $form = $this->create_waitlistform($classid); $now = time(); if ($form->is_cancelled()) { $this->display('available'); } else { if ($data = $form->get_data()) { $class = new pmclass($classid); $userid = cm_get_crlmuserid($USER->id); $position = $DB->get_field(waitlist::TABLE, 'MAX(position)', array('classid' => $classid)) + 1; $wait_record = new object(); $wait_record->userid = $userid; $wait_record->classid = $classid; $wait_record->enrolmenttime = $class->startdate; $wait_record->timecreated = $now; $wait_record->timemodified = $now; $wait_record->position = $position; $wait_list = new waitlist($wait_record); $wait_list->save(); // TBD: was ->add() $this->display('waitlist'); } } }
/** * Handle a request to resolve the enrolment limit for a class. * * Over-enrols, adds to waitlist, or skips enrolment based on user selection. * * @param array $elements An array of elements to perform the action on. * @param int $classid The ID of the class we're enrolling into. * @param string $rawuseractions The JSON string containing the actions we want to perform. This will be an array, indexed by * element ID, with values being "waitlist" for add to waitlist, "overenrol" for overenrol, or * anything else being skip enrolment. If we are performing a bulk enrolment, a "bulk_enrol" key * will be present, which will take precendence. * @param string $enroldata A JSON string containing enrolment data for the users we want to overenrol. * @return array An array consisting of 'result' and 'num_affected', indicating success, and the number of users either enroled, * or added to waitlist, respectively. */ protected function waitlistconfirm($elements, $classid, $rawuseractions, $enroldata) { set_time_limit(0); // Unpack and process incoming desired user actions. // $rawuseractions comes from jQuery's serializeArray function, which gives us an array of arrays, each containing a "name" // and "value" member. after processing here, we will get an array indexed by user ID, with the value being the desired // waitlist action (waitlist, overenrol, skip). if (is_string($rawuseractions)) { $rawuseractions = @json_decode($rawuseractions, true); } if (empty($rawuseractions) || !is_array($rawuseractions)) { $rawuseractions = array(); } $useractions = array(); foreach ($rawuseractions as $param) { if (is_numeric($param['name']) || $param['name'] == 'bulk_action') { $useractions[$param['name']] = $param['value']; } } if (empty($useractions)) { throw new Exception('Did not receive any valid user ids.'); } // Original enrolment data. $enroldata = $this->process_enrolment_data($classid, @json_decode($enroldata)); if (empty($enroldata)) { throw new Exception('Did not receive valid enrolment data.'); } $now = time(); $numaffected = 0; foreach ($elements as $userid) { // Skip invalid userids or users which we dont have permission to modify. if (!is_numeric($userid) || !student::can_manage_assoc($userid, $classid)) { continue; } // Get action. if (isset($useractions['bulk_action'])) { $action = $useractions['bulk_action']; } else { if (isset($useractions[$userid])) { $action = $useractions[$userid]; } else { continue; } } // Perform actions. try { if ($action === 'waitlist') { $waitrecord = new object(); $waitrecord->userid = $userid; $waitrecord->classid = $classid; $waitrecord->timecreated = $now; $waitrecord->timemodified = $now; $waitrecord->position = 0; $waitlist = new waitlist($waitrecord); $status = $waitlist->save(); $numaffected++; } else { if ($action === 'overenrol') { $sturecord = $enroldata; $sturecord['userid'] = $userid; $newstu = new student($sturecord); $newstu->validation_overrides[] = 'prerequisites'; $newstu->validation_overrides[] = 'enrolment_limit'; $status = $newstu->save(); $numaffected++; } } } catch (Exception $e) { $param = array('message' => $e->getMessage()); throw new Exception(get_string('record_not_created_reason', 'local_elisprogram', $param)); } } return array('result' => 'success', 'num_affected' => $numaffected); }
/** * 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)); } }