diff --git a/attendance.php b/attendance.php
index 4aeb0c4..c7b15df 100644
--- a/attendance.php
+++ b/attendance.php
@@ -40,9 +40,9 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST)
// Require the user is logged in.
require_login($course, true, $cm);
-if (!attendance_can_student_mark($attforsession)) {
- // TODO: should we add a log message here? - student has got to submit page but cannot save attendance (time ran out?)
- redirect(new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)));
+list($canmark, $reason) = attendance_can_student_mark($attforsession);
+if (!$canmark) {
+ redirect(new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)), get_string($reason, 'attendance'));
exit;
}
diff --git a/classes/output/mobile.php b/classes/output/mobile.php
index 82a08d0..b3b0ed9 100644
--- a/classes/output/mobile.php
+++ b/classes/output/mobile.php
@@ -39,7 +39,7 @@ class mobile {
* @return array HTML, javascript and other data
*/
public static function mobile_view_activity($args) {
- global $OUTPUT, $DB, $USER, $PAGE, $CFG;
+ global $OUTPUT, $DB, $USER, $USER, $CFG;
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
@@ -59,6 +59,19 @@ class mobile {
$attendance = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+ $data = array(); // Data to pass to renderer.
+ $data['cmid'] = $cmid;
+ $data['courseid'] = $courseid;
+ $data['attendance'] = $attendance;
+
+ $data['attendancefunction'] = 'mobile_take_attendance';
+ $isteacher = false;
+ if (has_capability('mod/attendance:takeattendances', $context)) {
+ $isteacher = true;
+ $data['attendancefunction'] = 'mobile_take_attendance_all';
+ }
+
+ // Add stats for this use to output.
$pageparams = new \mod_attendance_view_page_params();
$pageparams->studentid = $USER->id;
@@ -69,8 +82,45 @@ class mobile {
$summary = new \mod_attendance_summary($att->id, array($USER->id), $att->pageparams->startdate,
$att->pageparams->enddate);
- $data = array('attendance' => $attendance,
- 'summary' => $summary->get_all_sessions_summary_for($USER->id));
+ $data['summary'] = $summary->get_all_sessions_summary_for($USER->id);
+
+ // Get list of sessions within the next 24hrs and in last 6hrs.
+ // TODO: provide way of adjusting which sessions to show in app.
+ $time = time() - (6 * 60 * 60);
+
+ $data['sessions'] = array();
+
+ $userdata = new \attendance_user_data($att, $USER->id, true);
+
+ $sessions = $DB->get_records_select('attendance_sessions',
+ 'attendanceid = ? AND sessdate > ? ORDER BY sessdate', array($attendance->id, $time));
+
+ if (!empty($sessions)) {
+ foreach ($sessions as $sess) {
+ if (!$isteacher && empty($userdata->sessionslog['c'.$sess->id])) {
+ // This session isn't viewable to this student - probably a group session.
+ continue;
+ }
+ list($canmark, $unused) = attendance_can_student_mark($sess, false);
+ if ($isteacher || $canmark) {
+
+ $html = array('time' => attendance_construct_session_time($sess->sessdate, $sess->duration));
+ if (!empty($sess->groupid)) {
+ // TODO In-efficient way to get group name - we should get all groups in one query.
+ $groupname = $DB->get_field('groups', 'name', array('id' => $sess->groupid));
+ $html['time'] .= ' ('.$groupname.')';
+ }
+
+ if (!$isteacher && !empty($userdata->sessionslog['c'.$sess->id]->statusid)) {
+ $html['currentstatus'] = $userdata->statuses[$userdata->sessionslog['c'.$sess->id]->statusid]->description;
+ } else {
+ $html['sessid'] = $sess->id;
+ }
+
+ $data['sessions'][] = $html;
+ }
+ }
+ }
return [
'templates' => [
@@ -83,4 +133,129 @@ class mobile {
'otherdata' => ''
];
}
+ /**
+ * Returns the form to take attendance for the mobile app.
+ *
+ * @param array $args Arguments from tool_mobile_get_content WS
+ * @return array HTML, javascript and other data
+ */
+ public static function mobile_take_attendance($args) {
+ global $OUTPUT, $DB, $CFG;
+
+ require_once($CFG->dirroot.'/mod/attendance/locallib.php');
+
+ $args = (object) $args;
+ $cmid = $args->cmid;
+ $courseid = $args->courseid;
+ $sessid = $args->sessid;
+ $takenstatus = empty($args->status) ? '' : $args->status;
+
+ // Capabilities check.
+ $cm = get_coursemodule_from_id('attendance', $cmid);
+
+ require_login($courseid, false , $cm, true, true);
+
+ $context = \context_module::instance($cm->id);
+ require_capability('mod/attendance:view', $context);
+
+ $attendance = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST);
+ $attforsession = $DB->get_record('attendance_sessions', array('id' => $sessid), '*', MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ $pageparams = new \mod_attendance_sessions_page_params();
+ $pageparams->sessionid = $sessid;
+ $att = new \mod_attendance_structure($attendance, $cm, $course, $context, $pageparams);
+
+ $data = array(); // Data to pass to renderer.
+ $data['attendance'] = $attendance;
+ $data['cmid'] = $cmid;
+ $data['courseid'] = $courseid;
+ $data['sessid'] = $sessid;
+ $data['messages'] = array();
+ $data['showmessage'] = false;
+ $data['showstatuses'] = true;
+ $data['statuses'] = array();
+ $data['disabledduetotime'] = false;
+
+ list($canmark, $reason) = attendance_can_student_mark($attforsession, false);
+ // Check if subnet is set and if the user is in the allowed range.
+ if (!$canmark) {
+ $data['messages'][]['string'] = $reason; // Lang string to show as a message.
+ $data['showstatuses'] = false; // Hide all statuses.
+ } else if (!empty($attforsession->subnet) && !address_in_subnet(getremoteaddr(), $attforsession->subnet)) {
+ $data['messages'][]['string'] = 'subnetwrong'; // Lang string to show as a message.
+ $data['showstatuses'] = false; // Hide all statuses.
+ } else if ($attforsession->autoassignstatus && empty($attforsession->studentpassword)) {
+ $statusid = attendance_session_get_highest_status($att, $attforsession);
+ if (empty($statusid)) {
+ $data['messages'][]['string'] = 'attendance_no_status';
+ }
+ $take = new \stdClass();
+ $take->status = $statusid;
+ $take->sessid = $attforsession->id;
+ $success = $att->take_from_student($take);
+
+ if ($success) {
+ $data['messages'][]['string'] = 'attendancesuccess'; // Lang string to show as a message.
+ } else {
+ $data['messages'][]['string'] = 'attendance_already_submitted'; // Lang string to show as a message.
+ $data['showstatuses'] = false; // Hide all statuses.
+ }
+ } else {
+ // Show user form for submitting a status.
+ $statuses = $att->get_statuses();
+ // Check if user has access to all statuses.
+ foreach ($statuses as $status) {
+ if ($status->studentavailability === '0') {
+ unset($statuses[$status->id]);
+ continue;
+ }
+ if (!empty($status->studentavailability) &&
+ time() > $attforsession->sessdate + ($status->studentavailability * 60)) {
+ unset($statuses[$status->id]);
+ continue;
+ $data['disabledduetotime'] = true;
+ }
+ $data['statuses'][] = array('stid' => $status->id, 'description' => $status->description);
+ }
+ if (empty($data['statuses'])) {
+ $data['messages'][]['string'] = 'attendance_no_status';
+ $data['showstatuses'] = false; // Hide all statuses.
+ } else if (!empty($takenstatus)) {
+ // Check if user has submitted a status.
+ if (empty($statuses[$takenstatus])) {
+ // This status has probably expired and is not available - they need to choose a new one.
+ $data['messages'][]['string'] = 'invalidstatus';
+ } else {
+
+ $take = new \stdClass();
+ $take->status = $takenstatus;
+ $take->sessid = $attforsession->id;
+ $success = $att->take_from_student($take);
+
+ if ($success) {
+ $data['messages'][]['string'] = 'attendancesuccess'; // Lang string to show as a message.
+ $data['showstatuses'] = false; // Hide all statuses.
+ } else {
+ $data['messages'][]['string'] = 'attendance_already_submitted'; // Lang string to show as a message.
+ $data['showstatuses'] = false; // Hide all statuses.
+ }
+ }
+ }
+ }
+ if (!empty($data['messages'])) {
+ $data['showmessage'] = true;
+ }
+
+ return [
+ 'templates' => [
+ [
+ 'id' => 'main',
+ 'html' => $OUTPUT->render_from_template('mod_attendance/mobile_take_attendance', $data),
+ ],
+ ],
+ 'javascript' => '',
+ 'otherdata' => ''
+ ];
+ }
}
\ No newline at end of file
diff --git a/db/mobile.php b/db/mobile.php
index 290fb34..22c6815 100644
--- a/db/mobile.php
+++ b/db/mobile.php
@@ -46,6 +46,17 @@ $addons = [
['percentageallsessions', 'attendance'],
['maxpossiblepoints', 'attendance'],
['maxpossiblepercentage', 'attendance'],
+ ['submitattendance', 'attendance'],
+ ['strftimeh', 'attendance'],
+ ['strftimehm', 'attendance'],
+ ['attendancesuccess', 'attendance'],
+ ['attendance_no_status', 'attendance'],
+ ['attendance_already_submitted', 'attendance'],
+ ['somedisabledstatus', 'attendance'],
+ ['invalidstatus', 'attendance'],
+ ['preventsharederror', 'attendance'],
+ ['closed', 'attendance'],
+ ['subnetwrong', 'attendance']
]
]
];
\ No newline at end of file
diff --git a/lang/en/attendance.php b/lang/en/attendance.php
index d9a2522..594e332 100644
--- a/lang/en/attendance.php
+++ b/lang/en/attendance.php
@@ -53,7 +53,7 @@ $string['attendance:view'] = 'Viewing Attendances';
$string['attendance:viewreports'] = 'Viewing Reports';
$string['attendance:viewsummaryreports'] = 'View course summary reports';
$string['attendance:warningemails'] = 'Can be subscribed to emails with absentee users';
-$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';
+$string['attendance_already_submitted'] = 'Your attendance has already been set.';
$string['attendancedata'] = 'Attendance data';
$string['attendanceforthecourse'] = 'Attendance for the course';
$string['attendancegrade'] = 'Attendance grade';
@@ -94,6 +94,7 @@ $string['changeattendance'] = 'Change attendance';
$string['changeduration'] = 'Change duration';
$string['changesession'] = 'Change session';
$string['checkweekdays'] = 'Select weekdays that fall within your selected session date range.';
+$string['closed'] = 'This session is not currently available for self-marking';
$string['column'] = 'column';
$string['columns'] = 'columns';
$string['commonsession'] = 'All students';
diff --git a/locallib.php b/locallib.php
index cc2a2a2..7ac157d 100644
--- a/locallib.php
+++ b/locallib.php
@@ -426,15 +426,18 @@ function attendance_random_string($length=6) {
* Check to see if this session is open for student marking.
*
* @param stdclass $sess the session record from attendance_sessions.
- * @return boolean
+ * @param boolean $log - if student cannot mark, generate log event.
+ * @return array (boolean, string reason for failure)
*/
-function attendance_can_student_mark($sess) {
+function attendance_can_student_mark($sess, $log = true) {
global $DB, $USER, $OUTPUT;
$canmark = false;
+ $reason = 'closed';
$attconfig = get_config('attendance');
if (!empty($attconfig->studentscanmark) && !empty($sess->studentscanmark)) {
if (empty($attconfig->studentscanmarksessiontime)) {
$canmark = true;
+ $reason = '';
} else {
$duration = $sess->duration;
if (empty($duration)) {
@@ -442,6 +445,7 @@ function attendance_can_student_mark($sess) {
}
if ($sess->sessdate < time() && time() < ($sess->sessdate + $duration)) {
$canmark = true;
+ $reason = '';
}
}
}
@@ -460,25 +464,26 @@ function attendance_can_student_mark($sess) {
}
if (!empty($record)) {
- // Trigger an ip_shared event.
- $attendanceid = $DB->get_field('attendance_sessions', 'attendanceid', array('id' => $record->sessionid));
- $cm = get_coursemodule_from_instance('attendance', $attendanceid);
- $event = \mod_attendance\event\session_ip_shared::create(array(
- 'objectid' => 0,
- 'context' => \context_module::instance($cm->id),
- 'other' => array(
- 'sessionid' => $record->sessionid,
- 'otheruser' => $record->studentid
- )
- ));
-
- $event->trigger();
-
- echo $OUTPUT->notification(get_string('preventsharederror', 'attendance'));
- return false;
+ $canmark = false;
+ $reason = 'preventsharederror';
+ if ($log) {
+ // Trigger an ip_shared event.
+ $attendanceid = $DB->get_field('attendance_sessions', 'attendanceid', array('id' => $record->sessionid));
+ $cm = get_coursemodule_from_instance('attendance', $attendanceid);
+ $event = \mod_attendance\event\session_ip_shared::create(array(
+ 'objectid' => 0,
+ 'context' => \context_module::instance($cm->id),
+ 'other' => array(
+ 'sessionid' => $record->sessionid,
+ 'otheruser' => $record->studentid
+ )
+ ));
+
+ $event->trigger();
+ }
}
}
- return $canmark;
+ return array($canmark, $reason);
}
/**
@@ -973,3 +978,70 @@ function attendance_get_sharedipoptions() {
return $options;
}
+
+/**
+ * Used to print simple time - 1am instead of 1:00am.
+ *
+ * @param int $time - unix timestamp.
+ */
+function attendance_strftimehm($time) {
+ $mins = userdate($time, '%M');
+
+ if ($mins == '00') {
+ $format = get_string('strftimeh', 'attendance');
+ } else {
+ $format = get_string('strftimehm', 'attendance');
+ }
+
+ $userdate = userdate($time, $format);
+
+ // Some Lang packs use %p to suffix with AM/PM but not all strftime support this.
+ // Check if %p is in use and make sure it's being respected.
+ if (stripos($format, '%p')) {
+ // Check if $userdate did something with %p by checking userdate against the same format without %p.
+ $formatwithoutp = str_ireplace('%p', '', $format);
+ if (userdate($time, $formatwithoutp) == $userdate) {
+ // The date is the same with and without %p - we have a problem.
+ if (userdate($time, '%H') > 11) {
+ $userdate .= 'pm';
+ } else {
+ $userdate .= 'am';
+ }
+ }
+ // Some locales and O/S don't respect correct intended case of %p vs %P
+ // This can cause problems with behat which expects AM vs am.
+ if (strpos($format, '%p')) { // Should be upper case according to PHP spec.
+ $userdate = str_replace('am', 'AM', $userdate);
+ $userdate = str_replace('pm', 'PM', $userdate);
+ }
+ }
+
+ return $userdate;
+}
+
+/**
+ * Used to print simple time - 1am instead of 1:00am.
+ *
+ * @param int $datetime - unix timestamp.
+ * @param int $duration - number of seconds.
+ */
+function attendance_construct_session_time($datetime, $duration) {
+ $starttime = attendance_strftimehm($datetime);
+ $endtime = attendance_strftimehm($datetime + $duration);
+
+ return $starttime . ($duration > 0 ? ' - ' . $endtime : '');
+}
+
+/**
+ * Used to print session time.
+ *
+ * @param int $datetime - unix timestamp.
+ * @param int $duration - number of seconds duration.
+ * @return string.
+ */
+function construct_session_full_date_time($datetime, $duration) {
+ $sessinfo = userdate($datetime, get_string('strftimedmyw', 'attendance'));
+ $sessinfo .= ' '.attendance_construct_session_time($datetime, $duration);
+
+ return $sessinfo;
+}
\ No newline at end of file
diff --git a/renderables.php b/renderables.php
index a463906..c0dbc7f 100644
--- a/renderables.php
+++ b/renderables.php
@@ -466,8 +466,9 @@ class attendance_user_data implements renderable {
* attendance_user_data constructor.
* @param mod_attendance_structure $att
* @param int $userid
+ * @param boolean $mobile - this is called by the mobile code, don't generate everything.
*/
- public function __construct(mod_attendance_structure $att, $userid) {
+ public function __construct(mod_attendance_structure $att, $userid, $mobile = false) {
$this->user = $att->get_user($userid);
$this->pageparams = $att->pageparams;
@@ -475,10 +476,12 @@ class attendance_user_data implements renderable {
if ($this->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) {
$this->statuses = $att->get_statuses(true, true);
- $this->summary = new mod_attendance_summary($att->id, array($userid), $att->pageparams->startdate,
- $att->pageparams->enddate);
+ if (!$mobile) {
+ $this->summary = new mod_attendance_summary($att->id, array($userid), $att->pageparams->startdate,
+ $att->pageparams->enddate);
- $this->filtercontrols = new attendance_filter_controls($att);
+ $this->filtercontrols = new attendance_filter_controls($att);
+ }
$this->sessionslog = $att->get_user_filtered_sessions_log_extended($userid);
diff --git a/renderer.php b/renderer.php
index a2d4db1..166d2cc 100644
--- a/renderer.php
+++ b/renderer.php
@@ -1111,7 +1111,8 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cell->colspan = 3;
$row->cells[] = $cell;
} else {
- if (attendance_can_student_mark($sess)) {
+ list($canmark, $reason) = attendance_can_student_mark($sess, false);
+ if ($canmark) {
// Student can mark their own attendance.
// URL to the page that lets the student modify their attendance.
@@ -1150,7 +1151,7 @@ class mod_attendance_renderer extends plugin_renderer_base {
* @return string
*/
private function construct_time($datetime, $duration) {
- $time = html_writer::tag('nobr', construct_session_time($datetime, $duration));
+ $time = html_writer::tag('nobr', attendance_construct_session_time($datetime, $duration));
return $time;
}
diff --git a/renderhelpers.php b/renderhelpers.php
index a851218..fa22f84 100644
--- a/renderhelpers.php
+++ b/renderhelpers.php
@@ -309,72 +309,6 @@ class user_sessions_cells_text_generator extends user_sessions_cells_generator {
}
}
-/**
- * Used to print simple time - 1am instead of 1:00am.
- *
- * @param int $time - unix timestamp.
- */
-function attendance_strftimehm($time) {
- $mins = userdate($time, '%M');
- if ($mins == '00') {
- $format = get_string('strftimeh', 'attendance');
- } else {
- $format = get_string('strftimehm', 'attendance');
- }
-
- $userdate = userdate($time, $format);
-
- // Some Lang packs use %p to suffix with AM/PM but not all strftime support this.
- // Check if %p is in use and make sure it's being respected.
- if (stripos($format, '%p')) {
- // Check if $userdate did something with %p by checking userdate against the same format without %p.
- $formatwithoutp = str_ireplace('%p', '', $format);
- if (userdate($time, $formatwithoutp) == $userdate) {
- // The date is the same with and without %p - we have a problem.
- if (userdate($time, '%H') > 11) {
- $userdate .= 'pm';
- } else {
- $userdate .= 'am';
- }
- }
- // Some locales and O/S don't respect correct intended case of %p vs %P
- // This can cause problems with behat which expects AM vs am.
- if (strpos($format, '%p')) { // Should be upper case according to PHP spec.
- $userdate = str_replace('am', 'AM', $userdate);
- $userdate = str_replace('pm', 'PM', $userdate);
- }
- }
-
- return $userdate;
-}
-
-/**
- * Used to print simple time - 1am instead of 1:00am.
- *
- * @param int $datetime - unix timestamp.
- * @param int $duration - number of seconds.
- */
-function construct_session_time($datetime, $duration) {
- $starttime = attendance_strftimehm($datetime);
- $endtime = attendance_strftimehm($datetime + $duration);
-
- return $starttime . ($duration > 0 ? ' - ' . $endtime : '');
-}
-
-/**
- * Used to print session time.
- *
- * @param int $datetime - unix timestamp.
- * @param int $duration - number of seconds duration.
- * @return string.
- */
-function construct_session_full_date_time($datetime, $duration) {
- $sessinfo = userdate($datetime, get_string('strftimedmyw', 'attendance'));
- $sessinfo .= ' '.construct_session_time($datetime, $duration);
-
- return $sessinfo;
-}
-
/**
* Used to construct user summary.
*
diff --git a/templates/mobile_take_attendance.mustache b/templates/mobile_take_attendance.mustache
new file mode 100644
index 0000000..336c108
--- /dev/null
+++ b/templates/mobile_take_attendance.mustache
@@ -0,0 +1,70 @@
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see