Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Several improvements (watching slots, import via CSV, mobile support, ...) #42

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
25508e6
Setting to require attendance for activity completion
FMCorz Feb 3, 2020
385cc4c
Completion state is updated when students attend appointments
FMCorz Feb 3, 2020
954949b
Slots can be imported from a CSV file
FMCorz Feb 4, 2020
4025fab
Individual appointments can be revoked in group slots
FMCorz Feb 4, 2020
d2c7662
Add support for sorting the slots by date, location and teacher
FMCorz Feb 5, 2020
9f83cc3
Add support for filtering the slots by date, location and teacher
FMCorz Feb 5, 2020
91f3f99
Adding user interface for students to watch slots
FMCorz Feb 6, 2020
80cdd13
Logic for detecting when to notify a watcher
FMCorz Feb 6, 2020
2afc761
Update the privacy API provider to include watchers
FMCorz Feb 6, 2020
34b6a9a
Include watchers in backups containing user information
FMCorz Feb 7, 2020
30c560c
Students can view and book slots from the mobile app
FMCorz Feb 11, 2020
e001a3a
Suggest to visit the site when booking not supported or to edit/cancel
FMCorz Feb 12, 2020
8ec2571
Teachers can view their slots, mark as seen and grade
FMCorz Feb 12, 2020
46803b7
Refresh the slot page when it loads to update data
FMCorz Feb 12, 2020
5530310
Teachers can browse past slots when none are upcoming
FMCorz Feb 14, 2020
879e796
Display a better message when teacher does not have any slots
FMCorz Feb 14, 2020
e5b4250
Loosen check for whether teacher can import for others
FMCorz Feb 14, 2020
64bb531
Update description of slots in example CSV import file
FMCorz Feb 14, 2020
260963f
Fix issue with filtering 'on' start time and by teacher
FMCorz Feb 14, 2020
7cfc619
Include canwatch property when backing up the activity
FMCorz Feb 14, 2020
72b418b
Proper handling of missing or invalid CSV column data
FMCorz Feb 14, 2020
cf532e9
Student notes were mistakenly reported as being required
FMCorz Feb 14, 2020
f64db89
Various stylistic changes to please some code checkers
FMCorz Feb 14, 2020
6758282
Forgetting string parameter caused a rendering error in mobile
FMCorz Feb 19, 2020
0dc53bd
On landing page display to teachers the number of slots taken
FMCorz Feb 19, 2020
5cd7509
Ensure that info text about the limitations is wrapped
FMCorz Feb 19, 2020
d8b2bda
Use 'Full' instead of 'Booked' for students viewing fully booked slots
FMCorz Feb 25, 2020
f18ce5c
Support for limiting the number of slots students can watch
FMCorz Mar 6, 2020
0dc2c86
Minor changes for styling and compatibility with Moodle 4.x
FMCorz May 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions amd/build/revoke.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions amd/build/revoke.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 103 additions & 0 deletions amd/src/revoke.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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/>.

/**
* Revoke an appointment.
*
* This module allows for revoking an appointment from a student list.
*
* @module mod_scheduler/revoke
* @copyright 2019 Royal College of Art
* @author Frédéric Massart <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

define(['jquery', 'core/ajax', 'core/notification', 'core/str'], function($, Ajax, Notification, Str) {

var actionSelector = '.otherstudent a.revoke-student';
var studentWrapperSelector = '.otherstudent';

/**
* Revoker.
*
* @param {jQuery} root The root wrapper as jQuery element.
* @param {Number} cmid The cmid.
*/
function Revoker(root, cmid) {
this.cmid = cmid;

root.on('click', actionSelector, function(e) {
e.preventDefault();
var wrapper = $(e.target).closest(studentWrapperSelector);
this.triggerRevoke(wrapper);
}.bind(this));
}

/**
* Trigger the revoke from a node.
*
* @param {jQuery} studentWrapper The student wrapper node.
*/
Revoker.prototype.triggerRevoke = function(studentWrapper) {
var appId = parseInt(studentWrapper.data('appointmentid'), 10);
if (!appId) {
return;
}

Str.get_strings([
{key: 'confirmation', component: 'core_admin'},
{key: 'confirmsinglerevoke', component: 'mod_scheduler'},
{key: 'yes', component: 'core'},
{key: 'no', component: 'core'}
]).then(function(str) {
Notification.confirm(str[0], str[1], str[2], str[3], function() {
// The user confirmed.
studentWrapper.hide();
this.revokeAppointment(appId).then(function() {
studentWrapper.remove();
return;
}).fail(function(e) {
studentWrapper.show();
Notification.exception(e);
});
}.bind(this), function() {
// The user cancelled.
});
}.bind(this)).fail(Notification.exception);
};

/**
* Revoke an appointment.
*
* @param {Number} appId The appointment ID.
* @return {Deferred}
*/
Revoker.prototype.revokeAppointment = function(appId) {
return Ajax.call([{
methodname: 'mod_scheduler_revoke_appointment',
args: {
cmid: this.cmid,
appointmentid: appId
}
}])[0];
};

return {
init: function(wrapperSelector, cmid) {
new Revoker($(wrapperSelector), cmid);
}
};

});
10 changes: 9 additions & 1 deletion backup/moodle2/backup_scheduler_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected function define_structure() {
'scale', 'gradingstrategy', 'bookingrouping', 'usenotes',
'usebookingform', 'bookinginstructions', 'bookinginstructionsformat',
'usestudentnotes', 'requireupload', 'uploadmaxfiles', 'uploadmaxsize',
'usecaptcha', 'timemodified'));
'usecaptcha', 'timemodified', 'completionattended', 'canwatch'));

$slots = new backup_nested_element('slots');

Expand All @@ -63,13 +63,19 @@ protected function define_structure() {
'appointmentnote', 'appointmentnoteformat', 'teachernote', 'teachernoteformat',
'studentnote', 'studentnoteformat', 'timecreated', 'timemodified'));

$watchers = new backup_nested_element('watchers');
$watcher = new backup_nested_element('watcher', ['id'], ['slotid', 'userid']);

// Build the tree.

$scheduler->add_child($slots);
$slots->add_child($slot);

$slot->add_child($appointments);
$slot->add_child($watchers);

$appointments->add_child($appointment);
$watchers->add_child($watcher);

// Define sources.
$scheduler->set_source_table('scheduler', array('id' => backup::VAR_ACTIVITYID));
Expand All @@ -79,6 +85,7 @@ protected function define_structure() {
if ($userinfo) {
$slot->set_source_table('scheduler_slots', array('schedulerid' => backup::VAR_PARENTID));
$appointment->set_source_table('scheduler_appointment', array('slotid' => backup::VAR_PARENTID));
$watcher->set_source_table('scheduler_watcher', ['slotid' => backup::VAR_PARENTID]);
}

// Define id annotations.
Expand All @@ -87,6 +94,7 @@ protected function define_structure() {
if ($userinfo) {
$slot->annotate_ids('user', 'teacherid');
$appointment->annotate_ids('user', 'studentid');
$watcher->annotate_ids('user', 'userid');
}

// Define file annotations.
Expand Down
18 changes: 18 additions & 0 deletions backup/moodle2/restore_scheduler_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected function define_structure() {
$appointment = new restore_path_element('scheduler_appointment',
'/activity/scheduler/slots/slot/appointments/appointment');
$paths[] = $appointment;
$paths[] = new restore_path_element('scheduler_watcher', '/activity/scheduler/slots/slot/watchers/watcher');
}

// Return the paths wrapped into standard activity structure.
Expand Down Expand Up @@ -133,6 +134,23 @@ protected function process_scheduler_appointment($data) {
$this->set_mapping('scheduler_appointment', $oldid, $newitemid, true);
}

/**
* Process watcher.
*
* @param stdClass $data
*/
protected function process_scheduler_watcher($data) {
global $DB;

$data = (object)$data;
$oldid = $data->id;

$data->slotid = $this->get_new_parentid('scheduler_slot');
$data->userid = $this->get_mappingid('user', $data->userid);

$newitemid = $DB->insert_record('scheduler_watcher', $data);
}

/**
* after_execute
*/
Expand Down
24 changes: 1 addition & 23 deletions bookingform.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ protected function definition() {
'context' => $scheduler->get_context(),
'collapsed' => true);

$this->uploadoptions = array('subdirs' => 0,
'maxbytes' => $scheduler->uploadmaxsize,
'maxfiles' => $scheduler->uploadmaxfiles);
$this->uploadoptions = mod_scheduler_get_student_upload_options($scheduler);

// Text field for student-supplied data.
if ($scheduler->uses_studentnotes()) {
Expand Down Expand Up @@ -157,24 +155,4 @@ public function prepare_booking_data(appointment $appointment) {
return $newdata;
}

/**
* save_booking_data
*
* @param stdClass $formdata
* @param appointment $appointment
*/
public function save_booking_data(stdClass $formdata, appointment $appointment) {
$scheduler = $appointment->get_scheduler();
if ($scheduler->uses_studentnotes() && isset($formdata->studentnote_editor)) {
$editor = $formdata->studentnote_editor;
$appointment->studentnote = $editor['text'];
$appointment->studentnoteformat = $editor['format'];
}
if ($scheduler->uses_studentfiles()) {
file_save_draft_area_files($formdata->studentfiles, $scheduler->context->id,
'mod_scheduler', 'studentfiles', $appointment->id,
$this->uploadoptions);
}
$appointment->save();
}
}
Loading