MDL-39980 question engine: Attempt on last loses response files

When doing Each attempt builds on last, we need to copy any response
files into a draft file area, and then re-save them.

While writing the unit test for this, I had to deal with a todo in the
question engine so that questions with files in the response could be
unit-tested.

I also found an fixed a bug with qtype_essay_question::is_same_response
and fixed some notices in the existing essay/manual graded unit tests.
This commit is contained in:
Tim Hunt 2013-08-05 16:51:05 +01:00
parent 838d78a9ff
commit afb1b3d03b
8 changed files with 221 additions and 18 deletions

View file

@ -1379,6 +1379,35 @@ class question_file_loader implements question_response_files {
public function get_files() {
return $this->step->get_qt_files($this->name, $this->contextid);
}
/**
* Copy these files into a draft area, and return the corresponding
* {@link question_file_saver} that can save them again.
*
* This is used by {@link question_attempt::start_based_on()}, which is used
* (for example) by the quizzes 'Each attempt builds on last' feature.
*
* @return question_file_saver that can re-save these files again.
*/
public function get_question_file_saver() {
// Value will be either a plain MD5 hash, or some real content, followed
// by an MD5 hash in a HTML comment. We only want the value in the latter case.
if (preg_match('/\s*<!-- File hash: [0-9a-zA-Z]{32} -->\s*$/', $this->value)) {
$value = preg_replace('/\s*<!-- File hash: [0-9a-zA-Z]{32} -->\s*$/', '', $this->value);
} else if (preg_match('/^[0-9a-zA-Z]{32}$/', $this->value)) {
$value = null;
} else {
throw new coding_exception('$value passed to question_file_loader::get_question_file_saver' .
' was not of the expected form.');
}
list($draftid, $text) = $this->step->prepare_response_files_draft_itemid_with_text(
$this->name, $this->contextid, $value);
return new question_file_saver($draftid, 'question', 'response_' . $this->name, $text);
}
}

View file

@ -923,7 +923,13 @@ class question_attempt {
* @return array name => value pairs.
*/
protected function get_resume_data() {
return $this->behaviour->get_resume_data();
$resumedata = $this->behaviour->get_resume_data();
foreach ($resumedata as $name => $value) {
if ($value instanceof question_file_loader) {
$resumedata[$name] = $value->get_question_file_saver();
}
}
return $resumedata;
}
/**
@ -975,11 +981,12 @@ class question_attempt {
*/
protected function process_response_files($name, $draftidname, $postdata = null, $text = null) {
if ($postdata) {
// There can be no files with test data (at the moment).
return null;
// For simulated posts, get the draft itemid from there.
$draftitemid = $this->get_submitted_var($draftidname, PARAM_INT, $postdata);
} else {
$draftitemid = file_get_submitted_draft_itemid($draftidname);
}
$draftitemid = file_get_submitted_draft_itemid($draftidname);
if (!$draftitemid) {
return null;
}

View file

@ -106,7 +106,7 @@ class question_attempt_step {
global $USER;
if (!is_array($data)) {
echo format_backtrace(debug_backtrace());
throw new coding_exception('$data must be an array when constructing a question_attempt_step.');
}
$this->state = question_state::$unprocessed;
$this->data = $data;