Skip to content

Commit

Permalink
Merge branch 'MOODLE_403_STABLE' of github.com:danmarsden/moodle-mod_…
Browse files Browse the repository at this point in the history
…attendance into MOODLE_403_STABLE_bulk_mark_fix
  • Loading branch information
JohnOLane committed Jul 25, 2024
2 parents d2503d8 + 7386933 commit b380c0b
Show file tree
Hide file tree
Showing 23 changed files with 135 additions and 229 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions backup/moodle2/restore_attendance_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion classes/customfield/session_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
6 changes: 4 additions & 2 deletions classes/form/addsession.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
2 changes: 1 addition & 1 deletion classes/local/entities/attendance.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 3 additions & 4 deletions classes/output/mobile.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down Expand Up @@ -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' => '',
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions classes/output/password_icon.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
7 changes: 5 additions & 2 deletions classes/output/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions classes/report_page_params.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions classes/sessions_page_params.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
15 changes: 6 additions & 9 deletions classes/structure.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions classes/take_page_params.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class mod_attendance_take_page_params {
/** @var int */
public $gridcols;

/** @var int */
public $page;

/** @var int */
public $perpage;

/**
* Initialize params.
*/
Expand Down
13 changes: 10 additions & 3 deletions js/password/attendance_QRCodeRotate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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;
}
});
Expand All @@ -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());

}

Expand Down
9 changes: 6 additions & 3 deletions locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();

Expand All @@ -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;
}
Expand Down Expand Up @@ -1438,7 +1441,7 @@ function attendance_renderqrcoderotate($session) {
echo '
<script type="text/javascript">
let qrCodeRotate = new attendance_QRCodeRotate();
qrCodeRotate.start(' . $session->id . ', document.getElementById("qrcode"), document.getElementById("rotate-time"));
qrCodeRotate.start(' . $session->id . ', document.getElementById("qrcode"), document.getElementById("rotate-time"), '. time() .');
</script>';
}

Expand Down
1 change: 0 additions & 1 deletion manage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion sessions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,53 +52,53 @@
<%#showmessage%>
<%#messages%>
<span class="messages">
<ion-item>
{{ 'plugin.mod_attendance.<% string %>' | translate }}
<ion-item class="ion-text-wrap">
<ion-label>{{ 'plugin.mod_attendance.<% string %>' | translate }}</ion-label>
</ion-item>
</span>
<%/messages%>
<%/showmessage%>
<span class="attendance_selectall">
<ion-item>
{{ 'plugin.mod_attendance.setallstatuses' | translate }}
</ion-item>
<ion-list radio-group [(ngModel)]="CONTENT_OTHERDATA.statusall" (ionChange)="<% selectall %>">
<ion-item class="ion-text-wrap">
<ion-label>{{ 'plugin.mod_attendance.setallstatuses' | translate }}</ion-label>
</ion-item>
<ion-radio-group [(ngModel)]="CONTENT_OTHERDATA.statusall" (ionChange)="<% selectall %>">
<%#statuses%>

<span class="radiolabel">
<ion-item>
<span class="radiolabel">
<ion-item class="ion-text-wrap">
<ion-label><% acronym %></ion-label>
<ion-radio value="<% stid %>"></ion-radio>
</ion-item>
</span>
<%/statuses%>
</ion-list>
</ion-radio-group>
</span>
<%#users%>
<span class="attendance_user_row">
<!-- User and status of the submission. -->
<span ion-item text-wrap title="<% fullname %>">
<ion-avatar item-start>
<ion-item class="ion-text-wrap" title="<% fullname %>">
<ion-avatar slot="start">
<img src="<% profileimageurl %>" core-external-content role="presentation" onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
<h2><% fullname %></h2>
</span>
<ion-list radio-group [(ngModel)]="CONTENT_OTHERDATA.status<% userid %>">
<ion-label>
<h2><% fullname %></h2>
</ion-label>
</ion-item>
<ion-radio-group [(ngModel)]="CONTENT_OTHERDATA.status<% userid %>">
<%#statuses%>
<span class="radiolabel">
<ion-item>
<ion-label><% acronym %></ion-label>
<ion-radio value="<% stid %>"></ion-radio>
</ion-item>
</span>
<ion-item class="ion-text-wrap">
<ion-label><% acronym %></ion-label>
<ion-radio value="<% stid %>"></ion-radio>
</ion-item>
</span>
<%/statuses%>
</ion-list>
</ion-radio-group>
</span>
<%/users%>
<ion-item>
<button ion-button core-site-plugins-new-content component="mod_attendance" method="mobile_view_activity" [args]="{cmid: <% cmid %>, courseid: <% courseid %>, sessid: <% sessid %><% btnargs %>}">
{{ 'plugin.mod_attendance.submitattendance' | translate }}
</button>
</ion-item>
<ion-button class="ion-margin" expand="block" core-site-plugins-new-content component="mod_attendance" method="mobile_view_activity" [args]="{cmid: <% cmid %>, courseid: <% courseid %>, sessid: <% sessid %><% btnargs %>}">
{{ 'plugin.mod_attendance.submitattendance' | translate }}
</ion-button>

</div>
Loading

0 comments on commit b380c0b

Please sign in to comment.