Feature: Prevent students from sharing device while self-marking.
authorDan Marsden <dan@danmarsden.com>
Mon, 30 Apr 2018 07:59:18 +0000 (19:59 +1200)
committerDan Marsden <dan@danmarsden.com>
Tue, 1 May 2018 01:54:34 +0000 (13:54 +1200)
Adds IP recording when self-marking and checks if another student
has previously self-marked using the same IP address.

Thanks to Xi’an Jiaotong Liverpool University for funding this work.

13 files changed:
add_form.php
backup/moodle2/backup_attendance_stepslib.php
classes/event/session_ip_shared.php [new file with mode: 0644]
classes/import/sessions.php
classes/structure.php
db/install.xml
db/upgrade.php
externallib.php
lang/en/attendance.php
locallib.php
settings.php
update_form.php
version.php

index 756bb02..3fef14a 100644 (file)
@@ -241,6 +241,26 @@ class mod_attendance_add_form extends moodleform {
 
             $mform->hideif('subnetgrp', 'studentscanmark', 'notchecked');
             $mform->hideif('subnet', 'usedefaultsubnet', 'checked');
+
+            $mgroup3 = array();
+            $mgroup3[] = & $mform->createElement('checkbox', 'preventsharedip', '');
+            $mgroup3[] = & $mform->createElement('text', 'preventsharediptime',
+                get_string('preventsharediptime', 'attendance'), '', 'test');
+            $mgroup3[] = & $mform->createElement('static', 'preventsharediptimedesc', '',
+                get_string('preventsharedipminutes', 'attendance'));
+            $mform->addGroup($mgroup3, 'preventsharedgroup', get_string('preventsharedip', 'attendance'), array(' '), false);
+            $mform->addHelpButton('preventsharedgroup', 'preventsharedip', 'attendance');
+            $mform->setAdvanced('preventsharedgroup');
+            $mform->setType('preventsharediptime', PARAM_INT);
+            $mform->hideif('preventsharedgroup', 'studentscanmark', 'notchecked');
+            $mform->disabledIf('preventsharediptime', 'preventsharedip', 'notchecked');
+            if (isset($pluginconfig->preventsharedip)) {
+                $mform->setDefault('preventsharedip', $pluginconfig->preventsharedip);
+            }
+            if (isset($pluginconfig->preventsharediptime)) {
+                $mform->setDefault('preventsharediptime', $pluginconfig->preventsharediptime);
+            }
+
         } else {
             $mform->addElement('hidden', 'studentscanmark', '0');
             $mform->settype('studentscanmark', PARAM_INT);
@@ -251,6 +271,13 @@ class mod_attendance_add_form extends moodleform {
 
             $mform->addElement('hidden', 'subnet', '');
             $mform->setType('subnet', PARAM_TEXT);
+
+            $mform->addElement('hidden', 'preventsharedip', '0');
+            $mform->setType('preventsharedip', PARAM_INT);
+
+            $sharedtime = isset($pluginconfig->preventsharediptime) ? $pluginconfig->preventsharediptime : null;
+            $mform->addElement('hidden', 'preventsharediptime', $sharedtime);
+            $mform->setType('preventsharediptime', PARAM_INT);
         }
 
         $this->add_action_buttons(true, get_string('add', 'attendance'));
@@ -313,6 +340,11 @@ class mod_attendance_add_form extends moodleform {
             }
         }
 
+        if (!empty($data['studentscanmark']) && !empty($data['preventsharedip']) &&
+                empty($data['preventsharediptime'])) {
+            $errors['preventsharedgroup'] = get_string('iptimemissing', 'attendance');
+
+        }
         return $errors;
     }
 
index 6aefd4c..cdf21ab 100644 (file)
@@ -58,7 +58,8 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
         $session  = new backup_nested_element('session', array('id'), array(
             'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby', 'timemodified',
             'description', 'descriptionformat', 'studentscanmark', 'studentpassword', 'autoassignstatus',
-            'subnet', 'automark', 'automarkcompleted', 'statusset', 'absenteereport', 'caleventid'));
+            'subnet', 'automark', 'automarkcompleted', 'statusset', 'absenteereport', 'preventsharedip',
+            'preventsharediptime', 'caleventid'));
 
         // XML nodes declaration - user data.
         $logs = new backup_nested_element('logs');
diff --git a/classes/event/session_ip_shared.php b/classes/event/session_ip_shared.php
new file mode 100644 (file)
index 0000000..8d71dca
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+// 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 <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains an event for when self-marking is blocked because
+ * another student used the same IP address to self-mark.
+ *
+ * @package mod_attendance
+ * @author Dan Marsden <dan@danmarsden.com>
+ * @copyright 2018 Catalyst IT
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace mod_attendance\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event for when self-marking is blocked
+ *
+ * @property-read array $other {
+ *                Extra information about event properties.
+ *
+ *                string mode Mode of the report viewed.
+ *                }
+ * @package mod_attendance
+ * @author Dan Marsden <dan@danmarsden.com>
+ * @copyright 2018 Catalyst IT
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class session_ip_shared extends \core\event\base {
+
+    /**
+     * Init method.
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
+        $this->data['objecttable'] = 'attendance_log';
+    }
+
+    /**
+     * Returns non-localised description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return 'User with id ' . $this->userid . ' was blocked from taking attendance for sessionid: ' . $this->other['sessionid'] .
+               ' because user with id '.$this->other['otheruser'] . ' previously marked attendance with the same IP address.';
+    }
+
+    /**
+     * Returns localised general event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventsessionipshared', 'mod_attendance');
+    }
+
+    /**
+     * Get URL related to the action
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/mod/attendance/attendance.php');
+    }
+
+    /**
+     * Get objectid mapping
+     *
+     * @return array of parameters for object mapping.
+     */
+    public static function get_objectid_mapping() {
+        return array(
+            'db' => 'attendance',
+            'restore' => 'attendance'
+        );
+    }
+}
index 1919741..74b9dcf 100644 (file)
@@ -107,7 +107,9 @@ class sessions {
             get_string('subnet', 'attendance'),
             get_string('automark', 'attendance'),
             get_string('autoassignstatus', 'attendance'),
-            get_string('absenteereport', 'attendance')
+            get_string('absenteereport', 'attendance'),
+            get_string('preventsharedip', 'attendance'),
+            get_string('preventsharediptime', 'attendance')
         );
     }
 
@@ -143,7 +145,9 @@ class sessions {
                 'subnet' => $data->header12,
                 'automark' => $data->header13,
                 'autoassignstatus' => $data->header14,
-                'absenteereport' => $data->header15
+                'absenteereport' => $data->header15,
+                'preventsharedip' => $data->header16,
+                'preventsharediptime' => $data->header17,
             );
         } else {
             return array(
@@ -162,7 +166,9 @@ class sessions {
                 'subnet' => 12,
                 'automark' => 13,
                 'autoassignstatus' => 14,
-                'absenteereport' => 15
+                'absenteereport' => 15,
+                'preventsharedip' => 16,
+                'preventsharediptime' => 17
             );
         }
     }
@@ -317,6 +323,17 @@ class sessions {
             } else {
                 $session->absenteereport = $this->get_column_data($row, $mapping['absenteereport']);
             }
+            if ($mapping['preventsharedip'] == -1) {
+                $session->preventsharedip = $pluginconfig->preventsharedip;
+            } else {
+                $session->preventsharedip = $this->get_column_data($row, $mapping['preventsharedip']);
+            }
+            if ($mapping['preventsharediptime'] == -1) {
+                $session->preventsharediptime = $pluginconfig->preventsharediptime;
+            } else {
+                $session->preventsharediptime = $this->get_column_data($row, $mapping['preventsharediptime']);
+            }
+
             $session->statusset = 0;
 
             $sessions[] = $session;
index 5aa9b74..7a29187 100644 (file)
@@ -493,6 +493,14 @@ class mod_attendance_structure {
                 $sess->subnet = '';
             }
 
+            if (!isset($sess->preventsharedip)) {
+                $sess->preventsharedip = 0;
+            }
+
+            if (!isset($sess->preventsharediptime)) {
+                $sess->preventsharediptime = '';
+            }
+
             $event->add_record_snapshot('attendance_sessions', $sess);
             $event->trigger();
         }
@@ -529,6 +537,8 @@ class mod_attendance_structure {
         $sess->subnet = '';
         $sess->automark = 0;
         $sess->automarkcompleted = 0;
+        $sess->preventsharedip = 0;
+        $sess->preventsharediptime = '';
         if (!empty(get_config('attendance', 'enablewarnings'))) {
             $sess->absenteereport = empty($formdata->absenteereport) ? 0 : 1;
         }
@@ -549,6 +559,13 @@ class mod_attendance_structure {
             if (!empty($formdata->automark)) {
                 $sess->automark = $formdata->automark;
             }
+            if (!empty($formdata->preventsharedip)) {
+                $sess->preventsharedip = $formdata->preventsharedip;
+            }
+            if (!empty($formdata->preventsharediptime)) {
+                $sess->preventsharediptime = $formdata->preventsharediptime;
+            }
+
         }
 
         $sess->timemodified = time();
@@ -592,6 +609,7 @@ class mod_attendance_structure {
         $record->sessionid = $mformdata->sessid;
         $record->timetaken = $now;
         $record->takenby = $USER->id;
+        $record->ipaddress = getremoteaddr(null);
 
         $dbsesslog = $this->get_session_log($mformdata->sessid);
         if (array_key_exists($record->studentid, $dbsesslog)) {
@@ -1018,7 +1036,8 @@ class mod_attendance_structure {
             $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
+            $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks,
+                           ats.preventsharediptime, ats.preventsharedip
                   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
@@ -1033,7 +1052,8 @@ class mod_attendance_structure {
                 'edate'     => $this->pageparams->enddate);
 
         } else {
-            $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks
+            $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks,
+                           ats.preventsharediptime, ats.preventsharedip
                   FROM {attendance_sessions} ats
                   JOIN {attendance_log} al
                     ON ats.id = al.sessionid AND al.studentid = :uid
@@ -1075,7 +1095,8 @@ class mod_attendance_structure {
         $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, ats.autoassignstatus
+                           al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
+                           ats.preventsharedip, ats.preventsharediptime
                       FROM {attendance_sessions} ats
                 RIGHT JOIN {attendance_log} al
                         ON ats.id = al.sessionid AND al.studentid = :uid
@@ -1084,7 +1105,8 @@ class mod_attendance_structure {
                   ORDER BY ats.sessdate ASC";
         } else {
             $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
-                           al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus
+                           al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
+                           ats.preventsharedip, ats.preventsharediptime
                       FROM {attendance_sessions} ats
                 RIGHT JOIN {attendance_log} al
                         ON ats.id = al.sessionid AND al.studentid = :uid
@@ -1114,7 +1136,8 @@ class mod_attendance_structure {
             $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, ats.statusset,
-                       al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus
+                       al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
+                       ats.preventsharedip, ats.preventsharediptime
                   FROM {attendance_sessions} ats
              LEFT JOIN {attendance_log} al
                     ON ats.id = al.sessionid AND al.studentid = :uid
index 54ff929..e83a161 100644 (file)
@@ -45,6 +45,8 @@
         <FIELD NAME="automarkcompleted" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="statusset" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Which set of statuses to use"/>
         <FIELD NAME="absenteereport" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
+        <FIELD NAME="preventsharedip" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
+        <FIELD NAME="preventsharediptime" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
         <FIELD NAME="caleventid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
@@ -67,6 +69,7 @@
         <FIELD NAME="timetaken" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="When attendance of this student was taken"/>
         <FIELD NAME="takenby" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="remarks" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
+        <FIELD NAME="ipaddress" TYPE="char" LENGTH="45" NOTNULL="false" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance_log"/>
index cf38a89..e306234 100644 (file)
@@ -480,12 +480,36 @@ function xmldb_attendance_upgrade($oldversion=0) {
 
     if ($oldversion < 2018022204) {
         $table = new xmldb_table('attendance');
-        $field = new xmldb_field('showextrauserdetails', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1', 'showsessiondetails');
+        $field = new xmldb_field('showextrauserdetails', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED,
+            XMLDB_NOTNULL, null, '1', 'showsessiondetails');
         if (!$dbman->field_exists($table, $field)) {
             $dbman->add_field($table, $field);
         }
         upgrade_mod_savepoint(true, 2018022204, 'attendance');
     }
 
+    if ($oldversion < 2018050100) {
+        $table = new xmldb_table('attendance_sessions');
+        $field = new xmldb_field('preventsharedip', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED,
+            XMLDB_NOTNULL, null, '0', 'absenteereport');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+        $field = new xmldb_field('preventsharediptime', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
+            null, null, null, 'preventsharedip');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $table = new xmldb_table('attendance_log');
+        $field = new xmldb_field('ipaddress', XMLDB_TYPE_CHAR, '45', null,
+            null, null, '', 'remarks');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        upgrade_mod_savepoint(true, 2018050100, 'attendance');
+    }
+
     return $result;
 }
index 0c753e3..f302116 100644 (file)
@@ -70,6 +70,8 @@ class mod_wsattendance_external extends external_api {
                          'studentscanmark' => new external_value(PARAM_INT, 'Students can mark their own presence.'),
                          'absenteereport' => new external_value(PARAM_INT, 'Session included in absetee reports.'),
                          'autoassignstatus' => new external_value(PARAM_INT, 'Automatically assign a status to students.'),
+                         'preventsharedip' => new external_value(PARAM_INT, 'Prevent students from sharing IP addresses.'),
+                         'preventsharediptime' => new external_value(PARAM_INT, 'Time delay before IP address is allowed again.'),
                          'statusset' => new external_value(PARAM_INT, 'Session statusset.'));
 
         return $session;
index 5a6d625..d66899e 100644 (file)
@@ -203,6 +203,7 @@ $string['eventsdeleted'] = 'Calendar events deleted';
 $string['eventsessionadded'] = 'Session added';
 $string['eventsessiondeleted'] = 'Session deleted';
 $string['eventsessionsimported'] = 'Sessions imported';
+$string['eventsessionipshared'] = 'Attendance self-marking IP conflict';
 $string['eventsessionupdated'] = 'Session updated';
 $string['eventstatusadded'] = 'Status added';
 $string['eventstatusupdated'] = 'Status updated';
@@ -242,6 +243,7 @@ $string['invalidimportfile'] = 'File format is invalid.';
 $string['invalidsessionenddate'] = 'This date can not be earlier than the session date';
 $string['invalidsessionendtime'] = 'The end time must be greater than start time';
 $string['invalidstatus'] = 'You have selected an invalid status, please try again';
+$string['iptimemissing'] = 'Invalid minutes to release';
 $string['jumpto'] = 'Jump to';
 $string['lowgrade'] = 'Low grade';
 $string['maxpossible'] = 'Maximum possible';
@@ -327,6 +329,12 @@ $string['points'] = 'Points';
 $string['pointsallsessions'] = 'Points over all sessions';
 $string['pointssessionscompleted'] = 'Points over taken sessions';
 $string['preferences_desc'] = 'Changes to status sets will affect existing attendance sessions and may affect grading.';
+$string['preventsharedip'] = 'Prevent students sharing IP address';
+$string['preventsharedip_help'] = 'Prevent students from using the same device (identified using IP address) to take attendance for other students.';
+$string['preventsharediptime'] = 'Time to allow re-use of IP address (minutes)';
+$string['preventsharediptime_help'] = 'Allow an IP address to be re-used for taking attendance in this session after this time has elapsed.';
+$string['preventsharedipminutes'] = '(minutes to release IP)';
+$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.<br><br>Please change the session date or just click the "Add session" button again to confirm?';
 $string['processingfile'] = 'Processing file';
 $string['randompassword'] = 'Random password';
index d725447..c2c42c6 100644 (file)
@@ -425,6 +425,7 @@ function attendance_random_string($length=6) {
  * @return boolean
  */
 function attendance_can_student_mark($sess) {
+    global $DB, $USER, $OUTPUT;
     $canmark = false;
     $attconfig = get_config('attendance');
     if (!empty($attconfig->studentscanmark) && !empty($sess->studentscanmark)) {
@@ -440,6 +441,31 @@ function attendance_can_student_mark($sess) {
             }
         }
     }
+    // Check if another student has marked attendance from this IP address recently.
+    if ($canmark && !empty($sess->preventsharedip)) {
+        $time = time() - ($sess->preventsharediptime * 60);
+        $sql = 'sessionid = ? AND studentid <> ? AND timetaken > ? AND ipaddress = ?';
+        $params = array($sess->id, $USER->id, $time, getremoteaddr());
+        $record = $DB->get_record_select('attendance_log', $sql, $params);
+        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;
+        }
+    }
     return $canmark;
 }
 
@@ -601,10 +627,18 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st
                         } else if (!empty($formdata->studentpassword)) {
                             $sess->studentpassword = $formdata->studentpassword;
                         }
+                        if (!empty($formdata->preventsharedip)) {
+                            $sess->preventsharedip = $formdata->preventsharedip;
+                        }
+                        if (!empty($formdata->preventsharediptime)) {
+                            $sess->preventsharediptime = $formdata->preventsharediptime;
+                        }
                     } else {
                         $sess->subnet = '';
                         $sess->automark = 0;
                         $sess->automarkcompleted = 0;
+                        $sess->preventsharedip = 0;
+                        $sess->preventsharediptime = '';
                     }
                     $sess->statusset = $formdata->statusset;
 
@@ -652,6 +686,12 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st
             if (!empty($formdata->automark)) {
                 $sess->automark = $formdata->automark;
             }
+            if (!empty($formdata->preventsharedip)) {
+                $sess->preventsharedip = $formdata->preventsharedip;
+            }
+            if (!empty($formdata->preventsharediptime)) {
+                $sess->preventsharediptime = $formdata->preventsharediptime;
+            }
         }
         $sess->statusset = $formdata->statusset;
 
index dd3d102..3b2e2c4 100644 (file)
@@ -122,6 +122,12 @@ if ($ADMIN->fulltree) {
     $settings->add(new admin_setting_configcheckbox('attendance/autoassignstatus',
         get_string('autoassignstatus', 'attendance'), '', 0));
 
+    $settings->add(new admin_setting_configcheckbox('attendance/preventsharedip',
+        get_string('preventsharedip', 'attendance'), '', 0));
+
+    $settings->add(new admin_setting_configtext('attendance/preventsharediptime',
+        get_string('preventsharediptime', 'attendance'), get_string('preventsharediptime_help', 'attendance'), '', PARAM_RAW));
+
     $name = new lang_string('defaultwarningsettings', 'mod_attendance');
     $description = new lang_string('defaultwarningsettings_help', 'mod_attendance');
     $settings->add(new admin_setting_heading('defaultwarningsettings', $name, $description));
index fea6a77..a96e5c6 100644 (file)
@@ -72,7 +72,9 @@ class mod_attendance_update_form extends moodleform {
                 'subnet' => $sess->subnet,
                 'automark' => $sess->automark,
                 'absenteereport' => $sess->absenteereport,
-                'automarkcompleted' => 0);
+                'automarkcompleted' => 0,
+                'preventsharedip' => $sess->preventsharedip,
+                'preventsharediptime' => $sess->preventsharediptime);
         if ($sess->subnet == $attendancesubnet) {
             $data['usedefaultsubnet'] = 1;
         } else {
@@ -156,6 +158,19 @@ class mod_attendance_update_form extends moodleform {
             $mform->addElement('hidden', 'automarkcompleted', '0');
             $mform->settype('automarkcompleted', PARAM_INT);
 
+            $mgroup3 = array();
+            $mgroup3[] = & $mform->createElement('checkbox', 'preventsharedip', '');
+            $mgroup3[] = & $mform->createElement('text', 'preventsharediptime',
+                get_string('preventsharediptime', 'attendance'), '', 'test');
+            $mgroup3[] = & $mform->createElement('static', 'preventsharediptimedesc', '',
+                get_string('preventsharedipminutes', 'attendance'));
+            $mform->addGroup($mgroup3, 'preventsharedgroup',
+                get_string('preventsharedip', 'attendance'), array(' '), false);
+            $mform->addHelpButton('preventsharedgroup', 'preventsharedip', 'attendance');
+            $mform->setAdvanced('preventsharedgroup');
+            $mform->setType('preventsharediptime', PARAM_INT);
+            $mform->hideif('preventsharedgroup', 'studentscanmark', 'notchecked');
+            $mform->disabledIf('preventsharediptime', 'preventsharedip', 'notchecked');
         } else {
             $mform->addElement('hidden', 'studentscanmark', '0');
             $mform->settype('studentscanmark', PARAM_INT);
@@ -202,6 +217,11 @@ class mod_attendance_update_form extends moodleform {
             }
         }
 
+        if (!empty($data['studentscanmark']) && !empty($data['preventsharedip']) &&
+                empty($data['preventsharediptime'])) {
+            $errors['preventsharedgroup'] = get_string('iptimemissing', 'attendance');
+
+        }
         return $errors;
     }
 }
index e371694..91633b5 100644 (file)
@@ -23,7 +23,7 @@
  */
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version  = 2018032200;
+$plugin->version  = 2018050101;
 $plugin->requires = 2017102700; // Requires 3.4.
 $plugin->release = '3.4.4';
 $plugin->maturity  = MATURITY_ALPHA;