diff --git a/db/access.php b/db/access.php index 90ae65a..b7580a7 100644 --- a/db/access.php +++ b/db/access.php @@ -117,5 +117,18 @@ $capabilities = array( 'archetypes' => array( 'student' => CAP_ALLOW ) - ) + ), + + // Allow teachers to manage temporary users. + 'mod/attendance:managetemporaryusers' => array( + 'riskbitmask' => RISK_DATALOSS, + + 'captype' => 'write', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), ); diff --git a/db/install.xml b/db/install.xml index ca4e6b0..65e43d5 100644 --- a/db/install.xml +++ b/db/install.xml @@ -80,5 +80,22 @@ + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/db/upgrade.php b/db/upgrade.php index 12a0b95..8ec0802 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -85,5 +85,41 @@ function xmldb_attendance_upgrade($oldversion=0) { upgrade_plugin_savepoint($result, 2014112001, 'mod', 'attendance'); } + if ($oldversion < 2015040501) { + // Define table attendance_tempusers to be created. + $table = new xmldb_table('attendance_tempusers'); + + // Adding fields to table attendance_tempusers. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('studentid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + $table->add_field('fullname', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('email', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('created', XMLDB_TYPE_INTEGER, '10', null, null, null, null); + + // Adding keys to table attendance_tempusers. + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + + // Conditionally launch create table for attendance_tempusers. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Conditionally launch add index courseid. + $index = new xmldb_index('courseid', XMLDB_INDEX_NOTUNIQUE, array('courseid')); + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + + // Conditionally launch add index studentid. + $index = new xmldb_index('studentid', XMLDB_INDEX_UNIQUE, array('studentid')); + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + + // Attendance savepoint reached. + upgrade_mod_savepoint(true, 2015040501, 'attendance'); + } + return $result; } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 3dcb964..5ac7897 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -24,6 +24,7 @@ $string['attendance:addinstance'] = 'Add a new attendance activity'; $string['Aacronym'] = 'A'; +$string['adduser'] = 'Add user'; $string['Afull'] = 'Absent'; $string['Eacronym'] = 'E'; $string['Efull'] = 'Excused'; @@ -51,6 +52,7 @@ $string['attendance:changepreferences'] = 'Changing Preferences'; $string['attendance:changeattendances'] = 'Changing Attendances'; $string['attendance:export'] = 'Export Reports'; $string['attendance:manageattendances'] = 'Manage Attendances'; +$string['attendance:managetemporaryusers'] = 'Manage temporary users'; $string['attendance:takeattendances'] = 'Taking Attendances'; $string['attendance:view'] = 'Viewing Attendances'; $string['attendance:viewreports'] = 'Viewing Reports'; @@ -69,6 +71,7 @@ $string['column'] = 'column'; $string['columns'] = 'columns'; $string['commonsession'] = 'Common'; $string['commonsessions'] = 'Common'; +$string['confirmdeleteuser'] = 'Are you sure you want to delete user \'{$a->fullname}\' ({$a->email})?
All of their attendance records will be permanently deleted.'; $string['countofselected'] = 'Count of selected'; $string['copyfrom'] = 'Copy attendance data from'; $string['createmultiplesessions'] = 'Create multiple sessions'; @@ -88,6 +91,7 @@ $string['deletelogs'] = 'Delete attendance data'; $string['deleteselected'] = 'Delete selected'; $string['deletesession'] = 'Delete session'; $string['deletesessions'] = 'Delete all sessions'; +$string['deleteuser'] = 'Delete user'; $string['deletingsession'] = 'Deleting session for the course'; $string['deletingstatus'] = 'Deleting status for the course'; $string['description'] = 'Description'; @@ -99,6 +103,7 @@ $string['downloadtext'] = 'Download in text format'; $string['donotusepaging'] = 'Do not use paging'; $string['duration'] = 'Duration'; $string['editsession'] = 'Edit Session'; +$string['edituser'] = 'Edit user'; $string['endtime'] = 'Session end time'; $string['endofperiod'] = 'End of period'; $string['enrolmentend'] = 'User enrolment ends {$a}'; @@ -125,6 +130,7 @@ $string['indetail'] = 'In detail...'; $string['invalidsessionenddate'] = 'The session end date can not be earlier than the session start date'; $string['invalidaction'] = 'You must select an action'; $string['jumpto'] = 'Jump to'; +$string['mergeuser'] = 'Merge user'; $string['modulename'] = 'Attendance'; $string['modulename_help'] = 'The attendance activity module enables a teacher to take attendance during class and students to view their own attendance record. @@ -151,6 +157,7 @@ $string['nosessionsselected'] = 'No sessions selected'; $string['notfound'] = 'Attendance activity not found in this course!'; $string['noupgradefromthisversion'] = 'The Attendance module cannot upgrade from the version of attforblock you have installed. - please delete attforblock or upgrade it to the latest version before isntalling the new attendance module'; $string['olddate'] = 'Old date'; +$string['participant'] = 'Participant'; $string['period'] = 'Frequency'; $string['pluginname'] = 'Attendance'; $string['pluginadministration'] = 'Attendance administration'; @@ -158,6 +165,41 @@ $string['remark'] = 'Remark for: {$a}'; $string['remarks'] = 'Remarks'; $string['report'] = 'Report'; $string['required'] = 'Required*'; +$string['requiredentries'] = ' Temporary records overwrite participant attendance records'; +$string['requiredentry'] = ' Temporary user merge help guide'; +$string['requiredentry_help'] = '

Attendance

+

Merge Accounts

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Moodle UserTemporary UserAction
Attendance dataAttendance dataTemporary user will override Moodle user
No attendance dataAttendance dataTemporary user attendance will be transfered to Moodle user
Attendance dataNo attendance dataTemporary user will be deleted
No attendance dataNo attendance dataTemporary user will be deleted
+ +

+

Temporay user will be deleted in all cases after merge action

'; $string['resetdescription'] = 'Remember that deleting attendance data will erase information from database. You can just hide older sessions having changed start date of course!'; $string['resetstatuses'] = 'Reset statuses to default'; $string['restoredefaults'] = 'Restore defaults'; @@ -206,9 +248,22 @@ $string['strftimehm'] = '%H:%M'; // Line added to allow display of time. $string['strftimeshortdate'] = '%d.%m.%Y'; $string['studentid'] = 'Student ID'; $string['takeattendance'] = 'Take attendance'; +$string['tempaddform'] = 'Add temporary user'; +$string['tempexists'] = 'There is already a temporary user with this email address'; +$string['tempusers'] = 'Temporary users'; $string['thiscourse'] = 'This course'; $string['tablerenamefailed'] = 'Rename of old attforblock table to attendance failed'; +$string['tactions'] = 'Action'; +$string['tcreated'] = 'Created'; +$string['temptable'] = 'List of temporary users'; +$string['tempuser'] = 'Temporary user'; +$string['tempusersedit'] = 'Edit temporary user'; +$string['tempuserslist'] = 'Temporary users'; +$string['tempusermerge'] = 'Merge temporary user'; +$string['tuseremail'] = 'Email'; +$string['tusername'] = 'Full name'; $string['update'] = 'Update'; +$string['userexists'] = 'There is already a real user with this email address'; $string['variable'] = 'variable'; $string['variablesupdated'] = 'Variables successfully updated'; $string['versionforprinting'] = 'version for printing'; diff --git a/locallib.php b/locallib.php index f003c73..09c8b89 100644 --- a/locallib.php +++ b/locallib.php @@ -44,6 +44,7 @@ class attendance_permissions { private $cantake; private $canchange; private $canmanage; + private $canmanagetemp; // Can manage temporary users. private $canchangepreferences; private $canexport; private $canbelisted; @@ -122,6 +123,18 @@ class attendance_permissions { public function require_manage_capability() { require_capability('mod/attendance:manageattendances', $this->context); } + + // Check to see if the user can manage temporary users. + public function can_managetemp() { + if (is_null($this->canmanagetemp)) { + $this->canmanagetemp = has_capability('mod/attendance:managetemporaryusers', $this->context); + } + return $this->canmanagetemp; + } + + public function require_managetemp_capability() { + require_capability('mod/attendance:managetemporaryusers', $this->context); + } public function can_change_preferences() { if (is_null($this->canchangepreferences)) { @@ -588,7 +601,7 @@ class attendance { $this->cm = $cm; $this->course = $course; if (is_null($context)) { - $this->context = context_module::instance_by_id($this->cm->id); + $this->context = context_module::instance($this->cm->id); } else { $this->context = $context; } @@ -738,6 +751,42 @@ class attendance { return new moodle_url('/mod/attendance/manage.php', $params); } + /** + * @param array $params optional + * @return moodle_url of tempusers.php for attendance instance + */ + public function url_managetemp($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempusers.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempdelete.php for attendance instance + */ + public function url_tempdelete($params=array()) { + $params = array_merge(array('id' => $this->cm->id, 'action' => 'delete'), $params); + return new moodle_url('/mod/attendance/tempedit.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempedit.php for attendance instance + */ + public function url_tempedit($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempedit.php', $params); + } + + /** + * @param array $params optional + * @return moodle_url of tempedit.php for attendance instance + */ + public function url_tempmerge($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); + return new moodle_url('/mod/attendance/tempmerge.php', $params); + } + /** * @return moodle_url of sessions.php for attendance instance */ @@ -1058,17 +1107,53 @@ class attendance { $users[$user->id]->enrolmentstatus = $enrolments[$user->id]->status; $users[$user->id]->enrolmentstart = $enrolments[$user->id]->mintime; $users[$user->id]->enrolmentend = $enrolments[$user->id]->maxtime; + $users[$user->id]->type = 'standard'; // Mark as a standard (not a temporary) user. } } + // Add the 'temporary' users to this list. + $tempusers = $DB->get_records('attendance_tempusers', array('courseid' => $this->course->id)); + foreach ($tempusers as $tempuser) { + $users[] = self::tempuser_to_user($tempuser); + } + return $users; } + // Convert a tempuser record into a user object. + protected static function tempuser_to_user($tempuser) { + $ret = (object)array( + 'id' => $tempuser->studentid, + 'firstname' => $tempuser->fullname, + 'email' => $tempuser->email, + 'username' => '', + 'enrolmentstatus' => 0, + 'enrolmentstart' => 0, + 'enrolmentend' => 0, + 'picture' => 0, + 'type' => 'temporary', + ); + foreach (get_all_user_name_fields() as $namefield) { + if (!isset($ret->$namefield)) { + $ret->$namefield = ''; + } + } + return $ret; + } + public function get_user($userid) { global $DB; $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); + // Look for 'temporary' users and return their details from the attendance_tempusers table. + if ($user->idnumber == 'tempghost') { + $tempuser = $DB->get_record('attendance_tempusers', array('studentid' => $userid), '*', MUST_EXIST); + return self::tempuser_to_user($tempuser); + } + + $user->type = 'standard'; + // CONTRIB-4868 $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; $maxtime = 'MAX(ue.timeend)'; @@ -1516,8 +1601,34 @@ class attendance { $event->trigger(); } + /** + * Check if the email address is already in use by either another temporary user, + * or a real user. + * + * @param string $email the address to check for + * @param int $tempuserid optional the ID of the temporary user (to avoid matching against themself) + * @return null|string the error message to display, null if there is no error + */ + public static function check_existing_email($email, $tempuserid = 0) { + global $DB; + + if (empty($email)) { + return null; // Fine to create temporary users without an email address. + } + if ($tempuser = $DB->get_record('attendance_tempusers', array('email' => $email), 'id')) { + if ($tempuser->id != $tempuserid) { + return get_string('tempexists', 'attendance'); + } + } + if ($DB->record_exists('user', array('email' => $email))) { + return get_string('userexists', 'attendance'); + } + + return null; + } } + function att_get_statuses($attid, $onlyvisible=true) { global $DB; diff --git a/pix/ghost.png b/pix/ghost.png new file mode 100644 index 0000000..b199efa Binary files /dev/null and b/pix/ghost.png differ diff --git a/renderables.php b/renderables.php index 5dd2223..ac9b37e 100644 --- a/renderables.php +++ b/renderables.php @@ -42,6 +42,7 @@ class attendance_tabs implements renderable { const TAB_REPORT = 3; const TAB_EXPORT = 4; const TAB_PREFERENCES = 5; + const TAB_TEMPORARYUSERS = 6; // Tab for managing temporary users. public $currenttab; @@ -92,6 +93,10 @@ class attendance_tabs implements renderable { $toprow[] = new tabobject(self::TAB_PREFERENCES, $this->att->url_preferences()->out(), get_string('settings', 'attendance')); } + if ($this->att->perm->can_managetemp()) { + $toprow[] = new tabobject(self::TAB_TEMPORARYUSERS, $this->att->url_managetemp()->out(), + get_string('tempusers', 'attendance')); + } return array($toprow); } diff --git a/renderer.php b/renderer.php index ac4a378..46b4617 100644 --- a/renderer.php +++ b/renderer.php @@ -495,7 +495,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row = new html_table_row(); $row->cells[] = $i; $fullname = html_writer::link($takedata->url_view(array('studentid' => $user->id)), fullname($user)); - $fullname = $this->output->user_picture($user).$fullname; + $fullname = $this->user_picture($user).$fullname; // Show different picture if it is a temporary user. $ucdata = $this->construct_take_user_controls($takedata, $user); if (array_key_exists('warning', $ucdata)) { @@ -540,7 +540,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $i = 0; $row = new html_table_row(); foreach ($takedata->users as $user) { - $celltext = $this->output->user_picture($user, array('size' => 100)); + $celltext = $this->user_picture($user, array('size' => 100)); // Show different picture if it is a temporary user. $celltext .= html_writer::empty_tag('br'); $fullname = html_writer::link($takedata->url_view(array('studentid' => $user->id)), fullname($user)); $celltext .= html_writer::tag('span', $fullname, array('class' => 'fullname')); @@ -656,7 +656,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $table->attributes['class'] = 'userinfobox'; $table->colclasses = array('left side', ''); - $table->data[0][] = $this->output->user_picture($userdata->user, array('size' => 100)); + $table->data[0][] = $this->user_picture($userdata->user, array('size' => 100)); // Show different picture if it is a temporary user. $table->data[0][] = $this->construct_user_data($userdata); $o .= html_writer::table($table); @@ -671,9 +671,12 @@ class mod_attendance_renderer extends plugin_renderer_base { $userdata->url()->out(true, array('mode' => att_view_page_params::MODE_THIS_COURSE)), get_string('thiscourse', 'attendance')); - $tabs[] = new tabobject(att_view_page_params::MODE_ALL_COURSES, - $userdata->url()->out(true, array('mode' => att_view_page_params::MODE_ALL_COURSES)), - get_string('allcourses', 'attendance')); + // Skip the 'all courses' tab for 'temporary' users. + if ($userdata->user->type == 'standard') { + $tabs[] = new tabobject(att_view_page_params::MODE_ALL_COURSES, + $userdata->url()->out(true, array('mode' => att_view_page_params::MODE_ALL_COURSES)), + get_string('allcourses', 'attendance')); + } return print_tabs(array($tabs), $userdata->pageparams->mode, null, null, true); } @@ -842,7 +845,7 @@ class mod_attendance_renderer extends plugin_renderer_base { foreach ($reportdata->users as $user) { $row = new html_table_row(); - $row->cells[] = $this->output->user_picture($user); + $row->cells[] = $this->user_picture($user); // Show different picture if it is a temporary user. $row->cells[] = html_writer::link($reportdata->url_view(array('studentid' => $user->id)), fullname($user)); $cellsgenerator = new user_sessions_cells_html_generator($reportdata, $user); $row->cells = array_merge($row->cells, $cellsgenerator->get_cells()); @@ -1006,4 +1009,20 @@ class mod_attendance_renderer extends plugin_renderer_base { return html_writer::empty_tag('input', $attributes); } + // Show different picture if it is a temporary user. + protected function user_picture($user, array $opts = null) { + if ($user->type == 'temporary') { + $attrib = array( + 'width' => '35', + 'height' => '35', + 'class' => 'userpicture defaultuserpic', + ); + if (isset($opts['size'])) { + $attrib['width'] = $attrib['height'] = $opts['size']; + } + return $this->output->pix_icon('ghost', '', 'mod_attendance', $attrib); + } + + return $this->output->user_picture($user, $opts); + } } diff --git a/temp_form.php b/temp_form.php new file mode 100644 index 0000000..1fd5eec --- /dev/null +++ b/temp_form.php @@ -0,0 +1,68 @@ +. + +/** + * Form for creating temporary users. + * + * @package mod_attendance + * @copyright 2013 Davo Smith, Synergy Learning + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/formslib.php'); + +class temp_form extends moodleform { + function definition() { + + $mform = $this->_form; + + $mform->addElement('hidden', 'id', 0); + $mform->setType('id', PARAM_INT); + + $mform->addElement('header', 'attheader', get_string('tempaddform', 'attendance')); + $mform->addElement('text', 'tname', get_string('tusername', 'attendance')); + $mform->addRule('tname', 'Required', 'required', null, 'client'); + $mform->setType('tname', PARAM_TEXT); + + $mform->addElement('text', 'temail', get_string('tuseremail', 'attendance')); + $mform->addRule('temail', 'Email', 'email', null, 'client'); + $mform->addRule('temail', '', 'callback', null, 'server'); + $mform->setType('temail', PARAM_EMAIL); + + $mform->addElement('submit', 'submitbutton', get_string('adduser', 'attendance')); + $mform->closeHeaderBefore('submit'); + } + + function definition_after_data() { + $mform = $this->_form; + $mform->applyFilter('tname', 'trim'); + } + + function validation($data, $files) { + $errors = parent::validation($data, $files); + + if ($err = attendance::check_existing_email($data['temail'])) { + $errors['temail'] = $err; + } + + return $errors; + } + +} + diff --git a/tempedit.php b/tempedit.php new file mode 100644 index 0000000..15349df --- /dev/null +++ b/tempedit.php @@ -0,0 +1,115 @@ +. + +/** + * Attendance tempedit + * + * @package mod_attendance + * @copyright 2013 Davo Smith, Synergy Learning + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__).'/../../config.php'); + +global $CFG, $DB, $PAGE, $OUTPUT; +require_once($CFG->dirroot.'/mod/attendance/locallib.php'); +require_once($CFG->dirroot.'/mod/attendance/tempedit_form.php'); + +$id = required_param('id', PARAM_INT); +$userid = required_param('userid', PARAM_INT); +$action = optional_param('action', null, PARAM_ALPHA); + +$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); +$tempuser = $DB->get_record('attendance_tempusers', array('id' => $userid), '*', MUST_EXIST); + +$att = new attendance($att, $cm, $course); + +$params = array('userid' => $tempuser->id); +if ($action) { + $params['action'] = $action; +} +$PAGE->set_url($att->url_tempedit($params)); + +require_login($course, true, $cm); + +$att->perm->require_managetemp_capability(); + +$PAGE->set_title($course->shortname.": ".$att->name.' - '.get_string('tempusersedit', 'attendance')); +$PAGE->set_heading($course->fullname); +$PAGE->set_cacheable(true); +$PAGE->navbar->add(get_string('tempusersedit', 'attendance')); + +/** @var mod_attendance_renderer $output */ +$output = $PAGE->get_renderer('mod_attendance'); + +if ($action == 'delete') { + if (optional_param('confirm', false, PARAM_BOOL)) { + require_sesskey(); + + // Remove the user from the grades table, the attendance log and the tempusers table. + $DB->delete_records('grade_grades', array('userid' => $tempuser->studentid)); + $DB->delete_records('attendance_log', array('studentid' => $tempuser->studentid)); + $DB->delete_records('attendance_tempusers', array('id' => $tempuser->id)); + + redirect($att->url_managetemp()); + } else { + + $info = (object)array( + 'fullname' => $tempuser->fullname, + 'email' => $tempuser->email, + ); + $msg = get_string('confirmdeleteuser', 'attendance', $info); + $continue = new moodle_url($PAGE->url, array('confirm' => 1, 'sesskey' => sesskey())); + + echo $output->header(); + echo $output->confirm($msg, $continue, $att->url_managetemp()); + echo $output->footer(); + + die(); + } +} + +$formdata = new stdClass(); +$formdata->id = $cm->id; +$formdata->tname = $tempuser->fullname; +$formdata->userid = $tempuser->id; +$formdata->temail = $tempuser->email; + +$mform = new tempedit_form(); +$mform->set_data($formdata); + +if ($mform->is_cancelled()) { + redirect($att->url_managetemp()); +} else if ($tempuser = $mform->get_data()) { + global $DB; + $updateuser = new stdClass(); + $updateuser->id = $tempuser->userid; + $updateuser->fullname = $tempuser->tname; + $updateuser->email = $tempuser->temail; + $DB->update_record('attendance_tempusers', $updateuser); + redirect($att->url_managetemp()); +} + +$tabs = new attendance_tabs($att, attendance_tabs::TAB_TEMPORARYUSERS); + +echo $output->header(); +echo $output->heading(get_string('tempusersedit', 'attendance').' : '.$course->fullname); +echo $output->render($tabs); +$mform->display(); +echo $output->footer($course); + diff --git a/tempedit_form.php b/tempedit_form.php new file mode 100644 index 0000000..362b196 --- /dev/null +++ b/tempedit_form.php @@ -0,0 +1,71 @@ +. + +/** + * Form for editing temporary users. + * + * @package mod_attendance + * @copyright 2013 Davo Smith, Synergy Learning + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/formslib.php'); + +class tempedit_form extends moodleform { + + function definition() { + + $mform = $this->_form; + + $mform->addElement('hidden', 'userid', 0); + $mform->setType('userid', PARAM_INT); + $mform->addElement('hidden', 'id', 0); + $mform->setType('id', PARAM_INT); + + $mform->addElement('header', 'attheader', get_string('tempusersedit', 'attendance')); + $mform->addElement('text', 'tname', get_string('tusername', 'attendance')); + $mform->addRule('tname', 'Required', 'required', null, 'client'); + $mform->setType('tname', PARAM_TEXT); + + $mform->addElement('text', 'temail', get_string('tuseremail', 'attendance')); + $mform->addRule('temail', 'Email', 'email', null, 'client'); + $mform->setType('temail', PARAM_EMAIL); + + $buttonarray = array( + $mform->createElement('submit', 'submitbutton', get_string('edituser', 'attendance')), + $mform->createElement('cancel'), + ); + $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); + $mform->closeHeaderBefore('submit'); + } + + function definition_after_data() { + $mform = $this->_form; + $mform->applyFilter('tname', 'trim'); + } + + function validation($data, $files) { + $errors = parent::validation($data, $files); + + if ($err = attendance::check_existing_email($data['temail'], $data['userid'])) { + $errors['temail'] = $err; + } + return $errors; + } +} diff --git a/tempmerge.php b/tempmerge.php new file mode 100644 index 0000000..216d07a --- /dev/null +++ b/tempmerge.php @@ -0,0 +1,104 @@ +. + +/** + * Merge temporary user with real user. + * + * @package mod_attendance + * @copyright 2013 Davo Smith, Synergy Learning + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__).'/../../config.php'); + +global $CFG, $DB, $PAGE, $OUTPUT; +require_once($CFG->dirroot.'/mod/attendance/locallib.php'); +require_once($CFG->dirroot.'/mod/attendance/tempmerge_form.php'); + +$id = required_param('id', PARAM_INT); +$userid = required_param('userid', PARAM_INT); + +$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); +$tempuser = $DB->get_record('attendance_tempusers', array('id' => $userid), '*', MUST_EXIST); + +$att = new attendance($att, $cm, $course); +$params = array('userid' => $tempuser->id); +$PAGE->set_url($att->url_tempmerge($params)); + +require_login($course, true, $cm); + +$PAGE->set_title($course->shortname.": ".$att->name.' - '.get_string('tempusermerge', 'attendance')); +$PAGE->set_heading($course->fullname); +$PAGE->set_cacheable(true); +$PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance')); +$PAGE->navbar->add(get_string('tempusermerge', 'attendance')); + +$formdata = (object)array( + 'id' => $cm->id, + 'userid' => $tempuser->id, +); + +$custom = array( + 'description' => format_string($tempuser->fullname).' ('.format_string($tempuser->email).')', +); +$mform = new tempmerge_form(null, $custom); +$mform->set_data($formdata); + +if ($mform->is_cancelled()) { + redirect($att->url_managetemp()); + +} else if ($data = $mform->get_data()) { + + $sql = "SELECT s.id, lr.id AS reallogid, lt.id AS templogid + FROM {attendance_sessions} s + LEFT JOIN {attendance_log} lr ON lr.sessionid = s.id AND lr.studentid = :realuserid + LEFT JOIN {attendance_log} lt ON lt.sessionid = s.id AND lt.studentid = :tempuserid + WHERE s.attendanceid = :attendanceid AND lt.id IS NOT NULL + ORDER BY s.id"; + $params = array( + 'realuserid' => $data->participant, + 'tempuserid' => $tempuser->studentid, + 'attendanceid' => $att->id, + ); + $logs = $DB->get_recordset_sql($sql, $params); + + foreach ($logs as $log) { + if (!is_null($log->reallogid)) { + // Remove the existing attendance for the real user for this session. + $DB->delete_records('attendance_log', array('id' => $log->reallogid)); + } + // Adjust the 'temp user' attendance record to point at the real user. + $DB->set_field('attendance_log', 'studentid', $data->participant, array('id' => $log->templogid)); + } + + // Delete the temp user. + $DB->delete_records('attendance_tempusers', array('id' => $tempuser->id)); + $att->update_users_grade(array($data->participant)); // Update the gradebook after the merge. + + redirect($att->url_managetemp()); +} + +/** @var mod_attendance_renderer $output */ +$output = $PAGE->get_renderer('mod_attendance'); +$tabs = new attendance_tabs($att, attendance_tabs::TAB_TEMPORARYUSERS); + +echo $output->header(); +echo $output->heading(get_string('tempusermerge', 'attendance').' : '.$course->fullname); +echo $output->render($tabs); +$mform->display(); +echo $output->footer($course); \ No newline at end of file diff --git a/tempmerge_form.php b/tempmerge_form.php new file mode 100644 index 0000000..5e5de29 --- /dev/null +++ b/tempmerge_form.php @@ -0,0 +1,40 @@ +libdir.'/formslib.php'); + +class tempmerge_form extends moodleform { + + function definition() { + global $COURSE; + + $context = context_course::instance($COURSE->id); + $namefields = get_all_user_name_fields(true, 'u'); + $students = get_enrolled_users($context, 'mod/attendance:canbelisted', 0, 'u.id,'.$namefields.',u.email', + 'u.lastname, u.firstname', 0, 0, true); + $partarray = array(); + foreach ($students as $student) { + $partarray[$student->id] = fullname($student).' ('.$student->email.')'; + } + + $mform = $this->_form; + $description = $this->_customdata['description']; + + $mform->addElement('hidden', 'id', 0); + $mform->setType('id', PARAM_INT); + $mform->addElement('hidden', 'userid', 0); + $mform->setType('userid', PARAM_INT); + + $mform->addElement('header', 'attheader', get_string('tempusermerge', 'attendance')); + $mform->addElement('static', 'description', get_string('tempuser', 'attendance'), $description); + + $mform->addElement('select', 'participant', get_string('participant', 'attendance'), $partarray); + + $mform->addElement('static', 'requiredentries', '', get_string('requiredentries', 'attendance')); + $mform->addHelpButton('requiredentries', 'requiredentry', 'attendance'); + + $this->add_action_buttons(true, get_string('mergeuser', 'attendance')); + } +} \ No newline at end of file diff --git a/tempusers.php b/tempusers.php new file mode 100644 index 0000000..df1733f --- /dev/null +++ b/tempusers.php @@ -0,0 +1,127 @@ +. + +/** + * Temporary user management. + * + * @package mod_attendance + * @copyright 2013 Davo Smith, Synergy Learning + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__).'/../../config.php'); +global $CFG, $DB, $OUTPUT, $PAGE, $COURSE; +require_once($CFG->dirroot.'/mod/attendance/locallib.php'); +require_once($CFG->dirroot.'/mod/attendance/temp_form.php'); + +$id = required_param('id', PARAM_INT); + +$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); + +$att = new attendance($att, $cm, $course); +$PAGE->set_url($att->url_managetemp()); + +require_login($course, true, $cm); + +$att->perm->require_managetemp_capability(); + +$PAGE->set_title($course->shortname.": ".$att->name.' - '.get_string('tempusers', 'attendance')); +$PAGE->set_heading($course->fullname); +$PAGE->set_cacheable(true); +$PAGE->navbar->add(get_string('tempusers', 'attendance')); + +/** @var mod_attendance_renderer $output */ +$output = $PAGE->get_renderer('mod_attendance'); +$tabs = new attendance_tabs($att, attendance_tabs::TAB_TEMPORARYUSERS); + +$formdata = (object)array( + 'id' => $cm->id, +); +$mform = new temp_form(); +$mform->set_data($formdata); + +if ($data = $mform->get_data()) { + // Create temp user in main user table. + $user = new stdClass(); + $user->auth = 'manual'; + $user->confirmed = 1; + $user->deleted = 1; + $user->email = time().'@ghost.user.de'; + $user->username = time().'@ghost.user.de'; + $user->idnumber = 'tempghost'; + $studentid = $DB->insert_record('user', $user); + + // Create the temporary user record. + $newtempuser = new stdClass(); + $newtempuser->fullname = $data->tname; + $newtempuser->courseid = $COURSE->id; + $newtempuser->email = $data->temail; + $newtempuser->created = time(); + $newtempuser->studentid = $studentid; + $DB->insert_record('attendance_tempusers', $newtempuser); + + redirect($att->url_managetemp()); +} + +/// Output starts here +echo $output->header(); +echo $output->heading(get_string('tempusers', 'attendance').' : '.$course->fullname); +echo $output->render($tabs); +$mform->display(); + +$tempusers = $DB->get_records('attendance_tempusers', array('courseid' => $course->id), 'fullname, email'); + +echo '
'; +echo '

'.get_string('tempuserslist', 'attendance').'

'; +if ($tempusers) { + print_tempusers($tempusers, $att); +} +echo '
'; +echo $output->footer($course); + +function print_tempusers($tempusers, attendance $att) { + echo '

'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + $even = false; // used to colour rows + foreach ($tempusers as $tempuser) { + if ($even) { + echo ''; + } else { + echo ''; + } + $even = !$even; + echo ''; + echo ''; + echo ''; + $params = array('userid' => $tempuser->id); + $editlink = html_writer::link($att->url_tempedit($params), get_string('edituser', 'attendance')); + $deletelink = html_writer::link($att->url_tempdelete($params), get_string('deleteuser', 'attendance')); + $mergelink = html_writer::link($att->url_tempmerge($params), get_string('mergeuser', 'attendance')); + echo ''; + echo ''; + } + echo '
'.get_string('tusername', 'attendance').''.get_string('tuseremail', 'attendance').''.get_string('tcreated', 'attendance').''.get_string('tactions', 'attendance').'
'.format_string($tempuser->fullname).''.format_string($tempuser->email).''.userdate($tempuser->created, get_string('strftimedatetime')).''.$editlink.' | '.$deletelink.' | '.$mergelink.'
'; +} + + diff --git a/tests/behat/extra_features.feature b/tests/behat/extra_features.feature new file mode 100644 index 0000000..6c03d2b --- /dev/null +++ b/tests/behat/extra_features.feature @@ -0,0 +1,112 @@ +@mod @mod_attendance @javascript +Feature: Test the various new features in the attendance module + + Background: + Given the following "courses" exist: + | fullname | shortname | + | Course 1 | C1 | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + | student3 | Student | 3 | student3@example.com | + | student4 | Student | 4 | student4@example.com | + | student5 | Student | 5 | student5@example.com | + And the following "course enrolments" exist: + | course | user | role | + | C1 | teacher1 | editingteacher | + | C1 | student1 | student | + | C1 | student2 | student | + | C1 | student3 | student | + And I log in as "teacher1" + And I follow "Course 1" + And I turn editing mode on + And I add a "Attendance" to section "1" and I fill the form with: + | Name | Test attendance | + And I log out + + Scenario: A teacher can create and update temporary users + Given I log in as "teacher1" + And I follow "Course 1" + And I follow "Test attendance" + And I follow "Temporary users" + + When I set the following fields to these values: + | Full name | Temporary user 1 | + | Email | | + And I press "Add user" + And I set the following fields to these values: + | Full name | Temporary user test 2 | + | Email | tempuser2test@example.com | + And I press "Add user" + Then I should see "Temporary user 1" + And "tempuser2test@example.com" "text" should exist in the "Temporary user test 2" "table_row" + + When I click on "Edit user" "link" in the "Temporary user test 2" "table_row" + And the following fields match these values: + | Full name | Temporary user test 2 | + | Email | tempuser2test@example.com | + And I set the following fields to these values: + | Full name | Temporary user 2 | + | Email | tempuser2@example.com | + And I press "Edit user" + Then "tempuser2@example.com" "text" should exist in the "Temporary user 2" "table_row" + + When I click on "Delete user" "link" in the "Temporary user 1" "table_row" + And I press "Continue" + Then I should not see "Temporary user 1" + And I should see "Temporary user 2" + + Scenario: A teacher can take attendance for temporary users + Given I log in as "teacher1" + And I follow "Course 1" + And I follow "Test attendance" + And I follow "Temporary users" + And I set the following fields to these values: + | Full name | Temporary user 1 | + | Email | | + And I press "Add user" + And I set the following fields to these values: + | Full name | Temporary user 2 | + | Email | tempuser2@example.com | + And I press "Add user" + + And I follow "Add" + And I set the following fields to these values: + | Create multiple sessions | 0 | + And I click on "submitbutton" "button" + And I follow "Sessions" + + When I follow "Take attendance" + # Present + And I click on "td.c2 input" "css_element" in the "Student 1" "table_row" + # Late + And I click on "td.c3 input" "css_element" in the "Student 2" "table_row" + # Excused + And I click on "td.c4 input" "css_element" in the "Temporary user 1" "table_row" + # Absent + And I click on "td.c5 input" "css_element" in the "Temporary user 2" "table_row" + And I press "Save attendance" + And I follow "Report" + Then "P" "text" should exist in the "Student 1" "table_row" + And "L" "text" should exist in the "Student 2" "table_row" + And "E" "text" should exist in the "Temporary user 1" "table_row" + And "A" "text" should exist in the "Temporary user 2" "table_row" + + When I follow "Temporary user 2" + Then I should see "Absent" + + # Merge user. + When I follow "Test attendance" + And I follow "Temporary users" + And I click on "Merge user" "link" in the "Temporary user 2" "table_row" + And I set the field "Participant" to "Student 3" + And I press "Merge user" + And I follow "Report" + + Then "P" "text" should exist in the "Student 1" "table_row" + And "L" "text" should exist in the "Student 2" "table_row" + And "E" "text" should exist in the "Temporary user 1" "table_row" + And "A" "text" should exist in the "Student 3" "table_row" + And I should not see "Temporary user 2" diff --git a/version.php b/version.php index d021803..9f2dff3 100644 --- a/version.php +++ b/version.php @@ -22,7 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -$plugin->version = 2015040500; +$plugin->version = 2015040501; $plugin->requires = 2014042900; $plugin->release = '2.9.1'; $plugin->maturity = MATURITY_STABLE;