MDL-87916 course: Handle modules visibility in next/prev nav

Consider modules visibility in the next/previous linear navigation.

Note: Restricted modules are not handled in this change; this logic is
deferred to MDL-87573.
This commit is contained in:
Sara Arjona
2026-02-24 14:54:18 +01:00
parent 6b0f9327bb
commit 3a7ce91ac8
2 changed files with 86 additions and 31 deletions
@@ -120,8 +120,11 @@ class course_navigation {
* @return bool True if the course module is valid, false otherwise.
*/
private function is_valid_cm(cm_info $cm): bool {
// Skip modules that don't have a URL (like labels).
return !empty($cm->get_url());
return
// Skip modules that don't have a URL (like labels).
!empty($cm->get_url())
// Skip modules that are not visible to the user.
&& $cm->is_visible_on_course_page();
}
/**
@@ -83,28 +83,53 @@ final class course_navigation_test extends route_testcase {
'id' => 'cm2',
],
];
yield 'Simple case with hidden module (student)' => [
yield 'Hidden module (teacher)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
['name' => 'cm3'],
],
'current' => 'cm1',
'expected' => [
'id' => 'cm2',
],
];
yield 'Simple case with hidden module (teacher)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
],
'current' => 'cm1',
'expected' => [
'id' => 'cm2',
'id' => 'cm2', // Teachers can see hidden modules.
],
'role' => 'teacher',
];
yield 'Simple case last activity of a course (student)' => [
yield 'Hidden module (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
['name' => 'cm3'],
],
'current' => 'cm1',
'expected' => [
'id' => 'cm3', // Students cannot see hidden modules.
],
];
yield 'Stealth module (editingteacher)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visibleoncoursepage' => false]],
['name' => 'cm3'],
],
'current' => 'cm1',
'expected' => [
'id' => 'cm2', // Teachers can see stealth modules in the course page.
],
'role' => 'editingteacher',
];
yield 'Stealth module (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visibleoncoursepage' => false]],
['name' => 'cm3'],
],
'current' => 'cm1',
'expected' => [
'id' => 'cm3', // Students cannot see stealth modules in the course page.
],
];
yield 'Last activity of a course (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
@@ -213,28 +238,53 @@ final class course_navigation_test extends route_testcase {
'id' => 'cm1',
],
];
yield 'Simple case with hidden module (student)' => [
yield 'Hidden module (teacher)' => [
'cmsdef' => [
['name' => 'cm1', 'options' => ['visible' => false]],
['name' => 'cm2'],
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
['name' => 'cm3'],
],
'current' => 'cm2',
'current' => 'cm3',
'expected' => [
'id' => 'cm1',
],
];
yield 'Simple case with hidden module (teacher)' => [
'cmsdef' => [
['name' => 'cm1', 'options' => ['visible' => false]],
['name' => 'cm2'],
],
'current' => 'cm2',
'expected' => [
'id' => 'cm1',
'id' => 'cm2', // Teachers can see hidden modules.
],
'role' => 'teacher',
];
yield 'Simple case first activity of a course (student)' => [
yield 'Hidden module (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visible' => false]],
['name' => 'cm3'],
],
'current' => 'cm3',
'expected' => [
'id' => 'cm1', // Students cannot see hidden modules.
],
];
yield 'Stealth module (teacher)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visibleoncoursepage' => false]],
['name' => 'cm3'],
],
'current' => 'cm3',
'expected' => [
'id' => 'cm2', // Teachers can see stealth modules in the course page.
],
'role' => 'teacher',
];
yield 'Stealth module (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2', 'options' => ['visibleoncoursepage' => false]],
['name' => 'cm3'],
],
'current' => 'cm3',
'expected' => [
'id' => 'cm1', // Students cannot see stealth modules in the course page.
],
];
yield 'First activity of a course (student)' => [
'cmsdef' => [
['name' => 'cm1'],
['name' => 'cm2'],
@@ -309,6 +359,8 @@ final class course_navigation_test extends route_testcase {
string $direction = 'next',
): void {
$this->resetAfterTest();
set_config('allowstealth', 1);
$generator = $this->getDataGenerator();
$course = $generator->create_course(['numsections' => 2]);
$user = $generator->create_and_enrol($course, $role);