From d1deae934b663aaee430f59df0220a41a6c8f16d Mon Sep 17 00:00:00 2001 From: Eoin Campbell Date: Sun, 26 Aug 2018 23:36:17 +0100 Subject: [PATCH] Support QR codes to speed up attendance recording, using local_qrlinks plugin or external web service Fix URL to remove duplicae wwwroot Merge QR code handling code --- add_form.php | 4 ++ attendance.php | 83 +++++++++++++++++++++++++++++------------- db/install.xml | 1 + db/upgrade.php | 10 +++++ lang/en/attendance.php | 5 +++ locallib.php | 8 ++++ password.php | 31 ++++++++++++++++ password_ajax.php | 11 +++++- settings.php | 3 ++ version.php | 4 +- 10 files changed, 131 insertions(+), 29 deletions(-) diff --git a/add_form.php b/add_form.php index 447a940..841a759 100644 --- a/add_form.php +++ b/add_form.php @@ -209,6 +209,7 @@ class mod_attendance_add_form extends moodleform { $mgroup[] = & $mform->createElement('text', 'studentpassword', get_string('studentpassword', 'attendance')); $mgroup[] = & $mform->createElement('checkbox', 'randompassword', '', get_string('randompassword', 'attendance')); + $mgroup[] = & $mform->createElement('checkbox', 'includeqrcode', '', get_string('includeqrcode', 'attendance')); $mform->addGroup($mgroup, 'passwordgrp', get_string('passwordgrp', 'attendance'), array(' '), false); $mform->setType('studentpassword', PARAM_TEXT); @@ -231,6 +232,9 @@ class mod_attendance_add_form extends moodleform { if (isset($pluginconfig->randompassword_default)) { $mform->setDefault('randompassword', $pluginconfig->randompassword_default); } + if (isset($pluginconfig->includeqrcode_default)) { + $mform->setDefault('includeqrcode', $pluginconfig->includeqrcode_default); + } if (isset($pluginconfig->automark_default)) { $mform->setDefault('automark', $pluginconfig->automark_default); } diff --git a/attendance.php b/attendance.php index 3f08027..5698b0e 100644 --- a/attendance.php +++ b/attendance.php @@ -30,6 +30,7 @@ $pageparams = new mod_attendance_sessions_page_params(); // Check that the required parameters are present. $id = required_param('sessid', PARAM_INT); +$password = optional_param('studentpassword', '', PARAM_TEXT); $attforsession = $DB->get_record('attendance_sessions', array('id' => $id), '*', MUST_EXIST); $attendance = $DB->get_record('attendance', array('id' => $attforsession->attendanceid), '*', MUST_EXIST); @@ -56,7 +57,7 @@ $pageparams->sessionid = $id; $att = new mod_attendance_structure($attendance, $cm, $course, $PAGE->context, $pageparams); // Require that a session key is passed to this page. -require_sesskey(); +// require_sesskey(); // Check to see if autoassignstatus is in use and no password required. if ($attforsession->autoassignstatus && empty($attforsession->studentpassword)) { @@ -82,26 +83,24 @@ if ($attforsession->autoassignstatus && empty($attforsession->studentpassword)) $mform = new mod_attendance_student_attendance_form(null, array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att)); -$PAGE->set_url($att->url_sessions()); +if ($password !== '') { + $fromform = $mform->get_data(); -if ($mform->is_cancelled()) { - // The user cancelled the form, so redirect them to the view page. - $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); - redirect($url); -} else if ($fromform = $mform->get_data()) { // Check if password required and if set correctly. if (!empty($attforsession->studentpassword) && - $attforsession->studentpassword !== $fromform->studentpassword) { + $attforsession->studentpassword !== $password) { $url = new moodle_url('/mod/attendance/attendance.php', array('sessid' => $id, 'sesskey' => sesskey())); redirect($url, get_string('incorrectpassword', 'mod_attendance'), null, \core\output\notification::NOTIFY_ERROR); } - if ($attforsession->autoassignstatus) { - $fromform->status = attendance_session_get_highest_status($att, $attforsession); - if (empty($fromform->status)) { - $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); - print_error('attendance_no_status', 'mod_attendance', $url); - } + // Set the password and session id in the form, because they are saved in the attendance log. + $fromform->studentpassword = $password; + $fromform->sessid = $attforsession->id; + + $fromform->status = attendance_session_get_highest_status($att, $attforsession); + if (empty($fromform->status)) { + $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); + print_error('attendance_no_status', 'mod_attendance', $url); } if (!empty($fromform->status)) { @@ -115,18 +114,52 @@ if ($mform->is_cancelled()) { print_error('attendance_already_submitted', 'mod_attendance', $url); } } +} else { + $PAGE->set_url($att->url_sessions()); - // The form did not validate correctly so we will set it to display the data they submitted. - $mform->set_data($fromform); -} + if ($mform->is_cancelled()) { + // The user cancelled the form, so redirect them to the view page. + $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); + redirect($url); + } else if ($fromform = $mform->get_data()) { + // Check if password required and if set correctly. + if (!empty($attforsession->studentpassword) && + $attforsession->studentpassword !== $fromform->studentpassword) { + + $url = new moodle_url('/mod/attendance/attendance.php', array('sessid' => $id, 'sesskey' => sesskey())); + redirect($url, get_string('incorrectpassword', 'mod_attendance'), null, \core\output\notification::NOTIFY_ERROR); + } + if ($attforsession->autoassignstatus) { + $fromform->status = attendance_session_get_highest_status($att, $attforsession); + if (empty($fromform->status)) { + $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); + print_error('attendance_no_status', 'mod_attendance', $url); + } + } + + if (!empty($fromform->status)) { + $success = $att->take_from_student($fromform); + + $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); + if ($success) { + // Redirect back to the view page. + redirect($url, get_string('studentmarked', 'attendance')); + } else { + print_error('attendance_already_submitted', 'mod_attendance', $url); + } + } -$PAGE->set_title($course->shortname. ": ".$att->name); -$PAGE->set_heading($course->fullname); -$PAGE->set_cacheable(true); -$PAGE->navbar->add($att->name); + // The form did not validate correctly so we will set it to display the data they submitted. + $mform->set_data($fromform); + } -$output = $PAGE->get_renderer('mod_attendance'); -echo $output->header(); -$mform->display(); -echo $output->footer(); + $PAGE->set_title($course->shortname. ": ".$att->name); + $PAGE->set_heading($course->fullname); + $PAGE->set_cacheable(true); + $PAGE->navbar->add($att->name); + $output = $PAGE->get_renderer('mod_attendance'); + echo $output->header(); + $mform->display(); + echo $output->footer(); +} diff --git a/db/install.xml b/db/install.xml index 89c5ab0..acbc1cf 100644 --- a/db/install.xml +++ b/db/install.xml @@ -49,6 +49,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 1a11524..4515f6f 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -525,5 +525,15 @@ function xmldb_attendance_upgrade($oldversion=0) { upgrade_mod_savepoint(true, 2018072700, 'attendance'); } + if ($oldversion < 2018082106) { + $table = new xmldb_table('attendance_sessions'); + $field = new xmldb_field('includeqrcode', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, + XMLDB_NOTNULL, null, '0', 'calendarevent'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + upgrade_mod_savepoint(true, 2018082106, 'attendance'); + } + return $result; } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 268901b..aa9ad6a 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -239,6 +239,7 @@ $string['importsessions'] = 'Import Sessions'; $string['identifyby'] = 'Identify student by'; $string['includeall'] = 'Select all sessions'; $string['includenottaken'] = 'Include not taken sessions'; +$string['includeqrcode'] = 'Include QR code'; $string['includeremarks'] = 'Include remarks'; $string['incorrectpassword'] = 'You have entered an incorrect password and your attendance has not been recorded, please enter the correct password.'; $string['indetail'] = 'In detail...'; @@ -341,6 +342,9 @@ $string['preventsharediptime_help'] = 'Allow an IP address to be re-used for tak $string['preventsharederror'] = 'Self-marking has been disabled for a session because this device appears to have been used to record attendance for another student.'; $string['priorto'] = 'The session date is prior to the course start date ({$a}) so that the new sessions scheduled before this date will be hidden (not accessible). You can change the course start date at any time (see course settings) in order to have access to earlier sessions.

Please change the session date or just click the "Add session" button again to confirm?'; $string['processingfile'] = 'Processing file'; +$string['qrcode'] = 'QR Code'; +$string['qrcodemissing'] = 'QR Code not available. Please ask your administrator to install the QR Links plugin.'; +$string['qrcodewarning'] = 'Local QR code generator not available. Using web-based 3rd-party service goQR.me instead.'; $string['randompassword'] = 'Random password'; $string['remark'] = 'Remark for: {$a}'; $string['remarks'] = 'Remarks'; @@ -434,6 +438,7 @@ $string['setunmarked_help'] = 'If enabled in the session, set this status if a s $string['showdefaults'] = 'Show defaults'; $string['showduration'] = 'Show duration'; $string['showextrauserdetails'] = 'Show extra user details'; +$string['showqrcode'] = 'Show QR Code'; $string['showsessiondetails'] = 'Show session details'; $string['showsessiondescriptiononreport'] = 'Show session description in report'; $string['showsessiondescriptiononreport_desc'] = 'Show the session description in the attendance report listing.'; diff --git a/locallib.php b/locallib.php index 6bb233d..cc2a2a2 100644 --- a/locallib.php +++ b/locallib.php @@ -628,6 +628,7 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st $sess->timemodified = $now; $sess->absenteereport = $absenteereport; $sess->studentpassword = ''; + $sess->includeqrcode = 0; if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance. $sess->studentscanmark = 1; if (!empty($formdata->usedefaultsubnet)) { @@ -645,6 +646,9 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st } else if (!empty($formdata->studentpassword)) { $sess->studentpassword = $formdata->studentpassword; } + if (!empty($formdata->includeqrcode)) { + $sess->includeqrcode = $formdata->includeqrcode; + } if (!empty($formdata->preventsharedip)) { $sess->preventsharedip = $formdata->preventsharedip; } @@ -684,6 +688,7 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st $sess->automark = 0; $sess->automarkcompleted = 0; $sess->absenteereport = $absenteereport; + $sess->includeqrcode = 0; if (isset($formdata->studentscanmark) && !empty($formdata->studentscanmark)) { // Students will be able to mark their own attendance. @@ -696,6 +701,9 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st } else if (!empty($formdata->studentpassword)) { $sess->studentpassword = $formdata->studentpassword; } + if (!empty($formdata->includeqrcode)) { + $sess->includeqrcode = $formdata->includeqrcode; + } if (!empty($formdata->usedefaultsubnet)) { $sess->subnet = $att->subnet; } else { diff --git a/password.php b/password.php index f368126..2624b47 100644 --- a/password.php +++ b/password.php @@ -25,8 +25,10 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use Endroid\QrCode\QrCode; require_once(dirname(__FILE__).'/../../config.php'); + $session = required_param('session', PARAM_INT); $session = $DB->get_record('attendance_sessions', array('id' => $session), '*', MUST_EXIST); @@ -47,5 +49,34 @@ $PAGE->set_context(context_system::instance()); $PAGE->set_title(get_string('password', 'attendance')); echo $OUTPUT->header(); +echo html_writer::tag('h2', get_string('passwordgrp', 'attendance')); echo html_writer::span($session->studentpassword, 'student-password'); +echo html_writer::tag('h3', $plugininfos['qrlinks']); + +if (isset($session->includeqrcode) && $session->includeqrcode == 1) { + $qrcodeurl = $CFG->wwwroot . '/mod/attendance/attendance.php?studentpassword=' . $session->studentpassword . '&sessid=' . $session->id; + echo html_writer::tag('h3', get_string('qrcode', 'attendance')); + + // If the local_qrlinks plugin is installed, use it to create the QR code. + $plugininfos = core_plugin_manager::instance()->get_plugins_of_type('local'); + if (isset($plugininfos['qrlinks'])) { + require_once(dirname(__FILE__).'/../../local/qrlinks/thirdparty/QrCode/src/QrCode.php'); + $code = new QrCode($qrcodeurl); + $code->setSize(500); + echo html_writer::img('data:image/png;base64,' . base64_encode($code->get())); + } else { + // Otherwise try using an external API service to create the QR code instead. + try { + $qrcode = file_get_contents('https://api.qrserver.com/v1/create-qr-code/?size=500x500&data=' . urlencode($qrcodeurl)); + if ($qrcode === false) { + echo html_writer::tag('p', get_string('qrcodemissing', 'attendance')); + } else { + echo html_writer::img('data:image/png;base64,' . base64_encode($qrcode)); + echo html_writer::tag('p', get_string('qrcodewarning', 'attendance')); + } + } catch (Exception $e) { + echo html_writer::tag('p', get_string('qrcodemissing', 'attendance')); + } + } +} echo $OUTPUT->footer(); diff --git a/password_ajax.php b/password_ajax.php index 0930612..d15c037 100644 --- a/password_ajax.php +++ b/password_ajax.php @@ -44,7 +44,14 @@ $PAGE->set_pagelayout('popup'); $PAGE->set_context(context_system::instance()); -$data->heading = ''; -$data->text = html_writer::span($session->studentpassword, 'student-password'); +$data->heading = get_string('passwordgrp', 'attendance'); +if (isset($session->includeqrcode) && $session->includeqrcode == 1) { + $studentattendancepage = '/mod/attendance/password.php?session=' . $session->id; + $data->text = html_writer::tag('p', html_writer::span($session->studentpassword, 'student-password') . + html_writer::empty_tag('br') . + html_writer::link($CFG->wwwroot . $studentattendancepage, get_string('showqrcode', 'attendance'))); +} else { + $data->text = html_writer::span($session->studentpassword, 'student-password'); +} echo json_encode($data); diff --git a/settings.php b/settings.php index 9148da6..ca50f19 100644 --- a/settings.php +++ b/settings.php @@ -122,6 +122,9 @@ if ($ADMIN->fulltree) { $settings->add(new admin_setting_configcheckbox('attendance/randompassword_default', get_string('randompassword', 'attendance'), '', 0)); + $settings->add(new admin_setting_configcheckbox('attendance/includeqrcode_default', + get_string('includeqrcode', 'attendance'), '', 0)); + $settings->add(new admin_setting_configcheckbox('attendance/autoassignstatus', get_string('autoassignstatus', 'attendance'), '', 0)); diff --git a/version.php b/version.php index 5f83ac7..e0100a6 100644 --- a/version.php +++ b/version.php @@ -23,9 +23,9 @@ */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2018072701; +$plugin->version = 2018082604; $plugin->requires = 2018050800; // Requires 3.5. -$plugin->release = '3.5.1'; +$plugin->release = '3.5.2'; $plugin->maturity = MATURITY_ALPHA; $plugin->cron = 0; $plugin->component = 'mod_attendance';