MDL-32239 question bank: wrong cap checks editing/viewing quesitions
[moodle.git] / question / type / edit_question_form.php
CommitLineData
aeb15530 1<?php
fe6ce234
DC
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
36703ed7 17/**
18 * A base class for question editing forms.
19 *
7764183a 20 * @package moodlecore
2b7da645 21 * @subpackage questiontypes
b04a4319
TH
22 * @copyright 2006 The Open University
23 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
fe6ce234 24 */
36703ed7 25
2b7da645 26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
29
72553162
TH
30abstract class question_wizard_form extends moodleform {
31 /**
32 * Add all the hidden form fields used by question/question.php.
33 */
34 protected function add_hidden_fields() {
35 $mform = $this->_form;
36
37 $mform->addElement('hidden', 'id');
38 $mform->setType('id', PARAM_INT);
39
40 $mform->addElement('hidden', 'inpopup');
41 $mform->setType('inpopup', PARAM_INT);
42
43 $mform->addElement('hidden', 'cmid');
44 $mform->setType('cmid', PARAM_INT);
45
46 $mform->addElement('hidden', 'courseid');
47 $mform->setType('courseid', PARAM_INT);
48
49 $mform->addElement('hidden', 'returnurl');
50 $mform->setType('returnurl', PARAM_LOCALURL);
51
52 $mform->addElement('hidden', 'scrollpos');
53 $mform->setType('scrollpos', PARAM_INT);
54
55 $mform->addElement('hidden', 'appendqnumstring');
56 $mform->setType('appendqnumstring', PARAM_ALPHA);
57 }
58}
59
36703ed7 60/**
61 * Form definition base class. This defines the common fields that
62 * all question types need. Question types should define their own
63 * class that inherits from this one, and implements the definition_inner()
64 * method.
271e6dec 65 *
7764183a
TH
66 * @copyright 2006 The Open University
67 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
36703ed7 68 */
72553162 69abstract class question_edit_form extends question_wizard_form {
2b7da645
TH
70 const DEFAULT_NUM_HINTS = 2;
71
271ffe3f 72 /**
73 * Question object with options and answers already loaded by get_question_options
74 * Be careful how you use this it is needed sometimes to set up the structure of the
32db0d42 75 * form in definition_inner but data is always loaded into the form with set_data.
271ffe3f 76 * @var object
77 */
2b7da645
TH
78 protected $question;
79
80 protected $contexts;
81 protected $category;
82 protected $categorycontext;
271e6dec 83
fe6ce234
DC
84 /** @var object current context */
85 public $context;
86 /** @var array html editor options */
87 public $editoroptions;
88 /** @var array options to preapre draft area */
89 public $fileoptions;
90 /** @var object instance of question type */
91 public $instance;
92
2b7da645 93 public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
fe6ce234 94 global $DB;
271e6dec 95
271ffe3f 96 $this->question = $question;
271e6dec 97 $this->contexts = $contexts;
98
eaeb6b51
TH
99 $record = $DB->get_record('question_categories',
100 array('id' => $question->category), 'contextid');
fe6ce234
DC
101 $this->context = get_context_instance_by_id($record->contextid);
102
eaeb6b51
TH
103 $this->editoroptions = array('subdirs' => 1, 'maxfiles' => EDITOR_UNLIMITED_FILES,
104 'context' => $this->context);
41dcc2a5 105 $this->fileoptions = array('subdirs' => 1, 'maxfiles' => -1, 'maxbytes' => -1);
fe6ce234 106
271e6dec 107 $this->category = $category;
108 $this->categorycontext = get_context_instance_by_id($category->contextid);
271e6dec 109
2b7da645 110 parent::__construct($submiturl, null, 'post', '', null, $formeditable);
271ffe3f 111 }
8e652f02 112
36703ed7 113 /**
114 * Build the form definition.
1d284fbd 115 *
295043c2 116 * This adds all the form fields that the default question type supports.
36703ed7 117 * If your question type does not support all these fields, then you can
118 * override this method and remove the ones you don't want with $mform->removeElement().
119 */
c7df5006 120 protected function definition() {
f34488b2 121 global $COURSE, $CFG, $DB;
1d284fbd 122
36703ed7 123 $qtype = $this->qtype();
124 $langfile = "qtype_$qtype";
1d284fbd 125
f9b0500f 126 $mform = $this->_form;
36703ed7 127
128 // Standard fields at the start of the form.
271ffe3f 129 $mform->addElement('header', 'generalheader', get_string("general", 'form'));
1d284fbd 130
2b7da645 131 if (!isset($this->question->id)) {
5d548d3e 132 // Adding question
2b7da645 133 $mform->addElement('questioncategory', 'category', get_string('category', 'question'),
522b89d1 134 array('contexts' => $this->contexts->having_cap('moodle/question:add')));
eaeb6b51
TH
135 } else if (!($this->question->formoptions->canmove ||
136 $this->question->formoptions->cansaveasnew)) {
5d548d3e 137 // Editing question with no permission to move from category.
2b7da645 138 $mform->addElement('questioncategory', 'category', get_string('category', 'question'),
271e6dec 139 array('contexts' => array($this->categorycontext)));
eaeb6b51 140 } else if ($this->question->formoptions->movecontext) {
5d548d3e 141 // Moving question to another context.
eaeb6b51
TH
142 $mform->addElement('questioncategory', 'categorymoveto',
143 get_string('category', 'question'),
271e6dec 144 array('contexts' => $this->contexts->having_cap('moodle/question:add')));
145
146 } else {
5d548d3e 147 // Editing question with permission to move from category or save as new q
271e6dec 148 $currentgrp = array();
eaeb6b51
TH
149 $currentgrp[0] = $mform->createElement('questioncategory', 'category',
150 get_string('categorycurrent', 'question'),
271e6dec 151 array('contexts' => array($this->categorycontext)));
eaeb6b51
TH
152 if ($this->question->formoptions->canedit ||
153 $this->question->formoptions->cansaveasnew) {
271e6dec 154 //not move only form
eaeb6b51
TH
155 $currentgrp[1] = $mform->createElement('checkbox', 'usecurrentcat', '',
156 get_string('categorycurrentuse', 'question'));
271e6dec 157 $mform->setDefault('usecurrentcat', 1);
158 }
159 $currentgrp[0]->freeze();
160 $currentgrp[0]->setPersistantFreeze(false);
eaeb6b51
TH
161 $mform->addGroup($currentgrp, 'currentgrp',
162 get_string('categorycurrent', 'question'), null, false);
271e6dec 163
eaeb6b51
TH
164 $mform->addElement('questioncategory', 'categorymoveto',
165 get_string('categorymoveto', 'question'),
271e6dec 166 array('contexts' => array($this->categorycontext)));
eaeb6b51
TH
167 if ($this->question->formoptions->canedit ||
168 $this->question->formoptions->cansaveasnew) {
271e6dec 169 //not move only form
170 $mform->disabledIf('categorymoveto', 'usecurrentcat', 'checked');
171 }
172 }
375ed78a 173
eaeb6b51
TH
174 $mform->addElement('text', 'name', get_string('questionname', 'question'),
175 array('size' => 50));
271ffe3f 176 $mform->setType('name', PARAM_TEXT);
36703ed7 177 $mform->addRule('name', null, 'required', null, 'client');
1d284fbd 178
2b7da645 179 $mform->addElement('editor', 'questiontext', get_string('questiontext', 'question'),
5d548d3e 180 array('rows' => 15), $this->editoroptions);
36703ed7 181 $mform->setType('questiontext', PARAM_RAW);
1d284fbd 182
2b7da645 183 $mform->addElement('text', 'defaultmark', get_string('defaultmark', 'question'),
36703ed7 184 array('size' => 3));
7664182d 185 $mform->setType('defaultmark', PARAM_FLOAT);
2b7da645
TH
186 $mform->setDefault('defaultmark', 1);
187 $mform->addRule('defaultmark', null, 'required', null, 'client');
36703ed7 188
2b7da645 189 $mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'question'),
5d548d3e 190 array('rows' => 10), $this->editoroptions);
36703ed7 191 $mform->setType('generalfeedback', PARAM_RAW);
2b7da645 192 $mform->addHelpButton('generalfeedback', 'generalfeedback', 'question');
1d284fbd 193
36703ed7 194 // Any questiontype specific fields.
195 $this->definition_inner($mform);
196
c599a682 197 if (!empty($CFG->usetags)) {
198 $mform->addElement('header', 'tagsheader', get_string('tags'));
199 $mform->addElement('tags', 'tags', get_string('tags'));
200 }
201
2b7da645 202 if (!empty($this->question->id)) {
eaeb6b51
TH
203 $mform->addElement('header', 'createdmodifiedheader',
204 get_string('createdmodifiedheader', 'question'));
7f389342 205 $a = new stdClass();
2b7da645 206 if (!empty($this->question->createdby)) {
271e6dec 207 $a->time = userdate($this->question->timecreated);
eaeb6b51
TH
208 $a->user = fullname($DB->get_record(
209 'user', array('id' => $this->question->createdby)));
271e6dec 210 } else {
211 $a->time = get_string('unknown', 'question');
212 $a->user = get_string('unknown', 'question');
213 }
eaeb6b51
TH
214 $mform->addElement('static', 'created', get_string('created', 'question'),
215 get_string('byandon', 'question', $a));
2b7da645 216 if (!empty($this->question->modifiedby)) {
7f389342 217 $a = new stdClass();
271e6dec 218 $a->time = userdate($this->question->timemodified);
eaeb6b51
TH
219 $a->user = fullname($DB->get_record(
220 'user', array('id' => $this->question->modifiedby)));
221 $mform->addElement('static', 'modified', get_string('modified', 'question'),
222 get_string('byandon', 'question', $a));
271e6dec 223 }
224 }
225
72553162 226 $this->add_hidden_fields();
36703ed7 227
271e6dec 228 $mform->addElement('hidden', 'movecontext');
229 $mform->setType('movecontext', PARAM_BOOL);
230
72553162
TH
231 $mform->addElement('hidden', 'qtype');
232 $mform->setType('qtype', PARAM_ALPHA);
fa583f5f 233
375ed78a 234 $buttonarray = array();
2b7da645 235 if (!empty($this->question->id)) {
72553162 236 // Editing / moving question
2b7da645 237 if ($this->question->formoptions->movecontext) {
eaeb6b51
TH
238 $buttonarray[] = $mform->createElement('submit', 'submitbutton',
239 get_string('moveq', 'question'));
51c5e605 240 } else if ($this->question->formoptions->canedit) {
eaeb6b51
TH
241 $buttonarray[] = $mform->createElement('submit', 'submitbutton',
242 get_string('savechanges'));
271e6dec 243 }
2b7da645 244 if ($this->question->formoptions->cansaveasnew) {
eaeb6b51
TH
245 $buttonarray[] = $mform->createElement('submit', 'makecopy',
246 get_string('makecopy', 'question'));
271e6dec 247 }
eaeb6b51 248 $buttonarray[] = $mform->createElement('cancel');
271e6dec 249 } else {
72553162 250 // Adding new question
eaeb6b51
TH
251 $buttonarray[] = $mform->createElement('submit', 'submitbutton',
252 get_string('savechanges'));
253 $buttonarray[] = $mform->createElement('cancel');
375ed78a 254 }
375ed78a 255 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
256 $mform->closeHeaderBefore('buttonar');
271e6dec 257
5d548d3e 258 if ($this->question->formoptions->movecontext) {
271e6dec 259 $mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar'));
eaeb6b51
TH
260 } else if ((!empty($this->question->id)) && (!($this->question->formoptions->canedit ||
261 $this->question->formoptions->cansaveasnew))) {
271e6dec 262 $mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar', 'currentgrp'));
263 }
36703ed7 264 }
fa583f5f 265
36703ed7 266 /**
267 * Add any question-type specific form fields.
1d284fbd 268 *
269 * @param object $mform the form being built.
36703ed7 270 */
2b7da645 271 protected function definition_inner($mform) {
36703ed7 272 // By default, do nothing.
273 }
1d284fbd 274
2aef1fe5 275 /**
276 * Get the list of form elements to repeat, one for each answer.
277 * @param object $mform the form being built.
278 * @param $label the label to use for each option.
279 * @param $gradeoptions the possible grades for each answer.
280 * @param $repeatedoptions reference to array of repeated options to fill
eaeb6b51
TH
281 * @param $answersoption reference to return the name of $question->options
282 * field holding an array of answers
2aef1fe5 283 * @return array of form fields.
284 */
2a6c5c52 285 protected function get_per_answer_fields($mform, $label, $gradeoptions,
eaeb6b51 286 &$repeatedoptions, &$answersoption) {
2aef1fe5 287 $repeated = array();
1c2ed7c5 288 $repeated[] = $mform->createElement('header', 'answerhdr', $label);
eaeb6b51
TH
289 $repeated[] = $mform->createElement('text', 'answer',
290 get_string('answer', 'question'), array('size' => 80));
291 $repeated[] = $mform->createElement('select', 'fraction',
292 get_string('grade'), $gradeoptions);
293 $repeated[] = $mform->createElement('editor', 'feedback',
294 get_string('feedback', 'question'), array('rows' => 5), $this->editoroptions);
2aef1fe5 295 $repeatedoptions['answer']['type'] = PARAM_RAW;
296 $repeatedoptions['fraction']['default'] = 0;
297 $answersoption = 'answers';
298 return $repeated;
299 }
300
301 /**
302 * Add a set of form fields, obtained from get_per_answer_fields, to the form,
303 * one for each existing answer, with some blanks for some new ones.
304 * @param object $mform the form being built.
305 * @param $label the label to use for each option.
306 * @param $gradeoptions the possible grades for each answer.
eaeb6b51
TH
307 * @param $minoptions the minimum number of answer blanks to display.
308 * Default QUESTION_NUMANS_START.
2aef1fe5 309 * @param $addoptions the number of answer blanks to add. Default QUESTION_NUMANS_ADD.
310 */
eaeb6b51
TH
311 protected function add_per_answer_fields(&$mform, $label, $gradeoptions,
312 $minoptions = QUESTION_NUMANS_START, $addoptions = QUESTION_NUMANS_ADD) {
2aef1fe5 313 $answersoption = '';
314 $repeatedoptions = array();
eaeb6b51
TH
315 $repeated = $this->get_per_answer_fields($mform, $label, $gradeoptions,
316 $repeatedoptions, $answersoption);
2aef1fe5 317
2b7da645 318 if (isset($this->question->options)) {
2aef1fe5 319 $countanswers = count($this->question->options->$answersoption);
320 } else {
321 $countanswers = 0;
322 }
2b7da645 323 if ($this->question->formoptions->repeatelements) {
2aef1fe5 324 $repeatsatstart = max($minoptions, $countanswers + $addoptions);
325 } else {
326 $repeatsatstart = $countanswers;
327 }
328
eaeb6b51
TH
329 $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions,
330 'noanswers', 'addanswers', $addoptions,
331 get_string('addmorechoiceblanks', 'qtype_multichoice'));
2aef1fe5 332 }
333
2b7da645
TH
334 protected function add_combined_feedback_fields($withshownumpartscorrect = false) {
335 $mform = $this->_form;
336
eaeb6b51
TH
337 $mform->addElement('header', 'combinedfeedbackhdr',
338 get_string('combinedfeedback', 'question'));
2b7da645 339
eaeb6b51
TH
340 $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
341 foreach ($fields as $feedbackname) {
2b7da645
TH
342 $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'question'),
343 array('rows' => 5), $this->editoroptions);
344 $mform->setType($feedbackname, PARAM_RAW);
345
346 if ($withshownumpartscorrect && $feedbackname == 'partiallycorrectfeedback') {
f7094147 347 $mform->addElement('advcheckbox', 'shownumcorrect',
eaeb6b51 348 get_string('options', 'question'),
d5ffb789 349 get_string('shownumpartscorrectwhenfinished', 'question'));
2b7da645
TH
350 }
351 }
352 }
353
354 protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) {
355 $mform = $this->_form;
356
357 $repeated = array();
43e607ca 358 $repeated[] = $mform->createElement('header', 'hinthdr', get_string('hintn', 'question'));
7a719748
TH
359 $repeated[] = $mform->createElement('editor', 'hint', get_string('hinttext', 'question'),
360 array('rows' => 5), $this->editoroptions);
2b7da645
TH
361 $repeatedoptions['hint']['type'] = PARAM_RAW;
362
363 if ($withclearwrong) {
f7094147 364 $repeated[] = $mform->createElement('advcheckbox', 'hintclearwrong',
eaeb6b51 365 get_string('options', 'question'), get_string('clearwrongparts', 'question'));
2b7da645
TH
366 }
367 if ($withshownumpartscorrect) {
f7094147 368 $repeated[] = $mform->createElement('advcheckbox', 'hintshownumcorrect', '',
eaeb6b51 369 get_string('shownumpartscorrect', 'question'));
2b7da645
TH
370 }
371
372 return array($repeated, $repeatedoptions);
373 }
374
eaeb6b51
TH
375 protected function add_interactive_settings($withclearwrong = false,
376 $withshownumpartscorrect = false) {
2b7da645
TH
377 $mform = $this->_form;
378
eaeb6b51
TH
379 $mform->addElement('header', 'multitriesheader',
380 get_string('settingsformultipletries', 'question'));
2b7da645
TH
381
382 $penalties = array(
383 1.0000000,
384 0.5000000,
385 0.3333333,
386 0.2500000,
387 0.2000000,
388 0.1000000,
389 0.0000000
390 );
391 if (!empty($this->question->penalty) && !in_array($this->question->penalty, $penalties)) {
392 $penalties[] = $this->question->penalty;
393 sort($penalties);
394 }
395 $penaltyoptions = array();
396 foreach ($penalties as $penalty) {
397 $penaltyoptions["$penalty"] = (100 * $penalty) . '%';
398 }
eaeb6b51
TH
399 $mform->addElement('select', 'penalty',
400 get_string('penaltyforeachincorrecttry', 'question'), $penaltyoptions);
2b7da645 401 $mform->addRule('penalty', null, 'required', null, 'client');
068b4594 402 $mform->addHelpButton('penalty', 'penaltyforeachincorrecttry', 'question');
2b7da645
TH
403 $mform->setDefault('penalty', 0.3333333);
404
405 if (isset($this->question->hints)) {
406 $counthints = count($this->question->hints);
407 } else {
408 $counthints = 0;
409 }
410
411 if ($this->question->formoptions->repeatelements) {
412 $repeatsatstart = max(self::DEFAULT_NUM_HINTS, $counthints);
413 } else {
414 $repeatsatstart = $counthints;
415 }
416
417 list($repeated, $repeatedoptions) = $this->get_hint_fields(
418 $withclearwrong, $withshownumpartscorrect);
419 $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions,
420 'numhints', 'addhint', 1, get_string('addanotherhint', 'question'));
421 }
422
423 public function set_data($question) {
f9b0500f 424 question_bank::get_qtype($question->qtype)->set_default_options($question);
2b7da645 425
fe6ce234
DC
426 // prepare question text
427 $draftid = file_get_submitted_draft_itemid('questiontext');
428
429 if (!empty($question->questiontext)) {
430 $questiontext = $question->questiontext;
431 } else {
1a2de1c7
TH
432 $questiontext = $this->_form->getElement('questiontext')->getValue();
433 $questiontext = $questiontext['text'];
271ffe3f 434 }
eaeb6b51
TH
435 $questiontext = file_prepare_draft_area($draftid, $this->context->id,
436 'question', 'questiontext', empty($question->id) ? null : (int) $question->id,
437 $this->fileoptions, $questiontext);
fe6ce234
DC
438
439 $question->questiontext = array();
440 $question->questiontext['text'] = $questiontext;
eaeb6b51
TH
441 $question->questiontext['format'] = empty($question->questiontextformat) ?
442 editors_get_preferred_format() : $question->questiontextformat;
fe6ce234
DC
443 $question->questiontext['itemid'] = $draftid;
444
445 // prepare general feedback
446 $draftid = file_get_submitted_draft_itemid('generalfeedback');
447
448 if (empty($question->generalfeedback)) {
1a2de1c7
TH
449 $generalfeedback = $this->_form->getElement('generalfeedback')->getValue();
450 $question->generalfeedback = $generalfeedback['text'];
fe6ce234
DC
451 }
452
eaeb6b51
TH
453 $feedback = file_prepare_draft_area($draftid, $this->context->id,
454 'question', 'generalfeedback', empty($question->id) ? null : (int) $question->id,
455 $this->fileoptions, $question->generalfeedback);
fe6ce234
DC
456 $question->generalfeedback = array();
457 $question->generalfeedback['text'] = $feedback;
eaeb6b51
TH
458 $question->generalfeedback['format'] = empty($question->generalfeedbackformat) ?
459 editors_get_preferred_format() : $question->generalfeedbackformat;
fe6ce234 460 $question->generalfeedback['itemid'] = $draftid;
295043c2 461
9152f6a5 462 // Remove unnecessary trailing 0s form grade fields.
cfd24d98 463 if (isset($question->defaultgrade)) {
464 $question->defaultgrade = 0 + $question->defaultgrade;
465 }
466 if (isset($question->penalty)) {
467 $question->penalty = 0 + $question->penalty;
468 }
9152f6a5 469
295043c2 470 // Set any options.
eaeb6b51
TH
471 $extraquestionfields = question_bank::get_qtype($question->qtype)->extra_question_fields();
472 if (is_array($extraquestionfields) && !empty($question->options)) {
473 array_shift($extraquestionfields);
474 foreach ($extraquestionfields as $field) {
b555ce76 475 if (isset($question->options->$field)) {
8e652f02 476 $question->$field = $question->options->$field;
477 }
295043c2 478 }
479 }
c9c989a0 480
fe6ce234
DC
481 // subclass adds data_preprocessing code here
482 $question = $this->data_preprocessing($question);
2b7da645 483
32db0d42 484 parent::set_data($question);
36703ed7 485 }
1d284fbd 486
fe6ce234 487 /**
fdd015b7
TH
488 * Perform an preprocessing needed on the data passed to {@link set_data()}
489 * before it is used to initialise the form.
490 * @param object $question the data being passed to the form.
491 * @return object $question the modified data.
fe6ce234 492 */
c7df5006 493 protected function data_preprocessing($question) {
7a719748
TH
494 return $question;
495 }
496
fdd015b7
TH
497 /**
498 * Perform the necessary preprocessing for the fields added by
499 * {@link add_per_answer_fields()}.
500 * @param object $question the data being passed to the form.
501 * @return object $question the modified data.
502 */
2a6c5c52 503 protected function data_preprocessing_answers($question, $withanswerfiles = false) {
7a719748
TH
504 if (empty($question->options->answers)) {
505 return $question;
506 }
507
508 $key = 0;
eaeb6b51 509 foreach ($question->options->answers as $answer) {
2a6c5c52 510 if ($withanswerfiles) {
511 // Prepare the feedback editor to display files in draft area
512 $draftitemid = file_get_submitted_draft_itemid('answer['.$key.']');
513 $question->answer[$key]['text'] = file_prepare_draft_area(
514 $draftitemid, // draftid
515 $this->context->id, // context
516 'question', // component
517 'answer', // filarea
518 !empty($answer->id) ? (int) $answer->id : null, // itemid
519 $this->fileoptions, // options
520 $answer->answer // text
521 );
522 $question->answer[$key]['itemid'] = $draftitemid;
523 $question->answer[$key]['format'] = $answer->answerformat;
524 } else {
525 $question->answer[$key] = $answer->answer;
526 }
527
7a719748
TH
528 $question->fraction[$key] = 0 + $answer->fraction;
529 $question->feedback[$key] = array();
530
531 // Evil hack alert. Formslib can store defaults in two ways for
eaeb6b51
TH
532 // repeat elements:
533 // ->_defaultValues['fraction[0]'] and
534 // ->_defaultValues['fraction'][0].
535 // The $repeatedoptions['fraction']['default'] = 0 bit above means
536 // that ->_defaultValues['fraction[0]'] has already been set, but we
537 // are using object notation here, so we will be setting
538 // ->_defaultValues['fraction'][0]. That does not work, so we have
539 // to unset ->_defaultValues['fraction[0]']
7a719748
TH
540 unset($this->_form->_defaultValues["fraction[$key]"]);
541
542 // Prepare the feedback editor to display files in draft area
543 $draftitemid = file_get_submitted_draft_itemid('feedback['.$key.']');
544 $question->feedback[$key]['text'] = file_prepare_draft_area(
545 $draftitemid, // draftid
546 $this->context->id, // context
547 'question', // component
548 'answerfeedback', // filarea
549 !empty($answer->id) ? (int) $answer->id : null, // itemid
550 $this->fileoptions, // options
551 $answer->feedback // text
552 );
553 $question->feedback[$key]['itemid'] = $draftitemid;
554 $question->feedback[$key]['format'] = $answer->feedbackformat;
555 $key++;
556 }
557 return $question;
558 }
559
fdd015b7
TH
560 /**
561 * Perform the necessary preprocessing for the fields added by
562 * {@link add_combined_feedback_fields()}.
563 * @param object $question the data being passed to the form.
564 * @return object $question the modified data.
565 */
eaeb6b51
TH
566 protected function data_preprocessing_combined_feedback($question,
567 $withshownumcorrect = false) {
c9c989a0
TH
568 if (empty($question->options)) {
569 return $question;
570 }
571
eaeb6b51
TH
572 $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
573 foreach ($fields as $feedbackname) {
c9c989a0 574 $draftid = file_get_submitted_draft_itemid($feedbackname);
8b9dfc2b
TH
575 $feedback = array();
576 $feedback['text'] = file_prepare_draft_area(
c9c989a0
TH
577 $draftid, // draftid
578 $this->context->id, // context
93cadb1e 579 'question', // component
c9c989a0
TH
580 $feedbackname, // filarea
581 !empty($question->id) ? (int) $question->id : null, // itemid
582 $this->fileoptions, // options
583 $question->options->$feedbackname // text
584 );
585 $feedbackformat = $feedbackname . 'format';
8b9dfc2b
TH
586 $feedback['format'] = $question->options->$feedbackformat;
587 $feedback['itemid'] = $draftid;
588
589 $question->$feedbackname = $feedback;
c9c989a0
TH
590 }
591
592 if ($withshownumcorrect) {
593 $question->shownumcorrect = $question->options->shownumcorrect;
594 }
595
596 return $question;
597 }
598
fdd015b7
TH
599 /**
600 * Perform the necessary preprocessing for the hint fields.
601 * @param object $question the data being passed to the form.
602 * @return object $question the modified data.
603 */
eaeb6b51
TH
604 protected function data_preprocessing_hints($question, $withclearwrong = false,
605 $withshownumpartscorrect = false) {
7a719748
TH
606 if (empty($question->hints)) {
607 return $question;
608 }
609
610 $key = 0;
eaeb6b51 611 foreach ($question->hints as $hint) {
7a719748
TH
612 $question->hint[$key] = array();
613
614 // prepare feedback editor to display files in draft area
615 $draftitemid = file_get_submitted_draft_itemid('hint['.$key.']');
616 $question->hint[$key]['text'] = file_prepare_draft_area(
617 $draftitemid, // draftid
618 $this->context->id, // context
619 'question', // component
620 'hint', // filarea
621 !empty($hint->id) ? (int) $hint->id : null, // itemid
622 $this->fileoptions, // options
623 $hint->hint // text
624 );
625 $question->hint[$key]['itemid'] = $draftitemid;
626 $question->hint[$key]['format'] = $hint->hintformat;
627 $key++;
7a719748 628
8b9dfc2b
TH
629 if ($withclearwrong) {
630 $question->hintclearwrong[] = $hint->clearwrong;
631 }
632 if ($withshownumpartscorrect) {
633 $question->hintshownumcorrect[] = $hint->shownumcorrect;
634 }
635 }
7a719748 636
fe6ce234
DC
637 return $question;
638 }
639
2b7da645
TH
640 public function validation($fromform, $files) {
641 $errors = parent::validation($fromform, $files);
51c5e605 642 if (empty($fromform['makecopy']) && isset($this->question->id)
eaeb6b51
TH
643 && ($this->question->formoptions->canedit ||
644 $this->question->formoptions->cansaveasnew)
51c5e605 645 && empty($fromform['usecurrentcat']) && !$this->question->formoptions->canmove) {
2b7da645
TH
646 $errors['currentgrp'] = get_string('nopermissionmove', 'question');
647 }
648 return $errors;
649 }
650
36703ed7 651 /**
652 * Override this in the subclass to question type name.
eaeb6b51
TH
653 * @return the question type name, should be the same as the name() method
654 * in the question type class.
36703ed7 655 */
2b7da645 656 public abstract function qtype();
36703ed7 657}