From 2084b2fe36b277d6a8594b4a19bfa846cd9b9241 Mon Sep 17 00:00:00 2001 From: Igor Sazonov Date: Sat, 31 Mar 2018 15:46:35 +0300 Subject: [PATCH] Roles and roles limits support / Slot users observer Added feature Roles and roles limits for slots also added students slots conflicts observer --- appointmentforms.php | 2 + backup/moodle2/backup_scheduler_stepslib.php | 2 +- db/install.xml | 14 ++ db/upgrade.php | 24 ++++ lang/en/scheduler.php | 5 + lib.php | 84 +++++++++++ mod_form.php | 92 +++++++++++++ model/scheduler_appointment.php | 24 +++- model/scheduler_instance.php | 16 ++- renderable.php | 4 + renderer.php | 138 ++++++++++++------- slotforms.php | 45 +++++- studentview.controller.php | 12 +- teacherview.controller.php | 2 + version.php | 2 +- 15 files changed, 406 insertions(+), 60 deletions(-) diff --git a/appointmentforms.php b/appointmentforms.php index fb72df3e..38177e25 100644 --- a/appointmentforms.php +++ b/appointmentforms.php @@ -133,6 +133,8 @@ public function save_appointment_data(stdClass $formdata, scheduler_appointment $scheduler = $appointment->get_scheduler(); $cid = $scheduler->context->id; $appointment->set_data($formdata); + $appointment->roleid = ($scheduler->uses_roles() && isset($formdata->roleid) && + !empty($formdata->roleid)) ? intval($formdata->roleid) : 0; $appointment->attended = isset($formdata->attended); if ($scheduler->uses_appointmentnotes() && isset($formdata->appointmentnote_editor)) { $editor = $formdata->appointmentnote_editor; diff --git a/backup/moodle2/backup_scheduler_stepslib.php b/backup/moodle2/backup_scheduler_stepslib.php index 9e5c8194..53474e95 100644 --- a/backup/moodle2/backup_scheduler_stepslib.php +++ b/backup/moodle2/backup_scheduler_stepslib.php @@ -42,7 +42,7 @@ protected function define_structure() { $appointments = new backup_nested_element('appointments'); $appointment = new backup_nested_element('appointment', array('id'), array( - 'studentid', 'attended', 'grade', + 'studentid', 'roleid', 'attended', 'grade', 'appointmentnote', 'appointmentnoteformat', 'teachernote', 'teachernoteformat', 'studentnote', 'studentnoteformat', 'timecreated', 'timemodified')); diff --git a/db/install.xml b/db/install.xml index 5df0139f..44cae8a2 100644 --- a/db/install.xml +++ b/db/install.xml @@ -43,6 +43,8 @@ + + @@ -64,6 +66,7 @@ + @@ -83,5 +86,16 @@ + + + + + + + + + + +
\ No newline at end of file diff --git a/db/upgrade.php b/db/upgrade.php index 734f9e54..ee2d2db1 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -56,6 +56,30 @@ function xmldb_scheduler_upgrade($oldversion=0) { $result = true; + if ($oldversion < 2017051400) { + $table = new xmldb_table('scheduler'); + $field = new xmldb_field('rolessupport', XMLDB_TYPE_INTEGER, '1', null, null, null, '0', 'schedulermode'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $table2 = new xmldb_table('scheduler_slots'); + $field2 = new xmldb_field('ignoreconflicts', XMLDB_TYPE_INTEGER, '1', null, null, null, '0', 'teacherid'); + if (!$dbman->field_exists($table2, $field2)) { + $dbman->add_field($table2, $field2); + } + $field3 = new xmldb_field('notignoreconflictsstudents', XMLDB_TYPE_INTEGER, '1', null, null, null, '0', 'ignoreconflicts'); + if (!$dbman->field_exists($table2, $field3)) { + $dbman->add_field($table2, $field3); + } + $table3 = new xmldb_table('scheduler_appointment'); + $field3 = new xmldb_field('roleid', XMLDB_TYPE_INTEGER, '11', null, null, null, '0', 'studentid'); + if (!$dbman->field_exists($table3, $field3)) { + $dbman->add_field($table3, $field3); + } + + upgrade_mod_savepoint(true, 2017051400, 'scheduler'); + } + /* ******************* 2.0 upgrade line ********************** */ if ($oldversion < 2011081302) { diff --git a/lang/en/scheduler.php b/lang/en/scheduler.php index b0759ce0..ab74f85e 100644 --- a/lang/en/scheduler.php +++ b/lang/en/scheduler.php @@ -99,6 +99,7 @@ $string['bookinginstructions_help'] = 'This text will be displayed to students before they make a booking. It can, for example, instruct students how to fill out the optional message field or which files to upload.'; $string['bookslot'] = 'Book slot'; $string['bookaslot'] = 'Book a slot'; +$string['slotroleslimit'] = 'Limit for this role is exhausted'; $string['bookingdetails'] = 'Booking details'; $string['bookwithteacher'] = 'Teacher'; $string['break'] = 'Break between slots'; @@ -247,6 +248,8 @@ $string['ignoreconflicts'] = 'Ignore scheduling conflicts'; $string['ignoreconflicts_help'] = 'If this box is ticked, then the slot will be moved to the requested date and time, even if other slots exist at the same time. This may lead to overlapping appointments for some teachers or students, and should therefore be used with care.'; $string['ignoreconflicts_link'] = 'mod/scheduler/conflict'; +$string['notignoreconflictsstudents'] = 'Not ignore scheduling conflicts for students'; +$string['notignoreconflictsstudents_help'] = 'At the moment, the tracking function works only for teachers, students conflicts are ignored, this option solves this problem'; $string['includeemptyslots'] = 'Include empty slots'; $string['includeslotsfor'] = 'Include slots for'; $string['incourse'] = ' in course '; @@ -435,6 +438,8 @@ $string['usenotesstudent'] = 'Appointment note, visible to teacher and student'; $string['usenotesteacher'] = 'Confidential note, visible to teachers only'; $string['usenotesboth'] = 'Both types of notes'; +$string['rolessupport'] = 'Support roles'; +$string['limit'] = 'Limit'; $string['usestudentnotes'] = 'Let students enter a message'; $string['usestudentnotes_help'] = 'If enabled, the booking screen will contain a text box in which students can enter a message. Use the "booking instructions" above to instruct students what information they should supply.'; $string['viewbooking'] = 'See details'; diff --git a/lib.php b/lib.php index 24b8c3d1..6603f0cc 100644 --- a/lib.php +++ b/lib.php @@ -712,3 +712,87 @@ function scheduler_pluginfile($course, $cm, $context, $filearea, $args, $forcedo send_stored_file($file, 0, 0, $forcedownload, $options); } +function get_scheduler_user_roles($userid, $schedulerid, $slotid) { + global $DB; + + $ret = array(); + $sql = 'SELECT sr.id,sr.rname ' + . 'FROM {scheduler_appointment} sa LEFT JOIN {scheduler_roles} sr ' + . 'ON sa.roleid = sr.id LEFT JOIN {scheduler_slots} ss ' + . 'ON sa.slotid = ss.id LEFT JOIN {scheduler} s ' + . 'ON ss.schedulerid = s.id ' + . 'WHERE sa.studentid = ? AND s.id = ? AND sa.slotid = ?'; + if ($roles = $DB->get_records_sql($sql, array($userid, $schedulerid, $slotid))) { + foreach ($roles as $roleid => $role) { + if (!empty($roleid)) { + $ret[$role->id] = $role->rname; + } + } + } + + return $ret; +} + +/** + * Get roles for course module + * + * @global stdClass $DB + * @param int $cmid Course module id + * @param boolean $simple + * @param boolean $checklimits + * @param boolean $userid + * @param string $key + * @return array + */ +function get_scheduler_roles($cmid, $simple = false, $checklimits = false, $userid = false, $key = 'rname') { + global $DB; + + if ($cmid > 0) { + $sql = 'SELECT ' + . 'sr.id, sr.* ' + . 'FROM {scheduler_roles} sr ' + . 'LEFT JOIN {scheduler} s ' + . 'ON sr.schedulerid = s.id ' + . 'LEFT JOIN {course_modules} cm ' + . 'ON s.id = cm.instance ' + . 'LEFT JOIN {modules} m ' + . 'ON cm.module = m.id WHERE cm.id = ? AND m.name = ?'; + $conditions = array($cmid, 'scheduler'); + $array = array_values($DB->get_records_sql($sql, $conditions)); + if ($simple) { + $ret = array(); + foreach ($array as $item) { + $ret[$item->id] = $item->$key; + } + if ($checklimits) { + $tmp = $ret; + foreach (array_keys($tmp) as $roleid) { + if (!check_slot_role_limit($roleid, $userid)) { + unset($ret[$roleid]); + } + } + } + return $ret; + } + return $array; + } + + return array(); +} + +function check_slot_role_limit($roleid, $studentid = false) { + global $DB; + + if ($role = $DB->get_record('scheduler_roles', array('id' => $roleid), '*', MUST_EXIST)) { + if ($role->rlimit > 0 && $studentid) { + $appointments = $DB->get_records('scheduler_appointment', + array('roleid' => $roleid, 'studentid' => $studentid)); + if (count($appointments) < $role->rlimit) { + return true; + } + } else { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/mod_form.php b/mod_form.php index be63d97f..6ad3b76d 100644 --- a/mod_form.php +++ b/mod_form.php @@ -24,12 +24,15 @@ class mod_scheduler_mod_form extends moodleform_mod { protected $editoroptions; + protected $_rolescount = 5; function definition() { global $CFG, $COURSE, $OUTPUT; $mform =& $this->_form; + $cmid = optional_param('update', 0, PARAM_INT); + $roles = get_scheduler_roles($cmid); // General introduction. $mform->addElement('header', 'general', get_string('general', 'form')); @@ -106,6 +109,30 @@ function definition() { $mform->addElement('select', 'usenotes', get_string('usenotes', 'scheduler'), $noteoptions); $mform->setDefault('usenotes', '1'); + $mform->addElement('header', 'roleshdr', get_string('roles')); + + $mform->addElement('checkbox', 'rolessupport', get_string('rolessupport', 'scheduler')); + $mform->setDefault('rolessupport', false); + //$mform->addHelpButton('rolessupport', 'ignoreconflicts', 'scheduler'); + + for($i=1;$i<=$this->_rolescount;$i++) { + $mform->addElement('text', 'rolename['.($i-1).']', get_string('role').' '.$i.' '. get_string('name'), array('size' => '48')); + $mform->setType('rolename['.($i-1).']', PARAM_TEXT); + $mform->addRule('rolename['.($i-1).']', get_string('error'), 'maxlength', 255); + $mform->disabledIf('rolename['.($i-1).']', 'rolessupport'); + if (isset($roles[$i-1]->rname) && !empty($roles[$i-1]->rname)) { + $mform->setDefault('rolename['.($i-1).']', $roles[$i-1]->rname); + } + + $mform->addElement('text', 'rolelimit['.($i-1).']', get_string('role').' '.$i.' '.get_string('limit', 'scheduler'), array('size' => '20')); + $mform->setType('rolelimit['.($i-1).']', PARAM_INT); + $mform->addRule('rolelimit['.($i-1).']', get_string('error'), 'maxlength', 4); + $mform->disabledIf('rolelimit['.($i-1).']', 'rolessupport'); + if (isset($roles[$i-1]->rlimit) && !empty($roles[$i-1]->rlimit)) { + $mform->setDefault('rolelimit['.($i-1).']', $roles[$i-1]->rlimit); + } + } + // Grade settings. $this->standard_grading_coursemodule_elements(); @@ -206,6 +233,71 @@ public function save_mod_data(stdClass $data, context_module $context) { $this->editoroptions, $editor['text']); $data->bookinginstructionsformat = $editor['format']; $DB->update_record('scheduler', $data); + $schedulerid = 0; + if (isset($data->instance) && !empty($data->instance)) { + $schedulerid = intval($data->instance); + } else { + if (isset($data->id) && !empty($data->id)) { + $schedulerid = intval($data->id); + } + } + if ($schedulerid) { + $cmid = optional_param('update', 0, PARAM_INT); + $roles = get_scheduler_roles($cmid); + if (isset($data->rolessupport) && !empty($data->rolessupport)) { + $rolesnames = array(); + if (isset($data->rolename) && !empty($data->rolename) && is_array($data->rolename)) { + foreach ($data->rolename as $i => $rolename) { + $rolename = trim(clean_param($rolename, PARAM_CLEAN)); + if (!empty($rolename)) { + $rolesnames[$i] = $rolename; + } else { + if (isset($roles[$i]) && !empty($roles[$i])) { + if ($DB->get_record('scheduler_roles', array('id' => $roles[$i]->id))) { + $DB->delete_records('scheduler_roles', array('id' => $roles[$i]->id)); + } + } + } + } + } + $roleslimits = array(); + if (isset($data->rolelimit) && !empty($data->rolelimit) && is_array($data->rolelimit)) { + foreach ($data->rolelimit as $i => $rolelimit) { + $rolelimit = intval($rolelimit); + if ($rolelimit > 0) { + $roleslimits[$i] = $rolelimit; + } else { + if (isset($roles[$i]) && !empty($roles[$i])) { + if ($DB->get_record('scheduler_roles', array('id' => $roles[$i]->id))) { + $DB->delete_records('scheduler_roles', array('id' => $roles[$i]->id)); + } + } + } + } + } + if ($rolesnames) { + $args = new stdClass(); + $args->schedulerid = $schedulerid; + foreach ($rolesnames as $i => $rolename) { + if (isset($roleslimits[$i]) && !empty($roleslimits[$i])) { + $args->rname = $rolename; + $args->rlimit = $roleslimits[$i]; + if ($row = $DB->get_record('scheduler_roles', array('schedulerid' => $schedulerid, 'rname' => $rolename))) { + $args->id = $row->id; + $DB->update_record('scheduler_roles', $args); + } else { + $DB->insert_record('scheduler_roles', $args); + } + } + } + } + } else { + $DB->set_field('scheduler', 'rolessupport', 0); + if ($DB->get_records('scheduler_roles', array('schedulerid' => $schedulerid))) { + $DB->delete_records('scheduler_roles', array('schedulerid' => $schedulerid)); + } + } + } } } diff --git a/model/scheduler_appointment.php b/model/scheduler_appointment.php index a2c96924..49c0bc51 100644 --- a/model/scheduler_appointment.php +++ b/model/scheduler_appointment.php @@ -32,14 +32,36 @@ public function __construct(scheduler_slot $slot) { $this->data = new stdClass(); $this->set_parent($slot); $this->data->slotid = $slot->get_id(); + $this->data->roleid = 0; $this->data->attended = 0; $this->data->appointmentnoteformat = FORMAT_HTML; $this->data->teachernoteformat = FORMAT_HTML; } public function save() { + global $DB; + $this->data->slotid = $this->get_parent()->get_id(); - parent::save(); + if (isset($_REQUEST['roleid']) && !empty($_REQUEST['roleid'])) { + if (!is_array($_REQUEST['roleid'])) { + $this->data->roleid = intval($_REQUEST['roleid']); + } else { + if ($apps = array_values($DB->get_records('scheduler_appointment', + array('slotid' => intval($_REQUEST['slotid']))))) { + foreach ($apps as $i => $app) { + if ($app->studentid == $this->data->studentid) { + $this->data->roleid = intval($_REQUEST['roleid'][$i]); + break; + } + } + } + } + } + $uses_roles = $this->get_scheduler()->uses_roles(); + if (!$uses_roles || ($this->data->roleid && $uses_roles && + check_slot_role_limit($this->data->roleid, $this->data->studentid))) { + parent::save(); + } $scheddata = $this->get_scheduler()->get_data(); scheduler_update_grades($scheddata, $this->studentid); } diff --git a/model/scheduler_instance.php b/model/scheduler_instance.php index 36f31d00..48fd41a4 100644 --- a/model/scheduler_instance.php +++ b/model/scheduler_instance.php @@ -288,6 +288,15 @@ public function uses_teachernotes() { return (floor($this->data->usenotes / 2) % 2 == 1); } + /** + * Is scheduler supports roles or not + * @return int|boolean + */ + public function uses_roles() { + return (isset($this->data->rolessupport) && + !empty($this->data->rolessupport)) ? intval($this->data->rolessupport) : 0; + } + /** * Whether this scheduler uses booking forms at all * @return bool whether the booking form is used @@ -899,8 +908,11 @@ function get_conflicts($starttime, $endtime, $teacher = 0, $student = 0, $teacherscope = ""; } - $studentjoin = ($student != 0) ? "JOIN {scheduler_appointment} a ON a.slotid = sl.id AND a.studentid = :studentid " : ''; - $params['studentid'] = $student; + $studentjoin = ''; + if ($student != 0) { + $studentjoin = "JOIN {scheduler_appointment} a ON a.slotid = sl.id AND a.studentid = :studentid "; + $params['studentid'] = $student; + } $timeclause = "( (sl.starttime <= :starttime1 AND sl.starttime + sl.duration * 60 > :starttime2) OR (sl.starttime < :endtime1 AND sl.starttime + sl.duration * 60 >= :endtime2) OR diff --git a/renderable.php b/renderable.php index af123420..f7e339d0 100644 --- a/renderable.php +++ b/renderable.php @@ -78,6 +78,7 @@ public function add_slot(scheduler_slot $slotmodel, scheduler_appointment $appoi $slot->slotnote = $slotmodel->notes; $slot->slotnoteformat = $slotmodel->notesformat; $slot->teacher = $slotmodel->get_teacher(); + $slot->roleid = $appointmentmodel->roleid; $slot->appointmentid = $appointmentmodel->id; if ($this->scheduler->uses_appointmentnotes()) { $slot->appointmentnote = $appointmentmodel->appointmentnote; @@ -172,6 +173,8 @@ public function add_student(scheduler_appointment $appointment, $highlight, $che } else { $student->grade = null; } + $student->roles = get_scheduler_user_roles($student->user->id, + $appointment->get_scheduler()->get_id(), $appointment->__get('slotid')); $student->highlight = $highlight; $student->checked = $checked; $student->entryid = $appointment->id; @@ -250,6 +253,7 @@ public function add_slot(scheduler_slot $slotmodel, $canbook, $bookedbyme, $grou $slot->canbook = $canbook; $slot->groupinfo = $groupinfo; $slot->teacher = $slotmodel->get_teacher(); + $slot->notignoreconflictsstudents = $slotmodel->notignoreconflictsstudents; $slot->otherstudents = $otherstudents; $this->slots[] = $slot; diff --git a/renderer.php b/renderer.php index f665a94b..4e08e198 100644 --- a/renderer.php +++ b/renderer.php @@ -364,6 +364,8 @@ public function teacherview_tabs(scheduler_instance $scheduler, moodle_url $base * @return string the HTML output */ public function render_scheduler_slot_table(scheduler_slot_table $slottable) { + global $DB; + $table = new html_table(); if ($slottable->showslot) { @@ -465,6 +467,9 @@ public function render_scheduler_slot_table(scheduler_slot_table $slottable) { } if ($slottable->showactions) { $actions = ''; + if ($slottable->scheduler->uses_roles()) { + $actions .= get_string('role') . ': ' . $DB->get_field('scheduler_roles', 'rname', array('id' => $slot->roleid)) . '  '; + } if ($slot->canedit) { $buttonurl = new moodle_url($slottable->actionurl, array('what' => 'editbooking', 'appointmentid' => $slot->appointmentid)); @@ -539,6 +544,9 @@ public function render_scheduler_student_list(scheduler_student_list $studentlis } else { $name = fullname($student->user); } + if (!empty($student->roles)) { + $name .= ' (' . join(', ', $student->roles) . ')'; + } $studicons = ''; $studprovided = array(); if ($student->notesprovided) { @@ -586,12 +594,13 @@ public function render_scheduler_student_list(scheduler_student_list $studentlis * @return string */ public function render_scheduler_slot_booker(scheduler_slot_booker $booker) { + global $USER; $table = new html_table(); - $table->head = array( get_string('date', 'scheduler'), get_string('start', 'scheduler'), - get_string('end', 'scheduler'), get_string('location', 'scheduler'), - get_string('comments', 'scheduler'), s($booker->scheduler->get_teacher_name()), - get_string('groupsession', 'scheduler'), ''); + $table->head = array(get_string('date', 'scheduler'), get_string('start', 'scheduler'), + get_string('end', 'scheduler'), get_string('location', 'scheduler'), + get_string('comments', 'scheduler'), s($booker->scheduler->get_teacher_name()), + get_string('groupsession', 'scheduler'), ''); $table->align = array ('left', 'left', 'left', 'left', 'left', 'left', 'left', 'left'); $table->id = 'slotbookertable'; $table->data = array(); @@ -602,62 +611,89 @@ public function render_scheduler_slot_booker(scheduler_slot_booker $booker) { $canappoint = false; foreach ($booker->slots as $slot) { + if (!$slot->notignoreconflictsstudents || ($slot->notignoreconflictsstudents + && !count($booker->scheduler->get_conflicts($slot->starttime, + $slot->endtime, 0, $USER->id)))) { + + $rowdata = array(); + + $startdate = $this->userdate($slot->starttime); + $starttime = $this->usertime($slot->starttime); + $endtime = $this->usertime($slot->endtime); + // Simplify display of dates, start and end times. + if ($startdate == $previousdate && $starttime == $previoustime && $endtime == $previousendtime) { + // If this row exactly matches previous, there's nothing to display. + $startdatestr = ''; + $starttimestr = ''; + $endtimestr = ''; + } else if ($startdate == $previousdate) { + // If this date matches previous date, just display times. + $startdatestr = ''; + $starttimestr = $starttime; + $endtimestr = $endtime; + } else { + // Otherwise, display all elements. + $startdatestr = $startdate; + $starttimestr = $starttime; + $endtimestr = $endtime; + } - $rowdata = array(); - - $startdate = $this->userdate($slot->starttime); - $starttime = $this->usertime($slot->starttime); - $endtime = $this->usertime($slot->endtime); - // Simplify display of dates, start and end times. - if ($startdate == $previousdate && $starttime == $previoustime && $endtime == $previousendtime) { - // If this row exactly matches previous, there's nothing to display. - $startdatestr = ''; - $starttimestr = ''; - $endtimestr = ''; - } else if ($startdate == $previousdate) { - // If this date matches previous date, just display times. - $startdatestr = ''; - $starttimestr = $starttime; - $endtimestr = $endtime; - } else { - // Otherwise, display all elements. - $startdatestr = $startdate; - $starttimestr = $starttime; - $endtimestr = $endtime; - } + $rowdata[] = $startdatestr; + $rowdata[] = $starttimestr; + $rowdata[] = $endtimestr; - $rowdata[] = $startdatestr; - $rowdata[] = $starttimestr; - $rowdata[] = $endtimestr; + $rowdata[] = format_string($slot->location); - $rowdata[] = format_string($slot->location); + $rowdata[] = $this->format_notes($slot->notes, $slot->notesformat, $booker->scheduler->get_context(), + 'slotnote', $slot->slotid); - $rowdata[] = $this->format_notes($slot->notes, $slot->notesformat, $booker->scheduler->get_context(), - 'slotnote', $slot->slotid); + $rowdata[] = $this->user_profile_link($booker->scheduler, $slot->teacher); - $rowdata[] = $this->user_profile_link($booker->scheduler, $slot->teacher); + $groupinfo = $slot->bookedbyme ? get_string('complete', 'scheduler') : $slot->groupinfo; + if ($slot->otherstudents) { + $groupinfo .= $this->render($slot->otherstudents); + } - $groupinfo = $slot->bookedbyme ? get_string('complete', 'scheduler') : $slot->groupinfo; - if ($slot->otherstudents) { - $groupinfo .= $this->render($slot->otherstudents); - } + $rowdata[] = $groupinfo; + + if ($slot->canbook) { + $bookaction = $booker->scheduler->uses_bookingform() ? 'bookingform' : 'bookslot'; + $form = html_writer::start_tag('form', array('action' => $booker->actionurl)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'id', 'value' => optional_param('id', 0, PARAM_INT))); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'sesskey', 'value' => sesskey())); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'what', 'value' => $bookaction)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'slotid', 'value' => $slot->slotid)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'notignoreconflictsstudents', 'value' => $slot->notignoreconflictsstudents)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'starttime', 'value' => $slot->starttime)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'endtime', 'value' => $slot->endtime)); + $form .= html_writer::empty_tag('input', array('type' => 'hidden', + 'name' => 'teacherid', 'value' => $slot->teacher->id)); + $roles = get_scheduler_roles(optional_param('id', 0, PARAM_INT), + true, true, $USER->id); + if ($booker->scheduler->uses_roles() && $roles) { + $form .= html_writer::select($roles, 'roleid', null, null); + } + $form .= html_writer::tag('button', get_string('bookslot', 'scheduler'), + array('type' => 'submit', 'class' => 'btn btn-secondary mr-5')); + $form .= html_writer::end_tag('form'); + $rowdata[] = $form; + } else { + $rowdata[] = ''; + } - $rowdata[] = $groupinfo; + $table->data[] = $rowdata; - if ($slot->canbook) { - $bookaction = $booker->scheduler->uses_bookingform() ? 'bookingform' : 'bookslot'; - $bookurl = new moodle_url($booker->actionurl, array('what' => $bookaction, 'slotid' => $slot->slotid)); - $button = new single_button($bookurl, get_string('bookslot', 'scheduler')); - $rowdata[] = $this->render($button); - } else { - $rowdata[] = ''; + $previoustime = $starttime; + $previousendtime = $endtime; + $previousdate = $startdate; } - - $table->data[] = $rowdata; - - $previoustime = $starttime; - $previousendtime = $endtime; - $previousdate = $startdate; } return html_writer::table($table); diff --git a/slotforms.php b/slotforms.php index db9b9b9f..8ec83ec2 100644 --- a/slotforms.php +++ b/slotforms.php @@ -198,6 +198,10 @@ protected function definition() { $mform->setDefault('ignoreconflicts', false); $mform->addHelpButton('ignoreconflicts', 'ignoreconflicts', 'scheduler'); + $mform->addElement('checkbox', 'notignoreconflictsstudents', get_string('notignoreconflictsstudents', 'scheduler')); + $mform->setDefault('notignoreconflictsstudents', false); + $mform->addHelpButton('notignoreconflictsstudents', 'notignoreconflictsstudents', 'scheduler'); + // Common fields. $this->add_base_fields(); @@ -259,6 +263,11 @@ protected function definition() { // Tickbox to remove the student $repeatarray[] = $mform->createElement('advcheckbox', 'deletestudent', '', get_string('deleteonsave', 'scheduler')); + if ($this->scheduler->uses_roles()) { + if ($roles = get_scheduler_roles(optional_param('id', 0, PARAM_INT), true)) { + $repeatarray[] = $mform->createElement('select', 'roleid', get_string('role'), $roles); + } + } if (isset($this->_customdata['repeats'])) { $repeatno = $this->_customdata['repeats']; @@ -321,11 +330,22 @@ public function validation($data, $files) { } } - if (!isset($data['ignoreconflicts'])) { + if (!isset($data['ignoreconflicts']) || (isset($data['notignoreconflictsstudents']) && !empty($data['notignoreconflictsstudents']))) { /* Avoid overlapping slots by warning the user */ - $conflicts = $this->scheduler->get_conflicts( + if (isset($data['notignoreconflictsstudents']) && !empty($data['notignoreconflictsstudents'])) { + $conflicts = array(); + for ($i = 0; $i < $data['appointment_repeats']; $i++) { + if (isset($data['studentid'][$i]) && !empty($data['studentid'][$i])) { + $conflicts = array_merge($conflicts, $this->scheduler->get_conflicts( + $data['starttime'], $data['starttime'] + $data['duration'] * 60, + 0, intval($data['studentid'][$i]), SCHEDULER_ALL, $this->slotid)); + } + } + } else { + $conflicts = $this->scheduler->get_conflicts( $data['starttime'], $data['starttime'] + $data['duration'] * 60, $data['teacherid'], 0, SCHEDULER_ALL, $this->slotid); + } if (count($conflicts) > 0) { @@ -339,6 +359,17 @@ public function validation($data, $files) { $errors['starttime'] = $msg; } } + foreach ($data['roleid'] as $k => $roleid) { + if ($this->scheduler->uses_roles()) { + if (((!isset($data['deletestudent'][$k]) || empty($data['deletestudent'][$k])) + && (isset($data['studentid'][$k]) && !empty($data['studentid'][$k]))) + || (isset($data['studentid'][$k]) && !empty($data['studentid'][$k]))) { + if (!check_slot_role_limit($roleid, intval($data['studentid'][$k]))) { + $errors['roleid['.$k.']'] = get_string('slotroleslimit', 'mod_scheduler'); + } + } + } + } return $errors; } @@ -369,6 +400,7 @@ public function prepare_formdata(scheduler_slot $slot) { foreach ($slot->get_appointments() as $appointment) { $data->appointid[$i] = $appointment->id; $data->studentid[$i] = $appointment->studentid; + $data->roleid[$i] = $appointment->roleid; $data->attended[$i] = $appointment->attended; $draftid = file_get_submitted_draft_itemid('appointmentnote'); @@ -415,6 +447,8 @@ public function save_slot($slotid, $data) { $slot->duration = $data->duration; $slot->exclusivity = $data->exclusivityenable ? $data->exclusivity : 0; $slot->teacherid = $data->teacherid; + $slot->ignoreconflicts = (isset($data->ignoreconflicts) && !empty($data->ignoreconflicts)) ? 1 : 0; + $slot->notignoreconflictsstudents = (isset($data->notignoreconflictsstudents) && !empty($data->notignoreconflictsstudents)) ? 1 : 0; $slot->appointmentlocation = $data->appointmentlocation; $slot->hideuntil = $data->hideuntil; $slot->emaildate = $data->emaildate; @@ -444,6 +478,9 @@ public function save_slot($slotid, $data) { } else { $app = $slot->create_appointment(); $app->studentid = $data->studentid[$i]; + if ($this->scheduler->uses_roles()) { + $app->roleid = (isset($data->roleid[$i]) && !empty($data->roleid[$i])) ? intval($data->roleid[$i]) : 0; + } $app->save(); } $app->attended = isset($data->attended[$i]); @@ -577,6 +614,10 @@ protected function definition() { $mform->addElement('select', 'emaildaterel', get_string('emailreminder', 'scheduler'), $remindersel); $mform->setDefault('remindersel', -1); + + $mform->addElement('checkbox', 'notignoreconflictsstudents', get_string('notignoreconflictsstudents', 'scheduler')); + $mform->setDefault('notignoreconflictsstudents', false); + $mform->addHelpButton('notignoreconflictsstudents', 'notignoreconflictsstudents', 'scheduler'); $this->add_action_buttons(); diff --git a/studentview.controller.php b/studentview.controller.php index 245518a9..2b46fd57 100644 --- a/studentview.controller.php +++ b/studentview.controller.php @@ -77,6 +77,7 @@ function scheduler_book_slot($scheduler, $slotid, $userid, $groupid, $mform, $fo foreach ($userstobook as $studentid) { $appointment = $slot->create_appointment(); $appointment->studentid = $studentid; + $appointment->roleid = 0; $appointment->attended = 0; $appointment->timecreated = time(); $appointment->timemodified = time(); @@ -164,8 +165,15 @@ function scheduler_book_slot($scheduler, $slotid, $userid, $groupid, $mform, $fo // Get the request parameters. $slotid = required_param('slotid', PARAM_INT); - - scheduler_book_slot($scheduler, $slotid, $USER->id, $appointgroup, null, null, $returnurl); + $roleid = optional_param('roleid', 0, PARAM_INT); + $notignoreconflictsstudents = optional_param('notignoreconflictsstudents', 0, PARAM_INT); + if (!$roleid || ($roleid && $scheduler->uses_roles() && check_slot_role_limit($roleid, $USER->id))) { + if (!$notignoreconflictsstudents || ($notignoreconflictsstudents && count($scheduler->get_conflicts( + optional_param('starttime', 0, PARAM_INT), optional_param('endtime', 0, PARAM_INT), + 0, $USER->id)) > 0)) { + scheduler_book_slot($scheduler, $slotid, $USER->id, $appointgroup, null, null, $returnurl); + } + } } /******************************************** Show details of booking *******************************************/ diff --git a/teacherview.controller.php b/teacherview.controller.php index 6f72443c..5217bb8e 100644 --- a/teacherview.controller.php +++ b/teacherview.controller.php @@ -46,6 +46,8 @@ function scheduler_action_doaddsession($scheduler, $formdata, moodle_url $return $slot->notes = ''; $slot->notesformat = FORMAT_HTML; $slot->timemodified = time(); + $slot->notignoreconflictsstudents = (isset($data->notignoreconflictsstudents) + && !empty($data->notignoreconflictsstudents)) ? 1 : 0; for ($d = 0; $d <= $fordays; $d ++) { $starttime = $startfrom + ($d * DAYSECS); diff --git a/version.php b/version.php index 548988c5..5f8bff18 100644 --- a/version.php +++ b/version.php @@ -15,7 +15,7 @@ */ $plugin->component = 'mod_scheduler'; // Full name of the plugin (used for diagnostics). -$plugin->version = 2017051500; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2018032700; // The current module version (Date: YYYYMMDDXX). $plugin->release = '3.x dev'; // Human-friendly version name. $plugin->requires = 2017051200; // Requires Moodle 3.3. $plugin->maturity = MATURITY_ALPHA; // Alpha development code - not for production sites!