*/
public $history = self::HIDDEN;
+ /**
+ * @var int the context the attempt being output belongs to.
+ */
+ public $contextid;
+
/**
* Set all the feedback-related fields {@link $feedback}, {@link generalfeedback},
* {@link rightanswer} and {@link manualcomment} to
* @return string HTML fragment representing the question.
*/
public function render_question($slot, $options, $number = null) {
+ $options->contextid = $this->context->id;
return $this->get_question_attempt($slot)->render($options, $number);
}
* @return string HTML fragment.
*/
public function render_question_head_html($slot) {
+ $options->contextid = $this->context->id;
return $this->get_question_attempt($slot)->render_head_html();
}
* @return string HTML fragment representing the question.
*/
public function render_question_at_step($slot, $seq, $options, $number = null) {
+ $options->contextid = $this->context->id;
return $this->get_question_attempt($slot)->render_at_step($seq, $options, $number, $this->preferredbehaviour);
}
*/
const PARAM_MARK = 'parammark';
+ /**
+ * @var string special value used by manual grading because {@link PARAM_NUMBER}
+ * converts '' to 0.
+ */
+ const PARAM_FILES = 'paramfiles';
+
/** @var integer if this attempts is stored in the question_attempts table, the id of that row. */
protected $id = null;
return $default;
}
+ /**
+ * Get the latest set of files for a particular question type variable of
+ * type question_attempt::PARAM_FILES.
+ *
+ * @param string $name the name of the associated variable.
+ * @return array of {@link stored_files}.
+ */
+ public function get_last_qt_files($name, $contextid) {
+ foreach ($this->get_reverse_step_iterator() as $step) {
+ if ($step->has_qt_var($name)) {
+ return $step->get_qt_files($name, $contextid);
+ }
+ }
+ return array();
+ }
+
+ /**
+ * Get the URL of a file that belongs to a response variable of this
+ * question_attempt.
+ * @param stored_file $file the file to link to.
+ * @return string the URL of that file.
+ */
+ public function get_response_file_url(stored_file $file) {
+ return file_encode_url(new moodle_url('/pluginfile.php'), '/' . implode('/', array(
+ $file->get_contextid(),
+ $file->get_component(),
+ $file->get_filearea(),
+ $this->usageid,
+ $this->slot,
+ $file->get_itemid())) .
+ $file->get_filepath() . $file->get_filename());
+ }
+
/**
* Get the latest value of a particular behaviour variable. That is,
* get the value from the latest step that has it set. Return null if it is
} else {
$var = null;
}
+
if (is_string($var)) {
- $var = stripslashes($var);
+ $var = $var;
}
+
return $var;
}
/** @var array name => value pairs. The submitted data. */
private $data;
+ /** @var array name => array of {@link stored_file}s. Caches the contents of file areas. */
+ private $files = array();
+
/**
* You should not need to call this constructor in your own code. Steps are
* normally created by {@link question_attempt} methods like
$this->data[$name] = $value;
}
+ /**
+ * Get the latest set of files for a particular question type variable of
+ * type question_attempt::PARAM_FILES.
+ *
+ * @param string $name the name of the associated variable.
+ * @return array of {@link stored_files}.
+ */
+ public function get_qt_files($name, $contextid) {
+ if (array_key_exists($name, $this->files)) {
+ return $this->files[$name];
+ }
+
+ if (!$this->has_qt_var($name)) {
+ $this->files[$name] = array();
+ return array();
+ }
+
+ $fs = get_file_storage();
+ $this->files[$name] = $fs->get_area_files($contextid, 'question',
+ 'response_' . $name, $this->id, 'sortorder', false);
+
+ return $this->files[$name];
+ }
+
/**
* Get all the question type variables.
* @param array name => value pairs.
$step = new question_attempt_step_read_only($data, $record->timecreated, $record->userid);
$step->state = question_state::get($record->state);
+ $step->id = $record->attemptstepid;
if (!is_null($record->fraction)) {
$step->fraction = $record->fraction + 0;
}
}
public function get_expected_data() {
- return array('answer' => PARAM_CLEANHTML);
+ $expecteddata = array('answer' => PARAM_CLEANHTML);
+ if ($this->attachments != 0) {
+ $expecteddata['attachments'] = question_attempt::PARAM_FILES;
+ }
+ return $expecteddata;
}
public function summarise_response(array $response) {
return question_utils::arrays_same_at_key_missing_is_blank(
$prevresponse, $newresponse, 'answer');
}
+
+ public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
+ if ($component == 'question' && $filearea == 'response_attachments') {
+ // Response attachments visible if the question has them.
+ return $this->attachments != 0;
+
+ } else {
+ return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);
+ }
+ }
}
$inputname = $qa->get_qt_field_name('answer');
$response = $qa->get_last_qt_var('answer', '');
if (empty($options->readonly)) {
- $answer =$responseoutput->response_area_input($inputname,
+ $answer = $responseoutput->response_area_input($inputname,
$response, $question->responsefieldlines);
} else {
$files = '';
if (empty($options->readonly)) {
if ($question->attachments == 1) {
- $files = $this->filepicker_input();
+ $files = $this->filepicker_input($qa, $options);
} else if ($question->attachments != 0) {
- $files = $this->filemanager_input();
+ $files = $this->filemanager_input($qa, $options);
}
} else if ($question->attachments != 0) {
- $files = $this->files_read_only();
+ $files = $this->files_read_only($qa, $options);
}
$result = '';
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
$result .= html_writer::tag('div', $answer, array('class' => 'answer'));
+ $result .= html_writer::tag('div', $files, array('class' => 'attachments'));
$result .= html_writer::end_tag('div');
return $result;
/**
* Displays any attached files when the question is in read-only mode.
*/
- public function files_read_only() {
- return '';
+ public function files_read_only(question_attempt $qa, question_display_options $options) {
+ $files = $qa->get_last_qt_files('attachments', $options->contextid);
+ $output = array();
+
+ foreach ($files as $file) {
+ $mimetype = $file->get_mimetype();
+ $output[] = html_writer::tag('p', html_writer::link($qa->get_response_file_url($file),
+ $this->output->pix_icon(file_mimetype_icon($mimetype), $mimetype,
+ 'moodle', array('class' => 'icon')) . ' ' . s($file->get_filename())));
+ }
+ return implode($output);
}
/**
* Displays the input control for when the student should upload a single file.
*/
- public function filepicker_input() {
+ public function filepicker_input(question_attempt $qa, question_display_options $options) {
return '';
}
/**
* Displays the input control for when the student should upload a number of files.
*/
- public function filemanager_input() {
+ public function filemanager_input(question_attempt $qa, question_display_options $options) {
return '';
}
}