diff --git a/question/engine/questionusage.php b/question/engine/questionusage.php index 77541e24217..a1f949481c0 100644 --- a/question/engine/questionusage.php +++ b/question/engine/questionusage.php @@ -629,6 +629,26 @@ class question_usage_by_activity { return true; } } + + /** + * Check, based on the sequence number, whether this auto-save is still required. + * @param int $slot the number used to identify this question within this usage. + * @param array $submitteddata the submitted data that constitutes the action. + * @return bool true if the check variable is present and correct, otherwise false. + */ + public function is_autosave_required($slot, $postdata = null) { + $qa = $this->get_question_attempt($slot); + $sequencecheck = $qa->get_submitted_var( + $qa->get_control_field_name('sequencecheck'), PARAM_INT, $postdata); + if (is_null($sequencecheck)) { + return false; + } else if ($sequencecheck != $qa->get_num_steps()) { + return false; + } else { + return true; + } + } + /** * Update the flagged state for all question_attempts in this usage, if their * flagged state was changed in the request. diff --git a/question/engine/tests/helpers.php b/question/engine/tests/helpers.php index be6dd29a99c..8ff4e443819 100644 --- a/question/engine/tests/helpers.php +++ b/question/engine/tests/helpers.php @@ -684,13 +684,12 @@ abstract class qbehaviour_walkthrough_test_base extends question_testcase { $this->quba->start_question($this->slot, $variant); } - protected function process_submission($data) { - // Backwards compatibility. - reset($data); - if (count($data) == 1 && key($data) === '-finish') { - $this->finish(); - } - + /** + * Convert an array of data destined for one question to the equivalent POST data. + * @param array $data the data for the quetsion. + * @return array the complete post data. + */ + protected function response_data_to_post($data) { $prefix = $this->quba->get_field_prefix($this->slot); $fulldata = array( 'slots' => $this->slot, @@ -699,11 +698,21 @@ abstract class qbehaviour_walkthrough_test_base extends question_testcase { foreach ($data as $name => $value) { $fulldata[$prefix . $name] = $value; } - $this->quba->process_all_actions(time(), $fulldata); + return $fulldata; + } + + protected function process_submission($data) { + // Backwards compatibility. + reset($data); + if (count($data) == 1 && key($data) === '-finish') { + $this->finish(); + } + + $this->quba->process_all_actions(time(), $this->response_data_to_post($data)); } protected function process_autosave($data) { - $this->quba->process_autosave($this->slot, $data); + $this->quba->process_all_autosaves(null, $this->response_data_to_post($data)); } protected function finish() { diff --git a/question/engine/tests/questionusage_autosave_test.php b/question/engine/tests/questionusage_autosave_test.php index 9090d428b1e..304c94102e3 100644 --- a/question/engine/tests/questionusage_autosave_test.php +++ b/question/engine/tests/questionusage_autosave_test.php @@ -559,4 +559,49 @@ class question_usage_autosave_test extends qbehaviour_walkthrough_test_base { $DB2->dispose(); } + + public function test_autosave_with_wrong_seq_number_ignored() { + $this->resetAfterTest(); + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $cat = $generator->create_question_category(); + $question = $generator->create_question('shortanswer', null, + array('category' => $cat->id)); + + // Start attempt at a shortanswer question. + $q = question_bank::load_question($question->id); + $this->start_attempt_at_question($q, 'deferredfeedback', 1); + + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_step_count(1); + + // Process a response and check the expected result. + $this->process_submission(array('answer' => 'first response')); + + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_step_count(2); + $this->save_quba(); + + // Now check how that is re-displayed. + $this->render(); + $this->check_output_contains_text_input('answer', 'first response'); + + // Process an autosave with a sequence number 1 to small (so from the past). + $this->load_quba(); + $postdata = $this->response_data_to_post(array('answer' => 'obsolete response')); + $postdata[$this->quba->get_field_prefix($this->slot) . ':sequencecheck'] = $this->get_question_attempt()->get_num_steps() - 1; + $this->quba->process_all_autosaves(null, $postdata); + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_step_count(2); + $this->save_quba(); + + // Now check how that is re-displayed. + $this->load_quba(); + $this->render(); + $this->check_output_contains_text_input('answer', 'first response'); + + $this->delete_quba(); + } }