mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 00:46:50 +02:00
MDL-85721 qtype: Cope with missing options records
If we restore a question (or any other) which has had its qtype_xxx_options record deleted, we get a notification output when we try to build the options. This may be called from an AJAX request (such as when we duplicate a quiz), and outputting the notification breaks the AJAX response. Returning false also means we don't get the answers attached to the questiondata options, so the structure doesn't match the restored data, and we get duplication. This emits the errors via debugging instead, which allows it to be supressed or logged, and allows get_question_options() to continue running.
This commit is contained in:
parent
e8002198f3
commit
ca51acb3e5
2 changed files with 93 additions and 6 deletions
|
@ -904,7 +904,7 @@ class question_type {
|
|||
* specific information (it is passed by reference).
|
||||
*/
|
||||
public function get_question_options($question) {
|
||||
global $DB, $OUTPUT;
|
||||
global $DB;
|
||||
|
||||
if (!isset($question->options)) {
|
||||
$question->options = new stdClass();
|
||||
|
@ -921,9 +921,8 @@ class question_type {
|
|||
$question->options->$field = $extra_data->$field;
|
||||
}
|
||||
} else {
|
||||
echo $OUTPUT->notification('Failed to load question options from the table ' .
|
||||
debugging('Failed to load question options from the table ' .
|
||||
$question_extension_table . ' for questionid ' . $question->id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -938,9 +937,8 @@ class question_type {
|
|||
WHERE qa.question = ?
|
||||
ORDER BY qa.id", array($question->id));
|
||||
if (!$answers) {
|
||||
echo $OUTPUT->notification('Failed to load question answers from the table ' .
|
||||
$answerextensiontable . 'for questionid ' . $question->id);
|
||||
return false;
|
||||
debugging('Failed to load question answers from the table ' .
|
||||
$answerextensiontable . ' for questionid ' . $question->id);
|
||||
}
|
||||
} else {
|
||||
// Don't check for success or failure because some question types do
|
||||
|
|
89
question/type/shortanswer/tests/restore_test.php
Normal file
89
question/type/shortanswer/tests/restore_test.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?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_shortanswer;
|
||||
|
||||
/**
|
||||
* Unit tests for restore_qtype_shortanswer_plugin
|
||||
*
|
||||
* @package qtype_shortanswer
|
||||
* @copyright 2025 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \restore_qtype_shortanswer_plugin
|
||||
*/
|
||||
final class restore_test extends \advanced_testcase {
|
||||
/**
|
||||
* Duplicate a quiz containing a shortanswer question with no options 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();
|
||||
$qbank = $generator->get_plugin_generator('mod_qbank')->create_instance(['course' => $course1->id]);
|
||||
$context = \context_module::instance($qbank->cmid);
|
||||
$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 multichoice question from the qbank.
|
||||
$quiz = $this->getDataGenerator()->get_plugin_generator('mod_quiz')->create_instance(['course' => $course1->id]);
|
||||
$question = $questiongenerator->create_question('shortanswer', 'frogtoad', ['category' => $cat->id]);
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
|
||||
// Delete the multichoice_options record.
|
||||
$DB->delete_records('qtype_shortanswer_options', ['questionid' => $question->id]);
|
||||
|
||||
// Confirm we have created 1 additional question.
|
||||
$this->assertEquals($initialcount + 1, $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();
|
||||
|
||||
$debugging = "Failed to load question options from the table qtype_shortanswer_options for questionid {$question->id}";
|
||||
$this->assertdebuggingcalledcount(2, [$debugging, $debugging]);
|
||||
|
||||
// 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 + 1, $DB->count_records('question'));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue