diff --git a/availability/classes/info.php b/availability/classes/info.php index d47f43983c3..04fa5d92103 100644 --- a/availability/classes/info.php +++ b/availability/classes/info.php @@ -608,12 +608,34 @@ abstract class info { } $tree = $this->get_availability_tree(); $checker = new capability_checker($this->get_context()); + + // Filter using availability tree. $this->modinfo = get_fast_modinfo($this->get_course()); - $result = $tree->filter_user_list($users, false, $this, $checker); + $filtered = $tree->filter_user_list($users, false, $this, $checker); $this->modinfo = null; + + // Include users in the result if they're either in the filtered list, + // or they have viewhidden. This logic preserves ordering of the + // passed users array. + $result = array(); + $canviewhidden = $checker->get_users_by_capability($this->get_view_hidden_capability()); + foreach ($users as $userid => $data) { + if (array_key_exists($userid, $filtered) || array_key_exists($userid, $canviewhidden)) { + $result[$userid] = $users[$userid]; + } + } + return $result; } + /** + * Gets the capability used to view hidden activities/sections (as + * appropriate). + * + * @return string Name of capability used to view hidden items of this type + */ + protected abstract function get_view_hidden_capability(); + /** * Formats the $cm->availableinfo string for display. This includes * filling in the names of any course-modules that might be mentioned. diff --git a/availability/classes/info_module.php b/availability/classes/info_module.php index 906920435f8..2fb4308955b 100644 --- a/availability/classes/info_module.php +++ b/availability/classes/info_module.php @@ -109,6 +109,10 @@ class info_module extends info { return parent::filter_user_list($filtered); } + protected function get_view_hidden_capability() { + return 'moodle/course:viewhiddenactivities'; + } + /** * Checks if an activity is visible to the given user. * diff --git a/availability/classes/info_section.php b/availability/classes/info_section.php index 4f98522c879..767891b71f4 100644 --- a/availability/classes/info_section.php +++ b/availability/classes/info_section.php @@ -56,6 +56,10 @@ class info_section extends info { return \context_course::instance($this->get_course()->id); } + protected function get_view_hidden_capability() { + return 'moodle/course:viewhiddensections'; + } + protected function set_in_database($availability) { global $DB; $DB->set_field('course_sections', 'availability', $availability, diff --git a/availability/classes/tree_node.php b/availability/classes/tree_node.php index 0ac385acab6..0704a3c3af7 100644 --- a/availability/classes/tree_node.php +++ b/availability/classes/tree_node.php @@ -139,7 +139,8 @@ abstract class tree_node { /** * Tests this condition against a user list. Users who do not meet the - * condition will be removed from the list. + * condition will be removed from the list, unless they have the ability + * to view hidden activities/sections. * * This function must be implemented if is_applied_to_user_lists returns * true. Otherwise it will not be called. @@ -150,6 +151,10 @@ abstract class tree_node { * Within this function, if you need to check capabilities, please use * the provided checker which caches results where possible. * + * Conditions do not need to check the viewhiddenactivities or + * viewhiddensections capabilities. These are handled by + * core_availability\info::filter_user_list. + * * @param array $users Array of userid => object * @param bool $not True if this condition is applying in negative mode * @param \core_availability\info $info Item we're checking diff --git a/availability/tests/fixtures/mock_info.php b/availability/tests/fixtures/mock_info.php index f995c41482e..d7ef76e39d1 100644 --- a/availability/tests/fixtures/mock_info.php +++ b/availability/tests/fixtures/mock_info.php @@ -60,6 +60,10 @@ class mock_info extends info { return \context_course::instance($this->get_course()->id); } + protected function get_view_hidden_capability() { + return 'moodle/course:viewhiddensections'; + } + protected function set_in_database($availability) { } diff --git a/availability/tests/info_test.php b/availability/tests/info_test.php index d0165679735..6309ab23173 100644 --- a/availability/tests/info_test.php +++ b/availability/tests/info_test.php @@ -421,7 +421,14 @@ class info_testcase extends \advanced_testcase { $u1 = $generator->create_user(); $u2 = $generator->create_user(); $u3 = $generator->create_user(); + $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'), MUST_EXIST); $allusers = array($u1->id => $u1, $u2->id => $u2, $u3->id => $u3); + $generator->enrol_user($u1->id, $course->id, $studentroleid); + $generator->enrol_user($u2->id, $course->id, $studentroleid); + $generator->enrol_user($u3->id, $course->id, $studentroleid); + + // Page 2 allows access to users 2 and 3, while section 2 allows access + // to users 1 and 2. $pagegen = $generator->get_plugin_generator('mod_page'); $page = $pagegen->create_instance(array('course' => $course)); $page2 = $pagegen->create_instance(array('course' => $course, @@ -477,5 +484,19 @@ class info_testcase extends \advanced_testcase { $info = new info_module($modinfo->get_cm($page2->cmid)); $this->assertEquals(array($u2->id), array_keys($info->filter_user_list($allusers))); + + // If the students have viewhiddenactivities, they get past the module + // restriction. + role_change_permission($studentroleid, context_module::instance($page2->cmid), + 'moodle/course:viewhiddenactivities', CAP_ALLOW); + $expected = array($u1->id, $u2->id); + $this->assertEquals($expected, array_keys($info->filter_user_list($allusers))); + + // If they have viewhiddensections, they also get past the section + // restriction. + role_change_permission($studentroleid, context_course::instance($course->id), + 'moodle/course:viewhiddensections', CAP_ALLOW); + $expected = array($u1->id, $u2->id, $u3->id); + $this->assertEquals($expected, array_keys($info->filter_user_list($allusers))); } }