Browse Source

Merge notification/warning feature into master. (#267)

New feature/reports to allow warning thresholds to be set and e-mail notifications.
MOODLE_34_STABLE
Dan Marsden 8 years ago
committed by GitHub
parent
commit
00f95df6fb
  1. 1
      CHANGELOG.md
  2. 141
      atrisk.php
  3. 10
      backup/moodle2/backup_attendance_stepslib.php
  4. 18
      backup/moodle2/restore_attendance_stepslib.php
  5. 108
      classes/add_warning_form.php
  6. 24
      classes/structure.php
  7. 122
      classes/task/notify.php
  8. 33
      db/install.xml
  9. 8
      db/tasks.php
  10. 57
      db/upgrade.php
  11. 52
      lang/en/attendance.php
  12. 35
      lib.php
  13. 111
      locallib.php
  14. 16
      renderables.php
  15. 35
      settings.php
  16. 13
      tests/behat/report.feature
  17. 2
      version.php
  18. 199
      warnings.php

1
CHANGELOG.md

@ -1,5 +1,6 @@
### [Unreleased] ### [Unreleased]
- New Feature: Allow automatic marking using site logs. - 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: Allow default view for teachers to be set at admin level.
- Improvement: All courses user report now displays as table. - Improvement: All courses user report now displays as table.
- Bug fix: Restored attendances do not create calendar events correctly. - Bug fix: Restored attendances do not create calendar events correctly.

141
atrisk.php

@ -0,0 +1,141 @@
<?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->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', '', 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 {
$context = context_system::instance();
$courses = array(); // Show all courses.
$PAGE->set_context($context);
require_login();
}
// Check permissions.
require_capability('mod/attendance:viewreports', $context);
$exportfilename = 'attendanceatrisk.csv';
$PAGE->set_url('/mod/attendance/atrisk.php', array('category' => $category));
$PAGE->set_heading($SITE->fullname);
$table = new flexible_table('attendanceatrisk');
$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_ATRISK);
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('atriskreport', 'mod_attendance'));
if (empty($category)) {
// Only show tabs if displaying via the admin page.
$tabmenu = attendance_print_settings_tabs('atrisk');
echo $tabmenu;
}
}
}
$table->define_columns(array('course', 'attendance', '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->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 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();
}

10
backup/moodle2/backup_attendance_stepslib.php

@ -50,6 +50,10 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
$status = new backup_nested_element('status', array('id'), array( $status = new backup_nested_element('status', array('id'), array(
'acronym', 'description', 'grade', 'studentavailability', 'setunmarked', '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', 'emailuser', 'emailsubject', 'emailcontent', 'emailcontentformat', 'thirdpartyemails'));
$sessions = new backup_nested_element('sessions'); $sessions = new backup_nested_element('sessions');
$session = new backup_nested_element('session', array('id'), array( $session = new backup_nested_element('session', array('id'), array(
'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby', 'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby',
@ -65,6 +69,9 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
$attendance->add_child($statuses); $attendance->add_child($statuses);
$statuses->add_child($status); $statuses->add_child($status);
$attendance->add_child($warnings);
$warnings->add_child($warning);
$attendance->add_child($sessions); $attendance->add_child($sessions);
$sessions->add_child($session); $sessions->add_child($session);
@ -77,6 +84,9 @@ class backup_attendance_activity_structure_step extends backup_activity_structur
$status->set_source_table('attendance_statuses', array('attendanceid' => backup::VAR_PARENTID)); $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)); $session->set_source_table('attendance_sessions', array('attendanceid' => backup::VAR_PARENTID));
// Data sources - user related data. // Data sources - user related data.

18
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', $paths[] = new restore_path_element('attendance_status',
'/activity/attendance/statuses/status'); '/activity/attendance/statuses/status');
$paths[] = new restore_path_element('attendance_warning',
'/activity/attendance/warnings/warning');
$paths[] = new restore_path_element('attendance_session', $paths[] = new restore_path_element('attendance_session',
'/activity/attendance/sessions/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); $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 * Process attendance session restore
* @param object $data The data in object form * @param object $data The data in object form

108
classes/add_warning_form.php

@ -0,0 +1,108 @@
<?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 <= 50; $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('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:viewreports');
$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);
}
}

24
classes/structure.php

@ -368,6 +368,16 @@ class mod_attendance_structure {
return new moodle_url('/mod/attendance/report.php', $params); return new moodle_url('/mod/attendance/report.php', $params);
} }
/**
* Get url for report.
* @param array $params
* @return moodle_url of report.php for attendance instance
*/
public function url_atrisk($params=array()) {
$params = array_merge(array('id' => $this->cm->id), $params);
return new moodle_url('/mod/attendance/atrisk.php', $params);
}
/** /**
* Get url for export. * Get url for export.
* *
@ -392,6 +402,20 @@ class mod_attendance_structure {
return new moodle_url('/mod/attendance/preferences.php', $params); return new moodle_url('/mod/attendance/preferences.php', $params);
} }
/**
* 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. * Get take url.
* @param array $params * @param array $params

122
classes/task/notify.php

@ -0,0 +1,122 @@
<?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 {
public function get_name() {
// Shown in admin screens.
return get_string('notifytask', 'mod_attendance');
}
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.
$lastrun = get_config('mod_attendance', 'notifylastrun');
if (empty($lastrun)) {
$lastrun = 0;
}
$orderby = 'ORDER BY cm.id, atl.studentid, n.warningpercent ASC';
$records = attendance_get_users_to_notify(array(), $orderby, $lastrun, 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])) {
// Convert variables in emailcontent.
$record = attendance_template_variables($record);
$user = $DB->get_record('user', array('id' => $record->userid));
$from = \core_user::get_noreply_user();
$emailcontent = format_text($record->emailcontent, $record->emailcontentformat);
email_to_user($user, $from, $record->emailsubject, $emailcontent, $emailcontent);
$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)."%";
foreach ($sendto as $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);
}
}
}
$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();
$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);
$numsentthird++;
}
if (!empty($numsentthird)) {
mtrace($numsentthird ." thirdparty emails sent");
}
}
set_config('notifylastrun', $now, 'mod_attendance');
}
}

33
db/install.xml

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/attendance/db" VERSION="20170511" 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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
> >
@ -113,5 +113,36 @@
<INDEX NAME="studentid" UNIQUE="true" FIELDS="studentid"/> <INDEX NAME="studentid" UNIQUE="true" FIELDS="studentid"/>
</INDEXES> </INDEXES>
</TABLE> </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="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"/>
</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="true" FIELDS="notifyid, userid"/>
</INDEXES>
</TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

8
db/tasks.php

@ -32,5 +32,13 @@ $tasks = array(
'hour' => '*', 'hour' => '*',
'day' => '*', 'day' => '*',
'dayofweek' => '*', 'dayofweek' => '*',
'month' => '*'),
array(
'classname' => 'mod_attendance\task\notify',
'blocking' => 0,
'minute' => '30',
'hour' => '1',
'day' => '*',
'dayofweek' => '*',
'month' => '*') 'month' => '*')
); );

57
db/upgrade.php

@ -340,5 +340,62 @@ function xmldb_attendance_upgrade($oldversion=0) {
upgrade_mod_savepoint(true, 2017060900, 'attendance'); upgrade_mod_savepoint(true, 2017060900, 'attendance');
} }
// Add new warning table.
if ($oldversion < 2017061600) {
// Define table attendance_warning to be created.
$table = new xmldb_table('attendance_warning');
// 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'));
// Conditionally launch create table for attendance_warning.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Attendance savepoint reached.
upgrade_mod_savepoint(true, 2017061600, 'attendance');
}
if ($oldversion < 2017062000) {
// 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, 2017062000, 'attendance');
}
return $result; return $result;
} }

52
lang/en/attendance.php

@ -33,12 +33,14 @@ $string['Pfull'] = 'Present';
$string['acronym'] = 'Acronym'; $string['acronym'] = 'Acronym';
$string['add'] = 'Add'; $string['add'] = 'Add';
$string['addmultiplesessions'] = 'Multiple sessions'; $string['addmultiplesessions'] = 'Multiple sessions';
$string['addwarning'] = 'Add warning';
$string['addsession'] = 'Add session'; $string['addsession'] = 'Add session';
$string['adduser'] = 'Add user'; $string['adduser'] = 'Add user';
$string['all'] = 'All'; $string['all'] = 'All';
$string['allcourses'] = 'All courses'; $string['allcourses'] = 'All courses';
$string['allpast'] = 'All past'; $string['allpast'] = 'All past';
$string['allsessions'] = 'All sessions'; $string['allsessions'] = 'All sessions';
$string['atriskreport'] = 'At-risk report';
$string['attendance:addinstance'] = 'Add a new attendance activity'; $string['attendance:addinstance'] = 'Add a new attendance activity';
$string['attendance:canbelisted'] = 'Appears in the roster'; $string['attendance:canbelisted'] = 'Appears in the roster';
$string['attendance:changeattendances'] = 'Changing Attendances'; $string['attendance:changeattendances'] = 'Changing Attendances';
@ -104,6 +106,9 @@ The sessions begin on the date of the base session and continue until the \'repe
$string['createonesession'] = 'Create one session for the course'; $string['createonesession'] = 'Create one session for the course';
$string['days'] = 'Days'; $string['days'] = 'Days';
$string['defaultdisplaymode'] = 'Default display mode'; $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['defaults'] = 'Defaults';
$string['defaultsessionsettings'] = 'Default session settings'; $string['defaultsessionsettings'] = 'Default session settings';
$string['defaultsessionsettings_help'] = 'These settings define the defaults for all new sessions'; $string['defaultsessionsettings_help'] = 'These settings define the defaults for all new sessions';
@ -115,6 +120,7 @@ $string['defaultsubnet_help'] = 'Attendance recording may be restricted to parti
$string['defaultview'] = 'Default view on login'; $string['defaultview'] = 'Default view on login';
$string['defaultview_desc'] = 'This is the default view shown to teachers on first login.'; $string['defaultview_desc'] = 'This is the default view shown to teachers on first login.';
$string['delete'] = 'Delete'; $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['deletedgroup'] = 'The group associated with this session has been deleted';
$string['deletehiddensessions'] = 'Delete all hidden sessions'; $string['deletehiddensessions'] = 'Delete all hidden sessions';
$string['deletelogs'] = 'Delete attendance data'; $string['deletelogs'] = 'Delete attendance data';
@ -134,8 +140,35 @@ $string['downloadtext'] = 'Download in text format';
$string['duration'] = 'Duration'; $string['duration'] = 'Duration';
$string['editsession'] = 'Edit Session'; $string['editsession'] = 'Edit Session';
$string['edituser'] = 'Edit user'; $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>%precent%</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['emptyacronym'] = 'Empty acronyms are not allowed. Status record not updated.';
$string['emptydescription'] = 'Empty descriptions are not allowed. Status record not updated.'; $string['emptydescription'] = 'Empty descriptions are not allowed. Status record not updated.';
$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['endofperiod'] = 'End of period'; $string['endofperiod'] = 'End of period';
$string['endtime'] = 'Session end time'; $string['endtime'] = 'Session end time';
$string['enrolmentend'] = 'User enrolment ends {$a}'; $string['enrolmentend'] = 'User enrolment ends {$a}';
@ -175,6 +208,7 @@ $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['incorrectpassword'] = 'You have entered an incorrect password and your attendance has not been recorded, please enter the correct password.';
$string['indetail'] = 'In detail...'; $string['indetail'] = 'In detail...';
$string['invalidaction'] = 'You must select an action'; $string['invalidaction'] = 'You must select an action';
$string['invalidemails'] = 'You must specify addresses of existing user accounts, could not find: {$a}';
$string['invalidsessionenddate'] = 'This date can not be earlier than the session date'; $string['invalidsessionenddate'] = 'This date can not be earlier than the session date';
$string['invalidsessionendtime'] = 'The end time must be greater than start time'; $string['invalidsessionendtime'] = 'The end time must be greater than start time';
$string['invalidstatus'] = 'You have selected an invalid status, please try again'; $string['invalidstatus'] = 'You have selected an invalid status, please try again';
@ -217,9 +251,15 @@ $string['noofdayspresent'] = 'No of days present';
$string['nosessiondayselected'] = 'No Session day selected'; $string['nosessiondayselected'] = 'No Session day selected';
$string['nosessionexists'] = 'No Session exists for this course'; $string['nosessionexists'] = 'No Session exists for this course';
$string['nosessionsselected'] = 'No sessions selected'; $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['warnings'] = 'Warnings set';
$string['warningupdated'] = 'Updated warnings';
$string['notifytask'] = 'Send warnings to users';
$string['notfound'] = 'Attendance activity not found in this course!'; $string['notfound'] = 'Attendance activity not found in this course!';
$string['notmember'] = 'not&nbsp;member'; $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['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['olddate'] = 'Old date';
$string['onlyselectedusers'] = 'Export specific users'; $string['onlyselectedusers'] = 'Export specific users';
$string['overallsessions'] = 'Over all sessions'; $string['overallsessions'] = 'Over all sessions';
@ -384,10 +424,16 @@ $string['tempusermerge'] = 'Merge temporary user';
$string['tempusers'] = 'Temporary users'; $string['tempusers'] = 'Temporary users';
$string['tempusersedit'] = 'Edit temporary user'; $string['tempusersedit'] = 'Edit temporary user';
$string['tempuserslist'] = 'Temporary users'; $string['tempuserslist'] = 'Temporary users';
$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['thiscourse'] = 'This course';
$string['time'] = 'Time'; $string['time'] = 'Time';
$string['timeahead'] = 'Multiple sessions that exceed one year cannot be created, please adjust the start and end dates.'; $string['timeahead'] = 'Multiple sessions that exceed one year cannot be created, please adjust the start and end dates.';
$string['to'] = 'to:'; $string['to'] = 'to:';
$string['triggered'] = 'First triggered';
$string['tuseremail'] = 'Email'; $string['tuseremail'] = 'Email';
$string['tusername'] = 'Full name'; $string['tusername'] = 'Full name';
$string['unknowngroup'] = 'Unknown group'; $string['unknowngroup'] = 'Unknown group';
@ -400,6 +446,12 @@ $string['variable'] = 'variable';
$string['variablesupdated'] = 'Variables successfully updated'; $string['variablesupdated'] = 'Variables successfully updated';
$string['versionforprinting'] = 'version for printing'; $string['versionforprinting'] = 'version for printing';
$string['viewmode'] = 'View mode'; $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['week'] = 'week(s)';
$string['weeks'] = 'Weeks'; $string['weeks'] = 'Weeks';
$string['youcantdo'] = 'You can\'t do anything'; $string['youcantdo'] = 'You can\'t do anything';

35
lib.php

@ -69,6 +69,25 @@ function att_add_default_statuses($attid) {
$statuses->close(); $statuses->close();
} }
/**
* Add default set of warnings to the new attendance.
*
* @param int $attid - id of attendance instance.
*/
function attendance_add_default_warnings($cmid) {
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 = $cmid;
$DB->insert_record('attendance_warning', $rec);
}
$warnings->close();
}
/** /**
* Add new attendance instance. * Add new attendance instance.
* *
@ -84,6 +103,8 @@ function attendance_add_instance($attendance) {
att_add_default_statuses($attendance->id); att_add_default_statuses($attendance->id);
attendance_add_default_warnings($attendance->coursemodule);
attendance_grade_item_update($attendance); attendance_grade_item_update($attendance);
return $attendance->id; return $attendance->id;
@ -117,7 +138,8 @@ function attendance_update_instance($attendance) {
* @return bool * @return bool
*/ */
function attendance_delete_instance($id) { 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))) { if (! $attendance = $DB->get_record('attendance', array('id' => $id))) {
return false; return false;
@ -132,6 +154,8 @@ function attendance_delete_instance($id) {
} }
$DB->delete_records('attendance_statuses', array('attendanceid' => $id)); $DB->delete_records('attendance_statuses', array('attendanceid' => $id));
$DB->delete_records('attendance_warning', array('idnumber' => $id));
$DB->delete_records('attendance', array('id' => $id)); $DB->delete_records('attendance', array('id' => $id));
attendance_grade_item_delete($attendance); attendance_grade_item_delete($attendance);
@ -450,9 +474,18 @@ function attendance_print_settings_tabs($selected = 'settings') {
$tabs[] = new tabobject('defaultstatus', $CFG->wwwroot.'/mod/attendance/defaultstatus.php', $tabs[] = new tabobject('defaultstatus', $CFG->wwwroot.'/mod/attendance/defaultstatus.php',
get_string('defaultstatus', 'attendance'), get_string('defaultstatus', 'attendance'), false); 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', $tabs[] = new tabobject('coursesummary', $CFG->wwwroot.'/mod/attendance/coursesummary.php',
get_string('coursesummary', 'attendance'), get_string('coursesummary', 'attendance'), false); get_string('coursesummary', 'attendance'), get_string('coursesummary', 'attendance'), false);
if (get_config('attendance', 'enablewarnings')) {
$tabs[] = new tabobject('atrisk', $CFG->wwwroot . '/mod/attendance/atrisk.php',
get_string('atriskreport', 'attendance'), get_string('atriskreport', 'attendance'), false);
}
ob_start(); ob_start();
print_tabs(array($tabs), $selected); print_tabs(array($tabs), $selected);
$tabmenu = ob_get_contents(); $tabmenu = ob_get_contents();

111
locallib.php

@ -42,6 +42,7 @@ define('ATT_SORT_FIRSTNAME', 2);
define('ATTENDANCE_AUTOMARK_DISABLED', 0); define('ATTENDANCE_AUTOMARK_DISABLED', 0);
define('ATTENDANCE_AUTOMARK_ALL', 1); define('ATTENDANCE_AUTOMARK_ALL', 1);
define('ATTENDANCE_AUTOMARK_CLOSE', 2); define('ATTENDANCE_AUTOMARK_CLOSE', 2);
/** /**
* Get statuses, * Get statuses,
* *
@ -710,3 +711,113 @@ SELECT a.id, a.course as courseid, c.fullname as coursename, atl.studentid AS us
return $DB->get_records_sql($sql, $params); return $DB->get_records_sql($sql, $params);
} }
/**
* Generates a list of users flagged at-risk.
*
* @param array $courseids optional list of courses to return
* @param array $sincetime optional allows a list to be calculated for cron processing.
* @param bool $allfornotify get notification list for scheduled task.
* @return stdClass
*/
function attendance_get_users_to_notify($courseids = array(), $orderby = '', $sincetime = 0, $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)';
$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 been sent.
$where .= ' AND ns.id IS NULL ';
}
$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, ns.timesent, n.warnafter,
COUNT(DISTINCT ats.id) AS numtakensessions, SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints,
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 = cm.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.sessdate >= {$sincetime} {$where}
AND ats.lasttaken != 0
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.emailuser, n.thirdpartyemails, ns.timesent, cm.id, c.id, {$unames2}
HAVING n.warnafter <= COUNT(DISTINCT ats.id) AND n.warningpercent > ((SUM(stg.grade) / SUM(stm.maxgrade)) * 100)
{$orderby}";
if (!$allfornotify) {
$idfield = $DB->sql_concat('cmid', 'userid');
// Only show one record per attendance for teacher reports.
$sql = "SELECT {$idfield} as id, {$unames}, aid, cmid, courseid, aname, coursename, userid, MIN(warningpercent),
numtakensessions, points, maxpoints, percent, timesent
FROM ({$sql}) as m
GROUP BY id, aid, cmid, courseid, aname, userid, numtakensessions, points, maxpoints,
percent, coursename, timesent, {$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,
'/%precent%/' => $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;
}

16
renderables.php

@ -51,7 +51,10 @@ class attendance_tabs implements renderable {
const TAB_TEMPORARYUSERS = 6; // Tab for managing temporary users. const TAB_TEMPORARYUSERS = 6; // Tab for managing temporary users.
/** Update tab */ /** Update tab */
const TAB_UPDATE = 7; const TAB_UPDATE = 7;
/** Warnings tab */
const TAB_WARNINGS = 8;
/** At-risk tab */
const TAB_ATRISK = 9;
/** @var int current tab */ /** @var int current tab */
public $currenttab; public $currenttab;
@ -97,6 +100,12 @@ class attendance_tabs implements renderable {
get_string('report', 'attendance')); get_string('report', 'attendance'));
} }
if (has_capability('mod/attendance:viewreports', $context) &&
get_config('attendance', 'enablewarnings')) {
$toprow[] = new tabobject(self::TAB_ATRISK, $this->att->url_atrisk()->out(),
get_string('atriskreport', 'attendance'));
}
if (has_capability('mod/attendance:export', $context)) { if (has_capability('mod/attendance:export', $context)) {
$toprow[] = new tabobject(self::TAB_EXPORT, $this->att->url_export()->out(), $toprow[] = new tabobject(self::TAB_EXPORT, $this->att->url_export()->out(),
get_string('export', 'attendance')); get_string('export', 'attendance'));
@ -105,6 +114,11 @@ class attendance_tabs implements renderable {
if (has_capability('mod/attendance:changepreferences', $context)) { if (has_capability('mod/attendance:changepreferences', $context)) {
$toprow[] = new tabobject(self::TAB_PREFERENCES, $this->att->url_preferences()->out(), $toprow[] = new tabobject(self::TAB_PREFERENCES, $this->att->url_preferences()->out(),
get_string('statussetsettings', '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)) { if (has_capability('mod/attendance:managetemporaryusers', $context)) {
$toprow[] = new tabobject(self::TAB_TEMPORARYUSERS, $this->att->url_managetemp()->out(), $toprow[] = new tabobject(self::TAB_TEMPORARYUSERS, $this->att->url_managetemp()->out(),

35
settings.php

@ -74,6 +74,10 @@ if ($ADMIN->fulltree) {
get_string('defaultview', 'attendance'), get_string('defaultview', 'attendance'),
get_string('defaultview_desc', 'attendance'), ATT_VIEW_WEEKS, $options)); get_string('defaultview_desc', 'attendance'), ATT_VIEW_WEEKS, $options));
$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'); $name = new lang_string('defaultsettings', 'mod_attendance');
$description = new lang_string('defaultsettings_help', 'mod_attendance'); $description = new lang_string('defaultsettings_help', 'mod_attendance');
$settings->add(new admin_setting_heading('defaultsettings', $name, $description)); $settings->add(new admin_setting_heading('defaultsettings', $name, $description));
@ -98,4 +102,35 @@ if ($ADMIN->fulltree) {
$settings->add(new admin_setting_configcheckbox('attendance/randompassword_default', $settings->add(new admin_setting_configcheckbox('attendance/randompassword_default',
get_string('randompassword', 'attendance'), '', 0)); get_string('randompassword', 'attendance'), '', 0));
$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_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));
} }

13
tests/behat/report.feature

@ -14,6 +14,8 @@ Feature: Visiting reports
| course | user | role | timestart | | course | user | role | timestart |
| C1 | student1 | student | ##yesterday## | | C1 | student1 | student | ##yesterday## |
| C1 | teacher1 | editingteacher | ##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 log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on And I am on "Course 1" course homepage with editing mode on
@ -27,6 +29,12 @@ Feature: Visiting reports
| id_sestime_starthour | 01 | | id_sestime_starthour | 01 |
| id_sestime_endhour | 02 | | id_sestime_endhour | 02 |
And I click on "id_submitbutton" "button" 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 And I log out
Scenario: Teacher takes attendance Scenario: Teacher takes attendance
@ -144,7 +152,7 @@ Feature: Visiting reports
And I log out And I log out
Scenario: Teacher visit summary report Scenario: Teacher visit summary report and at-risk report
Given I log in as "teacher1" Given I log in as "teacher1"
And I am on "Course 1" course homepage And I am on "Course 1" course homepage
And I follow "Attendance" And I follow "Attendance"
@ -186,6 +194,9 @@ Feature: Visiting reports
And "5 / 6" "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 "83.3%" "text" should exist in the "Student 1" "table_row"
And I follow "At-risk report"
And I should see "Student 1"
And I log out And I log out
Scenario: Student visit user report Scenario: Student visit user report

2
version.php

@ -23,7 +23,7 @@
*/ */
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->version = 2017061400; $plugin->version = 2017062000;
$plugin->requires = 2017042100; $plugin->requires = 2017042100;
$plugin->release = '3.3.8'; $plugin->release = '3.3.8';
$plugin->maturity = MATURITY_ALPHA; $plugin->maturity = MATURITY_ALPHA;

199
warnings.php

@ -0,0 +1,199 @@
<?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 = $cm->id;
}
$notify->warningpercent = $data->warningpercent;
$notify->warnafter = $data->warnafter;
$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));
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 != $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->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));
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($id)) {
// Add id/level to array.
$params['idnumber'] = $cm->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)) {
echo $OUTPUT->box(get_string('warningdesc', 'mod_attendance'), 'generalbox', 'notice');
$existingnotifications = $DB->get_records('attendance_warning',
array('idnumber' => 0),
'warningpercent');
} else {
$existingnotifications = $DB->get_records('attendance_warning',
array('idnumber' => $cm->id),
'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();
Loading…
Cancel
Save