MDL-75329 course: defensive counting of course sections.

Avoids errors due to pre-existing integrity issues with course
data, where the absence entirely of section data would result in
PHP errors.
This commit is contained in:
Paul Holden 2023-09-07 00:13:18 +01:00
parent 83b4c96cad
commit 6de6f64199
No known key found for this signature in database
GPG Key ID: A81A96D6045F6164
2 changed files with 34 additions and 3 deletions

View File

@ -383,7 +383,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();
@ -392,6 +392,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));
}

View File

@ -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 {
@ -368,6 +367,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.
*