diff --git a/README.md b/README.md
index c7c805621d9..e2ca77113f6 100644
--- a/README.md
+++ b/README.md
@@ -8,19 +8,14 @@ The Attendance module was previously developed by
Branches
--------
-The git branches here support the following versions.
+The following git branches are supported:
| Moodle version | Branch |
|-----------------------|-------------------|
-| Mooodle 3.5 | MOODLE_35_STABLE |
-| Mooodle 3.6 | MOODLE_36_STABLE |
-| Moodle 3.7 | MOODLE_37_STABLE |
-| Moodle 3.8 - 3.10 | MOODLE_38_STABLE |
-| Moodle 3.11 | MOODLE_311_STABLE |
-| Moodle 4.0 | MOODLE_400_STABLE |
| Moodle 4.1 | MOODLE_401_STABLE |
| Moodle 4.2 | MOODLE_402_STABLE |
| Moodle 4.3 | MOODLE_403_STABLE |
+| Moodle 4.4 | MOODLE_404_STABLE |
# PURPOSE
The Attendance module allows teachers to maintain a record of attendance, replacing or supplementing a paper-based attendance register.
It is primarily used in blended-learning environments where students are required to attend classes, lectures and tutorials and allows
diff --git a/backup/moodle2/restore_attendance_stepslib.php b/backup/moodle2/restore_attendance_stepslib.php
index 91d4e50fa0c..6711a1edda3 100644
--- a/backup/moodle2/restore_attendance_stepslib.php
+++ b/backup/moodle2/restore_attendance_stepslib.php
@@ -145,6 +145,9 @@ protected function process_attendance_session($data) {
$data->lasttaken = 0;
$data->lasttakenby = 0;
}
+ if (!isset($data->allowupdatestatus)) {
+ $data->allowupdatestatus = 0;
+ }
$newitemid = $DB->insert_record('attendance_sessions', $data);
$data->id = $newitemid;
diff --git a/classes/customfield/session_handler.php b/classes/customfield/session_handler.php
index 544add97283..c45eee76b3f 100644
--- a/classes/customfield/session_handler.php
+++ b/classes/customfield/session_handler.php
@@ -87,12 +87,13 @@ public function can_configure() : bool {
* @return bool true if the current can edit custom fields, false otherwise
*/
public function can_edit(field_controller $field, int $instanceid = 0) : bool {
+ global $PAGE;
if ($instanceid) {
$context = $this->get_instance_context($instanceid);
return (!$field->get_configdata_property('locked') ||
has_capability('moodle/course:changelockedcustomfields', $context));
} else {
- $context = \context_system::instance();
+ $context = $PAGE->context->get_course_context();
return (!$field->get_configdata_property('locked') ||
guess_if_creator_will_have_course_capability('moodle/course:changelockedcustomfields', $context));
}
diff --git a/classes/form/addsession.php b/classes/form/addsession.php
index 586eb7fb885..75a19e776ce 100644
--- a/classes/form/addsession.php
+++ b/classes/form/addsession.php
@@ -240,14 +240,16 @@ public function definition() {
$mform->disabledif('studentpassword', 'rotateqrcode', 'checked');
$mgroup[] = & $mform->createElement('checkbox', 'randompassword', '', get_string('randompassword', 'attendance'));
$mform->disabledif('randompassword', 'rotateqrcode', 'checked');
- $mgroup[] = & $mform->createElement('checkbox', 'includeqrcode', '', get_string('includeqrcode', 'attendance'));
- $mform->disabledif('includeqrcode', 'rotateqrcode', 'checked');
$mform->addGroup($mgroup, 'passwordgrp', get_string('passwordgrp', 'attendance'), array(' '), false);
$mform->setType('studentpassword', PARAM_TEXT);
$mform->addHelpButton('passwordgrp', 'passwordgrp', 'attendance');
+ $mform->addElement('checkbox', 'includeqrcode', '', get_string('includeqrcode', 'attendance'));
+ $mform->hideif('includeqrcode', 'studentscanmark', 'notchecked');
+ $mform->disabledif('includeqrcode', 'rotateqrcode', 'checked');
+
$mform->addElement('checkbox', 'rotateqrcode', '', get_string('rotateqrcode', 'attendance'));
$mform->hideif('rotateqrcode', 'studentscanmark', 'notchecked');
diff --git a/classes/local/entities/attendance.php b/classes/local/entities/attendance.php
index 94bb10e3183..dadd90886a0 100644
--- a/classes/local/entities/attendance.php
+++ b/classes/local/entities/attendance.php
@@ -739,7 +739,7 @@ private function statusacronyms(): array {
* @return array
*/
private function acronymfieldnames(string $acronym): array {
- $fieldname = 'status_' . strtolower($acronym) . '_total_count';
+ $fieldname = 'status_' . rtrim(base64_encode($acronym), '=') . '_total_count';
return [
$fieldname,
$fieldname . '_current_week',
diff --git a/classes/output/mobile.php b/classes/output/mobile.php
index dcf05a6ac5f..4f408422887 100644
--- a/classes/output/mobile.php
+++ b/classes/output/mobile.php
@@ -53,7 +53,6 @@ public static function mobile_view_activity($args) {
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
- $versionname = $args['appversioncode'] >= 3950 ? 'latest' : 'ionic3';
$cmid = $args['cmid'];
$courseid = $args['courseid'];
$takenstatus = empty($args['status']) ? '' : $args['status'];
@@ -252,7 +251,7 @@ public static function mobile_view_activity($args) {
'templates' => [
[
'id' => 'main',
- 'html' => $OUTPUT->render_from_template("mod_attendance/mobile_view_page_$versionname", $data),
+ 'html' => $OUTPUT->render_from_template("mod_attendance/mobile_view_page_latest", $data),
],
],
'javascript' => '',
@@ -272,7 +271,7 @@ public static function mobile_user_form($args) {
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$args = (object) $args;
- $versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
+ $versionname = $args->appversioncode >= 44000 ? 'latest' : 'ionic5';
$cmid = $args->cmid;
$courseid = $args->courseid;
$sessid = $args->sessid;
@@ -374,7 +373,7 @@ public static function mobile_teacher_form($args) {
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$args = (object) $args;
- $versionname = $args->appversioncode >= 3950 ? 'latest' : 'ionic3';
+ $versionname = $args->appversioncode >= 44000 ? 'latest' : 'ionic5';
$cmid = $args->cmid;
$courseid = $args->courseid;
$sessid = $args->sessid;
diff --git a/classes/output/password_icon.php b/classes/output/password_icon.php
index f520c4f748b..0d4b3b4894e 100644
--- a/classes/output/password_icon.php
+++ b/classes/output/password_icon.php
@@ -49,6 +49,16 @@ class password_icon implements renderable, templatable {
*/
public $linktext = null;
+ /**
+ * @var int Session id.
+ */
+ public $sessionid;
+
+ /**
+ * @var int - should qr code be displayed.
+ */
+ public $includeqrcode;
+
/**
* Constructor
*
diff --git a/classes/output/renderer.php b/classes/output/renderer.php
index 0ccba357d1e..da1e12e0682 100644
--- a/classes/output/renderer.php
+++ b/classes/output/renderer.php
@@ -1147,10 +1147,13 @@ protected function render_user_report_tabs(user_data $userdata) {
* @return string
*/
private function construct_user_data(user_data $userdata) {
- global $USER;
+ global $USER, $COURSE;
$o = '';
if ($USER->id <> $userdata->user->id) {
- $o = html_writer::tag('h2', fullname($userdata->user));
+ $userlink = html_writer::link(new \moodle_url('/user/view.php',
+ ['id' => $userdata->user->id, 'course' => $COURSE->id]), fullname($userdata->user));
+
+ $o = html_writer::tag('h2', $userlink);
}
if ($userdata->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) {
diff --git a/classes/report_page_params.php b/classes/report_page_params.php
index 030e1e1c277..b6c44b07cd2 100644
--- a/classes/report_page_params.php
+++ b/classes/report_page_params.php
@@ -39,6 +39,8 @@ class mod_attendance_report_page_params extends mod_attendance_page_with_filter_
public $showsessiondetails;
/** @var int */
public $sessiondetailspos;
+ /** @var int */
+ public $page;
/**
* mod_attendance_report_page_params constructor.
diff --git a/classes/sessions_page_params.php b/classes/sessions_page_params.php
index 9c1878b490c..b58e1899dea 100644
--- a/classes/sessions_page_params.php
+++ b/classes/sessions_page_params.php
@@ -62,4 +62,7 @@ class mod_attendance_sessions_page_params {
/** @var int view mode of taking attendance page*/
public $action;
+
+ /** @var int - sessionid of session */
+ public $sessionid;
}
diff --git a/classes/structure.php b/classes/structure.php
index 5d8878e17e0..c5d63721ab8 100644
--- a/classes/structure.php
+++ b/classes/structure.php
@@ -476,19 +476,16 @@ public function add_sessions($sessions) {
}
/**
- * Undocumented function
+ * Save customfields
*
- * @param array $sessions
+ * @param int $sessionid
* @param object $formdata
* @return void
*/
- public function save_customfields($sessions, $formdata) {
- foreach ($sessions as $session) {
- $handler = mod_attendance\customfield\session_handler::create();
- $formdata->id = $session->id;
- $handler->instance_form_save($formdata, true);
- }
-
+ public function save_customfields(int $sessionid, $formdata) {
+ $handler = mod_attendance\customfield\session_handler::create();
+ $formdata->id = $sessionid;
+ $handler->instance_form_save($formdata, true);
}
/**
diff --git a/classes/take_page_params.php b/classes/take_page_params.php
index 594deb635b8..b68c5281770 100644
--- a/classes/take_page_params.php
+++ b/classes/take_page_params.php
@@ -54,6 +54,12 @@ class mod_attendance_take_page_params {
/** @var int */
public $gridcols;
+ /** @var int */
+ public $page;
+
+ /** @var int */
+ public $perpage;
+
/**
* Initialize params.
*/
diff --git a/js/password/attendance_QRCodeRotate.js b/js/password/attendance_QRCodeRotate.js
index 4aceb32ef71..2593bcd1cf4 100644
--- a/js/password/attendance_QRCodeRotate.js
+++ b/js/password/attendance_QRCodeRotate.js
@@ -13,12 +13,15 @@ class attendance_QRCodeRotate {
this.password = "";
this.qrCodeInstance = "";
this.qrCodeHTMLElement = "";
+ this.timeOffset = new Date();
}
- start(sessionId, qrCodeHTMLElement, timerHTMLElement) {
+ start(sessionId, qrCodeHTMLElement, timerHTMLElement, serverTime) {
this.sessionId = sessionId;
this.qrCodeHTMLElement = qrCodeHTMLElement;
this.timerHTMLElement = timerHTMLElement;
+ this.timeOffset = new Date() - new Date(serverTime * 1000);
+ console.log(`Sync OK - Server time is ${new Date(serverTime * 1000)}\nClient's time is ${this.timeOffset < 0 ? 'late' : 'early'} by ${Math.abs(this.timeOffset)} milliseconds.`);
this.fetchAndRotate();
}
@@ -43,13 +46,17 @@ class attendance_QRCodeRotate {
this.timerHTMLElement.innerHTML = timeLeft;
}
+ serverTime() {
+ return Math.round((new Date().getTime() - this.timeOffset) / 1000);
+ }
+
startRotating() {
var parent = this;
setInterval(function() {
var found = Object.values(parent.password).find(function(element) {
- if (element.expirytime > Math.round(new Date().getTime() / 1000)) {
+ if (element.expirytime > parent.serverTime()) {
return element;
}
});
@@ -58,7 +65,7 @@ class attendance_QRCodeRotate {
location.reload(true);
} else {
parent.changeQRCode(found.password);
- parent.updateTimer(found.expirytime - Math.round(new Date().getTime() / 1000));
+ parent.updateTimer(found.expirytime - parent.serverTime());
}
diff --git a/locallib.php b/locallib.php
index 80d57d34978..a668b066b45 100644
--- a/locallib.php
+++ b/locallib.php
@@ -564,7 +564,7 @@ function attendance_update_status($status, $acronym, $description, $grade, $visi
* @return string
*/
function attendance_random_string($length=6) {
- $randombytes = random_bytes_emulate($length);
+ $randombytes = random_bytes($length);
$pool = 'abcdefghijklmnopqrstuvwxyz';
$pool .= '0123456789';
$poollen = strlen($pool);
@@ -1246,6 +1246,7 @@ function attendance_session_get_highest_status(mod_attendance_structure $att, $a
* @return array
*/
function attendance_get_automarkoptions() {
+ global $COURSE;
$options = array();
@@ -1254,7 +1255,9 @@ function attendance_get_automarkoptions() {
$options[ATTENDANCE_AUTOMARK_ALL] = get_string('automarkall', 'attendance');
}
$options[ATTENDANCE_AUTOMARK_CLOSE] = get_string('automarkclose', 'attendance');
- $options[ATTENDANCE_AUTOMARK_ACTIVITYCOMPLETION] = get_string('onactivitycompletion', 'attendance');
+ if ($COURSE->id == SITEID || $COURSE->enablecompletion == COMPLETION_ENABLED) {
+ $options[ATTENDANCE_AUTOMARK_ACTIVITYCOMPLETION] = get_string('onactivitycompletion', 'attendance');
+ }
return $options;
}
@@ -1438,7 +1441,7 @@ function attendance_renderqrcoderotate($session) {
echo '
';
}
diff --git a/manage.php b/manage.php
index e0f891931e6..4bb857584a0 100644
--- a/manage.php
+++ b/manage.php
@@ -76,7 +76,6 @@
$PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(true);
$PAGE->force_settings_menu(true);
-$PAGE->navbar->add($att->name);
$output = $PAGE->get_renderer('mod_attendance');
$filtercontrols = new mod_attendance\output\filter_controls($att);
diff --git a/sessions.php b/sessions.php
index 88129c15df6..5ac28a7e9f7 100644
--- a/sessions.php
+++ b/sessions.php
@@ -73,7 +73,10 @@
if ($formdata = $mform->get_data()) {
$sessions = attendance_construct_sessions_data_for_add($formdata, $att);
$att->add_sessions($sessions);
- $att->save_customfields($sessions, $formdata);
+ // Save custom fields.
+ foreach ($sessions as $session) {
+ $att->save_customfields($session->id, $formdata);
+ }
if (count($sessions) == 1) {
$message = get_string('sessiongenerated', 'attendance');
@@ -106,6 +109,8 @@
$formdata->allowupdatestatus = 0;
}
$att->update_session_from_form_data($formdata, $sessionid);
+ // Save customfields data.
+ $att->save_customfields($sessionid, $formdata);
mod_attendance_notifyqueue::notify_success(get_string('sessionupdated', 'attendance'));
redirect($att->url_manage());
diff --git a/templates/mobile_teacher_form_ionic3.mustache b/templates/mobile_teacher_form_ionic5.mustache
similarity index 64%
rename from templates/mobile_teacher_form_ionic3.mustache
rename to templates/mobile_teacher_form_ionic5.mustache
index f25374f2a01..4beb511592e 100644
--- a/templates/mobile_teacher_form_ionic3.mustache
+++ b/templates/mobile_teacher_form_ionic5.mustache
@@ -52,53 +52,53 @@
<%#showmessage%>
<%#messages%>
- <% fullname %>
- <% fullname %>
+