diff --git a/course/format/classes/base.php b/course/format/classes/base.php index 30e7f300858..291072cfcab 100644 --- a/course/format/classes/base.php +++ b/course/format/classes/base.php @@ -371,7 +371,7 @@ abstract class base { * This method ensures that 3rd party course format plugins that still use 'numsections' continue to * work but at the same time we no longer expect formats to have 'numsections' property. * - * @return int + * @return int The last section number, or -1 if sections are entirely missing */ public function get_last_section_number() { $course = $this->get_course(); @@ -380,6 +380,12 @@ abstract class base { } $modinfo = get_fast_modinfo($course); $sections = $modinfo->get_section_info_all(); + + // Sections seem to be missing entirely. Avoid subsequent errors and return early. + if (count($sections) === 0) { + return -1; + } + return (int)max(array_keys($sections)); } diff --git a/course/format/tests/base_test.php b/course/format/tests/base_test.php index 55ca4d7c602..c6a1f6d1ece 100644 --- a/course/format/tests/base_test.php +++ b/course/format/tests/base_test.php @@ -14,14 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. -defined('MOODLE_INTERNAL') || die(); - /** * Course related unit tests * * @package core_course * @copyright 2014 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_courseformat\base */ class base_test extends advanced_testcase { @@ -348,6 +347,32 @@ class base_test extends advanced_testcase { ); } + /** + * Test that retrieving last section number for a course + * + * @covers ::get_last_section_number + */ + public function test_get_last_section_number(): void { + global $DB; + + $this->resetAfterTest(); + + // Course with two additional sections. + $courseone = $this->getDataGenerator()->create_course(['numsections' => 2]); + $this->assertEquals(2, course_get_format($courseone)->get_last_section_number()); + + // Course without additional sections, section zero is the "default" section that always exists. + $coursetwo = $this->getDataGenerator()->create_course(['numsections' => 0]); + $this->assertEquals(0, course_get_format($coursetwo)->get_last_section_number()); + + // Course without additional sections, manually remove section zero, as "course_delete_section" prevents that. This + // simulates course data integrity issues that previously triggered errors. + $coursethree = $this->getDataGenerator()->create_course(['numsections' => 0]); + $DB->delete_records('course_sections', ['course' => $coursethree->id, 'section' => 0]); + + $this->assertEquals(-1, course_get_format($coursethree)->get_last_section_number()); + } + /** * Test for the default delete format data behaviour. *