diff --git a/add_form.php b/add_form.php index f812dbb..21cbeb7 100644 --- a/add_form.php +++ b/add_form.php @@ -54,26 +54,26 @@ class mod_attendance_add_form extends moodleform { $mform->addElement('static', 'sessiontypedescription', get_string('sessiontype', 'attendance'), get_string('commonsession', 'attendance')); $mform->addHelpButton('sessiontypedescription', 'sessiontype', 'attendance'); - $mform->addElement('hidden', 'sessiontype', attendance::SESSION_COMMON); + $mform->addElement('hidden', 'sessiontype', mod_attendance_structure::SESSION_COMMON); $mform->setType('sessiontype', PARAM_INT); break; case SEPARATEGROUPS: $mform->addElement('static', 'sessiontypedescription', get_string('sessiontype', 'attendance'), get_string('groupsession', 'attendance')); $mform->addHelpButton('sessiontypedescription', 'sessiontype', 'attendance'); - $mform->addElement('hidden', 'sessiontype', attendance::SESSION_GROUP); + $mform->addElement('hidden', 'sessiontype', mod_attendance_structure::SESSION_GROUP); $mform->setType('sessiontype', PARAM_INT); break; case VISIBLEGROUPS: $radio = array(); $radio[] = &$mform->createElement('radio', 'sessiontype', '', - get_string('commonsession', 'attendance'), attendance::SESSION_COMMON); + get_string('commonsession', 'attendance'), mod_attendance_structure::SESSION_COMMON); $radio[] = &$mform->createElement('radio', 'sessiontype', '', - get_string('groupsession', 'attendance'), attendance::SESSION_GROUP); + get_string('groupsession', 'attendance'), mod_attendance_structure::SESSION_GROUP); $mform->addGroup($radio, 'sessiontype', get_string('sessiontype', 'attendance'), ' ', false); $mform->setType('sessiontype', PARAM_INT); $mform->addHelpButton('sessiontype', 'sessiontype', 'attendance'); - $mform->setDefault('sessiontype', attendance::SESSION_COMMON); + $mform->setDefault('sessiontype', mod_attendance_structure::SESSION_COMMON); break; } if ($groupmode == SEPARATEGROUPS or $groupmode == VISIBLEGROUPS) { @@ -89,7 +89,7 @@ class mod_attendance_add_form extends moodleform { } $select = &$mform->addElement('select', 'groups', get_string('groups', 'group'), $selectgroups); $select->setMultiple(true); - $mform->disabledIf('groups', 'sessiontype', 'neq', attendance::SESSION_GROUP); + $mform->disabledIf('groups', 'sessiontype', 'neq', mod_attendance_structure::SESSION_GROUP); } else { if ($groupmode == VISIBLEGROUPS) { $mform->updateElementAttr($radio, array('disabled' => 'disabled')); @@ -187,7 +187,7 @@ class mod_attendance_add_form extends moodleform { $errors['sessionenddate'] = get_string('invalidsessionenddate', 'attendance'); } - if ($data['sessiontype'] == attendance::SESSION_GROUP and empty($data['groups'])) { + if ($data['sessiontype'] == mod_attendance_structure::SESSION_GROUP and empty($data['groups'])) { $errors['groups'] = get_string('errorgroupsnotselected', 'attendance'); } diff --git a/attendance.php b/attendance.php index c51d536..5258ae3 100644 --- a/attendance.php +++ b/attendance.php @@ -40,7 +40,7 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST) require_login($course, true, $cm); $pageparams->sessionid = $id; -$att = new attendance($attendance, $cm, $course, $PAGE->context, $pageparams); +$att = new mod_attendance_structure($attendance, $cm, $course, $PAGE->context, $pageparams); // Require that a session key is passed to this page. require_sesskey(); diff --git a/classes/structure.php b/classes/structure.php new file mode 100644 index 0000000..2a25cd8 --- /dev/null +++ b/classes/structure.php @@ -0,0 +1,1149 @@ +. + +class mod_attendance_structure { + const SESSION_COMMON = 0; + const SESSION_GROUP = 1; + + /** @var stdclass course module record */ + public $cm; + + /** @var stdclass course record */ + public $course; + + /** @var stdclass context object */ + public $context; + + /** @var int attendance instance identifier */ + public $id; + + /** @var string attendance activity name */ + public $name; + + /** @var float number (10, 5) unsigned, the maximum grade for attendance */ + public $grade; + + /** current page parameters */ + public $pageparams; + + private $groupmode; + + private $statuses; + private $allstatuses; // Cache list of all statuses (not just one used by current session). + + // Array by sessionid. + private $sessioninfo = array(); + + // Arrays by userid. + private $usertakensesscount = array(); + private $userstatusesstat = array(); + + /** + * Initializes the attendance API instance using the data from DB + * + * Makes deep copy of all passed records properties. Replaces integer $course attribute + * with a full database record (course should not be stored in instances table anyway). + * + * @param stdClass $dbrecord Attandance instance data from {attendance} table + * @param stdClass $cm Course module record as returned by {@link get_coursemodule_from_id()} + * @param stdClass $course Course record from {course} table + * @param stdClass $context The context of the workshop instance + */ + public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null, $pageparams=null) { + foreach ($dbrecord as $field => $value) { + if (property_exists('mod_attendance_structure', $field)) { + $this->{$field} = $value; + } else { + throw new coding_exception('The attendance table has a field with no property in the attendance class'); + } + } + $this->cm = $cm; + $this->course = $course; + if (is_null($context)) { + $this->context = context_module::instance($this->cm->id); + } else { + $this->context = $context; + } + + $this->pageparams = $pageparams; + } + + public function get_group_mode() { + if (is_null($this->groupmode)) { + $this->groupmode = groups_get_activity_groupmode($this->cm, $this->course); + } + return $this->groupmode; + } + + /** + * Returns current sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return array of records or an empty array + */ + public function get_current_sessions() { + global $DB; + + $today = time(); // Because we compare with database, we don't need to use usertime(). + + $sql = "SELECT * + FROM {attendance_sessions} + WHERE :time BETWEEN sessdate AND (sessdate + duration) + AND attendanceid = :aid"; + $params = array( + 'time' => $today, + 'aid' => $this->id); + + return $DB->get_records_sql($sql, $params); + } + + /** + * Returns today sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return array of records or an empty array + */ + public function get_today_sessions() { + global $DB; + + $start = usergetmidnight(time()); + $end = $start + DAYSECS; + + $sql = "SELECT * + FROM {attendance_sessions} + WHERE sessdate >= :start AND sessdate < :end + AND attendanceid = :aid"; + $params = array( + 'start' => $start, + 'end' => $end, + 'aid' => $this->id); + + return $DB->get_records_sql($sql, $params); + } + + /** + * Returns today sessions suitable for copying attendance log + * + * Fetches data from {attendance_sessions} + * + * @return array of records or an empty array + */ + public function get_today_sessions_for_copy($sess) { + global $DB; + + $start = usergetmidnight($sess->sessdate); + + $sql = "SELECT * + FROM {attendance_sessions} + WHERE sessdate >= :start AND sessdate <= :end AND + (groupid = 0 OR groupid = :groupid) AND + lasttaken > 0 AND attendanceid = :aid"; + $params = array( + 'start' => $start, + 'end' => $sess->sessdate, + 'groupid' => $sess->groupid, + 'aid' => $this->id); + + return $DB->get_records_sql($sql, $params); + } + + /** + * Returns count of hidden sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return count of hidden sessions + */ + public function get_hidden_sessions_count() { + global $DB; + + $where = "attendanceid = :aid AND sessdate < :csdate"; + $params = array( + 'aid' => $this->id, + 'csdate' => $this->course->startdate); + + return $DB->count_records_select('attendance_sessions', $where, $params); + } + + /** + * Returns the hidden sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return hidden sessions + */ + public function get_hidden_sessions() { + global $DB; + + $where = "attendanceid = :aid AND sessdate < :csdate"; + $params = array( + 'aid' => $this->id, + 'csdate' => $this->course->startdate); + + return $DB->get_records_select('attendance_sessions', $where, $params); + } + + public function get_filtered_sessions() { + global $DB; + + if ($this->pageparams->startdate && $this->pageparams->enddate) { + $where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate >= :sdate AND sessdate < :edate"; + } else if ($this->pageparams->enddate) { + $where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate < :edate"; + } else { + $where = "attendanceid = :aid AND sessdate >= :csdate"; + } + + if ($this->pageparams->get_current_sesstype() > att_page_with_filter_controls::SESSTYPE_ALL) { + $where .= " AND (groupid = :cgroup OR groupid = 0)"; + } + $params = array( + 'aid' => $this->id, + 'csdate' => $this->course->startdate, + 'sdate' => $this->pageparams->startdate, + 'edate' => $this->pageparams->enddate, + 'cgroup' => $this->pageparams->get_current_sesstype()); + $sessions = $DB->get_records_select('attendance_sessions', $where, $params, 'sessdate asc'); + foreach ($sessions as $sess) { + if (empty($sess->description)) { + $sess->description = get_string('nodescription', 'attendance'); + } else { + $sess->description = file_rewrite_pluginfile_urls($sess->description, + 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); + } + } + + return $sessions; + } + + /** + * @return moodle_url of manage.php for attendance instance + */ + public function url_manage($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/manage.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempusers.php for attendance instance + */ + public function url_managetemp($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempusers.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempdelete.php for attendance instance + */ + public function url_tempdelete($params=array()) { + $params = array_merge(array('id' => $this->cm->id, 'action' => 'delete'), $params); + return new moodle_url('/mod/attendance/tempedit.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempedit.php for attendance instance + */ + public function url_tempedit($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempedit.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempedit.php for attendance instance + */ + public function url_tempmerge($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempmerge.php', $params); + } + + /** + * @return moodle_url of sessions.php for attendance instance + */ + public function url_sessions($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/sessions.php', $params); + } + + /** + * @return moodle_url of report.php for attendance instance + */ + public function url_report($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/report.php', $params); + } + + /** + * @return moodle_url of export.php for attendance instance + */ + public function url_export() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attendance/export.php', $params); + } + + /** + * @return moodle_url of attsettings.php for attendance instance + */ + public function url_preferences($params=array()) { + // Add the statusset params. + if (isset($this->pageparams->statusset) && !isset($params['statusset'])) { + $params['statusset'] = $this->pageparams->statusset; + } + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/preferences.php', $params); + } + + /** + * @return moodle_url of attendances.php for attendance instance + */ + public function url_take($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/take.php', $params); + } + + public function url_view($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/view.php', $params); + } + + public function add_sessions($sessions) { + global $DB; + + foreach ($sessions as $sess) { + $sess->attendanceid = $this->id; + + $sess->id = $DB->insert_record('attendance_sessions', $sess); + $description = file_save_draft_area_files($sess->descriptionitemid, + $this->context->id, 'mod_attendance', 'session', $sess->id, + array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), + $sess->description); + $DB->set_field('attendance_sessions', 'description', $description, array('id' => $sess->id)); + + $infoarray = array(); + $infoarray[] = construct_session_full_date_time($sess->sessdate, $sess->duration); + + // Trigger a session added event. + $event = \mod_attendance\event\session_added::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('info' => implode(',', $infoarray)) + )); + $event->add_record_snapshot('course_modules', $this->cm); + $sess->description = $description; + $sess->lasttaken = 0; + $sess->lasttakenby = 0; + $sess->studentscanmark = 0; + $event->add_record_snapshot('attendance_sessions', $sess); + $event->trigger(); + } + } + + public function update_session_from_form_data($formdata, $sessionid) { + global $DB; + + if (!$sess = $DB->get_record('attendance_sessions', array('id' => $sessionid) )) { + print_error('No such session in this course'); + } + + $sesstarttime = $formdata->sestime['starthour'] * HOURSECS + $formdata->sestime['startminute'] * MINSECS; + $sesendtime = $formdata->sestime['endhour'] * HOURSECS + $formdata->sestime['endminute'] * MINSECS; + + $sess->sessdate = $formdata->sessiondate + $sesstarttime; + $sess->duration = $sesendtime - $sesstarttime; + + $description = file_save_draft_area_files($formdata->sdescription['itemid'], + $this->context->id, 'mod_attendance', 'session', $sessionid, + array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), $formdata->sdescription['text']); + $sess->description = $description; + $sess->descriptionformat = $formdata->sdescription['format']; + + $sess->timemodified = time(); + $DB->update_record('attendance_sessions', $sess); + + $info = construct_session_full_date_time($sess->sessdate, $sess->duration); + $event = \mod_attendance\event\session_updated::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('info' => $info, 'sessionid' => $sessionid, 'action' => att_sessions_page_params::ACTION_UPDATE))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_sessions', $sess); + $event->trigger(); + } + + /** + * Used to record attendance submitted by the student. + * + * @global type $DB + * @global type $USER + * @param type $mformdata + * @return boolean + */ + public function take_from_student($mformdata) { + global $DB, $USER; + + $statuses = implode(',', array_keys( (array)$this->get_statuses() )); + $now = time(); + + $record = new stdClass(); + $record->studentid = $USER->id; + $record->statusid = $mformdata->status; + $record->statusset = $statuses; + $record->remarks = get_string('set_by_student', 'mod_attendance'); + $record->sessionid = $mformdata->sessid; + $record->timetaken = $now; + $record->takenby = $USER->id; + + $dbsesslog = $this->get_session_log($mformdata->sessid); + if (array_key_exists($record->studentid, $dbsesslog)) { + // Already recorded do not save. + return false; + } + + $logid = $DB->insert_record('attendance_log', $record, false); + $record->id = $logid; + + // Update the session to show that a register has been taken, or staff may overwrite records. + $session = $this->get_session_info($mformdata->sessid); + $session->lasttaken = $now; + $session->lasttakenby = $USER->id; + $DB->update_record('attendance_sessions', $session); + + // Update the users grade. + $this->update_users_grade(array($USER->id)); + + /* create url for link in log screen + * need to set grouptype to 0 to allow take attendance page to be called + * from report/log page */ + + $params = array( + 'sessionid' => $this->pageparams->sessionid, + 'grouptype' => 0); + + // Log the change. + $event = \mod_attendance\event\attendance_taken_by_student::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => $params)); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_sessions', $session); + $event->add_record_snapshot('attendance_log', $record); + $event->trigger(); + + return true; + } + + public function take_from_form_data($formdata) { + global $DB, $USER; + // TODO: WARNING - $formdata is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below. + $statuses = implode(',', array_keys( (array)$this->get_statuses() )); + $now = time(); + $sesslog = array(); + $formdata = (array)$formdata; + foreach ($formdata as $key => $value) { + if (substr($key, 0, 4) == 'user') { + $sid = substr($key, 4); + if (!(is_numeric($sid) && is_numeric($value))) { // Sanity check on $sid and $value. + print_error('nonnumericid', 'attendance'); + } + $sesslog[$sid] = new stdClass(); + $sesslog[$sid]->studentid = $sid; // We check is_numeric on this above. + $sesslog[$sid]->statusid = $value; // We check is_numeric on this above. + $sesslog[$sid]->statusset = $statuses; + $sesslog[$sid]->remarks = array_key_exists('remarks'.$sid, $formdata) ? clean_param($formdata['remarks'.$sid], PARAM_TEXT) : ''; + $sesslog[$sid]->sessionid = $this->pageparams->sessionid; + $sesslog[$sid]->timetaken = $now; + $sesslog[$sid]->takenby = $USER->id; + } + } + + $dbsesslog = $this->get_session_log($this->pageparams->sessionid); + foreach ($sesslog as $log) { + if ($log->statusid) { + if (array_key_exists($log->studentid, $dbsesslog)) { + $log->id = $dbsesslog[$log->studentid]->id; + $DB->update_record('attendance_log', $log); + } else { + $DB->insert_record('attendance_log', $log, false); + } + } + } + + $session = $this->get_session_info($this->pageparams->sessionid); + $session->lasttaken = $now; + $session->lasttakenby = $USER->id; + $DB->update_record('attendance_sessions', $session); + + if ($this->grade != 0) { + $this->update_users_grade(array_keys($sesslog)); + } + + // Create url for link in log screen. + $params = array( + 'sessionid' => $this->pageparams->sessionid, + 'grouptype' => $this->pageparams->grouptype); + $event = \mod_attendance\event\attendance_taken::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => $params)); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_sessions', $session); + $event->trigger(); + + $group = 0; + if ($this->pageparams->grouptype != self::SESSION_COMMON) { + $group = $this->pageparams->grouptype; + } else { + if ($this->pageparams->group) { + $group = $this->pageparams->group; + } + } + + $totalusers = count_enrolled_users(context_module::instance($this->cm->id), 'mod/attendance:canbelisted', $group); + $usersperpage = $this->pageparams->perpage; + + if (!empty($this->pageparams->page) && $this->pageparams->page && $totalusers && $usersperpage) { + $numberofpages = ceil($totalusers / $usersperpage); + if ($this->pageparams->page < $numberofpages) { + $params['page'] = $this->pageparams->page + 1; + redirect($this->url_take($params), get_string('moreattendance', 'attendance')); + } + } + + redirect($this->url_manage(), get_string('attendancesuccess', 'attendance')); + } + + /** + * MDL-27591 made this method obsolete. + */ + public function get_users($groupid = 0, $page = 1) { + global $DB, $CFG; + + // Fields we need from the user table. + $userfields = user_picture::fields('u', array('username' , 'idnumber' , 'institution' , 'department')); + + if (isset($this->pageparams->sort) and ($this->pageparams->sort == ATT_SORT_FIRSTNAME)) { + $orderby = "u.firstname ASC, u.lastname ASC, u.idnumber ASC, u.institution ASC, u.department ASC"; + } else { + $orderby = "u.lastname ASC, u.firstname ASC, u.idnumber ASC, u.institution ASC, u.department ASC"; + } + + if ($page) { + $usersperpage = $this->pageparams->perpage; + if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) { + $startusers = ($page - 1) * $usersperpage; + if ($groupid == 0) { + $groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id')); + } else { + $groups = $groupid; + } + $users = get_users_by_capability($this->context, 'mod/attendance:canbelisted', + $userfields.',u.id, u.firstname, u.lastname, u.email', + $orderby, $startusers, $usersperpage, $groups, + '', false, true); + } else { + $startusers = ($page - 1) * $usersperpage; + $users = get_enrolled_users($this->context, 'mod/attendance:canbelisted', $groupid, $userfields, + $orderby, $startusers, $usersperpage); + } + } else { + if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) { + if ($groupid == 0) { + $groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id')); + } else { + $groups = $groupid; + } + $users = get_users_by_capability($this->context, 'mod/attendance:canbelisted', + $userfields.',u.id, u.firstname, u.lastname, u.email', + $orderby, '', '', $groups, + '', false, true); + } else { + $users = get_enrolled_users($this->context, 'mod/attendance:canbelisted', $groupid, $userfields, $orderby); + } + } + + // Add a flag to each user indicating whether their enrolment is active. + if (!empty($users)) { + list($sql, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED, 'usid0'); + + // See CONTRIB-4868. + $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; + $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END'; + + // See CONTRIB-3549. + $sql = "SELECT ue.userid, MIN(ue.status) as status, + $mintime AS mintime, + $maxtime AS maxtime + FROM {user_enrolments} ue + JOIN {enrol} e ON e.id = ue.enrolid + WHERE ue.userid $sql + AND e.status = :estatus + AND e.courseid = :courseid + GROUP BY ue.userid"; + $params += array('zerotime' => 0, 'estatus' => ENROL_INSTANCE_ENABLED, 'courseid' => $this->course->id); + $enrolments = $DB->get_records_sql($sql, $params); + + foreach ($users as $user) { + $users[$user->id]->enrolmentstatus = $enrolments[$user->id]->status; + $users[$user->id]->enrolmentstart = $enrolments[$user->id]->mintime; + $users[$user->id]->enrolmentend = $enrolments[$user->id]->maxtime; + $users[$user->id]->type = 'standard'; // Mark as a standard (not a temporary) user. + } + } + + // Add the 'temporary' users to this list. + $tempusers = $DB->get_records('attendance_tempusers', array('courseid' => $this->course->id)); + foreach ($tempusers as $tempuser) { + $users[] = self::tempuser_to_user($tempuser); + } + + return $users; + } + + // Convert a tempuser record into a user object. + protected static function tempuser_to_user($tempuser) { + $ret = (object)array( + 'id' => $tempuser->studentid, + 'firstname' => $tempuser->fullname, + 'email' => $tempuser->email, + 'username' => '', + 'enrolmentstatus' => 0, + 'enrolmentstart' => 0, + 'enrolmentend' => 0, + 'picture' => 0, + 'type' => 'temporary', + ); + foreach (get_all_user_name_fields() as $namefield) { + if (!isset($ret->$namefield)) { + $ret->$namefield = ''; + } + } + return $ret; + } + + public function get_user($userid) { + global $DB; + + $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); + + // Look for 'temporary' users and return their details from the attendance_tempusers table. + if ($user->idnumber == 'tempghost') { + $tempuser = $DB->get_record('attendance_tempusers', array('studentid' => $userid), '*', MUST_EXIST); + return self::tempuser_to_user($tempuser); + } + + $user->type = 'standard'; + + // See CONTRIB-4868. + $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; + $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END'; + + $sql = "SELECT ue.userid, ue.status, + $mintime AS mintime, + $maxtime AS maxtime + FROM {user_enrolments} ue + JOIN {enrol} e ON e.id = ue.enrolid + WHERE ue.userid = :uid + AND e.status = :estatus + AND e.courseid = :courseid + GROUP BY ue.userid, ue.status"; + $params = array('zerotime' => 0, 'uid' => $userid, 'estatus' => ENROL_INSTANCE_ENABLED, 'courseid' => $this->course->id); + $enrolments = $DB->get_record_sql($sql, $params); + + $user->enrolmentstatus = $enrolments->status; + $user->enrolmentstart = $enrolments->mintime; + $user->enrolmentend = $enrolments->maxtime; + + return $user; + } + + public function get_statuses($onlyvisible = true, $allsets = false) { + if (!isset($this->statuses)) { + // Get the statuses for the current set only. + $statusset = 0; + if (isset($this->pageparams->statusset)) { + $statusset = $this->pageparams->statusset; + } else if (isset($this->pageparams->sessionid)) { + $sessioninfo = $this->get_session_info($this->pageparams->sessionid); + $statusset = $sessioninfo->statusset; + } + $this->statuses = att_get_statuses($this->id, $onlyvisible, $statusset); + $this->allstatuses = att_get_statuses($this->id, $onlyvisible); + } + + // Return all sets, if requested. + if ($allsets) { + return $this->allstatuses; + } + return $this->statuses; + } + + public function get_session_info($sessionid) { + global $DB; + + if (!array_key_exists($sessionid, $this->sessioninfo)) { + $this->sessioninfo[$sessionid] = $DB->get_record('attendance_sessions', array('id' => $sessionid)); + } + if (empty($this->sessioninfo[$sessionid]->description)) { + $this->sessioninfo[$sessionid]->description = get_string('nodescription', 'attendance'); + } else { + $this->sessioninfo[$sessionid]->description = file_rewrite_pluginfile_urls($this->sessioninfo[$sessionid]->description, + 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $this->sessioninfo[$sessionid]->id); + } + return $this->sessioninfo[$sessionid]; + } + + public function get_sessions_info($sessionids) { + global $DB; + + list($sql, $params) = $DB->get_in_or_equal($sessionids); + $sessions = $DB->get_records_select('attendance_sessions', "id $sql", $params, 'sessdate asc'); + + foreach ($sessions as $sess) { + if (empty($sess->description)) { + $sess->description = get_string('nodescription', 'attendance'); + } else { + $sess->description = file_rewrite_pluginfile_urls($sess->description, + 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); + } + } + + return $sessions; + } + + public function get_session_log($sessionid) { + global $DB; + + return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id'); + } + + public function get_user_stat($userid) { + $ret = array(); + $ret['completed'] = $this->get_user_taken_sessions_count($userid); + $ret['statuses'] = $this->get_user_statuses_stat($userid); + + return $ret; + } + + public function get_user_taken_sessions_count($userid) { + if (!array_key_exists($userid, $this->usertakensesscount)) { + if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) { + $this->usertakensesscount[$userid] = att_get_user_taken_sessions_count($this->id, $this->course->startdate, + $userid, $this->cm, $this->pageparams->startdate, $this->pageparams->enddate); + } else { + $this->usertakensesscount[$userid] = att_get_user_taken_sessions_count($this->id, $this->course->startdate, + $userid, $this->cm); + } + } + return $this->usertakensesscount[$userid]; + } + + /** + * + * @global type $DB + * @param type $userid + * @param type $filters - An array things to filter by. For now only enddate is valid. + * @return type + */ + public function get_user_statuses_stat($userid, array $filters = null) { + global $DB; + $params = array( + 'aid' => $this->id, + 'cstartdate' => $this->course->startdate, + 'uid' => $userid); + + $processedfilters = array(); + + // We test for any valid filters sent. + if (isset($filters['enddate'])) { + $processedfilters[] = 'ats.sessdate <= :enddate'; + $params['enddate'] = $filters['enddate']; + } + + // Make the filter array into a SQL string. + if (!empty($processedfilters)) { + $processedfilters = ' AND '.implode(' AND ', $processedfilters); + } else { + $processedfilters = ''; + } + + $period = ''; + if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) { + $period = ' AND ats.sessdate >= :sdate AND ats.sessdate < :edate '; + $params['sdate'] = $this->pageparams->startdate; + $params['edate'] = $this->pageparams->enddate; + } + + if ($this->get_group_mode()) { + $qry = "SELECT al.statusid, count(al.statusid) AS stcnt + FROM {attendance_log} al + JOIN {attendance_sessions} ats ON al.sessionid = ats.id + LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid + WHERE ats.attendanceid = :aid AND + ats.sessdate >= :cstartdate AND + al.studentid = :uid AND + (ats.groupid = 0 or gm.id is NOT NULL)".$period.$processedfilters." + GROUP BY al.statusid"; + } else { + $qry = "SELECT al.statusid, count(al.statusid) AS stcnt + FROM {attendance_log} al + JOIN {attendance_sessions} ats + ON al.sessionid = ats.id + WHERE ats.attendanceid = :aid AND + ats.sessdate >= :cstartdate AND + al.studentid = :uid".$period.$processedfilters." + GROUP BY al.statusid"; + } + + // We do not want to cache, or use a cached version of the results when a filter is set. + if ($filters !== null) { + return $DB->get_records_sql($qry, $params); + } else if (!array_key_exists($userid, $this->userstatusesstat)) { + // Not filtered so if we do not already have them do the query. + $this->userstatusesstat[$userid] = $DB->get_records_sql($qry, $params); + } + + // Return the cached stats. + return $this->userstatusesstat[$userid]; + } + + /** + * + * @param type $userid + * @param type $filters - An array things to filter by. For now only enddate is valid. + * @return type + */ + public function get_user_grade($userid, array $filters = null) { + return att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses(true, true)); + } + + // For getting sessions count implemented simplest method - taken sessions. + // It can have error if users don't have attendance info for some sessions. + // In the future we can implement another methods: + // * all sessions between user start enrolment date and now; + // * all sessions between user start and end enrolment date. + // While implementing those methods we need recalculate grades of all users + // on session adding. + public function get_user_max_grade($userid) { + return att_get_user_max_grade($this->get_user_taken_sessions_count($userid), $this->get_statuses(true, true)); + } + + public function update_users_grade($userids) { + $grades = array(); + + foreach ($userids as $userid) { + $grades[$userid] = new stdClass(); + $grades[$userid]->userid = $userid; + $grades[$userid]->rawgrade = attendance_calc_user_grade_fraction($this->get_user_grade($userid), + $this->get_user_max_grade($userid)) * $this->grade; + } + + return grade_update('mod/attendance', $this->course->id, 'mod', 'attendance', + $this->id, 0, $grades); + } + + public function get_user_filtered_sessions_log($userid) { + global $DB; + + if ($this->pageparams->startdate && $this->pageparams->enddate) { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND + ats.sessdate >= :sdate AND ats.sessdate < :edate"; + } else { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; + } + if ($this->get_group_mode()) { + $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks + FROM {attendance_sessions} ats + JOIN {attendance_log} al ON ats.id = al.sessionid AND al.studentid = :uid + LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid + WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL) + ORDER BY ats.sessdate ASC"; + + $params = array( + 'uid' => $userid, + 'aid' => $this->id, + 'csdate' => $this->course->startdate, + 'sdate' => $this->pageparams->startdate, + 'edate' => $this->pageparams->enddate); + + } else { + $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks + FROM {attendance_sessions} ats + JOIN {attendance_log} al + ON ats.id = al.sessionid AND al.studentid = :uid + WHERE $where + ORDER BY ats.sessdate ASC"; + + $params = array( + 'uid' => $userid, + 'aid' => $this->id, + 'csdate' => $this->course->startdate, + 'sdate' => $this->pageparams->startdate, + 'edate' => $this->pageparams->enddate); + } + $sessions = $DB->get_records_sql($sql, $params); + + return $sessions; + } + + public function get_user_filtered_sessions_log_extended($userid) { + global $DB; + // All taked sessions (including previous groups). + + if ($this->pageparams->startdate && $this->pageparams->enddate) { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND + ats.sessdate >= :sdate AND ats.sessdate < :edate"; + } else { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; + } + + // We need to add this concatination so that moodle will use it as the array index that is a string. + // If the array's index is a number it will not merge entries. + // It would be better as a UNION query but unfortunatly MS SQL does not seem to support doing a + // DISTINCT on a the description field. + $id = $DB->sql_concat(':value', 'ats.id'); + if ($this->get_group_mode()) { + $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, + al.statusid, al.remarks, ats.studentscanmark + FROM {attendance_sessions} ats + RIGHT JOIN {attendance_log} al + ON ats.id = al.sessionid AND al.studentid = :uid + LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid + WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL) + ORDER BY ats.sessdate ASC"; + } else { + $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, + al.statusid, al.remarks, ats.studentscanmark + FROM {attendance_sessions} ats + RIGHT JOIN {attendance_log} al + ON ats.id = al.sessionid AND al.studentid = :uid + WHERE $where + ORDER BY ats.sessdate ASC"; + } + + $params = array( + 'uid' => $userid, + 'aid' => $this->id, + 'csdate' => $this->course->startdate, + 'sdate' => $this->pageparams->startdate, + 'edate' => $this->pageparams->enddate, + 'value' => 'c'); + $sessions = $DB->get_records_sql($sql, $params); + + // All sessions for current groups. + + $groups = array_keys(groups_get_all_groups($this->course->id, $userid)); + $groups[] = 0; + list($gsql, $gparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'gid0'); + + if ($this->pageparams->startdate && $this->pageparams->enddate) { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND + ats.sessdate >= :sdate AND ats.sessdate < :edate AND ats.groupid $gsql"; + } else { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql"; + } + + $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, + al.statusid, al.remarks, ats.studentscanmark + FROM {attendance_sessions} ats + LEFT JOIN {attendance_log} al + ON ats.id = al.sessionid AND al.studentid = :uid + WHERE $where + ORDER BY ats.sessdate ASC"; + + $params = array_merge($params, $gparams); + $sessions = array_merge($sessions, $DB->get_records_sql($sql, $params)); + + foreach ($sessions as $sess) { + if (empty($sess->description)) { + $sess->description = get_string('nodescription', 'attendance'); + } else { + $sess->description = file_rewrite_pluginfile_urls($sess->description, + 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); + } + } + + return $sessions; + } + + public function delete_sessions($sessionsids) { + global $DB; + + list($sql, $params) = $DB->get_in_or_equal($sessionsids); + $DB->delete_records_select('attendance_log', "sessionid $sql", $params); + $DB->delete_records_list('attendance_sessions', 'id', $sessionsids); + $event = \mod_attendance\event\session_deleted::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('info' => implode(', ', $sessionsids)))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->trigger(); + } + + public function update_sessions_duration($sessionsids, $duration) { + global $DB; + + $now = time(); + $sessions = $DB->get_recordset_list('attendance_sessions', 'id', $sessionsids); + foreach ($sessions as $sess) { + $sess->duration = $duration; + $sess->timemodified = $now; + $DB->update_record('attendance_sessions', $sess); + $event = \mod_attendance\event\session_duration_updated::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('info' => implode(', ', $sessionsids)))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_sessions', $sess); + $event->trigger(); + } + $sessions->close(); + } + + /** + * Remove a status variable from an attendance instance + * + * @global moodle_database $DB + * @param stdClass $status + */ + public function remove_status($status) { + global $DB; + + $DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id)); + $event = \mod_attendance\event\status_removed::create(array( + 'objectid' => $status->id, + 'context' => $this->context, + 'other' => array( + 'acronym' => $status->acronym, + 'description' => $status->description + ))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_statuses', $status); + $event->trigger(); + } + + /** + * Add an attendance status variable + * + * @global moodle_database $DB + * @param string $acronym + * @param string $description + * @param int $grade + */ + public function add_status($acronym, $description, $grade) { + global $DB; + + if ($acronym && $description) { + $rec = new stdClass(); + $rec->courseid = $this->course->id; + $rec->attendanceid = $this->id; + $rec->acronym = $acronym; + $rec->description = $description; + $rec->grade = $grade; + $rec->setnumber = $this->pageparams->statusset; // Save which set it is part of. + $rec->deleted = 0; + $rec->visible = 1; + $id = $DB->insert_record('attendance_statuses', $rec); + $rec->id = $id; + + $event = \mod_attendance\event\status_added::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_statuses', $rec); + $event->trigger(); + } else { + print_error('cantaddstatus', 'attendance', $this->url_preferences()); + } + } + + /** + * Update status variable for a particular Attendance module instance + * + * @global moodle_database $DB + * @param stdClass $status + * @param string $acronym + * @param string $description + * @param int $grade + * @param bool $visible + */ + public function update_status($status, $acronym, $description, $grade, $visible) { + global $DB; + + if (isset($visible)) { + $status->visible = $visible; + $updated[] = $visible ? get_string('show') : get_string('hide'); + } else if (empty($acronym) || empty($description)) { + return array('acronym' => $acronym, 'description' => $description); + } + + $updated = array(); + + if ($acronym) { + $status->acronym = $acronym; + $updated[] = $acronym; + } + if ($description) { + $status->description = $description; + $updated[] = $description; + } + if (isset($grade)) { + $status->grade = $grade; + $updated[] = $grade; + } + $DB->update_record('attendance_statuses', $status); + + $event = \mod_attendance\event\status_updated::create(array( + 'objectid' => $this->id, + 'context' => $this->context, + 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade, + 'updated' => implode(' ', $updated)))); + $event->add_record_snapshot('course_modules', $this->cm); + $event->add_record_snapshot('attendance_statuses', $status); + $event->trigger(); + } + + /** + * Check if the email address is already in use by either another temporary user, + * or a real user. + * + * @param string $email the address to check for + * @param int $tempuserid optional the ID of the temporary user (to avoid matching against themself) + * @return null|string the error message to display, null if there is no error + */ + public static function check_existing_email($email, $tempuserid = 0) { + global $DB; + + if (empty($email)) { + return null; // Fine to create temporary users without an email address. + } + if ($tempuser = $DB->get_record('attendance_tempusers', array('email' => $email), 'id')) { + if ($tempuser->id != $tempuserid) { + return get_string('tempexists', 'attendance'); + } + } + if ($DB->record_exists('user', array('email' => $email))) { + return get_string('userexists', 'attendance'); + } + + return null; + } +} diff --git a/export.php b/export.php index 5258d4e..ffc98a4 100644 --- a/export.php +++ b/export.php @@ -39,7 +39,7 @@ require_login($course, true, $cm); $context = context_module::instance($cm->id); require_capability('mod/attendance:export', $context); -$att = new attendance($att, $cm, $course, $context); +$att = new mod_attendance_structure($att, $cm, $course, $context); $PAGE->set_url($att->url_export()); $PAGE->set_title($course->shortname. ": ".$att->name); diff --git a/locallib.php b/locallib.php index d057aa8..947b223 100644 --- a/locallib.php +++ b/locallib.php @@ -407,1139 +407,6 @@ class att_preferences_page_params { -class attendance { - const SESSION_COMMON = 0; - const SESSION_GROUP = 1; - - /** @var stdclass course module record */ - public $cm; - - /** @var stdclass course record */ - public $course; - - /** @var stdclass context object */ - public $context; - - /** @var int attendance instance identifier */ - public $id; - - /** @var string attendance activity name */ - public $name; - - /** @var float number (10, 5) unsigned, the maximum grade for attendance */ - public $grade; - - /** current page parameters */ - public $pageparams; - - private $groupmode; - - private $statuses; - private $allstatuses; // Cache list of all statuses (not just one used by current session). - - // Array by sessionid. - private $sessioninfo = array(); - - // Arrays by userid. - private $usertakensesscount = array(); - private $userstatusesstat = array(); - - /** - * Initializes the attendance API instance using the data from DB - * - * Makes deep copy of all passed records properties. Replaces integer $course attribute - * with a full database record (course should not be stored in instances table anyway). - * - * @param stdClass $dbrecord Attandance instance data from {attendance} table - * @param stdClass $cm Course module record as returned by {@link get_coursemodule_from_id()} - * @param stdClass $course Course record from {course} table - * @param stdClass $context The context of the workshop instance - */ - public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null, $pageparams=null) { - foreach ($dbrecord as $field => $value) { - if (property_exists('attendance', $field)) { - $this->{$field} = $value; - } else { - throw new coding_exception('The attendance table has a field with no property in the attendance class'); - } - } - $this->cm = $cm; - $this->course = $course; - if (is_null($context)) { - $this->context = context_module::instance($this->cm->id); - } else { - $this->context = $context; - } - - $this->pageparams = $pageparams; - } - - public function get_group_mode() { - if (is_null($this->groupmode)) { - $this->groupmode = groups_get_activity_groupmode($this->cm, $this->course); - } - return $this->groupmode; - } - - /** - * Returns current sessions for this attendance - * - * Fetches data from {attendance_sessions} - * - * @return array of records or an empty array - */ - public function get_current_sessions() { - global $DB; - - $today = time(); // Because we compare with database, we don't need to use usertime(). - - $sql = "SELECT * - FROM {attendance_sessions} - WHERE :time BETWEEN sessdate AND (sessdate + duration) - AND attendanceid = :aid"; - $params = array( - 'time' => $today, - 'aid' => $this->id); - - return $DB->get_records_sql($sql, $params); - } - - /** - * Returns today sessions for this attendance - * - * Fetches data from {attendance_sessions} - * - * @return array of records or an empty array - */ - public function get_today_sessions() { - global $DB; - - $start = usergetmidnight(time()); - $end = $start + DAYSECS; - - $sql = "SELECT * - FROM {attendance_sessions} - WHERE sessdate >= :start AND sessdate < :end - AND attendanceid = :aid"; - $params = array( - 'start' => $start, - 'end' => $end, - 'aid' => $this->id); - - return $DB->get_records_sql($sql, $params); - } - - /** - * Returns today sessions suitable for copying attendance log - * - * Fetches data from {attendance_sessions} - * - * @return array of records or an empty array - */ - public function get_today_sessions_for_copy($sess) { - global $DB; - - $start = usergetmidnight($sess->sessdate); - - $sql = "SELECT * - FROM {attendance_sessions} - WHERE sessdate >= :start AND sessdate <= :end AND - (groupid = 0 OR groupid = :groupid) AND - lasttaken > 0 AND attendanceid = :aid"; - $params = array( - 'start' => $start, - 'end' => $sess->sessdate, - 'groupid' => $sess->groupid, - 'aid' => $this->id); - - return $DB->get_records_sql($sql, $params); - } - - /** - * Returns count of hidden sessions for this attendance - * - * Fetches data from {attendance_sessions} - * - * @return count of hidden sessions - */ - public function get_hidden_sessions_count() { - global $DB; - - $where = "attendanceid = :aid AND sessdate < :csdate"; - $params = array( - 'aid' => $this->id, - 'csdate' => $this->course->startdate); - - return $DB->count_records_select('attendance_sessions', $where, $params); - } - - /** - * Returns the hidden sessions for this attendance - * - * Fetches data from {attendance_sessions} - * - * @return hidden sessions - */ - public function get_hidden_sessions() { - global $DB; - - $where = "attendanceid = :aid AND sessdate < :csdate"; - $params = array( - 'aid' => $this->id, - 'csdate' => $this->course->startdate); - - return $DB->get_records_select('attendance_sessions', $where, $params); - } - - public function get_filtered_sessions() { - global $DB; - - if ($this->pageparams->startdate && $this->pageparams->enddate) { - $where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate >= :sdate AND sessdate < :edate"; - } else if ($this->pageparams->enddate) { - $where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate < :edate"; - } else { - $where = "attendanceid = :aid AND sessdate >= :csdate"; - } - - if ($this->pageparams->get_current_sesstype() > att_page_with_filter_controls::SESSTYPE_ALL) { - $where .= " AND (groupid = :cgroup OR groupid = 0)"; - } - $params = array( - 'aid' => $this->id, - 'csdate' => $this->course->startdate, - 'sdate' => $this->pageparams->startdate, - 'edate' => $this->pageparams->enddate, - 'cgroup' => $this->pageparams->get_current_sesstype()); - $sessions = $DB->get_records_select('attendance_sessions', $where, $params, 'sessdate asc'); - foreach ($sessions as $sess) { - if (empty($sess->description)) { - $sess->description = get_string('nodescription', 'attendance'); - } else { - $sess->description = file_rewrite_pluginfile_urls($sess->description, - 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); - } - } - - return $sessions; - } - - /** - * @return moodle_url of manage.php for attendance instance - */ - public function url_manage($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/manage.php', $params); - } - - /** - * @param array $params optional - * @return moodle_url of tempusers.php for attendance instance - */ - public function url_managetemp($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/tempusers.php', $params); - } - - /** - * @param array $params optional - * @return moodle_url of tempdelete.php for attendance instance - */ - public function url_tempdelete($params=array()) { - $params = array_merge(array('id' => $this->cm->id, 'action' => 'delete'), $params); - return new moodle_url('/mod/attendance/tempedit.php', $params); - } - - /** - * @param array $params optional - * @return moodle_url of tempedit.php for attendance instance - */ - public function url_tempedit($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/tempedit.php', $params); - } - - /** - * @param array $params optional - * @return moodle_url of tempedit.php for attendance instance - */ - public function url_tempmerge($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/tempmerge.php', $params); - } - - /** - * @return moodle_url of sessions.php for attendance instance - */ - public function url_sessions($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/sessions.php', $params); - } - - /** - * @return moodle_url of report.php for attendance instance - */ - public function url_report($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/report.php', $params); - } - - /** - * @return moodle_url of export.php for attendance instance - */ - public function url_export() { - $params = array('id' => $this->cm->id); - return new moodle_url('/mod/attendance/export.php', $params); - } - - /** - * @return moodle_url of attsettings.php for attendance instance - */ - public function url_preferences($params=array()) { - // Add the statusset params. - if (isset($this->pageparams->statusset) && !isset($params['statusset'])) { - $params['statusset'] = $this->pageparams->statusset; - } - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/preferences.php', $params); - } - - /** - * @return moodle_url of attendances.php for attendance instance - */ - public function url_take($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/take.php', $params); - } - - public function url_view($params=array()) { - $params = array_merge(array('id' => $this->cm->id), $params); - return new moodle_url('/mod/attendance/view.php', $params); - } - - public function add_sessions($sessions) { - global $DB; - - foreach ($sessions as $sess) { - $sess->attendanceid = $this->id; - - $sess->id = $DB->insert_record('attendance_sessions', $sess); - $description = file_save_draft_area_files($sess->descriptionitemid, - $this->context->id, 'mod_attendance', 'session', $sess->id, - array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), - $sess->description); - $DB->set_field('attendance_sessions', 'description', $description, array('id' => $sess->id)); - - $infoarray = array(); - $infoarray[] = construct_session_full_date_time($sess->sessdate, $sess->duration); - - // Trigger a session added event. - $event = \mod_attendance\event\session_added::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('info' => implode(',', $infoarray)) - )); - $event->add_record_snapshot('course_modules', $this->cm); - $sess->description = $description; - $sess->lasttaken = 0; - $sess->lasttakenby = 0; - $sess->studentscanmark = 0; - $event->add_record_snapshot('attendance_sessions', $sess); - $event->trigger(); - } - } - - public function update_session_from_form_data($formdata, $sessionid) { - global $DB; - - if (!$sess = $DB->get_record('attendance_sessions', array('id' => $sessionid) )) { - print_error('No such session in this course'); - } - - $sesstarttime = $formdata->sestime['starthour'] * HOURSECS + $formdata->sestime['startminute'] * MINSECS; - $sesendtime = $formdata->sestime['endhour'] * HOURSECS + $formdata->sestime['endminute'] * MINSECS; - - $sess->sessdate = $formdata->sessiondate + $sesstarttime; - $sess->duration = $sesendtime - $sesstarttime; - - $description = file_save_draft_area_files($formdata->sdescription['itemid'], - $this->context->id, 'mod_attendance', 'session', $sessionid, - array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), $formdata->sdescription['text']); - $sess->description = $description; - $sess->descriptionformat = $formdata->sdescription['format']; - - $sess->timemodified = time(); - $DB->update_record('attendance_sessions', $sess); - - $info = construct_session_full_date_time($sess->sessdate, $sess->duration); - $event = \mod_attendance\event\session_updated::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('info' => $info, 'sessionid' => $sessionid, 'action' => att_sessions_page_params::ACTION_UPDATE))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_sessions', $sess); - $event->trigger(); - } - - /** - * Used to record attendance submitted by the student. - * - * @global type $DB - * @global type $USER - * @param type $mformdata - * @return boolean - */ - public function take_from_student($mformdata) { - global $DB, $USER; - - $statuses = implode(',', array_keys( (array)$this->get_statuses() )); - $now = time(); - - $record = new stdClass(); - $record->studentid = $USER->id; - $record->statusid = $mformdata->status; - $record->statusset = $statuses; - $record->remarks = get_string('set_by_student', 'mod_attendance'); - $record->sessionid = $mformdata->sessid; - $record->timetaken = $now; - $record->takenby = $USER->id; - - $dbsesslog = $this->get_session_log($mformdata->sessid); - if (array_key_exists($record->studentid, $dbsesslog)) { - // Already recorded do not save. - return false; - } - - $logid = $DB->insert_record('attendance_log', $record, false); - $record->id = $logid; - - // Update the session to show that a register has been taken, or staff may overwrite records. - $session = $this->get_session_info($mformdata->sessid); - $session->lasttaken = $now; - $session->lasttakenby = $USER->id; - $DB->update_record('attendance_sessions', $session); - - // Update the users grade. - $this->update_users_grade(array($USER->id)); - - /* create url for link in log screen - * need to set grouptype to 0 to allow take attendance page to be called - * from report/log page */ - - $params = array( - 'sessionid' => $this->pageparams->sessionid, - 'grouptype' => 0); - - // Log the change. - $event = \mod_attendance\event\attendance_taken_by_student::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => $params)); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_sessions', $session); - $event->add_record_snapshot('attendance_log', $record); - $event->trigger(); - - return true; - } - - public function take_from_form_data($formdata) { - global $DB, $USER; - // TODO: WARNING - $formdata is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below. - $statuses = implode(',', array_keys( (array)$this->get_statuses() )); - $now = time(); - $sesslog = array(); - $formdata = (array)$formdata; - foreach ($formdata as $key => $value) { - if (substr($key, 0, 4) == 'user') { - $sid = substr($key, 4); - if (!(is_numeric($sid) && is_numeric($value))) { // Sanity check on $sid and $value. - print_error('nonnumericid', 'attendance'); - } - $sesslog[$sid] = new stdClass(); - $sesslog[$sid]->studentid = $sid; // We check is_numeric on this above. - $sesslog[$sid]->statusid = $value; // We check is_numeric on this above. - $sesslog[$sid]->statusset = $statuses; - $sesslog[$sid]->remarks = array_key_exists('remarks'.$sid, $formdata) ? clean_param($formdata['remarks'.$sid], PARAM_TEXT) : ''; - $sesslog[$sid]->sessionid = $this->pageparams->sessionid; - $sesslog[$sid]->timetaken = $now; - $sesslog[$sid]->takenby = $USER->id; - } - } - - $dbsesslog = $this->get_session_log($this->pageparams->sessionid); - foreach ($sesslog as $log) { - if ($log->statusid) { - if (array_key_exists($log->studentid, $dbsesslog)) { - $log->id = $dbsesslog[$log->studentid]->id; - $DB->update_record('attendance_log', $log); - } else { - $DB->insert_record('attendance_log', $log, false); - } - } - } - - $session = $this->get_session_info($this->pageparams->sessionid); - $session->lasttaken = $now; - $session->lasttakenby = $USER->id; - $DB->update_record('attendance_sessions', $session); - - if ($this->grade != 0) { - $this->update_users_grade(array_keys($sesslog)); - } - - // Create url for link in log screen. - $params = array( - 'sessionid' => $this->pageparams->sessionid, - 'grouptype' => $this->pageparams->grouptype); - $event = \mod_attendance\event\attendance_taken::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => $params)); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_sessions', $session); - $event->trigger(); - - $group = 0; - if ($this->pageparams->grouptype != self::SESSION_COMMON) { - $group = $this->pageparams->grouptype; - } else { - if ($this->pageparams->group) { - $group = $this->pageparams->group; - } - } - - $totalusers = count_enrolled_users(context_module::instance($this->cm->id), 'mod/attendance:canbelisted', $group); - $usersperpage = $this->pageparams->perpage; - - if (!empty($this->pageparams->page) && $this->pageparams->page && $totalusers && $usersperpage) { - $numberofpages = ceil($totalusers / $usersperpage); - if ($this->pageparams->page < $numberofpages) { - $params['page'] = $this->pageparams->page + 1; - redirect($this->url_take($params), get_string('moreattendance', 'attendance')); - } - } - - redirect($this->url_manage(), get_string('attendancesuccess', 'attendance')); - } - - /** - * MDL-27591 made this method obsolete. - */ - public function get_users($groupid = 0, $page = 1) { - global $DB, $CFG; - - // Fields we need from the user table. - $userfields = user_picture::fields('u', array('username' , 'idnumber' , 'institution' , 'department')); - - if (isset($this->pageparams->sort) and ($this->pageparams->sort == ATT_SORT_FIRSTNAME)) { - $orderby = "u.firstname ASC, u.lastname ASC, u.idnumber ASC, u.institution ASC, u.department ASC"; - } else { - $orderby = "u.lastname ASC, u.firstname ASC, u.idnumber ASC, u.institution ASC, u.department ASC"; - } - - if ($page) { - $usersperpage = $this->pageparams->perpage; - if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) { - $startusers = ($page - 1) * $usersperpage; - if ($groupid == 0) { - $groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id')); - } else { - $groups = $groupid; - } - $users = get_users_by_capability($this->context, 'mod/attendance:canbelisted', - $userfields.',u.id, u.firstname, u.lastname, u.email', - $orderby, $startusers, $usersperpage, $groups, - '', false, true); - } else { - $startusers = ($page - 1) * $usersperpage; - $users = get_enrolled_users($this->context, 'mod/attendance:canbelisted', $groupid, $userfields, - $orderby, $startusers, $usersperpage); - } - } else { - if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) { - if ($groupid == 0) { - $groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id')); - } else { - $groups = $groupid; - } - $users = get_users_by_capability($this->context, 'mod/attendance:canbelisted', - $userfields.',u.id, u.firstname, u.lastname, u.email', - $orderby, '', '', $groups, - '', false, true); - } else { - $users = get_enrolled_users($this->context, 'mod/attendance:canbelisted', $groupid, $userfields, $orderby); - } - } - - // Add a flag to each user indicating whether their enrolment is active. - if (!empty($users)) { - list($sql, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED, 'usid0'); - - // See CONTRIB-4868. - $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; - $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END'; - - // See CONTRIB-3549. - $sql = "SELECT ue.userid, MIN(ue.status) as status, - $mintime AS mintime, - $maxtime AS maxtime - FROM {user_enrolments} ue - JOIN {enrol} e ON e.id = ue.enrolid - WHERE ue.userid $sql - AND e.status = :estatus - AND e.courseid = :courseid - GROUP BY ue.userid"; - $params += array('zerotime' => 0, 'estatus' => ENROL_INSTANCE_ENABLED, 'courseid' => $this->course->id); - $enrolments = $DB->get_records_sql($sql, $params); - - foreach ($users as $user) { - $users[$user->id]->enrolmentstatus = $enrolments[$user->id]->status; - $users[$user->id]->enrolmentstart = $enrolments[$user->id]->mintime; - $users[$user->id]->enrolmentend = $enrolments[$user->id]->maxtime; - $users[$user->id]->type = 'standard'; // Mark as a standard (not a temporary) user. - } - } - - // Add the 'temporary' users to this list. - $tempusers = $DB->get_records('attendance_tempusers', array('courseid' => $this->course->id)); - foreach ($tempusers as $tempuser) { - $users[] = self::tempuser_to_user($tempuser); - } - - return $users; - } - - // Convert a tempuser record into a user object. - protected static function tempuser_to_user($tempuser) { - $ret = (object)array( - 'id' => $tempuser->studentid, - 'firstname' => $tempuser->fullname, - 'email' => $tempuser->email, - 'username' => '', - 'enrolmentstatus' => 0, - 'enrolmentstart' => 0, - 'enrolmentend' => 0, - 'picture' => 0, - 'type' => 'temporary', - ); - foreach (get_all_user_name_fields() as $namefield) { - if (!isset($ret->$namefield)) { - $ret->$namefield = ''; - } - } - return $ret; - } - - public function get_user($userid) { - global $DB; - - $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); - - // Look for 'temporary' users and return their details from the attendance_tempusers table. - if ($user->idnumber == 'tempghost') { - $tempuser = $DB->get_record('attendance_tempusers', array('studentid' => $userid), '*', MUST_EXIST); - return self::tempuser_to_user($tempuser); - } - - $user->type = 'standard'; - - // See CONTRIB-4868. - $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; - $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END'; - - $sql = "SELECT ue.userid, ue.status, - $mintime AS mintime, - $maxtime AS maxtime - FROM {user_enrolments} ue - JOIN {enrol} e ON e.id = ue.enrolid - WHERE ue.userid = :uid - AND e.status = :estatus - AND e.courseid = :courseid - GROUP BY ue.userid, ue.status"; - $params = array('zerotime' => 0, 'uid' => $userid, 'estatus' => ENROL_INSTANCE_ENABLED, 'courseid' => $this->course->id); - $enrolments = $DB->get_record_sql($sql, $params); - - $user->enrolmentstatus = $enrolments->status; - $user->enrolmentstart = $enrolments->mintime; - $user->enrolmentend = $enrolments->maxtime; - - return $user; - } - - public function get_statuses($onlyvisible = true, $allsets = false) { - if (!isset($this->statuses)) { - // Get the statuses for the current set only. - $statusset = 0; - if (isset($this->pageparams->statusset)) { - $statusset = $this->pageparams->statusset; - } else if (isset($this->pageparams->sessionid)) { - $sessioninfo = $this->get_session_info($this->pageparams->sessionid); - $statusset = $sessioninfo->statusset; - } - $this->statuses = att_get_statuses($this->id, $onlyvisible, $statusset); - $this->allstatuses = att_get_statuses($this->id, $onlyvisible); - } - - // Return all sets, if requested. - if ($allsets) { - return $this->allstatuses; - } - return $this->statuses; - } - - public function get_session_info($sessionid) { - global $DB; - - if (!array_key_exists($sessionid, $this->sessioninfo)) { - $this->sessioninfo[$sessionid] = $DB->get_record('attendance_sessions', array('id' => $sessionid)); - } - if (empty($this->sessioninfo[$sessionid]->description)) { - $this->sessioninfo[$sessionid]->description = get_string('nodescription', 'attendance'); - } else { - $this->sessioninfo[$sessionid]->description = file_rewrite_pluginfile_urls($this->sessioninfo[$sessionid]->description, - 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $this->sessioninfo[$sessionid]->id); - } - return $this->sessioninfo[$sessionid]; - } - - public function get_sessions_info($sessionids) { - global $DB; - - list($sql, $params) = $DB->get_in_or_equal($sessionids); - $sessions = $DB->get_records_select('attendance_sessions', "id $sql", $params, 'sessdate asc'); - - foreach ($sessions as $sess) { - if (empty($sess->description)) { - $sess->description = get_string('nodescription', 'attendance'); - } else { - $sess->description = file_rewrite_pluginfile_urls($sess->description, - 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); - } - } - - return $sessions; - } - - public function get_session_log($sessionid) { - global $DB; - - return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id'); - } - - public function get_user_stat($userid) { - $ret = array(); - $ret['completed'] = $this->get_user_taken_sessions_count($userid); - $ret['statuses'] = $this->get_user_statuses_stat($userid); - - return $ret; - } - - public function get_user_taken_sessions_count($userid) { - if (!array_key_exists($userid, $this->usertakensesscount)) { - if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) { - $this->usertakensesscount[$userid] = att_get_user_taken_sessions_count($this->id, $this->course->startdate, - $userid, $this->cm, $this->pageparams->startdate, $this->pageparams->enddate); - } else { - $this->usertakensesscount[$userid] = att_get_user_taken_sessions_count($this->id, $this->course->startdate, - $userid, $this->cm); - } - } - return $this->usertakensesscount[$userid]; - } - - /** - * - * @global type $DB - * @param type $userid - * @param type $filters - An array things to filter by. For now only enddate is valid. - * @return type - */ - public function get_user_statuses_stat($userid, array $filters = null) { - global $DB; - $params = array( - 'aid' => $this->id, - 'cstartdate' => $this->course->startdate, - 'uid' => $userid); - - $processedfilters = array(); - - // We test for any valid filters sent. - if (isset($filters['enddate'])) { - $processedfilters[] = 'ats.sessdate <= :enddate'; - $params['enddate'] = $filters['enddate']; - } - - // Make the filter array into a SQL string. - if (!empty($processedfilters)) { - $processedfilters = ' AND '.implode(' AND ', $processedfilters); - } else { - $processedfilters = ''; - } - - $period = ''; - if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) { - $period = ' AND ats.sessdate >= :sdate AND ats.sessdate < :edate '; - $params['sdate'] = $this->pageparams->startdate; - $params['edate'] = $this->pageparams->enddate; - } - - if ($this->get_group_mode()) { - $qry = "SELECT al.statusid, count(al.statusid) AS stcnt - FROM {attendance_log} al - JOIN {attendance_sessions} ats ON al.sessionid = ats.id - LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid - WHERE ats.attendanceid = :aid AND - ats.sessdate >= :cstartdate AND - al.studentid = :uid AND - (ats.groupid = 0 or gm.id is NOT NULL)".$period.$processedfilters." - GROUP BY al.statusid"; - } else { - $qry = "SELECT al.statusid, count(al.statusid) AS stcnt - FROM {attendance_log} al - JOIN {attendance_sessions} ats - ON al.sessionid = ats.id - WHERE ats.attendanceid = :aid AND - ats.sessdate >= :cstartdate AND - al.studentid = :uid".$period.$processedfilters." - GROUP BY al.statusid"; - } - - // We do not want to cache, or use a cached version of the results when a filter is set. - if ($filters !== null) { - return $DB->get_records_sql($qry, $params); - } else if (!array_key_exists($userid, $this->userstatusesstat)) { - // Not filtered so if we do not already have them do the query. - $this->userstatusesstat[$userid] = $DB->get_records_sql($qry, $params); - } - - // Return the cached stats. - return $this->userstatusesstat[$userid]; - } - - /** - * - * @param type $userid - * @param type $filters - An array things to filter by. For now only enddate is valid. - * @return type - */ - public function get_user_grade($userid, array $filters = null) { - return att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses(true, true)); - } - - // For getting sessions count implemented simplest method - taken sessions. - // It can have error if users don't have attendance info for some sessions. - // In the future we can implement another methods: - // * all sessions between user start enrolment date and now; - // * all sessions between user start and end enrolment date. - // While implementing those methods we need recalculate grades of all users - // on session adding. - public function get_user_max_grade($userid) { - return att_get_user_max_grade($this->get_user_taken_sessions_count($userid), $this->get_statuses(true, true)); - } - - public function update_users_grade($userids) { - $grades = array(); - - foreach ($userids as $userid) { - $grades[$userid] = new stdClass(); - $grades[$userid]->userid = $userid; - $grades[$userid]->rawgrade = attendance_calc_user_grade_fraction($this->get_user_grade($userid), - $this->get_user_max_grade($userid)) * $this->grade; - } - - return grade_update('mod/attendance', $this->course->id, 'mod', 'attendance', - $this->id, 0, $grades); - } - - public function get_user_filtered_sessions_log($userid) { - global $DB; - - if ($this->pageparams->startdate && $this->pageparams->enddate) { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND - ats.sessdate >= :sdate AND ats.sessdate < :edate"; - } else { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; - } - if ($this->get_group_mode()) { - $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks - FROM {attendance_sessions} ats - JOIN {attendance_log} al ON ats.id = al.sessionid AND al.studentid = :uid - LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid - WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL) - ORDER BY ats.sessdate ASC"; - - $params = array( - 'uid' => $userid, - 'aid' => $this->id, - 'csdate' => $this->course->startdate, - 'sdate' => $this->pageparams->startdate, - 'edate' => $this->pageparams->enddate); - - } else { - $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks - FROM {attendance_sessions} ats - JOIN {attendance_log} al - ON ats.id = al.sessionid AND al.studentid = :uid - WHERE $where - ORDER BY ats.sessdate ASC"; - - $params = array( - 'uid' => $userid, - 'aid' => $this->id, - 'csdate' => $this->course->startdate, - 'sdate' => $this->pageparams->startdate, - 'edate' => $this->pageparams->enddate); - } - $sessions = $DB->get_records_sql($sql, $params); - - return $sessions; - } - - public function get_user_filtered_sessions_log_extended($userid) { - global $DB; - // All taked sessions (including previous groups). - - if ($this->pageparams->startdate && $this->pageparams->enddate) { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND - ats.sessdate >= :sdate AND ats.sessdate < :edate"; - } else { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; - } - - // We need to add this concatination so that moodle will use it as the array index that is a string. - // If the array's index is a number it will not merge entries. - // It would be better as a UNION query but unfortunatly MS SQL does not seem to support doing a - // DISTINCT on a the description field. - $id = $DB->sql_concat(':value', 'ats.id'); - if ($this->get_group_mode()) { - $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, - al.statusid, al.remarks, ats.studentscanmark - FROM {attendance_sessions} ats - RIGHT JOIN {attendance_log} al - ON ats.id = al.sessionid AND al.studentid = :uid - LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid - WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL) - ORDER BY ats.sessdate ASC"; - } else { - $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, - al.statusid, al.remarks, ats.studentscanmark - FROM {attendance_sessions} ats - RIGHT JOIN {attendance_log} al - ON ats.id = al.sessionid AND al.studentid = :uid - WHERE $where - ORDER BY ats.sessdate ASC"; - } - - $params = array( - 'uid' => $userid, - 'aid' => $this->id, - 'csdate' => $this->course->startdate, - 'sdate' => $this->pageparams->startdate, - 'edate' => $this->pageparams->enddate, - 'value' => 'c'); - $sessions = $DB->get_records_sql($sql, $params); - - // All sessions for current groups. - - $groups = array_keys(groups_get_all_groups($this->course->id, $userid)); - $groups[] = 0; - list($gsql, $gparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'gid0'); - - if ($this->pageparams->startdate && $this->pageparams->enddate) { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND - ats.sessdate >= :sdate AND ats.sessdate < :edate AND ats.groupid $gsql"; - } else { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql"; - } - - $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, - al.statusid, al.remarks, ats.studentscanmark - FROM {attendance_sessions} ats - LEFT JOIN {attendance_log} al - ON ats.id = al.sessionid AND al.studentid = :uid - WHERE $where - ORDER BY ats.sessdate ASC"; - - $params = array_merge($params, $gparams); - $sessions = array_merge($sessions, $DB->get_records_sql($sql, $params)); - - foreach ($sessions as $sess) { - if (empty($sess->description)) { - $sess->description = get_string('nodescription', 'attendance'); - } else { - $sess->description = file_rewrite_pluginfile_urls($sess->description, - 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); - } - } - - return $sessions; - } - - public function delete_sessions($sessionsids) { - global $DB; - - list($sql, $params) = $DB->get_in_or_equal($sessionsids); - $DB->delete_records_select('attendance_log', "sessionid $sql", $params); - $DB->delete_records_list('attendance_sessions', 'id', $sessionsids); - $event = \mod_attendance\event\session_deleted::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('info' => implode(', ', $sessionsids)))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->trigger(); - } - - public function update_sessions_duration($sessionsids, $duration) { - global $DB; - - $now = time(); - $sessions = $DB->get_recordset_list('attendance_sessions', 'id', $sessionsids); - foreach ($sessions as $sess) { - $sess->duration = $duration; - $sess->timemodified = $now; - $DB->update_record('attendance_sessions', $sess); - $event = \mod_attendance\event\session_duration_updated::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('info' => implode(', ', $sessionsids)))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_sessions', $sess); - $event->trigger(); - } - $sessions->close(); - } - - /** - * Remove a status variable from an attendance instance - * - * @global moodle_database $DB - * @param stdClass $status - */ - public function remove_status($status) { - global $DB; - - $DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id)); - $event = \mod_attendance\event\status_removed::create(array( - 'objectid' => $status->id, - 'context' => $this->context, - 'other' => array( - 'acronym' => $status->acronym, - 'description' => $status->description - ))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_statuses', $status); - $event->trigger(); - } - - /** - * Add an attendance status variable - * - * @global moodle_database $DB - * @param string $acronym - * @param string $description - * @param int $grade - */ - public function add_status($acronym, $description, $grade) { - global $DB; - - if ($acronym && $description) { - $rec = new stdClass(); - $rec->courseid = $this->course->id; - $rec->attendanceid = $this->id; - $rec->acronym = $acronym; - $rec->description = $description; - $rec->grade = $grade; - $rec->setnumber = $this->pageparams->statusset; // Save which set it is part of. - $rec->deleted = 0; - $rec->visible = 1; - $id = $DB->insert_record('attendance_statuses', $rec); - $rec->id = $id; - - $event = \mod_attendance\event\status_added::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_statuses', $rec); - $event->trigger(); - } else { - print_error('cantaddstatus', 'attendance', $this->url_preferences()); - } - } - - /** - * Update status variable for a particular Attendance module instance - * - * @global moodle_database $DB - * @param stdClass $status - * @param string $acronym - * @param string $description - * @param int $grade - * @param bool $visible - */ - public function update_status($status, $acronym, $description, $grade, $visible) { - global $DB; - - if (isset($visible)) { - $status->visible = $visible; - $updated[] = $visible ? get_string('show') : get_string('hide'); - } else if (empty($acronym) || empty($description)) { - return array('acronym' => $acronym, 'description' => $description); - } - - $updated = array(); - - if ($acronym) { - $status->acronym = $acronym; - $updated[] = $acronym; - } - if ($description) { - $status->description = $description; - $updated[] = $description; - } - if (isset($grade)) { - $status->grade = $grade; - $updated[] = $grade; - } - $DB->update_record('attendance_statuses', $status); - - $event = \mod_attendance\event\status_updated::create(array( - 'objectid' => $this->id, - 'context' => $this->context, - 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade, - 'updated' => implode(' ', $updated)))); - $event->add_record_snapshot('course_modules', $this->cm); - $event->add_record_snapshot('attendance_statuses', $status); - $event->trigger(); - } - - /** - * Check if the email address is already in use by either another temporary user, - * or a real user. - * - * @param string $email the address to check for - * @param int $tempuserid optional the ID of the temporary user (to avoid matching against themself) - * @return null|string the error message to display, null if there is no error - */ - public static function check_existing_email($email, $tempuserid = 0) { - global $DB; - - if (empty($email)) { - return null; // Fine to create temporary users without an email address. - } - if ($tempuser = $DB->get_record('attendance_tempusers', array('email' => $email), 'id')) { - if ($tempuser->id != $tempuserid) { - return get_string('tempexists', 'attendance'); - } - } - if ($DB->record_exists('user', array('email' => $email))) { - return get_string('userexists', 'attendance'); - } - - return null; - } -} function att_get_statuses($attid, $onlyvisible=true, $statusset = -1) { global $DB; @@ -1709,25 +576,21 @@ function attendance_calc_user_grade_fraction($grade, $maxgrade) { /** * Update all user grades - used when settings have changed. * - * @param $attid - * @param $course - * @param $context + * @param $attendance * @param $coursemodule */ -function attendance_update_all_users_grades($attid, $course, $context, $coursemodule) { - global $DB; +function attendance_update_all_users_grades(mod_attendance_structure $attendance, $coursemodule) { $grades = array(); + $course = $attendance->course; - $userids = array_keys(get_enrolled_users($context, 'mod/attendance:canbelisted', 0, 'u.id')); - $attgrades = grade_get_grades($course->id, 'mod', 'attendance', $attid, $userids); + $userids = array_keys(get_enrolled_users($attendance->context, 'mod/attendance:canbelisted', 0, 'u.id')); + $attgrades = grade_get_grades($course->id, 'mod', 'attendance', $attendance->id, $userids); $usergrades = []; if (!empty($attgrades->items[0]) and !empty($attgrades->items[0]->grades)) { $usergrades = $attgrades->items[0]->grades; } - $statuses = att_get_statuses($attid); - // TODO: we should be able to pass full $attendance record into this function and avoid this db call. - $gradebookmaxgrade = $DB->get_field('attendance', 'grade', array('id' => $attid)); + $statuses = att_get_statuses($attendance->id); foreach ($usergrades as $userid => $existinggrade) { if (is_null($existinggrade->grade)) { // Don't update grades where one doesn't exist yet. @@ -1735,17 +598,17 @@ function attendance_update_all_users_grades($attid, $course, $context, $coursemo } $grade = new stdClass; $grade->userid = $userid; - $userstatusesstat = att_get_user_statuses_stat($attid, $course->startdate, $userid, $coursemodule); - $usertakensesscount = att_get_user_taken_sessions_count($attid, $course->startdate, $userid, $coursemodule); + $userstatusesstat = att_get_user_statuses_stat($attendance->id, $course->startdate, $userid, $coursemodule); + $usertakensesscount = att_get_user_taken_sessions_count($attendance->id, $course->startdate, $userid, $coursemodule); $usergrade = att_get_user_grade($userstatusesstat, $statuses); $usermaxgrade = att_get_user_max_grade($usertakensesscount, $statuses); - $grade->rawgrade = attendance_calc_user_grade_fraction($usergrade, $usermaxgrade) * $gradebookmaxgrade; + $grade->rawgrade = attendance_calc_user_grade_fraction($usergrade, $usermaxgrade) * $attendance->grade; $grades[$userid] = $grade; } if (!empty($grades)) { $result = grade_update('mod/attendance', $course->id, 'mod', 'attendance', - $attid, 0, $grades); + $attendance->id, 0, $grades); } else { $result = true; } diff --git a/manage.php b/manage.php index 78aa119..384ae34 100644 --- a/manage.php +++ b/manage.php @@ -50,7 +50,7 @@ if (!has_any_capability($capabilities, $context)) { } $pageparams->init($cm); -$att = new attendance($att, $cm, $course, $context, $pageparams); +$att = new mod_attendance_structure($att, $cm, $course, $context, $pageparams); // If teacher is coming from block, then check for a session exists for today. if ($from === 'block') { diff --git a/preferences.php b/preferences.php index 03bbd3c..2f92eca 100644 --- a/preferences.php +++ b/preferences.php @@ -47,7 +47,7 @@ if ($pageparams->statusset > $maxstatusset + 1) { $pageparams->statusset = $maxstatusset + 1; } -$att = new attendance($att, $cm, $course, $context, $pageparams); +$att = new mod_attendance_structure($att, $cm, $course, $context, $pageparams); $PAGE->set_url($att->url_preferences()); $PAGE->set_title($course->shortname. ": ".$att->name.' - '.get_string('settings', 'attendance')); @@ -123,7 +123,7 @@ switch ($att->pageparams->action) { $errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null); } if ($att->grade > 0) { - attendance_update_all_users_grades($att->id, $att->course, $att->context, $cm); + attendance_update_all_users_grades($att, $cm); } break; } diff --git a/renderables.php b/renderables.php index 925393d..e36a979 100644 --- a/renderables.php +++ b/renderables.php @@ -56,7 +56,7 @@ class attendance_tabs implements renderable { * @param attendance $att instance * @param $currenttab - one of attendance_tabs constants */ - public function __construct(attendance $att, $currenttab=null) { + public function __construct(mod_attendance_structure $att, $currenttab=null) { $this->att = $att; $this->currenttab = $currenttab; } @@ -130,7 +130,7 @@ class attendance_filter_controls implements renderable { private $att; - public function __construct(attendance $att, $report = false) { + public function __construct(mod_attendance_structure $att, $report = false) { global $PAGE; $this->pageparams = $att->pageparams; @@ -230,7 +230,7 @@ class attendance_manage_data implements renderable { * * @param attendance $att instance */ - public function __construct(attendance $att) { + public function __construct(mod_attendance_structure $att) { $this->sessions = $att->get_filtered_sessions(); @@ -275,7 +275,7 @@ class attendance_take_data implements renderable { private $urlparams; private $att; - public function __construct(attendance $att) { + public function __construct(mod_attendance_structure $att) { if ($att->pageparams->grouptype) { $this->users = $att->get_users($att->pageparams->grouptype, $att->pageparams->page); } else { @@ -359,7 +359,7 @@ class attendance_user_data implements renderable { private $urlpath; private $urlparams; - public function __construct(attendance $att, $userid) { + public function __construct(mod_attendance_structure $att, $userid) { global $CFG; $this->user = $att->get_user($userid); @@ -465,7 +465,7 @@ class attendance_report_data implements renderable { public $att; - public function __construct(attendance $att) { + public function __construct(mod_attendance_structure $att) { global $CFG; $currenttime = time(); @@ -549,7 +549,7 @@ class attendance_preferences_data implements renderable { public $errors; - public function __construct(attendance $att, $errors) { + public function __construct(mod_attendance_structure $att, $errors) { $this->statuses = $att->get_statuses(false); $this->errors = $errors; @@ -575,7 +575,7 @@ class attendance_set_selector implements renderable { private $att; - public function __construct(attendance $att, $maxstatusset) { + public function __construct(mod_attendance_structure $att, $maxstatusset) { $this->att = $att; $this->maxstatusset = $maxstatusset; } diff --git a/renderer.php b/renderer.php index 2941170..0dba06a 100644 --- a/renderer.php +++ b/renderer.php @@ -395,7 +395,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $controls = ''; $context = context_module::instance($takedata->cm->id); $group = 0; - if ($takedata->pageparams->grouptype != attendance::SESSION_COMMON) { + if ($takedata->pageparams->grouptype != mod_attendance_structure::SESSION_COMMON) { $group = $takedata->pageparams->grouptype; } else { if ($takedata->pageparams->group) { @@ -434,7 +434,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } } - if ($takedata->pageparams->grouptype == attendance::SESSION_COMMON and + if ($takedata->pageparams->grouptype == mod_attendance_structure::SESSION_COMMON and ($takedata->groupmode == VISIBLEGROUPS or ($takedata->groupmode and has_capability('moodle/site:accessallgroups', $context)))) { $controls .= groups_print_activity_menu($takedata->cm, $takedata->url(), true); diff --git a/report.php b/report.php index 81510b2..8142e4e 100644 --- a/report.php +++ b/report.php @@ -46,7 +46,7 @@ $context = context_module::instance($cm->id); require_capability('mod/attendance:viewreports', $context); $pageparams->init($cm); -$att = new attendance($attrecord, $cm, $course, $context, $pageparams); +$att = new mod_attendance_structure($attrecord, $cm, $course, $context, $pageparams); $PAGE->set_url($att->url_report()); $PAGE->set_pagelayout('report'); diff --git a/sessions.php b/sessions.php index e42768f..8f1ed99 100644 --- a/sessions.php +++ b/sessions.php @@ -53,7 +53,7 @@ require_login($course, true, $cm); $context = context_module::instance($cm->id); require_capability('mod/attendance:manageattendances', $context); -$att = new attendance($att, $cm, $course, $context, $pageparams); +$att = new mod_attendance_structure($att, $cm, $course, $context, $pageparams); $PAGE->set_url($att->url_sessions(array('action' => $pageparams->action))); $PAGE->set_title($course->shortname. ": ".$att->name); @@ -114,7 +114,7 @@ switch ($att->pageparams->action) { if (isset($confirm) && confirm_sesskey()) { $att->delete_sessions(array($sessionid)); if ($att->grade > 0) { - attendance_update_all_users_grades($att->id, $att->course, $att->context, $cm); + attendance_update_all_users_grades($att, $cm); } redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); } @@ -143,7 +143,7 @@ switch ($att->pageparams->action) { $att->delete_sessions($sessionsids); if ($att->grade > 0) { - attendance_update_all_users_grades($att->id, $att->course, $att->context, $cm); + attendance_update_all_users_grades($att, $cm); } redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); } @@ -302,7 +302,7 @@ function construct_sessions_data_for_add($formdata) { } function fill_groupid($formdata, &$sessions, $sess) { - if ($formdata->sessiontype == attendance::SESSION_COMMON) { + if ($formdata->sessiontype == mod_attendance_structure::SESSION_COMMON) { $sess = clone $sess; $sess->groupid = 0; $sessions[] = $sess; diff --git a/take.php b/take.php index 6634995..9852e65 100644 --- a/take.php +++ b/take.php @@ -48,7 +48,7 @@ require_capability('mod/attendance:takeattendances', $context); $pageparams->group = groups_get_activity_group($cm, true); $pageparams->init($course->id); -$att = new attendance($att, $cm, $course, $PAGE->context, $pageparams); +$att = new mod_attendance_structure($att, $cm, $course, $PAGE->context, $pageparams); $allowedgroups = groups_get_activity_allowed_groups($cm); if (!empty($pageparams->grouptype) && !array_key_exists($pageparams->grouptype, $allowedgroups)) { diff --git a/temp_form.php b/temp_form.php index fe7bd32..1572647 100644 --- a/temp_form.php +++ b/temp_form.php @@ -57,7 +57,7 @@ class temp_form extends moodleform { public function validation($data, $files) { $errors = parent::validation($data, $files); - if ($err = attendance::check_existing_email($data['temail'])) { + if ($err = mod_attendance_structure::check_existing_email($data['temail'])) { $errors['temail'] = $err; } diff --git a/tempedit.php b/tempedit.php index 2a66026..6f93301 100644 --- a/tempedit.php +++ b/tempedit.php @@ -37,7 +37,7 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST) $att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); $tempuser = $DB->get_record('attendance_tempusers', array('id' => $userid), '*', MUST_EXIST); -$att = new attendance($att, $cm, $course); +$att = new mod_attendance_structure($att, $cm, $course); $params = array('userid' => $tempuser->id); if ($action) { diff --git a/tempedit_form.php b/tempedit_form.php index 5b00433..0376eb1 100644 --- a/tempedit_form.php +++ b/tempedit_form.php @@ -63,7 +63,7 @@ class tempedit_form extends moodleform { public function validation($data, $files) { $errors = parent::validation($data, $files); - if ($err = attendance::check_existing_email($data['temail'], $data['userid'])) { + if ($err = mod_attendance_structure::check_existing_email($data['temail'], $data['userid'])) { $errors['temail'] = $err; } return $errors; diff --git a/tempmerge.php b/tempmerge.php index 1afe21f..9bf3f44 100644 --- a/tempmerge.php +++ b/tempmerge.php @@ -36,7 +36,7 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST) $att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); $tempuser = $DB->get_record('attendance_tempusers', array('id' => $userid), '*', MUST_EXIST); -$att = new attendance($att, $cm, $course); +$att = new mod_attendance_structure($att, $cm, $course); $params = array('userid' => $tempuser->id); $PAGE->set_url($att->url_tempmerge($params)); diff --git a/tempusers.php b/tempusers.php index 62bf19b..b15f50a 100644 --- a/tempusers.php +++ b/tempusers.php @@ -33,7 +33,7 @@ $cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); $att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); -$att = new attendance($att, $cm, $course); +$att = new mod_attendance_structure($att, $cm, $course); $PAGE->set_url($att->url_managetemp()); require_login($course, true, $cm); @@ -94,7 +94,7 @@ if ($tempusers) { echo ''; echo $output->footer($course); -function print_tempusers($tempusers, attendance $att) { +function print_tempusers($tempusers, mod_attendance_structure $att) { echo '
'; echo '