Compare commits

...

46 Commits

Author SHA1 Message Date
Dan Marsden 3b9f928ed5 Fixes #223 allow student attendance to be disabled at site-level 8 years ago
Dan Marsden 597291a31a add travis config for 2.7 branch 9 years ago
Dan Marsden f5a572deba bump version for plugins db release of 2.7 branch 9 years ago
Dan Marsden 97c8b52f32 Merge pull request #179 from NeillM/MOODLE_27_STABLE 9 years ago
Neill Magill 37b5c126df Expired enrolments can prevent student self-attendance 9 years ago
Dan Marsden 3fa3265a6c Merge pull request #168 from barrysspace/MOODLE_27_STABLE 9 years ago
Barry Oosthuizen 6d26d4a972 Fix #155 properly. Undo regression caused by moving $statuses definition. 9 years ago
Syxton 2b9fea2609 Returns all users and their lowest status in a course. 9 years ago
Dan Marsden df3b56f5dd Merge pull request #162 from barrysspace/hotfix/issue_159_delete_selected_sessions_M27 9 years ago
Dan Marsden 469a8fa7f1 Merge pull request #156 from barrysspace/hotfix/issue_155_hiding_status_removes_it 9 years ago
Barry Oosthuizen e5b55cc04d Fix issue #159 - Delete no selected sessions 9 years ago
Barry Oosthuizen 9bb49b6510 Fix #155: Hiding statuses removes them from Settings 9 years ago
Dan Marsden cf6230e1bd Merge pull request #151 from barrysspace/hotfix/issue_145_hiding_status_M27 9 years ago
Barry Oosthuizen 6677ff2ea7 Fix #145 - hiding variable status items no longer working 9 years ago
Dan Marsden cc2ef207e1 update README 9 years ago
Dan Marsden 9b500d6740 Merge pull request #138 from barrysspace/MOODLE_27_STABLE 10 years ago
Barry Oosthuizen b4883b7ba7 Delete some whitespace introduced by b0638c67 10 years ago
Dan Marsden d04bd807c2 Fixes #87 show a useful error when form submitted incorrectly. 10 years ago
Dan Marsden d6fa8c3f39 Tidy up some debug messages. 10 years ago
Dan Marsden 9ece7cf1e2 Merge pull request #135 from barrysspace/MOODLE_27_STABLE 10 years ago
Barry Oosthuizen 0893ad210c Fix issue#134 - Add 'checkweekdays' language string 10 years ago
Dan Marsden 2602e8ad03 Merge pull request #128 from barrysspace/no_sessions_invalid_day 10 years ago
Dan Marsden 4db7f05256 Merge pull request #131 from barrysspace/hotfix/issue_129_empty_status_M27 10 years ago
Barry Oosthuizen 6463e40fa0 Behat test to notify error for empty acronym or description 10 years ago
Barry Oosthuizen 995e71671e Fix issue#129 - Notify error for empty acronym or description 10 years ago
Paolo Tramonti f72f1cfe3e When Multiple sessions is selected and no valid days of the week are selected show a tidy error. 11 years ago
Kathrin 45e1d9407c Optimized icon 10 years ago
Dan Marsden dc74f67b38 Fix #93 $record needs an id. 10 years ago
Dan Marsden b0b98426fd Merge pull request #98 from barrysspace/add_session_error_MDL27 10 years ago
Dan Marsden 8c0dfb29c6 Merge pull request #100 from barrysspace/add_record_snapshot_errors_MDL27 10 years ago
Dan Marsden b7699c3049 Merge pull request #97 from barrysspace/crud_and_objecttable_params_MDL27 10 years ago
Dan Marsden 47878ec7df Merge pull request #95 from barrysspace/session_filtering_MDL27 10 years ago
Dan Marsden bbf8b5c43e Merge pull request #101 from barrysspace/set_page_url_MDL27 10 years ago
Dan Marsden 4cfcbba68e Merge pull request #99 from barrysspace/deprecated_behat_steps_MDL27 10 years ago
Dan Marsden f391488440 Merge pull request #96 from barrysspace/broken_remarks_headers_MDL27 10 years ago
Barry Oosthuizen 0a250c0758 mod_attendance: Fix #92 Set page URL 10 years ago
Barry Oosthuizen f90ef5b8cf mod_attendance: Fix #91 deprecated behat steps 10 years ago
Barry Oosthuizen c30db3cb1c Fix #74 - crud and objecttable params in /classes/event/ need fixing 10 years ago
Barry Oosthuizen b1827357cf Fix #71 - Remarks headers in Export is broken 10 years ago
Barry Oosthuizen 05ae529f90 Fix documentation of $status variable 10 years ago
Barry Oosthuizen e3bcaad74d Fix #75 - add_record_snapshot() gives debug messages on various pages. Add status_removed event. 10 years ago
Barry Oosthuizen def7647ec8 Fix #73 - Debug warnings when adding session for "Separate groups" if no groups defined 10 years ago
Barry Oosthuizen b0ea71ddbb Better fix for Fix #72 10 years ago
Dan Marsden f4298d751e bump version for plugins db release 10 years ago
Kathrin e11af5973e Added a new icon 10 years ago
Barry Oosthuizen a085adf2bc Check if there is a manual enrolment with no expiry date 10 years ago
  1. 39
      .travis.yml
  2. 20
      README.md
  3. 37
      add_form.php
  4. 7
      attendance.php
  5. 4
      classes/event/attendance_taken.php
  6. 6
      classes/event/attendance_taken_by_student.php
  7. 4
      classes/event/session_added.php
  8. 4
      classes/event/session_deleted.php
  9. 4
      classes/event/session_duration_updated.php
  10. 4
      classes/event/session_updated.php
  11. 4
      classes/event/status_added.php
  12. 100
      classes/event/status_removed.php
  13. 4
      classes/event/status_updated.php
  14. 3
      duration_form.php
  15. 7
      lang/en/attendance.php
  16. 135
      locallib.php
  17. 114
      pix/icon.svg
  18. 23
      preferences.php
  19. 5
      renderables.php
  20. 33
      renderer.php
  21. 21
      sessions.php
  22. 3
      settings.php
  23. 7
      tests/behat/attendance_mod.feature
  24. 55
      tests/behat/preferences.feature
  25. 4
      version.php

39
.travis.yml

@ -0,0 +1,39 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 5.6
matrix:
env:
matrix:
- DB=pgsql MOODLE_BRANCH=MOODLE_27_STABLE
- DB=mysqli MOODLE_BRANCH=MOODLE_27_STABLE
before_install:
- phpenv config-rm xdebug.ini
- cd ../..
- composer selfupdate
- composer create-project -n --no-dev moodlerooms/moodle-plugin-ci ci ^1
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
- moodle-plugin-ci install
script:
- moodle-plugin-ci phplint
- moodle-plugin-ci phpcpd
- moodle-plugin-ci phpmd
- moodle-plugin-ci codechecker
- moodle-plugin-ci csslint
- moodle-plugin-ci shifter
- moodle-plugin-ci jshint
- moodle-plugin-ci validate
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat

20
README.md

@ -1,21 +1,15 @@
ABOUT ABOUT
========== ==========
The "Attendance" module was developed by The Attendance module is supported and maintained by Dan Marsden http://danmarsden.com
The Attendance module was previously developed by
Dmitry Pupinin, Novosibirsk, Russia, Dmitry Pupinin, Novosibirsk, Russia,
Artem Andreev, Taganrog, Russia. Artem Andreev, Taganrog, Russia.
This block may be distributed under the terms of the General Public License
(see http://www.gnu.org/licenses/gpl.txt for details)
PURPOSE PURPOSE
========== ==========
The attendance module and block are designed to allow instructors of a course keep an attendance log of the students in their courses. The instructor will setup the frequency of his classes (# of days per week & length of course) and the attendance is ready for use. To take attendance, the instructor clicks on the "Update Attendance" button and is presented with a list of all the students in that course, along with 4 options: Present, Absent, Late & Excused, with a Remarks textbox. Instructors can download the attendance for their course in Excel format or text format. The Attendance module allows teachers to maintain a record of attendance, replacing or supplementing a paper-based attendance register.
Only the instructor can update the attendance data. However, a student gets to see his attendance record. It is primarily used in blended-learning environments where students are required to attend classes, lectures and tutorials and allows
the teacher to track and optionally provide a grade for the students attendance.
INSTALLATION
==========
The attendance module follows standard installation procedure.
1. Create folder <path to your moodle dir>/mod/attendance. Sessions can be configured to allow students to record their own attendance and a range of different reports are available.
2. Extract files from folder inside archive to created folder.
3. Visit page Home ► Site administration ► Notifications to complete installation.

37
add_form.php

@ -91,7 +91,9 @@ class mod_attendance_add_form extends moodleform {
$select->setMultiple(true); $select->setMultiple(true);
$mform->disabledIf('groups', 'sessiontype', 'neq', attendance::SESSION_GROUP); $mform->disabledIf('groups', 'sessiontype', 'neq', attendance::SESSION_GROUP);
} else { } else {
if ($groupmode == VISIBLEGROUPS) {
$mform->updateElementAttr($radio, array('disabled'=>'disabled')); $mform->updateElementAttr($radio, array('disabled'=>'disabled'));
}
$mform->addElement('static', 'groups', get_string('groups', 'group'), $mform->addElement('static', 'groups', get_string('groups', 'group'),
get_string('nogroups', 'attendance')); get_string('nogroups', 'attendance'));
if ($groupmode == SEPARATEGROUPS) { if ($groupmode == SEPARATEGROUPS) {
@ -104,8 +106,13 @@ class mod_attendance_add_form extends moodleform {
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance'); $mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
// Students can mark own attendance. // Students can mark own attendance.
if (!empty(get_config('attendance', 'studentscanmark'))) {
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance')); $mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance'); $mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
} else {
$mform->addElement('hidden', 'studentscanmark', '0');
$mform->settype('studentscanmark', PARAM_INT);
}
$mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance')); $mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
@ -175,7 +182,37 @@ class mod_attendance_add_form extends moodleform {
$data['sdays']= array(); $data['sdays']= array();
$errors['sdays'] = get_string('required', 'attendance'); $errors['sdays'] = get_string('required', 'attendance');
} }
if (isset($data['sdays'])) {
if (!$this->checkWeekDays($data['sessiondate'], $data['sessionenddate'], $data['sdays']) ) {
$errors['sdays'] = get_string('checkweekdays', 'attendance');
}
}
return $errors; return $errors;
} }
private function checkWeekDays($sessiondate, $sessionenddate, $sdays) {
$found = false;
$daysOfWeek = array(0 => "Sun", 1 => "Mon", 2 => "Tue", 3 => "Wed", 4 => "Thu", 5 => "Fri", 6 => "Sat");
$start = new DateTime( date("Y-m-d",$sessiondate) );
$interval = new DateInterval('P1D');
$end = new DateTime( date("Y-m-d",$sessionenddate) );
$end->add( new DateInterval('P1D') );
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $date) {
if (!$found) {
foreach ($sdays as $name => $value) {
$key = array_search($name, $daysOfWeek);
if ($date->format("w") == $key) {
$found = true;
break;
}
}
}
}
return $found;
}
} }

7
attendance.php

@ -41,6 +41,10 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST)
// Require the user is logged in. // Require the user is logged in.
require_login($course, true, $cm); require_login($course, true, $cm);
if (empty(get_config('attendance', 'studentscanmark')) || empty($attforsession->studentscanmark)) {
redirect(new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)));
exit;
}
$pageparams->sessionid = $id; $pageparams->sessionid = $id;
$att = new attendance($attendance, $cm, $course, $PAGE->context, $pageparams); $att = new attendance($attendance, $cm, $course, $PAGE->context, $pageparams);
@ -51,6 +55,8 @@ require_sesskey();
$mform = new mod_attendance_student_attendance_form(null, $mform = new mod_attendance_student_attendance_form(null,
array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att)); array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att));
$PAGE->set_url($att->url_sessions());
if ($mform->is_cancelled()) { if ($mform->is_cancelled()) {
// The user cancelled the form, so redirect them to the view page. // The user cancelled the form, so redirect them to the view page.
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)); $url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
@ -72,7 +78,6 @@ if ($mform->is_cancelled()) {
$mform->set_data($fromform); $mform->set_data($fromform);
} }
$PAGE->set_url($att->url_sessions());
$PAGE->set_title($course->shortname. ": ".$att->name); $PAGE->set_title($course->shortname. ": ".$att->name);
$PAGE->set_heading($course->fullname); $PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(true); $PAGE->set_cacheable(true);

4
classes/event/attendance_taken.php

@ -44,9 +44,9 @@ class attendance_taken extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_log';
} }
/** /**

6
classes/event/attendance_taken_by_student.php

@ -44,9 +44,9 @@ class attendance_taken_by_student extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_log';
} }
/** /**

4
classes/event/session_added.php

@ -44,9 +44,9 @@ class session_added extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_sessions';
} }
/** /**

4
classes/event/session_deleted.php

@ -44,9 +44,9 @@ class session_deleted extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_sessions';
} }
/** /**

4
classes/event/session_duration_updated.php

@ -44,9 +44,9 @@ class session_duration_updated extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_sessions';
} }
/** /**

4
classes/event/session_updated.php

@ -44,9 +44,9 @@ class session_updated extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_sessions';
} }
/** /**

4
classes/event/status_added.php

@ -44,9 +44,9 @@ class status_added extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_statuses';
} }
/** /**

100
classes/event/status_removed.php

@ -0,0 +1,100 @@
<?php
// This file is part of the Attendance module for 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 an attendance status is updated.
*
* @package mod_attendance
* @copyright 2015 onwards, University of Nottingham
* @author Barry Oosthuizen <barry.oosthuizen@nottingham.ac.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\event;
defined('MOODLE_INTERNAL') || die();
/**
* Event for when an attendance status is removed.
*
* @property-read array $other {
* Extra information about event properties.
*
* @string mode Mode of the report viewed.
* }
* @package mod_attendance
* @since Moodle 2.7
* @copyright 2013 onwards Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class status_removed extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance_statuses';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return 'User with id ' . $this->userid . ' deleted attendance status "' . $this->data['other']['acronym'] . ' - ' . $this->data['other']['description'] . '" with instanceid ' .
$this->objectid . '';
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('statusdeleted', 'mod_attendance');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/attendance/preferences.php', array('id' => $this->contextinstanceid));
}
/**
* Replace add_to_log() statement.
*
* @return array of parameters to be passed to legacy add_to_log() function.
*/
protected function get_legacy_logdata() {
return array($this->courseid, 'attendance', 'status removed', $this->get_url(),
$this->other['acronym'] . ' - ' . $this->other['description'], $this->contextinstanceid);
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
}
}

4
classes/event/status_updated.php

@ -44,9 +44,9 @@ class status_updated extends \core\event\base {
* Init method. * Init method.
*/ */
protected function init() { protected function init() {
$this->data['crud'] = 'r'; $this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING; $this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'attendance'; $this->data['objecttable'] = 'attendance_statuses';
} }
/** /**

3
duration_form.php

@ -61,8 +61,11 @@ class mod_attendance_duration_form extends moodleform {
$mform->addGroup($durselect, 'durtime', get_string('newduration', 'attendance'), array(' '), true); $mform->addGroup($durselect, 'durtime', get_string('newduration', 'attendance'), array(' '), true);
$mform->addElement('hidden', 'ids', $ids); $mform->addElement('hidden', 'ids', $ids);
$mform->setType('ids', PARAM_ALPHANUMEXT);
$mform->addElement('hidden', 'id', $cm->id); $mform->addElement('hidden', 'id', $cm->id);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', att_sessions_page_params::ACTION_CHANGE_DURATION); $mform->addElement('hidden', 'action', att_sessions_page_params::ACTION_CHANGE_DURATION);
$mform->setType('action', PARAM_INT);
$mform->setDefaults(array('durtime' => array('hours'=>0, 'minutes'=>0))); $mform->setDefaults(array('durtime' => array('hours'=>0, 'minutes'=>0)));

7
lang/en/attendance.php

@ -65,6 +65,7 @@ $string['cannottakeforgroup'] = 'You can\'t take attendance for group "{$a}"';
$string['changeattendance'] = 'Change attendance'; $string['changeattendance'] = 'Change attendance';
$string['changeduration'] = 'Change duration'; $string['changeduration'] = 'Change duration';
$string['changesession'] = 'Change session'; $string['changesession'] = 'Change session';
$string['checkweekdays'] = 'Select weekdays that fall within your selected session date range.';
$string['column'] = 'column'; $string['column'] = 'column';
$string['columns'] = 'columns'; $string['columns'] = 'columns';
$string['commonsession'] = 'Common'; $string['commonsession'] = 'Common';
@ -99,6 +100,8 @@ $string['downloadtext'] = 'Download in text format';
$string['donotusepaging'] = 'Do not use paging'; $string['donotusepaging'] = 'Do not use paging';
$string['duration'] = 'Duration'; $string['duration'] = 'Duration';
$string['editsession'] = 'Edit Session'; $string['editsession'] = 'Edit Session';
$string['emptyacronym'] = 'Empty acronyms are not allowed. Status record not updated.';
$string['emptydescription'] = 'Empty descriptions are not allowed. Status record not updated.';
$string['endtime'] = 'Session end time'; $string['endtime'] = 'Session end time';
$string['endofperiod'] = 'End of period'; $string['endofperiod'] = 'End of period';
$string['enrolmentend'] = 'User enrolment ends {$a}'; $string['enrolmentend'] = 'User enrolment ends {$a}';
@ -123,6 +126,7 @@ $string['includenottaken'] = 'Include not taken sessions';
$string['includeremarks'] = 'Include remarks'; $string['includeremarks'] = 'Include remarks';
$string['indetail'] = 'In detail...'; $string['indetail'] = 'In detail...';
$string['invalidsessionenddate'] = 'The session end date can not be earlier than the session start date'; $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['jumpto'] = 'Jump to';
$string['modulename'] = 'Attendance'; $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. $string['modulename_help'] = 'The attendance activity module enables a teacher to take attendance during class and students to view their own attendance record.
@ -153,7 +157,7 @@ $string['olddate'] = 'Old date';
$string['period'] = 'Frequency'; $string['period'] = 'Frequency';
$string['pluginname'] = 'Attendance'; $string['pluginname'] = 'Attendance';
$string['pluginadministration'] = 'Attendance administration'; $string['pluginadministration'] = 'Attendance administration';
$string['remark'] = 'Remark for: {a}'; $string['remark'] = 'Remark for: {$a}';
$string['remarks'] = 'Remarks'; $string['remarks'] = 'Remarks';
$string['report'] = 'Report'; $string['report'] = 'Report';
$string['required'] = 'Required*'; $string['required'] = 'Required*';
@ -227,6 +231,7 @@ $string['eventstatusupdated'] = 'Status updated';
$string['eventstatusadded'] = 'Status added'; $string['eventstatusadded'] = 'Status added';
$string['studentscanmark'] = 'Allow students to record own attendance'; $string['studentscanmark'] = 'Allow students to record own attendance';
$string['studentscanmark_desc'] = 'If checked, teachers will be able to allow students to mark their own attendance.';
$string['studentscanmark_help'] = 'If checked students will be able to change their own attendance status for the session.'; $string['studentscanmark_help'] = 'If checked students will be able to change their own attendance status for the session.';
$string['set_by_student'] = 'Self-recorded'; $string['set_by_student'] = 'Self-recorded';
$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.'; $string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';

135
locallib.php

@ -707,8 +707,9 @@ class attendance {
} else { } else {
$where = "attendanceid = :aid AND sessdate >= :csdate"; $where = "attendanceid = :aid AND sessdate >= :csdate";
} }
if ($this->pageparams->get_current_sesstype() > att_page_with_filter_controls::SESSTYPE_ALL) { if ($this->pageparams->get_current_sesstype() > att_page_with_filter_controls::SESSTYPE_ALL) {
$where .= " AND groupid=:cgroup"; $where .= " AND (groupid = :cgroup OR groupid = 0)";
} }
$params = array( $params = array(
'aid' => $this->id, 'aid' => $this->id,
@ -794,32 +795,24 @@ class attendance {
array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0),
$sess->description); $sess->description);
$DB->set_field('attendance_sessions', 'description', $description, array('id' => $sess->id)); $DB->set_field('attendance_sessions', 'description', $description, array('id' => $sess->id));
}
$info_array = array(); $info_array = array();
$maxlog = 7; // Only log first 10 sessions and last session in the log info. as we can only store 255 chars.
$i = 0;
foreach ($sessions as $sess) {
if ($i > $maxlog) {
$lastsession = end($sessions);
$info_array[] = '...';
$info_array[] = construct_session_full_date_time($lastsession->sessdate, $lastsession->duration);
break;
} else {
$info_array[] = construct_session_full_date_time($sess->sessdate, $sess->duration); $info_array[] = construct_session_full_date_time($sess->sessdate, $sess->duration);
}
$i++; // Trigger a session added event.
}
// Trigger a report viewed event.
$event = \mod_attendance\event\session_added::create(array( $event = \mod_attendance\event\session_added::create(array(
'objectid' => $this->id, 'objectid' => $this->id,
'context' => $this->context, 'context' => $this->context,
'other' => array('info' => implode(',', $info_array)) 'other' => array('info' => implode(',', $info_array))
)); ));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $sess->description = $description;
$sess->lasttaken = 0;
$sess->lasttakenby = 0;
$sess->studentscanmark = 0;
$event->add_record_snapshot('attendance_sessions', $sess);
$event->trigger(); $event->trigger();
}
} }
public function update_session_from_form_data($formdata, $sessionid) { public function update_session_from_form_data($formdata, $sessionid) {
@ -845,7 +838,7 @@ class attendance {
'context' => $this->context, 'context' => $this->context,
'other' => array('info' => $info, 'sessionid' => $sessionid, 'action' => att_sessions_page_params::ACTION_UPDATE))); 'other' => array('info' => $info, 'sessionid' => $sessionid, 'action' => att_sessions_page_params::ACTION_UPDATE)));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_sessions', $sess);
$event->trigger(); $event->trigger();
} }
@ -877,16 +870,15 @@ class attendance {
// Already recorded do not save. // Already recorded do not save.
return false; return false;
} }
else {
$DB->insert_record('attendance_log', $record, false); $logid = $DB->insert_record('attendance_log', $record, false);
} $record->id = $logid;
// Update the session to show that a register has been taken, or staff may overwrite records. // Update the session to show that a register has been taken, or staff may overwrite records.
$rec = new object(); $session = $this->get_session_info($mformdata->sessid);
$rec->id = $mformdata->sessid; $session->lasttaken = $now;
$rec->lasttaken = $now; $session->lasttakenby = $USER->id;
$rec->lasttakenby = $USER->id; $DB->update_record('attendance_sessions', $session);
$DB->update_record('attendance_sessions', $rec);
// Update the users grade. // Update the users grade.
$this->update_users_grade(array($USER->id)); $this->update_users_grade(array($USER->id));
@ -905,7 +897,8 @@ class attendance {
'context' => $this->context, 'context' => $this->context,
'other' => $params)); 'other' => $params));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_sessions', $session);
$event->add_record_snapshot('attendance_log', $record);
$event->trigger(); $event->trigger();
return true; return true;
@ -948,11 +941,10 @@ class attendance {
} }
} }
$rec = new stdClass(); $session = $this->get_session_info($this->pageparams->sessionid);
$rec->id = $this->pageparams->sessionid; $session->lasttaken = $now;
$rec->lasttaken = $now; $session->lasttakenby = $USER->id;
$rec->lasttakenby = $USER->id; $DB->update_record('attendance_sessions', $session);
$DB->update_record('attendance_sessions', $rec);
if ($this->grade != 0) { if ($this->grade != 0) {
$this->update_users_grade(array_keys($sesslog)); $this->update_users_grade(array_keys($sesslog));
@ -967,7 +959,7 @@ class attendance {
'context' => $this->context, 'context' => $this->context,
'other' => $params)); 'other' => $params));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_sessions', $session);
$event->trigger(); $event->trigger();
$group = 0; $group = 0;
@ -1047,10 +1039,10 @@ class attendance {
// CONTRIB-4868 // CONTRIB-4868
$mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)';
$maxtime = 'MAX(ue.timeend)'; $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END';
// CONTRIB-3549 // CONTRIB-3549
$sql = "SELECT ue.userid, ue.status, $sql = "SELECT ue.userid, MIN(ue.status) as status,
$mintime AS mintime, $mintime AS mintime,
$maxtime AS maxtime $maxtime AS maxtime
FROM {user_enrolments} ue FROM {user_enrolments} ue
@ -1058,7 +1050,7 @@ class attendance {
WHERE ue.userid $sql WHERE ue.userid $sql
AND e.status = :estatus AND e.status = :estatus
AND e.courseid = :courseid AND e.courseid = :courseid
GROUP BY ue.userid, ue.status"; GROUP BY ue.userid";
$params += array('zerotime'=>0, 'estatus'=>ENROL_INSTANCE_ENABLED, 'courseid'=>$this->course->id); $params += array('zerotime'=>0, 'estatus'=>ENROL_INSTANCE_ENABLED, 'courseid'=>$this->course->id);
$enrolments = $DB->get_records_sql($sql, $params); $enrolments = $DB->get_records_sql($sql, $params);
@ -1079,7 +1071,7 @@ class attendance {
// CONTRIB-4868 // CONTRIB-4868
$mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)';
$maxtime = 'MAX(ue.timeend)'; $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END';
$sql = "SELECT ue.userid, ue.status, $sql = "SELECT ue.userid, ue.status,
$mintime AS mintime, $mintime AS mintime,
@ -1402,7 +1394,6 @@ class attendance {
'context' => $this->context, 'context' => $this->context,
'other' => array('info' => implode(', ', $sessionsids)))); 'other' => array('info' => implode(', ', $sessionsids))));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this);
$event->trigger(); $event->trigger();
} }
@ -1415,23 +1406,47 @@ class attendance {
$sess->duration = $duration; $sess->duration = $duration;
$sess->timemodified = $now; $sess->timemodified = $now;
$DB->update_record('attendance_sessions', $sess); $DB->update_record('attendance_sessions', $sess);
}
$sessions->close();
$event = \mod_attendance\event\session_duration_updated::create(array( $event = \mod_attendance\event\session_duration_updated::create(array(
'objectid' => $this->id, 'objectid' => $this->id,
'context' => $this->context, 'context' => $this->context,
'other' => array('info' => implode(', ', $sessionsids)))); 'other' => array('info' => implode(', ', $sessionsids))));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_sessions', $sess);
$event->trigger(); $event->trigger();
} }
$sessions->close();
}
public function remove_status($statusid) { /**
* Remove a status variable from an attendance instance
*
* @global moodle_database $DB
* @param stdClass $status
*/
public function remove_status($status) {
global $DB; global $DB;
$DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $statusid)); $DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id));
$event = \mod_attendance\event\status_removed::create(array(
'objectid' => $status->id,
'context' => $this->context,
'other' => array(
'acronym' => $status->acronym,
'description' => $status->description
)));
$event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
} }
/**
* Add an attendance status variable
*
* @global moodle_database $DB
* @param string $acronym
* @param string $description
* @param int $grade
*/
public function add_status($acronym, $description, $grade) { public function add_status($acronym, $description, $grade) {
global $DB; global $DB;
@ -1442,27 +1457,45 @@ class attendance {
$rec->acronym = $acronym; $rec->acronym = $acronym;
$rec->description = $description; $rec->description = $description;
$rec->grade = $grade; $rec->grade = $grade;
$DB->insert_record('attendance_statuses', $rec); $rec->deleted = 0;
$rec->visible = 1;
$id = $DB->insert_record('attendance_statuses', $rec);
$rec->id = $id;
$event = \mod_attendance\event\status_added::create(array( $event = \mod_attendance\event\status_added::create(array(
'objectid' => $this->id, 'objectid' => $this->id,
'context' => $this->context, 'context' => $this->context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade))); 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade)));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_statuses', $rec);
$event->trigger(); $event->trigger();
} else { } else {
print_error('cantaddstatus', 'attendance', $this->url_preferences()); print_error('cantaddstatus', 'attendance', $this->url_preferences());
} }
} }
public function update_status($statusid, $acronym, $description, $grade, $visible) { /**
* Update status variable for a particular Attendance module instance
*
* @global moodle_database $DB
* @param stdClass $status
* @param string $acronym
* @param string $description
* @param int $grade
* @param bool $visible
*/
public function update_status($status, $acronym, $description, $grade, $visible) {
global $DB; global $DB;
if (isset($visible)) {
$status->visible = $visible;
$updated[] = $visible ? get_string('show') : get_string('hide');
} else if (empty($acronym) || empty($description)) {
return array('acronym' => $acronym, 'description' => $description);
}
$updated = array(); $updated = array();
$status = new stdClass();
$status->id = $statusid;
if ($acronym) { if ($acronym) {
$status->acronym = $acronym; $status->acronym = $acronym;
$updated[] = $acronym; $updated[] = $acronym;
@ -1475,10 +1508,6 @@ class attendance {
$status->grade = $grade; $status->grade = $grade;
$updated[] = $grade; $updated[] = $grade;
} }
if (isset($visible)) {
$status->visible = $visible;
$updated[] = $visible ? get_string('show') : get_string('hide');
}
$DB->update_record('attendance_statuses', $status); $DB->update_record('attendance_statuses', $status);
$event = \mod_attendance\event\status_updated::create(array( $event = \mod_attendance\event\status_updated::create(array(
@ -1486,7 +1515,7 @@ class attendance {
'context' => $this->context, 'context' => $this->context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade, 'updated' => implode(' ', $updated)))); 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade, 'updated' => implode(' ', $updated))));
$event->add_record_snapshot('course_modules', $this->cm); $event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance', $this); $event->add_record_snapshot('attendance_statuses', $status);
$event->trigger(); $event->trigger();
} }

114
pix/icon.svg

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="svg2" sodipodi:docname="Group_Choice.svg" inkscape:version="0.48.4 r9939" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<sodipodi:namedview bordercolor="#666666" borderopacity="1" id="namedview73" inkscape:window-width="1440" objecttolerance="10" gridtolerance="10" guidetolerance="10" pagecolor="#ffffff" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="878" showgrid="false" inkscape:zoom="9.8333333" inkscape:cx="-4.1694915" inkscape:cy="12" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg2">
</sodipodi:namedview>
<g>
<linearGradient id="path32_3_" gradientUnits="userSpaceOnUse" x1="-19.9995" y1="-817" x2="-19.9995" y2="-833.0005" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#90C50E"/>
<stop offset="1" style="stop-color:#70A034"/>
</linearGradient>
<path id="path32_1_" fill="url(#path32_3_)" d="M11,16v-3.4l-3-1.5c-0.5-0.3-0.6-0.8-0.301-1.199c0,0,1.6-2,1.6-4.2
C9.299,2.5,7.4,0,4.9,0C2.5,0,0.5,2.6,0.5,5.7c0,2.1,1.6,4.2,1.6,4.2c0.301,0.399,0.199,1-0.301,1.3L0,12.444c0,0,0,2.955,0,3.556
H11z"/>
<linearGradient id="path39_3_" gradientUnits="userSpaceOnUse" x1="-19.9995" y1="-818" x2="-19.9995" y2="-832.0147" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#D9F991"/>
<stop offset="1" style="stop-color:#B1DD4B"/>
</linearGradient>
<path id="path39_1_" fill="url(#path39_3_)" d="M0.9,13l1.6-1c0.5-0.3,0.799-0.7,1-1.2c0.1-0.5,0-1.1-0.4-1.5c0,0-1.4-1.8-1.4-3.6
C1.7,3.1,3.2,1,5.1,1s3.4,2.1,3.4,4.7c0,1.8-1.4,3.6-1.4,3.6c-0.301,0.4-0.5,1-0.4,1.5c0.1,0.5,0.5,1,1,1.2l2.4,1.201V15H0.9V13z"
/>
<linearGradient id="path46_3_" gradientUnits="userSpaceOnUse" x1="-20.5005" y1="-818.9863" x2="-20.5005" y2="-831.0005" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#B3E73A"/>
<stop offset="1" style="stop-color:#90C61D"/>
</linearGradient>
<path id="path46_1_" fill="url(#path46_3_)" d="M0.9,14L3,12.9C3.799,12.5,4.299,11.8,4.5,11c0.199-0.8,0-1.7-0.6-2.3
c-0.301-0.4-1.201-1.8-1.201-3c0-2,1.1-3.7,2.4-3.7c1.3,0,2.4,1.7,2.4,3.7c0,1.2-0.9,2.5-1.201,3c-0.5,0.7-0.699,1.5-0.6,2.3
c0.201,0.8,0.701,1.5,1.5,1.9l1.9,1V14H0.9z"/>
</g>
<g>
<linearGradient id="path11_1_" gradientUnits="userSpaceOnUse" x1="-1157.0952" y1="-819.666" x2="-1157.0952" y2="-835.6655" gradientTransform="matrix(-1 0 0 -1 -1149.5 -817)">
<stop offset="0" style="stop-color:#DB6D17"/>
<stop offset="1" style="stop-color:#BF3B08"/>
</linearGradient>
<path id="path11" fill="url(#path11_1_)" d="M12.05,12.566c0,0,1.601-2.101,1.601-4.2c0-3.1-2-5.7-4.398-5.7s-4.4,2.5-4.3,5.6
c0,2.2,1.6,4.2,1.6,4.2c0.3,0.401,0.2,0.901-0.3,1.2l-6,3.1c-0.5,0.301-0.252,1.9-0.252,1.9h15.252v-3.199l-3-1.6
C11.75,13.566,11.65,12.967,12.05,12.566z"/>
<linearGradient id="path18_1_" gradientUnits="userSpaceOnUse" x1="-1157.0005" y1="-820.667" x2="-1157.0005" y2="-834.6655" gradientTransform="matrix(-1 0 0 -1 -1149.5 -817)">
<stop offset="0" style="stop-color:#F6A55E"/>
<stop offset="1" style="stop-color:#EA5B03"/>
</linearGradient>
<path id="path18" fill="url(#path18_1_)" d="M0.75,17.666l5.9-3c0.5-0.199,0.9-0.699,1-1.199s-0.1-1.1-0.4-1.5
c0-0.1-1.4-1.899-1.4-3.6c0-2.6,1.6-4.7,3.4-4.7s3.4,2.1,3.3,4.7c0,1.8-1.4,3.6-1.4,3.6c-0.4,0.4-0.5,1-0.4,1.5
c0.2,0.5,0.5,0.9,1,1.199l2.5,1.301v1.699H0.75z"/>
<linearGradient id="path25_1_" gradientUnits="userSpaceOnUse" x1="-1158.5503" y1="-821.6523" x2="-1158.5503" y2="-833.6642" gradientTransform="matrix(-1 0 0 -1 -1149.5 -817)">
<stop offset="0" style="stop-color:#F17219"/>
<stop offset="1" style="stop-color:#EA5B03"/>
</linearGradient>
<path id="path25" fill="url(#path25_1_)" d="M4.85,16.666l2.2-1.1c0.8-0.4,1.3-1.102,1.5-1.9c0.2-0.799,0-1.6-0.5-2.299
c-0.3-0.5-1.2-1.9-1.2-3c0-2,1.1-3.7,2.4-3.7s2.4,1.7,2.4,3.7c0,1.2-0.9,2.6-1.2,3c-0.6,0.6-0.8,1.5-0.6,2.299
c0.2,0.801,0.7,1.5,1.5,1.9l1.9,1.1l0,0H4.85z"/>
</g>
<g>
<linearGradient id="path32_4_" gradientUnits="userSpaceOnUse" x1="-14.3999" y1="-822" x2="-14.3999" y2="-838.0005" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#90C50E"/>
<stop offset="1" style="stop-color:#70A034"/>
</linearGradient>
<path id="path32_2_" fill="url(#path32_4_)" d="M19.199,21v-3.4l-3-1.5c-0.5-0.299-0.6-0.799-0.301-1.199c0,0,1.601-2,1.601-4.198
C17.499,7.5,15.6,5,13.1,5C10.7,5,8.7,7.6,8.7,10.7c0,2.1,1.6,4.2,1.6,4.2c0.301,0.398,0.2,1-0.3,1.301L4,19.4
c-0.5,0.301-1,0.898-1,1.5V21H19.199z"/>
<linearGradient id="path39_4_" gradientUnits="userSpaceOnUse" x1="-13.8511" y1="-823.001" x2="-13.8511" y2="-837.0157" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#D9F991"/>
<stop offset="1" style="stop-color:#B1DD4B"/>
</linearGradient>
<path id="path39_2_" fill="url(#path39_4_)" d="M4.999,20l5.7-3c0.5-0.299,0.8-0.699,1-1.199c0.1-0.5,0-1.1-0.4-1.5
c0,0-1.399-1.801-1.399-3.6c0-2.6,1.5-4.7,3.399-4.7c1.9,0,3.4,2.101,3.4,4.7c0,1.799-1.4,3.6-1.4,3.6c-0.3,0.4-0.5,1-0.398,1.5
c0.101,0.5,0.5,1,1,1.199l2.398,1.201V20H4.999z"/>
<linearGradient id="path46_4_" gradientUnits="userSpaceOnUse" x1="-12.3013" y1="-823.9863" x2="-12.3013" y2="-836.001" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#B3E73A"/>
<stop offset="1" style="stop-color:#90C61D"/>
</linearGradient>
<path id="path46_2_" fill="url(#path46_4_)" d="M9.1,19l2.1-1.1c0.8-0.4,1.3-1.102,1.5-1.9s0-1.699-0.6-2.299
c-0.301-0.4-1.2-1.801-1.2-3c0-2,1.1-3.7,2.399-3.7c1.301,0,2.4,1.7,2.4,3.7c0,1.199-0.9,2.5-1.2,3c-0.5,0.699-0.7,1.5-0.601,2.299
c0.201,0.801,0.701,1.5,1.5,1.9l1.898,1V19H9.1z"/>
</g>
<g>
<linearGradient id="path32_5_" gradientUnits="userSpaceOnUse" x1="-9.6001" y1="-825" x2="-9.6001" y2="-841.0005" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#90C50E"/>
<stop offset="1" style="stop-color:#70A034"/>
</linearGradient>
<path id="path32" fill="url(#path32_5_)" d="M24,24v-3.4l-3-1.5c-0.5-0.3-0.6-0.8-0.3-1.199c0,0,1.6-2,1.6-4.198
C22.3,10.5,20.4,8,17.9,8c-2.4,0-4.4,2.6-4.4,5.7c0,2.1,1.6,4.2,1.6,4.2c0.301,0.397,0.2,1-0.3,1.3l-6,3.2c-0.5,0.3-1,0.897-1,1.5
V24H24z"/>
<linearGradient id="path39_5_" gradientUnits="userSpaceOnUse" x1="-9.0503" y1="-826" x2="-9.0503" y2="-840.0152" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#D9F991"/>
<stop offset="1" style="stop-color:#B1DD4B"/>
</linearGradient>
<path id="path39" fill="url(#path39_5_)" d="M9.8,23l5.7-3c0.5-0.3,0.8-0.7,1-1.2c0.1-0.5,0-1.1-0.4-1.5c0,0-1.397-1.8-1.397-3.6
c0-2.6,1.5-4.7,3.397-4.7c1.9,0,3.4,2.1,3.4,4.7c0,1.8-1.4,3.6-1.4,3.6c-0.3,0.4-0.5,1-0.397,1.5c0.1,0.5,0.5,1,1,1.2l2.397,1.2V23
H9.8z"/>
<linearGradient id="path46_5_" gradientUnits="userSpaceOnUse" x1="-7.502" y1="-826.9863" x2="-7.502" y2="-839.0015" gradientTransform="matrix(1 0 0 -1 25.5 -817)">
<stop offset="0" style="stop-color:#B3E73A"/>
<stop offset="1" style="stop-color:#90C61D"/>
</linearGradient>
<path id="path46" fill="url(#path46_5_)" d="M13.9,22l2.1-1.1c0.8-0.4,1.3-1.103,1.5-1.9s0-1.7-0.6-2.3c-0.301-0.4-1.2-1.8-1.2-3
c0-2,1.1-3.7,2.399-3.7c1.301,0,2.398,1.7,2.398,3.7c0,1.2-0.898,2.5-1.2,3c-0.5,0.7-0.698,1.5-0.6,2.3
c0.198,0.8,0.698,1.5,1.5,1.9l1.897,1V22H13.9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

23
preferences.php

@ -48,6 +48,8 @@ $PAGE->set_cacheable(true);
$PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance')); $PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance'));
$PAGE->navbar->add(get_string('settings', 'attendance')); $PAGE->navbar->add(get_string('settings', 'attendance'));
$errors = array();
switch ($att->pageparams->action) { switch ($att->pageparams->action) {
case att_preferences_page_params::ACTION_ADD: case att_preferences_page_params::ACTION_ADD:
$newacronym = optional_param('newacronym', null, PARAM_TEXT); $newacronym = optional_param('newacronym', null, PARAM_TEXT);
@ -62,13 +64,14 @@ switch ($att->pageparams->action) {
} }
$confirm = optional_param('confirm', null, PARAM_INT); $confirm = optional_param('confirm', null, PARAM_INT);
$statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid];
if (isset($confirm)) { if (isset($confirm)) {
$att->remove_status($att->pageparams->statusid); $att->remove_status($status);
redirect($att->url_preferences(), get_string('statusdeleted', 'attendance')); redirect($att->url_preferences(), get_string('statusdeleted', 'attendance'));
} }
$statuses = $att->get_statuses();
$status = $statuses[$att->pageparams->statusid];
$message = get_string('deletecheckfull', '', get_string('variable', 'attendance')); $message = get_string('deletecheckfull', '', get_string('variable', 'attendance'));
$message .= str_repeat(html_writer::empty_tag('br'), 2); $message .= str_repeat(html_writer::empty_tag('br'), 2);
$message .= $status->acronym.': '. $message .= $status->acronym.': '.
@ -80,18 +83,24 @@ switch ($att->pageparams->action) {
echo $OUTPUT->footer(); echo $OUTPUT->footer();
exit; exit;
case att_preferences_page_params::ACTION_HIDE: case att_preferences_page_params::ACTION_HIDE:
$att->update_status($att->pageparams->statusid, null, null, null, 0); $statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid];
$att->update_status($status, null, null, null, 0);
break; break;
case att_preferences_page_params::ACTION_SHOW: case att_preferences_page_params::ACTION_SHOW:
$att->update_status($att->pageparams->statusid, null, null, null, 1); $statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid];
$att->update_status($status, null, null, null, 1);
break; break;
case att_preferences_page_params::ACTION_SAVE: case att_preferences_page_params::ACTION_SAVE:
$acronym = required_param_array('acronym', PARAM_MULTILANG); $acronym = required_param_array('acronym', PARAM_MULTILANG);
$description = required_param_array('description', PARAM_MULTILANG); $description = required_param_array('description', PARAM_MULTILANG);
$grade = required_param_array('grade', PARAM_INT); $grade = required_param_array('grade', PARAM_INT);
$statuses = $att->get_statuses(false);
foreach ($acronym as $id => $v) { foreach ($acronym as $id => $v) {
$att->update_status($id, $acronym[$id], $description[$id], $grade[$id], null); $status = $statuses[$id];
$errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null);
} }
if ($att->grade > 0) { if ($att->grade > 0) {
att_update_all_users_grades($att->id, $att->course, $att->context, $cm); att_update_all_users_grades($att->id, $att->course, $att->context, $cm);
@ -101,7 +110,7 @@ switch ($att->pageparams->action) {
$output = $PAGE->get_renderer('mod_attendance'); $output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_PREFERENCES); $tabs = new attendance_tabs($att, attendance_tabs::TAB_PREFERENCES);
$prefdata = new attendance_preferences_data($att); $prefdata = new attendance_preferences_data($att, array_filter($errors));
// Output starts here. // Output starts here.

5
renderables.php

@ -535,8 +535,11 @@ class attendance_preferences_data implements renderable {
private $att; private $att;
public function __construct(attendance $att) { public $errors;
public function __construct(attendance $att, $errors) {
$this->statuses = $att->get_statuses(false); $this->statuses = $att->get_statuses(false);
$this->errors = $errors;
foreach ($this->statuses as $st) { foreach ($this->statuses as $st) {
$st->haslogs = att_has_logs_for_status ($st->id); $st->haslogs = att_has_logs_for_status ($st->id);

33
renderer.php

@ -294,6 +294,7 @@ class mod_attendance_renderer extends plugin_renderer_base {
$options = array( $options = array(
att_sessions_page_params::ACTION_DELETE_SELECTED => get_string('delete'), att_sessions_page_params::ACTION_DELETE_SELECTED => get_string('delete'),
att_sessions_page_params::ACTION_CHANGE_DURATION => get_string('changeduration', 'attendance')); att_sessions_page_params::ACTION_CHANGE_DURATION => get_string('changeduration', 'attendance'));
$controls = html_writer::select($options, 'action'); $controls = html_writer::select($options, 'action');
$attributes = array( $attributes = array(
'type' => 'submit', 'type' => 'submit',
@ -747,7 +748,8 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cell->colspan = 2; $cell->colspan = 2;
$row->cells[] = $cell; $row->cells[] = $cell;
} else { } else {
if (!empty($sess->studentscanmark)) { // Student can mark their own attendance. if (!empty(get_config('attendance', 'studentscanmark')) && !empty($sess->studentscanmark)) {
// Student can mark their own attendance.
// URL to the page that lets the student modify their attendance. // URL to the page that lets the student modify their attendance.
$url = new moodle_url('/mod/attendance/attendance.php', $url = new moodle_url('/mod/attendance/attendance.php',
array('sessid' => $sess->id, 'sesskey' => sesskey())); array('sessid' => $sess->id, 'sesskey' => sesskey()));
@ -926,9 +928,22 @@ class mod_attendance_renderer extends plugin_renderer_base {
$i = 1; $i = 1;
foreach ($prefdata->statuses as $st) { foreach ($prefdata->statuses as $st) {
$emptyacronym = '';
$emptydescription = '';
if (array_key_exists($st->id, $prefdata->errors)) {
if (empty($prefdata->errors[$st->id]['acronym'])) {
$emptyacronym = $this->construct_notice(get_string('emptyacronym', 'mod_attendance'), 'notifyproblem');
}
if (empty($prefdata->errors[$st->id]['description'])) {
$emptydescription = $this->construct_notice(get_string('emptydescription', 'mod_attendance') , 'notifyproblem');
}
}
$table->data[$i][] = $i; $table->data[$i][] = $i;
$table->data[$i][] = $this->construct_text_input('acronym['.$st->id.']', 2, 2, $st->acronym); $table->data[$i][] = $this->construct_text_input('acronym['.$st->id.']', 2, 2, $st->acronym)
$table->data[$i][] = $this->construct_text_input('description['.$st->id.']', 30, 30, $st->description); . $emptyacronym;
$table->data[$i][] = $this->construct_text_input('description['.$st->id.']', 30, 30, $st->description)
. $emptydescription;
$table->data[$i][] = $this->construct_text_input('grade['.$st->id.']', 4, 4, $st->grade); $table->data[$i][] = $this->construct_text_input('grade['.$st->id.']', 4, 4, $st->grade);
$table->data[$i][] = $this->construct_preferences_actions_icons($st, $prefdata); $table->data[$i][] = $this->construct_preferences_actions_icons($st, $prefdata);
@ -1003,4 +1018,16 @@ class mod_attendance_renderer extends plugin_renderer_base {
return html_writer::empty_tag('input', $attributes); return html_writer::empty_tag('input', $attributes);
} }
/**
* Construct a notice message
*
* @param string $text
* @param string $class
* @return string
*/
private function construct_notice($text, $class = 'notifymessage') {
$attributes = array('class' => $class);
return html_writer::tag('p', $text, $attributes);
}
} }

21
sessions.php

@ -33,6 +33,13 @@ $pageparams = new att_sessions_page_params();
$id = required_param('id', PARAM_INT); $id = required_param('id', PARAM_INT);
$pageparams->action = required_param('action', PARAM_INT); $pageparams->action = required_param('action', PARAM_INT);
if (empty($pageparams->action)) {
// The form on manage.php can submit with the "choose" option - this should be fixed in the long term,
// but in the meantime show a useful error and redirect when it occurs.
$url = new moodle_url('/mod/attendance/view.php', array('id' => $id));
redirect($url, get_string('invalidaction', 'mod_attendance'), 2);
}
$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); $cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', 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 = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST);
@ -119,8 +126,10 @@ switch ($att->pageparams->action) {
} }
redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance'));
} }
$sessid = required_param('sessid', PARAM_SEQUENCE); $sessid = optional_param_array('sessid', '', PARAM_SEQUENCE);
if (empty($sessid)) {
print_error('nosessionsselected', 'attendance', $att->url_manage());
}
$sessionsinfo = $att->get_sessions_info($sessid); $sessionsinfo = $att->get_sessions_info($sessid);
$message = get_string('deletecheckfull', '', get_string('session', 'attendance')); $message = get_string('deletecheckfull', '', get_string('session', 'attendance'));
@ -142,10 +151,10 @@ switch ($att->pageparams->action) {
echo $OUTPUT->footer(); echo $OUTPUT->footer();
exit; exit;
case att_sessions_page_params::ACTION_CHANGE_DURATION: case att_sessions_page_params::ACTION_CHANGE_DURATION:
$sessid = optional_param('sessid', '', PARAM_SEQUENCE); $sessid = optional_param_array('sessid', '', PARAM_SEQUENCE);
$ids = optional_param('ids', '', PARAM_ALPHANUMEXT); $ids = optional_param('ids', '', PARAM_ALPHANUMEXT);
$slist = isset($sessid) ? implode('_', $sessid) : ''; $slist = !empty($sessid) ? implode('_', $sessid) : '';
$url = $att->url_sessions(array('action' => att_sessions_page_params::ACTION_CHANGE_DURATION)); $url = $att->url_sessions(array('action' => att_sessions_page_params::ACTION_CHANGE_DURATION));
$formparams['ids'] = $slist; $formparams['ids'] = $slist;
@ -185,6 +194,10 @@ function construct_sessions_data_for_add($formdata) {
$duration = $formdata->durtime['hours']*HOURSECS + $formdata->durtime['minutes']*MINSECS; $duration = $formdata->durtime['hours']*HOURSECS + $formdata->durtime['minutes']*MINSECS;
$now = time(); $now = time();
if (empty(get_config('attendance', 'studentscanmark'))) {
$formdata->studentscanmark = 0;
}
$sessions = array(); $sessions = array();
if (isset($formdata->addmultiply)) { if (isset($formdata->addmultiply)) {
$startdate = $formdata->sessiondate; $startdate = $formdata->sessiondate;

3
settings.php

@ -41,4 +41,7 @@ if ($ADMIN->fulltree) {
$settings->add(new admin_setting_configselect('attendance/resultsperpage', $settings->add(new admin_setting_configselect('attendance/resultsperpage',
get_string('resultsperpage', 'attendance'), get_string('resultsperpage_desc', 'attendance'), 25, $options)); get_string('resultsperpage', 'attendance'), get_string('resultsperpage_desc', 'attendance'), 25, $options));
$settings->add(new admin_setting_configcheckbox('attendance/studentscanmark',
get_string('studentscanmark', 'attendance'), get_string('studentscanmark_desc', 'attendance'), 1));
} }

7
tests/behat/attendance_mod.feature

@ -78,6 +78,7 @@ Feature: Teachers and Students can record session attendance
Then "Attendance report viewed" "link" should exist Then "Attendance report viewed" "link" should exist
# Dependency - selenium running with firefox profile with auto saving of txt files to $CFG->behat_download. # Dependency - selenium running with firefox profile with auto saving of txt files to $CFG->behat_download.
# e.g. $CFG->behat_download = 'C:\\Users\\username\\Downloads\\';
@javascript @_file_download @javascript @_file_download
Scenario: Export report includes id number, department and institution Scenario: Export report includes id number, department and institution
When I log in as "teacher1" When I log in as "teacher1"
@ -89,9 +90,9 @@ Feature: Teachers and Students can record session attendance
And I click on "id_submitbutton" "button" And I click on "id_submitbutton" "button"
And I follow "Continue" And I follow "Continue"
And I follow "Export" And I follow "Export"
Then the "id_ident_idnumber" checkbox should not be checked Then the field "id_ident_idnumber" matches value ""
And the "id_ident_institution" checkbox should not be checked And the field "id_ident_institution" matches value ""
And the "id_ident_department" checkbox should not be checked And the field "id_ident_department" matches value ""
And I set the field "id_ident_idnumber" to "1" And I set the field "id_ident_idnumber" to "1"
And I set the field "id_ident_institution" to "1" And I set the field "id_ident_institution" to "1"
And I set the field "id_ident_department" to "1" And I set the field "id_ident_department" to "1"

55
tests/behat/preferences.feature

@ -0,0 +1,55 @@
@mod @uon @mod_attendance @mod_attendance_preferences
Feature: Teachers can't change status variables to have empty acronyms or descriptions
In order to update status variables
As a teacher
I need to see an error notice below each acronym / description that I try to set to be empty
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category |
| Course 1 | C101 | Prove the attendance activity settings works | 0 |
And the following "users" exist:
| username | firstname | lastname |
| student1 | Sam | Student |
| teacher1 | Teacher | One |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C101 | student |
| teacher1 | C101 | editingteacher |
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 press "Save and display"
And I follow "Settings"
@javascript
Scenario: Teachers can add status variables
# Set the second status acronym to be empty
Given I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[2]/input" to ""
# Set the second status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/input" to ""
# Set the second status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[4]/input" to ""
When I click on "Update" "button" in the "#preferencesform" "css_element"
Then I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[2]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/p" "xpath_element"
And I click on "Update" "button" in the "#preferencesform" "css_element"
# Set the first status acronym to be empty
Given I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/input" to ""
# Set the first status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/input" to ""
# Set the first status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[4]/input" to ""
# Set the third status acronym to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[2]/input" to ""
# Set the third status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[3]/input" to ""
# Set the third status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[4]/input" to ""
When I click on "Update" "button" in the "#preferencesform" "css_element"
Then I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/p" "xpath_element"
And I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[3]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[3]/td[3]/p" "xpath_element"

4
version.php

@ -22,9 +22,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
$plugin->version = 2014112001; $plugin->version = 2014112003;
$plugin->requires = 2014042900; $plugin->requires = 2014042900;
$plugin->release = '2.7.1'; $plugin->release = '2.7.2';
$plugin->maturity = MATURITY_STABLE; $plugin->maturity = MATURITY_STABLE;
$plugin->cron = 0; $plugin->cron = 0;
$plugin->component = 'mod_attendance'; $plugin->component = 'mod_attendance';

Loading…
Cancel
Save