diff --git a/question/type/multianswer/lang/en/qtype_multianswer.php b/question/type/multianswer/lang/en/qtype_multianswer.php
index e6e1ce1ecdf52..cc6009d4419ff 100644
--- a/question/type/multianswer/lang/en/qtype_multianswer.php
+++ b/question/type/multianswer/lang/en/qtype_multianswer.php
@@ -67,6 +67,7 @@
$string['questiontypechanged'] = 'Question type changed';
$string['questiontypechangedcomment'] = 'At least one question type has been changed.
Did you add, delete or move a question?
Look ahead.';
$string['questionusedinquiz'] = 'This question is used in {$a->nb_of_quiz} quiz(s), total attempt(s) : {$a->nb_of_attempts} ';
+$string['regradeissuenumsubquestionschanged'] = 'The number embedded sub-questions in the question has changed.';
$string['storedqtype'] = 'Stored question type {$a}';
$string['subqresponse'] = 'part {$a->i}: {$a->response}';
$string['unknownquestiontypeofsubquestion'] = 'Unknown question type: {$a->type} of question part # {$a->sub}';
diff --git a/question/type/multianswer/question.php b/question/type/multianswer/question.php
index 2910fca66d9ab..62036c263a47b 100644
--- a/question/type/multianswer/question.php
+++ b/question/type/multianswer/question.php
@@ -78,6 +78,43 @@ public function apply_attempt_state(question_attempt_step $step) {
}
}
+ public function validate_can_regrade_with_other_version(question_definition $otherversion): ?string {
+ $basemessage = parent::validate_can_regrade_with_other_version($otherversion);
+ if ($basemessage) {
+ return $basemessage;
+ }
+
+ if (count($this->subquestions) != count($otherversion->subquestions)) {
+ return get_string('regradeissuenumsubquestionschanged', 'qtype_multianswer');
+ }
+
+ foreach ($this->subquestions as $i => $subq) {
+ $subqmessage = $subq->validate_can_regrade_with_other_version($otherversion->subquestions[$i]);
+ if ($subqmessage) {
+ return $subqmessage;
+ }
+ }
+
+ return null;
+ }
+
+ public function update_attempt_state_data_for_new_version(
+ question_attempt_step $oldstep, question_definition $oldquestion) {
+ parent::update_attempt_state_data_for_new_version($oldstep, $oldquestion);
+
+ $result = [];
+ foreach ($this->subquestions as $i => $subq) {
+ $substep = $this->get_substep($oldstep, $i);
+ $statedata = $subq->update_attempt_state_data_for_new_version(
+ $substep, $oldquestion->subquestions[$i]);
+ foreach ($statedata as $name => $value) {
+ $result[$substep->add_prefix($name)] = $value;
+ }
+ }
+
+ return $result;
+ }
+
public function get_question_summary() {
$summary = $this->html_to_text($this->questiontext, $this->questiontextformat);
foreach ($this->subquestions as $i => $subq) {
diff --git a/question/type/multianswer/tests/question_test.php b/question/type/multianswer/tests/question_test.php
index 0744ee128ea62..ae38468730a7a 100644
--- a/question/type/multianswer/tests/question_test.php
+++ b/question/type/multianswer/tests/question_test.php
@@ -32,6 +32,7 @@
* @package qtype_multianswer
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @covers \qtype_multianswer_question
*/
class question_test extends \advanced_testcase {
public function test_get_expected_data() {
@@ -252,4 +253,100 @@ public function test_get_question_definition_for_external_rendering() {
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
$this->assertNull($options);
}
+
+ /**
+ * Helper method to make a simulated second version of the standard multianswer test question.
+ *
+ * The key think is that all the answer ids are changed (increased by 20).
+ *
+ * @param \qtype_multianswer_question $question
+ * @return \qtype_multianswer_question
+ */
+ protected function make_second_version(
+ \qtype_multianswer_question $question): \qtype_multianswer_question {
+ $newquestion = fullclone($question);
+
+ $newquestion->subquestions[1]->answers = [
+ 36 => new \question_answer(16, 'Apple', 0.3333333,
+ 'Good', FORMAT_HTML),
+ 37 => new \question_answer(17, 'Burger', -0.5,
+ '', FORMAT_HTML),
+ 38 => new \question_answer(18, 'Hot dog', -0.5,
+ 'Not a fruit', FORMAT_HTML),
+ 39 => new \question_answer(19, 'Pizza', -0.5,
+ '', FORMAT_HTML),
+ 40 => new \question_answer(20, 'Orange', 0.3333333,
+ 'Correct', FORMAT_HTML),
+ 41 => new \question_answer(21, 'Banana', 0.3333333,
+ '', FORMAT_HTML),
+ ];
+
+ $newquestion->subquestions[2]->answers = [
+ 42 => new \question_answer(22, 'Raddish', 0.5,
+ 'Good', FORMAT_HTML),
+ 43 => new \question_answer(23, 'Chocolate', -0.5,
+ '', FORMAT_HTML),
+ 44 => new \question_answer(24, 'Biscuit', -0.5,
+ 'Not a vegetable', FORMAT_HTML),
+ 45 => new \question_answer(25, 'Cheese', -0.5,
+ '', FORMAT_HTML),
+ 46 => new \question_answer(26, 'Carrot', 0.5,
+ 'Correct', FORMAT_HTML),
+ ];
+
+ return $newquestion;
+ }
+
+ public function test_validate_can_regrade_with_other_version_ok() {
+ /** @var \qtype_multianswer_question $question */
+ $question = \test_question_maker::make_question('multianswer', 'multiple');
+
+ $newquestion = $this->make_second_version($question);
+
+ $this->assertNull($newquestion->validate_can_regrade_with_other_version($question));
+ }
+
+ public function test_validate_can_regrade_with_other_version_wrong_subquestions() {
+ /** @var \qtype_multianswer_question $question */
+ $question = \test_question_maker::make_question('multianswer', 'multiple');
+
+ $newquestion = $this->make_second_version($question);
+ unset($newquestion->subquestions[2]);
+
+ $this->assertEquals(
+ get_string('regradeissuenumsubquestionschanged', 'qtype_multianswer'),
+ $newquestion->validate_can_regrade_with_other_version($question));
+ }
+
+ public function test_validate_can_regrade_with_other_version_one_wrong_subquestion() {
+ /** @var \qtype_multianswer_question $question */
+ $question = \test_question_maker::make_question('multianswer', 'multiple');
+
+ $newquestion = $this->make_second_version($question);
+ unset($newquestion->subquestions[1]->answers[41]);
+
+ $this->assertEquals(
+ get_string('regradeissuenumchoiceschanged', 'qtype_multichoice'),
+ $newquestion->validate_can_regrade_with_other_version($question));
+ }
+
+ public function test_update_attempt_state_date_from_old_version_ok() {
+ /** @var \qtype_multianswer_question $question */
+ $question = \test_question_maker::make_question('multianswer', 'multiple');
+
+ $newquestion = $this->make_second_version($question);
+
+ $oldstep = new question_attempt_step();
+ $oldstep->set_qt_var('_sub1_order', '16,17,18,19,20,21');
+ $oldstep->set_qt_var('_sub2_order', '22,23,24,25,26');
+
+ $expected = [
+ '_sub1_order' => '36,37,38,39,40,41',
+ '_sub2_order' => '42,43,44,45,46',
+ ];
+
+ $this->assertEquals($expected,
+ $newquestion->update_attempt_state_data_for_new_version($oldstep, $question));
+ }
+
}