diff --git a/calendar/lib.php b/calendar/lib.php index da878b80b4a..11abe2eeac1 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -3722,23 +3722,8 @@ function calendar_get_allowed_event_types(int $courseid = null) { if (!empty($courseid) && $courseid != SITEID) { $context = \context_course::instance($courseid); - $groups = groups_get_all_groups($courseid); - $types['user'] = has_capability('moodle/calendar:manageownentries', $context); - - if (has_capability('moodle/calendar:manageentries', $context)) { - $types['course'] = true; - - $types['group'] = (!empty($groups) && has_capability('moodle/site:accessallgroups', $context)) - || array_filter($groups, function($group) use ($USER) { - return groups_is_member($group->id); - }); - } else if (has_capability('moodle/calendar:managegroupentries', $context)) { - $types['group'] = (!empty($groups) && has_capability('moodle/site:accessallgroups', $context)) - || array_filter($groups, function($group) use ($USER) { - return groups_is_member($group->id); - }); - } + calendar_internal_update_course_and_group_permission($courseid, $context, $types); } if (has_capability('moodle/calendar:manageentries', \context_course::instance(SITEID))) { @@ -3823,23 +3808,7 @@ function calendar_get_allowed_event_types(int $courseid = null) { context_helper::preload_from_record($coursewithgroup); $context = context_course::instance($coursewithgroup->id); - if (has_capability('moodle/calendar:manageentries', $context)) { - // The user has access to manage calendar entries for the whole course. - // This includes groups if they have the accessallgroups capability. - $types['course'] = true; - if (has_capability('moodle/site:accessallgroups', $context)) { - // The user also has access to all groups so they can add calendar entries to any group. - // The manageentries capability overrides the managegroupentries capability. - $types['group'] = true; - break; - } - - if (empty($types['group']) && has_capability('moodle/calendar:managegroupentries', $context)) { - // The user has the managegroupentries capability. - // If they have access to _any_ group, then they can create calendar entries within that group. - $types['group'] = !empty(groups_get_all_groups($coursewithgroup->id, $USER->id)); - } - } + calendar_internal_update_course_and_group_permission($coursewithgroup->id, $context, $types); // Okay, course and group event types are allowed, no need to keep the loop iteration. if ($types['course'] == true && $types['group'] == true) { @@ -3873,3 +3842,43 @@ function calendar_get_allowed_event_types(int $courseid = null) { return $types; } + +/** + * Given a course id, and context, updates the permission types array to add the 'course' or 'group' + * permission if it is relevant for that course. + * + * For efficiency, if they already have 'course' or 'group' then it skips checks. + * + * Do not call this function directly, it is only for use by calendar_get_allowed_event_types(). + * + * @param int $courseid Course id + * @param context $context Context for that course + * @param array $types Current permissions + */ +function calendar_internal_update_course_and_group_permission(int $courseid, context $context, array &$types) { + if (!$types['course']) { + // If they have manageentries permission on the course, then they can update this course. + if (has_capability('moodle/calendar:manageentries', $context)) { + $types['course'] = true; + } + } + // To update group events they must have EITHER manageentries OR managegroupentries. + if (!$types['group'] && (has_capability('moodle/calendar:manageentries', $context) || + has_capability('moodle/calendar:managegroupentries', $context))) { + // And they also need for a group to exist on the course. + $groups = groups_get_all_groups($courseid); + if (!empty($groups)) { + // And either accessallgroups, or belong to one of the groups. + if (has_capability('moodle/site:accessallgroups', $context)) { + $types['group'] = true; + } else { + foreach ($groups as $group) { + if (groups_is_member($group->id)) { + $types['group'] = true; + break; + } + } + } + } + } +} diff --git a/calendar/tests/lib_test.php b/calendar/tests/lib_test.php index 5264d9da7a8..96af6075ba8 100644 --- a/calendar/tests/lib_test.php +++ b/calendar/tests/lib_test.php @@ -546,6 +546,10 @@ class core_calendar_lib_testcase extends advanced_testcase { $this->setUser($user); + // In general for all courses, they don't have the ability to add course events yet. + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['course']); + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context1, true); assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context2, true); @@ -554,12 +558,20 @@ class core_calendar_lib_testcase extends advanced_testcase { $types = calendar_get_allowed_event_types($course1->id); $this->assertTrue($types['course']); + // If calling function without specified course, there is still a course where they have it. + $types = calendar_get_allowed_event_types(); + $this->assertTrue($types['course']); + assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context1, true); // The user only now has the correct capability in both course 1 and 2 so we // expect both to be in the results. $types = calendar_get_allowed_event_types($course3->id); $this->assertFalse($types['course']); + + // They now do not have permission in any course. + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['course']); } public function test_calendar_get_allowed_event_types_group_no_acces_to_diff_groups() { @@ -582,6 +594,11 @@ class core_calendar_lib_testcase extends advanced_testcase { $types = calendar_get_allowed_event_types($course->id); $this->assertTrue($types['course']); $this->assertFalse($types['group']); + + // Same result applies when not providing a specific course as they are only on one course. + $types = calendar_get_allowed_event_types(); + $this->assertTrue($types['course']); + $this->assertFalse($types['group']); } public function test_calendar_get_allowed_event_types_group_no_groups() { @@ -598,6 +615,12 @@ class core_calendar_lib_testcase extends advanced_testcase { // no groups so we shouldn't see a group type. $types = calendar_get_allowed_event_types($course->id); $this->assertTrue($types['course']); + $this->assertFalse($types['group']); + + // Same result applies when not providing a specific course as they are only on one course. + $types = calendar_get_allowed_event_types(); + $this->assertTrue($types['course']); + $this->assertFalse($types['group']); } public function test_calendar_get_allowed_event_types_group_access_all_groups() { @@ -623,7 +646,12 @@ class core_calendar_lib_testcase extends advanced_testcase { // the accessallgroups capability. $types = calendar_get_allowed_event_types($course1->id); $this->assertTrue($types['group']); + + // Same result applies when not providing a specific course as they are only on one course. + $types = calendar_get_allowed_event_types(); + $this->assertTrue($types['group']); } + public function test_calendar_get_allowed_event_types_group_no_access_all_groups() { $generator = $this->getDataGenerator(); $user = $generator->create_user(); @@ -642,10 +670,87 @@ class core_calendar_lib_testcase extends advanced_testcase { // groups that they are not a member of. $types = calendar_get_allowed_event_types($course->id); $this->assertFalse($types['group']); + + // Same result applies when not providing a specific course as they are only on one course. + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['group']); + assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true); assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $context, true); $types = calendar_get_allowed_event_types($course->id); $this->assertTrue($types['group']); + + // Same result applies when not providing a specific course as they are only on one course. + $types = calendar_get_allowed_event_types(); + $this->assertTrue($types['group']); + } + + public function test_calendar_get_allowed_event_types_group_cap_no_groups() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $group = $generator->create_group(['courseid' => $course->id]); + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + assign_capability('moodle/calendar:managegroupentries', CAP_ALLOW, $roleid, $context, true); + + $this->setUser($user); + $types = calendar_get_allowed_event_types($course->id); + $this->assertFalse($types['course']); + $this->assertFalse($types['group']); + + // Check without specifying a course (same result as user only has one course). + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['course']); + $this->assertFalse($types['group']); + } + + public function test_calendar_get_allowed_event_types_group_cap_has_group() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $group = $generator->create_group(['courseid' => $course->id]); + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + groups_add_member($group, $user); + assign_capability('moodle/calendar:managegroupentries', CAP_ALLOW, $roleid, $context, true); + + $this->setUser($user); + $types = calendar_get_allowed_event_types($course->id); + $this->assertFalse($types['course']); + $this->assertTrue($types['group']); + + // Check without specifying a course (same result as user only has one course). + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['course']); + $this->assertTrue($types['group']); + } + + public function test_calendar_get_allowed_event_types_group_cap_access_all_groups() { + $generator = $this->getDataGenerator(); + $user = $generator->create_user(); + $course = $generator->create_course(); + $context = context_course::instance($course->id); + $roleid = $generator->create_role(); + $group = $generator->create_group(['courseid' => $course->id]); + $generator->enrol_user($user->id, $course->id, 'student'); + $generator->role_assign($roleid, $user->id, $context->id); + assign_capability('moodle/calendar:managegroupentries', CAP_ALLOW, $roleid, $context, true); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $roleid, $context, true); + + $this->setUser($user); + $types = calendar_get_allowed_event_types($course->id); + $this->assertFalse($types['course']); + $this->assertTrue($types['group']); + + // Check without specifying a course (same result as user only has one course). + $types = calendar_get_allowed_event_types(); + $this->assertFalse($types['course']); + $this->assertTrue($types['group']); } /**