diff --git a/add_form.php b/add_form.php
index 9fc813a..53dc051 100644
--- a/add_form.php
+++ b/add_form.php
@@ -103,6 +103,10 @@ class mod_attendance_add_form extends moodleform {
$mform->addElement('checkbox', 'addmultiply', '', get_string('createmultiplesessions', 'attendance'));
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
+ // Studetns can mark own attendance.
+ $mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark','attforblock'));
+ $mform->addHelpButton('studentscanmark', 'studentscanmark', 'attforblock');
+
$mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
for ($i=0; $i<=23; $i++) {
diff --git a/attendance.php b/attendance.php
new file mode 100644
index 0000000..9f5699b
--- /dev/null
+++ b/attendance.php
@@ -0,0 +1,84 @@
+.
+
+/**
+ * Prints attendance info for particular user
+ *
+ * @package mod
+ * @subpackage attforblock
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__).'/../../config.php');
+require_once(dirname(__FILE__).'/locallib.php');
+require_once(dirname(__FILE__).'/student_attenance_form.php');
+
+$pageparams = new att_sessions_page_params();
+
+// Check that the required parameters are present.
+$id = required_param('sessid', PARAM_INT);
+$attendance_session_id = required_param('sessid', PARAM_INT);
+
+
+$attforsession = $DB->get_record('attendance_sessions', array('id' => $id), '*', MUST_EXIST);
+$attforblock = $DB->get_record('attforblock', array('id' => $attforsession->attendanceid), '*', MUST_EXIST);
+$cm = get_coursemodule_from_instance('attforblock', $attforblock->id, 0, false, MUST_EXIST);
+$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+// Require the user is logged in.
+require_login($course, true, $cm);
+
+$pageparams->sessionid = $id;
+$att = new attforblock($attforblock, $cm, $course, $PAGE->context, $pageparams);
+
+// Require that a session key is passed to this page.
+require_sesskey();
+
+// Create the form.
+$mform = new mod_attforblock_student_attendance_form(null,
+ array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att));
+
+if ($mform->is_cancelled()) {
+ // The user cancelled the form, so redirect them to the view page.
+ $url = new moodle_url('/mod/attforblock/view.php', array('id' => $cm->id));
+ redirect($url);
+} else if ($fromform = $mform->get_data()) {
+ if (!empty($fromform->status)) {
+ $success = $att->take_from_student($fromform);
+
+ $url = new moodle_url('/mod/attforblock/view.php', array('id' => $cm->id));
+ if ($success) {
+ // Redirect back to the view page for the block.
+ redirect($url);
+ } else {
+ print_error ('attendance_already_submitted', 'mod_attforblock', $url);
+ }
+ }
+
+ // The form did not validate correctly so we will set it to display the data they submitted.
+ $mform->set_data($fromform);
+}
+
+$PAGE->set_url($att->url_sessions());
+$PAGE->set_title($course->shortname. ": ".$att->name);
+$PAGE->set_heading($course->fullname);
+$PAGE->set_cacheable(true);
+$PAGE->navbar->add($att->name);
+
+$output = $PAGE->get_renderer('mod_attforblock');
+echo $output->header();
+$mform->display();
+echo $output->footer();
diff --git a/db/install.xml b/db/install.xml
index 1bc5a05..ca4e6b0 100644
--- a/db/install.xml
+++ b/db/install.xml
@@ -30,6 +30,7 @@
+
diff --git a/db/upgrade.php b/db/upgrade.php
index c00926f..b38d71f 100644
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -32,7 +32,17 @@ function xmldb_attendance_upgrade($oldversion=0) {
global $CFG, $THEME, $DB;
$dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes.
- $result = true;
+ if ($oldversion < 2013082901) {
+ $table = new xmldb_table('attendance_sessions');
+
+ $field = new xmldb_field('studentscanmark');
+ $field->set_attributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ upgrade_mod_savepoint(true, 2013082901, 'attendance');
+ }
if ($oldversion < 2013082902) {
// Replace values that reference old module "attforblock" to "attendance".
diff --git a/lang/en/attendance.php b/lang/en/attendance.php
index 4c8df1f..ee35575 100755
--- a/lang/en/attendance.php
+++ b/lang/en/attendance.php
@@ -216,3 +216,11 @@ $string['viewmode'] = 'View mode';
$string['week'] = 'week(s)';
$string['weeks'] = 'Weeks';
$string['youcantdo'] = 'You can\'t do anything';
+// New strings.
+$string['studentscanmark'] = 'Allow students to record 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';
diff --git a/locallib.php b/locallib.php
index ace0f42..d5d2d79 100644
--- a/locallib.php
+++ b/locallib.php
@@ -33,6 +33,7 @@ define('ATT_VIEW_WEEKS', 2);
define('ATT_VIEW_MONTHS', 3);
define('ATT_VIEW_ALLPAST', 4);
define('ATT_VIEW_ALL', 5);
+define('ATT_VIEW_NOTPRESENT', 6);
define('ATT_SORT_LASTNAME', 1);
define('ATT_SORT_FIRSTNAME', 2);
@@ -701,6 +702,8 @@ class attendance {
if ($this->pageparams->startdate && $this->pageparams->enddate) {
$where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate >= :sdate AND sessdate < :edate";
+ } else if ($this->pageparams->enddate) {
+ $where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate < :edate";
} else {
$where = "attendanceid = :aid AND sessdate >= :csdate";
}
@@ -833,6 +836,57 @@ class attendance {
add_to_log($this->course->id, 'attendance', 'session updated', $url, $info, $this->cm->id);
}
+ /**
+ * Used to record attendance submitted by the student.
+ *
+ * @global type $DB
+ * @global type $USER
+ * @param type $mformdata
+ * @return boolean
+ */
+ public function take_from_student($mformdata) {
+ global $DB, $USER;
+
+ $statuses = implode(',', array_keys( (array)$this->get_statuses() ));
+ $now = time();
+
+ $record = new stdClass();
+ $record->studentid = $USER->id;
+ $record->statusid = $mformdata->status;
+ $record->statusset = $statuses;
+ $record->remarks = get_string('set_by_student', 'mod_attforblock');
+ $record->sessionid = $mformdata->sessid;
+ $record->timetaken = $now;
+ $record->takenby = $USER->id;
+
+ $dbsesslog = $this->get_session_log($mformdata->sessid);
+ if (array_key_exists($record->studentid, $dbsesslog)) {
+ // Already recorded do not save.
+ return false;
+ }
+ else {
+ $DB->insert_record('attendance_log', $record, false);
+ }
+
+ // Update the session to show that a register has been taken, or staff may overwrite records.
+ $rec = new object();
+ $rec->id = $mformdata->sessid;
+ $rec->lasttaken = $now;
+ $rec->lasttakenby = $USER->id;
+ $DB->update_record('attendance_sessions', $rec);
+
+ // Update the users grade.
+ $this->update_users_grade(array($USER->id));
+
+ // Log the change.
+ $params = array(
+ 'sessionid' => $mformdata->sessid);
+ $url = $this->url_take($params);
+ $this->log('attendance taked', $url, $USER->firstname.' '.$USER->lastname);
+
+ return true;
+ }
+
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.
@@ -1082,7 +1136,14 @@ class attendance {
return $this->usertakensesscount[$userid];
}
- public function get_user_statuses_stat($userid) {
+ /**
+ *
+ * @global type $DB
+ * @param type $userid
+ * @param type $filters - An array things to filter by. For now only enddate is valid.
+ * @return type
+ */
+ public function get_user_statuses_stat($userid, array $filters = null) {
global $DB;
$params = array(
'aid' => $this->id,
@@ -1120,14 +1181,42 @@ class attendance {
}
+ // Make the filter array into a SQL string.
+ if (!empty($processed_filters)) {
+ $processed_filters = 'AND '.implode(' AND ', $processed_filters);
+ } else {
+ $processed_filters = '';
+ }
+
+ $qry = "SELECT al.statusid, count(al.statusid) AS stcnt
+ FROM {attendance_log} al
+ JOIN {attendance_sessions} ats
+ ON al.sessionid = ats.id
+ WHERE ats.attendanceid = :aid AND
+ ats.sessdate >= :cstartdate AND
+ al.studentid = :uid
+ $processed_filters
+ GROUP BY al.statusid";
+
+ if ($filters !== null) { // We do not want to cache, or use a cached version of the results when a filter is set.
+ return $DB->get_records_sql($qry, $params);
+ } else if (!array_key_exists($userid, $this->userstatusesstat)) {
+ // Not filtered so if we do not already have them do the query.
$this->userstatusesstat[$userid] = $DB->get_records_sql($qry, $params);
}
+ // Return the cached stats.
return $this->userstatusesstat[$userid];
}
- public function get_user_grade($userid) {
- return att_get_user_grade($this->get_user_statuses_stat($userid), $this->get_statuses());
+ /**
+ *
+ * @param type $userid
+ * @param type $filters - An array things to filter by. For now only enddate is valid.
+ * @return type
+ */
+ public function get_user_grade($userid, array $filters = null) {
+ return att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses());
}
// For getting sessions count implemented simplest method - taken sessions.
diff --git a/renderables.php b/renderables.php
index 373bdc0..75c5324 100644
--- a/renderables.php
+++ b/renderables.php
@@ -109,19 +109,23 @@ class attendance_filter_controls implements renderable {
public $prevcur;
public $nextcur;
public $curdatetxt;
+ public $reportcontrol;
private $urlpath;
private $urlparams;
private $att;
- public function __construct(attendance $att) {
+ public function __construct(attendance $att, $report = false) {
global $PAGE;
$this->pageparams = $att->pageparams;
$this->cm = $att->cm;
+ // This is a report control only if $reports is true and the attendance block can be graded.
+ $this->reportcontrol = $report && ($att->grade > 0);
+
$this->curdate = $att->pageparams->curdate;
$date = usergetdate($att->pageparams->curdate);
@@ -456,6 +460,12 @@ class attendance_report_data implements renderable {
global $CFG;
$this->perm = $att->perm;
+
+ $currenttime = time();
+ if ($att->pageparams->view == ATT_VIEW_NOTPRESENT) {
+ $att->pageparams->enddate = $currenttime;
+ }
+
$this->pageparams = $att->pageparams;
$this->users = $att->get_users($att->pageparams->group, $att->pageparams->page);
@@ -473,16 +483,28 @@ class attendance_report_data implements renderable {
$this->decimalpoints = $CFG->grade_decimalpoints;
}
- foreach ($this->users as $user) {
- $this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id);
+ $maxgrade = att_get_user_max_grade(count($this->sessions), $this->statuses);
- $this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id);
+ foreach ($this->users as $key => $user) {
+ $grade = 0;
+ if ($this->gradable) {
+ $grade = $att->get_user_grade($user->id, array('enddate' => $currenttime));
+ $totalgrade = $att->get_user_grade($user->id);
+ }
- $this->usersstats[$user->id] = $att->get_user_statuses_stat($user->id);
+ if ($att->pageparams->view != ATT_VIEW_NOTPRESENT || $grade < $maxgrade) {
+ $this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id);
- if ($this->gradable) {
- $this->grades[$user->id] = $att->get_user_grade($user->id);
- $this->maxgrades[$user->id] = $att->get_user_max_grade($user->id);
+ $this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id);
+
+ $this->usersstats[$user->id] = $att->get_user_statuses_stat($user->id);
+
+ if ($this->gradable) {
+ $this->grades[$user->id] = $totalgrade;
+ $this->maxgrades[$user->id] = $att->get_user_max_grade($user->id);;
+ }
+ } else {
+ unset($this->users[$key]);
}
}
diff --git a/renderer.php b/renderer.php
old mode 100755
new mode 100644
index f8ce347..7ec3216
--- a/renderer.php
+++ b/renderer.php
@@ -173,6 +173,9 @@ class mod_attendance_renderer extends plugin_renderer_base {
protected function render_view_controls(attendance_filter_controls $fcontrols) {
$views[ATT_VIEW_ALL] = get_string('all', 'attendance');
$views[ATT_VIEW_ALLPAST] = get_string('allpast', 'attendance');
+ if ($fcontrols->reportcontrol) {
+ $views[ATT_VIEW_NOTPRESENT] = get_string('lowgrade', 'attforblock');
+ }
$views[ATT_VIEW_MONTHS] = get_string('months', 'attendance');
$views[ATT_VIEW_WEEKS] = get_string('weeks', 'attendance');
$views[ATT_VIEW_DAYS] = get_string('days', 'attendance');
@@ -734,8 +737,17 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cell->colspan = 2;
$row->cells[] = $cell;
} else {
- $row->cells[] = '?';
- $row->cells[] = '';
+ if (!empty($sess->studentscanmark)) { // Student can mark their own attendance.
+ // URL to the page that lets the student modify their attendance.
+ $url = new moodle_url('/mod/attforblock/attendance.php',
+ array('sessid' => $sess->id, 'sesskey' => sesskey()));
+ $cell = new html_table_cell(html_writer::link($url, get_string('submitattendance', 'attforblock')));
+ $cell->colspan = 2;
+ $row->cells[] = $cell;
+ } else { // Student cannot mark their own attendace.
+ $row->cells[] = '?';
+ $row->cells[] = '';
+ }
}
$table->data[] = $row;
@@ -751,6 +763,14 @@ class mod_attendance_renderer extends plugin_renderer_base {
}
protected function render_attendance_report_data(attendance_report_data $reportdata) {
+ global $PAGE;
+
+ // Initilise Javascript used to (un)check all checkboxes.
+ $this->page->requires->js_init_call('M.mod_attforblock.init_manage');
+
+ // Check if the user should be able to bulk send messages to other users on the course.
+ $bulkmessagecapability = has_capability('moodle/course:bulkmessaging', $PAGE->context);
+
$table = new html_table();
$table->attributes['class'] = 'generaltable attwidth';
@@ -792,6 +812,14 @@ class mod_attendance_renderer extends plugin_renderer_base {
$table->align[] = 'center';
$table->size[] = '1px';
}
+
+ if ($bulkmessagecapability) { // Display the table header for bulk messaging.
+ // The checkbox must have an id of cb_selector so that the JavaScript will pick it up.
+ $table->head[] = html_writer::checkbox('cb_selector', 0, false, '', array('id' => 'cb_selector'));
+ $table->align[] = 'center';
+ $table->size[] = '1px';
+ }
+
if ($reportdata->sessionslog) {
$table->head[] = get_string('remarks', 'attendance');
@@ -820,6 +848,28 @@ class mod_attendance_renderer extends plugin_renderer_base {
$row->cells[] = $reportdata->grades[$user->id].' / '.$reportdata->maxgrades[$user->id];
}
+ if ($bulkmessagecapability) { // Create the checkbox for bulk messaging.
+ $row->cells[] = html_writer::checkbox('user'.$user->id, 'on', false);
+ }
+
+ $table->data[] = $row;
+ }
+
+ if ($bulkmessagecapability) { // Require that the user can bulk message users.
+ // Display check boxes that will allow the user to send a message to the students that have been checked.
+ $output = html_writer::empty_tag('input', array('name' => 'sesskey', 'type' => 'hidden', 'value' => sesskey()));
+ $output .= html_writer::empty_tag('input', array('name' => 'formaction', 'type' => 'hidden', 'value' => 'messageselect.php'));
+ $output .= html_writer::empty_tag('input', array('name' => 'id', 'type' => 'hidden', 'value' => $GLOBALS['COURSE']->id));
+ $output .= html_writer::empty_tag('input', array('name' => 'returnto', 'type' => 'hidden', 'value' => s(me())));
+ $output .= html_writer::table($table);
+ $output .= html_writer::tag('div',
+ html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('messageselectadd'))),
+ array('class' => 'buttons'));
+ $url = new moodle_url('/user/action_redir.php');
+ return html_writer::tag('form', $output, array('action' => $url->out(), 'method' => 'post'));
+ } else {
+ return html_writer::table($table);
+ }
if ($reportdata->sessionslog) {
if (isset($sess) && isset($reportdata->sessionslog[$user->id][$sess->id]->remarks)) {
$row->cells[] = $reportdata->sessionslog[$user->id][$sess->id]->remarks;
diff --git a/report.php b/report.php
index 8a4d4cf..a0503f5 100644
--- a/report.php
+++ b/report.php
@@ -58,7 +58,7 @@ $PAGE->navbar->add(get_string('report', 'attendance'));
$output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_REPORT);
$filtercontrols = new attendance_filter_controls($att);
-$reportdata = new attendance_report_data($att);
+$reportdata = new attendance_report_data($att, true);
add_to_log($course->id, 'attendance', 'report viewed', '/mod/attendance/report.php?id='.$id, '', $cm->id);
diff --git a/sessions.php b/sessions.php
index b03b3df..fda84fe 100644
--- a/sessions.php
+++ b/sessions.php
@@ -220,6 +220,9 @@ function construct_sessions_data_for_add($formdata) {
$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;
+ }
fill_groupid($formdata, $sessions, $sess);
}
@@ -237,6 +240,9 @@ function construct_sessions_data_for_add($formdata) {
$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;
+ }
fill_groupid($formdata, $sessions, $sess);
}
diff --git a/student_attenance_form.php b/student_attenance_form.php
new file mode 100644
index 0000000..7089b39
--- /dev/null
+++ b/student_attenance_form.php
@@ -0,0 +1,63 @@
+.
+
+require_once($CFG->libdir.'/formslib.php');
+
+class mod_attforblock_student_attendance_form extends moodleform {
+ public function definition() {
+ global $CFG, $USER;
+
+ $mform =& $this->_form;
+
+ $course = $this->_customdata['course'];
+ $cm = $this->_customdata['cm'];
+ $modcontext = $this->_customdata['modcontext'];
+ $attforsession = $this->_customdata['session'];
+ $attblock = $this->_customdata['attendance'];
+
+ $statuses = $attblock->get_statuses();
+
+ $mform->addElement('hidden', 'sessid', null);
+ $mform->setType('sessid', PARAM_INT);
+ $mform->setConstant('sessid', $attforsession->id);
+
+ $mform->addElement('hidden', 'sesskey', null);
+ $mform->setType('sesskey', PARAM_INT);
+ $mform->setConstant('sesskey', sesskey());
+
+ // Set a title as the date and time of the session.
+ $sesstiontitle = userdate($attforsession->sessdate, get_string('strftimedate')).' '
+ .userdate($attforsession->sessdate, get_string('strftimehm', 'mod_attforblock'));
+
+ $mform->addElement('header', 'session', $sesstiontitle);
+
+ // If a session description is set display it.
+ 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());
+ }
+ // 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', 'attforblock'), 'required', '', 'client', false, false);
+
+ $this->add_action_buttons();
+ }
+}
\ No newline at end of file