MDL-85018 qtype_multianswer: Prevent errors on backup/restore
If a multianswer question has its question_multianswer record missing for some reason, attempting to backup and restore a bank containing the question will result a dml_missing_record exception. The qtype_multianswer::get_question_options() method already copes with the sequence from this record being empty, so we can just remove the MUST_EXIST check and let it fall back to that.
This commit is contained in:
@@ -110,8 +110,7 @@ class qtype_multianswer extends question_type {
|
||||
|
||||
parent::get_question_options($question);
|
||||
// Get relevant data indexed by positionkey from the multianswers table.
|
||||
$sequence = $DB->get_field('question_multianswer', 'sequence',
|
||||
array('question' => $question->id), MUST_EXIST);
|
||||
$sequence = $DB->get_field('question_multianswer', 'sequence', ['question' => $question->id]);
|
||||
|
||||
if (empty($sequence)) {
|
||||
$question->options->questions = [];
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace qtype_multianswer;
|
||||
|
||||
/**
|
||||
* Unit tests for
|
||||
*
|
||||
* @package qtype_multianswer
|
||||
* @copyright 2025 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \restore_qtype_multianswer_plugin
|
||||
*/
|
||||
final class restore_test extends \advanced_testcase {
|
||||
/**
|
||||
* Duplicate a quiz containing a multianswer question with no multianswer record.
|
||||
*/
|
||||
public function test_restore_quiz_with_edited_questions(): void {
|
||||
global $CFG, $DB, $USER;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a course and a user with editing teacher capabilities.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course1 = $generator->create_course();
|
||||
$context = \context_course::instance($course1->id);
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$initialcount = $DB->count_records('question');
|
||||
|
||||
// Create a question category.
|
||||
$cat = $questiongenerator->create_question_category(['contextid' => $context->id]);
|
||||
|
||||
// Create a quiz containing a multianswer question from the qbank.
|
||||
$quiz = $this->getDataGenerator()->get_plugin_generator('mod_quiz')->create_instance(['course' => $course1->id]);
|
||||
$question = $questiongenerator->create_question('multianswer', 'twosubq', ['category' => $cat->id]);
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
|
||||
// Delete the multianswer record.
|
||||
$DB->delete_records('question_multianswer', ['question' => $question->id]);
|
||||
|
||||
// Confirm we have created 3 additional questions (one parent, 2 children).
|
||||
$this->assertEquals($initialcount + 3, $DB->count_records('question'));
|
||||
|
||||
// Backup quiz.
|
||||
$bc = new \backup_controller(
|
||||
\backup::TYPE_1ACTIVITY,
|
||||
$quiz->cmid,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_IMPORT,
|
||||
$USER->id,
|
||||
);
|
||||
$backupid = $bc->get_backupid();
|
||||
$bc->execute_plan();
|
||||
$bc->destroy();
|
||||
|
||||
// Restore the backup into the same course.
|
||||
$rc = new \restore_controller(
|
||||
$backupid,
|
||||
$course1->id,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_IMPORT,
|
||||
$USER->id,
|
||||
\backup::TARGET_CURRENT_ADDING,
|
||||
);
|
||||
$rc->execute_precheck();
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
// Both quizzes should refer to the same original question.
|
||||
$quizzes = get_fast_modinfo($course1->id)->get_instances_of('quiz');
|
||||
$this->assertCount(2, $quizzes);
|
||||
foreach ($quizzes as $quiz) {
|
||||
$structure = \mod_quiz\question\bank\qbank_helper::get_question_structure($quiz->instance, $quiz->context);
|
||||
$this->assertEquals($structure[1]->questionid, $question->id);
|
||||
}
|
||||
|
||||
// There should be no additional questions created during the restore.
|
||||
$this->assertEquals($initialcount + 3, $DB->count_records('question'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user