Compare commits
85 Commits
40-behatfi
...
MOODLE_35_
Author | SHA1 | Date |
---|---|---|
Dan Marsden | f485ce7d89 | 5 years ago |
Dan Marsden | a37d399b03 | 5 years ago |
Dan Marsden | 0d3a162057 | 5 years ago |
Dan Marsden | e078bb9d87 | 5 years ago |
Nadav Kavalerchik | 119946f24a | 5 years ago |
Dan Marsden | 9f35bbe24f | 5 years ago |
Dan Marsden | 29e822459d | 5 years ago |
Dan Marsden | 5af8de8c36 | 5 years ago |
Dan Marsden | 9836d3cfb2 | 5 years ago |
Dan Marsden | baf90b0304 | 6 years ago |
maksudr | 53f075ab6c | 6 years ago |
Dan Marsden | 9bc4d9e8dd | 6 years ago |
maksudr | 2a91d6d7f5 | 6 years ago |
Morgan Harris | 71bc7f15aa | 6 years ago |
Dan Marsden | f9a0425f04 | 6 years ago |
SSH230 | 0d932f23a3 | 6 years ago |
Morgan Harris | 6d4cf2d81d | 6 years ago |
Dan Marsden | 5082677a2d | 6 years ago |
Dan Marsden | fdbabea033 | 6 years ago |
Dan Marsden | c338beb31a | 6 years ago |
Dan Marsden | cf09ca702e | 6 years ago |
Dan Marsden | 650611ee4e | 6 years ago |
Dan Marsden | 24b54e1062 | 6 years ago |
Dan Marsden | 2540512f1e | 6 years ago |
Dan Marsden | ac06e6cf76 | 6 years ago |
Dan Marsden | 0478b99c45 | 6 years ago |
Dan Marsden | 596c63ecba | 6 years ago |
Dan Marsden | 126dc33dc8 | 6 years ago |
Dan Marsden | 253fd5f504 | 6 years ago |
Tõnis Tartes | a412914418 | 6 years ago |
Dan Marsden | b69eb13de4 | 6 years ago |
Dan Marsden | d17a0d1ad3 | 6 years ago |
Dan Marsden | 04042370dd | 6 years ago |
Dan Marsden | d6aedc26b0 | 6 years ago |
Dan Marsden | 31a29ff647 | 6 years ago |
Dan Marsden | a93f6d202a | 6 years ago |
Dan Marsden | e98011347d | 6 years ago |
Dan Marsden | 899abd4911 | 6 years ago |
Dan Marsden | 310522bb93 | 6 years ago |
Dan Marsden | c394dccf87 | 6 years ago |
Dan Marsden | 78f5b91185 | 6 years ago |
Dan Marsden | 4576ce28ca | 6 years ago |
Dan Marsden | a84562228c | 6 years ago |
Dan Marsden | 22c45da5e4 | 6 years ago |
Dan Marsden | 1906820255 | 6 years ago |
Dan Marsden | 1be1851416 | 6 years ago |
Dan Marsden | 1d57b0ebfa | 6 years ago |
Dan Marsden | c5d9b46791 | 6 years ago |
Dan Marsden | edcc1a9c97 | 6 years ago |
Dan Marsden | d632ac4eae | 6 years ago |
Dan Marsden | 818ae8e559 | 6 years ago |
Dan Marsden | 38d5e8cee2 | 6 years ago |
Dan Marsden | f0bbb6795d | 6 years ago |
Dan Marsden | dcac278642 | 6 years ago |
Dan Marsden | f4a7d9fbb2 | 6 years ago |
Dan Marsden | 6324708c86 | 6 years ago |
Dan Marsden | 3a0759067b | 6 years ago |
Dan Marsden | 7ac9b2eb7d | 6 years ago |
Dan Marsden | 8e513a7b6b | 6 years ago |
Dan Marsden | 67c63bfb1b | 6 years ago |
Eoin Campbell | 37a5549c6a | 6 years ago |
Dan Marsden | dd705d75df | 6 years ago |
Dan Marsden | f6f408e1b5 | 6 years ago |
Dan Marsden | 89b974f184 | 6 years ago |
Dan Marsden | 6542eee57e | 6 years ago |
Dan Marsden | fabd021710 | 7 years ago |
Dan Marsden | ec74ba2dbc | 7 years ago |
Dan Marsden | 4855bfcc29 | 7 years ago |
Dan Marsden | 37c42b4bc2 | 7 years ago |
Dan Marsden | a6576acd19 | 7 years ago |
Nick Phillips | 965e7f5e02 | 7 years ago |
Dan Marsden | 4e11ea78b2 | 7 years ago |
Dan Marsden | 8d3ca3d143 | 7 years ago |
Dan Marsden | 6a4a0572aa | 7 years ago |
Dan Marsden | 2522b8b619 | 7 years ago |
Dan Marsden | 6826bb831e | 7 years ago |
Dan Marsden | c94fc8679a | 7 years ago |
Dan Marsden | a7f83c2e8d | 7 years ago |
Dan Marsden | 0400a932dd | 7 years ago |
Dan Marsden | df9edb01a7 | 7 years ago |
Dan Marsden | a971a53fcd | 7 years ago |
Neill Magill | 35c88f84ea | 7 years ago |
Cameron Ball | b8a6731dbe | 7 years ago |
Dan Marsden | 3872569a88 | 7 years ago |
Dan Marsden | dec2abf3fc | 7 years ago |
53 changed files with 2332 additions and 358 deletions
@ -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/>. |
|||
|
|||
/** |
|||
* The mod_attendance instance list viewed event. |
|||
* |
|||
* @package mod_attendance |
|||
* @copyright 2018 Dan Marsden |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
|
|||
namespace mod_attendance\event; |
|||
defined('MOODLE_INTERNAL') || die(); |
|||
|
|||
/** |
|||
* The mod_attendance instance list viewed event class. |
|||
* |
|||
* @package mod_attendance |
|||
* @copyright 2018 Dan Marsden |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed { |
|||
/** |
|||
* Create the event from course record. |
|||
* |
|||
* @param \stdClass $course |
|||
* @return course_module_instance_list_viewed |
|||
*/ |
|||
public static function create_from_course(\stdClass $course) { |
|||
$params = array( |
|||
'context' => \context_course::instance($course->id) |
|||
); |
|||
$event = self::create($params); |
|||
$event->add_record_snapshot('course', $course); |
|||
return $event; |
|||
} |
|||
} |
@ -0,0 +1,451 @@ |
|||
<?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 the mobile output class for the attendance |
|||
* |
|||
* @package mod_attendance |
|||
* @copyright 2018 Dan Marsden |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
|
|||
namespace mod_attendance\output; |
|||
|
|||
defined('MOODLE_INTERNAL') || die(); |
|||
/** |
|||
* Mobile output class for the attendance. |
|||
* |
|||
* @copyright 2018 Dan Marsden |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
class mobile { |
|||
|
|||
/* |
|||
* Subnet warning. - constants used to prevent warnings from showing multiple times. |
|||
*/ |
|||
const MESSAGE_SUBNET = 10; |
|||
|
|||
/* |
|||
* Prevent shared warning. used to prevent warnings from showing multiple times. |
|||
*/ |
|||
const MESSAGE_PREVENTSHARED = 30; |
|||
|
|||
/** |
|||
* Returns the initial page when viewing the activity for the mobile app. |
|||
* |
|||
* @param array $args Arguments from tool_mobile_get_content WS |
|||
* @return array HTML, javascript and other data |
|||
*/ |
|||
public static function mobile_view_activity($args) { |
|||
global $OUTPUT, $DB, $USER, $USER, $CFG; |
|||
|
|||
require_once($CFG->dirroot.'/mod/attendance/locallib.php'); |
|||
|
|||
$cmid = $args['cmid']; |
|||
$courseid = $args['courseid']; |
|||
$takenstatus = empty($args['status']) ? '' : $args['status']; |
|||
$sessid = empty($args['sessid']) ? '' : $args['sessid']; |
|||
$password = empty($args['studentpass']) ? '' : $args['studentpass']; |
|||
|
|||
// Capabilities check. |
|||
$cm = get_coursemodule_from_id('attendance', $cmid); |
|||
|
|||
require_login($courseid, false , $cm, true, true); |
|||
|
|||
$context = \context_module::instance($cm->id); |
|||
require_capability('mod/attendance:view', $context); |
|||
|
|||
$attendance = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); |
|||
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); |
|||
|
|||
$data = array(); // Data to pass to renderer. |
|||
$data['cmid'] = $cmid; |
|||
$data['courseid'] = $courseid; |
|||
$data['attendance'] = $attendance; |
|||
$data['timestamp'] = time(); // Used to prevent attendance session marking page to be cached. |
|||
|
|||
$data['attendancefunction'] = 'mobile_user_form'; |
|||
$isteacher = false; |
|||
if (has_capability('mod/attendance:takeattendances', $context)) { |
|||
$isteacher = true; |
|||
$data['attendancefunction'] = 'mobile_teacher_form'; |
|||
} |
|||
|
|||
// Add stats for this use to output. |
|||
$pageparams = new \mod_attendance_view_page_params(); |
|||
$pageparams->studentid = $USER->id; |
|||
$pageparams->group = groups_get_activity_group($cm, true); |
|||
$canseegroupsession = true; |
|||
if (!empty($sessid) && (!empty($takenstatus) || $isteacher)) { |
|||
$session = $DB->get_record('attendance_sessions', array('id' => $sessid)); |
|||
$pageparams->grouptype = $session->groupid; |
|||
$pageparams->sessionid = $sessid; |
|||
|
|||
if ($isteacher && !empty($session->groupid)) { |
|||
$allowedgroups = groups_get_activity_allowed_groups($cm); |
|||
if (!array_key_exists($session->groupid, $allowedgroups)) { |
|||
$canseegroupsession = false; |
|||
} |
|||
} |
|||
} |
|||
$pageparams->mode = \mod_attendance_view_page_params::MODE_THIS_COURSE; |
|||
$pageparams->view = 5; // Show all sessions for this course? |
|||
|
|||
$att = new \mod_attendance_structure($attendance, $cm, $course, $context, $pageparams); |
|||
|
|||
// Check if this teacher is allowed to view/mark this group session. |
|||
|
|||
if ($isteacher && $canseegroupsession) { |
|||
$keys = array_keys($args); |
|||
$userkeys = preg_grep("/status\d+/", $keys); |
|||
if (!empty($userkeys)) { // If this is a post from the teacher form. |
|||
// Build data to pass to take_from_form_data. |
|||
$formdata = new \stdClass(); |
|||
foreach ($userkeys as $uk) { |
|||
$userid = str_replace('status', '', $uk); |
|||
$status = $args[$uk]; |
|||
$formdata->{'remarks'.$userid} = ''; |
|||
$formdata->{'user'.$userid} = $status; |
|||
} |
|||
$att->take_from_form_data($formdata); |
|||
$data['showmessage'] = true; |
|||
$data['messages'][]['string'] = 'attendancesuccess'; |
|||
} |
|||
} |
|||
|
|||
// Get list of sessions within the next 24hrs and in last 6hrs. |
|||
// TODO: provide way of adjusting which sessions to show in app. |
|||
$time = time() - (6 * 60 * 60); |
|||
|
|||
$data['sessions'] = array(); |
|||
|
|||
$sessions = $DB->get_records_select('attendance_sessions', |
|||
'attendanceid = ? AND sessdate > ? ORDER BY sessdate', array($attendance->id, $time)); |
|||
|
|||
if (!empty($sessions)) { |
|||
$userdata = new \attendance_user_data($att, $USER->id, true); |
|||
foreach ($sessions as $sess) { |
|||
if (!$isteacher && empty($userdata->sessionslog['c'.$sess->id])) { |
|||
// This session isn't viewable to this student - probably a group session. |
|||
continue; |
|||
} |
|||
|
|||
// Check if this teacher is allowed to view this group session. |
|||
if ($isteacher && !empty($sess->groupid)) { |
|||
$allowedgroups = groups_get_activity_allowed_groups($cm); |
|||
if (!array_key_exists($sess->groupid, $allowedgroups)) { |
|||
continue; |
|||
} |
|||
} |
|||
list($canmark, $reason) = attendance_can_student_mark($sess); |
|||
if (!$isteacher && $reason == 'preventsharederror') { |
|||
$data['showmessage'] = true; |
|||
$data['messages'][self::MESSAGE_PREVENTSHARED]['string'] = 'preventsharederror'; // Lang string to show as a message. |
|||
} |
|||
|
|||
if ($isteacher || $canmark) { |
|||
$html = array('time' => strip_tags(construct_session_full_date_time($sess->sessdate, $sess->duration)), |
|||
'groupname' => ''); |
|||
if (!empty($sess->groupid)) { |
|||
// TODO In-efficient way to get group name - we should get all groups in one query. |
|||
$html['groupname'] = $DB->get_field('groups', 'name', array('id' => $sess->groupid)); |
|||
} |
|||
|
|||
// Check if Status already recorded. |
|||
if (!$isteacher && !empty($userdata->sessionslog['c'.$sess->id]->statusid)) { |
|||
$html['currentstatus'] = $userdata->statuses[$userdata->sessionslog['c'.$sess->id]->statusid]->description; |
|||
} else { |
|||
// Status has not been recorded - If student, check auto-assign and form data. |
|||
$html['sessid'] = $sess->id; |
|||
|
|||
if (!$isteacher) { |
|||
if (!empty($sess->subnet) && !address_in_subnet(getremoteaddr(), $sess->subnet)) { |
|||
$data['showmessage'] = true; |
|||
$data['messages'][self::MESSAGE_SUBNET]['string'] = 'subnetwrong'; // Lang string to show as a message. |
|||
$html['sessid'] = null; // Unset sessid as we cannot record session on this ip. |
|||
} else if ($sess->autoassignstatus && empty($sess->studentpassword)) { |
|||
$statusid = attendance_session_get_highest_status($att, $sess); |
|||
if (empty($statusid)) { |
|||
$data['showmessage'] = true; |
|||
$data['messages'][]['string'] = 'attendance_no_status'; |
|||
} |
|||
$take = new \stdClass(); |
|||
$take->status = $statusid; |
|||
$take->sessid = $sess->id; |
|||
$success = $att->take_from_student($take); |
|||
|
|||
if ($success) { |
|||
$html['currentstatus'] = $userdata->statuses[$statusid]->description; |
|||
$html['sessid'] = null; // Unset sessid as we have recorded session. |
|||
} |
|||
} else if ($sess->id == $sessid) { |
|||
if (!empty($sess->studentpassword) && $password != $sess->studentpassword) { |
|||
// Password incorrect. |
|||
$data['showmessage'] = true; |
|||
$data['messages'][]['string'] = 'incorrectpasswordshort'; |
|||
} else { |
|||
$statuses = $att->get_statuses(); |
|||
// Check if user has access to all statuses. |
|||
foreach ($statuses as $status) { |
|||
if ($status->studentavailability === '0') { |
|||
unset($statuses[$status->id]); |
|||
continue; |
|||
} |
|||
if (!empty($status->studentavailability) && |
|||
time() > $sess->sessdate + ($status->studentavailability * 60)) { |
|||
unset($statuses[$status->id]); |
|||
continue; |
|||
} |
|||
} |
|||
if ($sess->autoassignstatus) { |
|||
// If this is an auto-assign, get the highest status available. |
|||
$takenstatus = attendance_session_get_highest_status($att, $sess); |
|||
} |
|||
|
|||
if (empty($statuses[$takenstatus])) { |
|||
// This status has probably expired and is not available - they need to choose a new one. |
|||
$data['showmessage'] = true; |
|||
$data['messages'][]['string'] = 'invalidstatus'; |
|||
} else { |
|||
$take = new \stdClass(); |
|||
$take->status = $takenstatus; |
|||
$take->sessid = $sess->id; |
|||
$success = $att->take_from_student($take); |
|||
|
|||
if ($success) { |
|||
$html['currentstatus'] = $userdata->statuses[$takenstatus]->description; |
|||
$html['sessid'] = null; // Unset sessid as we have recorded session. |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
$data['sessions'][] = $html; |
|||
} |
|||
} |
|||
} |
|||
|
|||
$summary = new \mod_attendance_summary($att->id, array($USER->id), $att->pageparams->startdate, |
|||
$att->pageparams->enddate); |
|||
$data['summary'] = $summary->get_all_sessions_summary_for($USER->id); |
|||
|
|||
return [ |
|||
'templates' => [ |
|||
[ |
|||
'id' => 'main', |
|||
'html' => $OUTPUT->render_from_template('mod_attendance/mobile_view_page', $data), |
|||
], |
|||
], |
|||
'javascript' => '', |
|||
'otherdata' => '' |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Returns the form to take attendance for the mobile app. |
|||
* |
|||
* @param array $args Arguments from tool_mobile_get_content WS |
|||
* @return array HTML, javascript and other data |
|||
*/ |
|||
public static function mobile_user_form($args) { |
|||
global $OUTPUT, $DB, $CFG; |
|||
|
|||
require_once($CFG->dirroot.'/mod/attendance/locallib.php'); |
|||
|
|||
$args = (object) $args; |
|||
$cmid = $args->cmid; |
|||
$courseid = $args->courseid; |
|||
$sessid = $args->sessid; |
|||
|
|||
// Capabilities check. |
|||
$cm = get_coursemodule_from_id('attendance', $cmid); |
|||
|
|||
require_login($courseid, false , $cm, true, true); |
|||
|
|||
$context = \context_module::instance($cm->id); |
|||
require_capability('mod/attendance:view', $context); |
|||
|
|||
$attendance = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); |
|||
$attforsession = $DB->get_record('attendance_sessions', array('id' => $sessid), '*', MUST_EXIST); |
|||
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); |
|||
|
|||
$pageparams = new \mod_attendance_sessions_page_params(); |
|||
$pageparams->sessionid = $sessid; |
|||
$att = new \mod_attendance_structure($attendance, $cm, $course, $context, $pageparams); |
|||
|
|||
$data = array(); // Data to pass to renderer. |
|||
$data['attendance'] = $attendance; |
|||
$data['cmid'] = $cmid; |
|||
$data['courseid'] = $courseid; |
|||
$data['sessid'] = $sessid; |
|||
$data['messages'] = array(); |
|||
$data['showmessage'] = false; |
|||
$data['showstatuses'] = true; |
|||
$data['showpassword'] = false; |
|||
$data['statuses'] = array(); |
|||
$data['disabledduetotime'] = false; |
|||
|
|||
list($canmark, $reason) = attendance_can_student_mark($attforsession, false); |
|||
// Check if subnet is set and if the user is in the allowed range. |
|||
if (!$canmark) { |
|||
$data['messages'][]['string'] = $reason; // Lang string to show as a message. |
|||
$data['showstatuses'] = false; // Hide all statuses. |
|||
} else if (!empty($attforsession->subnet) && !address_in_subnet(getremoteaddr(), $attforsession->subnet)) { |
|||
$data['messages'][self::MESSAGE_SUBNET]['string'] = 'subnetwrong'; // Lang string to show as a message. |
|||
$data['showstatuses'] = false; // Hide all statuses. |
|||
} else if ($attforsession->autoassignstatus && empty($attforsession->studentpassword)) { |
|||
// This shouldn't happen as the main function should handle this scenario. |
|||
// Hide all status just in case the user manages to hit this page accidentally. |
|||
$data['showstatuses'] = false; // Hide all statuses. |
|||
} else { |
|||
// Show user form for submitting a status. |
|||
$statuses = $att->get_statuses(); |
|||
// Check if user has access to all statuses. |
|||
foreach ($statuses as $status) { |
|||
if ($status->studentavailability === '0') { |
|||
unset($statuses[$status->id]); |
|||
continue; |
|||
} |
|||
if (!empty($status->studentavailability) && |
|||
time() > $attforsession->sessdate + ($status->studentavailability * 60)) { |
|||
unset($statuses[$status->id]); |
|||
continue; |
|||
$data['disabledduetotime'] = true; |
|||
} |
|||
$data['statuses'][] = array('stid' => $status->id, 'description' => $status->description); |
|||
} |
|||
if (empty($data['statuses'])) { |
|||
$data['messages'][]['string'] = 'attendance_no_status'; |
|||
$data['showstatuses'] = false; // Hide all statuses. |
|||
} else if (!empty($attforsession->studentpassword)) { |
|||
$data['showpassword'] = true; |
|||
if ($attforsession->autoassignstatus) { |
|||
// If this is an auto status - don't show the statuses, but show the form. |
|||
$data['statuses'] = array(); |
|||
} |
|||
} |
|||
} |
|||
if (!empty($data['messages'])) { |
|||
$data['showmessage'] = true; |
|||
} |
|||
|
|||
return [ |
|||
'templates' => [ |
|||
[ |
|||
'id' => 'main', |
|||
'html' => $OUTPUT->render_from_template('mod_attendance/mobile_user_form', $data), |
|||
'cache-view' => false |
|||
], |
|||
], |
|||
'javascript' => '', |
|||
'otherdata' => '' |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Returns the form to take attendance for the mobile app. |
|||
* |
|||
* @param array $args Arguments from tool_mobile_get_content WS |
|||
* @return array HTML, javascript and other data |
|||
*/ |
|||
public static function mobile_teacher_form($args) { |
|||
global $OUTPUT, $DB, $CFG, $PAGE; |
|||
|
|||
require_once($CFG->dirroot.'/mod/attendance/locallib.php'); |
|||
|
|||
$args = (object) $args; |
|||
$cmid = $args->cmid; |
|||
$courseid = $args->courseid; |
|||
$sessid = $args->sessid; |
|||
|
|||
// Capabilities check. |
|||
$cm = get_coursemodule_from_id('attendance', $cmid); |
|||
|
|||
require_login($courseid, false , $cm, true, true); |
|||
|
|||
$context = \context_module::instance($cm->id); |
|||
require_capability('mod/attendance:takeattendances', $context); |
|||
|
|||
$attendance = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); |
|||
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); |
|||
|
|||
$pageparams = new \mod_attendance_sessions_page_params(); |
|||
$pageparams->sessionid = $sessid; |
|||
$att = new \mod_attendance_structure($attendance, $cm, $course, $context, $pageparams); |
|||
|
|||
$data = array(); // Data to pass to renderer. |
|||
$data['attendance'] = $attendance; |
|||
$data['cmid'] = $cmid; |
|||
$data['courseid'] = $courseid; |
|||
$data['sessid'] = $sessid; |
|||
$data['messages'] = array(); |
|||
$data['showmessage'] = false; |
|||
$data['statuses'] = array(); |
|||
$data['btnargs'] = ''; // Stores list of userid status args that should be added to form post. |
|||
|
|||
$statuses = $att->get_statuses(); |
|||
$otherdata = array(); |
|||
$existinglog = $DB->get_records('attendance_log', |
|||
array('sessionid' => $sessid), '', 'studentid,statusid'); |
|||
foreach ($existinglog as $log) { |
|||
if (!empty($log->statusid)) { |
|||
$otherdata['status'.$log->studentid] = $log->statusid; |
|||
} |
|||
} |
|||
|
|||
foreach ($statuses as $status) { |
|||
$data['statuses'][] = array('stid' => $status->id, 'acronym' => $status->acronym, |
|||
'description' => $status->description, 'selectall' => ''); |
|||
} |
|||
|
|||
$data['users'] = array(); |
|||
$users = $att->get_users($att->get_session_info($sessid)->groupid, 0); |
|||
foreach ($users as $user) { |
|||
$userpicture = new \user_picture($user); |
|||
$userpicture->size = 1; // Size f1. |
|||
$profileimageurl = $userpicture->get_url($PAGE)->out(false); |
|||
$data['users'][] = array('userid' => $user->id, 'fullname' => $user->fullname, 'profileimageurl' => $profileimageurl); |
|||
// Generate args to use in submission button here. |
|||
$data['btnargs'] .= ', status'. $user->id. ': CONTENT_OTHERDATA.status'. $user->id; |
|||
// Really Hacky way to do a select-all. This really needs to be moved into a JS function within the app. |
|||
foreach ($statuses as $status) { |
|||
foreach ($data['statuses'] as $id => $st) { // Statuses not ordered by statusid. |
|||
if ($st['stid'] == $status->id) { // Find the item that we need to add to. |
|||
$data['statuses'][$id]['selectall'] .= "CONTENT_OTHERDATA.status".$user->id."=".$status->id.";"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
if (!empty($data['messages'])) { |
|||
$data['showmessage'] = true; |
|||
} |
|||
|
|||
return [ |
|||
'templates' => [ |
|||
[ |
|||
'id' => 'main', |
|||
'html' => $OUTPUT->render_from_template('mod_attendance/mobile_teacher_form', $data), |
|||
'cache-view' => false |
|||
], |
|||
], |
|||
'javascript' => '', |
|||
'otherdata' => $otherdata |
|||
]; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,495 @@ |
|||
<?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/>. |
|||
|
|||
/** |
|||
* mod_attendance Data provider. |
|||
* |
|||
* @package mod_attendance |
|||
* @copyright 2018 Cameron Ball <cameron@cameron1729.xyz> |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
|
|||
namespace mod_attendance\privacy; |
|||
defined('MOODLE_INTERNAL') || die(); |
|||
|
|||
use context; |
|||
use context_module; |
|||
use core_privacy\local\metadata\collection; |
|||
use core_privacy\local\request\{writer, transform, helper, contextlist, approved_contextlist}; |
|||
use stdClass; |
|||
|
|||
/** |
|||
* Data provider for mod_attendance. |
|||
* |
|||
* @copyright 2018 Cameron Ball <cameron@cameron1729.xyz> |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
final class provider implements |
|||
\core_privacy\local\request\plugin\provider, |
|||
\core_privacy\local\metadata\provider |
|||
{ |
|||
|
|||
/** |
|||
* Returns meta data about this system. |
|||
* |
|||
* @param collection $collection The initialised collection to add items to. |
|||
* @return collection A listing of user data stored through this system. |
|||
*/ |
|||
public static function get_metadata(collection $collection) : collection { |
|||
$collection->add_database_table( |
|||
'attendance_log', |
|||
[ |
|||
'sessionid' => 'privacy:metadata:sessionid', |
|||
'studentid' => 'privacy:metadata:studentid', |
|||
'statusid' => 'privacy:metadata:statusid', |
|||
'statusset' => 'privacy:metadata:statusset', |
|||
'timetaken' => 'privacy:metadata:timetaken', |
|||
'takenby' => 'privacy:metadata:takenby', |
|||
'remarks' => 'privacy:metadata:remarks', |
|||
'ipaddress' => 'privacy:metadata:ipaddress' |
|||
], |
|||
'privacy:metadata:attendancelog' |
|||
); |
|||
|
|||
$collection->add_database_table( |
|||
'attendance_sessions', |
|||
[ |
|||
'groupid' => 'privacy:metadata:groupid', |
|||
'sessdate' => 'privacy:metadata:sessdate', |
|||
'duration' => 'privacy:metadata:duration', |
|||
'lasttaken' => 'privacy:metadata:lasttaken', |
|||
'lasttakenby' => 'privacy:metadata:lasttakenby', |
|||
'timemodified' => 'privacy:metadata:timemodified' |
|||
], |
|||
'privacy:metadata:attendancesessions' |
|||
); |
|||
|
|||
$collection->add_database_table( |
|||
'attendance_warning_done', |
|||
[ |
|||
'notifyid' => 'privacy:metadata:notifyid', |
|||
'userid' => 'privacy:metadata:userid', |
|||
'timesent' => 'privacy:metadata:timesent' |
|||
], |
|||
'privacy:metadata:attendancewarningdone' |
|||
); |
|||
|
|||
return $collection; |
|||
} |
|||
|
|||
/** |
|||
* Get the list of contexts that contain user information for the specified user. |
|||
* |
|||
* In the case of attendance, that is any attendance where a student has had their |
|||
* attendance taken or has taken attendance for someone else. |
|||
* |
|||
* @param int $userid The user to search. |
|||
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. |
|||
*/ |
|||
public static function get_contexts_for_userid(int $userid) : contextlist { |
|||
return (new contextlist)->add_from_sql( |
|||
"SELECT ctx.id |
|||
FROM {course_modules} cm |
|||
JOIN {modules} m ON cm.module = m.id AND m.name = :modulename |
|||
JOIN {attendance} a ON cm.instance = a.id |
|||
JOIN {attendance_sessions} asess ON asess.attendanceid = a.id |
|||
JOIN {context} ctx ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextlevel |
|||
JOIN {attendance_log} al ON asess.id = al.sessionid AND (al.studentid = :userid OR al.takenby = :takenbyid)", |
|||
[ |
|||
'modulename' => 'attendance', |
|||
'contextlevel' => CONTEXT_MODULE, |
|||
'userid' => $userid, |
|||
'takenbyid' => $userid |
|||
] |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Delete all data for all users in the specified context. |
|||
* |
|||
* @param context $context The specific context to delete data for. |
|||
*/ |
|||
public static function delete_data_for_all_users_in_context(context $context) { |
|||
global $DB; |
|||
|
|||
if (!$context instanceof context_module) { |
|||
return; |
|||
} |
|||
|
|||
if (!$cm = get_coursemodule_from_id('attendance', $context->instanceid)) { |
|||
return; |
|||
} |
|||
|
|||
// Delete all information recorded against sessions associated with this module. |
|||
$DB->delete_records_select( |
|||
'attendance_log', |
|||
"sessionid IN (SELECT id FROM {attendance_sessions} WHERE attendanceid = :attendanceid", |
|||
[ |
|||
'attendanceid' => $cm->instance |
|||
] |
|||
); |
|||
|
|||
// Delete all completed warnings associated with a warning associated with this module. |
|||
$DB->delete_records_select( |
|||
'attendance_warning_done', |
|||
"notifyid IN (SELECT id from {attendance_warning} WHERE idnumber = :attendanceid)", |
|||
['attendanceid' => $cm->instance] |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Delete all user data for the specified user, in the specified contexts. |
|||
* |
|||
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for. |
|||
*/ |
|||
public static function delete_data_for_user(approved_contextlist $contextlist) { |
|||
global $DB; |
|||
$userid = (int)$contextlist->get_user()->id; |
|||
|
|||
foreach ($contextlist as $context) { |
|||
if (!$context instanceof context_module) { |
|||
continue; |
|||
} |
|||
|
|||
if (!$cm = get_coursemodule_from_id('attendance', $context->instanceid)) { |
|||
continue; |
|||
} |
|||
|
|||
$attendanceid = (int)$DB->get_record('attendance', ['id' => $cm->instance])->id; |
|||
$sessionids = array_keys( |
|||
$DB->get_records('attendance_sessions', ['attendanceid' => $attendanceid]) |
|||
); |
|||
|
|||
self::delete_user_from_session_attendance_log($userid, $sessionids); |
|||
self::delete_user_from_sessions($userid, $sessionids); |
|||
self::delete_user_from_attendance_warnings_log($userid, $attendanceid); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Export all user data for the specified user, in the specified contexts. |
|||
* |
|||
* @param approved_contextlist $contextlist The approved contexts to export information for. |
|||
*/ |
|||
public static function export_user_data(approved_contextlist $contextlist) { |
|||
global $DB; |
|||
|
|||
$params = [ |
|||
'modulename' => 'attendance', |
|||
'contextlevel' => CONTEXT_MODULE, |
|||
'studentid' => $contextlist->get_user()->id, |
|||
'takenby' => $contextlist->get_user()->id |
|||
]; |
|||
|
|||
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); |
|||
|
|||
$sql = "SELECT |
|||
al.*, |
|||
asess.id as session, |
|||
asess.description, |
|||
ctx.id as contextid, |
|||
a.name as attendancename, |
|||
a.id as attendanceid, |
|||
statuses.description as statusdesc, statuses.grade as statusgrade |
|||
FROM {course_modules} cm |
|||
JOIN {attendance} a ON cm.instance = a.id |
|||
JOIN {attendance_sessions} asess ON asess.attendanceid = a.id |
|||
JOIN {attendance_log} al on (al.sessionid = asess.id AND (studentid = :studentid OR al.takenby = :takenby)) |
|||
JOIN {context} ctx ON cm.id = ctx.instanceid |
|||
JOIN {attendance_statuses} statuses ON statuses.id = al.statusid |
|||
WHERE (ctx.id {$contextsql})"; |
|||
|
|||
$attendances = $DB->get_records_sql($sql, $params + $contextparams); |
|||
|
|||
self::export_attendance_logs( |
|||
get_string('attendancestaken', 'mod_attendance'), |
|||
array_filter( |
|||
$attendances, |
|||
function(stdClass $attendance) use ($contextlist) : bool { |
|||
return $attendance->takenby == $contextlist->get_user()->id; |
|||
} |
|||
) |
|||
); |
|||
|
|||
self::export_attendance_logs( |
|||
get_string('attendanceslogged', 'mod_attendance'), |
|||
array_filter( |
|||
$attendances, |
|||
function(stdClass $attendance) use ($contextlist) : bool { |
|||
return $attendance->studentid == $contextlist->get_user()->id; |
|||
} |
|||
) |
|||
); |
|||
|
|||
self::export_attendances( |
|||
$contextlist->get_user(), |
|||
$attendances, |
|||
self::group_by_property( |
|||
$DB->get_records_sql( |
|||
"SELECT |
|||
*, |
|||
a.id as attendanceid |
|||
FROM {attendance_warning_done} awd |
|||
JOIN {attendance_warning} aw ON awd.notifyid = aw.id |
|||
JOIN {attendance} a on aw.idnumber = a.id |
|||
WHERE userid = :userid", |
|||
['userid' => $contextlist->get_user()->id] |
|||
), |
|||
'notifyid' |
|||
) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Delete a user from session logs. |
|||
* |
|||
* @param int $userid The id of the user to remove. |
|||
* @param array $sessionids Array of session ids from which to remove the student from the relevant logs. |
|||
*/ |
|||
private static function delete_user_from_session_attendance_log(int $userid, array $sessionids) { |
|||
global $DB; |
|||
|
|||
// Delete records where user was marked as attending. |
|||
list($sessionsql, $sessionparams) = $DB->get_in_or_equal($sessionids, SQL_PARAMS_NAMED); |
|||
$DB->delete_records_select( |
|||
'attendance_log', |
|||
"(studentid = :studentid) AND sessionid $sessionsql", |
|||
['studentid' => $userid] + $sessionparams |
|||
); |
|||
|
|||
// Get every log record where user took the attendance. |
|||
$attendancetakenids = array_keys( |
|||
$DB->get_records_sql( |
|||
"SELECT * from {attendance_log} |
|||
WHERE takenby = :takenbyid AND sessionid $sessionsql", |
|||
['takenbyid' => $userid] + $sessionparams |
|||
) |
|||
); |
|||
|
|||
if (!$attendancetakenids) { |
|||
return; |
|||
} |
|||
|
|||
// Don't delete the record from the log, but update to site admin taking attendance. |
|||
list($attendancetakensql, $attendancetakenparams) = $DB->get_in_or_equal($attendancetakenids, SQL_PARAMS_NAMED); |
|||
$DB->set_field_select( |
|||
'attendance_log', |
|||
'takenby', |
|||
2, |
|||
"id $attendancetakensql", |
|||
$attendancetakenparams |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Delete a user from sessions. |
|||
* |
|||
* Not much user data is stored in a session, but it's possible that a user id is saved |
|||
* in the "lasttakenby" field. |
|||
* |
|||
* @param int $userid The id of the user to remove. |
|||
* @param array $sessionids Array of session ids from which to remove the student. |
|||
*/ |
|||
private static function delete_user_from_sessions(int $userid, array $sessionids) { |
|||
global $DB; |
|||
|
|||
// Get all sessions where user was last to mark attendance. |
|||
list($sessionsql, $sessionparams) = $DB->get_in_or_equal($sessionids, SQL_PARAMS_NAMED); |
|||
$sessionstaken = $DB->get_records_sql( |
|||
"SELECT * from {attendance_sessions} |
|||
WHERE lasttakenby = :lasttakenbyid AND id $sessionsql", |
|||
['lasttakenbyid' => $userid] + $sessionparams |
|||
); |
|||
|
|||
if (!$sessionstaken) { |
|||
return; |
|||
} |
|||
|
|||
// Don't delete the session, but update last taken by to the site admin. |
|||
list($sessionstakensql, $sessionstakenparams) = $DB->get_in_or_equal(array_keys($sessionstaken), SQL_PARAMS_NAMED); |
|||
$DB->set_field_select( |
|||
'attendance_sessions', |
|||
'lasttakenby', |
|||
2, |
|||
"id $sessionstakensql", |
|||
$sessionstakenparams |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Delete a user from the attendance waring log. |
|||
* |
|||
* @param int $userid The id of the user to remove. |
|||
* @param int $attendanceid The id of the attendance instance to remove the relevant warnings from. |
|||
*/ |
|||
private static function delete_user_from_attendance_warnings_log(int $userid, int $attendanceid) { |
|||
global $DB; |
|||
|
|||
// Get all warnings because the user could have their ID listed in the thirdpartyemails column as a comma delimited string. |
|||
$warnings = $DB->get_records( |
|||
'attendance_warning', |
|||
['idnumber' => $attendanceid] |
|||
); |
|||
|
|||
if (!$warnings) { |
|||
return; |
|||
} |
|||
|
|||
// Update the third party emails list for all the relevant warnings. |
|||
$updatedwarnings = array_map( |
|||
function(stdClass $warning) use ($userid) : stdClass { |
|||
$warning->thirdpartyemails = implode(',', array_diff(explode(',', $warning->thirdpartyemails), [$userid])); |
|||
return $warning; |
|||
}, |
|||
array_filter( |
|||
$warnings, |
|||
function (stdClass $warning) use ($userid) : bool { |
|||
return in_array($userid, explode(',', $warning->thirdpartyemails)); |
|||
} |
|||
) |
|||
); |
|||
|
|||
// Sadly need to update each individually, no way to bulk update as all the thirdpartyemails field can be different. |
|||
foreach ($updatedwarnings as $updatedwarning) { |
|||
$DB->update_record('attendance_warning', $updatedwarning); |
|||
} |
|||
|
|||
// Delete any record of the user being notified. |
|||
list($warningssql, $warningsparams) = $DB->get_in_or_equal(array_keys($warnings), SQL_PARAMS_NAMED); |
|||
$DB->delete_records_select( |
|||
'attendance_warning_done', |
|||
"userid = :userid AND notifyid $warningssql", |
|||
['userid' => $userid] + $warningsparams |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Helper function to group an array of stdClasses by a common property. |
|||
* |
|||
* @param array $classes An array of classes to group. |
|||
* @param string $property A common property to group the classes by. |
|||
*/ |
|||
private static function group_by_property(array $classes, string $property) : array { |
|||
return array_reduce( |
|||
$classes, |
|||
function (array $classes, stdClass $class) use ($property) : array { |
|||
$classes[$class->{$property}][] = $class; |
|||
return $classes; |
|||
}, |
|||
[] |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Helper function to transform a row from the database in to session data to export. |
|||
* |
|||
* The properties of the "dbrow" are very specific to the result of the SQL from |
|||
* the export_user_data function. |
|||
* |
|||
* @param stdClass $dbrow A row from the database containing session information. |
|||
* @return stdClass The transformed row. |
|||
*/ |
|||
private static function transform_db_row_to_session_data(stdClass $dbrow) : stdClass { |
|||
return (object) [ |
|||
'name' => $dbrow->attendancename, |
|||
'session' => $dbrow->session, |
|||
'takenbyid' => $dbrow->takenby, |
|||
'studentid' => $dbrow->studentid, |
|||
'status' => $dbrow->statusdesc, |
|||
'grade' => $dbrow->statusgrade, |
|||
'sessiondescription' => $dbrow->description, |
|||
'timetaken' => transform::datetime($dbrow->timetaken), |
|||
'remarks' => $dbrow->remarks, |
|||
'ipaddress' => $dbrow->ipaddress |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Helper function to transform a row from the database in to warning data to export. |
|||
* |
|||
* The properties of the "dbrow" are very specific to the result of the SQL from |
|||
* the export_user_data function. |
|||
* |
|||
* @param stdClass $warning A row from the database containing warning information. |
|||
* @return stdClass The transformed row. |
|||
*/ |
|||
private static function transform_warning_data(stdClass $warning) : stdClass { |
|||
return (object) [ |
|||
'timesent' => transform::datetime($warning->timesent), |
|||
'thirdpartyemails' => $warning->thirdpartyemails, |
|||
'subject' => $warning->emailsubject, |
|||
'body' => $warning->emailcontent |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* Helper function to export attendance logs. |
|||
* |
|||
* The array of "attendances" is actually the result returned by the SQL in export_user_data. |
|||
* It is more of a list of sessions. Which is why it needs to be grouped by context id. |
|||
* |
|||
* @param string $path The path in the export (relative to the current context). |
|||
* @param array $attendances Array of attendances to export the logs for. |
|||
*/ |
|||
private static function export_attendance_logs(string $path, array $attendances) { |
|||
$attendancesbycontextid = self::group_by_property($attendances, 'contextid'); |
|||
|
|||
foreach ($attendancesbycontextid as $contextid => $sessions) { |
|||
$context = context::instance_by_id($contextid); |
|||
$sessionsbyid = self::group_by_property($sessions, 'sessionid'); |
|||
|
|||
foreach ($sessionsbyid as $sessionid => $sessions) { |
|||
writer::with_context($context)->export_data( |
|||
[get_string('session', 'attendance') . ' ' . $sessionid, $path], |
|||
(object)[array_map([self::class, 'transform_db_row_to_session_data'], $sessions)] |
|||
); |
|||
}; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Helper function to export attendances (and associated warnings for the user). |
|||
* |
|||
* The array of "attendances" is actually the result returned by the SQL in export_user_data. |
|||
* It is more of a list of sessions. Which is why it needs to be grouped by context id. |
|||
* |
|||
* @param stdClass $user The user to export attendances for. This is needed to retrieve context data. |
|||
* @param array $attendances Array of attendances to export. |
|||
* @param array $warningsmap Mapping between an attendance id and warnings. |
|||
*/ |
|||
private static function export_attendances(stdClass $user, array $attendances, array $warningsmap) { |
|||
$attendancesbycontextid = self::group_by_property($attendances, 'contextid'); |
|||
|
|||
foreach ($attendancesbycontextid as $contextid => $attendance) { |
|||
$context = context::instance_by_id($contextid); |
|||
|
|||
// It's "safe" to get the attendanceid from the first element in the array - since they're grouped by context. |
|||
// i.e., module context. |
|||
// The reason there can be more than one "attendance" is that the attendances array will contain multiple records |
|||
// for the same attendance instance if there are multiple sessions. It is not the same as a raw record from the |
|||
// attendances table. See the SQL in export_user_data. |
|||
$warnings = array_map([self::class, 'transform_warning_data'], $warningsmap[$attendance[0]->attendanceid] ?? []); |
|||
|
|||
writer::with_context($context)->export_data( |
|||
[], |
|||
(object)array_merge( |
|||
(array) helper::get_context_data($context, $user), |
|||
['warnings' => $warnings] |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,70 @@ |
|||
<?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/>. |
|||
|
|||
/** |
|||
* Defines mobile handlers. |
|||
* |
|||
* @package mod_attendance |
|||
* @copyright 2018 Dan Marsdenb |
|||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
|||
*/ |
|||
|
|||
defined('MOODLE_INTERNAL') || die(); |
|||
|
|||
$addons = [ |
|||
'mod_attendance' => [ |
|||
'handlers' => [ |
|||
'view' => [ |
|||
'displaydata' => [ |
|||
'icon' => $CFG->wwwroot . '/mod/attendance/pix/icon.png', |
|||
'class' => '', |
|||
], |
|||
'delegate' => 'CoreCourseModuleDelegate', |
|||
'method' => 'mobile_view_activity', |
|||
'styles' => [ |
|||
'url' => '/mod/attendance/mobilestyles.css', |
|||
'version' => 22 |
|||
] |
|||
] |
|||
], |
|||
'lang' => [ // Language strings that are used in all the handlers. |
|||
['pluginname', 'attendance'], |
|||
['sessionscompleted', 'attendance'], |
|||
['pointssessionscompleted', 'attendance'], |
|||
['percentagesessionscompleted', 'attendance'], |
|||
['sessionstotal', 'attendance'], |
|||
['pointsallsessions', 'attendance'], |
|||
['percentageallsessions', 'attendance'], |
|||
['maxpossiblepoints', 'attendance'], |
|||
['maxpossiblepercentage', 'attendance'], |
|||
['submitattendance', 'attendance'], |
|||
['strftimeh', 'attendance'], |
|||
['strftimehm', 'attendance'], |
|||
['attendancesuccess', 'attendance'], |
|||
['attendance_no_status', 'attendance'], |
|||
['attendance_already_submitted', 'attendance'], |
|||
['somedisabledstatus', 'attendance'], |
|||
['invalidstatus', 'attendance'], |
|||
['preventsharederror', 'attendance'], |
|||
['closed', 'attendance'], |
|||
['subnetwrong', 'attendance'], |
|||
['enterpassword', 'attendance'], |
|||
['incorrectpasswordshort', 'attendance'], |
|||
['attendancesuccess', 'attendance'], |
|||
['setallstatuses', 'attendance'] |
|||
], |
|||
] |
|||
]; |
@ -0,0 +1,30 @@ |
|||
.attendance_mobile_teacher_form .item-radio { |
|||
display: inline-block; |
|||
margin-top: 10px; |
|||
margin-left: 5px; |
|||
padding: 0; |
|||
width: 70px; |
|||
} |
|||
.attendance_mobile_teacher_form .item-inner { |
|||
padding: 0; |
|||
} |
|||
.attendance_mobile_teacher_form .radiolabel .item-inner { |
|||
text-align: center; |
|||
} |
|||
.attendance_mobile_teacher_form .item-inner .input-wrapper label, |
|||
.attendance_mobile_teacher_form .radio { |
|||
margin: 0; |
|||
} |
|||
.attendance_mobile_teacher_form .radio .radio-icon { |
|||
display: none; |
|||
} |
|||
|
|||
.attendance_mobile_teacher_form .messages .label, |
|||
.attendance_mobile_user_form .messages .label, |
|||
.attendance_mobile_view_page .messages .label { |
|||
white-space: normal; |
|||
} |
|||
|
|||
.attendance_mobile_teacher_form .attendance_user_row { |
|||
padding-bottom: 5px; |
|||
} |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
@ -0,0 +1,105 @@ |
|||
{{! |
|||
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/>. |
|||
}} |
|||
{{! |
|||
@template mod_attendance/mobile_user_form |
|||
|
|||
The page to take attendance |
|||
|
|||
Classes required for JS: |
|||
* None |
|||
|
|||
Data attibutes required for JS: |
|||
* All data attributes are required |
|||
|
|||
Context variables required for this template: |
|||
* attendance |
|||
* summary |
|||
* cmid |
|||
|
|||
Example context (json): |
|||
{ |
|||
"attendance": { |
|||
"id": "1", |
|||
"course": "2", |
|||
"name": "Class Attendance", |
|||
"intro": "Intro" |
|||
}, |
|||
"cmid": "25", |
|||
"courseid": "4", |
|||
"sessid": "43", |
|||
"btnargs" : "" |
|||
} |
|||
}} |
|||
{{=<% %>=}} |
|||
<div class="attendance_mobile_teacher_form"> |
|||
<span class="description"> |
|||
<core-course-module-description description="<% attendance.intro %>" component="mod_attendance" componentId="<% cmid %>"></core-course-module-description> |
|||
</span> |
|||
<%#showmessage%> |
|||
<%#messages%> |
|||
<span class="messages"> |
|||
<ion-item> |
|||
{{ 'plugin.mod_attendance.<% string %>' | translate }} |
|||
</ion-item> |
|||
</span> |
|||
<%/messages%> |
|||
<%/showmessage%> |
|||
<span class="attendance_selectall"> |
|||
<ion-item> |
|||
{{ 'plugin.mod_attendance.setallstatuses' | translate }} |
|||
</ion-item> |
|||
<ion-list radio-group> |
|||
<%#statuses%> |
|||
|
|||
<span class="radiolabel"> |
|||
<ion-item> |
|||
<ion-label><% acronym %></ion-label> |
|||
<ion-radio (ionSelect)="<% selectall %>" value="<% stid %>"></ion-radio> |
|||
</ion-item> |
|||
</span> |
|||
<%/statuses%> |
|||
</ion-list> |
|||
</span> |
|||
<%#users%> |
|||
<span class="attendance_user_row"> |
|||
<!-- User and status of the submission. --> |
|||
<span ion-item text-wrap title="<% fullname %>"> |
|||
<ion-avatar item-start> |
|||
<img src="<% profileimageurl %>" core-external-content role="presentation" onError="this.src='assets/img/user-avatar.png'"> |
|||
</ion-avatar> |
|||
<h2><% fullname %></h2> |
|||
<ng-container *ngTemplateOutlet="submissionStatus"></ng-container> |
|||
</span> |
|||
<ion-list radio-group [(ngModel)]="CONTENT_OTHERDATA.status<% userid %>"> |
|||
<%#statuses%> |
|||
<span class="radiolabel"> |
|||
<ion-item> |
|||
<ion-label><% acronym %></ion-label> |
|||
<ion-radio value="<% stid %>"></ion-radio> |
|||
</ion-item> |
|||
</span> |
|||
<%/statuses%> |
|||
</ion-list> |
|||
</span> |
|||
<%/users%> |
|||
<ion-item> |
|||
<button ion-button core-site-plugins-new-content component="mod_attendance" method="mobile_view_activity" [args]="{cmid: <% cmid %>, courseid: <% courseid %>, sessid: <% sessid %><% btnargs %>}"> |
|||
{{ 'plugin.mod_attendance.submitattendance' | translate }} |
|||
</button> |
|||
</ion-item> |
|||
|
|||
</div> |
@ -0,0 +1,82 @@ |
|||
{{! |
|||
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/>. |
|||
}} |
|||
{{! |
|||
@template mod_attendance/mobile_user_form |
|||
|
|||
The page to take attendance |
|||
|
|||
Classes required for JS: |
|||
* None |
|||
|
|||
Data attibutes required for JS: |
|||
* All data attributes are required |
|||
|
|||
Context variables required for this template: |
|||
* attendance |
|||
* summary |
|||
* cmid |
|||
|
|||
Example context (json): |
|||
{ |
|||
"attendance": { |
|||
"id": "1", |
|||
"course": "2", |
|||
"name": "Class Attendance", |
|||
"intro": "Intro" |
|||
}, |
|||
"cmid": "25", |
|||
"courseid": "4", |
|||
"sessid": "43" |
|||
} |
|||
}} |
|||
{{=<% %>=}} |
|||
<div class="attendance_mobile_user_form"> |
|||
<core-course-module-description description="<% attendance.intro %>" component="mod_attendance" componentId="<% cmid %>"></core-course-module-description> |
|||
<%#showmessage%> |
|||
<%#messages%> |
|||
<span class="messages"> |
|||
<ion-item> |
|||
{{ 'plugin.mod_attendance.<% string %>' | translate }} |
|||
</ion-item> |
|||
</span> |
|||
<%/messages%> |
|||
<%/showmessage%> |
|||
<%#showpassword%> |
|||
<ion-list [(ngModel)]="studentpass"> |
|||
<ion-item> |
|||
<ion-label>{{ 'plugin.mod_attendance.enterpassword' | translate }}:</ion-label> |
|||
<ion-input type="text" name="studentpass"></ion-input> |
|||
</ion-item> |
|||
</ion-list> |
|||
<%/showpassword%> |
|||
<%#showstatuses%> |
|||
<ion-list radio-group [(ngModel)]="status"> |
|||
<%#statuses%> |
|||
<ion-item> |
|||
<ion-label><% description %></ion-label> |
|||
<ion-radio value="<% stid %>"></ion-radio> |
|||
</ion-item> |
|||
<%/statuses%> |
|||
</ion-list> |
|||
<button ion-button core-site-plugins-new-content component="mod_attendance" method="mobile_view_activity" [args]="{cmid: <% cmid %>, courseid: <% courseid %>, sessid: <% sessid %>, status: status, studentpass: studentpass}"> |
|||
{{ 'plugin.mod_attendance.submitattendance' | translate }} |
|||
</button> |
|||
<%/showstatuses%> |
|||
<%#disabledduetotime%> |
|||
{{ 'plugin.mod_attendance.somedisabledstatus' | translate }} |
|||
<%/disabledduetotime%> |
|||
</div> |
@ -0,0 +1,144 @@ |
|||
{{! |
|||
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/>. |
|||
}} |
|||
{{! |
|||
@template mod_attendance/mobile_view_page |
|||
|
|||
The main page to view the attendance activity |
|||
|
|||
Classes required for JS: |
|||
* None |
|||
|
|||
Data attibutes required for JS: |
|||
* All data attributes are required |
|||
|
|||
Context variables required for this template: |
|||
* attendance |
|||
* summary |
|||
* cmid |
|||
|
|||
Example context (json): |
|||
{ |
|||
"attendance": { |
|||
"id": "1", |
|||
"course": "2", |
|||
"name": "Class Attendance", |
|||
"intro": "Intro" |
|||
}, |
|||
"summary": { |
|||
"numtakensessions": "1", |
|||
"pointssessionscompleted": "2", |
|||
"percentagesessionscompleted": "2" |
|||
}, |
|||
"cmid": "25", |
|||
"timestamp": "1234" |
|||
} |
|||
}} |
|||
{{=<% %>=}} |
|||
<div class="attendance_mobile_view_page"> |
|||
<core-course-module-description description="<% attendance.intro %>" component="mod_attendance" componentId="<% cmid %>"></core-course-module-description> |
|||
<%#showmessage%> |
|||
<%#messages%> |
|||
<span class="messages"> |
|||
<ion-item> |
|||
{{ 'plugin.mod_attendance.<% string %>' | translate }} |
|||
</ion-item> |
|||
</span> |
|||
<%/messages%> |
|||
<%/showmessage%> |
|||
<%#sessions%> |
|||
<ion-item> |
|||
<h2><% time %></h2> |
|||
<h3><% groupname %></h3> |
|||
<h3><% currentstatus %></h3> |
|||
<%#sessid%> |
|||
<button ion-button core-site-plugins-new-content component="mod_attendance" method="<% attendancefunction %>" [args]="{cmid: <% cmid %>, courseid: <% courseid %>, sessid: <% sessid %>, timestamp: <% timestamp %>}"> |
|||
{{ 'plugin.mod_attendance.submitattendance' | translate }} |
|||
</button> |
|||
<%/sessid%> |
|||
</ion-item> |
|||
<%/sessions%> |
|||
<ion-item> |
|||
<ion-grid> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.sessionscompleted' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.numtakensessions %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.pointssessionscompleted' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.pointssessionscompleted %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.percentagesessionscompleted' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.percentagesessionscompleted %> |
|||
</ion-col> |
|||
</ion-row> |
|||
|
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.sessionstotal' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.numallsessions %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.pointsallsessions' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.percentagesessionscompleted %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.percentageallsessions' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.allsessionspercentage %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.maxpossiblepoints' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.maxpossiblepoints %> |
|||
</ion-col> |
|||
</ion-row> |
|||
<ion-row> |
|||
<ion-col col-9 class="text-left"> |
|||
{{ 'plugin.mod_attendance.maxpossiblepercentage' | translate }} |
|||
</ion-col> |
|||
<ion-col col-2 class="text-left"> |
|||
<% summary.maxpossiblepercentage %> |
|||
</ion-col> |
|||
</ion-row> |
|||
|
|||
</ion-grid> |
|||
</ion-item> |
|||
</div> |
Loading…
Reference in new issue