Skip to content

Commit

Permalink
FIX sysadmin editing user drops them from courses
Browse files Browse the repository at this point in the history
When editing a user, a sysadmin can unknowingly drop them from courses
on save. This is because the user controller expects the form submit to
contain every single enrolment and this stops happening once you pass
around 1000 courses. Missing enrolments not sent in the form submit are
presumed to be the user dropping the course. While this can happen to
any role that can edit another user, it's most likely to happen to
sysadmins since they have access to every course.

The main fix is to make sure existing enrolments are always in the list
of enrolments to be processed. So missing enrolments not sent in the
form submit gets defaulted to existing enrolments. We also limit
enrolment changes to only the ones that are sent in the form submit.

I tried not to change the behaviour too much, as iPeer is very
sideffects prone and hard to work with. Example: we call
removeStudent/Tutor/Instructor even if the user's role just changed,
they didn't unenrol from the course entirely. I tried to keep that
behavior even though I suspect there are problematic edge cases there.
  • Loading branch information
ionparticle committed Nov 28, 2023
1 parent 7cd42b2 commit ec9c8d4
Showing 1 changed file with 34 additions and 24 deletions.
58 changes: 34 additions & 24 deletions app/controllers/users_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -730,32 +730,42 @@ public function edit($userId = null, $courseId = null) {
$this->data['User'] = array_map('trim', $this->data['User']);

// add list of courses the user is enrolled in but the logged
// in user does not have access to so that the user would not
// be unenrolled from the course when their profile is edited.
// in user does not have access to
$hiddenCourses = $this->_notUnenrolCourses($this->Auth->user('id'), $userId);

// REMOVE OLD STUDENT STATUSES
// unenrol student from course, group, surveygroup
// only students will go in because only they have records in Enrolment
foreach ($enrolCourses as $course) {
if (!in_array($course, $hiddenCourses) && $this->data['CourseEnroll'][$course] != 5) {
$this->User->removeStudent($userId, $course);
// create a map of existing course enrolment, course id => role id
$courseEnrol = [];
foreach ($enrolCourses as $course) $courseEnrol[$course] = 5;
foreach ($tutorCourses as $course) $courseEnrol[$course] = 4;
foreach ($instructors as $course) $courseEnrol[$course] = 3;
// NOTE: In previous implementation, sysadmins can mistakenly
// unenrol users when editing a user. This is because it required
// every submit to contain every enrolment and this stopped
// happening at around 1k courses. To avoid this, this new
// implementation only modify enrolments that are actually sent in
// the submit.
foreach ($this->data['CourseEnroll'] as $course => $newRole) {
if (in_array($course, $hiddenCourses)) {
// logged in user isn't allowed to edit enrolment for course
continue;
}
}

// REMOVE OLD TUTOR STATUSES
// unenrol tutor from course, group
foreach ($tutorCourses as $course) {
if (!in_array($course, $hiddenCourses) && $this->data['CourseEnroll'][$course] != 4) {
$this->User->removeTutor($userId, $course);
elseif (isset($courseEnrol[$course])) {
// modify existing enrolment
$oldRole = $courseEnrol[$course];
if ($oldRole != $newRole) {
// honestly not sure if the remove functions are good
// but trying not to change too much, so calling them
if ($oldRole == 5)
$this->User->removeStudent($userId, $course);
elseif ($oldRole == 4)
$this->User->removeTutor($userId, $course);
elseif ($oldRole == 3)
$this->User->removeInstructor($userId, $course);
$courseEnrol[$course] = $newRole;
}
}
}

// REMOVE OLD INSTRUCTOR STATUSES
// unenrol instructor from course
foreach ($instructors as $course) {
if (!in_array($course, $hiddenCourses) && $this->data['CourseEnroll'][$course] != 3) {
$this->User->removeInstructor($userId, $course);
elseif ($newRole > 0) {
// new enrolment in a course
$courseEnrol[$course] = $newRole;
}
}

Expand All @@ -764,7 +774,7 @@ public function edit($userId = null, $courseId = null) {
$newStudentCourses = array();

// ADD NEW (possibly existing) STATUSES
foreach($this->data['CourseEnroll'] as $currCourseId => $currRoleId) {
foreach($courseEnrol as $currCourseId => $currRoleId) {
if(!is_numeric($currCourseId)) {
continue;
}
Expand Down

0 comments on commit ec9c8d4

Please sign in to comment.