Compare commits

...

176 Commits

Author SHA1 Message Date
Morgan Harris de770cd85e Allow group sessions in webservices (#364) 6 years ago
Dan Marsden 2a4a6e346f Fix #363 typo in key defintion - causes failure in Oracle upgrades. 6 years ago
Dan Marsden 9fed2b2b56 Fix #340 Check duration of session when checking if a student enrolled before a session. 6 years ago
Dan Marsden 67a4d16038 Fix #358 check index exists before dropping. 6 years ago
Tõnis Tartes 90648f0797 ISSUE-336 - Viewing single session table total counts in also users w… (#337) 6 years ago
Dan Marsden 73ef68c1e2 Don't save values if not numeric. 6 years ago
Dan Marsden b9e65039e1 Fix for session add/update when studentscanmark is empty but automark set to close 7 years ago
Dan Marsden 6f96016904 Fix #317 typo in html entity, and entities not rendering inside select options. 7 years ago
Dan Marsden 0de2abeede Prevent text_to_html from adding divs to email subject. 7 years ago
Dan Marsden 95d90eb931 remove cpd from 33 branch - not required in older branches. 7 years ago
Dan Marsden 7fd0d7770b Fixes #302 make sure user lang is used when sending mail. 7 years ago
Dan Marsden 7ee4db4c64 Feature: Prevent students from sharing device while self-marking. 7 years ago
Dan Marsden 6479912c3b Add class to set status for all users row. 7 years ago
Dan Marsden 297edb8d8c Check if userto is empty (extra comma in thirdpartyusers field) 7 years ago
Dan Marsden 9befe0c66c Fix issue when checking if a status set has an absent flag when updating a session. 7 years ago
Dan Marsden 267a947b41 bump version. 7 years ago
Dan Marsden 6798c344c8 add setting to allow sessiont description to show in report. 7 years ago
Dan Marsden dc94a0ed54 Show warning if teacher using mark on close and no status available. 7 years ago
Dan Marsden 52363e7b89 remove spacing. 7 years ago
Dan Marsden 5597f5f0ce Add missing string and fix upgrade previous value. 7 years ago
Nick Phillips b365a6facb Make display of user 'extrafields' optional in report. (#294) 7 years ago
Nick Phillips 2941e69810 Fix userpic max-width in attreport. Some themes (e.g. adaptable) (#295) 7 years ago
Dan Marsden 6462700904 include description format when creating event. 7 years ago
Nick Phillips 7a5a69df23 Add description to event, and group to event name if present. (#296) 7 years ago
Dan Marsden 377c0b5408 remove reference to user.de domain. 7 years ago
Dan Marsden 69dc833194 fix phpdocs. 7 years ago
Dan Marsden f045c7f34d codechecker fix. 7 years ago
Dan Marsden 3a700014c8 codechecker stuff. 7 years ago
Dan Marsden bed019e33d bump version for plugins db release. 7 years ago
Dan Marsden 561cccb681 Fix link on subnet error. 7 years ago
Dan Marsden c33127770f Revert "Fix #290 cross-db compatible method to only include sessions with absenteereport set." 7 years ago
Dan Marsden 464e0d1c5d Revert "Fix #290 correct $where statement." 7 years ago
Dan Marsden ab8a5455be Fix #292 studentpassword undefined warning 7 years ago
Dan Marsden 25e3f42638 Split out graded and ungraded sessions on students all courses list. 7 years ago
Dan Marsden d72613065f Fix #290 correct $where statement. 7 years ago
Dan Marsden 5f405665f7 Fix #290 cross-db compatible method to only include sessions with absenteereport set. 7 years ago
Dan Marsden f6902c2bc6 Hide option to automark if standard log store disabled. 7 years ago
Dan Marsden f8ab61f9f4 Fix issue that occurs when updating an individual warning. 7 years ago
Dan Marsden 66acd7e0ef Put magic number into constant. 7 years ago
Dan Marsden ebaef74abf Prevent sessions from being added to attendance activites in recycle bin. 7 years ago
Dan Marsden c17174598f Fix #287 prevent restore from setting takenby when no userdata restored. 7 years ago
Dan Marsden 0e1e449109 Exclude ungraded attendance activities from student all courses average. 7 years ago
Dan Marsden b3ed9f6217 Add missing fields to csv and set defaults when not mapped. 7 years ago
Dan Marsden bb30545010 Correct list of groups for each session. 7 years ago
Dan Marsden a55eadcc72 remove old comment. 7 years ago
Dan Marsden 7fb1fbfd01 Improve auto-marking when no password. 7 years ago
James Voong d69d70172e Added ability to automatically mark all students with a status 7 years ago
Dan Marsden 109a1f02af include absenteereport field in externalib structure. 7 years ago
Dan Marsden 2c5a2c8e89 Add absenteereport to class when adding session. 7 years ago
Dan Marsden ec75d8095b Add missing backup field, and new field in tests. 7 years ago
Dan Marsden c38a02cd0e Add ability to exlude sessions from absentee report calculations. 7 years ago
Dan Marsden 39732e8c2f fix travis config. 7 years ago
Dan Marsden 11362ad458 Add output buffering level for progress bar. 7 years ago
Chris Wharton c1f1f9f765 Allow bulk importing of attendance sessions 7 years ago
Dan Marsden 105ae71bf1 Improve error handling when adding a new status fails. 7 years ago
Dan Marsden b88244a706 Prevent error when updating status when student marking disabled. 7 years ago
Dan Marsden 2e8387442c update travis commands. 7 years ago
Dan Marsden 9bb37e5668 fix phpdoc warning. 7 years ago
Dan Marsden c56313c9f6 Update travis config. 7 years ago
Dan Marsden 8d8322229e Fix phpdocs. 7 years ago
Dan Marsden c0a1d50e52 bump version for plugins db release. 7 years ago
Dan Marsden b64c0ead61 try behat on precise. 7 years ago
Dan Marsden 9b71d5ee4d Don't need to call composer selfupdate. 7 years ago
Dan Marsden 9c13beca74 Fixes #270 - allow activity level warnings to be deleted. 7 years ago
Dan Marsden 96fb1017f7 Add description to warnings/status set. 7 years ago
Dan Marsden fe20e88964 Use attendance-id instead of cmid in warning table. 7 years ago
Dan Marsden bf7e16f2c9 Add missing activity level subnet field to backup. 7 years ago
Dan Marsden 37f5a2735f Bump version. 7 years ago
Dan Marsden c75a060d65 Fix behat test with at-risk change to absentee 7 years ago
Dan Marsden 5ce7ee488c Rename at-risk report to absentee report. 7 years ago
Dan Marsden 9488a5585b Don't show 0% in user report if no taken sessions. 7 years ago
Dan Marsden e8f7cbcc0b Swap multiple sessions settings and student recording settings 8 years ago
Dan Marsden d8357769cc Improve use of triggered string. is really first notified. 8 years ago
Dan Marsden fc3078ecd3 Feature: Allow warnings to send multiple emails. 8 years ago
Dan Marsden afe0a72e7f Fixes #269 Allow Calendar events to be enabled/disabled 8 years ago
Dan Marsden b9c07817f6 Drop key doesn't work well, drop incorrect key and add correct one. 8 years ago
Dan Marsden d9c1236772 Allow warnings with same percentage but different warnlevel. 8 years ago
Dan Marsden 2aea1988e9 Fix url used in sort column. 8 years ago
Dan Marsden 9f2251c315 Make tables full width on small screens. 8 years ago
Dan Marsden ea32a3535f fix spacing in css file. 8 years ago
Dan Marsden 4bbed2c160 Improve responsive display of user pages. 8 years ago
Dan Marsden e2580f491d fix php warning when no status set. 8 years ago
Dan Marsden 332ee78344 Fix incorrect class namespacing. 8 years ago
Dan Marsden 782f4086e0 Use lasttaken instead of session end to obtain sessions to calculate 8 years ago
Dan Marsden a27adac3e9 Improve messages in cron, check end of session correctly. 8 years ago
Dan Marsden 3260294bfe Improve css on status set page. 8 years ago
Dan Marsden 9fc4221a81 Fix typo in var translation. 8 years ago
Dan Marsden ad6919e3ec Add new capability to control who gets warning emails. 8 years ago
Dan Marsden e487adaccc Fix bug with userdisplay check. 8 years ago
Dan Marsden 49b7ca4999 Simplify user view page to make it better on small devices 8 years ago
Dan Marsden 296a696904 Fix behat 3.2 tests. 8 years ago
Dan Marsden 181cd6234b Set default sort on atrisk page, 8 years ago
Dan Marsden 60a2f79bbb Add some button classes to attendance buttons to improve display. 8 years ago
Dan Marsden a985d1dab3 Fix style for password pop-up and mustache template fix. 8 years ago
Dan Marsden 2eb0517644 coding guidline improvements. 8 years ago
Dan Marsden 39644e9476 Improve password-pop-up handling. 8 years ago
Dan Marsden 70e1bb6e59 Add category selector to coursesummary admin report. 8 years ago
Dan Marsden 18d46879c5 fix breadcrumbs in atrisk and coursesummary reports. 8 years ago
Dan Marsden 478d7fee2c Merge notification/warning feature into master. (#267) 8 years ago
Dan Marsden 2d9fb84dd1 Allow default subnet setting at activity level to be hidden. 8 years ago
Dan Marsden ae91534ddc Fix hardcoded reference to mdl_ 8 years ago
Dan Marsden bf93d84aa9 Fixes #266 - groupmemembersonly setting removed by MDL-44725 8 years ago
Dan Marsden 959f576025 Use correct link to show all sessions in behat test. 8 years ago
Dan Marsden 34183f0c5c Fix version and changelog. 8 years ago
Dan Marsden 5547855ad0 Fix coding guideline typo. 8 years ago
Dan Marsden f8420243ff Improve display of "all courses" user report - display as table 8 years ago
Dan Marsden 1e65aa6785 Task doesn't just look at closed sessions. 8 years ago
Dan Marsden f6b5190fe0 New Feature: allow automarking of attendance using logs table. 8 years ago
Dan Marsden 5337794927 Fixes #265 create calendar events when restoring an attendance. 8 years ago
Dan Marsden 201601e359 Fix #264 improve radio button spacing on self-marking page 8 years ago
Dan Marsden 39f2b19832 Allow default view on login to be set at admin level. 8 years ago
Dan Marsden 8a94c10a09 Resort strings in alphabetical order 8 years ago
Dan Marsden 35c1712ed4 bump version to match block requirements. 8 years ago
Dan Marsden 38f5469cf6 Add lang strings for block and config course category report. 8 years ago
Dan Marsden 70569a53df fix some coding guideline things. 8 years ago
Dan Marsden 8fdf17d30f Modify subnet field to hopefully improve usability with teachers. 8 years ago
Dan Marsden 7ca915eb68 New site-level/course category report for average course attendance. 8 years ago
Dan Marsden 17f51afaf1 Move subnet to advanced setting on session form. 8 years ago
Dan Marsden 142bafb5ef Lasttakenby = 0 is a valid id when auto-marked. 8 years ago
Dan Marsden 142fc543cc when course reset, reset automarkcompleted just in case. 8 years ago
Dan Marsden 2d9c88224c Feature: Allow unmarked students to be automatically marked after session close. 8 years ago
Dan Marsden eccf8784d1 Sanity check - make sure session id is for this attendance. 8 years ago
Dan Marsden af7d666777 fix space issue in test. 8 years ago
Dan Marsden f34760ca17 Fix some coding guideline issues. 8 years ago
Dan Marsden 9eaf63b746 Fix some tests. 8 years ago
Dan Marsden d4498ccebe fix some eslint stuff. 8 years ago
Dan Marsden 00ce366a41 Move subnet from attendance level to session level 8 years ago
Dan Marsden e89c46aaa9 fix case of am/pm in tests. 8 years ago
Dan Marsden f2df0d239e Fixes #262 improves compatibility with Mac/Win when converting times. 8 years ago
Dan Marsden 3569d778d5 Add new field for student availability, 8 years ago
Dan Marsden 40721dc1d5 Fixes #262 date time not displaying on windows servers. 8 years ago
Dan Marsden 22a98145cf Improve error when incorrect password used. 8 years ago
Dan Marsden be48371a17 bump version to clear css cache. 8 years ago
Dan Marsden 913c35aa35 Add abilty to view student password from session list page. 8 years ago
Dan Marsden c668a63213 Fixes #258 add link to take attendance on student overview report. 8 years ago
Dan Marsden 1617ce7cdd fix eslint warning. 8 years ago
Dan Marsden 9de4a283fe phpdoc stuff. 8 years ago
Dan Marsden f9c61f4efc more phpdoc changes. 8 years ago
Dan Marsden c6739acf7f fix some phpdoc stuff. 8 years ago
Dan Marsden 9766cae6ef behat test (fragile) 8 years ago
Dan Marsden c55d38af16 fix up some phpdoc stuff. 8 years ago
Dan Marsden 6122dc179d Tidy up the classes/phpdocs a bit. 8 years ago
Dan Marsden 848e1f6349 make session full day for tests. 8 years ago
Dan Marsden 92ddadb87f tests - use sessiondate for 5min ago to allow self student marking 8 years ago
Dan Marsden d73402bf7a Add setting to force self-marking during session. 8 years ago
Dan Marsden 67fc97868d travis php 5.6 passing correctly now. 8 years ago
Dan Marsden 4ad3201de8 allow admin to set default values for record own attendance/random pass. 8 years ago
Dan Marsden 2e0bfea6bf coding guidline fix. 8 years ago
Dan Marsden d5618de89d use custom function to generate a very basic random string 8 years ago
Dan Marsden 25e3f2f7a0 fix some codechecker warnings. 8 years ago
Dan Marsden cc8db5bab9 allow random password to be used when creating sessions. 8 years ago
Dan Marsden 4ba19a86ca set default values a bit better. 8 years ago
Dan Marsden 51751271f1 fix notnull values. 8 years ago
Dan Marsden fb96b30c29 Allow a password to be set when using student self marking. 8 years ago
Dan Marsden f1311f3cf2 attempt to fix php 5.6 issue in tests. 8 years ago
Dan Marsden 84d0609ac6 fix php warning in php 5.6 8 years ago
Dan Marsden 8ca6c26f96 coding guideline spacing 8 years ago
Dan Marsden c2282bc6ae Fix #259 undefined property warnings when showing temp users. 8 years ago
Dan Marsden 6d8c2c2418 Fixes #254 - issue with colspan in header/footer. 8 years ago
Dan Marsden 9bf96ab3bc Fix broken tests due to identity patch. 8 years ago
Dan Marsden 0e31f6796a Fix some coding guideline issues. 8 years ago
Dan Marsden 73011e5bdb Fixes #63 use core useridentity setting when showing list of users. 8 years ago
Dan Marsden f02845a616 Use same test structure as master. 8 years ago
Dan Marsden 1936ddf929 Refactor duplicated renderer. 8 years ago
Dan Marsden 64e6df0154 Add test for changing default staus set, 8 years ago
Dan Marsden 7c3845ae58 Fix #187 allow default status set to be editable. 8 years ago
Daniel Thee Roperto b79dac055f Moodle 32 - Global Search issue with Database Schema (#248) 8 years ago
Dan Marsden a624a59139 Add ability to set default value for subnet restriction. 8 years ago
Dan Marsden 4a7789f1df Remove inclusion of config file. 8 years ago
Dan Marsden 682ce4af85 Fix coding guideline stuff found by plugins db check. 8 years ago
Dan Marsden 27095c459e Bump for plugins db release 8 years ago
Dan Marsden 9ef35c4ea4 Fix coding guideline issues. 8 years ago
Dan Marsden 49714bf427 Fixes #241 convert deprecated hook to observer. 8 years ago
Dan Marsden eef7cf7afe fix behat test. 8 years ago
Dan Marsden 6895d64c92 Fixes #240 Save comment fields even when status not set. 8 years ago
Dan Marsden 4cff027414 Update travis to check 3.2 branch. 8 years ago
  1. 25
      .travis.yml
  2. 35
      CHANGELOG.md
  3. 140
      absentee.php
  4. 163
      add_form.php
  5. 54
      attendance.php
  6. 20
      backup/moodle2/backup_attendance_stepslib.php
  7. 34
      backup/moodle2/restore_attendance_stepslib.php
  8. 19
      calendar.js
  9. 113
      classes/add_warning_form.php
  10. 39
      classes/attendance_webservices_handler.php
  11. 35
      classes/calendar_helpers.php
  12. 93
      classes/event/session_ip_shared.php
  13. 92
      classes/event/sessions_imported.php
  14. 2
      classes/event/status_updated.php
  15. 86
      classes/form/import/sessions.php
  16. 73
      classes/form/import/sessions_confirm.php
  17. 80
      classes/header.php
  18. 497
      classes/import/sessions.php
  19. 7
      classes/manage_page_params.php
  20. 57
      classes/observer.php
  21. 57
      classes/page_with_filter_controls.php
  22. 12
      classes/preferences_page_params.php
  23. 22
      classes/report_page_params.php
  24. 23
      classes/sessions_page_params.php
  25. 469
      classes/structure.php
  26. 26
      classes/summary.php
  27. 25
      classes/take_page_params.php
  28. 230
      classes/task/auto_mark.php
  29. 158
      classes/task/notify.php
  30. 16
      classes/view_page_params.php
  31. 124
      coursesummary.php
  32. 21
      db/access.php
  33. 36
      db/events.php
  34. 3
      db/install.php
  35. 54
      db/install.xml
  36. 2
      db/services.php
  37. 44
      db/tasks.php
  38. 296
      db/upgrade.php
  39. 7
      db/upgradelib.php
  40. 131
      defaultstatus.php
  41. 68
      export.php
  42. 6
      export_form.php
  43. 64
      externallib.php
  44. 94
      import/sessions.php
  45. 361
      lang/en/attendance.php
  46. 159
      lib.php
  47. 698
      locallib.php
  48. 6
      manage.php
  49. 17
      mod_form.php
  50. 8
      module.js
  51. 51
      password.php
  52. 50
      password_ajax.php
  53. 1
      pix/key.svg
  54. 40
      preferences.php
  55. 397
      renderables.php
  56. 593
      renderer.php
  57. 140
      renderhelpers.php
  58. 8
      report.php
  59. 92
      resetcalendar.php
  60. 97
      sessions.php
  61. 117
      settings.php
  62. 78
      student_attendance_form.php
  63. 109
      styles.css
  64. 3
      take.php
  65. 21
      temp_form.php
  66. 20
      tempedit_form.php
  67. 21
      templates/attendance_password_icon.mustache
  68. 28
      templates/attendance_password_icon_boost.mustache
  69. 21
      tempmerge_form.php
  70. 14
      tempusers.php
  71. 56
      tests/attendance_webservices_test.php
  72. 27
      tests/behat/attendance_mod.feature
  73. 31
      tests/behat/defaultstatus.feature
  74. 16
      tests/behat/extra_features.feature
  75. 2
      tests/behat/preferences.feature
  76. 488
      tests/behat/report.feature
  77. 9
      tests/generator/lib.php
  78. 116
      update_form.php
  79. 4
      version.php
  80. 2
      view.php
  81. 203
      warnings.php
  82. 5
      yui/build/moodle-mod_attendance-groupfilter/moodle-mod_attendance-groupfilter-debug.js
  83. 5
      yui/build/moodle-mod_attendance-groupfilter/moodle-mod_attendance-groupfilter.js
  84. 5
      yui/src/groupfilter/js/groupfilter.js

25
.travis.yml

@ -1,6 +1,9 @@
language: php
sudo: required
sudo: true
addons:
firefox: "47.0.1"
cache:
directories:
@ -10,20 +13,17 @@ php:
- 5.6
- 7.0
matrix:
allow_failures:
- php: 5.6 # travis seems to fail on adding activity - doesn't fail when running behat locally.
env:
matrix:
- DB=pgsql MOODLE_BRANCH=master
- DB=mysqli MOODLE_BRANCH=master
- DB=pgsql MOODLE_BRANCH=MOODLE_32_STABLE
- DB=mysqli MOODLE_BRANCH=MOODLE_32_STABLE
before_install:
- phpenv config-rm xdebug.ini
- nvm install 8.9
- nvm use 8.9
- cd ../..
- composer selfupdate
- composer create-project -n --no-dev --prefer-dist moodlerooms/moodle-plugin-ci ci ^1
- composer create-project -n --no-dev --prefer-dist moodlerooms/moodle-plugin-ci ci ^2
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
@ -31,12 +31,11 @@ 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 savepoints
- moodle-plugin-ci mustache
- moodle-plugin-ci grunt
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat

35
CHANGELOG.md

@ -0,0 +1,35 @@
### Date: 2017-June-22
### Release: 3.2.14
- New Feature: Allow automatic marking using site logs.
- New Feature: Warn users when attendance drops below threshold.
- Improvement: Allow default view for teachers to be set at admin level.
- Improvement: All courses user report now displays as table.
- Bug fix: Restored attendances do not create calendar events correctly.
### Date: 2017-May-23
### Release: 3.2.12
- New Feature: New site Level/course category report with average course attendance.
- New Feature: Allow unmarked students to be automatically marked when session closes.
---
### Date: 2017-May-11
- New Feature: Allow subnet mask to be set at the attendance session level.
- New Feature: Allow certain statuses to be hidden from students when self-marking attendance.
- New Feature: Allow student password to be viewed on session list page.
- Improvement: Improve usablity by grouping settings on session add form.
- Bug fix - fix issue with displaying dates when site hosted on Windows server.
- Bug fix - improve compliance with Moodle coding guidelines.
---
### Date: 2017-Apr-21
- Feature: Allow a random self-marking password to be used when creating session.
- Improvement: #63 use core useridentity setting when showing list of users.
- Improvement: #258 Add link to attendance on student overview report.
- Improvement: allow student self-marking to be restricted to the session time.
- Improvement: allow admin to set default values when teachers creating new sessions.
- Bug fix - improve compliance with Moodle coding guidelines - phpdocs etc.
---

140
absentee.php

@ -0,0 +1,140 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance course summary report.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/mod/attendance/lib.php');
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir.'/coursecatlib.php');
$category = optional_param('category', 0, PARAM_INT);
$attendancecm = optional_param('id', 0, PARAM_INT);
$download = optional_param('download', '', PARAM_ALPHA);
$sort = optional_param('tsort', 'timesent', PARAM_ALPHA);
if (!empty($category)) {
$context = context_coursecat::instance($category);
$coursecat = coursecat::get($category);
$courses = $coursecat->get_courses(array('recursive' => true, 'idonly' => true));
$PAGE->set_category_by_id($category);
require_login();
} else if (!empty($attendancecm)) {
$cm = get_coursemodule_from_id('attendance', $attendancecm, 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);
$courses = array($course->id);
$context = context_module::instance($cm->id);
require_login($course, false, $cm);
} else {
admin_externalpage_setup('managemodules');
$context = context_system::instance();
$courses = array(); // Show all courses.
}
// Check permissions.
require_capability('mod/attendance:viewreports', $context);
$exportfilename = 'attendance-absentee.csv';
$PAGE->set_url('/mod/attendance/absentee.php', array('category' => $category, 'id' => $attendancecm));
$PAGE->set_heading($SITE->fullname);
$table = new flexible_table('attendanceabsentee');
$table->define_baseurl($PAGE->url);
if (!$table->is_downloading($download, $exportfilename)) {
if (!empty($attendancecm)) {
$pageparams = new mod_attendance_sessions_page_params();
$att = new mod_attendance_structure($att, $cm, $course, $context, $pageparams);
$output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_ABSENTEE);
echo $output->header();
echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname));
echo $output->render($tabs);
} else {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('absenteereport', 'mod_attendance'));
if (empty($category)) {
// Only show tabs if displaying via the admin page.
$tabmenu = attendance_print_settings_tabs('absentee');
echo $tabmenu;
}
}
}
$table->define_columns(array('coursename', 'aname', 'userid', 'numtakensessions', 'percent', 'timesent'));
$table->define_headers(array(get_string('course'),
get_string('pluginname', 'attendance'),
get_string('user'),
get_string('takensessions', 'attendance'),
get_string('averageattendance', 'attendance'),
get_string('triggered', 'attendance')));
$table->sortable(true);
$table->set_attribute('cellspacing', '0');
$table->set_attribute('class', 'generaltable generalbox');
$table->show_download_buttons_at(array(TABLE_P_BOTTOM));
$table->setup();
// Work out direction of sort required.
$sortcolumns = $table->get_sort_columns();
// Now do sorting if specified.
$orderby = ' ORDER BY percent ASC';
if (!empty($sort)) {
$direction = ' DESC';
if (!empty($sortcolumns[$sort]) && $sortcolumns[$sort] == SORT_ASC) {
$direction = ' ASC';
}
$orderby = " ORDER BY $sort $direction";
}
$records = attendance_get_users_to_notify($courses, $orderby);
foreach ($records as $record) {
if (!$table->is_downloading($download, $exportfilename)) {
$url = new moodle_url('/mod/attendance/index.php', array('id' => $record->courseid));
$name = html_writer::link($url, $record->coursename);
} else {
$name = $record->coursename;
}
$url = new moodle_url('/mod/attendance/view.php', array('studentid' => $record->userid,
'id' => $record->cmid, 'view' => ATT_VIEW_ALL));
$attendancename = html_writer::link($url, $record->aname);
$username = html_writer::link($url, fullname($record));
$percent = round($record->percent * 100)."%";
$timesent = "-";
if (!empty($record->timesent)) {
$timesent = userdate($record->timesent);
}
$table->add_data(array($name, $attendancename, $username, $record->numtakensessions, $percent, $timesent));
}
$table->finish_output();
if (!$table->is_downloading()) {
echo $OUTPUT->footer();
}

163
add_form.php

@ -48,6 +48,8 @@ class mod_attendance_add_form extends moodleform {
$cm = $this->_customdata['cm'];
$modcontext = $this->_customdata['modcontext'];
$pluginconfig = get_config('attendance');
$mform->addElement('header', 'general', get_string('addsession', 'attendance'));
$groupmode = groups_get_activity_groupmode($cm);
@ -119,23 +121,26 @@ class mod_attendance_add_form extends moodleform {
$mform->setType('statusset', PARAM_INT);
}
// Students can mark own attendance.
if (!empty(get_config('attendance', 'studentscanmark'))) {
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
} else {
$mform->addElement('hidden', 'studentscanmark', '0');
$mform->settype('studentscanmark', PARAM_INT);
}
$mform->addElement('editor', 'sdescription', get_string('description', 'attendance'), array('rows' => 1, 'columns' => 80),
array('maxfiles' => EDITOR_UNLIMITED_FILES, 'noclean' => true, 'context' => $modcontext));
$mform->setType('sdescription', PARAM_RAW);
// If warnings allow selector for reporting.
if (!empty(get_config('attendance', 'enablewarnings'))) {
$mform->addElement('checkbox', 'absenteereport', '', get_string('includeabsentee', 'attendance'));
$mform->addHelpButton('absenteereport', 'includeabsentee', 'attendance');
if (isset($pluginconfig->absenteereport_default)) {
$mform->setDefault('absenteereport', $pluginconfig->absenteereport_default);
}
} else {
$mform->addElement('hidden', 'absenteereport', 1);
$mform->setType('absenteereport', PARAM_INT);
}
// For multiple sessions.
$mform->addElement('header', 'headeraddmultiplesessions', get_string('addmultiplesessions', 'attendance'));
if (!empty($pluginconfig->multisessionexpanded)) {
$mform->setExpanded('headeraddmultiplesessions');
}
$mform->addElement('checkbox', 'addmultiply', '', get_string('repeatasfollows', 'attendance'));
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
@ -156,7 +161,7 @@ class mod_attendance_add_form extends moodleform {
$mform->disabledIf('sdays', 'addmultiply', 'notchecked');
$period = array(1 => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36);
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36);
$periodgroup = array();
$periodgroup[] =& $mform->createElement('select', 'period', '', $period, false, true);
$periodgroup[] =& $mform->createElement('static', 'perioddesc', '', get_string('week', 'attendance'));
@ -172,6 +177,114 @@ class mod_attendance_add_form extends moodleform {
$mform->addElement('hidden', 'previoussessiondate', 0);
$mform->setType('previoussessiondate', PARAM_INT);
// Students can mark own attendance.
if (!empty(get_config('attendance', 'studentscanmark'))) {
$mform->addElement('header', 'headerstudentmarking', get_string('studentmarking', 'attendance'), true);
if (!empty($pluginconfig->studentrecordingexpanded)) {
$mform->setExpanded('headerstudentmarking');
}
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
$options = attendance_get_automarkoptions();
$mform->addElement('select', 'automark', get_string('automark', 'attendance'), $options);
$mform->setType('automark', PARAM_INT);
$mform->addHelpButton('automark', 'automark', 'attendance');
$mform->disabledif('automark', 'studentscanmark', 'notchecked');
$mform->setDefault('automark', $this->_customdata['att']->automark);
$mgroup = array();
$mgroup[] = & $mform->createElement('text', 'studentpassword', get_string('studentpassword', 'attendance'));
$mgroup[] = & $mform->createElement('checkbox', 'randompassword', '', get_string('randompassword', 'attendance'));
$mform->addGroup($mgroup, 'passwordgrp', get_string('passwordgrp', 'attendance'), array(' '), false);
$mform->setType('studentpassword', PARAM_TEXT);
$mform->disabledif('studentpassword', 'studentscanmark', 'notchecked');
$mform->addHelpButton('passwordgrp', 'passwordgrp', 'attendance');
$mform->disabledif('randompassword', 'studentscanmark', 'notchecked');
$mform->disabledif('studentpassword', 'randompassword', 'checked');
$mform->disabledif('studentpassword', 'automark', 'eq', ATTENDANCE_AUTOMARK_ALL);
$mform->disabledif('randompassword', 'automark', 'eq', ATTENDANCE_AUTOMARK_ALL);
$mform->addElement('checkbox', 'autoassignstatus', '', get_string('autoassignstatus', 'attendance'));
$mform->addHelpButton('autoassignstatus', 'autoassignstatus', 'attendance');
$mform->disabledif('autoassignstatus', 'studentscanmark', 'notchecked');
if (isset($pluginconfig->autoassignstatus)) {
$mform->setDefault('autoassignstatus', $pluginconfig->autoassignstatus);
}
if (isset($pluginconfig->studentscanmark_default)) {
$mform->setDefault('studentscanmark', $pluginconfig->studentscanmark_default);
}
if (isset($pluginconfig->randompassword_default)) {
$mform->setDefault('randompassword', $pluginconfig->randompassword_default);
}
if (isset($pluginconfig->automark_default)) {
$mform->setDefault('automark', $pluginconfig->automark_default);
}
$mgroup2 = array();
$mgroup2[] = & $mform->createElement('text', 'subnet', get_string('requiresubnet', 'attendance'));
if (empty(get_config('attendance', 'subnetactivitylevel'))) {
$mform->setDefault('subnet', get_config('attendance', 'subnet'));
} else {
$mform->setDefault('subnet', $this->_customdata['att']->subnet);
}
$mgroup2[] = & $mform->createElement('checkbox', 'usedefaultsubnet', get_string('usedefaultsubnet', 'attendance'));
$mform->setDefault('usedefaultsubnet', 1);
$mform->setType('subnet', PARAM_TEXT);
$mform->addGroup($mgroup2, 'subnetgrp', get_string('requiresubnet', 'attendance'), array(' '), false);
$mform->setAdvanced('subnetgrp');
$mform->addHelpButton('subnetgrp', 'requiresubnet', 'attendance');
$mform->disabledif('usedefaultsubnet', 'studentscanmark', 'notchecked');
$mform->disabledif('subnet', 'studentscanmark', 'notchecked');
$mform->disabledif('subnet', 'usedefaultsubnet', 'checked');
$mgroup3 = array();
$mgroup3[] = & $mform->createElement('checkbox', 'preventsharedip', '');
$mgroup3[] = & $mform->createElement('text', 'preventsharediptime',
get_string('preventsharediptime', 'attendance'), '', 'test');
$mgroup3[] = & $mform->createElement('static', 'preventsharediptimedesc', '',
get_string('preventsharedipminutes', 'attendance'));
$mform->addGroup($mgroup3, 'preventsharedgroup', get_string('preventsharedip', 'attendance'), array(' '), false);
$mform->addHelpButton('preventsharedgroup', 'preventsharedip', 'attendance');
$mform->setAdvanced('preventsharedgroup');
$mform->setType('preventsharediptime', PARAM_INT);
$mform->disabledif('preventsharedgroup', 'studentscanmark', 'notchecked');
$mform->disabledif('preventsharedip', 'studentscanmark', 'notchecked');
$mform->disabledif('preventsharediptime', 'studentscanmark', 'notchecked');
$mform->disabledIf('preventsharediptime', 'preventsharedip', 'notchecked');
if (isset($pluginconfig->preventsharedip)) {
$mform->setDefault('preventsharedip', $pluginconfig->preventsharedip);
}
if (isset($pluginconfig->preventsharediptime)) {
$mform->setDefault('preventsharediptime', $pluginconfig->preventsharediptime);
}
} else {
$mform->addElement('hidden', 'studentscanmark', '0');
$mform->settype('studentscanmark', PARAM_INT);
$mform->addElement('hidden', 'automark', '0');
$mform->setType('automark', PARAM_INT);
$mform->addElement('hidden', 'autoassignstatus', '0');
$mform->setType('autoassignstatus', PARAM_INT);
$mform->addElement('hidden', 'subnet', '');
$mform->setType('subnet', PARAM_TEXT);
$mform->addElement('hidden', 'preventsharedip', '0');
$mform->setType('preventsharedip', PARAM_INT);
$sharedtime = isset($pluginconfig->preventsharediptime) ? $pluginconfig->preventsharediptime : null;
$mform->addElement('hidden', 'preventsharediptime', $sharedtime);
$mform->setType('preventsharediptime', PARAM_INT);
}
$this->add_action_buttons(true, get_string('add', 'attendance'));
}
@ -181,6 +294,7 @@ class mod_attendance_add_form extends moodleform {
* @param array $files
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
$sesstarttime = $data['sestime']['starthour'] * HOURSECS + $data['sestime']['startminute'] * MINSECS;
@ -218,9 +332,34 @@ class mod_attendance_add_form extends moodleform {
$this->_form->setConstant('previoussessiondate', $data['sessiondate']);
}
if (!empty($data['studentscanmark']) && $data['automark'] == ATTENDANCE_AUTOMARK_CLOSE) {
$cm = $this->_customdata['cm'];
// Check that the selected statusset has a status to use when unmarked.
$sql = 'SELECT id
FROM {attendance_statuses}
WHERE deleted = 0 AND (attendanceid = 0 or attendanceid = ?)
AND setnumber = ? AND setunmarked = 1';
$params = array($cm->instance, $data['statusset']);
if (!$DB->record_exists_sql($sql, $params)) {
$errors['automark'] = get_string('noabsentstatusset', 'attendance');
}
}
if (!empty($data['studentscanmark']) && !empty($data['preventsharedip']) &&
empty($data['preventsharediptime'])) {
$errors['preventsharedgroup'] = get_string('iptimemissing', 'attendance');
}
return $errors;
}
/**
* Check weekdays function.
* @param int $sessiondate
* @param int $sessionenddate
* @param int $sdays
* @return bool
*/
private function checkweekdays($sessiondate, $sessionenddate, $sdays) {
$found = false;

54
attendance.php

@ -17,8 +17,8 @@
/**
* Prints attendance info for particular user
*
* @package mod
* @subpackage attendance
* @package mod_attendance
* @copyright 2014 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -39,14 +39,16 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST)
// Require the user is logged in.
require_login($course, true, $cm);
if (empty(get_config('attendance', 'studentscanmark')) || empty($attforsession->studentscanmark)) {
if (!attendance_can_student_mark($attforsession)) {
// TODO: should we add a log message here? - student has got to submit page but cannot save attendance (time ran out?)
redirect(new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)));
exit;
}
// Check if subnet is set and if the user is in the allowed range.
if (!empty($attendance->subnet) && !address_in_subnet(getremoteaddr(), $attendance->subnet)) {
notice(get_string('subnetwrong', 'attendance'));
if (!empty($attforsession->subnet) && !address_in_subnet(getremoteaddr(), $attforsession->subnet)) {
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
notice(get_string('subnetwrong', 'attendance'), $url);
exit; // Notice calls this anyway.
}
@ -56,6 +58,26 @@ $att = new mod_attendance_structure($attendance, $cm, $course, $PAGE->context, $
// Require that a session key is passed to this page.
require_sesskey();
// Check to see if autoassignstatus is in use and no password required.
if ($attforsession->autoassignstatus && empty($attforsession->studentpassword)) {
$statusid = attendance_session_get_highest_status($att, $attforsession);
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
if (empty($statusid)) {
print_error('attendance_no_status', 'mod_attendance', $url);
}
$take = new stdClass();
$take->status = $statusid;
$take->sessid = $attforsession->id;
$success = $att->take_from_student($take);
if ($success) {
// Redirect back to the view page.
redirect($url, get_string('studentmarked', 'attendance'));
} else {
print_error('attendance_already_submitted', 'mod_attendance', $url);
}
}
// Create the form.
$mform = new mod_attendance_student_attendance_form(null,
array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att));
@ -67,15 +89,30 @@ if ($mform->is_cancelled()) {
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
redirect($url);
} else if ($fromform = $mform->get_data()) {
// Check if password required and if set correctly.
if (!empty($attforsession->studentpassword) &&
$attforsession->studentpassword !== $fromform->studentpassword) {
$url = new moodle_url('/mod/attendance/attendance.php', array('sessid' => $id, 'sesskey' => sesskey()));
redirect($url, get_string('incorrectpassword', 'mod_attendance'), null, \core\output\notification::NOTIFY_ERROR);
}
if ($attforsession->autoassignstatus) {
$fromform->status = attendance_session_get_highest_status($att, $attforsession);
if (empty($fromform->status)) {
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
print_error('attendance_no_status', 'mod_attendance', $url);
}
}
if (!empty($fromform->status)) {
$success = $att->take_from_student($fromform);
$url = new moodle_url('/mod/attendance/view.php', array('id' => $cm->id));
if ($success) {
// Redirect back to the view page for the block.
redirect($url);
// Redirect back to the view page.
redirect($url, get_string('studentmarked', 'attendance'));
} else {
print_error ('attendance_already_submitted', 'mod_attendance', $url);
print_error('attendance_already_submitted', 'mod_attendance', $url);
}
}
@ -92,3 +129,4 @@ $output = $PAGE->get_renderer('mod_attendance');
echo $output->header();
$mform->display();
echo $output->footer();

20
backup/moodle2/backup_attendance_stepslib.php

@ -44,16 +44,22 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
// XML nodes declaration - non-user data.
$attendance = new backup_nested_element('attendance', array('id'), array(
'name', 'grade', 'showsessiondetails', 'sessiondetailspos'));
'name', 'intro', 'introformat', 'grade', 'showextrauserdetails', 'showsessiondetails', 'sessiondetailspos', 'subnet'));
$statuses = new backup_nested_element('statuses');
$status = new backup_nested_element('status', array('id'), array(
'acronym', 'description', 'grade', 'visible', 'deleted', 'setnumber'));
'acronym', 'description', 'grade', 'studentavailability', 'setunmarked', 'visible', 'deleted', 'setnumber'));
$warnings = new backup_nested_element('warnings');
$warning = new backup_nested_element('warning', array('id'), array('warningpercent', 'warnafter',
'maxwarn', 'emailuser', 'emailsubject', 'emailcontent', 'emailcontentformat', 'thirdpartyemails'));
$sessions = new backup_nested_element('sessions');
$session = new backup_nested_element('session', array('id'), array(
'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby',
'timemodified', 'description', 'descriptionformat', 'studentscanmark', 'statusset', 'caleventid'));
'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby', 'timemodified',
'description', 'descriptionformat', 'studentscanmark', 'studentpassword', 'autoassignstatus',
'subnet', 'automark', 'automarkcompleted', 'statusset', 'absenteereport', 'preventsharedip',
'preventsharediptime', 'caleventid'));
// XML nodes declaration - user data.
$logs = new backup_nested_element('logs');
@ -64,6 +70,9 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
$attendance->add_child($statuses);
$statuses->add_child($status);
$attendance->add_child($warnings);
$warnings->add_child($warning);
$attendance->add_child($sessions);
$sessions->add_child($session);
@ -76,6 +85,9 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
$status->set_source_table('attendance_statuses', array('attendanceid' => backup::VAR_PARENTID));
$warning->set_source_table('attendance_warning',
array('idnumber' => backup::VAR_PARENTID));
$session->set_source_table('attendance_sessions', array('attendanceid' => backup::VAR_PARENTID));
// Data sources - user related data.

34
backup/moodle2/restore_attendance_stepslib.php

@ -49,6 +49,9 @@ class restore_attendance_activity_structure_step extends restore_activity_struct
$paths[] = new restore_path_element('attendance_status',
'/activity/attendance/statuses/status');
$paths[] = new restore_path_element('attendance_warning',
'/activity/attendance/warnings/warning');
$paths[] = new restore_path_element('attendance_session',
'/activity/attendance/sessions/session');
@ -100,6 +103,21 @@ class restore_attendance_activity_structure_step extends restore_activity_struct
$this->set_mapping('attendance_status', $oldid, $newitemid);
}
/**
* Process attendance warning restore
* @param object $data The data in object form
* @return void
*/
protected function process_attendance_warning($data) {
global $DB;
$data = (object)$data;
$data->idnumber = $this->get_new_parentid('attendance');
$DB->insert_record('attendance_warning', $data);
}
/**
* Process attendance session restore
* @param object $data The data in object form
@ -108,19 +126,31 @@ class restore_attendance_activity_structure_step extends restore_activity_struct
protected function process_attendance_session($data) {
global $DB;
$userinfo = $this->get_setting_value('userinfo'); // Are we including userinfo?
$data = (object)$data;
$oldid = $data->id;
$data->attendanceid = $this->get_new_parentid('attendance');
$data->groupid = $this->get_mappingid('group', $data->groupid);
$data->sessdate = $this->apply_date_offset($data->sessdate);
$data->lasttaken = $this->apply_date_offset($data->lasttaken);
$data->lasttakenby = $this->get_mappingid('user', $data->lasttakenby);
$data->timemodified = $this->apply_date_offset($data->timemodified);
$data->caleventid = $this->get_mappingid('event', $data->caleventid);
if ($userinfo) {
$data->lasttaken = $this->apply_date_offset($data->lasttaken);
$data->lasttakenby = $this->get_mappingid('user', $data->lasttakenby);
} else {
$data->lasttaken = 0;
$data->lasttakenby = 0;
}
$newitemid = $DB->insert_record('attendance_sessions', $data);
$data->id = $newitemid;
$this->set_mapping('attendance_session', $oldid, $newitemid, true);
// Create Calendar event.
attendance_create_calendar_event($data);
}
/**

19
calendar.js

@ -1,9 +1,11 @@
/* global YUI */
// eslint-disable-next-line new-cap
YUI().use('yui2-container', 'yui2-calendar', function(Y) {
var YAHOO = Y.YUI2;
document.body.className += ' yui-skin-sam';
YAHOO.util.Event.onDOMReady(function(){
YAHOO.util.Event.onDOMReady(function() {
var Event = YAHOO.util.Event,
Dom = YAHOO.util.Dom,
@ -36,12 +38,12 @@ YUI().use('yui2-container', 'yui2-calendar', function(Y) {
});
dialog = new YAHOO.widget.Dialog("attcalendarcontainer", {
visible:false,
context:["show", "tl", "bl"],
buttons:[{text: M.str.attendance.caltoday, handler: resetHandler, isDefault:true},
visible: false,
context: ["show", "tl", "bl"],
buttons: [{text: M.str.attendance.caltoday, handler: resetHandler, isDefault: true},
{text: M.str.attendance.calclose, handler: closeHandler}],
draggable:false,
close:false
draggable: false,
close: false
});
dialog.setHeader('');
dialog.setBody('<div id="cal"></div>');
@ -61,8 +63,9 @@ YUI().use('yui2-container', 'yui2-calendar', function(Y) {
if (!calendar) {
calendar = new YAHOO.widget.Calendar("cal", {
iframe:false, // Turn iframe off, since container has iframe support.
hide_blank_weeks:true // Enable, to demonstrate how we handle changing height, using changeContent.
iframe: false, // Turn iframe off, since container has iframe support.
// eslint-disable-next-line camelcase
hide_blank_weeks: true // Enable, to demonstrate how we handle changing height, using changeContent.
});
calendar.cfg.setProperty("start_weekday", M.attendance.cal_start_weekday);

113
classes/add_warning_form.php

@ -0,0 +1,113 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains class mod_attendance_add_warning_form
*
* @package mod_attendance
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class mod_attendance_add_warning_form
*
* @package mod_attendance
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_add_warning_form extends moodleform {
/**
* Form definition
*/
public function definition() {
global $COURSE;
$mform = $this->_form;
// Load global defaults.
$config = get_config('attendance');
$options = array();
for ($i = 1; $i <= 100; $i++) {
$options[$i] = "$i%";
}
$mform->addElement('select', 'warningpercent', get_string('warningpercent', 'mod_attendance'), $options);
$mform->addHelpButton('warningpercent', 'warningpercent', 'mod_attendance');
$mform->setType('warningpercent', PARAM_INT);
$mform->setDefault('warningpercent', $config->warningpercent);
$options = array();
for ($i = 1; $i <= ATTENDANCE_MAXWARNAFTER; $i++) {
$options[$i] = "$i";
}
$mform->addElement('select', 'warnafter', get_string('warnafter', 'mod_attendance'), $options);
$mform->addHelpButton('warnafter', 'warnafter', 'mod_attendance');
$mform->setType('warnafter', PARAM_INT);
$mform->setDefault('warnafter', $config->warnafter);
$mform->addElement('select', 'maxwarn', get_string('maxwarn', 'mod_attendance'), $options);
$mform->addHelpButton('maxwarn', 'maxwarn', 'mod_attendance');
$mform->setType('maxwarn', PARAM_INT);
$mform->setDefault('maxwarn', $config->maxwarn);
$mform->addElement('checkbox', 'emailuser', get_string('emailuser', 'mod_attendance'));
$mform->addHelpButton('emailuser', 'emailuser', 'mod_attendance');
$mform->setDefault('emailuser', $config->emailuser);
$mform->addElement('text', 'emailsubject', get_string('emailsubject', 'mod_attendance'), array('size' => '64'));
$mform->setType('emailsubject', PARAM_TEXT);
$mform->addRule('emailsubject', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('emailsubject', 'emailsubject', 'mod_attendance');
$mform->setDefault('emailsubject', $config->emailsubject);
$mform->addElement('editor', 'emailcontent', get_string('emailcontent', 'mod_attendance'), null, null);
$mform->setDefault('emailcontent', array('text' => format_text($config->emailcontent)));
$mform->setType('emailcontent', PARAM_RAW);
$mform->addHelpButton('emailcontent', 'emailcontent', 'mod_attendance');
$users = get_users_by_capability(context_course::instance($COURSE->id), 'mod/attendance:warningemails');
$options = array();
foreach ($users as $user) {
$options[$user->id] = fullname($user);
}
$select = $mform->addElement('searchableselector', 'thirdpartyemails',
get_string('thirdpartyemails', 'mod_attendance'), $options);
$mform->setType('thirdpartyemails', PARAM_TEXT);
$mform->addHelpButton('thirdpartyemails', 'thirdpartyemails', 'mod_attendance');
$select->setMultiple(true);
// Need to set hidden elements when adding default options.
$mform->addElement('hidden', 'idnumber', 0); // Default options use 0 as the idnumber.
$mform->setType('idnumber', PARAM_INT);
$mform->addElement('hidden', 'notid', 0); // The id of warning record.
$mform->setType('notid', PARAM_INT);
$mform->addElement('hidden', 'id', $this->_customdata['id']); // The id of course module record if attendance level.
$mform->setType('id', PARAM_INT);
if (!empty($this->_customdata['notid'])) {
$btnstring = get_string('update', 'attendance');
} else {
$btnstring = get_string('add', 'attendance');
}
$this->add_action_buttons(true, $btnstring);
}
}

39
classes/attendance_webservices_handler.php

@ -13,23 +13,33 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Web Services for Attendance plugin.
*
* @package local_attendance
* @package mod_attendance
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__).'/../../../config.php');
defined('MOODLE_INTERNAL') || die();
require_once(dirname(__FILE__).'/../locallib.php');
require_once(dirname(__FILE__).'/structure.php');
require_once(dirname(__FILE__).'/../../../lib/sessionlib.php');
require_once(dirname(__FILE__).'/../../../lib/datalib.php');
/**
* Class attendance_handler
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_handler {
/**
* For this user, this method searches in all the courses that this user has permission to take attendance,
* looking for today sessions and returns the courses with the sessions.
* @param int $userid
* @return array
*/
public static function get_courses_with_today_sessions($userid) {
$usercourses = enrol_get_users_courses($userid);
@ -67,6 +77,12 @@ class attendance_handler {
return self::prepare_data($coursessessions);
}
/**
* Prepare data.
*
* @param array $coursessessions
* @return array
*/
private static function prepare_data($coursessessions) {
$courses = array();
@ -80,8 +96,11 @@ class attendance_handler {
return $courses;
}
/*
** For this session, returns all the necessary data to take an attendance
/**
* For this session, returns all the necessary data to take an attendance.
*
* @param int $sessionid
* @return mixed
*/
public static function get_session($sessionid) {
global $DB;
@ -90,7 +109,8 @@ class attendance_handler {
$session->courseid = $DB->get_field('attendance', 'course', array('id' => $session->attendanceid));
$session->statuses = attendance_get_statuses($session->attendanceid, true, $session->statusset);
$coursecontext = context_course::instance($session->courseid);
$session->users = get_enrolled_users($coursecontext, 'mod/attendance:canbelisted', 0, 'u.id, u.firstname, u.lastname');
$session->users = get_enrolled_users($coursecontext, 'mod/attendance:canbelisted',
$session->groupid, 'u.id, u.firstname, u.lastname');
$session->attendance_log = array();
if ($attendancelog = $DB->get_records('attendance_log', array('sessionid' => $sessionid),
@ -101,6 +121,15 @@ class attendance_handler {
return $session;
}
/**
* Update user status
*
* @param int $sessionid
* @param int $studentid
* @param int $takenbyid
* @param int $statusid
* @param int $statusset
*/
public static function update_user_status($sessionid, $studentid, $takenbyid, $statusid, $statusset) {
global $DB;

35
classes/calendar_helpers.php

@ -32,12 +32,17 @@ require_once(dirname(__FILE__).'/../../../calendar/lib.php');
* @return bool result of calendar event creation
*/
function attendance_create_calendar_event(&$session) {
global $DB;
// We don't want to create multiple calendar events for 1 session.
if ($session->caleventid) {
return $session->caleventid;
}
if (empty(get_config('attendance', 'enablecalendar'))) {
// Calendar events are not used.
return true;
}
global $DB;
$attendance = $DB->get_record('attendance', array('id' => $session->attendanceid));
$caleventdata = new stdClass();
@ -47,10 +52,16 @@ function attendance_create_calendar_event(&$session) {
$caleventdata->instance = $session->attendanceid;
$caleventdata->timestart = $session->sessdate;
$caleventdata->timeduration = $session->duration;
$caleventdata->description = $session->description;
$caleventdata->format = $session->descriptionformat;
$caleventdata->eventtype = 'attendance';
$caleventdata->timemodified = time();
$caleventdata->modulename = 'attendance';
if (!empty($session->groupid)) {
$caleventdata->name .= " (". get_string('group', 'group') ." ". groups_get_group_name($session->groupid) .")";
}
$calevent = new stdClass();
if ($calevent = calendar_event::create($caleventdata, false)) {
$session->caleventid = $calevent->id;
@ -64,10 +75,16 @@ function attendance_create_calendar_event(&$session) {
/**
* Create multiple calendar events based on sessions data.
*
* @param array %sessionsids array of sessions ids
* @param array $sessionsids array of sessions ids
*/
function attendance_create_calendar_events($sessionsids) {
global $DB;
if (empty(get_config('attendance', 'enablecalendar'))) {
// Calendar events are not used.
return true;
}
$sessions = $DB->get_recordset_list('attendance_sessions', 'id', $sessionsids);
foreach ($sessions as $session) {
@ -81,12 +98,18 @@ function attendance_create_calendar_events($sessionsids) {
/**
* Update calendar event duration and date
*
* @param $caleventid int calendar event id
* @param $timeduration int duration of the event
* @param $timestart int start time of the event
* @param int $caleventid calendar event id
* @param int $timeduration duration of the event
* @param int $timestart start time of the event
* @return bool result of updating
*/
function attendance_update_calendar_event($caleventid, $timeduration, $timestart) {
if (empty(get_config('attendance', 'enablecalendar'))) {
// Calendar events are not used.
return true;
}
$caleventdata = new stdClass();
$caleventdata->timeduration = $timeduration;
$caleventdata->timestart = $timestart;
@ -103,7 +126,7 @@ function attendance_update_calendar_event($caleventid, $timeduration, $timestart
/**
* Delete calendar events for sessions
*
* @param array %sessionsids array of sessions ids
* @param array $sessionsids array of sessions ids
* @return bool result of updating
*/
function attendance_delete_calendar_events($sessionsids) {

93
classes/event/session_ip_shared.php

@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains an event for when self-marking is blocked because
* another student used the same IP address to self-mark.
*
* @package mod_attendance
* @author Dan Marsden <dan@danmarsden.com>
* @copyright 2018 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\event;
defined('MOODLE_INTERNAL') || die();
/**
* Event for when self-marking is blocked
*
* @property-read array $other {
* Extra information about event properties.
*
* string mode Mode of the report viewed.
* }
* @package mod_attendance
* @author Dan Marsden <dan@danmarsden.com>
* @copyright 2018 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class session_ip_shared extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->data['objecttable'] = 'attendance_log';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return 'User with id ' . $this->userid . ' was blocked from taking attendance for sessionid: ' . $this->other['sessionid'] .
' because user with id '.$this->other['otheruser'] . ' previously marked attendance with the same IP address.';
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventsessionipshared', 'mod_attendance');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/attendance/attendance.php');
}
/**
* Get objectid mapping
*
* @return array of parameters for object mapping.
*/
public static function get_objectid_mapping() {
return array(
'db' => 'attendance',
'restore' => 'attendance'
);
}
}

92
classes/event/sessions_imported.php

@ -0,0 +1,92 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains an event for when an attendance sessions is imported.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\event;
defined('MOODLE_INTERNAL') || die();
/**
* Event for when an attendance sessions is imported
*
* @property-read array $other {
* Extra information about event properties.
*
* string mode Mode of the report viewed.
* }
* @package mod_attendance
* @since Moodle 2.7
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sessions_imported extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'attendance_sessions';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return 'User with id ' . $this->userid . ' imported ' . $this->other['count'] . ' sessions';
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventsessionsimported', 'mod_attendance');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/attendance/import/sessions.php');
}
/**
* Get objectid mapping
*
* @return array of parameters for object mapping.
*/
public static function get_objectid_mapping() {
return array(
'db' => 'attendance',
'restore' => 'attendance'
);
}
}

2
classes/event/status_updated.php

@ -84,7 +84,7 @@ class status_updated extends \core\event\base {
*/
protected function get_legacy_logdata() {
return array($this->courseid, 'attendance', 'status updated', $this->get_url(),
$this->other['updated'], $this->contextinstanceid);
'', $this->contextinstanceid);
}
/**

86
classes/form/import/sessions.php

@ -0,0 +1,86 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains the form for importing sessions from a file.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\form\import;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
use core_text;
use csv_import_reader;
use moodleform;
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->libdir . '/csvlib.class.php');
/**
* Import attendance sessions.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sessions extends moodleform {
/**
* Define the form - called by parent constructor
*/
public function definition() {
global $CFG;
$mform = $this->_form;
$element = $mform->createElement('filepicker', 'importfile', get_string('importfile', 'mod_attendance'));
$mform->addElement($element);
$mform->addHelpButton('importfile', 'importfile', 'mod_attendance');
$mform->addRule('importfile', null, 'required');
$mform->addElement('hidden', 'confirm', 0);
$mform->setType('confirm', PARAM_BOOL);
$choices = csv_import_reader::get_delimiter_list();
$mform->addElement('select', 'delimiter_name', get_string('csvdelimiter', 'mod_attendance'), $choices);
if (array_key_exists('cfg', $choices)) {
$mform->setDefault('delimiter_name', 'cfg');
} else if (get_string('listsep', 'langconfig') == ';') {
$mform->setDefault('delimiter_name', 'semicolon');
} else {
$mform->setDefault('delimiter_name', 'comma');
}
$choices = core_text::get_encodings();
$mform->addElement('select', 'encoding', get_string('encoding', 'mod_attendance'), $choices);
$mform->setDefault('encoding', 'UTF-8');
$this->add_action_buttons(false, get_string('import', 'mod_attendance'));
}
/**
* Display an error on the import form.
*
* @param string $msg
*/
public function set_import_error($msg) {
$mform = $this->_form;
$mform->setElementError('importfile', $msg);
}
}

73
classes/form/import/sessions_confirm.php

@ -0,0 +1,73 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Import attendance sessions.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\form\import;
defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
use moodleform;
require_once($CFG->libdir . '/formslib.php');
/**
* Import attendance sessions.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sessions_confirm extends moodleform {
/**
* Define the form - called by parent constructor
*/
public function definition() {
$importer = $this->_customdata;
$mform = $this->_form;
$mform->addElement('hidden', 'confirm', 1);
$mform->setType('confirm', PARAM_BOOL);
$mform->addElement('hidden', 'importid', $importer->get_importid());
$mform->setType('importid', PARAM_INT);
$requiredheaders = $importer->list_required_headers();
$foundheaders = $importer->list_found_headers();
if (empty($foundheaders)) {
$foundheaders = range(0, count($requiredheaders));
}
$foundheaders[- 1] = get_string('none');
foreach ($requiredheaders as $index => $requiredheader) {
$mform->addElement('select', 'header' . $index, $requiredheader, $foundheaders);
if (isset($foundheaders[$index])) {
$mform->setDefault('header' . $index, $index);
} else {
$mform->setDefault('header' . $index, - 1);
}
}
$this->add_action_buttons(true, get_string('confirm', 'mod_attendance'));
}
}

80
classes/header.php

@ -0,0 +1,80 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class definition for mod_attendance_header
*
* @package mod_attendance
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2017 Catalyst IT Australia {@link http://www.catalyst-au.net}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Used to render the page header.
*
* @package mod_attendance
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2017 Catalyst IT Australia {@link http://www.catalyst-au.net}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_header implements renderable {
/** @var mod_attendance_structure */
private $attendance;
/** @var string */
private $title;
/**
* mod_attendance_header constructor.
*
* @param mod_attendance_structure $attendance
* @param null $title
*/
public function __construct(mod_attendance_structure $attendance, $title = null) {
$this->attendance = $attendance;
$this->title = $title;
}
/**
* Gets the attendance data.
*
* @return mod_attendance_structure
*/
public function get_attendance() {
return $this->attendance;
}
/**
* Gets the title. If title was not provided, use the module name.
*
* @return string
*/
public function get_title() {
return is_null($this->title) ? $this->attendance->name : $this->title;
}
/**
* Checks if the header should be rendered.
*
* @return bool
*/
public function should_render() {
return !is_null($this->title) || !empty($this->attendance->intro);
}
}

497
classes/import/sessions.php

@ -0,0 +1,497 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Import attendance sessions class.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\import;
defined('MOODLE_INTERNAL') || die();
use csv_import_reader;
use mod_attendance_notifyqueue;
use mod_attendance_structure;
use stdClass;
/**
* Import attendance sessions.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sessions {
/** @var string $error The errors message from reading the xml */
protected $error = '';
/** @var array $sessions The sessions info */
protected $sessions = array();
/** @var array $mappings The mappings info */
protected $mappings = array();
/** @var int The id of the csv import */
protected $importid = 0;
/** @var csv_import_reader|null $importer */
protected $importer = null;
/** @var array $foundheaders */
protected $foundheaders = array();
/** @var bool $useprogressbar Control whether importing should use progress bars or not. */
protected $useprogressbar = false;
/** @var \core\progress\display_if_slow|null $progress The progress bar instance. */
protected $progress = null;
/**
* Store an error message for display later
*
* @param string $msg
*/
public function fail($msg) {
$this->error = $msg;
return false;
}
/**
* Get the CSV import id
*
* @return string The import id.
*/
public function get_importid() {
return $this->importid;
}
/**
* Get the list of headers required for import.
*
* @return array The headers (lang strings)
*/
public static function list_required_headers() {
return array(
get_string('course', 'attendance'),
get_string('groups', 'attendance'),
get_string('sessiondate', 'attendance'),
get_string('from', 'attendance'),
get_string('to', 'attendance'),
get_string('description', 'attendance'),
get_string('repeaton', 'attendance'),
get_string('repeatevery', 'attendance'),
get_string('repeatuntil', 'attendance'),
get_string('studentscanmark', 'attendance'),
get_string('passwordgrp', 'attendance'),
get_string('randompassword', 'attendance'),
get_string('subnet', 'attendance'),
get_string('automark', 'attendance'),
get_string('autoassignstatus', 'attendance'),
get_string('absenteereport', 'attendance'),
get_string('preventsharedip', 'attendance'),
get_string('preventsharediptime', 'attendance')
);
}
/**
* Get the list of headers found in the import.
*
* @return array The found headers (names from import)
*/
public function list_found_headers() {
return $this->foundheaders;
}
/**
* Read the data from the mapping form.
*
* @param array $data The mapping data.
*/
protected function read_mapping_data($data) {
if ($data) {
return array(
'course' => $data->header0,
'groups' => $data->header1,
'sessiondate' => $data->header2,
'from' => $data->header3,
'to' => $data->header4,
'description' => $data->header5,
'repeaton' => $data->header6,
'repeatevery' => $data->header7,
'repeatuntil' => $data->header8,
'studentscanmark' => $data->header9,
'passwordgrp' => $data->header10,
'randompassword' => $data->header11,
'subnet' => $data->header12,
'automark' => $data->header13,
'autoassignstatus' => $data->header14,
'absenteereport' => $data->header15,
'preventsharedip' => $data->header16,
'preventsharediptime' => $data->header17,
);
} else {
return array(
'course' => 0,
'groups' => 1,
'sessiondate' => 2,
'from' => 3,
'to' => 4,
'description' => 5,
'repeaton' => 6,
'repeatevery' => 7,
'repeatuntil' => 8,
'studentscanmark' => 9,
'passwordgrp' => 10,
'randompassword' => 11,
'subnet' => 12,
'automark' => 13,
'autoassignstatus' => 14,
'absenteereport' => 15,
'preventsharedip' => 16,
'preventsharediptime' => 17
);
}
}
/**
* Get the a column from the imported data.
*
* @param array $row The imported raw row
* @param int $index The column index we want
* @return string The column data.
*/
protected function get_column_data($row, $index) {
if ($index < 0) {
return '';
}
return isset($row[$index]) ? $row[$index] : '';
}
/**
* Constructor - parses the raw text for sanity.
*
* @param string $text The raw csv text.
* @param string $encoding The encoding of the csv file.
* @param string $delimiter The specified delimiter for the file.
* @param string $importid The id of the csv import.
* @param array $mappingdata The mapping data from the import form.
* @param bool $useprogressbar Whether progress bar should be displayed, to avoid html output on CLI.
*/
public function __construct($text = null, $encoding = null, $delimiter = null, $importid = 0,
$mappingdata = null, $useprogressbar = false) {
global $CFG;
require_once($CFG->libdir . '/csvlib.class.php');
$pluginconfig = get_config('attendance');
$type = 'sessions';
if (! $importid) {
if ($text === null) {
return;
}
$this->importid = csv_import_reader::get_new_iid($type);
$this->importer = new csv_import_reader($this->importid, $type);
if (! $this->importer->load_csv_content($text, $encoding, $delimiter)) {
$this->fail(get_string('invalidimportfile', 'attendance'));
$this->importer->cleanup();
return;
}
} else {
$this->importid = $importid;
$this->importer = new csv_import_reader($this->importid, $type);
}
if (! $this->importer->init()) {
$this->fail(get_string('invalidimportfile', 'attendance'));
$this->importer->cleanup();
return;
}
$this->foundheaders = $this->importer->get_columns();
$this->useprogressbar = $useprogressbar;
$domainid = 1;
$sessions = array();
while ($row = $this->importer->next()) {
// This structure mimics what the UI form returns.
$mapping = $this->read_mapping_data($mappingdata);
$session = new stdClass();
$session->course = $this->get_column_data($row, $mapping['course']);
if (empty($session->course)) {
\mod_attendance_notifyqueue::notify_problem(get_string('error:sessioncourseinvalid', 'attendance'));
continue;
}
// Handle multiple group assignments per session. Expect semicolon separated group names.
$groups = $this->get_column_data($row, $mapping['groups']);
if (! empty($groups)) {
$session->groups = explode(';', $groups);
$session->sessiontype = \mod_attendance_structure::SESSION_GROUP;
} else {
$session->sessiontype = \mod_attendance_structure::SESSION_COMMON;
}
// Expect standardised date format, eg YYYY-MM-DD.
$sessiondate = strtotime($this->get_column_data($row, $mapping['sessiondate']));
if ($sessiondate === false) {
\mod_attendance_notifyqueue::notify_problem(get_string('error:sessiondateinvalid', 'attendance'));
continue;
}
$session->sessiondate = $sessiondate;
// Expect standardised time format, eg HH:MM.
$from = $this->get_column_data($row, $mapping['from']);
if (empty($from)) {
\mod_attendance_notifyqueue::notify_problem(get_string('error:sessionstartinvalid', 'attendance'));
continue;
}
$from = explode(':', $from);
$session->sestime['starthour'] = $from[0];
$session->sestime['startminute'] = $from[1];
$to = $this->get_column_data($row, $mapping['to']);
if (empty($to)) {
\mod_attendance_notifyqueue::notify_problem(get_string('error:sessionendinvalid', 'attendance'));
continue;
}
$to = explode(':', $to);
$session->sestime['endhour'] = $to[0];
$session->sestime['endminute'] = $to[1];
// Wrap the plain text description in html tags.
$session->sdescription['text'] = '<p>' . $this->get_column_data($row, $mapping['description']) . '</p>';
$session->sdescription['format'] = FORMAT_HTML;
$session->sdescription['itemid'] = 0;
$session->repeaton = $this->get_column_data($row, $mapping['repeaton']);
$session->repeatevery = $this->get_column_data($row, $mapping['repeatevery']);
$session->repeatuntil = $this->get_column_data($row, $mapping['repeatuntil']);
$session->passwordgrp = $this->get_column_data($row, $mapping['passwordgrp']);
$session->subnet = $this->get_column_data($row, $mapping['subnet']);
// Set session subnet restriction. Use the default activity level subnet if there isn't one set for this session.
if (empty($session->subnet)) {
$session->usedefaultsubnet = '1';
} else {
$session->usedefaultsubnet = '';
}
if ($mapping['studentscanmark'] == -1) {
$session->studentscanmark = $pluginconfig->studentscanmark_default;
} else {
$session->studentscanmark = $this->get_column_data($row, $mapping['studentscanmark']);
}
if ($mapping['randompassword'] == -1) {
$session->randompassword = $pluginconfig->randompassword_default;
} else {
$session->randompassword = $this->get_column_data($row, $mapping['randompassword']);
}
if ($mapping['automark'] == -1) {
$session->automark = $pluginconfig->automark_default;
} else {
$session->automark = $this->get_column_data($row, $mapping['automark']);
}
if ($mapping['autoassignstatus'] == -1) {
$session->autoassignstatus = $pluginconfig->autoassignstatus;
} else {
$session->autoassignstatus = $this->get_column_data($row, $mapping['autoassignstatus']);
}
if ($mapping['absenteereport'] == -1) {
$session->absenteereport = $pluginconfig->absenteereport_default;
} else {
$session->absenteereport = $this->get_column_data($row, $mapping['absenteereport']);
}
if ($mapping['preventsharedip'] == -1) {
$session->preventsharedip = $pluginconfig->preventsharedip;
} else {
$session->preventsharedip = $this->get_column_data($row, $mapping['preventsharedip']);
}
if ($mapping['preventsharediptime'] == -1) {
$session->preventsharediptime = $pluginconfig->preventsharediptime;
} else {
$session->preventsharediptime = $this->get_column_data($row, $mapping['preventsharediptime']);
}
$session->statusset = 0;
$sessions[] = $session;
}
$this->sessions = $sessions;
$this->importer->close();
if ($this->sessions == null) {
$this->fail(get_string('invalidimportfile', 'attendance'));
return;
} else {
// We are calling from browser, display progress bar.
if ($this->useprogressbar === true) {
$this->progress = new \core\progress\display_if_slow(get_string('processingfile', 'attendance'));
$this->progress->start_html();
} else {
// Avoid html output on CLI scripts.
$this->progress = new \core\progress\none();
}
$this->progress->start_progress('', count($this->sessions));
raise_memory_limit(MEMORY_EXTRA);
$this->progress->end_progress();
}
}
/**
* Get parse errors.
*
* @return array of errors from parsing the xml.
*/
public function get_error() {
return $this->error;
}
/**
* Create sessions using the CSV data.
*
* @return void
*/
public function import() {
global $DB;
// Count of sessions added.
$okcount = 0;
foreach ($this->sessions as $session) {
$groupids = array();
// Check course shortname matches.
if ($DB->record_exists('course', array(
'shortname' => $session->course
))) {
// Get course.
$course = $DB->get_record('course', array(
'shortname' => $session->course
), '*', MUST_EXIST);
// Check course has activities.
if ($DB->record_exists('attendance', array(
'course' => $course->id
))) {
// Translate group names to group IDs. They are unique per course.
if ($session->sessiontype === \mod_attendance_structure::SESSION_GROUP) {
foreach ($session->groups as $groupname) {
$gid = groups_get_group_by_name($course->id, $groupname);
if ($gid === false) {
\mod_attendance_notifyqueue::notify_problem(get_string('sessionunknowngroup',
'attendance', $groupname));
} else {
$groupids[] = $gid;
}
}
$session->groups = $groupids;
}
// Get activities in course.
$activities = $DB->get_recordset('attendance', array(
'course' => $course->id
), 'id', 'id');
foreach ($activities as $activity) {
// Build the session data.
$cm = get_coursemodule_from_instance('attendance', $activity->id, $course->id);
if (!empty($cm->deletioninprogress)) {
// Don't do anything if this attendance is in recycle bin.
continue;
}
$att = new mod_attendance_structure($activity, $cm, $course);
$sessions = attendance_construct_sessions_data_for_add($session, $att);
foreach ($sessions as $index => $sess) {
// Check for duplicate sessions.
if ($this->session_exists($sess)) {
mod_attendance_notifyqueue::notify_message(get_string('sessionduplicate', 'attendance', (array(
'course' => $session->course,
'activity' => $cm->name
))));
unset($sessions[$index]);
} else {
$okcount ++;
}
}
if (! empty($sessions)) {
$att->add_sessions($sessions);
}
}
$activities->close();
} else {
mod_attendance_notifyqueue::notify_problem(get_string('error:coursehasnoattendance',
'attendance', $session->course));
}
} else {
mod_attendance_notifyqueue::notify_problem(get_string('error:coursenotfound', 'attendance', $session->course));
}
}
$message = get_string('sessionsgenerated', 'attendance', $okcount);
if ($okcount < 1) {
mod_attendance_notifyqueue::notify_message($message);
} else {
mod_attendance_notifyqueue::notify_success($message);
}
// Trigger a sessions imported event.
$event = \mod_attendance\event\sessions_imported::create(array(
'objectid' => 0,
'context' => \context_system::instance(),
'other' => array(
'count' => $okcount
)
));
$event->trigger();
}
/**
* Check if an identical session exists.
*
* @param stdClass $session
* @return boolean
*/
private function session_exists(stdClass $session) {
global $DB;
$check = clone $session;
// Remove the properties that aren't useful to check.
unset($check->description);
unset($check->descriptionitemid);
unset($check->timemodified);
$check = (array) $check;
if ($DB->record_exists('attendance_sessions', $check)) {
return true;
}
return false;
}
}

7
classes/manage_page_params.php

@ -30,10 +30,17 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_manage_page_params extends mod_attendance_page_with_filter_controls {
/**
* mod_attendance_manage_page_params constructor.
*/
public function __construct() {
$this->selectortype = mod_attendance_page_with_filter_controls::SELECTOR_SESS_TYPE;
}
/**
* Get page params.
* @return array
*/
public function get_significant_params() {
return array();
}

57
classes/observer.php

@ -0,0 +1,57 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Event observers supported by this module
*
* @package mod_attendance
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Event observers supported by this module
*
* @package mod_attendance
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_observer {
/**
* Observer for the event course_content_deleted - delete all attendance stuff.
*
* @param \core\event\course_content_deleted $event
*/
public static function course_content_deleted(\core\event\course_content_deleted $event) {
global $DB;
$attids = array_keys($DB->get_records('attendance', array('course' => $event->objectid), '', 'id'));
$sessids = array_keys($DB->get_records_list('attendance_sessions', 'attendanceid', $attids, '', 'id'));
if (attendance_existing_calendar_events_ids($sessids)) {
attendance_delete_calendar_events($sessids);
}
if ($sessids) {
$DB->delete_records_list('attendance_log', 'sessionid', $sessids);
}
if ($attids) {
$DB->delete_records_list('attendance_statuses', 'attendanceid', $attids);
$DB->delete_records_list('attendance_sessions', 'attendanceid', $attids);
}
}
}

57
classes/page_with_filter_controls.php

@ -24,18 +24,28 @@
defined('MOODLE_INTERNAL') || die();
/**
* base filter controls class - overridden by different views where needed.
* Base filter controls class - overridden by different views where needed.
*
* @copyright 2016 Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_page_with_filter_controls {
/** No filter. */
const SELECTOR_NONE = 1;
/** Filter by group. */
const SELECTOR_GROUP = 2;
/** Filter by session type. */
const SELECTOR_SESS_TYPE = 3;
/** Common. */
const SESSTYPE_COMMON = 0;
/** All. */
const SESSTYPE_ALL = -1;
/** No value. */
const SESSTYPE_NO_VALUE = -2;
/** @var int current view mode */
@ -50,23 +60,39 @@ class mod_attendance_page_with_filter_controls {
/** @var int end date of displayed date range */
public $enddate;
/** @var int type. */
public $selectortype = self::SELECTOR_NONE;
protected $defaultview = ATT_VIEW_WEEKS;
/** @var int default view. */
protected $defaultview;
/** @var stdClass course module record. */
private $cm;
/** @var array */
private $sessgroupslist;
/** @var int */
private $sesstype;
/**
* initialise stuff.
*
* @param stdClass $cm
*/
public function init($cm) {
$this->cm = $cm;
if (empty($this->defaultview)) {
$this->defaultview = get_config('attendance', 'defaultview');
}
$this->init_view();
$this->init_curdate();
$this->init_start_end_date();
}
/**
* Initialise the view.
*/
private function init_view() {
global $SESSION;
@ -79,6 +105,9 @@ class mod_attendance_page_with_filter_controls {
}
}
/**
* Initialise the current date.
*/
private function init_curdate() {
global $SESSION;
@ -91,6 +120,9 @@ class mod_attendance_page_with_filter_controls {
}
}
/**
* Initialise the end date.
*/
public function init_start_end_date() {
global $CFG;
@ -132,6 +164,9 @@ class mod_attendance_page_with_filter_controls {
}
}
/**
* Calculate the session group list type.
*/
private function calc_sessgroupslist_sesstype() {
global $SESSION;
@ -180,6 +215,9 @@ class mod_attendance_page_with_filter_controls {
}
}
/**
* Calculate the session group list
*/
private function calc_sessgroupslist() {
global $USER, $PAGE;
@ -207,6 +245,11 @@ class mod_attendance_page_with_filter_controls {
}
}
/**
* Return the session groups.
*
* @return array
*/
public function get_sess_groups_list() {
if (is_null($this->sessgroupslist)) {
$this->calc_sessgroupslist_sesstype();
@ -215,6 +258,11 @@ class mod_attendance_page_with_filter_controls {
return $this->sessgroupslist;
}
/**
* Get the current session type.
*
* @return int
*/
public function get_current_sesstype() {
if (is_null($this->sesstype)) {
$this->calc_sessgroupslist_sesstype();
@ -223,6 +271,11 @@ class mod_attendance_page_with_filter_controls {
return $this->sesstype;
}
/**
* Set the current session type.
*
* @param int $sesstype
*/
public function set_current_sesstype($sesstype) {
$this->sesstype = $sesstype;
}

12
classes/preferences_page_params.php

@ -30,19 +30,31 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_preferences_page_params {
/** Add */
const ACTION_ADD = 1;
/** Delete */
const ACTION_DELETE = 2;
/** Hide */
const ACTION_HIDE = 3;
/** Show */
const ACTION_SHOW = 4;
/** Save */
const ACTION_SAVE = 5;
/** @var int view mode of taking attendance page*/
public $action;
/** @var int */
public $statusid;
/** @var array */
public $statusset;
/**
* Get params for this page.
*
* @return array
*/
public function get_significant_params() {
$params = array();

22
classes/report_page_params.php

@ -30,15 +30,29 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_report_page_params extends mod_attendance_page_with_filter_controls {
/** @var int */
public $group;
/** @var int */
public $sort;
/** @var int */
public $showextrauserdetails;
/** @var int */
public $showsessiondetails;
/** @var int */
public $sessiondetailspos;
/**
* mod_attendance_report_page_params constructor.
*/
public function __construct() {
$this->selectortype = self::SELECTOR_GROUP;
}
/**
* Initialise params.
*
* @param stdClass $cm
*/
public function init($cm) {
parent::init($cm);
@ -50,6 +64,10 @@ class mod_attendance_report_page_params extends mod_attendance_page_with_filter_
}
}
/**
* Get params for this page.
* @return array
*/
public function get_significant_params() {
$params = array();
@ -57,6 +75,10 @@ class mod_attendance_report_page_params extends mod_attendance_page_with_filter_
$params['sort'] = $this->sort;
}
if (empty($this->showextrauserdetails)) {
$params['showextrauserdetails'] = 0;
}
if (empty($this->showsessiondetails)) {
$params['showsessiondetails'] = 0;
}

23
classes/sessions_page_params.php

@ -31,11 +31,34 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_sessions_page_params {
/**
* Add Session.
*/
const ACTION_ADD = 1;
/**
* Update Session.
*/
const ACTION_UPDATE = 2;
/**
* Delete Session
*/
const ACTION_DELETE = 3;
/**
* Delete selected Sessions.
*/
const ACTION_DELETE_SELECTED = 4;
/**
* Change duration of a session.
*/
const ACTION_CHANGE_DURATION = 5;
/**
* Delete a hidden session.
*/
const ACTION_DELETE_HIDDEN = 6;
/** @var int view mode of taking attendance page*/

469
classes/structure.php

@ -32,7 +32,9 @@ require_once(dirname(__FILE__) . '/calendar_helpers.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_structure {
/** Common sessions */
const SESSION_COMMON = 0;
/** Group sessions */
const SESSION_GROUP = 1;
/** @var stdclass course module record */
@ -56,23 +58,42 @@ class mod_attendance_structure {
/** @var int last time attendance was modified - used for global search */
public $timemodified;
/** current page parameters */
/** @var string required field for activity modules and searching */
public $intro;
/** @var int format of the intro (see above) */
public $introformat;
/** @var array current page parameters */
public $pageparams;
/** @var string subnets (IP range) for student self selection. */
public $subnet;
/** Define if session details should be shown in reports */
/** @var string subnets (IP range) for student self selection. */
public $automark;
/** @var boolean flag set when automarking is complete. */
public $automarkcompleted;
/** @var int Define if extra user details should be shown in reports */
public $showextrauserdetails;
/** @var int Define if session details should be shown in reports */
public $showsessiondetails;
/** Position for the session detail columns related to summary columns.*/
/** @var int Position for the session detail columns related to summary columns.*/
public $sessiondetailspos;
/** @var int groupmode */
private $groupmode;
/** @var array */
private $statuses;
private $allstatuses; // Cache list of all statuses (not just one used by current session).
/** @var array Cache list of all statuses (not just one used by current session). */
private $allstatuses;
// Array by sessionid.
/** @var array of sessionid. */
private $sessioninfo = array();
/**
@ -85,8 +106,9 @@ class mod_attendance_structure {
* @param stdClass $cm Course module record as returned by {@link get_coursemodule_from_id()}
* @param stdClass $course Course record from {course} table
* @param stdClass $context The context of the workshop instance
* @param stdClass $pageparams
*/
public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null, $pageparams=null) {
public function __construct(stdClass $dbrecord, stdClass $cm, stdClass $course, stdClass $context=null, $pageparams=null) {
global $DB;
foreach ($dbrecord as $field => $value) {
@ -106,6 +128,9 @@ class mod_attendance_structure {
$this->pageparams = $pageparams;
if (isset($pageparams->showextrauserdetails) && $pageparams->showextrauserdetails != $this->showextrauserdetails) {
$DB->set_field('attendance', 'showextrauserdetails', $pageparams->showextrauserdetails, array('id' => $this->id));
}
if (isset($pageparams->showsessiondetails) && $pageparams->showsessiondetails != $this->showsessiondetails) {
$DB->set_field('attendance', 'showsessiondetails', $pageparams->showsessiondetails, array('id' => $this->id));
}
@ -114,6 +139,11 @@ class mod_attendance_structure {
}
}
/**
* Get group mode.
*
* @return int
*/
public function get_group_mode() {
if (is_null($this->groupmode)) {
$this->groupmode = groups_get_activity_groupmode($this->cm, $this->course);
@ -173,7 +203,7 @@ class mod_attendance_structure {
* Returns today sessions suitable for copying attendance log
*
* Fetches data from {attendance_sessions}
*
* @param stdClass $sess
* @return array of records or an empty array
*/
public function get_today_sessions_for_copy($sess) {
@ -231,6 +261,11 @@ class mod_attendance_structure {
return $DB->get_records_select('attendance_sessions', $where, $params);
}
/**
* Get filtered sessions.
*
* @return array
*/
public function get_filtered_sessions() {
global $DB;
@ -267,6 +302,8 @@ class mod_attendance_structure {
}
/**
* Get manage url.
* @param array $params
* @return moodle_url of manage.php for attendance instance
*/
public function url_manage($params=array()) {
@ -275,6 +312,7 @@ class mod_attendance_structure {
}
/**
* Get manage temp users url.
* @param array $params optional
* @return moodle_url of tempusers.php for attendance instance
*/
@ -284,6 +322,8 @@ class mod_attendance_structure {
}
/**
* Get temp delete url.
*
* @param array $params optional
* @return moodle_url of tempdelete.php for attendance instance
*/
@ -293,6 +333,8 @@ class mod_attendance_structure {
}
/**
* Get temp edit url.
*
* @param array $params optional
* @return moodle_url of tempedit.php for attendance instance
*/
@ -302,6 +344,8 @@ class mod_attendance_structure {
}
/**
* Get temp merge url
*
* @param array $params optional
* @return moodle_url of tempedit.php for attendance instance
*/
@ -311,6 +355,8 @@ class mod_attendance_structure {
}
/**
* Get url for sessions.
* @param array $params
* @return moodle_url of sessions.php for attendance instance
*/
public function url_sessions($params=array()) {
@ -319,6 +365,8 @@ class mod_attendance_structure {
}
/**
* Get url for report.
* @param array $params
* @return moodle_url of report.php for attendance instance
*/
public function url_report($params=array()) {
@ -327,6 +375,18 @@ class mod_attendance_structure {
}
/**
* Get url for report.
* @param array $params
* @return moodle_url of report.php for attendance instance
*/
public function url_absentee($params=array()) {
$params = array_merge(array('id' => $this->cm->id), $params);
return new moodle_url('/mod/attendance/absentee.php', $params);
}
/**
* Get url for export.
*
* @return moodle_url of export.php for attendance instance
*/
public function url_export() {
@ -335,6 +395,8 @@ class mod_attendance_structure {
}
/**
* Get preferences url
* @param array $params
* @return moodle_url of attsettings.php for attendance instance
*/
public function url_preferences($params=array()) {
@ -347,6 +409,22 @@ class mod_attendance_structure {
}
/**
* Get preferences url
* @param array $params
* @return moodle_url of attsettings.php for attendance instance
*/
public function url_warnings($params=array()) {
// Add the statusset params.
if (isset($this->pageparams->statusset) && !isset($params['statusset'])) {
$params['statusset'] = $this->pageparams->statusset;
}
$params = array_merge(array('id' => $this->cm->id), $params);
return new moodle_url('/mod/attendance/warnings.php', $params);
}
/**
* Get take url.
* @param array $params
* @return moodle_url of attendances.php for attendance instance
*/
public function url_take($params=array()) {
@ -354,16 +432,30 @@ class mod_attendance_structure {
return new moodle_url('/mod/attendance/take.php', $params);
}
/**
* Get view url.
* @param array $params
* @return moodle_url
*/
public function url_view($params=array()) {
$params = array_merge(array('id' => $this->cm->id), $params);
return new moodle_url('/mod/attendance/view.php', $params);
}
/**
* Add sessions.
*
* @param array $sessions
*/
public function add_sessions($sessions) {
global $DB;
foreach ($sessions as $sess) {
$sess->attendanceid = $this->id;
$sess->automarkcompleted = 0;
if (!isset($sess->automark)) {
$sess->automark = 0;
}
$sess->id = $DB->insert_record('attendance_sessions', $sess);
$description = file_save_draft_area_files($sess->descriptionitemid,
@ -388,12 +480,38 @@ class mod_attendance_structure {
$sess->description = $description;
$sess->lasttaken = 0;
$sess->lasttakenby = 0;
$sess->studentscanmark = 0;
if (!isset($sess->studentscanmark)) {
$sess->studentscanmark = 0;
}
if (!isset($sess->autoassignstatus)) {
$sess->autoassignstatus = 0;
}
if (!isset($sess->studentpassword)) {
$sess->studentpassword = '';
}
if (!isset($sess->subnet)) {
$sess->subnet = '';
}
if (!isset($sess->preventsharedip)) {
$sess->preventsharedip = 0;
}
if (!isset($sess->preventsharediptime)) {
$sess->preventsharediptime = '';
}
$event->add_record_snapshot('attendance_sessions', $sess);
$event->trigger();
}
}
/**
* Update session from form.
*
* @param stdClass $formdata
* @param int $sessionid
*/
public function update_session_from_form_data($formdata, $sessionid) {
global $DB;
@ -413,17 +531,59 @@ class mod_attendance_structure {
$sess->description = $description;
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->studentscanmark = 0;
$sess->autoassignstatus = 0;
$sess->studentpassword = '';
$sess->subnet = '';
$sess->automark = 0;
$sess->automarkcompleted = 0;
$sess->preventsharedip = 0;
$sess->preventsharediptime = '';
if (!empty(get_config('attendance', 'enablewarnings'))) {
$sess->absenteereport = empty($formdata->absenteereport) ? 0 : 1;
}
if (!empty($formdata->autoassignstatus)) {
$sess->autoassignstatus = $formdata->autoassignstatus;
}
if (!empty(get_config('attendance', 'studentscanmark')) &&
!empty($formdata->studentscanmark)) {
$sess->studentscanmark = $formdata->studentscanmark;
$sess->studentpassword = $formdata->studentpassword;
$sess->autoassignstatus = $formdata->autoassignstatus;
if (!empty($formdata->usedefaultsubnet)) {
$sess->subnet = $this->subnet;
} else {
$sess->subnet = $formdata->subnet;
}
if (!empty($formdata->automark)) {
$sess->automark = $formdata->automark;
}
if (!empty($formdata->preventsharedip)) {
$sess->preventsharedip = $formdata->preventsharedip;
}
if (!empty($formdata->preventsharediptime)) {
$sess->preventsharediptime = $formdata->preventsharediptime;
}
}
$sess->timemodified = time();
$DB->update_record('attendance_sessions', $sess);
attendance_update_calendar_event($sess->caleventid, $sess->duration, $sess->sessdate);
if (empty($sess->caleventid)) {
// This shouldn't really happen, but just in case to prevent fatal error.
attendance_create_calendar_event($sess);
} else {
attendance_update_calendar_event($sess->caleventid, $sess->duration, $sess->sessdate);
}
$info = construct_session_full_date_time($sess->sessdate, $sess->duration);
$event = \mod_attendance\event\session_updated::create(array(
'objectid' => $this->id,
'context' => $this->context,
'other' => array('info' => $info, 'sessionid' => $sessionid,
'action' => mod_attendance_sessions_page_params::ACTION_UPDATE)));
'action' => mod_attendance_sessions_page_params::ACTION_UPDATE)));
$event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance_sessions', $sess);
$event->trigger();
@ -432,7 +592,7 @@ class mod_attendance_structure {
/**
* Used to record attendance submitted by the student.
*
* @param type $mformdata
* @param stdClass $mformdata
* @return boolean
*/
public function take_from_student($mformdata) {
@ -449,6 +609,7 @@ class mod_attendance_structure {
$record->sessionid = $mformdata->sessid;
$record->timetaken = $now;
$record->takenby = $USER->id;
$record->ipaddress = getremoteaddr(null);
$dbsesslog = $this->get_session_log($mformdata->sessid);
if (array_key_exists($record->studentid, $dbsesslog)) {
@ -489,39 +650,53 @@ class mod_attendance_structure {
return true;
}
/**
* Take attendance from form data.
*
* @param stdClass $formdata
*/
public function take_from_form_data($formdata) {
global $DB, $USER;
// TODO: WARNING - $formdata is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below.
// This whole function could do with a nice clean up.
$statuses = implode(',', array_keys( (array)$this->get_statuses() ));
$now = time();
$sesslog = array();
$formdata = (array)$formdata;
foreach ($formdata as $key => $value) {
if (substr($key, 0, 4) == 'user') {
$sid = substr($key, 4);
if (!(is_numeric($sid) && is_numeric($value))) { // Sanity check on $sid and $value.
// Look at Remarks field because the user options may not be passed if empty.
if (substr($key, 0, 7) == 'remarks') {
$sid = substr($key, 7);
if (!(is_numeric($sid))) { // Sanity check on $sid.
print_error('nonnumericid', 'attendance');
}
$sesslog[$sid] = new stdClass();
$sesslog[$sid]->studentid = $sid; // We check is_numeric on this above.
$sesslog[$sid]->statusid = $value; // We check is_numeric on this above.
$sesslog[$sid]->statusset = $statuses;
$sesslog[$sid]->remarks = '';
if (array_key_exists('remarks'.$sid, $formdata)) {
$sesslog[$sid]->remarks = clean_param($formdata['remarks' . $sid], PARAM_TEXT);
if (array_key_exists('user'.$sid, $formdata) && is_numeric($formdata['user' . $sid])) {
$sesslog[$sid]->statusid = $formdata['user' . $sid];
}
$sesslog[$sid]->statusset = $statuses;
$sesslog[$sid]->remarks = $value;
$sesslog[$sid]->sessionid = $this->pageparams->sessionid;
$sesslog[$sid]->timetaken = $now;
$sesslog[$sid]->takenby = $USER->id;
}
}
// Get existing session log.
$dbsesslog = $this->get_session_log($this->pageparams->sessionid);
foreach ($sesslog as $log) {
if ($log->statusid) {
// Don't save a record if no statusid or remark.
if (!empty($log->statusid) || !empty($log->remarks)) {
if (array_key_exists($log->studentid, $dbsesslog)) {
$log->id = $dbsesslog[$log->studentid]->id;
$DB->update_record('attendance_log', $log);
// Check if anything important has changed before updating record.
// Don't update timetaken/takenby records if nothing has changed.
if ($dbsesslog[$log->studentid]->remarks <> $log->remarks ||
$dbsesslog[$log->studentid]->statusid <> $log->statusid ||
$dbsesslog[$log->studentid]->statusset <> $log->statusset) {
$log->id = $dbsesslog[$log->studentid]->id;
$DB->update_record('attendance_log', $log);
}
} else {
$DB->insert_record('attendance_log', $log, false);
}
@ -531,6 +706,7 @@ class mod_attendance_structure {
$session = $this->get_session_info($this->pageparams->sessionid);
$session->lasttaken = $now;
$session->lasttakenby = $USER->id;
$DB->update_record('attendance_sessions', $session);
if ($this->grade != 0) {
@ -573,13 +749,21 @@ class mod_attendance_structure {
}
/**
* MDL-27591 made this method obsolete.
* Get users with enrolment status (Feature request MDL-27591)
*
* @param int $groupid
* @param int $page
* @return array
*/
public function get_users($groupid = 0, $page = 1) {
global $DB, $CFG;
// Fields we need from the user table.
$userfields = user_picture::fields('u', array('username' , 'idnumber' , 'institution' , 'department'));
$fields = array('username' , 'idnumber' , 'institution' , 'department');
// Get user identity fields if required - doesn't return original $fields array.
$extrafields = get_extra_user_fields($this->context, $fields);
$fields = array_merge($fields, $extrafields);
$userfields = user_picture::fields('u', $fields);
if (empty($this->pageparams->sort)) {
$this->pageparams->sort = ATT_SORT_DEFAULT;
@ -594,7 +778,7 @@ class mod_attendance_structure {
if ($page) {
$usersperpage = $this->pageparams->perpage;
if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) {
if (!empty($this->cm->groupingid)) {
$startusers = ($page - 1) * $usersperpage;
if ($groupid == 0) {
$groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id'));
@ -611,7 +795,7 @@ class mod_attendance_structure {
$orderby, $startusers, $usersperpage);
}
} else {
if (!empty($CFG->enablegroupmembersonly) and $this->cm->groupmembersonly) {
if (!empty($this->cm->groupingid)) {
if ($groupid == 0) {
$groups = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id'));
} else {
@ -658,14 +842,21 @@ class mod_attendance_structure {
// 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);
$users[$tempuser->studentid] = self::tempuser_to_user($tempuser);
}
return $users;
}
// Convert a tempuser record into a user object.
/**
* Convert a tempuser record into a user object.
*
* @param stdClass $tempuser
* @return object
*/
protected static function tempuser_to_user($tempuser) {
global $CFG;
$ret = (object)array(
'id' => $tempuser->studentid,
'firstname' => $tempuser->fullname,
@ -677,14 +868,26 @@ class mod_attendance_structure {
'picture' => 0,
'type' => 'temporary',
);
foreach (get_all_user_name_fields() as $namefield) {
$allfields = get_all_user_name_fields();
if (!empty($CFG->showuseridentity)) {
$allfields = array_merge($allfields, explode(',', $CFG->showuseridentity));
}
foreach ($allfields as $namefield) {
if (!isset($ret->$namefield)) {
$ret->$namefield = '';
}
}
return $ret;
}
/**
* Get user and include extra info.
*
* @param int $userid
* @return mixed|object
*/
public function get_user($userid) {
global $DB;
@ -726,6 +929,13 @@ class mod_attendance_structure {
return $user;
}
/**
* Get possible statuses.
*
* @param bool $onlyvisible
* @param bool $allsets
* @return array
*/
public function get_statuses($onlyvisible = true, $allsets = false) {
if (!isset($this->statuses)) {
// Get the statuses for the current set only.
@ -747,6 +957,11 @@ class mod_attendance_structure {
return $this->statuses;
}
/**
* Get session info.
* @param int $sessionid
* @return mixed
*/
public function get_session_info($sessionid) {
global $DB;
@ -762,6 +977,12 @@ class mod_attendance_structure {
return $this->sessioninfo[$sessionid];
}
/**
* Get sessions info
*
* @param array $sessionids
* @return array
*/
public function get_sessions_info($sessionids) {
global $DB;
@ -780,16 +1001,31 @@ class mod_attendance_structure {
return $sessions;
}
/**
* Get log.
*
* @param int $sessionid
* @return array
*/
public function get_session_log($sessionid) {
global $DB;
return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id');
return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id,statusset');
}
/**
* Update user grade.
* @param array $userids
*/
public function update_users_grade($userids) {
attendance_update_users_grade($this, $userids);
}
/**
* Get filtered log.
* @param int $userid
* @return array
*/
public function get_user_filtered_sessions_log($userid) {
global $DB;
@ -800,7 +1036,8 @@ class mod_attendance_structure {
$where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate";
}
if ($this->get_group_mode()) {
$sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks
$sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks,
ats.preventsharediptime, ats.preventsharedip
FROM {attendance_sessions} ats
JOIN {attendance_log} al ON ats.id = al.sessionid AND al.studentid = :uid
LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
@ -815,7 +1052,8 @@ class mod_attendance_structure {
'edate' => $this->pageparams->enddate);
} else {
$sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks
$sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid, al.remarks,
ats.preventsharediptime, ats.preventsharedip
FROM {attendance_sessions} ats
JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
@ -834,6 +1072,11 @@ class mod_attendance_structure {
return $sessions;
}
/**
* Get filtered log extended.
* @param int $userid
* @return array
*/
public function get_user_filtered_sessions_log_extended($userid) {
global $DB;
// All taked sessions (including previous groups).
@ -852,7 +1095,8 @@ class mod_attendance_structure {
$id = $DB->sql_concat(':value', 'ats.id');
if ($this->get_group_mode()) {
$sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description,
al.statusid, al.remarks, ats.studentscanmark
al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
ats.preventsharedip, ats.preventsharediptime
FROM {attendance_sessions} ats
RIGHT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
@ -861,7 +1105,8 @@ class mod_attendance_structure {
ORDER BY ats.sessdate ASC";
} else {
$sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
al.statusid, al.remarks, ats.studentscanmark
al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
ats.preventsharedip, ats.preventsharediptime
FROM {attendance_sessions} ats
RIGHT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
@ -890,9 +1135,9 @@ class mod_attendance_structure {
} else {
$where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql";
}
$sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
al.statusid, al.remarks, ats.studentscanmark
al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
ats.preventsharedip, ats.preventsharediptime
FROM {attendance_sessions} ats
LEFT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
@ -914,6 +1159,10 @@ class mod_attendance_structure {
return $sessions;
}
/**
* Delete sessions.
* @param array $sessionsids
*/
public function delete_sessions($sessionsids) {
global $DB;
if (attendance_existing_calendar_events_ids($sessionsids)) {
@ -931,6 +1180,12 @@ class mod_attendance_structure {
$event->trigger();
}
/**
* Update duration.
*
* @param array $sessionsids
* @param int $duration
*/
public function update_sessions_duration($sessionsids, $duration) {
global $DB;
@ -954,107 +1209,6 @@ class mod_attendance_structure {
$sessions->close();
}
/**
* Remove a status variable from an attendance instance
*
* @param stdClass $status
*/
public function remove_status($status) {
global $DB;
$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
*
* @param string $acronym
* @param string $description
* @param int $grade
*/
public function add_status($acronym, $description, $grade) {
global $DB;
if ($acronym && $description) {
$rec = new stdClass();
$rec->courseid = $this->course->id;
$rec->attendanceid = $this->id;
$rec->acronym = $acronym;
$rec->description = $description;
$rec->grade = $grade;
$rec->setnumber = $this->pageparams->statusset; // Save which set it is part of.
$rec->deleted = 0;
$rec->visible = 1;
$id = $DB->insert_record('attendance_statuses', $rec);
$rec->id = $id;
$event = \mod_attendance\event\status_added::create(array(
'objectid' => $this->id,
'context' => $this->context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade)));
$event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance_statuses', $rec);
$event->trigger();
} else {
print_error('cantaddstatus', 'attendance', $this->url_preferences());
}
}
/**
* Update status variable for a particular Attendance module instance
*
* @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;
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();
if ($acronym) {
$status->acronym = $acronym;
$updated[] = $acronym;
}
if ($description) {
$status->description = $description;
$updated[] = $description;
}
if (isset($grade)) {
$status->grade = $grade;
$updated[] = $grade;
}
$DB->update_record('attendance_statuses', $status);
$event = \mod_attendance\event\status_updated::create(array(
'objectid' => $this->id,
'context' => $this->context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade,
'updated' => implode(' ', $updated))));
$event->add_record_snapshot('course_modules', $this->cm);
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
}
/**
* Check if the email address is already in use by either another temporary user,
* or a real user.
@ -1080,4 +1234,41 @@ class mod_attendance_structure {
return null;
}
/**
* Gets the status to use when auto-marking.
*
* @param int $time the time the user first accessed the course.
* @param int $sessionid the related sessionid to check.
* @return int the statusid to assign to this user.
*/
public function get_automark_status($time, $sessionid) {
$statuses = $this->get_statuses();
// Statuses are returned highest grade first, find the first high grade we can assign to this user.
// Get status to use when unmarked.
$session = $this->sessioninfo[$sessionid];
$duration = $session->duration;
if (empty($duration)) {
$duration = get_config('attendance', 'studentscanmarksessiontimeend') * 60;
}
if ($time > $session->sessdate + $duration) {
// This session closed after the users access - use the unmarked state.
foreach ($statuses as $status) {
if (!empty($status->setunmarked)) {
return $status->id;
}
}
} else {
foreach ($statuses as $status) {
if ($status->studentavailability !== '0' &&
$this->sessioninfo[$sessionid]->sessdate + ($status->studentavailability * 60) > $time) {
// Found first status we could set.
return $status->id;
}
}
}
return;
}
}

26
classes/summary.php

@ -14,6 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that computes summary of users points
*
* @package mod_attendance
* @copyright 2016 Antonio Carlos Mariani http://antonio.c.mariani@gmail.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/attendance/locallib.php');
@ -47,8 +55,8 @@ class mod_attendance_summary {
/**
* Initializes the class
*
* @param int attendance instance identifier
* @param array userids user instances identifier
* @param int $attendanceid instance identifier
* @param array $userids user instances identifier
* @param int $startdate Attendance sessions startdate
* @param int $enddate Attendance sessions enddate
*/
@ -62,7 +70,7 @@ class mod_attendance_summary {
/**
* Returns true if the user has some session with points
*
* @param int userid User instance id
* @param int $userid User instance id
*
* @return boolean
*/
@ -106,7 +114,7 @@ class mod_attendance_summary {
/**
* Returns a summary of the points assigned to the user related to the taken sessions
*
* @param int userid User instance id
* @param int $userid User instance id
*
* @return array
*/
@ -135,7 +143,7 @@ class mod_attendance_summary {
/**
* Returns a summary of the points assigned to the user, both related to taken sessions and related to all sessions
*
* @param int userid User instance id
* @param int $userid User instance id
*
* @return array
*/
@ -172,7 +180,7 @@ class mod_attendance_summary {
/**
* Computes the summary of points for the users that have some taken session
*
* @param array userids user instances identifier
* @param array $userids user instances identifier
* @param int $startdate Attendance sessions startdate
* @param int $enddate Attendance sessions enddate
* @return (userid, numtakensessions, points, maxpoints)
@ -227,7 +235,7 @@ class mod_attendance_summary {
{$joingroup}
WHERE ats.attendanceid = :attid
AND ats.sessdate >= :cstartdate
AND ats.lasttakenby != 0
AND ats.lasttaken != 0
{$where}
GROUP BY atl.studentid";
$this->userspoints = $DB->get_records_sql($sql, $params);
@ -236,7 +244,7 @@ class mod_attendance_summary {
/**
* Computes the summary of taken sessions by acronym
*
* @param array userids user instances identifier
* @param array $userids user instances identifier
* @param int $startdate Attendance sessions startdate
* @param int $enddate Attendance sessions enddate
* @return null
@ -285,7 +293,7 @@ class mod_attendance_summary {
{$joingroup}
WHERE ats.attendanceid = :attid
AND ats.sessdate >= :cstartdate
AND ats.lasttakenby != 0
AND ats.lasttaken != 0
{$where}
GROUP BY atl.studentid, sts.setnumber, sts.acronym";
$this->userstakensessionsbyacronym = array();

25
classes/take_page_params.php

@ -13,7 +13,6 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Class definition for mod_attendance_take_page_params
@ -23,6 +22,8 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* contains functions/constants used by take attendance page.
*
@ -30,22 +31,34 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_take_page_params {
/** Sorted list. */
const SORTED_LIST = 1;
/** Sorted grid. */
const SORTED_GRID = 2;
/** Default view */
const DEFAULT_VIEW_MODE = self::SORTED_LIST;
/** @var int */
public $sessionid;
/** @var int */
public $grouptype;
/** @var int */
public $group;
/** @var int */
public $sort;
/** @var int */
public $copyfrom;
/** @var int view mode of taking attendance page*/
public $viewmode;
/** @var int */
public $gridcols;
/**
* Initialize params.
*/
public function init() {
if (!isset($this->group)) {
$this->group = 0;
@ -57,6 +70,9 @@ class mod_attendance_take_page_params {
$this->init_gridcols();
}
/**
* Initialise view mode params.
*/
private function init_view_mode() {
if (isset($this->viewmode)) {
set_user_preference("attendance_take_view_mode", $this->viewmode);
@ -65,6 +81,9 @@ class mod_attendance_take_page_params {
}
}
/**
* Initilise grid columns.
*/
private function init_gridcols() {
if (isset($this->gridcols)) {
set_user_preference("attendance_gridcolumns", $this->gridcols);
@ -73,6 +92,10 @@ class mod_attendance_take_page_params {
}
}
/**
* Get main page params.
* @return array
*/
public function get_significant_params() {
$params = array();

230
classes/task/auto_mark.php

@ -0,0 +1,230 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance task - auto mark.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\task;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/attendance/locallib.php');
/**
* get_scores class, used to get scores for submitted files.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class auto_mark extends \core\task\scheduled_task {
/**
* Returns localised general event name.
*
* @return string
*/
public function get_name() {
// Shown in admin screens.
return get_string('automarktask', 'mod_attendance');
}
/**
* Execte the task.
*/
public function execute() {
global $DB;
// Create some cache vars - might be nice to restructure this and make a smaller number of sql calls.
$cachecm = array();
$cacheatt = array();
$cachecourse = array();
$now = time(); // Store current time to use in queries so they all match nicely.
$sessions = $DB->get_recordset_select('attendance_sessions',
'automark > 0 AND automarkcompleted < 2 AND sessdate < ? ', array($now));
foreach ($sessions as $session) {
if ($session->sessdate + $session->duration < $now || // If session is over.
// OR if session is currently open and automark is set to do all.
($session->sessdate < $now && $session->automark == 1)) {
$userfirstaccess = array();
$donesomething = false; // Only trigger grades/events when an update actually occurs.
$sessionover = false; // Is this session over?
if ($session->sessdate + $session->duration < $now) {
$sessionover = true;
}
// Store cm/att/course in cachefields so we don't make unnecessary db calls.
// Would probably be nice to grab this stuff outside of the loop.
// Make sure this status set has something to setunmarked.
$setunmarked = $DB->get_field('attendance_statuses', 'id',
array('attendanceid' => $session->attendanceid, 'setnumber' => $session->statusset,
'setunmarked' => 1, 'deleted' => 0));
if (empty($setunmarked)) {
mtrace("No unmarked status configured for session id: ".$session->id);
continue;
}
if (empty($cacheatt[$session->attendanceid])) {
$cacheatt[$session->attendanceid] = $DB->get_record('attendance', array('id' => $session->attendanceid));
}
if (empty($cachecm[$session->attendanceid])) {
$cachecm[$session->attendanceid] = get_coursemodule_from_instance('attendance',
$session->attendanceid, $cacheatt[$session->attendanceid]->course);
}
$courseid = $cacheatt[$session->attendanceid]->course;
if (empty($cachecourse[$courseid])) {
$cachecourse[$courseid] = $DB->get_record('course', array('id' => $courseid));
}
$context = \context_module::instance($cachecm[$session->attendanceid]->id);
$pageparams = new \mod_attendance_take_page_params();
$pageparams->group = $session->groupid;
if (empty($session->groupid)) {
$pageparams->grouptype = 0;
} else {
$pageparams->grouptype = 1;
}
$pageparams->sessionid = $session->id;
if ($session->automark == 1) {
$userfirstacess = array();
// If set to do full automarking, get all users that have accessed course during session open.
$id = $DB->sql_concat('userid', 'ip'); // Users may access from multiple ip, make the first field unique.
$sql = "SELECT $id, userid, ip, min(timecreated) as timecreated
FROM {logstore_standard_log}
WHERE courseid = ? AND timecreated > ? AND timecreated < ?
GROUP BY userid, ip";
$timestart = $session->sessdate;
if (empty($session->lasttakenby) && $session->lasttaken > $timestart) {
// If the last time session was taken it was done automatically, use the last time taken
// as the start time for the logs we are interested in to help with performance.
$timestart = $session->lasttaken;
}
$duration = $session->duration;
if (empty($duration)) {
$duration = get_config('attendance', 'studentscanmarksessiontimeend') * 60;
}
$timeend = $timestart + $duration;
$logusers = $DB->get_recordset_sql($sql, array($courseid, $timestart, $timeend));
// Check if user access is in allowed subnet.
foreach ($logusers as $loguser) {
if (!empty($session->subnet) && !address_in_subnet($loguser->ip, $session->subnet)) {
// This record isn't in the right subnet.
continue;
}
if (empty($userfirstaccess[$loguser->userid]) ||
$userfirstaccess[$loguser->userid] > $loguser->timecreated) {
// Users may have accessed from mulitple ip addresses, find the earliest access.
$userfirstaccess[$loguser->userid] = $loguser->timecreated;
}
}
$logusers->close();
}
// Get all unmarked students.
$att = new \mod_attendance_structure($cacheatt[$session->attendanceid],
$cachecm[$session->attendanceid], $cachecourse[$courseid], $context, $pageparams);
$users = $att->get_users($session->groupid, 0);
$existinglog = $DB->get_recordset('attendance_log', array('sessionid' => $session->id));
$updated = 0;
foreach ($existinglog as $log) {
if (empty($log->statusid)) {
if ($sessionover || !empty($userfirstaccess[$log->studentid])) {
// Status needs updating.
if ($sessionover) {
$log->statusid = $setunmarked;
} else if (!empty($userfirstaccess[$log->studentid])) {
$log->statusid = $att->get_automark_status($userfirstaccess[$log->studentid], $session->id);
}
if (!empty($log->statusid)) {
$log->timetaken = $now;
$log->takenby = 0;
$log->remarks = get_string('autorecorded', 'attendance');
$DB->update_record('attendance_log', $log);
$updated++;
$donesomething = true;
}
}
}
unset($users[$log->studentid]);
}
$existinglog->close();
mtrace($updated . " session status updated");
$newlog = new \stdClass();
$newlog->timetaken = $now;
$newlog->takenby = 0;
$newlog->sessionid = $session->id;
$newlog->remarks = get_string('autorecorded', 'attendance');
$newlog->statusset = implode(',', array_keys( (array)$att->get_statuses()));
$added = 0;
foreach ($users as $user) {
if ($sessionover || !empty($userfirstaccess[$user->id])) {
if ($sessionover) {
$newlog->statusid = $setunmarked;
} else if (!empty($userfirstaccess[$user->id])) {
$newlog->statusid = $att->get_automark_status($userfirstaccess[$user->id], $session->id);
}
if (!empty($newlog->statusid)) {
$newlog->studentid = $user->id;
$DB->insert_record('attendance_log', $newlog);
$added++;
$donesomething = true;
}
}
}
mtrace($added . " session status inserted");
// Update lasttaken time and automarkcompleted for this session.
$session->lasttaken = $now;
$session->lasttakenby = 0;
if ($sessionover) {
$session->automarkcompleted = 2;
} else {
$session->automarkcompleted = 1;
}
$DB->update_record('attendance_sessions', $session);
if ($donesomething) {
if ($att->grade != 0) {
$att->update_users_grade(array_keys($users));
}
$params = array(
'sessionid' => $att->pageparams->sessionid,
'grouptype' => $att->pageparams->grouptype);
$event = \mod_attendance\event\attendance_taken::create(array(
'objectid' => $att->id,
'context' => $att->context,
'other' => $params));
$event->add_record_snapshot('course_modules', $att->cm);
$event->add_record_snapshot('attendance_sessions', $session);
$event->trigger();
}
}
}
}
}

158
classes/task/notify.php

@ -0,0 +1,158 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance task - Send warnings.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\task;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/attendance/locallib.php');
/**
* Task class
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class notify extends \core\task\scheduled_task {
/**
* Returns localised general event name.
*
* @return string
*/
public function get_name() {
// Shown in admin screens.
return get_string('notifytask', 'mod_attendance');
}
/**
* Execute the task.
*/
public function execute() {
global $DB;
if (empty(get_config('attendance', 'enablewarnings'))) {
return; // Warnings not enabled.
}
$now = time(); // Store current time to use in queries so they all match nicely.
$orderby = 'ORDER BY cm.id, atl.studentid, n.warningpercent ASC';
// Get records for attendance sessions that have been updated since last time this task ran.
// Note: this returns all users for these sessions - even if the users attendance wasn't changed
// since last time we ran, before sending a notification we check to see if the users have
// updated attendance logs since last time they were notified.
$records = attendance_get_users_to_notify(array(), $orderby, true);
$sentnotifications = array();
$thirdpartynotifications = array();
$numsentusers = 0;
$numsentthird = 0;
foreach ($records as $record) {
if (empty($sentnotifications[$record->userid])) {
$sentnotifications[$record->userid] = array();
}
if (!empty($record->emailuser)) {
// Only send one warning to this user from each attendance in this run.
// Flag any higher percent notifications as sent.
if (empty($sentnotifications[$record->userid]) || !in_array($record->aid, $sentnotifications[$record->userid])) {
// If has previously been sent a warning, check to see if this user has
// attendance updated since the last time the notification was sent.
if (!empty($record->timesent)) {
$sql = "SELECT *
FROM {attendance_log} l
JOIN {attendance_sessions} s ON s.id = l.sessionid
WHERE s.attendanceid = ? AND studentid = ? AND timetaken > ?";
if (!$DB->record_exists_sql($sql, array($record->aid, $record->userid, $record->timesent))) {
continue; // Skip this record and move to the next user.
}
}
// Convert variables in emailcontent.
$record = attendance_template_variables($record);
$user = $DB->get_record('user', array('id' => $record->userid));
$from = \core_user::get_noreply_user();
$oldforcelang = force_current_language($user->lang);
$emailcontent = format_text($record->emailcontent, $record->emailcontentformat);
$emailsubject = format_text($record->emailsubject, FORMAT_HTML);
email_to_user($user, $from, $emailsubject, $emailcontent, $emailcontent);
force_current_language($oldforcelang);
$sentnotifications[$record->userid][] = $record->aid;
$numsentusers++;
}
}
// Only send one warning to this user from each attendance in this run. - flag any higher percent notifications as sent.
if (!empty($record->thirdpartyemails)) {
$sendto = explode(',', $record->thirdpartyemails);
$record->percent = round($record->percent * 100)."%";
$context = \context_module::instance($record->cmid);
foreach ($sendto as $senduser) {
if (empty($senduser)) {
// Probably an extra comma in the thirdpartyusers field.
continue;
}
// Check user is allowed to receive warningemails.
if (has_capability('mod/attendance:warningemails', $context, $senduser)) {
if (empty($thirdpartynotifications[$senduser])) {
$thirdpartynotifications[$senduser] = array();
}
if (!isset($thirdpartynotifications[$senduser][$record->aid . '_' . $record->userid])) {
$thirdpartynotifications[$senduser][$record->aid . '_' . $record->userid]
= get_string('thirdpartyemailtext', 'attendance', $record);
}
} else {
mtrace("user".$senduser. "does not have capablity in cm".$record->cmid);
}
}
}
$notify = new \stdClass();
$notify->userid = $record->userid;
$notify->notifyid = $record->notifyid;
$notify->timesent = $now;
$DB->insert_record('attendance_warning_done', $notify);
}
if (!empty($numsentusers)) {
mtrace($numsentusers ." user emails sent");
}
if (!empty($thirdpartynotifications)) {
foreach ($thirdpartynotifications as $sendid => $notifications) {
$user = $DB->get_record('user', array('id' => $sendid));
$from = \core_user::get_noreply_user();
$oldforcelang = force_current_language($user->lang);
$emailcontent = implode("\n", $notifications);
$emailcontent .= "\n\n".get_string('thirdpartyemailtextfooter', 'attendance');
$emailcontent = format_text($emailcontent);
$emailsubject = get_string('thirdpartyemailsubject', 'attendance');
email_to_user($user, $from, $emailsubject, $emailcontent, $emailcontent);
force_current_language($oldforcelang);
$numsentthird++;
}
if (!empty($numsentthird)) {
mtrace($numsentthird ." thirdparty emails sent");
}
}
}
}

16
classes/view_page_params.php

@ -13,7 +13,6 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Class definition for mod_attendance_view_page_params
@ -23,6 +22,8 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* contains functions/constants used by attendance view page.
*
@ -30,17 +31,30 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_view_page_params extends mod_attendance_page_with_filter_controls {
/** Only This course */
const MODE_THIS_COURSE = 0;
/** All courses */
const MODE_ALL_COURSES = 1;
/** @var int */
public $studentid;
/** @var string */
public $mode;
/**
* mod_attendance_view_page_params constructor.
*/
public function __construct() {
$this->defaultview = ATT_VIEW_MONTHS;
}
/**
* Get params for url.
*
* @return array
*/
public function get_significant_params() {
$params = array();

124
coursesummary.php

@ -0,0 +1,124 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance course summary report.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/mod/attendance/lib.php');
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir.'/coursecatlib.php');
$category = optional_param('category', 0, PARAM_INT);
$download = optional_param('download', '', PARAM_ALPHA);
$sort = optional_param('tsort', '', PARAM_ALPHA);
$fromcourse = optional_param('fromcourse', 0, PARAM_INT);
$admin = false;
if (empty($fromcourse)) {
$admin = true;
admin_externalpage_setup('managemodules');
} else {
require_login($fromcourse);
}
if (empty($category)) {
$context = context_system::instance();
$courses = array(); // Show all courses.
} else {
$context = context_coursecat::instance($category);
$coursecat = coursecat::get($category);
$courses = $coursecat->get_courses(array('recursive' => true, 'idonly' => true));
}
// Check permissions.
require_capability('mod/attendance:viewsummaryreports', $context);
$exportfilename = 'attendancecoursesummary.csv';
$PAGE->set_url('/mod/attendance/coursesummary.php', array('category' => $category));
$PAGE->set_heading($SITE->fullname);
$table = new flexible_table('attendancecoursesummary');
$table->define_baseurl($PAGE->url);
if (!$table->is_downloading($download, $exportfilename)) {
echo $OUTPUT->header();
$heading = get_string('coursesummary', 'mod_attendance');
if (!empty($category)) {
$heading .= " (".$coursecat->name.")";
}
echo $OUTPUT->heading($heading);
if ($admin) {
// Only show tabs if displaying via the admin page.
$tabmenu = attendance_print_settings_tabs('coursesummary');
echo $tabmenu;
}
$url = new moodle_url('/mod/attendance/coursesummary.php', array('category' => $category, 'fromcourse' => $fromcourse));
if ($admin) {
$options = coursecat::make_categories_list('mod/attendance:viewsummaryreports');
echo $OUTPUT->single_select($url, 'category', $options, $category);
}
}
$table->define_columns(array('course', 'percentage'));
$table->define_headers(array(get_string('course'),
get_string('averageattendance', 'attendance')));
$table->sortable(true);
$table->no_sorting('course');
$table->set_attribute('cellspacing', '0');
$table->set_attribute('class', 'generaltable generalbox');
$table->show_download_buttons_at(array(TABLE_P_BOTTOM));
$table->setup();
// Work out direction of sort required.
$sortcolumns = $table->get_sort_columns();
// Now do sorting if specified.
$orderby = ' ORDER BY percentage ASC';
if (!empty($sort)) {
$direction = ' DESC';
if (!empty($sortcolumns[$sort]) && $sortcolumns[$sort] == SORT_ASC) {
$direction = ' ASC';
}
$orderby = " ORDER BY $sort $direction";
}
$records = attendance_course_users_points($courses, $orderby);
foreach ($records as $record) {
if (!$table->is_downloading($download, $exportfilename)) {
$url = new moodle_url('/mod/attendance/index.php', array('id' => $record->courseid));
$name = html_writer::link($url, $record->coursename);
} else {
$name = $record->coursename;
}
$table->add_data(array($name, round($record->percentage * 100)."%"));
}
$table->finish_output();
if (!$table->is_downloading()) {
echo $OUTPUT->footer();
}

21
db/access.php

@ -132,4 +132,25 @@ $capabilities = array(
'manager' => CAP_ALLOW
)
),
// Allow access to site level reports.
'mod/attendance:viewsummaryreports' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_COURSECAT,
'archetypes' => array(
'manager' => CAP_ALLOW
)
),
// Users that can receive extra warning e-mails.
'mod/attendance:warningemails' => array(
'riskbitmask' => RISK_DATALOSS,
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
)
);

36
db/events.php

@ -0,0 +1,36 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance event handler definition.
*
* @package mod_attendance
* @category event
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// List of observers.
$observers = array(
array(
'eventname' => '\core\event\course_content_deleted',
'callback' => 'mod_attendance_observer::course_content_deleted',
),
);

3
db/install.php

@ -13,7 +13,6 @@
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* post installation hook for adding data.
@ -23,6 +22,8 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Post installation procedure
*/

54
db/install.xml

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/attendance/db" VERSION="20161107" COMMENT="XMLDB file for Moodle mod/attendance"
<XMLDB PATH="mod/attendance/db" VERSION="20170620" COMMENT="XMLDB file for Moodle mod/attendance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
>
<TABLES>
<TABLE NAME="attendance" COMMENT="Attendance module table">
<FIELDS>
@ -11,9 +11,12 @@
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="100" SEQUENCE="false" COMMENT="This is maximum grade for instance"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time the settings for this attendance instance were last modified."/>
<FIELD NAME="subnet" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Restrict ability for students to mark by subnet."/>
<FIELD NAME="intro" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="This field is a requirement for activity modules."/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="This field is a requirement for activity modules."/>
<FIELD NAME="subnet" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Default subnet used when creating sessions."/>
<FIELD NAME="sessiondetailspos" TYPE="char" LENGTH="5" NOTNULL="true" DEFAULT="left" SEQUENCE="false" COMMENT="Position for the session detail columns related to summary columns."/>
<FIELD NAME="showsessiondetails" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Define if session details should be shown in reports."/>
<FIELD NAME="showextrauserdetails" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Define if extra user details should be shown in reports."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance"/>
@ -22,7 +25,7 @@
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
</INDEXES>
</TABLE>
<TABLE NAME="attendance_sessions" COMMENT="attendance_sessions table retrofitted from MySQL">
<TABLE NAME="attendance_sessions" COMMENT="attendance_sessions table">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="attendanceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
@ -35,7 +38,15 @@
<FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="studentscanmark" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="autoassignstatus" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="studentpassword" TYPE="char" LENGTH="50" NOTNULL="false" DEFAULT="" SEQUENCE="false"/>
<FIELD NAME="subnet" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Restrict ability for students to mark by subnet."/>
<FIELD NAME="automark" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="automarkcompleted" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="statusset" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Which set of statuses to use"/>
<FIELD NAME="absenteereport" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="preventsharedip" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="preventsharediptime" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="caleventid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
@ -58,6 +69,7 @@
<FIELD NAME="timetaken" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="When attendance of this student was taken"/>
<FIELD NAME="takenby" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="remarks" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="ipaddress" TYPE="char" LENGTH="45" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance_log"/>
@ -75,6 +87,8 @@
<FIELD NAME="acronym" TYPE="char" LENGTH="2" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="description" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="number" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="2"/>
<FIELD NAME="studentavailability" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="How many minutes this status is available when self marking is enabled."/>
<FIELD NAME="setunmarked" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false" COMMENT="Set this status if unmarked at end of session."/>
<FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="deleted" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="setnumber" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Allows different sets of statuses to be allocated to different sessions"/>
@ -105,5 +119,37 @@
<INDEX NAME="studentid" UNIQUE="true" FIELDS="studentid"/>
</INDEXES>
</TABLE>
<TABLE NAME="attendance_warning" COMMENT="Warning configuration">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="idnumber" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="attendance id or other id relating to this warning."/>
<FIELD NAME="warningpercent" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Percentage that triggers this warning."/>
<FIELD NAME="warnafter" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Start warning after this number of taken sessions."/>
<FIELD NAME="maxwarn" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Maximum number of warnings to send."/>
<FIELD NAME="emailuser" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Should the user be notified at this level."/>
<FIELD NAME="emailsubject" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Email subject line for emails going to user"/>
<FIELD NAME="emailcontent" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The html-formatted text that should be sent to the user"/>
<FIELD NAME="emailcontentformat" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Format of the emailcontent field"/>
<FIELD NAME="thirdpartyemails" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="list of extra users to receive warnings"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="level_id" TYPE="unique" FIELDS="idnumber, warningpercent, warnafter"/>
</KEYS>
</TABLE>
<TABLE NAME="attendance_warning_done" COMMENT="Warnings processed">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="notifyid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="id of warning"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="user id of user"/>
<FIELD NAME="timesent" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Time warning sent to user."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="notifyid_userid" UNIQUE="false" FIELDS="notifyid, userid"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>

2
db/services.php

@ -17,7 +17,7 @@
/**
* Web service local plugin attendance external functions and service definitions.
*
* @package localwsattendance
* @package mod_attendance
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

44
db/tasks.php

@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Attendance module tasks.
*
* @package mod_attendance
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tasks = array(
array(
'classname' => 'mod_attendance\task\auto_mark',
'blocking' => 0,
'minute' => '8',
'hour' => '*',
'day' => '*',
'dayofweek' => '*',
'month' => '*'),
array(
'classname' => 'mod_attendance\task\notify',
'blocking' => 0,
'minute' => '30',
'hour' => '1',
'day' => '*',
'dayofweek' => '*',
'month' => '*')
);

296
db/upgrade.php

@ -14,9 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
require_once(dirname(__FILE__) . '/upgradelib.php');
/**
* upgrade processes for this module.
*
@ -25,6 +22,9 @@ require_once(dirname(__FILE__) . '/upgradelib.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(dirname(__FILE__) . '/upgradelib.php');
/**
* upgrade this attendance instance - this function could be skipped but it will be needed later
* @param int $oldversion The old version of the attendance module
@ -46,7 +46,7 @@ function xmldb_attendance_upgrade($oldversion=0) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint($result, 2014112000, 'attendance');
upgrade_mod_savepoint(true, 2014112000, 'attendance');
}
if ($oldversion < 2014112001) {
@ -85,7 +85,7 @@ function xmldb_attendance_upgrade($oldversion=0) {
// Delete old capabilities.
$DB->delete_records_select('capabilities', 'component = ?', array('mod_attforblock'));
upgrade_plugin_savepoint($result, 2014112001, 'mod', 'attendance');
upgrade_mod_savepoint(true, 2014112001, 'attendance');
}
if ($oldversion < 2015040501) {
@ -214,5 +214,291 @@ function xmldb_attendance_upgrade($oldversion=0) {
upgrade_mod_savepoint(true, 2016121300, 'attendance');
}
if ($oldversion < 2016121305) {
// Define field timemodified to be added to attendance.
$table = new xmldb_table('attendance');
$fields = [];
$fields[] = new xmldb_field('intro', XMLDB_TYPE_TEXT, null, null, null, null, null, 'timemodified');
$fields[] = new xmldb_field('introformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, 0, 'intro');
// Conditionally launch add field.
foreach ($fields as $field) {
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121305, 'attendance');
}
if ($oldversion < 2016121306) {
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('studentpassword');
$field->set_attributes(XMLDB_TYPE_CHAR, '50', null, false, null, '', 'studentscanmark');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2016121306, 'attendance');
}
if ($oldversion < 2016121309) {
// Define field studentavailability to be added to attendance_statuses.
$table = new xmldb_table('attendance_statuses');
$field = new xmldb_field('studentavailability', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'grade');
// Conditionally launch add field studentavailability.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121309, 'attendance');
}
if ($oldversion < 2016121310) {
$table = new xmldb_table('attendance_sessions');
$newfield = $table->add_field('subnet', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'studentpassword');
if (!$dbman->field_exists($table, $newfield)) {
$dbman->add_field($table, $newfield);
// The meaning of the subnet in the attendance table has changed - it is now the "default" value - find all existing
// Attendance with subnet set and set the session subnet for these.
$attendances = $DB->get_recordset_select('attendance', 'subnet IS NOT NULL');
foreach ($attendances as $attendance) {
if (!empty($attendance->subnet)) {
// Get all sessions for this attendance.
$sessions = $DB->get_recordset('attendance_sessions', array('attendanceid' => $attendance->id));
foreach ($sessions as $session) {
$session->subnet = $attendance->subnet;
$DB->update_record('attendance_sessions', $session);
}
$sessions->close();
}
}
$attendances->close();
}
upgrade_mod_savepoint(true, 2016121310, 'attendance');
}
if ($oldversion < 2016121311) {
// Define field setunmarked to be added to attendance_statuses.
$table = new xmldb_table('attendance_statuses');
$field = new xmldb_field('setunmarked', XMLDB_TYPE_INTEGER, '2', null, null, null, null, 'studentavailability');
// Conditionally launch add field setunmarked.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field setunmarked to be added to attendance_statuses.
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('automark', XMLDB_TYPE_INTEGER, '1', null, true, null, '0', 'subnet');
// Conditionally launch add field automark.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$field = new xmldb_field('automarkcompleted', XMLDB_TYPE_INTEGER, '1', null, true, null, '0', 'automark');
// Conditionally launch add field automarkcompleted.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121311, 'attendance');
}
if ($oldversion < 2016121314) {
// Automark values changed.
$default = get_config('attendance', 'automark_default');
if (!empty($default)) { // Change default if set.
set_config('automark_default', 2, 'attendance');
}
// Update any sessions set to use automark = 1.
$sql = "UPDATE {attendance_sessions} SET automark = 2 WHERE automark = 1";
$DB->execute($sql);
// Update automarkcompleted to 2 if already complete.
$sql = "UPDATE {attendance_sessions} SET automarkcompleted = 2 WHERE automarkcompleted = 1";
$DB->execute($sql);
upgrade_mod_savepoint(true, 2016121314, 'attendance');
}
// Add new warning table.
if ($oldversion < 2016121315) {
// Define table attendance_warning_done to be created.
$table = new xmldb_table('attendance_warning_done');
// Adding fields to table attendance_warning_done.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('notifyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('timesent', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
// Adding keys to table attendance_warning_done.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
// Adding indexes to table attendance_warning_done.
$table->add_index('notifyid_userid', XMLDB_INDEX_UNIQUE, array('notifyid', 'userid'));
// Conditionally launch create table for attendance_warning_done.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121315, 'attendance');
}
if ($oldversion < 2016121318) {
// Fix key.
$table = new xmldb_table('attendance_warning');
if (!$dbman->table_exists($table)) {
// Adding fields to table attendance_warning.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('idnumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('warningpercent', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('warnafter', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('emailuser', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
$table->add_field('emailsubject', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
$table->add_field('emailcontent', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null);
$table->add_field('emailcontentformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
$table->add_field('thirdpartyemails', XMLDB_TYPE_TEXT, null, null, null, null, null);
// Adding keys to table attendance_warning.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('level_id', XMLDB_KEY_UNIQUE, array('idnumber', 'warningpercent', 'warnafter'));
// Conditionally launch create table for attendance_warning.
$dbman->create_table($table);
} else {
// Key definition is probably incorrect so fix it - drop_key dml function doesn't seem to work.
$indexes = $DB->get_indexes('attendance_warning');
foreach ($indexes as $name => $index) {
if ($DB->get_dbfamily() === 'mysql') {
$DB->execute("ALTER TABLE {attendance_warning} DROP INDEX ". $name);
} else {
$DB->execute("DROP INDEX ". $name);
}
}
$index = new xmldb_key('level_id', XMLDB_KEY_UNIQUE, array('idnumber', 'warningpercent', 'warnafter'));
$dbman->add_key($table, $index);
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121318, 'attendance');
}
if ($oldversion < 2016121319) {
// Define field setunmarked to be added to attendance_statuses.
$table = new xmldb_table('attendance_warning');
$field = new xmldb_field('maxwarn', XMLDB_TYPE_INTEGER, '10', null, true, null, '1', 'warnafter');
// Conditionally launch add field automark.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field setunmarked to be added to attendance_statuses.
$table = new xmldb_table('attendance_warning_done');
$index = new xmldb_index('notifyid_userid', XMLDB_INDEX_UNIQUE, array('notifyid', 'userid'));
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
$index = new xmldb_index('notifyid', XMLDB_INDEX_NOTUNIQUE, array('notifyid', 'userid'));
$dbman->add_index($table, $index);
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121319, 'attendance');
}
if ($oldversion < 2016121321) {
// Warnings idnumber field should use attendanceid instead of cmid.
$sql = "SELECT cm.id, cm.instance
FROM {course_modules} cm
JOIN {modules} md ON md.id = cm.module AND md.name = 'attendance'";
$idnumbers = $DB->get_records_sql_menu($sql);
$warnings = $DB->get_recordset('attendance_warning');
foreach ($warnings as $warning) {
if (!empty($warning->idnumber) && !empty($idnumbers[$warning->idnumber])) {
$warning->idnumber = $idnumbers[$warning->idnumber];
$DB->update_record("attendance_warning", $warning);
}
}
$warnings->close();
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2016121321, 'attendance');
}
if ($oldversion < 2016121324) {
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('absenteereport');
$field->set_attributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1', 'statusset');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2016121324, 'attendance');
}
if ($oldversion < 2016121325) {
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('autoassignstatus');
$field->set_attributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'studentscanmark');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2016121325, 'attendance');
}
if ($oldversion < 2016121328) {
$table = new xmldb_table('attendance');
$field = new xmldb_field('showextrauserdetails', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED,
XMLDB_NOTNULL, null, '1', 'showsessiondetails');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2016121328, 'attendance');
}
if ($oldversion < 2016121330) {
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('preventsharedip', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED,
XMLDB_NOTNULL, null, '0', 'absenteereport');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$field = new xmldb_field('preventsharediptime', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
null, null, null, 'preventsharedip');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$table = new xmldb_table('attendance_log');
$field = new xmldb_field('ipaddress', XMLDB_TYPE_CHAR, '45', null,
null, null, '', 'remarks');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2016121330, 'attendance');
}
return $result;
}

7
db/upgradelib.php

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Helper functions to keep upgrade.php clean.
*
@ -24,6 +22,11 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Function to help upgrade old attendance records and create calendar events.
*/
function attendance_upgrade_create_calendar_events() {
global $DB;

131
defaultstatus.php

@ -0,0 +1,131 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Allows default status set to be modified.
*
* @package mod_attendance
* @copyright 2017 Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/mod/attendance/lib.php');
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$action = optional_param('action', null, PARAM_INT);
$statusid = optional_param('statusid', null, PARAM_INT);
admin_externalpage_setup('managemodules');
$url = new moodle_url('/mod/attendance/defaultstatus.php', array('statusid' => $statusid, 'action' => $action));
// Check sesskey if we are performing an action.
if (!empty($action)) {
require_sesskey();
}
$output = $PAGE->get_renderer('mod_attendance');
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('defaultstatus', 'mod_attendance'));
$tabmenu = attendance_print_settings_tabs('defaultstatus');
echo $tabmenu;
// TODO: Would be good to combine this code block with the one in preferences to avoid duplication.
$errors = array();
switch ($action) {
case mod_attendance_preferences_page_params::ACTION_ADD:
$newacronym = optional_param('newacronym', null, PARAM_TEXT);
$newdescription = optional_param('newdescription', null, PARAM_TEXT);
$newgrade = optional_param('newgrade', 0, PARAM_RAW);
$newstudentavailability = optional_param('newstudentavailability', null, PARAM_INT);
$newgrade = unformat_float($newgrade);
// Default value uses setnumber/attendanceid = 0.
$status = new stdClass();
$status->attendanceid = 0;
$status->acronym = $newacronym;
$status->description = $newdescription;
$status->grade = $newgrade;
$status->studentavailability = $newstudentavailability;
$status->setnumber = 0;
attendance_add_status($status);
break;
case mod_attendance_preferences_page_params::ACTION_DELETE:
$confirm = optional_param('confirm', null, PARAM_INT);
$statuses = attendance_get_statuses(0, false);
$status = $statuses[$statusid];
if (isset($confirm)) {
attendance_remove_status($status);
echo $OUTPUT->notification(get_string('statusdeleted', 'attendance'), 'success');
break;
}
$message = get_string('deletecheckfull', '', get_string('variable', 'attendance'));
$message .= str_repeat(html_writer::empty_tag('br'), 2);
$message .= $status->acronym.': '.
($status->description ? $status->description : get_string('nodescription', 'attendance'));
$confirmurl = $url;
$confirmurl->param('confirm', 1);
echo $OUTPUT->confirm($message, $confirmurl, $url);
echo $OUTPUT->footer();
exit;
case mod_attendance_preferences_page_params::ACTION_HIDE:
$statuses = attendance_get_statuses(0, false);
$status = $statuses[$statusid];
attendance_update_status($status, null, null, null, 0);
break;
case mod_attendance_preferences_page_params::ACTION_SHOW:
$statuses = attendance_get_statuses(0, false);
$status = $statuses[$statusid];
attendance_update_status($status, null, null, null, 1);
break;
case mod_attendance_preferences_page_params::ACTION_SAVE:
$acronym = required_param_array('acronym', PARAM_TEXT);
$description = required_param_array('description', PARAM_TEXT);
$grade = required_param_array('grade', PARAM_RAW);
$studentavailability = optional_param_array('studentavailability', '0', PARAM_RAW);
$unmarkedstatus = optional_param('setunmarked', null, PARAM_INT);
foreach ($grade as &$val) {
$val = unformat_float($val);
}
$statuses = attendance_get_statuses(0, false);
foreach ($acronym as $id => $v) {
$status = $statuses[$id];
$setunmarked = false;
if ($unmarkedstatus == $id) {
$setunmarked = true;
}
if (!isset($studentavailability[$id]) || !is_numeric($studentavailability[$id])) {
$studentavailability[$id] = 0;
}
$errors[$id] = attendance_update_status($status, $acronym[$id], $description[$id], $grade[$id],
null, null, null, $studentavailability[$id], $setunmarked);
}
echo $OUTPUT->notification(get_string('eventstatusupdated', 'attendance'), 'success');
break;
}
$statuses = attendance_get_statuses(0, false);
$prefdata = new attendance_default_statusset($statuses, $errors);
echo $output->render($prefdata);
echo $OUTPUT->footer();

68
export.php

@ -186,9 +186,9 @@ if ($formdata = $mform->get_data()) {
}
if ($formdata->format === 'text') {
exporttocsv($data, $filename);
attendance_exporttocsv($data, $filename);
} else {
exporttotableed($data, $filename, $formdata->format);
attendance_exporttotableed($data, $filename, $formdata->format);
}
exit;
} else {
@ -207,68 +207,4 @@ $mform->display();
echo $OUTPUT->footer();
function exporttotableed($data, $filename, $format) {
global $CFG;
if ($format === 'excel') {
require_once("$CFG->libdir/excellib.class.php");
$filename .= ".xls";
$workbook = new MoodleExcelWorkbook("-");
} else {
require_once("$CFG->libdir/odslib.class.php");
$filename .= ".ods";
$workbook = new MoodleODSWorkbook("-");
}
// Sending HTTP headers.
$workbook->send($filename);
// Creating the first worksheet.
$myxls = $workbook->add_worksheet('Attendances');
// Format types.
$formatbc = $workbook->add_format();
$formatbc->set_bold(1);
$myxls->write(0, 0, get_string('course'), $formatbc);
$myxls->write(0, 1, $data->course);
$myxls->write(1, 0, get_string('group'), $formatbc);
$myxls->write(1, 1, $data->group);
$i = 3;
$j = 0;
foreach ($data->tabhead as $cell) {
// Merge cells if the heading would be empty (remarks column).
if (empty($cell)) {
$myxls->merge_cells($i, $j - 1, $i, $j);
} else {
$myxls->write($i, $j, $cell, $formatbc);
}
$j++;
}
$i++;
$j = 0;
foreach ($data->table as $row) {
foreach ($row as $cell) {
$myxls->write($i, $j++, $cell);
}
$i++;
$j = 0;
}
$workbook->close();
}
function exporttocsv($data, $filename) {
$filename .= ".txt";
header("Content-Type: application/download\n");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
header("Pragma: public");
echo get_string('course')."\t".$data->course."\n";
echo get_string('group')."\t".$data->group."\n\n";
echo implode("\t", $data->tabhead)."\n";
foreach ($data->table as $row) {
echo implode("\t", $row)."\n";
}
}

6
export_form.php

@ -137,6 +137,12 @@ class mod_attendance_export_form extends moodleform {
$mform->addElement('hidden', 'id', $cm->id);
}
/**
* Validate form.
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);

64
externallib.php

@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Externallib.php file for attendance plugin.
*
* @package local_attendance
* @package mod_attendance
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -25,17 +26,36 @@ defined('MOODLE_INTERNAL') || die;
require_once("$CFG->libdir/externallib.php");
require_once(dirname(__FILE__).'/classes/attendance_webservices_handler.php');
/**
* Class mod_wsattendance_external
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_wsattendance_external extends external_api {
/**
* Get parameter list.
* @return external_function_parameters
*/
public static function get_courses_with_today_sessions_parameters() {
return new external_function_parameters (
array('userid' => new external_value(PARAM_INT, 'User id.', VALUE_DEFAULT, 0)));
}
/**
* Get list of courses with active sessions for today.
* @param int $userid
* @return array
*/
public static function get_courses_with_today_sessions($userid) {
return attendance_handler::get_courses_with_today_sessions($userid);
}
/**
* Get structure of an attendance session.
*
* @return array
*/
private static function get_session_structure() {
$session = array('id' => new external_value(PARAM_INT, 'Session id.'),
'attendanceid' => new external_value(PARAM_INT, 'Attendance id.'),
@ -48,11 +68,19 @@ class mod_wsattendance_external extends external_api {
'description' => new external_value(PARAM_TEXT, 'Session description.'),
'descriptionformat' => new external_value(PARAM_INT, 'Session description format.'),
'studentscanmark' => new external_value(PARAM_INT, 'Students can mark their own presence.'),
'absenteereport' => new external_value(PARAM_INT, 'Session included in absetee reports.'),
'autoassignstatus' => new external_value(PARAM_INT, 'Automatically assign a status to students.'),
'preventsharedip' => new external_value(PARAM_INT, 'Prevent students from sharing IP addresses.'),
'preventsharediptime' => new external_value(PARAM_INT, 'Time delay before IP address is allowed again.'),
'statusset' => new external_value(PARAM_INT, 'Session statusset.'));
return $session;
}
/**
* Show structure of return.
* @return external_multiple_structure
*/
public static function get_courses_with_today_sessions_returns() {
$todaysessions = self::get_session_structure();
@ -68,15 +96,31 @@ class mod_wsattendance_external extends external_api {
return new external_multiple_structure(new external_single_structure(($courses)));
}
/**
* Get session params.
*
* @return external_function_parameters
*/
public static function get_session_parameters() {
return new external_function_parameters (
array('sessionid' => new external_value(PARAM_INT, 'session id')));
}
/**
* Get session.
*
* @param int $sessionid
* @return mixed
*/
public static function get_session($sessionid) {
return attendance_handler::get_session($sessionid);
}
/**
* Show return values of get_session.
*
* @return external_single_structure
*/
public static function get_session_returns() {
$statuses = array('id' => new external_value(PARAM_INT, 'Status id.'),
'attendanceid' => new external_value(PARAM_INT, 'Attendance id.'),
@ -105,6 +149,11 @@ class mod_wsattendance_external extends external_api {
return new external_single_structure($session);
}
/**
* Update user status params.
*
* @return external_function_parameters
*/
public static function update_user_status_parameters() {
return new external_function_parameters(
array('sessionid' => new external_value(PARAM_INT, 'Session id'),
@ -114,10 +163,23 @@ class mod_wsattendance_external extends external_api {
'statusset' => new external_value(PARAM_TEXT, 'Status set of session')));
}
/**
* Update user status.
*
* @param int $sessionid
* @param int $studentid
* @param int $takenbyid
* @param int $statusid
* @param int $statusset
*/
public static function update_user_status($sessionid, $studentid, $takenbyid, $statusid, $statusset) {
return attendance_handler::update_user_status($sessionid, $studentid, $takenbyid, $statusid, $statusset);
}
/**
* Show return values.
* @return external_value
*/
public static function update_user_status_returns() {
return new external_value(PARAM_TEXT, 'Http code');
}

94
import/sessions.php

@ -0,0 +1,94 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Import attendance sessions.
*
* @package mod_attendance
* @author Chris Wharton <chriswharton@catalyst.net.nz>
* @copyright 2017 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true);
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->dirroot . '/mod/attendance/lib.php');
require_once($CFG->dirroot . '/mod/attendance/locallib.php');
admin_externalpage_setup('managemodules');
$pagetitle = get_string('importsessions', 'attendance');
$context = context_system::instance();
$url = new moodle_url('/mod/attendance/import/sessions.php');
$PAGE->set_context($context);
$PAGE->set_url($url);
$PAGE->set_title($pagetitle);
$PAGE->set_pagelayout('admin');
$PAGE->set_heading($pagetitle);
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('importsessions', 'attendance'));
$tabmenu = attendance_print_settings_tabs('importsessions');
echo $tabmenu;
$form = null;
if (optional_param('needsconfirm', 0, PARAM_BOOL)) {
$form = new \mod_attendance\form\import\sessions($url->out(false));
} else if (optional_param('confirm', 0, PARAM_BOOL)) {
$importer = new \mod_attendance\import\sessions();
$form = new \mod_attendance\form\import\sessions_confirm(null, $importer);
} else {
$form = new \mod_attendance\form\import\sessions($url->out(false));
}
if ($form->is_cancelled()) {
$form = new \mod_attendance\form\import\sessions($url->out(false));
} else if ($data = $form->get_data()) {
require_sesskey();
if ($data->confirm) {
$importid = $data->importid;
$importer = new \mod_attendance\import\sessions(null, null, null, $importid, $data, true);
$error = $importer->get_error();
if ($error) {
$form = new \mod_attendance\form\import\sessions($url->out(false));
$form->set_import_error($error);
} else {
$sessions = $importer->import();
mod_attendance_notifyqueue::show();
echo $OUTPUT->continue_button($url);
die();
}
} else {
$text = $form->get_file_content('importfile');
$encoding = $data->encoding;
$delimiter = $data->delimiter_name;
$importer = new \mod_attendance\import\sessions($text, $encoding, $delimiter, 0, null, true);
$confirmform = new \mod_attendance\form\import\sessions_confirm(null, $importer);
$form = $confirmform;
$pagetitle = get_string('confirmcolumnmappings', 'attendance');
}
}
echo $OUTPUT->heading($pagetitle);
$form->display();
echo $OUTPUT->footer();

361
lang/en/attendance.php

@ -22,11 +22,8 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['attendance:addinstance'] = 'Add a new attendance activity';
$string['Aacronym'] = 'A';
$string['adduser'] = 'Add user';
$string['Afull'] = 'Absent';
$string['allsessions'] = 'All sessions';
$string['Eacronym'] = 'E';
$string['Efull'] = 'Excused';
$string['Lacronym'] = 'L';
@ -34,31 +31,50 @@ $string['Lfull'] = 'Late';
$string['Pacronym'] = 'P';
$string['Pfull'] = 'Present';
$string['acronym'] = 'Acronym';
$string['absenteereport'] = 'Absentee report';
$string['add'] = 'Add';
$string['addmultiplesessions'] = 'Multiple sessions';
$string['addwarning'] = 'Add warning';
$string['addsession'] = 'Add session';
$string['allcourses'] = 'All courses';
$string['adduser'] = 'Add user';
$string['all'] = 'All';
$string['allcourses'] = 'All courses';
$string['allpast'] = 'All past';
$string['attendancedata'] = 'Attendance data';
$string['attendanceforthecourse'] = 'Attendance for the course';
$string['attendancegrade'] = 'Attendance grade';
$string['attendancenotstarted'] = 'Attendance has not started yet for this course';
$string['attendancepercent'] = 'Attendance percent';
$string['attendancereport'] = 'Attendance report';
$string['attendancesuccess'] = 'Attendance has been successfully taken';
$string['attendanceupdated'] = 'Attendance successfully updated';
$string['allsessions'] = 'All sessions';
$string['attendance:addinstance'] = 'Add a new attendance activity';
$string['attendance:canbelisted'] = 'Appears in the roster';
$string['attendance:changepreferences'] = 'Changing Preferences';
$string['attendance:changeattendances'] = 'Changing Attendances';
$string['attendance:changepreferences'] = 'Changing Preferences';
$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';
$string['attendance:viewsummaryreports'] = 'View course summary reports';
$string['attendance:warningemails'] = 'Can be subscribed to emails with absentee users';
$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';
$string['attendancedata'] = 'Attendance data';
$string['attendanceforthecourse'] = 'Attendance for the course';
$string['attendancegrade'] = 'Attendance grade';
$string['attendancenotset'] = 'You must set your attendance';
$string['attendancenotstarted'] = 'Attendance has not started yet for this course';
$string['attendancepercent'] = 'Attendance percent';
$string['attendancereport'] = 'Attendance report';
$string['attendancesuccess'] = 'Attendance has been successfully taken';
$string['attendanceupdated'] = 'Attendance successfully updated';
$string['attforblockdirstillexists'] = 'old mod/attforblock directory still exists - you must delete this directory on your server before running this upgrade.';
$string['attrecords'] = 'Attendances records';
$string['automark'] = 'Automatic marking';
$string['automarkall'] = 'Yes';
$string['automarkclose'] = 'Set unmarked at end of session';
$string['automark_help'] = 'Allows marking to be completed automatically.
If "Yes" students will be automatically marked depending on their first access to the course.
If "Set unmarked at end of session" any students who have not marked their attendance will be set to the unmarked status selected.';
$string['automarktask'] = 'Check for attendance sessions that require auto marking';
$string['autorecorded'] = 'system auto recorded';
$string['averageattendance'] = 'Average attendance';
$string['averageattendancegraded'] = 'Average attendance';
$string['calclose'] = 'Close';
$string['caleventcreated'] = 'Calendar event for session successfully created';
$string['caleventdeleted'] = 'Calendar event for session successfully deleted';
@ -67,6 +83,8 @@ $string['calshow'] = 'Choose date';
$string['caltoday'] = 'Today';
$string['calweekdays'] = 'Su,Mo,Tu,We,Th,Fr,Sa';
$string['cannottakeforgroup'] = 'You can\'t take attendance for group "{$a}"';
$string['cantaddstatus'] = 'You must set an acronym and description when adding a new status.';
$string['categoryreport'] = 'Course category report';
$string['changeattendance'] = 'Change attendance';
$string['changeduration'] = 'Change duration';
$string['changesession'] = 'Change session';
@ -75,9 +93,14 @@ $string['column'] = 'column';
$string['columns'] = 'columns';
$string['commonsession'] = 'All students';
$string['commonsessions'] = 'All students';
$string['confirm'] = 'Confirm';
$string['confirmcolumnmappings'] = 'Confirm column mappings';
$string['confirmdeletehiddensessions'] = 'Are you sure you want to delete {$a->count} sessions scheduled before the course start date ({$a->date})?';
$string['confirmdeleteuser'] = 'Are you sure you want to delete user \'{$a->fullname}\' ({$a->email})?<br/>All of their attendance records will be permanently deleted.';
$string['countofselected'] = 'Count of selected';
$string['copyfrom'] = 'Copy attendance data from';
$string['countofselected'] = 'Count of selected';
$string['course'] = 'Course';
$string['coursesummary'] = 'Course summary report';
$string['createmultiplesessions'] = 'Create multiple sessions';
$string['createmultiplesessions_help'] = 'This function allows you to create multiple sessions in one simple step.
The sessions begin on the date of the base session and continue until the \'repeat until\' date.
@ -86,11 +109,30 @@ The sessions begin on the date of the base session and continue until the \'repe
* <strong>Repeat every</strong>: This allows for a frequency setting. If your class will meet every week, select 1; if it will meet every other week, select 2; every 3rd week, select 3, etc.
* <strong>Repeat until</strong>: Select the last day of class (the last day you want to take attendance).
';
$string['autoassignstatus'] = 'Automatically select highest status available';
$string['autoassignstatus_help'] = 'If this is selected, students will automatically be assigned the highest available grade.';
$string['createonesession'] = 'Create one session for the course';
$string['csvdelimiter'] = 'CSV delimiter';
$string['date'] = 'Date';
$string['days'] = 'Days';
$string['defaults'] = 'Defaults';
$string['defaultdisplaymode'] = 'Default display mode';
$string['defaultwarnings'] = 'Default warning set';
$string['defaultwarningsettings'] = 'Default warning settings';
$string['defaultwarningsettings_help'] = 'These settings define the defaults for all new warnings';
$string['defaults'] = 'Defaults';
$string['defaultsessionsettings'] = 'Default session settings';
$string['defaultsessionsettings_help'] = 'These settings define the defaults for all new sessions';
$string['defaultsettings'] = 'Default attendance settings';
$string['defaultsettings_help'] = 'These settings define the defaults for all new attendances';
$string['defaultstatus'] = 'Default status set';
$string['defaultsubnet'] = 'Default network address';
$string['defaultsubnet_help'] = 'Attendance recording may be restricted to particular subnets by specifying a comma-separated list of partial or full IP addresses. This is the default value used when creating new sessions.';
$string['defaultview'] = 'Default view on login';
$string['defaultview_desc'] = 'This is the default view shown to teachers on first login.';
$string['delete'] = 'Delete';
$string['deletewarningconfirm'] = 'Are you sure you want to delete this warning?';
$string['deletedgroup'] = 'The group associated with this session has been deleted';
$string['deletehiddensessions'] = 'Delete all hidden sessions';
$string['deletelogs'] = 'Delete attendance data';
$string['deleteselected'] = 'Delete selected';
$string['deletesession'] = 'Delete session';
@ -101,50 +143,119 @@ $string['deletingstatus'] = 'Deleting status for the course';
$string['description'] = 'Description';
$string['display'] = 'Display';
$string['displaymode'] = 'Display mode';
$string['donotusepaging'] = 'Do not use paging';
$string['downloadexcel'] = 'Download in Excel format';
$string['downloadooo'] = 'Download in OpenOffice format';
$string['downloadtext'] = 'Download in text format';
$string['donotusepaging'] = 'Do not use paging';
$string['duration'] = 'Duration';
$string['editsession'] = 'Edit Session';
$string['edituser'] = 'Edit user';
$string['emailcontent_default'] = 'Hi %userfirstname%,
Your attendance in %coursename% %attendancename% has dropped below %warningpercent% and is currently %percent% - we hope you are ok!
To get the most out of this course you should improve your attendance, please get in touch if you require any further support.';
$string['emailcontent'] = 'Email content';
$string['emailcontent_help'] = 'When a warning is sent to a student, it takes the email content from this field. The following wildcards can be used:
<ul>
<li>%coursename%</li>
<li>%userfirstname%</li>
<li>%userlastname%</li>
<li>%userid%</li>
<li>%warningpercent%</li>
<li>%attendancename%</li>
<li>%cmid%</li>
<li>%numtakensessions%</li>
<li>%points%</li>
<li>%maxpoints%</li>
<li>%percent%</li>
</ul>';
$string['emailsubject'] = 'Email subject';
$string['emailsubject_help'] = 'When a warning is sent to a student, it takes the email subject from this field.';
$string['emailsubject_default'] = 'Attendance warning';
$string['emailuser'] = 'Email user';
$string['emailuser_help'] = 'If checked, a warning will be sent to the student.';
$string['emptyacronym'] = 'Empty acronyms are not allowed. Status record not updated.';
$string['emptydescription'] = 'Empty descriptions are not allowed. Status record not updated.';
$string['edituser'] = 'Edit user';
$string['endtime'] = 'Session end time';
$string['enablecalendar'] = 'Create calendar events';
$string['enablecalendar_desc'] = 'If enabled, a calendar event will be created for each attendance session. After changing this setting you should run the reset calendar report.';
$string['enablewarnings'] = 'Enable warnings';
$string['enablewarnings_desc'] = 'This allows a warning set to be defined for an attendance and email notifications to users when attendance drops below the configured threshold. <br/><strong>WARNING: This is a new feature and has not been tested extensively. Please use at your own-risk and provide feeback in the moodle forums if you find it works well.</strong>';
$string['encoding'] = 'Encoding';
$string['endofperiod'] = 'End of period';
$string['endtime'] = 'Session end time';
$string['enrolmentend'] = 'User enrolment ends {$a}';
$string['enrolmentstart'] = 'User enrolment starts {$a}';
$string['enrolmentsuspended'] = 'Enrolment suspended';
$string['error:coursenotfound'] = 'A course with the short name {$a} can not be found.';
$string['error:coursehasnoattendance'] = 'The course with the short name {$a} has no attendance activities.';
$string['error:sessioncourseinvalid'] = 'A session course is invalid! Skipping.';
$string['error:sessiondateinvalid'] = 'A session date is invalid! Skipping.';
$string['error:sessionendinvalid'] = 'A session end time is invalid! Skipping.';
$string['error:sessionstartinvalid'] = 'A session start time is invalid! Skipping.';
$string['errorgroupsnotselected'] = 'Select one or more groups';
$string['errorinaddingsession'] = 'Error in adding session';
$string['erroringeneratingsessions'] = 'Error in generating sessions ';
$string['eventdurationupdated'] = 'Session duration updated';
$string['eventreportviewed'] = 'Attendance report viewed';
$string['eventscreated'] = 'Calendar events created';
$string['eventsdeleted'] = 'Calendar events deleted';
$string['eventsessionadded'] = 'Session added';
$string['eventsessiondeleted'] = 'Session deleted';
$string['eventsessionsimported'] = 'Sessions imported';
$string['eventsessionipshared'] = 'Attendance self-marking IP conflict';
$string['eventsessionupdated'] = 'Session updated';
$string['eventstatusadded'] = 'Status added';
$string['eventstatusupdated'] = 'Status updated';
$string['eventtaken'] = 'Attendance taken';
$string['eventtakenbystudent'] = 'Attendance taken by student';
$string['export'] = 'Export';
$string['extrarestrictions'] = 'Extra restrictions';
$string['from'] = 'from:';
$string['gradebookexplanation'] = 'Grade in gradebook';
$string['gradebookexplanation_help'] = 'The Attendance module displays your current attendance grade based on the number of points you have earned to date and the number of points that could have been earned to date; it does not include class periods in the future. In the gradebook, your attendance grade is based on your current attendance percentage and the number of points that can be earned over the entire duration of the course, including future class periods. As such, your attendance grades displayed in the Attendance module and in the gradebook may not be the same number of points but they are the same percentage.
For example, if you have earned 8 of 10 points to date (80% attendance) and attendance for the entire course is worth 50 points, the Attendance module will display 8/10 and the gradebook will display 40/50. You have not yet earned 40 points but 40 is the equivalent point value to your current attendance percentage of 80%. The point value you have earned in the Attendance module can never decrease, as it is based only on attendance to date; however, the attendance point value shown in the gradebook may increase or decrease depending on your future attendance, as it is based on attendance for the entire course.';
$string['gridcolumns'] = 'Grid columns';
$string['group'] = 'Group';
$string['groups'] = 'Groups';
$string['groupsession'] = 'Group of students';
$string['hiddensessions'] = 'Hidden sessions';
$string['hiddensessions_help'] = 'Sessions are hidden if they are scheduled before the course start date.
You can use this feature to hide older sessions instead of deleting them. Only visible sessions will appear in the Gradebook.';
$string['hiddensessionsdeleted'] = 'All hidden sessions were delete';
$string['hideextrauserdetails'] = 'Hide extra user details';
$string['hidensessiondetails'] = 'Hide session details';
$string['import'] = 'Import';
$string['importfile'] = 'Import file';
$string['importfile_help'] = 'Import file';
$string['importsessions'] = 'Import Sessions';
$string['identifyby'] = 'Identify student by';
$string['includeall'] = 'Select all sessions';
$string['includenottaken'] = 'Include not taken sessions';
$string['includeremarks'] = 'Include remarks';
$string['incorrectpassword'] = 'You have entered an incorrect password and your attendance has not been recorded, please enter the correct password.';
$string['indetail'] = 'In detail...';
$string['invalidsessionenddate'] = 'This date can not be earlier than the session date';
$string['invalidaction'] = 'You must select an action';
$string['invalidemails'] = 'You must specify addresses of existing user accounts, could not find: {$a}';
$string['invalidimportfile'] = 'File format is invalid.';
$string['invalidsessionenddate'] = 'This date can not be earlier than the session date';
$string['invalidsessionendtime'] = 'The end time must be greater than start time';
$string['invalidstatus'] = 'You have selected an invalid status, please try again';
$string['iptimemissing'] = 'Invalid minutes to release';
$string['jumpto'] = 'Jump to';
$string['lowgrade'] = 'Low grade';
$string['maxpossible'] = 'Maximum possible';
$string['maxpossible_help'] = 'Shows the score each user can reach if they receive the maximum points in each session not yet taken (past and future):
<ul>
<li><strong>Points</strong>: maximum points each user can reach over all sessions.</li>
<li><strong>Percentage</strong>: maximum percentage each user can reach over all sessions.</li>
</ul>';
$string['maxpossiblepoints'] = 'Maximum possible points';
$string['maxpossiblepercentage'] = 'Maximum possible percentage';
$string['maxpossiblepoints'] = 'Maximum possible points';
$string['maxwarn'] = 'Maximum number of e-mail warnings';
$string['maxwarn_help'] = 'The maximum number of times a warning should be sent (only one warning per session is sent)';
$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.
@ -157,15 +268,20 @@ $string['months'] = 'Months';
$string['moreattendance'] = 'Attendance has been successfully taken for this page';
$string['moveleft'] = 'Move left';
$string['moveright'] = 'Move right';
$string['multisessionexpanded'] = 'Multiple sessions expanded';
$string['multisessionexpanded_desc'] = 'Show the "Multiple sessions" settings as expanded by default when creating new sessions.';
$string['mustselectusers'] = 'Must select users to export';
$string['myvariables'] = 'My Variables';
$string['newdate'] = 'New date';
$string['newduration'] = 'New duration';
$string['newstatusset'] = 'New set of statuses';
$string['noabsentstatusset'] = 'The status set in use does not have a status to use when not marked.';
$string['noattendanceusers'] = 'It is not possible to export any data as there are no students enrolled in the course.';
$string['noautomark'] = 'Disabled';
$string['noattforuser'] = 'No attendance records exist for the user';
$string['nodescription'] = 'Regular class session';
$string['noguest'] = 'Guest can\'t see attendance';
$string['noeventstoreset'] = 'There are no calendar events that require an update.';
$string['nogroups'] = 'You can\'t add group sessions. No groups exists in course.';
$string['noguest'] = 'Guest can\'t see attendance';
$string['noofdaysabsent'] = 'No of days absent';
$string['noofdaysexcused'] = 'No of days excused';
$string['noofdayslate'] = 'No of days late';
@ -173,18 +289,61 @@ $string['noofdayspresent'] = 'No of days present';
$string['nosessiondayselected'] = 'No Session day selected';
$string['nosessionexists'] = 'No Session exists for this course';
$string['nosessionsselected'] = 'No sessions selected';
$string['warningdeleted'] = 'Warning deleted';
$string['warningdesc'] = 'These warnings will be automatically added to any new attendance activities. If more than one warning is triggered at exactly the same time, only the warning with the lower warning threshold will be sent.';
$string['warningdesc_course'] = 'Warnings thresholds set here affect the absentee report and allow students and third parties to be notified. If more than one warning is triggered at exactly the same time, only the warning with the lower warning threshold will be sent.';
$string['warnings'] = 'Warnings set';
$string['warningupdated'] = 'Updated warnings';
$string['notifytask'] = 'Send warnings to users';
$string['notfound'] = 'Attendance activity not found in this course!';
$string['notmember'] = 'not&nbsp;member';
$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['numsessions'] = 'Number of sessions';
$string['olddate'] = 'Old date';
$string['onlyselectedusers'] = 'Export specific users';
$string['overallsessions'] = 'Over all sessions';
$string['overallsessions_help'] = 'Shows statistics for all sessions including those not yet taken (past and future):
<ul>
<li><strong>Sessions</strong>: total number of sessions.</li>
<li><strong>Points</strong>: points awarded based on the taken sessions.</li>
<li><strong>Percentage</strong>: percentage of points awarded over the maxium possible points for all sessions.</li>
</ul>';
$string['oversessionstaken'] = 'Over taken sessions';
$string['oversessionstaken_help'] = 'Shows statistics for sessions where attendance has been taken:
<ul>
<li><strong>Sessions</strong>: number of already taken sessions.</li>
<li><strong>Points</strong>: points awarded based on the taken sessions.</li>
<li><strong>Percentage</strong>: percentage of points awarded over the maxium possible points of the taken sessions.</li>
</ul>';
$string['participant'] = 'Participant';
$string['password'] = 'Password';
$string['passwordgrp'] = 'Student password';
$string['passwordgrp_help'] = 'If set students will be required to enter this password before they can set their own attendance status for the session. If empty, no password is required.';
$string['passwordrequired'] = 'You must enter the session password before you can submit your attendance';
$string['percentage'] = 'Percentage';
$string['percentagesessionscompleted'] = 'Percentage over taken sessions';
$string['percentageallsessions'] = 'Percentage over all sessions';
$string['pluginname'] = 'Attendance';
$string['percentagesessionscompleted'] = 'Percentage over taken sessions';
$string['pluginadministration'] = 'Attendance administration';
$string['pluginname'] = 'Attendance';
$string['points'] = 'Points';
$string['pointsallsessions'] = 'Points over all sessions';
$string['pointssessionscompleted'] = 'Points over taken sessions';
$string['preferences_desc'] = 'Changes to status sets will affect existing attendance sessions and may affect grading.';
$string['preventsharedip'] = 'Prevent students sharing IP address';
$string['preventsharedip_help'] = 'Prevent students from using the same device (identified using IP address) to take attendance for other students.';
$string['preventsharediptime'] = 'Time to allow re-use of IP address (minutes)';
$string['preventsharediptime_help'] = 'Allow an IP address to be re-used for taking attendance in this session after this time has elapsed.';
$string['preventsharedipminutes'] = '(minutes to release IP)';
$string['preventsharederror'] = 'Self-marking has been disabled for a session because this device appears to have been used to record attendance for another student.';
$string['priorto'] = 'The session date is prior to the course start date ({$a}) so that the new sessions scheduled before this date will be hidden (not accessible). You can change the course start date at any time (see course settings) in order to have access to earlier sessions.<br><br>Please change the session date or just click the "Add session" button again to confirm?';
$string['processingfile'] = 'Processing file';
$string['randompassword'] = 'Random password';
$string['remark'] = 'Remark for: {$a}';
$string['remarks'] = 'Remarks';
$string['repeatasfollows'] = 'Repeat the session above as follows';
$string['repeatevery'] = 'Repeat every';
$string['repeaton'] = 'Repeat on';
$string['repeatuntil'] = 'Repeat until';
$string['report'] = 'Report';
$string['required'] = 'Required*';
$string['requiredentries'] = ' Temporary records overwrite participant attendance records';
@ -222,6 +381,11 @@ $string['requiredentry_help'] = '<p align="center"><b>Attendance</b></p>
</p>
<p align="left"><strong>Temporay user will be deleted in all cases after merge action</strong></p>';
$string['requiresubnet'] = 'Require network address';
$string['requiresubnet_help'] = 'Attendance recording may be restricted to particular subnets by specifying a comma-separated list of partial or full IP addresses.';
$string['resetcalendar'] = 'Reset calendar';
$string['resetcaledarcreate'] = 'Calendar events have been enabled but a number of existing sessions do not have events. Do you want to create calendar events for all existing sessions?';
$string['resetcaledardelete'] = 'Calendar events have been disabled but a number of existing sessions have events that should be deleted. Do you want to delete all existing events?';
$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';
@ -236,130 +400,123 @@ $string['sessionalreadyexists'] = 'Session already exists for this date';
$string['sessiondate'] = 'Date';
$string['sessiondays'] = 'Session Days';
$string['sessiondeleted'] = 'Session successfully deleted';
$string['sessionduplicate'] = 'A duplicate session exists for course: {$a->course} in attendance: {$a->activity}';
$string['sessionexist'] = 'Session not added (already exists)!';
$string['sessiongenerated'] = 'One session was successfully generated';
$string['sessionunknowngroup'] = 'A session specifies unknown group(s): {$a}';
$string['sessions'] = 'Sessions';
$string['sessionscompleted'] = 'Taken sessions';
$string['sessionstotal'] = 'Total number of sessions';
$string['sessionsids'] = 'IDs of sessions: ';
$string['sessiongenerated'] = 'One session was successfully generated';
$string['sessionsgenerated'] = '{$a} sessions were successfully generated';
$string['sessionsids'] = 'IDs of sessions: ';
$string['sessionsnotfound'] = 'There is no sessions in the selected timespan';
$string['sessionstartdate'] = 'Session start date';
$string['sessiontype'] = 'Type';
$string['sessionstotal'] = 'Total number of sessions';
$string['sessiontype_help'] = 'You can add sessions for all students or for a group of students. Ability to add different types depends on activity group mode.
* In group mode "No groups" you can add only sessions for all students.
* In group mode "Separate groups" you can add only sessions for a group of students.
* In group mode "Visible groups" you can add both types of sessions.
';
$string['sessiontype'] = 'Type';
$string['sessiontypeshort'] = 'Type';
$string['sessionupdated'] = 'Session successfully updated';
$string['set_by_student'] = 'Self-recorded';
$string['setallstatuses'] = 'Set status for all users';
$string['setallstatusesto'] = 'Set status for all users to «{$a}»';
$string['settings'] = 'Settings';
$string['setunmarked'] = 'Automatically set when not marked';
$string['setunmarked_help'] = 'If enabled in the session, set this status if a student has not marked their own attendance.';
$string['showdefaults'] = 'Show defaults';
$string['showduration'] = 'Show duration';
$string['showextrauserdetails'] = 'Show extra user details';
$string['showsessiondetails'] = 'Show session details';
$string['showsessiondescriptiononreport'] = 'Show session description in report';
$string['showsessiondescriptiononreport_desc'] = 'Show the session description in the attendance report listing.';
$string['somedisabledstatus'] = '(Some options have been removed as the session has started.)';
$string['sortedgrid'] = 'Sorted grid';
$string['sortedlist'] = 'Sorted list';
$string['startofperiod'] = 'Start of period';
$string['starttime'] = 'Start time';
$string['status'] = 'Status';
$string['statuses'] = 'Statuses';
$string['statusdeleted'] = 'Status deleted';
$string['statuses'] = 'Statuses';
$string['statusset'] = 'Status set {$a}';
$string['strftimedm'] = '%h %d';
$string['strftimedmy'] = '%d %h %Y';
$string['strftimedmyhm'] = '%d %h %Y %I.%M%P'; // Line added to allow multiple sessions in the same day.
$string['strftimedmyw'] = '<nobr>%a %d %h %Y</nobr>';
$string['strftimeh'] = '%l%P';
$string['strftimehm'] = '%l:%M%P';
$string['statussetsettings'] = 'Status set';
$string['strftimedm'] = '%b %d';
$string['strftimedmy'] = '%d %b %Y';
$string['strftimedmyhm'] = '%d %b %Y %I.%M%p'; // Line added to allow multiple sessions in the same day.
$string['strftimedmyw'] = '<nobr>%a %d %b %Y</nobr>';
$string['strftimeh'] = '%I%p';
$string['strftimehm'] = '%I:%M%p';
$string['strftimeshortdate'] = '%d.%m.%Y';
$string['studentavailability'] = 'Available for students (minutes)';
$string['studentavailability_help'] = 'When students are marking their own attendance, the number of minutes after session starts that this status is available.
<br/>If empty, this status will always be available, If set to 0 it will always be hidden to students.';
$string['studentid'] = 'Student ID';
$string['studentmarking'] = 'Student recording';
$string['studentpassword'] = 'Student password';
$string['studentrecordingexpanded'] = 'Student recording expanded';
$string['studentrecordingexpanded_desc'] = 'Show the "Student recording" settings as expanded by default when creating new sessions.';
$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['studentscanmarksessiontime'] = 'Students record attendance during session time';
$string['studentscanmarksessiontime_desc'] = 'If checked students can only record their attendance during the session.';
$string['studentscanmarksessiontimeend'] = 'Session end (minutes)';
$string['studentscanmarksessiontimeend_desc'] = 'If the session does not have an end time, how many minutes should the session be available for students to record their attendance.';
$string['submitattendance'] = 'Submit attendance';
$string['subnet'] = 'Subnet';
$string['subnetactivitylevel'] = 'Allow subnet config at activity level';
$string['subnetactivitylevel_desc'] = 'If enabled, teachers can override the default subnet at the activity level when creating an attendance. Otherwise the site default will be used when creating a session.';
$string['subnetwrong'] = 'Attendance can only be recorded from certain locations, and this computer is not on the allowed list.';
$string['summary'] = 'Summary';
$string['tablerenamefailed'] = 'Rename of old attforblock table to attendance failed';
$string['tactions'] = 'Action';
$string['takeattendance'] = 'Take attendance';
$string['takensessions'] = 'Taken sessions';
$string['tcreated'] = 'Created';
$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['tempusermerge'] = 'Merge temporary user';
$string['tempusers'] = 'Temporary users';
$string['tempusersedit'] = 'Edit temporary user';
$string['tempuserslist'] = 'Temporary users';
$string['tempusermerge'] = 'Merge temporary user';
$string['thirdpartyemailsubject'] = 'Attendance warning';
$string['thirdpartyemailtext'] = '{$a->firstname} {$a->lastname} attendance within {$a->coursename} {$a->aname} is lower than {$a->warningpercent} ({$a->percent})';
$string['thirdpartyemailtextfooter'] = 'You are receiving this because the teacher of this course has added your email to the recipient’s list';
$string['thirdpartyemails'] = 'Notify other users';
$string['thirdpartyemails_help'] = 'List of other users who will be notified. (requires the capability mod/attendance:viewreports)';
$string['thiscourse'] = 'This course';
$string['time'] = 'Time';
$string['timeahead'] = 'Multiple sessions that exceed one year cannot be created, please adjust the start and end dates.';
$string['to'] = 'to:';
$string['triggered'] = 'First notified';
$string['tuseremail'] = 'Email';
$string['tusername'] = 'Full name';
$string['graded'] = 'Graded sessions';
$string['ungraded'] = 'Ungraded sessions';
$string['unknowngroup'] = 'Unknown group';
$string['update'] = 'Update';
$string['usestatusset'] = 'Status set';
$string['usedefaultsubnet'] = 'Use default';
$string['userexists'] = 'There is already a real user with this email address';
$string['users'] = 'Users to export';
$string['usestatusset'] = 'Status set';
$string['variable'] = 'variable';
$string['variablesupdated'] = 'Variables successfully updated';
$string['versionforprinting'] = 'version for printing';
$string['viewmode'] = 'View mode';
$string['warnafter'] = 'Number of sessions taken before warning';
$string['warnafter_help'] = 'Warnings will only be triggered when the user has had their attendance taken for at least this number of sessions.';
$string['warningfailed'] = 'You cannot create a warning that uses the same percentage and number of sessions.';
$string['warningpercent'] = 'Warn if percentage falls under';
$string['warningpercent_help'] = 'A warning will be triggered when the overall percentage falls below this number.';
$string['warningthreshold'] = 'Warning threshold';
$string['week'] = 'week(s)';
$string['weeks'] = 'Weeks';
$string['youcantdo'] = 'You can\'t do anything';
$string['eventreportviewed'] = 'Attendance report viewed';
$string['eventsessionadded'] = 'Session added';
$string['eventsessionupdated'] = 'Session updated';
$string['eventtaken'] = 'Attendance taken';
$string['eventtakenbystudent'] = 'Attendance taken by student';
$string['eventsessiondeleted'] = 'Session deleted';
$string['eventdurationupdated'] = 'Session duration updated';
$string['eventstatusupdated'] = 'Status updated';
$string['eventstatusadded'] = 'Status added';
$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['set_by_student'] = 'Self-recorded';
$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';
$string['lowgrade'] = 'Low grade';
$string['submitattendance'] = 'Submit attendance';
$string['attendancenotset'] = 'You must set your attendance';
$string['export'] = 'Export';
$string['points'] = 'Points';
$string['oversessionstaken'] = 'Over taken sessions';
$string['oversessionstaken_help'] = 'Shows statistics for sessions where attendance has been taken:
<ul>
<li><strong>Sessions</strong>: number of already taken sessions.</li>
<li><strong>Points</strong>: points awarded based on the taken sessions.</li>
<li><strong>Percentage</strong>: percentage of points awarded over the maxium possible points of the taken sessions.</li>
</ul>';
$string['overallsessions'] = 'Over all sessions';
$string['overallsessions_help'] = 'Shows statistics for all sessions including those not yet taken (past and future):
<ul>
<li><strong>Sessions</strong>: total number of sessions.</li>
<li><strong>Points</strong>: points awarded based on the taken sessions.</li>
<li><strong>Percentage</strong>: percentage of points awarded over the maxium possible points for all sessions.</li>
</ul>';
$string['pointssessionscompleted'] = 'Points over taken sessions';
$string['pointsallsessions'] = 'Points over all sessions';
$string['unknowngroup'] = 'Unknown group';
$string['notmember'] = 'not&nbsp;member';
$string['deletehiddensessions'] = 'Delete all hidden sessions';
$string['confirmdeletehiddensessions'] = 'Are you sure you want to delete {$a->count} sessions scheduled before the course start date ({$a->date})?';
$string['hiddensessionsdeleted'] = 'All hidden sessions were delete';
$string['timeahead'] = 'Multiple sessions that exceed one year cannot be created, please adjust the start and end dates.';
$string['priorto'] = 'The session date is prior to the course start date ({$a}) so that the new sessions scheduled before this date will be hidden (not accessible). You can change the course start date at any time (see course settings) in order to have access to earlier sessions.<br><br>Please change the session date or just click the "Add session" button again to confirm?';
$string['noattendanceusers'] = 'It is not possible to export any data as there are no students enrolled in the course.';
$string['time'] = 'Time';
$string['from'] = 'from:';
$string['to'] = 'to:';
$string['repeatasfollows'] = 'Repeat the session above as follows';
$string['repeatevery'] = 'Repeat every';
$string['repeatuntil'] = 'Repeat until';
$string['repeaton'] = 'Repeat on';
$string['invalidsessionendtime'] = 'The end time must be greater than start time';
$string['deletedgroup'] = 'The group associated with this session has been deleted';
$string['extrarestrictions'] = 'Extra restrictions';
$string['requiresubnet'] = 'Students can only record own attendance from these computers.';
$string['subnetwrong'] = 'Attendance can only be recorded from certain locations, and this computer is not on the allowed list.';
$string['requiresubnet_help'] = 'Attendance recording may be restricted to particular subnets by specifying a comma-separated list of partial or full IP addresses.';
$string['includeabsentee'] = 'Include session when calculating absentee report';
$string['includeabsentee_help'] = 'If checked this session will be included in the absentee report calculations.';
$string['attendance_no_status'] = 'No valid status was available - you may be too late to record attendance.';
$string['studentmarked'] = 'Your attendance in this session has been recorded.';

159
lib.php

@ -40,10 +40,8 @@ function attendance_supports($feature) {
return true;
case FEATURE_GROUPINGS:
return true;
case FEATURE_GROUPMEMBERSONLY:
return true;
case FEATURE_MOD_INTRO:
return false;
return true;
case FEATURE_BACKUP_MOODLE2:
return true;
// Artem Andreev: AFAIK it's not tested.
@ -54,6 +52,11 @@ function attendance_supports($feature) {
}
}
/**
* Add default set of statuses to the new attendance.
*
* @param int $attid - id of attendance instance.
*/
function att_add_default_statuses($attid) {
global $DB;
@ -66,6 +69,31 @@ function att_add_default_statuses($attid) {
$statuses->close();
}
/**
* Add default set of warnings to the new attendance.
*
* @param int $id - id of attendance instance.
*/
function attendance_add_default_warnings($id) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$warnings = $DB->get_recordset('attendance_warning',
array('idnumber' => 0), 'id');
foreach ($warnings as $n) {
$rec = $n;
$rec->idnumber = $id;
$DB->insert_record('attendance_warning', $rec);
}
$warnings->close();
}
/**
* Add new attendance instance.
*
* @param stdClass $attendance
* @return bool|int
*/
function attendance_add_instance($attendance) {
global $DB;
@ -75,12 +103,19 @@ function attendance_add_instance($attendance) {
att_add_default_statuses($attendance->id);
attendance_add_default_warnings($attendance->id);
attendance_grade_item_update($attendance);
return $attendance->id;
}
/**
* Update existing attendance instance.
*
* @param stdClass $attendance
* @return bool
*/
function attendance_update_instance($attendance) {
global $DB;
@ -96,9 +131,15 @@ function attendance_update_instance($attendance) {
return true;
}
/**
* Delete existing attendance
*
* @param int $id
* @return bool
*/
function attendance_delete_instance($id) {
global $DB;
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
if (! $attendance = $DB->get_record('attendance', array('id' => $id))) {
return false;
@ -113,6 +154,8 @@ function attendance_delete_instance($id) {
}
$DB->delete_records('attendance_statuses', array('attendanceid' => $id));
$DB->delete_records('attendance_warning', array('idnumber' => $id));
$DB->delete_records('attendance', array('id' => $id));
attendance_grade_item_delete($attendance);
@ -120,29 +163,9 @@ function attendance_delete_instance($id) {
return true;
}
function attendance_delete_course($course, $feedback=true) {
global $DB;
$attids = array_keys($DB->get_records('attendance', array('course' => $course->id), '', 'id'));
$sessids = array_keys($DB->get_records_list('attendance_sessions', 'attendanceid', $attids, '', 'id'));
if (attendance_existing_calendar_events_ids($sessids)) {
attendance_delete_calendar_events($sessids);
}
if ($sessids) {
$DB->delete_records_list('attendance_log', 'sessionid', $sessids);
}
if ($attids) {
$DB->delete_records_list('attendance_statuses', 'attendanceid', $attids);
$DB->delete_records_list('attendance_sessions', 'attendanceid', $attids);
}
$DB->delete_records('attendance', array('course' => $course->id));
return true;
}
/**
* Called by course/reset.php
* @param $mform form passed by reference
* @param moodleform $mform form passed by reference
*/
function attendance_reset_course_form_definition(&$mform) {
$mform->addElement('header', 'attendanceheader', get_string('modulename', 'attendance'));
@ -161,11 +184,20 @@ function attendance_reset_course_form_definition(&$mform) {
/**
* Course reset form defaults.
*
* @param stdClass $course
* @return array
*/
function attendance_reset_course_form_defaults($course) {
return array('reset_attendance_log' => 0, 'reset_attendance_statuses' => 0, 'reset_attendance_sessions' => 0);
}
/**
* Reset user data within attendance.
*
* @param stdClass $data
* @return array
*/
function attendance_reset_userdata($data) {
global $DB;
@ -180,6 +212,10 @@ function attendance_reset_userdata($data) {
$DB->delete_records_select('attendance_log', "sessionid $sql", $params);
list($sql, $params) = $DB->get_in_or_equal($attids);
$DB->set_field_select('attendance_sessions', 'lasttaken', 0, "attendanceid $sql", $params);
if (empty($data->reset_attendance_sessions)) {
// If sessions are being retained, clear automarkcompleted value.
$DB->set_field_select('attendance_sessions', 'automarkcompleted', 0, "attendanceid $sql", $params);
}
$status[] = array(
'component' => get_string('modulenameplural', 'attendance'),
@ -218,12 +254,18 @@ function attendance_reset_userdata($data) {
return $status;
}
/*
/**
* Return a small object with summary information about what a
* user has done with a given particular instance of this module
* Used for user activity reports.
* $return->time = the time they did it
* $return->info = a short text description
*
* @param stdClass $course - full course record.
* @param stdClass $user - full user record
* @param stdClass $mod
* @param stdClass $attendance
* @return stdClass.
*/
function attendance_user_outline($course, $user, $mod, $attendance) {
global $CFG;
@ -249,10 +291,14 @@ function attendance_user_outline($course, $user, $mod, $attendance) {
return $result;
}
/*
/**
* Print a detailed representation of what a user has done with
* a given particular instance of this module, for user activity reports.
*
* @param stdClass $course
* @param stdClass $user
* @param stdClass $mod
* @param stdClass $attendance
*/
function attendance_user_complete($course, $user, $mod, $attendance) {
global $CFG;
@ -265,14 +311,21 @@ function attendance_user_complete($course, $user, $mod, $attendance) {
}
}
/**
* Dummy function - must exist to allow quick editing of module name.
*
* @param stdClass $attendance
* @param int $userid
* @param bool $nullifnone
*/
function attendance_update_grades($attendance, $userid=0, $nullifnone=true) {
// We need this function to exist so that quick editing of module name is passed to gradebook.
}
/**
* Create grade item for given attendance
*
* @param object $attendance object with extra cmidnumber
* @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
* @param stdClass $attendance object with extra cmidnumber
* @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
* @return int 0 if ok, error code otherwise
*/
function attendance_grade_item_update($attendance, $grades=null) {
@ -295,7 +348,7 @@ function attendance_grade_item_update($attendance, $grades=null) {
$params = array('itemname' => $attendance->name, 'idnumber' => $attendance->cmidnumber);
} else {
// MDL-14303.
$params = array('itemname' => $attendance->name/*, 'idnumber'=>$attendance->id*/);
$params = array('itemname' => $attendance->name);
}
if ($attendance->grade > 0) {
@ -405,3 +458,45 @@ function attendance_pluginfile($course, $cm, $context, $filearea, $args, $forced
}
send_stored_file($file, 0, 0, true);
}
/**
* Print tabs on attendance settings page.
*
* @param string $selected - current selected tab.
*/
function attendance_print_settings_tabs($selected = 'settings') {
global $CFG;
// Print tabs for different settings pages.
$tabs = array();
$tabs[] = new tabobject('settings', $CFG->wwwroot.'/admin/settings.php?section=modsettingattendance',
get_string('settings', 'attendance'), get_string('settings'), false);
$tabs[] = new tabobject('defaultstatus', $CFG->wwwroot.'/mod/attendance/defaultstatus.php',
get_string('defaultstatus', 'attendance'), get_string('defaultstatus', 'attendance'), false);
if (get_config('attendance', 'enablewarnings')) {
$tabs[] = new tabobject('defaultwarnings', $CFG->wwwroot . '/mod/attendance/warnings.php',
get_string('defaultwarnings', 'attendance'), get_string('defaultwarnings', 'attendance'), false);
}
$tabs[] = new tabobject('coursesummary', $CFG->wwwroot.'/mod/attendance/coursesummary.php',
get_string('coursesummary', 'attendance'), get_string('coursesummary', 'attendance'), false);
if (get_config('attendance', 'enablewarnings')) {
$tabs[] = new tabobject('absentee', $CFG->wwwroot . '/mod/attendance/absentee.php',
get_string('absenteereport', 'attendance'), get_string('absenteereport', 'attendance'), false);
}
$tabs[] = new tabobject('resetcalendar', $CFG->wwwroot.'/mod/attendance/resetcalendar.php',
get_string('resetcalendar', 'attendance'), get_string('resetcalendar', 'attendance'), false);
$tabs[] = new tabobject('importsessions', $CFG->wwwroot . '/mod/attendance/import/sessions.php',
get_string('importsessions', 'attendance'), get_string('importsessions', 'attendance'), false);
ob_start();
print_tabs(array($tabs), $selected);
$tabmenu = ob_get_contents();
ob_end_clean();
return $tabmenu;
}

698
locallib.php

@ -39,6 +39,21 @@ define('ATT_SORT_DEFAULT', 0);
define('ATT_SORT_LASTNAME', 1);
define('ATT_SORT_FIRSTNAME', 2);
define('ATTENDANCE_AUTOMARK_DISABLED', 0);
define('ATTENDANCE_AUTOMARK_ALL', 1);
define('ATTENDANCE_AUTOMARK_CLOSE', 2);
// Max number of sessions available in the warnings set form to trigger warnings.
define('ATTENDANCE_MAXWARNAFTER', 100);
/**
* Get statuses,
*
* @param int $attid
* @param bool $onlyvisible
* @param int $statusset
* @return array
*/
function attendance_get_statuses($attid, $onlyvisible=true, $statusset = -1) {
global $DB;
@ -80,7 +95,7 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) {
if ($statusesout) {
if (count($statusesout) > 6) {
$statusesout = array_slice($statusesout, 0, 6);
$statusesout[] = '&helip;';
$statusesout[] = '...';
}
$statusesout = implode(' ', $statusesout);
$statusname .= ' ('.$statusesout.')';
@ -90,6 +105,12 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) {
return $statusname;
}
/**
* Get users courses and the relevant attendances.
*
* @param int $userid
* @return array
*/
function attendance_get_user_courses_attendances($userid) {
global $DB;
@ -181,7 +202,7 @@ function attendance_get_max_statusset($attendanceid) {
/**
* Returns the maxpoints for each statusset
*
* @param array statuses
* @param array $statuses
* @return array
*/
function attendance_get_statusset_maxpoints($statuses) {
@ -197,7 +218,7 @@ function attendance_get_statusset_maxpoints($statuses) {
/**
* Update user grades
*
* @param mixed mod_attendance_structure|stdClass $attendance
* @param mod_attendance_structure|stdClass $attendance
* @param array $userids
*/
function attendance_update_users_grade($attendance, $userids=array()) {
@ -240,3 +261,674 @@ function attendance_update_users_grade($attendance, $userids=array()) {
return grade_update('mod/attendance', $course->id, 'mod', 'attendance', $attendance->id, 0, $grades);
}
/**
* Add an attendance status variable
*
* @param stdClass $status
* @return bool
*/
function attendance_add_status($status) {
global $DB;
if (empty($status->context)) {
$status->context = context_system::instance();
}
if (!empty($status->acronym) && !empty($status->description)) {
$status->deleted = 0;
$status->visible = 1;
$status->setunmarked = 0;
$id = $DB->insert_record('attendance_statuses', $status);
$status->id = $id;
$event = \mod_attendance\event\status_added::create(array(
'objectid' => $status->attendanceid,
'context' => $status->context,
'other' => array('acronym' => $status->acronym,
'description' => $status->description,
'grade' => $status->grade)));
if (!empty($status->cm)) {
$event->add_record_snapshot('course_modules', $status->cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
return true;
} else {
return false;
}
}
/**
* Remove a status variable from an attendance instance
*
* @param stdClass $status
* @param stdClass $context
* @param stdClass $cm
*/
function attendance_remove_status($status, $context = null, $cm = null) {
global $DB;
if (empty($context)) {
$context = context_system::instance();
}
$DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id));
$event = \mod_attendance\event\status_removed::create(array(
'objectid' => $status->id,
'context' => $context,
'other' => array(
'acronym' => $status->acronym,
'description' => $status->description
)));
if (!empty($cm)) {
$event->add_record_snapshot('course_modules', $cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
}
/**
* Update status variable for a particular Attendance module instance
*
* @param stdClass $status
* @param string $acronym
* @param string $description
* @param int $grade
* @param bool $visible
* @param stdClass $context
* @param stdClass $cm
* @param int $studentavailability
* @param bool $setunmarked
* @return array
*/
function attendance_update_status($status, $acronym, $description, $grade, $visible,
$context = null, $cm = null, $studentavailability = null, $setunmarked = false) {
global $DB;
if (empty($context)) {
$context = context_system::instance();
}
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();
if ($acronym) {
$status->acronym = $acronym;
$updated[] = $acronym;
}
if ($description) {
$status->description = $description;
$updated[] = $description;
}
if (isset($grade)) {
$status->grade = $grade;
$updated[] = $grade;
}
if (isset($studentavailability)) {
if (empty($studentavailability)) {
if ($studentavailability !== '0') {
$studentavailability = null;
}
}
$status->studentavailability = $studentavailability;
$updated[] = $studentavailability;
}
if ($setunmarked) {
$status->setunmarked = 1;
} else {
$status->setunmarked = 0;
}
$DB->update_record('attendance_statuses', $status);
$event = \mod_attendance\event\status_updated::create(array(
'objectid' => $status->attendanceid,
'context' => $context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade,
'updated' => implode(' ', $updated))));
if (!empty($cm)) {
$event->add_record_snapshot('course_modules', $cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
}
/**
* Similar to core random_string function but only lowercase letters.
* designed to make it relatively easy to provide a simple password in class.
*
* @param int $length The length of the string to be created.
* @return string
*/
function attendance_random_string($length=6) {
$randombytes = random_bytes_emulate($length);
$pool = 'abcdefghijklmnopqrstuvwxyz';
$pool .= '0123456789';
$poollen = strlen($pool);
$string = '';
for ($i = 0; $i < $length; $i++) {
$rand = ord($randombytes[$i]);
$string .= substr($pool, ($rand % ($poollen)), 1);
}
return $string;
}
/**
* Check to see if this session is open for student marking.
*
* @param stdclass $sess the session record from attendance_sessions.
* @return boolean
*/
function attendance_can_student_mark($sess) {
global $DB, $USER, $OUTPUT;
$canmark = false;
$attconfig = get_config('attendance');
if (!empty($attconfig->studentscanmark) && !empty($sess->studentscanmark)) {
if (empty($attconfig->studentscanmarksessiontime)) {
$canmark = true;
} else {
$duration = $sess->duration;
if (empty($duration)) {
$duration = $attconfig->studentscanmarksessiontimeend * 60;
}
if ($sess->sessdate < time() && time() < ($sess->sessdate + $duration)) {
$canmark = true;
}
}
}
// Check if another student has marked attendance from this IP address recently.
if ($canmark && !empty($sess->preventsharedip)) {
$time = time() - ($sess->preventsharediptime * 60);
$sql = 'sessionid = ? AND studentid <> ? AND timetaken > ? AND ipaddress = ?';
$params = array($sess->id, $USER->id, $time, getremoteaddr());
$record = $DB->get_record_select('attendance_log', $sql, $params);
if (!empty($record)) {
// Trigger an ip_shared event.
$attendanceid = $DB->get_field('attendance_sessions', 'attendanceid', array('id' => $record->sessionid));
$cm = get_coursemodule_from_instance('attendance', $attendanceid);
$event = \mod_attendance\event\session_ip_shared::create(array(
'objectid' => 0,
'context' => \context_module::instance($cm->id),
'other' => array(
'sessionid' => $record->sessionid,
'otheruser' => $record->studentid
)
));
$event->trigger();
echo $OUTPUT->notification(get_string('preventsharederror', 'attendance'));
return false;
}
}
return $canmark;
}
/**
* Generate worksheet for Attendance export
*
* @param stdclass $data The data for the report
* @param string $filename The name of the file
* @param string $format excel|ods
*
*/
function attendance_exporttotableed($data, $filename, $format) {
global $CFG;
if ($format === 'excel') {
require_once("$CFG->libdir/excellib.class.php");
$filename .= ".xls";
$workbook = new MoodleExcelWorkbook("-");
} else {
require_once("$CFG->libdir/odslib.class.php");
$filename .= ".ods";
$workbook = new MoodleODSWorkbook("-");
}
// Sending HTTP headers.
$workbook->send($filename);
// Creating the first worksheet.
$myxls = $workbook->add_worksheet('Attendances');
// Format types.
$formatbc = $workbook->add_format();
$formatbc->set_bold(1);
$myxls->write(0, 0, get_string('course'), $formatbc);
$myxls->write(0, 1, $data->course);
$myxls->write(1, 0, get_string('group'), $formatbc);
$myxls->write(1, 1, $data->group);
$i = 3;
$j = 0;
foreach ($data->tabhead as $cell) {
// Merge cells if the heading would be empty (remarks column).
if (empty($cell)) {
$myxls->merge_cells($i, $j - 1, $i, $j);
} else {
$myxls->write($i, $j, $cell, $formatbc);
}
$j++;
}
$i++;
$j = 0;
foreach ($data->table as $row) {
foreach ($row as $cell) {
$myxls->write($i, $j++, $cell);
}
$i++;
$j = 0;
}
$workbook->close();
}
/**
* Generate csv for Attendance export
*
* @param stdclass $data The data for the report
* @param string $filename The name of the file
*
*/
function attendance_exporttocsv($data, $filename) {
$filename .= ".txt";
header("Content-Type: application/download\n");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
header("Pragma: public");
echo get_string('course')."\t".$data->course."\n";
echo get_string('group')."\t".$data->group."\n\n";
echo implode("\t", $data->tabhead)."\n";
foreach ($data->table as $row) {
echo implode("\t", $row)."\n";
}
}
/**
* Get session data for form.
* @param stdClass $formdata moodleform - attendance form.
* @param mod_attendance_structure $att - used to get attendance level subnet.
* @return array.
*/
function attendance_construct_sessions_data_for_add($formdata, mod_attendance_structure $att) {
global $CFG;
$sesstarttime = $formdata->sestime['starthour'] * HOURSECS + $formdata->sestime['startminute'] * MINSECS;
$sesendtime = $formdata->sestime['endhour'] * HOURSECS + $formdata->sestime['endminute'] * MINSECS;
$sessiondate = $formdata->sessiondate + $sesstarttime;
$duration = $sesendtime - $sesstarttime;
if (empty(get_config('attendance', 'enablewarnings'))) {
$absenteereport = get_config('attendance', 'absenteereport_default');
} else {
$absenteereport = empty($formdata->absenteereport) ? 0 : 1;
}
$now = time();
if (empty(get_config('attendance', 'studentscanmark'))) {
$formdata->studentscanmark = 0;
}
$sessions = array();
if (isset($formdata->addmultiply)) {
$startdate = $sessiondate;
$enddate = $formdata->sessionenddate + DAYSECS; // Because enddate in 0:0am.
if ($enddate < $startdate) {
return null;
}
// Getting first day of week.
$sdate = $startdate;
$dinfo = usergetdate($sdate);
if ($CFG->calendar_startwday === '0') { // Week start from sunday.
$startweek = $startdate - $dinfo['wday'] * DAYSECS; // Call new variable.
} else {
$wday = $dinfo['wday'] === 0 ? 7 : $dinfo['wday'];
$startweek = $startdate - ($wday - 1) * DAYSECS;
}
$wdaydesc = array(0 => 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
while ($sdate < $enddate) {
if ($sdate < $startweek + WEEKSECS) {
$dinfo = usergetdate($sdate);
if (isset($formdata->sdays) && array_key_exists($wdaydesc[$dinfo['wday']], $formdata->sdays)) {
$sess = new stdClass();
$sess->sessdate = make_timestamp($dinfo['year'], $dinfo['mon'], $dinfo['mday'],
$formdata->sestime['starthour'], $formdata->sestime['startminute']);
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
$sess->absenteereport = $absenteereport;
$sess->studentpassword = '';
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
if (!empty($formdata->usedefaultsubnet)) {
$sess->subnet = $att->subnet;
} else {
$sess->subnet = $formdata->subnet;
}
$sess->automark = $formdata->automark;
if (isset($formdata->autoassignstatus)) {
$sess->autoassignstatus = 1;
}
$sess->automarkcompleted = 0;
if (!empty($formdata->randompassword)) {
$sess->studentpassword = attendance_random_string();
} else if (!empty($formdata->studentpassword)) {
$sess->studentpassword = $formdata->studentpassword;
}
if (!empty($formdata->preventsharedip)) {
$sess->preventsharedip = $formdata->preventsharedip;
}
if (!empty($formdata->preventsharediptime)) {
$sess->preventsharediptime = $formdata->preventsharediptime;
}
} else {
$sess->subnet = '';
$sess->automark = 0;
$sess->automarkcompleted = 0;
$sess->preventsharedip = 0;
$sess->preventsharediptime = '';
}
$sess->statusset = $formdata->statusset;
attendance_fill_groupid($formdata, $sessions, $sess);
}
$sdate += DAYSECS;
} else {
$startweek += WEEKSECS * $formdata->period;
$sdate = $startweek;
}
}
} else {
$sess = new stdClass();
$sess->sessdate = $sessiondate;
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
$sess->studentscanmark = 0;
$sess->autoassignstatus = 0;
$sess->subnet = '';
$sess->studentpassword = '';
$sess->automark = 0;
$sess->automarkcompleted = 0;
$sess->absenteereport = $absenteereport;
if (isset($formdata->studentscanmark) && !empty($formdata->studentscanmark)) {
// Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
if (isset($formdata->autoassignstatus) && !empty($formdata->autoassignstatus)) {
$sess->autoassignstatus = 1;
}
if (!empty($formdata->randompassword)) {
$sess->studentpassword = attendance_random_string();
} else if (!empty($formdata->studentpassword)) {
$sess->studentpassword = $formdata->studentpassword;
}
if (!empty($formdata->usedefaultsubnet)) {
$sess->subnet = $att->subnet;
} else {
$sess->subnet = $formdata->subnet;
}
if (!empty($formdata->automark)) {
$sess->automark = $formdata->automark;
}
if (!empty($formdata->preventsharedip)) {
$sess->preventsharedip = $formdata->preventsharedip;
}
if (!empty($formdata->preventsharediptime)) {
$sess->preventsharediptime = $formdata->preventsharediptime;
}
}
$sess->statusset = $formdata->statusset;
attendance_fill_groupid($formdata, $sessions, $sess);
}
return $sessions;
}
/**
* Helper function for attendance_construct_sessions_data_for_add().
*
* @param stdClass $formdata
* @param stdClass $sessions
* @param stdClass $sess
*/
function attendance_fill_groupid($formdata, &$sessions, $sess) {
if ($formdata->sessiontype == mod_attendance_structure::SESSION_COMMON) {
$sess = clone $sess;
$sess->groupid = 0;
$sessions[] = $sess;
} else {
foreach ($formdata->groups as $groupid) {
$sess = clone $sess;
$sess->groupid = $groupid;
$sessions[] = $sess;
}
}
}
/**
* Generates a summary of points for the courses selected.
*
* @param array $courseids optional list of courses to return
* @param string $orderby - optional order by param
* @return stdClass
*/
function attendance_course_users_points($courseids = array(), $orderby = '') {
global $DB;
$where = '';
$params = array();
$where .= ' AND ats.sessdate < :enddate ';
$params['enddate'] = time();
$joingroup = 'LEFT JOIN {groups_members} gm ON (gm.userid = atl.studentid AND gm.groupid = ats.groupid)';
$where .= ' AND (ats.groupid = 0 or gm.id is NOT NULL)';
if (!empty($courseids)) {
list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
$where .= ' AND c.id ' . $insql;
$params = array_merge($params, $inparams);
}
$sql = "SELECT courseid, coursename, sum(points) / sum(maxpoints) as percentage FROM (
SELECT a.id, a.course as courseid, c.fullname as coursename, atl.studentid AS userid, COUNT(DISTINCT ats.id) AS numtakensessions,
SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints
FROM {attendance_sessions} ats
JOIN {attendance} a ON a.id = ats.attendanceid
JOIN {course} c ON c.id = a.course
JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE deleted = 0
AND visible = 1
GROUP BY attendanceid, setnumber) stm
ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
{$joingroup}
WHERE ats.sessdate >= c.startdate
AND ats.lasttaken != 0
{$where}
GROUP BY a.id, a.course, c.fullname, atl.studentid
) p GROUP by courseid, coursename {$orderby}";
return $DB->get_records_sql($sql, $params);
}
/**
* Generates a list of users flagged absent.
*
* @param array $courseids optional list of courses to return
* @param string $orderby how to order results.
* @param bool $allfornotify get notification list for scheduled task.
* @return stdClass
*/
function attendance_get_users_to_notify($courseids = array(), $orderby = '', $allfornotify = false) {
global $DB;
$joingroup = 'LEFT JOIN {groups_members} gm ON (gm.userid = atl.studentid AND gm.groupid = ats.groupid)';
$where = ' AND (ats.groupid = 0 or gm.id is NOT NULL)';
$having = '';
$params = array();
if (!empty($courseids)) {
list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
$where .= ' AND c.id ' . $insql;
$params = array_merge($params, $inparams);
}
if ($allfornotify) {
// Exclude warnings that have already sent the max num.
$having .= ' AND n.maxwarn > COUNT(DISTINCT ns.id) ';
}
$unames = get_all_user_name_fields(true);
$unames2 = get_all_user_name_fields(true, 'u');
$idfield = $DB->sql_concat('cm.id', 'atl.studentid', 'n.id');
$sql = "SELECT {$idfield} as uniqueid, a.id as aid, {$unames2}, a.name as aname, cm.id as cmid, c.id as courseid,
c.fullname as coursename, atl.studentid AS userid, n.id as notifyid, n.warningpercent, n.emailsubject,
n.emailcontent, n.emailcontentformat, n.emailuser, n.thirdpartyemails, n.warnafter, n.maxwarn,
COUNT(DISTINCT ats.id) AS numtakensessions, SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints,
COUNT(DISTINCT ns.id) as nscount, MAX(ns.timesent) as timesent,
SUM(stg.grade) / SUM(stm.maxgrade) AS percent
FROM {attendance_sessions} ats
JOIN {attendance} a ON a.id = ats.attendanceid
JOIN {course_modules} cm ON cm.instance = a.id
JOIN {course} c on c.id = cm.course
JOIN {modules} md ON md.id = cm.module AND md.name = 'attendance'
JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
JOIN {user} u ON (u.id = atl.studentid)
JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
JOIN {attendance_warning} n ON n.idnumber = a.id
LEFT JOIN {attendance_warning_done} ns ON ns.notifyid = n.id AND ns.userid = atl.studentid
JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE deleted = 0
AND visible = 1
GROUP BY attendanceid, setnumber) stm
ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
{$joingroup}
WHERE ats.absenteereport = 1 {$where}
GROUP BY uniqueid, a.id, a.name, a.course, c.fullname, atl.studentid, n.id, n.warningpercent,
n.emailsubject, n.emailcontent, n.emailcontentformat, n.warnafter, n.maxwarn,
n.emailuser, n.thirdpartyemails, cm.id, c.id, {$unames2}, ns.userid
HAVING n.warnafter <= COUNT(DISTINCT ats.id) AND n.warningpercent > ((SUM(stg.grade) / SUM(stm.maxgrade)) * 100)
{$having}
{$orderby}";
if (!$allfornotify) {
$idfield = $DB->sql_concat('cmid', 'userid');
// Only show one record per attendance for teacher reports.
$sql = "SELECT DISTINCT {$idfield} as id, {$unames}, aid, cmid, courseid, aname, coursename, userid,
numtakensessions, percent, MAX(timesent) as timesent
FROM ({$sql}) as m
GROUP BY id, aid, cmid, courseid, aname, userid, numtakensessions,
percent, coursename, {$unames} {$orderby}";
}
return $DB->get_records_sql($sql, $params);
}
/**
* Template variables into place in supplied email content.
*
* @param object $record db record of details
* @return array - the content of the fields after templating.
*/
function attendance_template_variables($record) {
$templatevars = array(
'/%coursename%/' => $record->coursename,
'/%courseid%/' => $record->courseid,
'/%userfirstname%/' => $record->firstname,
'/%userlastname%/' => $record->lastname,
'/%userid%/' => $record->userid,
'/%warningpercent%/' => $record->warningpercent,
'/%attendancename%/' => $record->aname,
'/%cmid%/' => $record->cmid,
'/%numtakensessions%/' => $record->numtakensessions,
'/%points%/' => $record->points,
'/%maxpoints%/' => $record->maxpoints,
'/%percent%/' => $record->percent,
);
$extrauserfields = get_all_user_name_fields();
foreach ($extrauserfields as $extra) {
$templatevars['/%'.$extra.'%/'] = $record->$extra;
}
$patterns = array_keys($templatevars); // The placeholders which are to be replaced.
$replacements = array_values($templatevars); // The values which are to be templated in for the placeholders.
// Array to describe which fields in reengagement object should have a template replacement.
$replacementfields = array('emailsubject', 'emailcontent');
// Replace %variable% with relevant value everywhere it occurs in reengagement->field.
foreach ($replacementfields as $field) {
$record->$field = preg_replace($patterns, $replacements, $record->$field);
}
return $record;
}
/**
* Find highest available status for a user.
*
* @param mod_attendance_structure $att attendance structure
* @param stdclass $attforsession attendance_session record.
* @return bool/int
*/
function attendance_session_get_highest_status(mod_attendance_structure $att, $attforsession) {
// Find the status to set here.
$statuses = $att->get_statuses();
$highestavailablegrade = 0;
$highestavailablestatus = new stdClass();
foreach ($statuses as $status) {
if ($status->studentavailability === '0') {
// This status is never available to students.
continue;
}
if (!empty($status->studentavailability)) {
$toolateforstatus = (($attforsession->sessdate + ($status->studentavailability * 60)) < time());
if ($toolateforstatus) {
continue;
}
}
// This status is available to the student.
if ($status->grade > $highestavailablegrade) {
// This is the most favourable grade so far; save it.
$highestavailablegrade = $status->grade;
$highestavailablestatus = $status;
}
}
if (empty($highestavailablestatus)) {
return false;
}
return $highestavailablestatus->id;
}
/**
* Get available automark options.
*
* @return array
*/
function attendance_get_automarkoptions() {
$options = array();
$options[ATTENDANCE_AUTOMARK_DISABLED] = get_string('noautomark', 'attendance');
if (strpos(get_config('tool_log', 'enabled_stores'), 'logstore_standard') !== false) {
$options[ATTENDANCE_AUTOMARK_ALL] = get_string('automarkall', 'attendance');
}
$options[ATTENDANCE_AUTOMARK_CLOSE] = get_string('automarkclose', 'attendance');
return $options;
}

6
manage.php

@ -82,10 +82,14 @@ $tabs = new attendance_tabs($att, attendance_tabs::TAB_SESSIONS);
$filtercontrols = new attendance_filter_controls($att);
$sesstable = new attendance_manage_data($att);
$title = get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname);
$header = new mod_attendance_header($att, $title);
// Output starts here.
echo $output->header();
echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname));
echo $output->render($header);
mod_attendance_notifyqueue::show();
echo $output->render($tabs);
echo $output->render($filtercontrols);

17
mod_form.php

@ -52,17 +52,24 @@ class mod_attendance_mod_form extends moodleform_mod {
$mform->addRule('name', null, 'required', null, 'client');
$mform->setDefault('name', get_string('modulename', 'attendance'));
$this->standard_intro_elements();
// Grade settings.
$this->standard_grading_coursemodule_elements();
$this->standard_coursemodule_elements(true);
$mform->addElement('header', 'security', get_string('extrarestrictions', 'attendance'));
// IP address.
$mform->addElement('text', 'subnet', get_string('requiresubnet', 'attendance'), array('size' => '164'));
$mform->setType('subnet', PARAM_TEXT);
$mform->addHelpButton('subnet', 'requiresubnet', 'attendance');
$mform->setDefault('subnet', $attendanceconfig->subnet);
if (get_config('attendance', 'subnetactivitylevel')) {
$mform->addElement('header', 'security', get_string('extrarestrictions', 'attendance'));
$mform->addElement('text', 'subnet', get_string('defaultsubnet', 'attendance'), array('size' => '164'));
$mform->setType('subnet', PARAM_TEXT);
$mform->addHelpButton('subnet', 'defaultsubnet', 'attendance');
$mform->setDefault('subnet', $attendanceconfig->subnet);
} else {
$mform->addElement('hidden', 'subnet', '');
$mform->setType('subnet', PARAM_TEXT);
}
$this->add_action_buttons();
}

8
module.js

@ -1,6 +1,6 @@
M.mod_attendance = {};
M.mod_attendance = {}; // eslint-disable-line camelcase
M.mod_attendance.init_manage = function(Y) {
M.mod_attendance.init_manage = function(Y) { // eslint-disable-line camelcase
Y.on('click', function(e) {
if (e.target.get('checked')) {
@ -12,10 +12,10 @@ M.mod_attendance.init_manage = function(Y) {
this.set('checked', '');
});
}
}, '#cb_selector' );
}, '#cb_selector');
};
M.mod_attendance.set_preferences_action = function(action) {
M.mod_attendance.set_preferences_action = function(action) { // eslint-disable-line camelcase
var item = document.getElementById('preferencesaction');
if (item) {
item.setAttribute('value', action);

51
password.php

@ -0,0 +1,51 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Displays help via AJAX call or in a new page
*
* Use {@link core_renderer::help_icon()} or {@link addHelpButton()} to display
* the help icon.
*
* @copyright 2017 Dan Marsden
* @package mod_attendance
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__).'/../../config.php');
$session = required_param('session', PARAM_INT);
$session = $DB->get_record('attendance_sessions', array('id' => $session), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('attendance', $session->attendanceid);
require_login($cm->course, $cm);
$context = context_module::instance($cm->id);
$capabilities = array('mod/attendance:manageattendances', 'mod/attendance:takeattendances', 'mod/attendance:changeattendances');
if (!has_any_capability($capabilities, $context)) {
exit;
}
$PAGE->set_url('/mod/attendance/password.php');
$PAGE->set_pagelayout('popup');
$PAGE->set_context(context_system::instance());
$PAGE->set_title(get_string('password', 'attendance'));
echo $OUTPUT->header();
echo html_writer::span($session->studentpassword, 'student-password');
echo $OUTPUT->footer();

50
password_ajax.php

@ -0,0 +1,50 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Displays help via AJAX call or in a new page
*
* Use {@link core_renderer::help_icon()} or {@link addHelpButton()} to display
* the help icon.
*
* @copyright 2017 Dan Marsden
* @package mod_attendance
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('AJAX_SCRIPT', true);
require_once(dirname(__FILE__).'/../../config.php');
$session = required_param('session', PARAM_INT);
$session = $DB->get_record('attendance_sessions', array('id' => $session), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('attendance', $session->attendanceid);
require_login($cm->course, $cm);
$context = context_module::instance($cm->id);
$capabilities = array('mod/attendance:manageattendances', 'mod/attendance:takeattendances', 'mod/attendance:changeattendances');
if (!has_any_capability($capabilities, $context)) {
exit;
}
$PAGE->set_url('/mod/attendance/password.php');
$PAGE->set_pagelayout('popup');
$PAGE->set_context(context_system::instance());
$data->heading = '';
$data->text = html_writer::span($session->studentpassword, 'student-password');
echo json_encode($data);

1
pix/key.svg

@ -0,0 +1 @@
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M832 512q0-80-56-136t-136-56-136 56-56 136q0 42 19 83-41-19-83-19-80 0-136 56t-56 136 56 136 136 56 136-56 56-136q0-42-19-83 41 19 83 19 80 0 136-56t56-136zm851 704q0 17-49 66t-66 49q-9 0-28.5-16t-36.5-33-38.5-40-24.5-26l-96 96 220 220q28 28 28 68 0 42-39 81t-81 39q-40 0-68-28l-671-671q-176 131-365 131-163 0-265.5-102.5t-102.5-265.5q0-160 95-313t248-248 313-95q163 0 265.5 102.5t102.5 265.5q0 189-131 365l355 355 96-96q-3-3-26-24.5t-40-38.5-33-36.5-16-28.5q0-17 49-66t66-49q13 0 23 10 6 6 46 44.5t82 79.5 86.5 86 73 78 28.5 41z"/></svg>

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

40
preferences.php

@ -61,15 +61,31 @@ $errors = array();
if (!empty($att->pageparams->action)) {
require_sesskey();
}
$notification = '';
// TODO: combine this with the stuff in defaultstatus.php to avoid code duplication.
switch ($att->pageparams->action) {
case mod_attendance_preferences_page_params::ACTION_ADD:
$newacronym = optional_param('newacronym', null, PARAM_TEXT);
$newdescription = optional_param('newdescription', null, PARAM_TEXT);
$newgrade = optional_param('newgrade', 0, PARAM_RAW);
$newstudentavailability = optional_param('newstudentavailability', null, PARAM_INT);
$newgrade = unformat_float($newgrade);
$att->add_status($newacronym, $newdescription, $newgrade);
$newstatus = new stdClass();
$newstatus->attendanceid = $att->id;
$newstatus->acronym = $newacronym;
$newstatus->description = $newdescription;
$newstatus->grade = $newgrade;
$newstatus->studentavailability = $newstudentavailability;
$newstatus->setnumber = $att->pageparams->statusset;
$newstatus->cm = $att->cm;
$newstatus->context = $att->context;
$status = attendance_add_status($newstatus);
if (!$status) {
$notification = $OUTPUT->notification(get_string('cantaddstatus', 'attendance'), 'error');
}
if ($pageparams->statusset > $maxstatusset) {
$maxstatusset = $pageparams->statusset; // Make sure the new maximum is shown without a page refresh.
}
@ -84,7 +100,7 @@ switch ($att->pageparams->action) {
$status = $statuses[$att->pageparams->statusid];
if (isset($confirm)) {
$att->remove_status($status);
attendance_remove_status($status);
redirect($att->url_preferences(), get_string('statusdeleted', 'attendance'));
}
@ -101,17 +117,20 @@ switch ($att->pageparams->action) {
case mod_attendance_preferences_page_params::ACTION_HIDE:
$statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid];
$att->update_status($status, null, null, null, 0);
attendance_update_status($status, null, null, null, 0, $att->context, $att->cm);
break;
case mod_attendance_preferences_page_params::ACTION_SHOW:
$statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid];
$att->update_status($status, null, null, null, 1);
attendance_update_status($status, null, null, null, 1, $att->context, $att->cm);
break;
case mod_attendance_preferences_page_params::ACTION_SAVE:
$acronym = required_param_array('acronym', PARAM_TEXT);
$description = required_param_array('description', PARAM_TEXT);
$grade = required_param_array('grade', PARAM_RAW);
$studentavailability = optional_param_array('studentavailability', null, PARAM_RAW);
$unmarkedstatus = optional_param('setunmarked', null, PARAM_INT);
foreach ($grade as &$val) {
$val = unformat_float($val);
}
@ -119,7 +138,12 @@ switch ($att->pageparams->action) {
foreach ($acronym as $id => $v) {
$status = $statuses[$id];
$errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null);
$setunmarked = false;
if ($unmarkedstatus == $id) {
$setunmarked = true;
}
$errors[$id] = attendance_update_status($status, $acronym[$id], $description[$id], $grade[$id],
null, $att->context, $att->cm, $studentavailability[$id], $setunmarked);
}
attendance_update_users_grade($att);
break;
@ -133,8 +157,12 @@ $setselector = new attendance_set_selector($att, $maxstatusset);
// Output starts here.
echo $output->header();
if (!empty($notification)) {
echo $notification;
}
echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: '. format_string($course->fullname));
echo $output->render($tabs);
echo $OUTPUT->box(get_string('preferences_desc', 'attendance'), 'generalbox attendancedesc', 'notice');
echo $output->render($setselector);
echo $output->render($prefdata);

397
renderables.php

@ -37,24 +37,35 @@ require_once(dirname(__FILE__).'/locallib.php');
*
*/
class attendance_tabs implements renderable {
/** Sessions tab */
const TAB_SESSIONS = 1;
/** Add tab */
const TAB_ADD = 2;
/** Rerort tab */
const TAB_REPORT = 3;
/** Export tab */
const TAB_EXPORT = 4;
/** Preferences tab */
const TAB_PREFERENCES = 5;
/** Temp users tab */
const TAB_TEMPORARYUSERS = 6; // Tab for managing temporary users.
/** Update tab */
const TAB_UPDATE = 7;
/** Warnings tab */
const TAB_WARNINGS = 8;
/** Absentee tab */
const TAB_ABSENTEE = 9;
/** @var int current tab */
public $currenttab;
/** @var attendance */
/** @var stdClass attendance */
private $att;
/**
* Prepare info about sessions for attendance taking into account view parameters.
*
* @param attendance $att instance
* @param $currenttab - one of attendance_tabs constants
* @param mod_attendance_structure $att
* @param int $currenttab - one of attendance_tabs constants
*/
public function __construct(mod_attendance_structure $att, $currenttab=null) {
$this->att = $att;
@ -89,6 +100,12 @@ class attendance_tabs implements renderable {
get_string('report', 'attendance'));
}
if (has_capability('mod/attendance:viewreports', $context) &&
get_config('attendance', 'enablewarnings')) {
$toprow[] = new tabobject(self::TAB_ABSENTEE, $this->att->url_absentee()->out(),
get_string('absenteereport', 'attendance'));
}
if (has_capability('mod/attendance:export', $context)) {
$toprow[] = new tabobject(self::TAB_EXPORT, $this->att->url_export()->out(),
get_string('export', 'attendance'));
@ -96,7 +113,12 @@ class attendance_tabs implements renderable {
if (has_capability('mod/attendance:changepreferences', $context)) {
$toprow[] = new tabobject(self::TAB_PREFERENCES, $this->att->url_preferences()->out(),
get_string('settings', 'attendance'));
get_string('statussetsettings', 'attendance'));
if (get_config('attendance', 'enablewarnings')) {
$toprow[] = new tabobject(self::TAB_WARNINGS, $this->att->url_warnings()->out(),
get_string('warnings', 'attendance'));
}
}
if (has_capability('mod/attendance:managetemporaryusers', $context)) {
$toprow[] = new tabobject(self::TAB_TEMPORARYUSERS, $this->att->url_managetemp()->out(),
@ -113,25 +135,38 @@ class attendance_tabs implements renderable {
}
}
/**
* Class attendance_filter_controls
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_filter_controls implements renderable {
/** @var int current view mode */
public $pageparams;
/** @var stdclass */
public $cm;
/** @var int */
public $curdate;
/** @var int */
public $prevcur;
/** @var int */
public $nextcur;
/** @var string */
public $curdatetxt;
/** @var boolean */
public $reportcontrol;
/** @var string */
private $urlpath;
/** @var array */
private $urlparams;
/** @var mod_attendance_structure */
public $att;
/**
* attendance_filter_controls constructor.
* @param mod_attendance_structure $att
* @param bool $report
*/
public function __construct(mod_attendance_structure $att, $report = false) {
global $PAGE;
@ -179,30 +214,57 @@ class attendance_filter_controls implements renderable {
$this->att = $att;
}
/**
* Helper function for url.
*
* @param array $params
* @return moodle_url
*/
public function url($params=array()) {
$params = array_merge($this->urlparams, $params);
return new moodle_url($this->urlpath, $params);
}
/**
* Helper function for url path.
* @return string
*/
public function url_path() {
return $this->urlpath;
}
/**
* Helper function for url_params.
* @param array $params
* @return array
*/
public function url_params($params=array()) {
$params = array_merge($this->urlparams, $params);
return $params;
}
/**
* Return groupmode.
* @return int
*/
public function get_group_mode() {
return $this->att->get_group_mode();
}
/**
* Return groupslist.
* @return mixed
*/
public function get_sess_groups_list() {
return $this->att->pageparams->get_sess_groups_list();
}
/**
* Get current session type.
* @return mixed
*/
public function get_current_sesstype() {
return $this->att->pageparams->get_current_sesstype();
}
@ -220,17 +282,17 @@ class attendance_manage_data implements renderable {
/** @var int number of hidden sessions (sessions before $course->startdate)*/
public $hiddensessionscount;
/** @var array */
public $groups;
/** @var int */
public $hiddensesscount;
/** @var attendance */
/** @var mod_attendance_structure */
public $att;
/**
* Prepare info about attendance sessions taking into account view parameters.
*
* @param attendance $att instance
* @param mod_attendance_structure $att instance
*/
public function __construct(mod_attendance_structure $att) {
@ -243,40 +305,64 @@ class attendance_manage_data implements renderable {
$this->att = $att;
}
/**
* Helper function to return urls.
* @param int $sessionid
* @param int $grouptype
* @return mixed
*/
public function url_take($sessionid, $grouptype) {
return url_helpers::url_take($this->att, $sessionid, $grouptype);
}
/**
* Must be called without or with both parameters
*
* @param int $sessionid
* @param null $action
* @return mixed
*/
public function url_sessions($sessionid=null, $action=null) {
return url_helpers::url_sessions($this->att, $sessionid, $action);
}
}
/**
* class take data.
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_take_data implements renderable {
/** @var array */
public $users;
/** @var array|null|stdClass */
public $pageparams;
/** @var int */
public $groupmode;
/** @var stdclass */
public $cm;
/** @var array */
public $statuses;
/** @var mixed */
public $sessioninfo;
/** @var array */
public $sessionlog;
/** @var array */
public $sessions4copy;
/** @var bool */
public $updatemode;
/** @var string */
private $urlpath;
/** @var array */
private $urlparams;
private $att;
/** @var mod_attendance_structure */
public $att;
/**
* attendance_take_data constructor.
* @param mod_attendance_structure $att
*/
public function __construct(mod_attendance_structure $att) {
if ($att->pageparams->grouptype) {
$this->users = $att->get_users($att->pageparams->grouptype, $att->pageparams->page);
@ -314,6 +400,12 @@ class attendance_take_data implements renderable {
$this->att = $att;
}
/**
* Url function
* @param array $params
* @param array $excludeparams
* @return moodle_url
*/
public function url($params=array(), $excludeparams=array()) {
$params = array_merge($this->urlparams, $params);
@ -324,35 +416,57 @@ class attendance_take_data implements renderable {
return new moodle_url($this->urlpath, $params);
}
/**
* Url view helper.
* @param array $params
* @return mixed
*/
public function url_view($params=array()) {
return url_helpers::url_view($this->att, $params);
}
/**
* Url path helper.
* @return string
*/
public function url_path() {
return $this->urlpath;
}
}
/**
* Class user data.
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_user_data implements renderable {
/** @var mixed|object */
public $user;
/** @var array|null|stdClass */
public $pageparams;
/** @var array */
public $statuses;
/** @var array */
public $summary;
/** @var attendance_filter_controls */
public $filtercontrols;
/** @var array */
public $sessionslog;
/** @var array */
public $groups;
/** @var array */
public $coursesatts;
/** @var string */
private $urlpath;
/** @var array */
private $urlparams;
/**
* attendance_user_data constructor.
* @param mod_attendance_structure $att
* @param int $userid
*/
public function __construct(mod_attendance_structure $att, $userid) {
$this->user = $att->get_user($userid);
@ -375,9 +489,12 @@ class attendance_user_data implements renderable {
$this->summary = array();
foreach ($this->coursesatts as $atid => $ca) {
// Check to make sure the user can view this cm.
if (!get_fast_modinfo($ca->courseid)->instances['attendance'][$ca->attid]->uservisible) {
unset($this->courseatts[$atid]);
$modinfo = get_fast_modinfo($ca->courseid);
if (!$modinfo->instances['attendance'][$ca->attid]->uservisible) {
unset($this->coursesatts[$atid]);
continue;
} else {
$this->coursesatts[$atid]->cmid = $modinfo->instances['attendance'][$ca->attid]->get_course_module_record()->id;
}
$this->statuses[$ca->attid] = attendance_get_statuses($ca->attid);
$this->summary[$ca->attid] = new mod_attendance_summary($ca->attid, array($userid));
@ -389,32 +506,47 @@ class attendance_user_data implements renderable {
$this->urlparams = $params;
}
/**
* url helper.
* @return moodle_url
*/
public function url() {
return new moodle_url($this->urlpath, $this->urlparams);
}
}
/**
* Class report data.
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_report_data implements renderable {
/** @var array|null|stdClass */
public $pageparams;
/** @var array */
public $users;
/** @var array */
public $groups;
/** @var array */
public $sessions;
/** @var array */
public $statuses;
// Includes disablrd/deleted statuses.
/** @var array includes disablrd/deleted statuses. */
public $allstatuses;
/** @var array */
public $usersgroups = array();
/** @var array */
public $sessionslog = array();
/** @var array|mod_attendance_summary */
public $summary = array();
/** @var mod_attendance_structure */
public $att;
/**
* attendance_report_data constructor.
* @param mod_attendance_structure $att
*/
public function __construct(mod_attendance_structure $att) {
$currenttime = time();
if ($att->pageparams->view == ATT_VIEW_NOTPRESENT) {
@ -463,14 +595,30 @@ class attendance_report_data implements renderable {
$this->att = $att;
}
/**
* url take helper.
* @param int $sessionid
* @param int $grouptype
* @return mixed
*/
public function url_take($sessionid, $grouptype) {
return url_helpers::url_take($this->att, $sessionid, $grouptype);
}
/**
* url view helper.
* @param array $params
* @return mixed
*/
public function url_view($params=array()) {
return url_helpers::url_view($this->att, $params);
}
/**
* url helper.
* @param array $params
* @return moodle_url
*/
public function url($params=array()) {
$params = array_merge($params, $this->pageparams->get_significant_params());
@ -479,13 +627,25 @@ class attendance_report_data implements renderable {
}
/**
* Class preferences data.
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_preferences_data implements renderable {
/** @var array */
public $statuses;
/** @var mod_attendance_structure */
private $att;
/** @var array */
public $errors;
/**
* attendance_preferences_data constructor.
* @param mod_attendance_structure $att
* @param array $errors
*/
public function __construct(mod_attendance_structure $att, $errors) {
$this->statuses = $att->get_statuses(false);
$this->errors = $errors;
@ -497,6 +657,12 @@ class attendance_preferences_data implements renderable {
$this->att = $att;
}
/**
* url helper function
* @param array $params
* @param bool $significantparams
* @return moodle_url
*/
public function url($params=array(), $significantparams=true) {
if ($significantparams) {
$params = array_merge($this->att->pageparams->get_significant_params(), $params);
@ -506,17 +672,65 @@ class attendance_preferences_data implements renderable {
}
}
// Output a selector to change between status sets.
/**
* Default status set
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_default_statusset implements renderable {
/** @var array */
public $statuses;
/** @var array */
public $errors;
/**
* attendance_default_statusset constructor.
* @param array $statuses
* @param array $errors
*/
public function __construct($statuses, $errors) {
$this->statuses = $statuses;
$this->errors = $errors;
}
/**
* url helper.
* @param stdClass $params
* @return moodle_url
*/
public function url($params) {
return new moodle_url('/mod/attendance/defaultstatus.php', $params);
}
}
/**
* Output a selector to change between status sets.
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_set_selector implements renderable {
/** @var int */
public $maxstatusset;
/** @var mod_attendance_structure */
private $att;
/**
* attendance_set_selector constructor.
* @param mod_attendance_structure $att
* @param int $maxstatusset
*/
public function __construct(mod_attendance_structure $att, $maxstatusset) {
$this->att = $att;
$this->maxstatusset = $maxstatusset;
}
/**
* url helper
* @param array $statusset
* @return moodle_url
*/
public function url($statusset) {
$params = array();
$params['statusset'] = $statusset;
@ -524,6 +738,10 @@ class attendance_set_selector implements renderable {
return $this->att->url_preferences($params);
}
/**
* get current statusset.
* @return int
*/
public function get_current_statusset() {
if (isset($this->att->pageparams->statusset)) {
return $this->att->pageparams->statusset;
@ -531,12 +749,30 @@ class attendance_set_selector implements renderable {
return 0;
}
/**
* get statusset name.
* @param int $statusset
* @return string
*/
public function get_status_name($statusset) {
return attendance_get_setname($this->att->id, $statusset, true);
}
}
/**
* Url helpers
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class url_helpers {
/**
* Url take.
* @param stdClass $att
* @param int $sessionid
* @param int $grouptype
* @return mixed
*/
public static function url_take($att, $sessionid, $grouptype) {
$params = array('sessionid' => $sessionid);
if (isset($grouptype)) {
@ -548,6 +784,10 @@ class url_helpers {
/**
* Must be called without or with both parameters
* @param stdClass $att
* @param null $sessionid
* @param null $action
* @return mixed
*/
public static function url_sessions($att, $sessionid=null, $action=null) {
if (isset($sessionid) && isset($action)) {
@ -559,7 +799,70 @@ class url_helpers {
return $att->url_sessions($params);
}
/**
* Url view helper.
* @param stdClass $att
* @param array $params
* @return mixed
*/
public static function url_view($att, $params=array()) {
return $att->url_view($params);
}
}
/**
* Data structure representing an attendance password icon.
*
* @copyright 2017 Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_password_icon implements renderable, templatable {
/**
* @var string text to show
*/
public $text;
/**
* @var string Extra descriptive text next to the icon
*/
public $linktext = null;
/**
* Constructor
*
* @param string $text string for help page title,
* string with _help suffix is used for the actual help text.
* string with _link suffix is used to create a link to further info (if it exists)
* @param string $sessionid
*/
public function __construct($text, $sessionid) {
$this->text = $text;
$this->sessionid = $sessionid;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return array
*/
public function export_for_template(renderer_base $output) {
$title = get_string('password', 'attendance');
$data = new stdClass();
$data->heading = '';
$data->text = $this->text;
$data->alt = $title;
$data->icon = (new pix_icon('key', '', 'attendance'))->export_for_template($output);
$data->linktext = '';
$data->title = $title;
$data->url = (new moodle_url('/mod/attendance/password.php', [
'session' => $this->sessionid]))->out(false);
$data->ltr = !right_to_left();
return $data;
}
}

593
renderer.php

File diff suppressed because it is too large

140
renderhelpers.php

@ -33,20 +33,34 @@ require_once(dirname(__FILE__).'/renderables.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_sessions_cells_generator {
/** @var array $cells - list of table cells. */
protected $cells = array();
/** @var stdClass $reportdata - data for report. */
protected $reportdata;
/** @var stdClass $user - user record. */
protected $user;
/**
* Set up params.
* @param attendance_report_data $reportdata - reportdata.
* @param stdClass $user - user record.
*/
public function __construct(attendance_report_data $reportdata, $user) {
$this->reportdata = $reportdata;
$this->user = $user;
}
/**
* Get cells for the table.
*
* @param boolean $remarks - include remarks cell.
*/
public function get_cells($remarks = false) {
$this->init_cells();
foreach ($this->reportdata->sessions as $sess) {
if (array_key_exists($sess->id, $this->reportdata->sessionslog[$this->user->id])) {
if (array_key_exists($sess->id, $this->reportdata->sessionslog[$this->user->id]) &&
!empty($this->reportdata->sessionslog[$this->user->id][$sess->id]->statusid)) {
$statusid = $this->reportdata->sessionslog[$this->user->id][$sess->id]->statusid;
if (array_key_exists($statusid, $this->reportdata->statuses)) {
$points = format_float($this->reportdata->statuses[$statusid]->grade, 1, true, true);
@ -60,7 +74,7 @@ class user_sessions_cells_generator {
$this->construct_remarks_cell($this->reportdata->sessionslog[$this->user->id][$sess->id]->remarks);
}
} else {
if ($this->user->enrolmentstart > $sess->sessdate) {
if ($this->user->enrolmentstart > ($sess->sessdate + $sess->duration)) {
$starttext = get_string('enrolmentstart', 'attendance', userdate($this->user->enrolmentstart, '%d.%m.%Y'));
$this->construct_enrolments_info_cell($starttext);
} else if ($this->user->enrolmentend and $this->user->enrolmentend < $sess->sessdate) {
@ -87,34 +101,63 @@ class user_sessions_cells_generator {
return $this->cells;
}
protected function init_cells() {
}
/**
* Construct status cell.
*
* @param string $text - text for the cell.
*/
protected function construct_existing_status_cell($text) {
$this->cells[] = $text;
}
/**
* Construct hidden status cell.
*
* @param string $text - text for the cell.
*/
protected function construct_hidden_status_cell($text) {
$this->cells[] = $text;
}
/**
* Construct enrolments info cell.
*
* @param string $text - text for the cell.
*/
protected function construct_enrolments_info_cell($text) {
$this->cells[] = $text;
}
/**
* Construct not taken cell.
*
* @param string $text - text for the cell.
*/
protected function construct_not_taken_cell($text) {
$this->cells[] = $text;
}
/**
* Construct remarks cell.
*
* @param string $text - text for the cell.
*/
protected function construct_remarks_cell($text) {
$this->cells[] = $text;
}
/**
* Construct not existing user session cell.
*
* @param string $text - text for the cell.
*/
protected function construct_not_existing_for_user_session_cell($text) {
$this->cells[] = $text;
}
/**
* Dummy stub method, called at the end. - override if you need/
*/
protected function finalize_cells() {
}
}
@ -126,17 +169,33 @@ class user_sessions_cells_generator {
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_sessions_cells_html_generator extends user_sessions_cells_generator {
/** @var html_table_cell $cell */
private $cell;
/**
* Construct status cell.
*
* @param string $text - text for the cell.
*/
protected function construct_existing_status_cell($text) {
$this->close_open_cell_if_needed();
$this->cells[] = html_writer::span($text, 'attendancestatus-'.$text);
}
/**
* Construct hidden status cell.
*
* @param string $text - text for the cell.
*/
protected function construct_hidden_status_cell($text) {
$this->cells[] = html_writer::tag('s', $text);
}
/**
* Construct enrolments info cell.
*
* @param string $text - text for the cell.
*/
protected function construct_enrolments_info_cell($text) {
if (is_null($this->cell)) {
$this->cell = new html_table_cell($text);
@ -152,6 +211,9 @@ class user_sessions_cells_html_generator extends user_sessions_cells_generator {
}
}
/**
* Close cell if needed.
*/
private function close_open_cell_if_needed() {
if ($this->cell) {
$this->cells[] = $this->cell;
@ -159,11 +221,21 @@ class user_sessions_cells_html_generator extends user_sessions_cells_generator {
}
}
/**
* Construct not taken cell.
*
* @param string $text - text for the cell.
*/
protected function construct_not_taken_cell($text) {
$this->close_open_cell_if_needed();
$this->cells[] = $text;
}
/**
* Construct remarks cell.
*
* @param string $text - text for the cell.
*/
protected function construct_remarks_cell($text) {
global $OUTPUT;
@ -182,11 +254,20 @@ class user_sessions_cells_html_generator extends user_sessions_cells_generator {
$this->cells[] = $markcell;
}
/**
* Construct not existing for user session cell.
*
* @param string $text - text for the cell.
*/
protected function construct_not_existing_for_user_session_cell($text) {
$this->close_open_cell_if_needed();
$this->cells[] = $text;
}
/**
* Finalize cells.
*
*/
protected function finalize_cells() {
if ($this->cell) {
$this->cells[] = $this->cell;
@ -201,12 +282,23 @@ class user_sessions_cells_html_generator extends user_sessions_cells_generator {
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_sessions_cells_text_generator extends user_sessions_cells_generator {
/** @var string $enrolmentsinfocelltext. */
private $enrolmentsinfocelltext;
/**
* Construct hidden status cell.
*
* @param string $text - text for the cell.
*/
protected function construct_hidden_status_cell($text) {
$this->cells[] = '-'.$text;
}
/**
* Construct enrolments info cell.
*
* @param string $text - text for the cell.
*/
protected function construct_enrolments_info_cell($text) {
if ($this->enrolmentsinfocelltext != $text) {
$this->enrolmentsinfocelltext = $text;
@ -217,7 +309,11 @@ class user_sessions_cells_text_generator extends user_sessions_cells_generator {
}
}
// Used to print simple time - 1am instead of 1:00am
/**
* Used to print simple time - 1am instead of 1:00am.
*
* @param int $time - unix timestamp.
*/
function attendance_strftimehm($time) {
$mins = userdate($time, '%M');
if ($mins == '00') {
@ -227,6 +323,12 @@ function attendance_strftimehm($time) {
}
}
/**
* Used to print simple time - 1am instead of 1:00am.
*
* @param int $datetime - unix timestamp.
* @param int $duration - number of seconds.
*/
function construct_session_time($datetime, $duration) {
$starttime = attendance_strftimehm($datetime);
$endtime = attendance_strftimehm($datetime + $duration);
@ -234,6 +336,13 @@ function construct_session_time($datetime, $duration) {
return $starttime . ($duration > 0 ? ' - ' . $endtime : '');
}
/**
* Used to print session time.
*
* @param int $datetime - unix timestamp.
* @param int $duration - number of seconds duration.
* @return string.
*/
function construct_session_full_date_time($datetime, $duration) {
$sessinfo = userdate($datetime, get_string('strftimedmyw', 'attendance'));
$sessinfo .= ' '.construct_session_time($datetime, $duration);
@ -241,6 +350,13 @@ function construct_session_full_date_time($datetime, $duration) {
return $sessinfo;
}
/**
* Used to construct user summary.
*
* @param stdclass $usersummary - data for summary.
* @param int $view - ATT_VIEW_ALL|ATT_VIEW_
* @return string.
*/
function construct_user_data_stat($usersummary, $view) {
$stattable = new html_table();
$stattable->attributes['class'] = 'attlist';
@ -300,6 +416,14 @@ function construct_user_data_stat($usersummary, $view) {
return html_writer::table($stattable);
}
/**
* Returns html user summary
*
* @param stdclass $attendance - attendance record.
* @param stdclass $user - user record
* @return string.
*
*/
function construct_full_user_stat_html_table($attendance, $user) {
$summary = new mod_attendance_summary($attendance->id, $user->id);
return construct_user_data_stat($summary->get_all_sessions_summary_for($user->id), ATT_VIEW_ALL);

8
report.php

@ -46,6 +46,7 @@ $context = context_module::instance($cm->id);
require_capability('mod/attendance:viewreports', $context);
$pageparams->init($cm);
$pageparams->showextrauserdetails = optional_param('showextrauserdetails', $attrecord->showextrauserdetails, PARAM_INT);
$pageparams->showsessiondetails = optional_param('showsessiondetails', $attrecord->showsessiondetails, PARAM_INT);
$pageparams->sessiondetailspos = optional_param('sessiondetailspos', $attrecord->sessiondetailspos, PARAM_TEXT);
@ -73,13 +74,14 @@ $event->add_record_snapshot('course_modules', $cm);
$event->add_record_snapshot('attendance', $attrecord);
$event->trigger();
// Output starts here.
$title = get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname);
$header = new mod_attendance_header($att, $title);
// Output starts here.
echo $output->header();
echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname));
echo $output->render($header);
echo $output->render($tabs);
echo $output->render($filtercontrols);
echo $output->render($reportdata);
echo $output->footer();

92
resetcalendar.php

@ -0,0 +1,92 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Reset Calendar events.
*
* @package mod_attendance
* @copyright 2017 onwards Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/mod/attendance/lib.php');
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$action = optional_param('action', '', PARAM_ALPHA);
admin_externalpage_setup('managemodules');
$context = context_system::instance();
// Check permissions.
require_capability('mod/attendance:viewreports', $context);
$exportfilename = 'attendance-absentee.csv';
$PAGE->set_url('/mod/attendance/resetcalendar.php');
$PAGE->set_heading($SITE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('resetcalendar', 'mod_attendance'));
$tabmenu = attendance_print_settings_tabs('resetcalendar');
echo $tabmenu;
if (get_config('attendance', 'enablecalendar')) {
// Check to see if all sessions have calendar events.
if ($action == 'create' && confirm_sesskey()) {
$sessions = $DB->get_recordset('attendance_sessions', array('caleventid' => 0));
foreach ($sessions as $session) {
attendance_create_calendar_event($session);
if ($session->caleventid) {
$DB->update_record('attendance_sessions', $session);
}
}
$sessions->close();
echo $OUTPUT->notification(get_string('eventscreated', 'mod_attendance'), 'notifysuccess');
} else {
if ($DB->record_exists('attendance_sessions', array('caleventid' => 0))) {
$createurl = new moodle_url('/mod/attendance/resetcalendar.php', array('action' => 'create'));
$returnurl = new moodle_url('/admin/settings.php', array('section' => 'modsettingattendance'));
echo $OUTPUT->confirm(get_string('resetcaledarcreate', 'mod_attendance'), $createurl, $returnurl);
} else {
echo $OUTPUT->box(get_string("noeventstoreset", "mod_attendance"));
}
}
} else {
if ($action == 'delete' && confirm_sesskey()) {
$caleventids = $DB->get_records_select_menu('attendance_sessions', 'caleventid > 0', array(),
'', 'caleventid, caleventid as id2');
$DB->delete_records_list('event', 'id', $caleventids);
$DB->execute("UPDATE {attendance_sessions} set caleventid = 0");
echo $OUTPUT->notification(get_string('eventsdeleted', 'mod_attendance'), 'notifysuccess');
} else {
// Check to see if there are any events that need to be deleted.
if ($DB->record_exists_select('attendance_sessions', 'caleventid > 0')) {
$deleteurl = new moodle_url('/mod/attendance/resetcalendar.php', array('action' => 'delete'));
$returnurl = new moodle_url('/admin/settings.php', array('section' => 'modsettingattendance'));
echo $OUTPUT->confirm(get_string('resetcaledardelete', 'mod_attendance'), $deleteurl, $returnurl);
} else {
echo $OUTPUT->box(get_string("noeventstoreset", "mod_attendance"));
}
}
}
echo $OUTPUT->footer();

97
sessions.php

@ -73,7 +73,7 @@ switch ($att->pageparams->action) {
}
if ($formdata = $mform->get_data()) {
$sessions = construct_sessions_data_for_add($formdata);
$sessions = attendance_construct_sessions_data_for_add($formdata, $att);
$att->add_sessions($sessions);
if (count($sessions) == 1) {
$message = get_string('sessiongenerated', 'attendance');
@ -99,6 +99,9 @@ switch ($att->pageparams->action) {
}
if ($formdata = $mform->get_data()) {
if (empty($formdata->autoassignstatus)) {
$formdata->autoassignstatus = 0;
}
$att->update_session_from_form_data($formdata, $sessionid);
mod_attendance_notifyqueue::notify_success(get_string('sessionupdated', 'attendance'));
@ -223,95 +226,3 @@ echo $output->render($tabs);
$mform->display();
echo $OUTPUT->footer();
function construct_sessions_data_for_add($formdata) {
global $CFG;
$sesstarttime = $formdata->sestime['starthour'] * HOURSECS + $formdata->sestime['startminute'] * MINSECS;
$sesendtime = $formdata->sestime['endhour'] * HOURSECS + $formdata->sestime['endminute'] * MINSECS;
$sessiondate = $formdata->sessiondate + $sesstarttime;
$duration = $sesendtime - $sesstarttime;
$now = time();
if (empty(get_config('attendance', 'studentscanmark'))) {
$formdata->studentscanmark = 0;
}
$sessions = array();
if (isset($formdata->addmultiply)) {
$startdate = $sessiondate;
$enddate = $formdata->sessionenddate + DAYSECS; // Because enddate in 0:0am.
if ($enddate < $startdate) {
return null;
}
// Getting first day of week.
$sdate = $startdate;
$dinfo = usergetdate($sdate);
if ($CFG->calendar_startwday === '0') { // Week start from sunday.
$startweek = $startdate - $dinfo['wday'] * DAYSECS; // Call new variable.
} else {
$wday = $dinfo['wday'] === 0 ? 7 : $dinfo['wday'];
$startweek = $startdate - ($wday - 1) * DAYSECS;
}
$wdaydesc = array(0 => 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
while ($sdate < $enddate) {
if ($sdate < $startweek + WEEKSECS) {
$dinfo = usergetdate($sdate);
if (isset($formdata->sdays) && array_key_exists($wdaydesc[$dinfo['wday']], $formdata->sdays)) {
$sess = new stdClass();
$sess->sessdate = make_timestamp($dinfo['year'], $dinfo['mon'], $dinfo['mday'],
$formdata->sestime['starthour'], $formdata->sestime['startminute']);
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
}
$sess->statusset = $formdata->statusset;
fill_groupid($formdata, $sessions, $sess);
}
$sdate += DAYSECS;
} else {
$startweek += WEEKSECS * $formdata->period;
$sdate = $startweek;
}
}
} else {
$sess = new stdClass();
$sess->sessdate = $sessiondate;
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
}
$sess->statusset = $formdata->statusset;
fill_groupid($formdata, $sessions, $sess);
}
return $sessions;
}
function fill_groupid($formdata, &$sessions, $sess) {
if ($formdata->sessiontype == mod_attendance_structure::SESSION_COMMON) {
$sess = clone $sess;
$sess->groupid = 0;
$sessions[] = $sess;
} else {
foreach ($formdata->groups as $groupid) {
$sess = clone $sess;
$sess->groupid = $groupid;
$sessions[] = $sess;
}
}
}

117
settings.php

@ -26,6 +26,10 @@ defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
require_once(dirname(__FILE__).'/lib.php');
require_once(dirname(__FILE__).'/locallib.php');
$tabmenu = attendance_print_settings_tabs();
$settings->add(new admin_setting_heading('attendance_header', '', $tabmenu));
// Paging options.
$options = array(
@ -44,4 +48,117 @@ if ($ADMIN->fulltree) {
$settings->add(new admin_setting_configcheckbox('attendance/studentscanmark',
get_string('studentscanmark', 'attendance'), get_string('studentscanmark_desc', 'attendance'), 1));
$settings->add(new admin_setting_configcheckbox('attendance/studentscanmarksessiontime',
get_string('studentscanmarksessiontime', 'attendance'),
get_string('studentscanmarksessiontime_desc', 'attendance'), 1));
$settings->add(new admin_setting_configtext('attendance/studentscanmarksessiontimeend',
get_string('studentscanmarksessiontimeend', 'attendance'),
get_string('studentscanmarksessiontimeend_desc', 'attendance'), '60', PARAM_INT));
$settings->add(new admin_setting_configcheckbox('attendance/subnetactivitylevel',
get_string('subnetactivitylevel', 'attendance'),
get_string('subnetactivitylevel_desc', 'attendance'), 1));
$options = array(
ATT_VIEW_ALL => get_string('all', 'attendance'),
ATT_VIEW_ALLPAST => get_string('allpast', 'attendance'),
ATT_VIEW_NOTPRESENT => get_string('lowgrade', 'attendance'),
ATT_VIEW_MONTHS => get_string('months', 'attendance'),
ATT_VIEW_WEEKS => get_string('weeks', 'attendance'),
ATT_VIEW_DAYS => get_string('days', 'attendance')
);
$settings->add(new admin_setting_configselect('attendance/defaultview',
get_string('defaultview', 'attendance'),
get_string('defaultview_desc', 'attendance'), ATT_VIEW_WEEKS, $options));
$settings->add(new admin_setting_configcheckbox('attendance/multisessionexpanded',
get_string('multisessionexpanded', 'attendance'),
get_string('multisessionexpanded_desc', 'attendance'), 0));
$settings->add(new admin_setting_configcheckbox('attendance/showsessiondescriptiononreport',
get_string('showsessiondescriptiononreport', 'attendance'),
get_string('showsessiondescriptiononreport_desc', 'attendance'), 0));
$settings->add(new admin_setting_configcheckbox('attendance/studentrecordingexpanded',
get_string('studentrecordingexpanded', 'attendance'),
get_string('studentrecordingexpanded_desc', 'attendance'), 1));
$settings->add(new admin_setting_configcheckbox('attendance/enablecalendar',
get_string('enablecalendar', 'attendance'),
get_string('enablecalendar_desc', 'attendance'), 1));
$settings->add(new admin_setting_configcheckbox('attendance/enablewarnings',
get_string('enablewarnings', 'attendance'),
get_string('enablewarnings_desc', 'attendance'), 0));
$name = new lang_string('defaultsettings', 'mod_attendance');
$description = new lang_string('defaultsettings_help', 'mod_attendance');
$settings->add(new admin_setting_heading('defaultsettings', $name, $description));
$settings->add(new admin_setting_configtext('attendance/subnet',
get_string('requiresubnet', 'attendance'), get_string('requiresubnet_help', 'attendance'), '', PARAM_RAW));
$name = new lang_string('defaultsessionsettings', 'mod_attendance');
$description = new lang_string('defaultsessionsettings_help', 'mod_attendance');
$settings->add(new admin_setting_heading('defaultsessionsettings', $name, $description));
$settings->add(new admin_setting_configcheckbox('attendance/absenteereport_default',
get_string('includeabsentee', 'attendance'), '', 1));
$settings->add(new admin_setting_configcheckbox('attendance/studentscanmark_default',
get_string('studentscanmark', 'attendance'), '', 0));
$options = attendance_get_automarkoptions();
$settings->add(new admin_setting_configselect('attendance/automark_default',
get_string('automark', 'attendance'), '', 0, $options));
$settings->add(new admin_setting_configcheckbox('attendance/randompassword_default',
get_string('randompassword', 'attendance'), '', 0));
$settings->add(new admin_setting_configcheckbox('attendance/autoassignstatus',
get_string('autoassignstatus', 'attendance'), '', 0));
$settings->add(new admin_setting_configcheckbox('attendance/preventsharedip',
get_string('preventsharedip', 'attendance'), '', 0));
$settings->add(new admin_setting_configtext('attendance/preventsharediptime',
get_string('preventsharediptime', 'attendance'), get_string('preventsharediptime_help', 'attendance'), '', PARAM_RAW));
$name = new lang_string('defaultwarningsettings', 'mod_attendance');
$description = new lang_string('defaultwarningsettings_help', 'mod_attendance');
$settings->add(new admin_setting_heading('defaultwarningsettings', $name, $description));
$options = array();
for ($i = 1; $i <= 100; $i++) {
$options[$i] = "$i%";
}
$settings->add(new admin_setting_configselect('attendance/warningpercent',
get_string('warningpercent', 'attendance'), get_string('warningpercent_help', 'attendance'), 70, $options));
$options = array();
for ($i = 1; $i <= 50; $i++) {
$options[$i] = "$i";
}
$settings->add(new admin_setting_configselect('attendance/warnafter',
get_string('warnafter', 'attendance'), get_string('warnafter_help', 'attendance'), 5, $options));
$settings->add(new admin_setting_configselect('attendance/maxwarn',
get_string('maxwarn', 'attendance'), get_string('maxwarn_help', 'attendance'), 1, $options));
$settings->add(new admin_setting_configcheckbox('attendance/emailuser',
get_string('emailuser', 'attendance'), get_string('emailuser_help', 'attendance'), 1));
$settings->add(new admin_setting_configtext('attendance/emailsubject',
get_string('emailsubject', 'attendance'), get_string('emailsubject_help', 'attendance'),
get_string('emailsubject_default', 'attendance'), PARAM_RAW));
$settings->add(new admin_setting_configtextarea('attendance/emailcontent',
get_string('emailcontent', 'attendance'), get_string('emailcontent_help', 'attendance'),
get_string('emailcontent_default', 'attendance'), PARAM_RAW));
}

78
student_attendance_form.php

@ -14,10 +14,29 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Student form class.
*
* @package mod_attendance
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Class mod_attendance_student_attendance_form
*
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_student_attendance_form extends moodleform {
/**
* Called to define this moodle form
*
* @return void
*/
public function definition() {
global $USER;
@ -27,6 +46,18 @@ class mod_attendance_student_attendance_form extends moodleform {
$attblock = $this->_customdata['attendance'];
$statuses = $attblock->get_statuses();
// Check if user has access to all statuses.
$disabledduetotime = false;
foreach ($statuses as $status) {
if ($status->studentavailability === '0') {
unset($statuses[$status->id]);
}
if (!empty($status->studentavailability) &&
time() > $attforsession->sessdate + ($status->studentavailability * 60)) {
unset($statuses[$status->id]);
$disabledduetotime = true;
}
}
$mform->addElement('hidden', 'sessid', null);
$mform->setType('sessid', PARAM_INT);
@ -46,16 +77,47 @@ class mod_attendance_student_attendance_form extends moodleform {
if (!empty($attforsession->description)) {
$mform->addElement('html', $attforsession->description);
}
// Create radio buttons for setting the attendance status.
$radioarray = array();
foreach ($statuses as $status) {
$radioarray[] =& $mform->createElement('radio', 'status', '', $status->description, $status->id, array());
if (!empty($attforsession->studentpassword)) {
$mform->addElement('text', 'studentpassword', get_string('password', 'attendance'));
$mform->setType('studentpassword', PARAM_TEXT);
$mform->addRule('studentpassword', get_string('passwordrequired', 'attendance'), 'required');
}
// Add the radio buttons as a control with the user's name in front.
$mform->addGroup($radioarray, 'statusarray', $USER->firstname.' '.$USER->lastname.':', array(''), false);
$mform->addRule('statusarray', get_string('attendancenotset', 'attendance'), 'required', '', 'client', false, false);
if (!$attforsession->autoassignstatus) {
// Create radio buttons for setting the attendance status.
$radioarray = array();
foreach ($statuses as $status) {
$name = html_writer::span($status->description, 'statusdesc');
$radioarray[] =& $mform->createElement('radio', 'status', '', $name, $status->id, array());
}
if ($disabledduetotime) {
$warning = html_writer::span(get_string('somedisabledstatus', 'attendance'), 'somedisabledstatus');
$radioarray[] =& $mform->createElement('static', '', '', $warning);
}
// Add the radio buttons as a control with the user's name in front.
$radiogroup = $mform->addGroup($radioarray, 'statusarray', $USER->firstname.' '.$USER->lastname.':', array(''), false);
$radiogroup->setAttributes(array('class' => 'statusgroup'));
$mform->addRule('statusarray', get_string('attendancenotset', 'attendance'), 'required', '', 'client', false, false);
}
$this->add_action_buttons();
}
/**
* Validate Form.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = array();
if (!($this->_customdata['session']->autoassignstatus)) {
// Check if this status is allowed to be set.
if (empty($data['status'])) {
$errors['statusarray'] = get_string('invalidstatus', 'attendance');
}
}
return $errors;
}
}

109
styles.css

@ -1,5 +1,5 @@
.path-mod-attendance .attbtn {
border:1px solid #AAAAAA;
border: 1px solid #aaa;
border-radius: 5px;
margin-left: 2px;
margin-right: 2px;
@ -14,27 +14,27 @@
.path-mod-attendance .attfiltercontrols {
margin-bottom: 10px;
margin-left:auto;
margin-right:auto;
width:90%;
margin-left: auto;
margin-right: auto;
width: 90%;
}
.path-mod-attendance .attfiltercontrols #currentdate{
.path-mod-attendance .attfiltercontrols #currentdate {
display: inline;
}
.path-mod-attendance .attwidth {
margin: auto;
width:90%;
width: 90%;
}
.path-mod-attendance .userwithoutenrol,
.path-mod-attendance .userwithoutenrol a{
.path-mod-attendance .userwithoutenrol a {
color: gray;
}
.path-mod-attendance .userwithoutdata,
.path-mod-attendance .userwithoutdata a{
.path-mod-attendance .userwithoutdata a {
color: red;
}
@ -43,8 +43,8 @@
}
.path-mod-attendance .takelist .userpicture {
margin:0 3px;
vertical-align:middle;
margin: 0 3px;
vertical-align: middle;
}
.path-mod-attendance .takegrid input {
@ -74,14 +74,14 @@
}
/* for IE7*/
.path-mod-attendance .filtercontrols td {
padding:6px;
padding: 6px;
}
.path-mod-attendance .takecontrols {
margin: 0 auto 20px auto;
width: 800px;
}
.path-mod-attendance .takecontrols table{
.path-mod-attendance .takecontrols table {
margin: 0 auto;
}
.path-mod-attendance .takecontrols .c0 {
@ -99,11 +99,11 @@
}
.path-mod-attendance table.userinfobox {
border: 1px solid #EEEEEE;
border: 1px solid #eee;
padding: 0;
}
.path-mod-attendance table.userinfobox td.left {
background-color: #EEEEEE;
background-color: #eee;
padding: 30px 10px;
}
.path-mod-attendance table.attlist td.c0 {
@ -152,11 +152,11 @@
}
.path-mod-attendance .attendancestatus-E {
color: #00AEE3;
color: #00aee3;
}
.path-mod-attendance .attendancestatus-L {
color: #F7931E;
color: #f7931e;
}
.path-mod-attendance .attendancestatus-A {
@ -164,25 +164,90 @@
}
.path-mod-attendance .attreport .contrast {
background-color: #EAEAEA;
background-color: #eaeaea;
}
.path-mod-attendance .attreport .center {
text-align:center;
text-align: center;
}
.path-mod-attendance .attreport .left {
text-align:left;
text-align: left;
}
.path-mod-attendance .attreport .bottom {
vertical-align:bottom;
vertical-align: bottom;
}
.path-mod-attendance .attreport .nowrap {
white-space:nowrap;
white-space: nowrap;
}
.path-mod-attendance .attreport .narrow {
width:1px;
width: 1px;
}
.path-mod-attendance .attreport img.userpicture {
max-width: inherit;
}
.path-mod-attendance .student-password {
font-size: x-large;
text-align: center;
}
.path-mod-attendance .ungraded {
font-size: smaller;
font-style: italic;
}
#page-mod-attendance-sessions .statusgroup .statusdesc {
margin-right: 12px;
}
#page-mod-attendance-view .averageattendance {
font-weight: bold;
}
#page-mod-attendance-preferences .form-control {
width: inherit;
display: inherit;
}
@media (max-width: 767px) {
.path-mod-attendance .remarkscol {
display: none;
}
.path-mod-attendance .statusgroup .form-check-inline {
display: block;
padding-top: 10px;
padding-bottom: 10px;
}
#page-mod-attendance-view .colatt {
display: none;
}
.path-mod-attendance .attfiltercontrols,
.path-mod-attendance .attwidth {
width: 100%;
}
}
@media (max-width: 480px) {
.path-mod-attendance .desccol {
display: none;
}
.path-mod-attendance .pointscol {
display: none;
}
.path-mod-attendance .attfiltercontrols #currentdate {
display: none;
}
#page-mod-attendance-view .colsessionscompleted,
#page-mod-attendance-view .colpointssessionscompleted {
display: none;
}
}

3
take.php

@ -40,6 +40,9 @@ $pageparams->perpage = optional_param('perpage', get_config('attendance', 're
$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);
// Check this is a valid session for this attendance.
$session = $DB->get_record('attendance_sessions', array('id' => $pageparams->sessionid, 'attendanceid' => $att->id),
'*', MUST_EXIST);
require_login($course, true, $cm);
$context = context_module::instance($cm->id);

21
temp_form.php

@ -27,9 +27,16 @@ defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir.'/formslib.php');
/**
* Class temp_form
* @copyright 2013 Davo Smith, Synergy Learning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class temp_form extends moodleform {
/**
* Define form.
*/
public function definition() {
$mform = $this->_form;
$mform->addElement('hidden', 'id', 0);
@ -49,11 +56,21 @@ class temp_form extends moodleform {
$mform->closeHeaderBefore('submit');
}
/**
* Do stuff to form after creation.
*/
public function definition_after_data() {
$mform = $this->_form;
$mform->applyFilter('tname', 'trim');
}
/**
* Form validation.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
@ -63,6 +80,4 @@ class temp_form extends moodleform {
return $errors;
}
}

20
tempedit_form.php

@ -27,8 +27,19 @@ defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir.'/formslib.php');
/**
* class for displaying tempedit form.
*
* @copyright 2013 Davo Smith, Synergy Learning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tempedit_form extends moodleform {
/**
* Called to define this moodle form
*
* @return void
*/
public function definition() {
$mform = $this->_form;
@ -55,11 +66,20 @@ class tempedit_form extends moodleform {
$mform->closeHeaderBefore('submit');
}
/**
* Apply filter to form
*
*/
public function definition_after_data() {
$mform = $this->_form;
$mform->applyFilter('tname', 'trim');
}
/**
* Perform validation on the form
* @param array $data
* @param array $files
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);

21
templates/attendance_password_icon.mustache

@ -0,0 +1,21 @@
{{!
@template attendance/attendance_password_icon
attendance_password icon.
Example context (json):
{
"title": "Help with something",
"url": "http://example.org/help",
"linktext": "",
"icon":{
"attributes": [
{"name": "src", "value": "../pix/key.svg"},
{"name": "alt", "value": "Password icon"}
]
}
}
}}
<span class="helptooltip">
<a href="{{url}}" title={{#quote}}{{title}}{{/quote}} aria-haspopup="true" target="_blank">{{#icon}}{{>core/pix_icon}}{{/icon}}{{#linktext}}{{.}}{{/linktext}}</a>
</span>

28
templates/attendance_password_icon_boost.mustache

@ -0,0 +1,28 @@
{{!
@template attendance/attendance_password_icon Boost Example.
This is an example of a template you could copy into a boost based theme to use proper popover.
At the moment we cannot specify different templates to use in plugin so we use
a cross-compatible link based pop-up for the password.
attendance_password icon.
Example context (json):
{
"title": "Help with something",
"url": "http://example.org/help",
"linktext": "",
"icon":{
"attributes": [
{"name": "class", "value": "iconhelp"},
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
}
}}
<a class="btn btn-link p-a-0" role="button"
data-container="body" data-toggle="popover"
data-placement="{{#ltr}}left{{/ltr}}{{^ltr}}right{{/ltr}}" data-content="<span class='student-pass'>{{text}}</span> {{completedoclink}}"
data-html="true" tabindex="0" data-trigger="focus">
{{#pix}}key, attendance, {{alt}}{{/pix}}
</a>

21
tempmerge_form.php

@ -14,13 +14,32 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Temp merge form class.
*
* @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');
/**
* Temp merge form class.
*
* @package mod_attendance
* @copyright 2013 Davo Smith, Synergy Learning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tempmerge_form extends moodleform {
/**
* Called to define this moodle form
*
* @return void
*/
public function definition() {
global $COURSE;

14
tempusers.php

@ -60,8 +60,8 @@ if ($data = $mform->get_data()) {
$user->auth = 'manual';
$user->confirmed = 1;
$user->deleted = 1;
$user->email = time().'@ghost.user.de';
$user->username = time().'@ghost.user.de';
$user->email = time().'@attendance.danmarsden.com';
$user->username = time().'@attendance.danmarsden.com';
$user->idnumber = 'tempghost';
$user->mnethostid = $CFG->mnet_localhost_id;
$studentid = $DB->insert_record('user', $user);
@ -89,12 +89,18 @@ $tempusers = $DB->get_records('attendance_tempusers', array('courseid' => $cours
echo '<div>';
echo '<p style="margin-left:10%;">'.get_string('tempuserslist', 'attendance').'</p>';
if ($tempusers) {
print_tempusers($tempusers, $att);
attendance_print_tempusers($tempusers, $att);
}
echo '</div>';
echo $output->footer($course);
function print_tempusers($tempusers, mod_attendance_structure $att) {
/**
* Print list of users.
*
* @param stdClass $tempusers
* @param mod_attendance_structure $att
*/
function attendance_print_tempusers($tempusers, mod_attendance_structure $att) {
echo '<p></p>';
echo '<table border="1" bordercolor="#EEEEEE" style="background-color:#fff" cellpadding="2" align="center"'.
'width="80%" summary="'.get_string('temptable', 'attendance').'"><tr>';

56
tests/attendance_webservices_test.php

@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Webservices test for attendance plugin.
*
* @package local_attendance
* @package mod_attendance
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@ -30,14 +31,28 @@ global $CFG;
require_once($CFG->dirroot . '/mod/attendance/classes/attendance_webservices_handler.php');
require_once($CFG->dirroot . '/mod/attendance/classes/structure.php');
/** This class contains the test cases for the functions in attendance_webservices_handler.php. */
/**
* This class contains the test cases for the functions in attendance_webservices_handler.php.
* @copyright 2015 Caio Bressan Doneda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attendance_webservices_tests extends advanced_testcase {
/** @var coursecat */
protected $category;
/** @var stdClass */
protected $course;
/** @var stdClass */
protected $attendance;
/** @var stdClass */
protected $teacher;
/** @var array */
protected $students;
/** @var array */
protected $sessions;
/**
* Setup class.
*/
public function setUp() {
global $DB;
@ -72,6 +87,7 @@ class attendance_webservices_tests extends advanced_testcase {
$session->timemodified = time();
$session->statusset = 0;
$session->groupid = 0;
$session->absenteereport = 1;
// Creating two sessions.
$this->sessions[] = $session;
@ -81,9 +97,11 @@ class attendance_webservices_tests extends advanced_testcase {
/** Creating 10 students and 1 teacher. */
protected function create_and_enrol_users() {
$this->students = array();
for ($i = 0; $i < 10; $i++) {
$student = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($student->id, $this->course->id, 5); // Enrol as student.
$this->students[] = $student;
}
$this->teacher = $this->getDataGenerator()->create_user();
@ -122,6 +140,40 @@ class attendance_webservices_tests extends advanced_testcase {
$this->assertEquals(count($sessioninfo->users), 10);
}
public function test_get_session_with_group() {
$this->resetAfterTest(true);
// Create a group in our course, and add some students to it.
$group = new stdClass();
$group->courseid = $this->course->id;
$group = $this->getDataGenerator()->create_group($group);
for ($i = 0; $i < 5; $i++) {
$member = new stdClass;
$member->groupid = $group->id;
$member->userid = $this->students[$i]->id;
$this->getDataGenerator()->create_group_member($member);
}
// Add a session that's identical to the first, but with a group.
$session = $this->sessions[0];
$session->groupid = $group->id;
$session->sessdate += 3600; // Make sure it appears second in the list.
$this->attendance->add_sessions($this->sessions);
$courseswithsessions = attendance_handler::get_courses_with_today_sessions($this->teacher->id);
$course = array_pop($courseswithsessions);
$attendanceinstance = array_pop($course->attendance_instances);
$session = array_pop($attendanceinstance['today_sessions']);
$sessioninfo = attendance_handler::get_session($session->id);
$this->assertEquals($session->id, $sessioninfo->id);
$this->assertEquals($group->id, $sessioninfo->groupid);
$this->assertEquals(count($sessioninfo->users), 5);
}
public function test_update_user_status() {
$this->resetAfterTest(true);

27
tests/behat/attendance_mod.feature

@ -38,26 +38,41 @@ Feature: Teachers and Students can record session attendance
Then I should see "Attendance"
And I log out
Scenario: Students can mark their own attendance
Scenario: Students can mark their own attendance and teacher can hide specific status from students.
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Add"
And I set the field "Allow students to record own attendance" to "1"
And I set the following fields to these values:
| id_sestime_starthour | 22 |
| id_sestime_endhour | 23 |
| id_sestime_starthour | 00 |
| id_sestime_endhour | 23 |
| id_sestime_endminute | 55 |
And I click on "id_submitbutton" "button"
And I log out
When I log in as "student1"
And I log in as "student1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Submit attendance"
And I should see "Excused"
And I log out
And I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Status set"
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[5]/input" to "0"
And I press "Update"
And I log out
And I log in as "student1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Submit attendance"
And I should not see "Excused"
And I set the field "Present" to "1"
And I press "Save changes"
Then I should see "Self-recorded"
And I should see "Self-recorded"
And I log out
When I log in as "teacher1"
And I log in as "teacher1"
And I follow "Course 1"
And I expand "Reports" node
And I follow "Logs"

31
tests/behat/defaultstatus.feature

@ -0,0 +1,31 @@
@mod @mod_attendance
Feature: Admin can set default status set for use in new attendance
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category | timecreated | timemodified |
| Course 1 | C1 | Prove the attendance activity works | 0 | ##yesterday## | ##yesterday## |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| course | user | role | timestart |
| C1 | teacher1 | editingteacher | ##yesterday## |
And I log in as "admin"
And I navigate to "Attendance" node in "Site administration > Plugins"
And I follow "Default status set"
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/input" to "customstatusdescription"
And I click on "Update" "button" in the "#preferencesform" "css_element"
And I should see "Status updated"
And I log out
@javascript
Scenario: Modified default status set added to new attendance
Given 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 | Attendance1 |
And I follow "Attendance1"
And I follow "Status set"
Then the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/input" matches value "customstatusdescription"

16
tests/behat/extra_features.feature

@ -80,13 +80,13 @@ Feature: Test the various new features in the attendance module
When I follow "Take attendance"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I click on "td.c3 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"
And I click on "td.c4 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"
And I click on "td.c5 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 click on "td.c6 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"
@ -149,7 +149,7 @@ Feature: Test the various new features in the attendance module
Given I log in as "teacher1"
And I follow "Course 1"
And I follow "Test attendance"
And I follow "Settings"
And I follow "Status set"
And I set the field "jump" to "New set of statuses"
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/input" to "G"
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/input" to "Great"
@ -182,14 +182,14 @@ Feature: Test the various new features in the attendance module
| id_sestime_endhour | 13 |
And I click on "submitbutton" "button"
When I click on "Take attendance" "link" in the "10am" "table_row"
When I click on "Take attendance" "link" in the "10AM" "table_row"
Then "Set status for all users to «Present»" "link" should exist
And "Set status for all users to «Late»" "link" should exist
And "Set status for all users to «Excused»" "link" should exist
And "Set status for all users to «Absent»" "link" should exist
When I follow "Sessions"
And I click on "Take attendance" "link" in the "12pm" "table_row"
And I click on "Take attendance" "link" in the "12PM" "table_row"
Then "Set status for all users to «Great»" "link" should exist
And "Set status for all users to «OK»" "link" should exist
And "Set status for all users to «Bad»" "link" should exist
@ -204,7 +204,7 @@ Feature: Test the various new features in the attendance module
And I click on "submitbutton" "button"
And I click on "Take attendance" "link"
When I click on "setallstatuses" "field" in the ".takelist tbody td.c3" "css_element"
When I click on "setallstatuses" "field" in the ".takelist tbody td.c4" "css_element"
And I press "Save attendance"
And I follow "Report"
Then "L" "text" should exist in the "Student 1" "table_row"

2
tests/behat/preferences.feature

@ -23,7 +23,7 @@ Feature: Teachers can't change status variables to have empty acronyms or descri
And I add a "Attendance" to section "1" and I fill the form with:
| Name | Attendancepreftest |
And I follow "Attendancepreftest"
And I follow "Settings"
And I follow "Status set"
@javascript
Scenario: Teachers can add status variables

488
tests/behat/report.feature

@ -1,242 +1,250 @@
@javascript @mod @uon @mod_attendance
Feature: Visiting reports
As a teacher I visit the reports
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category | timecreated | timemodified |
| Course 1 | C1 | Prove the attendance activity works | 0 | ##yesterday## | ##yesterday## |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | department | institution |
| student1 | Student | 1 | student1@asd.com | 1234 | computer science | University of Nottingham |
| teacher1 | Teacher | 1 | teacher1@asd.com | 5678 | computer science | University of Nottingham |
And the following "course enrolments" exist:
| course | user | role | timestart |
| C1 | student1 | student | ##yesterday## |
| C1 | teacher1 | editingteacher | ##yesterday## |
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 | Attendance |
And I follow "Attendance"
And I follow "Add a block"
And I follow "Administration"
And I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 01 |
| id_sestime_endhour | 02 |
And I click on "id_submitbutton" "button"
And I log out
Scenario: Teacher takes attendance
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Report"
Then "0 / 0" "text" should exist in the "Student 1" "table_row"
And "0.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "-" "text" should exist in the "Student 1" "table_row"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1am - 2am" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "25.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher changes the maximum points in the attendance settings
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1am - 2am" "table_row"
# Excused
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 70 |
And I press "Save and display"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "35.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher take attendance of group session
Given the following "groups" exist:
| course | name | idnumber |
| C1 | Group1 | Group1 |
And the following "group members" exist:
| group | user |
| Group1 | student1 |
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
| id_groupmode | Visible groups |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1am - 2am" "table_row"
# Excused
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
| id_sessiontype_1 | 1 |
| id_groups | Group1 |
And I click on "id_submitbutton" "button"
Then I should see "3am - 4am"
And "Group: Group1" "text" should exist in the "3am - 4am" "table_row"
When I click on "Take attendance" "link" in the "3am - 4am" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "3 / 4" "text" should exist in the "Student 1" "table_row"
And "75.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "37.50" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher visit summary report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "1am - 2am" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
Then I should see "3am - 4am"
When I click on "Take attendance" "link" in the "3am - 4am" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I should see "05:00 - 06:00"
When I follow "Report"
And I click on "Summary" "link" in the "All" "table_row"
Then "3 / 6" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
And "5 / 6" "text" should exist in the "Student 1" "table_row"
And "83.3%" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Student visit user report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "1am - 2am" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
When I click on "Take attendance" "link" in the "3am - 4am" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I log out
When I log in as "student1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "All"
Then "2" "text" should exist in the "Taken sessions" "table_row"
And "3 / 4" "text" should exist in the "Points over taken sessions:" "table_row"
And "75.0%" "text" should exist in the "Percentage over taken sessions:" "table_row"
And "3" "text" should exist in the "Total number of sessions:" "table_row"
And "3 / 6" "text" should exist in the "Points over all sessions:" "table_row"
And "50.0%" "text" should exist in the "Percentage over all sessions:" "table_row"
And "5 / 6" "text" should exist in the "Maximum possible points:" "table_row"
And "83.3%" "text" should exist in the "Maximum possible percentage:" "table_row"
And I log out
As a teacher I visit the reports
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category | timecreated | timemodified |
| Course 1 | C1 | Prove the attendance activity works | 0 | ##yesterday## | ##yesterday## |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | department | institution |
| student1 | Student | 1 | student1@asd.com | 1234 | computer science | University of Nottingham |
| teacher1 | Teacher | 1 | teacher1@asd.com | 5678 | computer science | University of Nottingham |
And the following "course enrolments" exist:
| course | user | role | timestart |
| C1 | student1 | student | ##yesterday## |
| C1 | teacher1 | editingteacher | ##yesterday## |
And the following config values are set as admin:
| enablewarnings | 1 | attendance |
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 | Attendance |
And I follow "Attendance"
And I follow "Add a block"
And I follow "Administration"
And I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 01 |
| id_sestime_endhour | 02 |
And I click on "id_submitbutton" "button"
And I follow "Warnings set"
And I press "Add warning"
And I set the following fields to these values:
| id_warningpercent | 84 |
| id_warnafter | 2 |
And I click on "id_submitbutton" "button"
And I log out
Scenario: Teacher takes attendance
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Report"
Then "0 / 0" "text" should exist in the "Student 1" "table_row"
And "0.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "-" "text" should exist in the "Student 1" "table_row"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1AM - 2AM" "table_row"
# Late
And I click on "td.c4 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "25.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher changes the maximum points in the attendance settings
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1AM - 2AM" "table_row"
# Excused
And I click on "td.c4 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 70 |
And I press "Save and display"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "35.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher take attendance of group session
Given the following "groups" exist:
| course | name | idnumber |
| C1 | Group1 | Group1 |
And the following "group members" exist:
| group | user |
| Group1 | student1 |
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
| id_groupmode | Visible groups |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "1AM - 2AM" "table_row"
# Excused
And I click on "td.c4 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
| id_sessiontype_1 | 1 |
| id_groups | Group1 |
And I click on "id_submitbutton" "button"
Then I should see "3AM - 4AM"
And "Group: Group1" "text" should exist in the "3AM - 4AM" "table_row"
When I click on "Take attendance" "link" in the "3AM - 4AM" "table_row"
# Present
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "3 / 4" "text" should exist in the "Student 1" "table_row"
And "75.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "37.50" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher visit summary report and absentee report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "1AM - 2AM" "table_row"
# Late
And I click on "td.c4 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
Then I should see "3AM - 4AM"
When I click on "Take attendance" "link" in the "3AM - 4AM" "table_row"
# Present
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I should see "5AM - 6AM"
When I follow "Report"
And I click on "Summary" "link" in the "All" "table_row"
Then "3 / 6" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
And "5 / 6" "text" should exist in the "Student 1" "table_row"
And "83.3%" "text" should exist in the "Student 1" "table_row"
And I follow "Absentee report"
And I should see "Student 1"
And I log out
Scenario: Student visit user report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "1AM - 2AM" "table_row"
# Late
And I click on "td.c4 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
When I click on "Take attendance" "link" in the "3AM - 4AM" "table_row"
# Present
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I log out
When I log in as "student1"
And I follow "Course 1"
And I follow "Attendance"
And I click on "All" "link" in the ".attfiltercontrols" "css_element"
Then "2" "text" should exist in the "Taken sessions" "table_row"
And "3 / 4" "text" should exist in the "Points over taken sessions:" "table_row"
And "75.0%" "text" should exist in the "Percentage over taken sessions:" "table_row"
And "3" "text" should exist in the "Total number of sessions:" "table_row"
And "3 / 6" "text" should exist in the "Points over all sessions:" "table_row"
And "50.0%" "text" should exist in the "Percentage over all sessions:" "table_row"
And "5 / 6" "text" should exist in the "Maximum possible points:" "table_row"
And "83.3%" "text" should exist in the "Maximum possible percentage:" "table_row"
And I log out

9
tests/generator/lib.php

@ -25,7 +25,14 @@
defined('MOODLE_INTERNAL') || die();
/**
* mod_attendance data generator
*
* @package mod_attendance
* @category test
* @copyright 2013 Davo Smith, Synergy Learning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_attendance_generator extends testing_module_generator {
/**

116
update_form.php

@ -49,6 +49,7 @@ class mod_attendance_update_form extends moodleform {
if (!$sess = $DB->get_record('attendance_sessions', array('id' => $sessionid) )) {
error('No such session in this course');
}
$attendancesubnet = $DB->get_field('attendance', 'subnet', array('id' => $sess->attendanceid));
$defopts = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'noclean' => true, 'context' => $modcontext);
$sess = file_prepare_standard_editor($sess, 'description', $defopts, $modcontext, 'mod_attendance', 'session', $sess->id);
@ -64,7 +65,21 @@ class mod_attendance_update_form extends moodleform {
$data = array('sessiondate' => $sess->sessdate,
'sestime' => array('starthour' => $starthour, 'startminute' => $startminute,
'endhour' => $endhour, 'endminute' => $endminute),
'sdescription' => $sess->description_editor);
'sdescription' => $sess->description_editor,
'studentscanmark' => $sess->studentscanmark,
'studentpassword' => $sess->studentpassword,
'autoassignstatus' => $sess->autoassignstatus,
'subnet' => $sess->subnet,
'automark' => $sess->automark,
'absenteereport' => $sess->absenteereport,
'automarkcompleted' => 0,
'preventsharedip' => $sess->preventsharedip,
'preventsharediptime' => $sess->preventsharediptime);
if ($sess->subnet == $attendancesubnet) {
$data['usedefaultsubnet'] = 1;
} else {
$data['usedefaultsubnet'] = 0;
}
$mform->addElement('header', 'general', get_string('changesession', 'attendance'));
@ -86,12 +101,92 @@ class mod_attendance_update_form extends moodleform {
if ($maxstatusset > 0) {
$mform->addElement('static', 'statusset', get_string('usestatusset', 'mod_attendance'),
attendance_get_setname($this->_customdata['att']->id, $sess->statusset));
} else {
$mform->addElement('hidden', 'statusset', $maxstatusset);
$mform->setType('statusset', PARAM_INT);
}
$mform->addElement('editor', 'sdescription', get_string('description', 'attendance'),
array('rows' => 1, 'columns' => 80), $defopts);
$mform->setType('sdescription', PARAM_RAW);
// If warnings allow selector for reporting.
if (!empty(get_config('attendance', 'enablewarnings'))) {
$mform->addElement('checkbox', 'absenteereport', '', get_string('includeabsentee', 'attendance'));
$mform->addHelpButton('absenteereport', 'includeabsentee', 'attendance');
}
// Students can mark own attendance.
if (!empty(get_config('attendance', 'studentscanmark'))) {
$mform->addElement('header', 'headerstudentmarking', get_string('studentmarking', 'attendance'), true);
$mform->setExpanded('headerstudentmarking');
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
$options2 = attendance_get_automarkoptions();
$mform->addElement('select', 'automark', get_string('automark', 'attendance'), $options2);
$mform->setType('automark', PARAM_INT);
$mform->addHelpButton('automark', 'automark', 'attendance');
$mform->disabledif('automark', 'studentscanmark', 'notchecked');
$mform->addElement('text', 'studentpassword', get_string('studentpassword', 'attendance'));
$mform->setType('studentpassword', PARAM_TEXT);
$mform->addHelpButton('studentpassword', 'passwordgrp', 'attendance');
$mform->disabledif('studentpassword', 'studentscanmark', 'notchecked');
$mform->disabledif('studentpassword', 'automark', 'eq', ATTENDANCE_AUTOMARK_ALL);
$mform->disabledif('randompassword', 'automark', 'eq', ATTENDANCE_AUTOMARK_ALL);
$mform->addElement('checkbox', 'autoassignstatus', '', get_string('autoassignstatus', 'attendance'));
$mform->addHelpButton('autoassignstatus', 'autoassignstatus', 'attendance');
$mform->disabledif('autoassignstatus', 'studentscanmark', 'notchecked');
$mgroup = array();
$mgroup[] = & $mform->createElement('text', 'subnet', get_string('requiresubnet', 'attendance'));
$mform->setDefault('subnet', $this->_customdata['att']->subnet);
$mgroup[] = & $mform->createElement('checkbox', 'usedefaultsubnet', get_string('usedefaultsubnet', 'attendance'));
$mform->setDefault('usedefaultsubnet', 1);
$mform->setType('subnet', PARAM_TEXT);
$mform->addGroup($mgroup, 'subnetgrp', get_string('requiresubnet', 'attendance'), array(' '), false);
$mform->setAdvanced('subnetgrp');
$mform->addHelpButton('subnetgrp', 'requiresubnet', 'attendance');
$mform->disabledif('usedefaultsubnet', 'studentscanmark', 'notchecked');
$mform->disabledif('subnet', 'studentscanmark', 'notchecked');
$mform->disabledif('subnet', 'usedefaultsubnet', 'checked');
$mform->addElement('hidden', 'automarkcompleted', '0');
$mform->settype('automarkcompleted', PARAM_INT);
$mgroup3 = array();
$mgroup3[] = & $mform->createElement('checkbox', 'preventsharedip', '');
$mgroup3[] = & $mform->createElement('text', 'preventsharediptime',
get_string('preventsharediptime', 'attendance'), '', 'test');
$mgroup3[] = & $mform->createElement('static', 'preventsharediptimedesc', '',
get_string('preventsharedipminutes', 'attendance'));
$mform->addGroup($mgroup3, 'preventsharedgroup',
get_string('preventsharedip', 'attendance'), array(' '), false);
$mform->addHelpButton('preventsharedgroup', 'preventsharedip', 'attendance');
$mform->setAdvanced('preventsharedgroup');
$mform->setType('preventsharediptime', PARAM_INT);
$mform->disabledif('preventsharedgroup', 'studentscanmark', 'notchecked');
$mform->disabledif('preventsharedip', 'studentscanmark', 'notchecked');
$mform->disabledif('preventsharediptime', 'studentscanmark', 'notchecked');
$mform->disabledIf('preventsharediptime', 'preventsharedip', 'notchecked');
} else {
$mform->addElement('hidden', 'studentscanmark', '0');
$mform->settype('studentscanmark', PARAM_INT);
$mform->addElement('hidden', 'subnet', '0');
$mform->settype('subnet', PARAM_TEXT);
$mform->addElement('hidden', 'automark', '0');
$mform->settype('automark', PARAM_INT);
$mform->addElement('hidden', 'automarkcompleted', '0');
$mform->settype('automarkcompleted', PARAM_INT);
$mform->addElement('hidden', 'autoassignstatus', '0');
$mform->setType('autoassignstatus', PARAM_INT);
}
$mform->setDefaults($data);
$this->add_action_buttons(true);
@ -103,6 +198,7 @@ class mod_attendance_update_form extends moodleform {
* @param array $files
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
$sesstarttime = $data['sestime']['starthour'] * HOURSECS + $data['sestime']['startminute'] * MINSECS;
@ -111,6 +207,24 @@ class mod_attendance_update_form extends moodleform {
$errors['sestime'] = get_string('invalidsessionendtime', 'attendance');
}
if (!empty($data['studentscanmark']) && $data['automark'] == ATTENDANCE_AUTOMARK_CLOSE) {
$cm = $this->_customdata['cm'];
// Check that the selected statusset has a status to use when unmarked.
$sql = 'SELECT id
FROM {attendance_statuses}
WHERE deleted = 0 AND (attendanceid = 0 or attendanceid = ?)
AND setnumber = ? AND setunmarked = 1';
$params = array($cm->instance, $data['statusset']);
if (!$DB->record_exists_sql($sql, $params)) {
$errors['automark'] = get_string('noabsentstatusset', 'attendance');
}
}
if (!empty($data['studentscanmark']) && !empty($data['preventsharedip']) &&
empty($data['preventsharediptime'])) {
$errors['preventsharedgroup'] = get_string('iptimemissing', 'attendance');
}
return $errors;
}
}

4
version.php

@ -23,9 +23,9 @@
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2016121300;
$plugin->version = 2016121330;
$plugin->requires = 2016111800;
$plugin->release = '3.2.2';
$plugin->release = '3.2.20';
$plugin->maturity = MATURITY_STABLE;
$plugin->cron = 0;
$plugin->component = 'mod_attendance';

2
view.php

@ -78,9 +78,11 @@ if (isset($pageparams->studentid) && $USER->id != $pageparams->studentid) {
}
$userdata = new attendance_user_data($att, $userid);
$header = new mod_attendance_header($att);
echo $output->header();
echo $output->render($header);
echo $output->render($userdata);
echo $output->footer();

203
warnings.php

@ -0,0 +1,203 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Allows default warnings to be modified.
*
* @package mod_attendance
* @copyright 2017 Dan Marsden http://danmarsden.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->libdir.'/formslib.php');
require_once($CFG->dirroot.'/mod/attendance/lib.php');
require_once($CFG->dirroot.'/mod/attendance/locallib.php');
$action = optional_param('action', '', PARAM_ALPHA);
$notid = optional_param('notid', 0, PARAM_INT);
$id = optional_param('id', 0, PARAM_INT);
$url = new moodle_url('/mod/attendance/warnings.php');
// This page is used for configuring default set and for configuring attendance level set.
if (empty($id)) {
// This is the default status set - show appropriate admin stuff and check admin permissions.
admin_externalpage_setup('managemodules');
$output = $PAGE->get_renderer('mod_attendance');
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('defaultwarnings', 'mod_attendance'));
$tabmenu = attendance_print_settings_tabs('defaultwarnings');
echo $tabmenu;
} else {
// This is an attendance level config.
$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);
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/attendance:changepreferences', $context);
$att = new mod_attendance_structure($att, $cm, $course, $PAGE->context);
$PAGE->set_url($url);
$PAGE->set_title($course->shortname. ": ".$att->name);
$PAGE->set_heading($course->fullname);
$PAGE->navbar->add($att->name);
$output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_WARNINGS);
echo $output->header();
echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: ' .format_string($course->fullname));
echo $output->render($tabs);
}
$mform = new mod_attendance_add_warning_form($url, array('notid' => $notid, 'id' => $id));
if ($data = $mform->get_data()) {
if (empty($data->notid)) {
// Insert new record.
$notify = new stdClass();
if (empty($id)) {
$notify->idnumber = 0;
} else {
$notify->idnumber = $att->id;
}
$notify->warningpercent = $data->warningpercent;
$notify->warnafter = $data->warnafter;
$notify->maxwarn = $data->maxwarn;
$notify->emailuser = empty($data->emailuser) ? 0 : $data->emailuser;
$notify->emailsubject = $data->emailsubject;
$notify->emailcontent = $data->emailcontent['text'];
$notify->emailcontentformat = $data->emailcontent['format'];
$notify->thirdpartyemails = '';
if (!empty($data->thirdpartyemails)) {
$notify->thirdpartyemails = implode(',', $data->thirdpartyemails);
}
$existingrecord = $DB->record_exists('attendance_warning', array('idnumber' => $notify->idnumber,
'warningpercent' => $notify->warningpercent,
'warnafter' => $notify->warnafter));
if (empty($existingrecord)) {
$DB->insert_record('attendance_warning', $notify);
echo $OUTPUT->notification(get_string('warningupdated', 'mod_attendance'), 'success');
} else {
echo $OUTPUT->notification(get_string('warningfailed', 'mod_attendance'), 'warning');
}
} else {
$notify = $DB->get_record('attendance_warning', array('id' => $data->notid));
if (!empty($id) && $data->idnumber != $att->id) {
// Someone is trying to update a record for a different attendance.
print_error('invalidcoursemodule');
} else {
$notify = new stdClass();
$notify->id = $data->notid;
$notify->idnumber = $data->idnumber;
$notify->warningpercent = $data->warningpercent;
$notify->warnafter = $data->warnafter;
$notify->maxwarn = $data->maxwarn;
$notify->emailuser = empty($data->emailuser) ? 0 : $data->emailuser;
$notify->emailsubject = $data->emailsubject;
$notify->emailcontentformat = $data->emailcontent['format'];
$notify->emailcontent = $data->emailcontent['text'];
$notify->thirdpartyemails = '';
if (!empty($data->thirdpartyemails)) {
$notify->thirdpartyemails = implode(',', $data->thirdpartyemails);
}
$existingrecord = $DB->get_record('attendance_warning', array('idnumber' => $notify->idnumber,
'warningpercent' => $notify->warningpercent, 'warnafter' => $notify->warnafter));
if (empty($existingrecord) || $existingrecord->id == $notify->id) {
$DB->update_record('attendance_warning', $notify);
echo $OUTPUT->notification(get_string('warningupdated', 'mod_attendance'), 'success');
} else {
echo $OUTPUT->notification(get_string('warningfailed', 'mod_attendance'), 'error');
}
}
}
}
if ($action == 'delete' && !empty($notid)) {
if (!optional_param('confirm', false, PARAM_BOOL)) {
$cancelurl = $url;
$url->params(array('action' => 'delete', 'notid' => $notid, 'sesskey' => sesskey(), 'confirm' => true, 'id' => $id));
echo $OUTPUT->confirm(get_string('deletewarningconfirm', 'mod_attendance'), $url, $cancelurl);
echo $OUTPUT->footer();
exit;
} else {
require_sesskey();
$params = array('id' => $notid);
if (!empty($att)) {
// Add id/level to array.
$params['idnumber'] = $att->id;
}
$DB->delete_records('attendance_warning', $params);
echo $OUTPUT->notification(get_string('warningdeleted', 'mod_attendance'), 'success');
}
}
if ($action == 'update' && !empty($notid)) {
$existing = $DB->get_record('attendance_warning', array('id' => $notid));
$content = $existing->emailcontent;
$existing->emailcontent = array();
$existing->emailcontent['text'] = $content;
$existing->emailcontent['format'] = $existing->emailcontentformat;
$existing->notid = $existing->id;
$existing->id = $id;
$mform->set_data($existing);
$mform->display();
} else if ($action == 'add' && confirm_sesskey()) {
$mform->display();
} else {
if (empty($id)) {
$warningdesc = get_string('warningdesc', 'mod_attendance');
$idnumber = 0;
} else {
$warningdesc = get_string('warningdesc_course', 'mod_attendance');
$idnumber = $att->id;
}
echo $OUTPUT->box($warningdesc, 'generalbox attendancedesc', 'notice');
$existingnotifications = $DB->get_records('attendance_warning',
array('idnumber' => $idnumber),
'warningpercent');
if (!empty($existingnotifications)) {
$table = new html_table();
$table->head = array(get_string('warningthreshold', 'mod_attendance'),
get_string('numsessions', 'mod_attendance'),
get_string('emailsubject', 'mod_attendance'),
'');
foreach ($existingnotifications as $notification) {
$url->params(array('action' => 'delete', 'notid' => $notification->id, 'id' => $id));
$actionbuttons = $OUTPUT->action_icon($url, new pix_icon('t/delete',
get_string('delete', 'attendance')), null, null);
$url->params(array('action' => 'update', 'notid' => $notification->id, 'id' => $id));
$actionbuttons .= $OUTPUT->action_icon($url, new pix_icon('t/edit',
get_string('update', 'attendance')), null, null);
$table->data[] = array($notification->warningpercent, $notification->warnafter,
$notification->emailsubject, $actionbuttons);
}
echo html_writer::table($table);
}
$addurl = new moodle_url('/mod/attendance/warnings.php', array('action' => 'add', 'id' => $id));
echo $OUTPUT->single_button($addurl, get_string('addwarning', 'mod_attendance'));
}
echo $OUTPUT->footer();

5
yui/build/moodle-mod_attendance-groupfilter/moodle-mod_attendance-groupfilter-debug.js

@ -1,6 +1,7 @@
YUI.add('moodle-mod_attendance-groupfilter', function (Y, NAME) {
/*global M*/
/* global M */
// eslint-disable-next-line camelcase
M.mod_attendance = M.mod_attendance || {};
M.mod_attendance.groupfilter = {
groupmappings: null,
@ -15,7 +16,7 @@ M.mod_attendance.groupfilter = {
/**
* Update the user list with those found in the selected group.
*/
update_user_list: function() {
update_user_list: function() { // eslint-disable-line camelcase
"use strict";
var groupid, userlist, users, userid, opt;

5
yui/build/moodle-mod_attendance-groupfilter/moodle-mod_attendance-groupfilter.js

@ -1,6 +1,7 @@
YUI.add('moodle-mod_attendance-groupfilter', function (Y, NAME) {
/*global M*/
/* global M */
// eslint-disable-next-line camelcase
M.mod_attendance = M.mod_attendance || {};
M.mod_attendance.groupfilter = {
groupmappings: null,
@ -15,7 +16,7 @@ M.mod_attendance.groupfilter = {
/**
* Update the user list with those found in the selected group.
*/
update_user_list: function() {
update_user_list: function() { // eslint-disable-line camelcase
"use strict";
var groupid, userlist, users, userid, opt;

5
yui/src/groupfilter/js/groupfilter.js

@ -1,4 +1,5 @@
/*global M*/
/* global M */
// eslint-disable-next-line camelcase
M.mod_attendance = M.mod_attendance || {};
M.mod_attendance.groupfilter = {
groupmappings: null,
@ -13,7 +14,7 @@ M.mod_attendance.groupfilter = {
/**
* Update the user list with those found in the selected group.
*/
update_user_list: function() {
update_user_list: function() { // eslint-disable-line camelcase
"use strict";
var groupid, userlist, users, userid, opt;

Loading…
Cancel
Save