From e3fa94aa5170600331d9f671afe5d6dfee0b5ef8 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Wed, 4 Mar 2026 15:24:16 +0100 Subject: [PATCH] MDL-88111 course: Fix error in next/prev when last activity is invalid --- .../route/controller/course_navigation.php | 8 +- .../controller/course_navigation_test.php | 84 +++++++++++++++++-- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/public/course/classes/route/controller/course_navigation.php b/public/course/classes/route/controller/course_navigation.php index 8c6dc3176e4..120a6c99b6e 100644 --- a/public/course/classes/route/controller/course_navigation.php +++ b/public/course/classes/route/controller/course_navigation.php @@ -80,7 +80,9 @@ class course_navigation { return $this->redirect($response, $nextcm->get_navigation_url()); } } - return $this->redirect_to_course($response, $cm->get_course()->id); + + // If there is no next module, redirect to the next section. + return $this->redirect_to_next_section($response, $modinfo, $section); } /** @@ -129,7 +131,9 @@ class course_navigation { return $this->redirect($response, $prevcm->get_navigation_url()); } } - return $this->redirect_to_course($response, $cm->get_course()->id); + + // If there is no previous module, redirect to the previous section. + return $this->redirect_to_previous_section($response, $modinfo, $section); } /** diff --git a/public/course/tests/route/controller/course_navigation_test.php b/public/course/tests/route/controller/course_navigation_test.php index 1ead3ab4c35..ef8c766332c 100644 --- a/public/course/tests/route/controller/course_navigation_test.php +++ b/public/course/tests/route/controller/course_navigation_test.php @@ -137,6 +137,48 @@ final class course_navigation_test extends route_testcase { 'id' => 'cm3', // Students cannot see stealth modules in the course page. ], ]; + yield 'Hidden last module (teacher)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 2]], + ['name' => 'cm2', 'options' => ['section' => 2, 'visible' => false]], + ], + 'current' => 'cm1', + 'expected' => [ + 'id' => 'cm2', + ], + 'role' => 'teacher', + ]; + yield 'Hidden last module (student)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 2]], + ['name' => 'cm2', 'options' => ['section' => 2, 'visible' => false]], + ], + 'current' => 'cm1', + 'expected' => [ + 'type' => 'course', // Students cannot see hidden modules. + ], + ]; + yield 'Stealth last module (teacher)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 2]], + ['name' => 'cm2', 'options' => ['section' => 2, 'visibleoncoursepage' => false]], + ], + 'current' => 'cm1', + 'expected' => [ + 'id' => 'cm2', + ], + 'role' => 'teacher', + ]; + yield 'Stealth last module (student)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 2]], + ['name' => 'cm2', 'options' => ['section' => 2, 'visibleoncoursepage' => false]], + ], + 'current' => 'cm1', + 'expected' => [ + 'type' => 'course', // Students cannot see stealth modules in the course page. + ], + ]; yield 'Restricted module visible (editingteacher)' => [ 'cmsdef' => [ ['name' => 'cm1'], @@ -220,6 +262,16 @@ final class course_navigation_test extends route_testcase { 'id' => 'cm2', ], ]; + yield 'Subsection: With next module being a subsection in the last section (student)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 2]], + ['name' => 'subsection1', 'type' => 'subsection', 'options' => ['section' => 2]], + ], + 'current' => 'cm1', + 'expected' => [ + 'type' => 'course', + ], + ]; yield 'Subsection: With next module being a label and subsections (student)' => [ 'cmsdef' => [ ['name' => 'cm1'], @@ -734,10 +786,21 @@ final class course_navigation_test extends route_testcase { 'type' => 'course', ], ]; - yield 'With last module without url (student)' => [ + yield 'With last module without url in the first section (student)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 0]], + ['name' => 'cm2', 'type' => 'label', 'options' => ['section' => 0]], + ], + 'current' => 'cm1', + 'expected' => [ + 'type' => 'section', + 'id' => '1', + ], + ]; + yield 'With last module without url in the last section (student)' => [ 'cmsdef' => [ ['name' => 'cm1', 'options' => ['section' => 2]], - ['name' => 'cm2', 'type' => 'label'], + ['name' => 'cm2', 'type' => 'label', 'options' => ['section' => 2]], ], 'current' => 'cm1', 'expected' => [ @@ -889,7 +952,7 @@ final class course_navigation_test extends route_testcase { ], 'current' => 'cm2', 'expected' => [ - 'id' => 'cm1', // Students cannot see stealth modules in the course page. + 'id' => 'cm1', ], 'role' => 'teacher', ]; @@ -1439,6 +1502,17 @@ final class course_navigation_test extends route_testcase { 'type' => 'course', ], ]; + yield 'First activity of a section (student)' => [ + 'cmsdef' => [ + ['name' => 'cm1', 'options' => ['section' => 1]], + ['name' => 'cm2', 'options' => ['section' => 1]], + ], + 'current' => 'cm1', + 'expected' => [ + 'type' => 'section', + 'id' => '1', + ], + ]; yield 'With first module without url in the first section (student)' => [ 'cmsdef' => [ ['name' => 'cm1', 'type' => 'label'], @@ -1449,9 +1523,9 @@ final class course_navigation_test extends route_testcase { 'type' => 'course', ], ]; - yield 'With first module without url (student)' => [ + yield 'With first module without url in the last section (student)' => [ 'cmsdef' => [ - ['name' => 'cm1', 'type' => 'label'], + ['name' => 'cm1', 'type' => 'label', 'options' => ['section' => 2]], ['name' => 'cm2', 'options' => ['section' => 2]], ], 'current' => 'cm2',