diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index a17ea4b54af..75580f0fc59 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -45,6 +45,11 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st /** @var stdClass */ protected $oldquizlayout; + /** + * @var array Track old question ids that need to be removed at the end of the restore. + */ + protected $oldquestionids = []; + protected function define_structure() { $paths = []; @@ -347,8 +352,7 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $filtercondition->includingsubcategories = $data->includingsubcategories ?? false; $questionsetreference->filtercondition = json_encode($filtercondition); $DB->insert_record('question_set_references', $questionsetreference); - // Cleanup leftover random qtype data from question table. - question_delete_question($question->questionid); + $this->oldquestionids[$question->questionid] = 1; } else { // Reference data. $questionreference = new \stdClass(); @@ -609,4 +613,12 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st 'shufflequestions' => $this->legacyshufflequestionsoption]); } } + + protected function after_restore() { + parent::after_restore(); + // Delete old random questions that have been converted to set references. + foreach (array_keys($this->oldquestionids) as $oldquestionid) { + question_delete_question($oldquestionid); + } + } } diff --git a/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz b/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz new file mode 100644 index 00000000000..9dcf49e8557 Binary files /dev/null and b/mod/quiz/tests/fixtures/pre-40-shared-random-question.mbz differ diff --git a/mod/quiz/tests/quiz_question_restore_test.php b/mod/quiz/tests/quiz_question_restore_test.php index e27b37f054e..afc77a713ce 100644 --- a/mod/quiz/tests/quiz_question_restore_test.php +++ b/mod/quiz/tests/quiz_question_restore_test.php @@ -380,6 +380,59 @@ class quiz_question_restore_test extends \advanced_testcase { } + /** + * Test pre 4.0 quiz restore for random question used on multiple quizzes. + * + * @covers ::process_quiz_question_legacy_instance + */ + public function test_pre_4_quiz_restore_shared_random_question() { + global $USER, $DB; + $this->resetAfterTest(); + + $backupid = 'abc'; + $backuppath = make_backup_temp_directory($backupid); + get_file_packer('application/vnd.moodle.backup')->extract_to_pathname( + __DIR__ . "/fixtures/pre-40-shared-random-question.mbz", $backuppath); + + // Do the restore to new course with default settings. + $categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); + $newcourseid = \restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid); + $rc = new \restore_controller($backupid, $newcourseid, \backup::INTERACTIVE_NO, \backup::MODE_GENERAL, $USER->id, + \backup::TARGET_NEW_COURSE); + + $this->assertTrue($rc->execute_precheck()); + $rc->execute_plan(); + $rc->destroy(); + + // Get the information about the resulting course and check that it is set up correctly. + // Each quiz should contain an instance of the random question. + $modinfo = get_fast_modinfo($newcourseid); + $quizzes = $modinfo->get_instances_of('quiz'); + $this->assertCount(2, $quizzes); + foreach ($quizzes as $quiz) { + $quizobj = \mod_quiz\quiz_settings::create($quiz->instance); + $structure = structure::create_for_quiz($quizobj); + + // Are the correct slots returned? + $slots = $structure->get_slots(); + $this->assertCount(1, $slots); + + $quizobj->preload_questions(); + $quizobj->load_questions(); + $questions = $quizobj->get_questions(); + $this->assertCount(1, $questions); + } + + // Count the questions for course question bank. + // We should have a single question, the random question should have been deleted after the restore. + $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id)); + $this->assertEquals(1, $this->question_count(\context_course::instance($newcourseid)->id, + "AND q.qtype <> 'random'")); + + // Count the questions in quiz qbank. + $this->assertEquals(0, $this->question_count($quizobj->get_context()->id)); + } + /** * Ensure that question slots are correctly backed up and restored with all properties. *