From 8a54f7a096bdf4f69753194c51ee09104efb66ff Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 2 Oct 2024 12:01:55 +0100 Subject: [PATCH] MDL-83070 question restore: recode links in hints --- backup/moodle2/restore_qtype_plugin.class.php | 8 +- lib/testing/generator/data_generator.php | 6 +- .../tests/qformat_xml_import_export_test.php | 6 +- question/tests/backup_test.php | 78 ++++++++++++++++++- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/backup/moodle2/restore_qtype_plugin.class.php b/backup/moodle2/restore_qtype_plugin.class.php index 850af2b02b3b6..23d0a3dc076d9 100644 --- a/backup/moodle2/restore_qtype_plugin.class.php +++ b/backup/moodle2/restore_qtype_plugin.class.php @@ -380,7 +380,8 @@ public function recode_legacy_state_answer($state) { * * Only common stuff to all plugins, in this case: * - question: text and feedback - * - question_answers: text and feedbak + * - question_answers: text and feedback + * - question_hints: hint * * Note each qtype will have, if needed, its own define_decode_contents method */ @@ -388,8 +389,9 @@ public static function define_plugin_decode_contents() { $contents = array(); - $contents[] = new restore_decode_content('question', array('questiontext', 'generalfeedback'), 'question_created'); - $contents[] = new restore_decode_content('question_answers', array('answer', 'feedback'), 'question_answer'); + $contents[] = new restore_decode_content('question', ['questiontext', 'generalfeedback'], 'question_created'); + $contents[] = new restore_decode_content('question_answers', ['answer', 'feedback'], 'question_answer'); + $contents[] = new restore_decode_content('question_hints', ['hint'], 'question_hint'); return $contents; } diff --git a/lib/testing/generator/data_generator.php b/lib/testing/generator/data_generator.php index 54293d0a508bc..d76435ec99dc8 100644 --- a/lib/testing/generator/data_generator.php +++ b/lib/testing/generator/data_generator.php @@ -1001,9 +1001,11 @@ public function create_tag($record = null) { public function combine_defaults_and_record(array $defaults, $record) { $record = (array) $record; - foreach ($defaults as $key => $defaults) { + foreach ($defaults as $key => $default) { if (!array_key_exists($key, $record)) { - $record[$key] = $defaults; + $record[$key] = $default; + } else if (is_array($record[$key]) && is_array($default)) { + $record[$key] = $this->combine_defaults_and_record($default, $record[$key]); } } return $record; diff --git a/question/format/xml/tests/qformat_xml_import_export_test.php b/question/format/xml/tests/qformat_xml_import_export_test.php index 7db772ad17c10..37be1dceb1608 100644 --- a/question/format/xml/tests/qformat_xml_import_export_test.php +++ b/question/format/xml/tests/qformat_xml_import_export_test.php @@ -36,6 +36,7 @@ * * @copyright 2014 Nikita Nikitsky, Volgograd State Technical University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \qformat_xml */ class qformat_xml_import_export_test extends advanced_testcase { /** @@ -393,7 +394,10 @@ public function test_export_nested_categories_with_questions(): void { $kappaquestion = $generator->create_question('essay', null, [ 'category' => $categorykappa->id, 'name' => 'Kappa Essay Question', - 'questiontext' => ['text' => 'Testing Kappa Essay Question'], + 'questiontext' => [ + 'format' => '0', + 'text' => 'Testing Kappa Essay Question', + ], 'generalfeedback' => '', 'responseformat' => 'editor', 'responserequired' => 1, diff --git a/question/tests/backup_test.php b/question/tests/backup_test.php index a0906b2b63a30..f439617aa9dc4 100644 --- a/question/tests/backup_test.php +++ b/question/tests/backup_test.php @@ -16,6 +16,10 @@ namespace core_question; +use context_course; +use moodle_url; +use question_bank; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -29,6 +33,8 @@ * @category test * @copyright 2018 Shamim Rezaie * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \restore_qtype_plugin + * @covers \restore_create_categories_and_questions */ class backup_test extends \advanced_testcase { @@ -116,7 +122,7 @@ public function test_backup_question_tags(): void { // Tag the questions with 2 question tags and 2 course level question tags. $qcontext = \context::instance_by_id($qcat->contextid); - $coursecontext = \context_course::instance($course->id); + $coursecontext = context_course::instance($course->id); \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $qcontext, ['qtag1', 'qtag2']); \core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $qcontext, ['qtag3', 'qtag4']); \core_tag_tag::set_item_tags('core_question', 'question', $question1->id, $coursecontext, ['ctag1', 'ctag2']); @@ -160,7 +166,7 @@ public function test_backup_question_tags(): void { foreach ($tags as $tag) { if (in_array($tag->name, ['ctag1', 'ctag2', 'ctag3', 'ctag4'])) { - $expected = \context_course::instance($courseid2)->id; + $expected = context_course::instance($courseid2)->id; } else if (in_array($tag->name, ['qtag1', 'qtag2', 'qtag3', 'qtag4'])) { $expected = $qcontext->id; } @@ -189,7 +195,7 @@ public function test_backup_question_tags(): void { // Restore to a new course in the new course category. $courseid3 = $this->restore_course($backupid2, $coursefullname, $courseshortname . '_3', $category2->id, $expectedwarnings); - $coursecontext3 = \context_course::instance($courseid3); + $coursecontext3 = context_course::instance($courseid3); // The questions should have been moved to a question category that belongs to a course context. $questions = $DB->get_records_sql("SELECT q.* @@ -390,4 +396,70 @@ public function test_backup_question_author_reset(): void { $this->assertEquals($USER->id, $question->createdby); $this->assertEquals($USER->id, $question->modifiedby); } + + public function test_backup_and_restore_recodes_links_in_questions(): void { + global $DB, $USER, $CFG; + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a course and a category. + $course = $this->getDataGenerator()->create_course(); + $category = $this->getDataGenerator()->create_category(); + + // Create a question with links in all the places that should be recoded. + $testlink = new moodle_url('/course/view.php', ['id' => $course->id]); + $testcontent = 'Look at the course.'; + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $questioncategory = $questiongenerator->create_question_category( + ['contextid' => context_course::instance($course->id)->id]); + $question = $questiongenerator->create_question('multichoice', null, [ + 'name' => 'Test question', + 'category' => $questioncategory->id, + 'questiontext' => ['text' => 'This is the question. ' . $testcontent], + 'generalfeedback' => ['text' => 'Why is this right? ' . $testcontent], + 'answer' => [ + '0' => ['text' => 'Choose me! ' . $testcontent], + ], + 'feedback' => [ + '0' => ['text' => 'The reason: ' . $testcontent], + ], + 'hint' => [ + '0' => ['text' => 'Hint: ' . $testcontent], + ], + ]); + + // Create a quiz and add the question. + $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); + quiz_add_quiz_question($question->id, $quiz); + + // Backup and restore the course. + $backupid = $this->backup_course($course); + $newcourseid = $this->restore_course($backupid, 'New course', 'C2', $category->id); + + // Get the question from the restored course - we are expecting just one, but that is not the real test here. + $restoredquestions = $DB->get_records_sql(" + SELECT q.id, q.name + FROM {question} q + JOIN {question_versions} qv ON qv.questionid = q.id + JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid + JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid + WHERE qc.contextid = ? + ", [context_course::instance($newcourseid)->id]); + $this->assertCount(1, $restoredquestions); + $questionid = array_key_first($restoredquestions); + $this->assertEquals('Test question', $restoredquestions[$questionid]->name); + + // Verify the links have been recoded. + $restoredquestion = question_bank::load_question_data($questionid); + $recodedlink = new moodle_url('/course/view.php', ['id' => $newcourseid]); + $recodedcontent = 'Look at the course.'; + $firstanswerid = array_key_first($restoredquestion->options->answers); + $firsthintid = array_key_first($restoredquestion->hints); + + $this->assertEquals('This is the question. ' . $recodedcontent, $restoredquestion->questiontext); + $this->assertEquals('Why is this right? ' . $recodedcontent, $restoredquestion->generalfeedback); + $this->assertEquals('Choose me! ' . $recodedcontent, $restoredquestion->options->answers[$firstanswerid]->answer); + $this->assertEquals('The reason: ' . $recodedcontent, $restoredquestion->options->answers[$firstanswerid]->feedback); + $this->assertEquals('Hint: ' . $recodedcontent, $restoredquestion->hints[$firsthintid]->hint); + } }