MDL-27410 Big cleanup of the coding style in qtype_calculated.
[moodle.git] / question / type / calculatedmulti / edit_calculatedmulti_form.php
CommitLineData
2d279432 1<?php
d3603157
TH
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
2d279432 17/**
d3603157 18 * Defines the editing form for calculated multiple-choice questions.
2d279432 19 *
b04a4319 20 * @package qtype
d3603157 21 * @subpackage calculatedmulti
b04a4319
TH
22 * @copyright 2007 Jamie Pratt me@jamiep.org
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2d279432
PP
24 */
25
d3603157 26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
29
2d279432 30/**
d3603157 31 * Calculated multiple-choice question editing form.
b04a4319
TH
32 *
33 * @copyright 2007 Jamie Pratt me@jamiep.org
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2d279432
PP
35 */
36class question_edit_calculatedmulti_form extends question_edit_form {
37 /**
38 * Handle to the question type for this question.
39 *
40 * @var question_calculatedmulti_qtype
41 */
fe6ce234 42 public $qtypeobj;
d90b016b
PP
43 public $questiondisplay ;
44 public $initialname = '';
45 public $reload = false ;
fe6ce234 46 function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true) {
2d279432 47 global $QTYPES, $SESSION, $CFG, $DB;
2d279432
PP
48 $this->question = $question;
49 $this->qtypeobj =& $QTYPES[$this->question->qtype];
d90b016b
PP
50 if ( "1" == optional_param('reload','', PARAM_INT )) {
51 $this->reload = true ;
52 }else {
53 $this->reload = false ;
54 }
55 if(!$this->reload ){ // use database data as this is first pass
56 if(isset($this->question->id )){
57 // remove prefix #{..}# if exists
58 $this->initialname = $question->name ;
59 $regs= array();
60 if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
61 $question->name = str_replace($regs[0], '', $question->name);
fe6ce234 62 };
d90b016b
PP
63 }
64 }else {
fe6ce234 65 }
2d279432
PP
66 parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
67 }
68
2d279432 69 function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
fe6ce234
DC
70 // $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
71 $repeated = array();
2d279432 72 $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
fe6ce234 73 // if ($this->editasmultichoice == 1){
5e8a85aa 74 $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'question'), array('size' => 50));
2d279432 75 $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
5e8a85aa 76 $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'question'), null, $this->editoroptions);
2d279432
PP
77 $repeatedoptions['answer']['type'] = PARAM_RAW;
78 $repeatedoptions['fraction']['default'] = 0;
79 $answersoption = 'answers';
80
81 $mform->setType('answer', PARAM_NOTAGS);
82
83 $addrepeated = array();
fe6ce234
DC
84 $addrepeated[] =& $mform->createElement('hidden', 'tolerance');
85 $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
2d279432
PP
86 $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
87 $repeatedoptions['tolerance']['default'] = 0.01;
88
89 $addrepeated[] =& $mform->createElement('select', 'correctanswerlength', get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
90 $repeatedoptions['correctanswerlength']['default'] = 2;
91
5e8a85aa 92 $answerlengthformats = array('1' => get_string('decimalformat', 'qtype_numerical'), '2' => get_string('significantfiguresformat', 'qtype_calculated'));
2d279432
PP
93 $addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
94 array_splice($repeated, 3, 0, $addrepeated);
fe6ce234 95 $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
2d279432
PP
96
97 return $repeated;
98 }
99
100 /**
101 * Add question-type specific form fields.
102 *
103 * @param MoodleQuickForm $mform the form being built.
104 */
c7df5006 105 protected function definition_inner($mform) {
2d279432
PP
106 global $QTYPES;
107 $this->qtypeobj =& $QTYPES[$this->qtype()];
fe6ce234
DC
108 // echo code left for testing period
109 // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
d7be65a3 110 $label = get_string("sharedwildcards", "qtype_calculated");
2d279432 111 $mform->addElement('hidden', 'initialcategory', 1);
d90b016b 112 $mform->addElement('hidden', 'reload', 1);
2d279432
PP
113 $mform->setType('initialcategory', PARAM_INT);
114
fe6ce234
DC
115 // $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
116 $html2 ="";
2d279432 117 $mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
d90b016b
PP
118 if(isset($this->question->id )){
119 $mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name');
120 };
2d279432
PP
121 $addfieldsname='updatecategory';
122 $addstring=get_string("updatecategory", "qtype_calculated");
fe6ce234 123 $mform->registerNoSubmitButton($addfieldsname);
2d279432 124 $this->editasmultichoice = 1 ;
fe6ce234 125
2d279432
PP
126
127 $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
128 $mform->registerNoSubmitButton('createoptionbutton');
fe6ce234
DC
129 $mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
130 $mform->setType('multichoice', PARAM_INT);
2d279432 131
fe6ce234
DC
132
133 // $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
134 $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
135 $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
136 $mform->setDefault('single', 1);
137
138 $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
ed14ad02 139 $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
fe6ce234
DC
140 $mform->setDefault('shuffleanswers', 1);
141
142 $numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
143 $menu = array();
144 foreach ($numberingoptions as $numberingoption) {
145 $menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
146 }
147 $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
148 $mform->setDefault('answernumbering', 'abc');
2d279432
PP
149
150 $creategrades = get_grade_options();
fe6ce234
DC
151 $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
152 $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
153
2d279432
PP
154
155 $repeated = array();
fe6ce234
DC
156 // if ($this->editasmultichoice == 1){
157 $nounits = optional_param('nounits', 1, PARAM_INT);
158 $mform->addElement('hidden', 'nounits', $nounits);
159 $mform->setType('nounits', PARAM_INT);
160 $mform->setConstants(array('nounits'=>$nounits));
161 for ($i=0; $i< $nounits; $i++) {
162 $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
163 $mform->setType('unit'."[$i]", PARAM_NOTAGS);
164 $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
165 $mform->setType('multiplier'."[$i]", PARAM_NUMBER);
166 }
2d279432 167
fe6ce234
DC
168 $mform->setType('addunits','hidden');
169 $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
2d279432 170
fe6ce234
DC
171 foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
172 $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), null, $this->editoroptions);
173 $mform->setType($feedbackname, PARAM_RAW);
174 }
2d279432
PP
175 //hidden elements
176 $mform->addElement('hidden', 'synchronize', '');
177 $mform->setType('synchronize', PARAM_INT);
178 if (isset($this->question->options)&& isset($this->question->options->synchronize) ){
179 $mform->setDefault("synchronize", $this->question->options->synchronize);
180 } else {
181 $mform->setDefault("synchronize", 0 );
182 }
183 $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
184 $mform->setType('wizard', PARAM_ALPHA);
2d279432
PP
185 }
186
fe6ce234
DC
187 function data_preprocessing($question) {
188 $default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
2d279432
PP
189 if (isset($question->options)){
190 $answers = $question->options->answers;
191 if (count($answers)) {
192 $key = 0;
193 foreach ($answers as $answer){
fe6ce234 194 $draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
2d279432
PP
195 $default_values['answer['.$key.']'] = $answer->answer;
196 $default_values['fraction['.$key.']'] = $answer->fraction;
197 $default_values['tolerance['.$key.']'] = $answer->tolerance;
198 $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
199 $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
200 $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
fe6ce234
DC
201 $default_values['feedback['.$key.']'] = array();
202 // prepare draftarea
41dcc2a5 203 $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area($draftid, $this->context->id, 'question', 'answerfeedback', empty($answer->id)?null:(int)$answer->id, $this->fileoptions, $answer->feedback);
fe6ce234
DC
204 $default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
205 $default_values['feedback['.$key.']']['itemid'] = $draftid;
2d279432
PP
206 $key++;
207 }
208 }
fe6ce234 209 $default_values['synchronize'] = $question->options->synchronize ;
2d279432
PP
210
211 if (isset($question->options->units)){
212 $units = array_values($question->options->units);
213 // make sure the default unit is at index 0
214 usort($units, create_function('$a, $b',
fe6ce234
DC
215 'if (1.0 === (float)$a->multiplier) { return -1; } else '.
216 'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
2d279432
PP
217 if (count($units)) {
218 $key = 0;
219 foreach ($units as $unit){
220 $default_values['unit['.$key.']'] = $unit->unit;
221 $default_values['multiplier['.$key.']'] = $unit->multiplier;
222 $key++;
223 }
224 }
225 }
226 }
227 if (isset($question->options->single)){
fe6ce234
DC
228 $default_values['single'] = $question->options->single;
229 $default_values['answernumbering'] = $question->options->answernumbering;
230 $default_values['shuffleanswers'] = $question->options->shuffleanswers;
231 }
2d279432
PP
232 $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
233 $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
fe6ce234
DC
234
235 // prepare draft files
236 foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
237 if (!isset($question->options->$feedbackname)) {
238 continue;
239 }
240 $text = $question->options->$feedbackname;
241 $draftid = file_get_submitted_draft_itemid($feedbackname);
242 $feedbackformat = $feedbackname . 'format';
243 $format = $question->options->$feedbackformat;
244 $default_values[$feedbackname] = array();
245 $default_values[$feedbackname]['text'] = file_prepare_draft_area(
246 $draftid, // draftid
247 $this->context->id, // context
248 'qtype_calculatedmulti', // component
249 $feedbackname, // filarea
250 !empty($question->id)?(int)$question->id:null, // itemid
251 $this->fileoptions, // options
252 $text // text
253 );
254 $default_values[$feedbackname]['format'] = $format;
255 $default_values[$feedbackname]['itemid'] = $draftid;
256 }
257 /**
258 * set the wild cards category display given that on loading the category element is
259 * unselected when processing this function but have a valid value when processing the
260 * update category button. The value can be obtain by
261 * $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
262 * but is coded using existing functions
263 */
0ff4bd08
TH
264 $qu = new stdClass();
265 $el = new stdClass();
fe6ce234
DC
266 /* no need to call elementExists() here */
267 if ($this->_form->elementExists('category')){
2d279432 268 $el=$this->_form->getElement('category');
fe6ce234 269 } else {
2d279432 270 $el=$this->_form->getElement('categorymoveto');
fe6ce234
DC
271 }
272 if($value =$el->getSelected()) {
2d279432
PP
273 $qu->category =$value[0];
274 }else {
275 $qu->category=$question->category;// on load $question->category is set by question.php
276 }
277 $html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
278 $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
279 $question = (object)((array)$question + $default_values);
fe6ce234 280 return $question;
2d279432
PP
281 }
282
283 function qtype() {
284 return 'calculatedmulti';
285 }
286
287 function validation($data, $files) {
fe6ce234
DC
288 // echo code left for testing period
289 // echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
290 // echo "<p>data <pre>";print_r($data);echo "</pre></p>";
2d279432
PP
291
292 $errors = parent::validation($data, $files);
293 //verifying for errors in {=...} in question text;
294 $qtext = "";
fe6ce234
DC
295 $qtextremaining = $data['questiontext']['text'];
296 $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
297 foreach ($possibledatasets as $name => $value) {
2d279432
PP
298 $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
299 }
fe6ce234
DC
300 // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
301 while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
2d279432
PP
302 $qtextsplits = explode($regs1[0], $qtextremaining, 2);
303 $qtext =$qtext.$qtextsplits[0];
304 $qtextremaining = $qtextsplits[1];
305 if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
306 if(!isset($errors['questiontext'])){
307 $errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
308 }else {
309 $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
310 }
311 }
312 }
313 $answers = $data['answer'];
314 $answercount = 0;
315 $maxgrade = false;
fe6ce234 316 $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
2d279432
PP
317 $mandatorydatasets = array();
318 foreach ($answers as $key => $answer){
319 $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
320 }
321 if ( count($mandatorydatasets )==0){
fe6ce234 322 // $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
2d279432
PP
323 foreach ($answers as $key => $answer){
324 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
325 }
326 }
327 if ($data['multichoice']== 1 ){
328 foreach ($answers as $key => $answer){
329 $trimmedanswer = trim($answer);
fe6ce234 330 if (($trimmedanswer!='')||$answercount==0){
2d279432
PP
331 //verifying for errors in {=...} in answer text;
332 $qanswer = "";
333 $qanswerremaining = $trimmedanswer ;
334 $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
fe6ce234 335 foreach ($possibledatasets as $name => $value) {
2d279432
PP
336 $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
337 }
fe6ce234 338 // echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
2d279432
PP
339 while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
340 $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
341 $qanswer =$qanswer.$qanswersplits[0];
342 $qanswerremaining = $qanswersplits[1];
343 if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
344 if(!isset($errors['answer['.$key.']'])){
345 $errors['answer['.$key.']'] = $formulaerrors.':'.$regs1[1] ;
346 }else {
347 $errors['answer['.$key.']'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
348 }
349 }
350 }
351 }
352 if ($trimmedanswer!=''){
353 if ('2' == $data['correctanswerformat'][$key]
fe6ce234 354 && '0' == $data['correctanswerlength'][$key]) {
5e8a85aa 355 $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','qtype_calculated');
fe6ce234 356 }
2d279432
PP
357 if (!is_numeric($data['tolerance'][$key])){
358 $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
359 }
360 if ($data['fraction'][$key] == 1) {
fe6ce234 361 $maxgrade = true;
2d279432 362 }
fe6ce234 363
2d279432
PP
364 $answercount++;
365 }
366 //check grades
367 $totalfraction = 0 ;
368 $maxfraction = 0 ;
369 if ($answer != '') {
370 if ($data['fraction'][$key] > 0) {
371 $totalfraction += $data['fraction'][$key];
372 }
373 if ($data['fraction'][$key] > $maxfraction) {
374 $maxfraction = $data['fraction'][$key];
375 }
fe6ce234 376 }
2d279432
PP
377 }
378 if ($answercount==0){
379 $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
380 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
381 } elseif ($answercount==1){
382 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
fe6ce234 383
2d279432
PP
384 }
385
386 /// Perform sanity checks on fractional grades
387 if ($data['single']) {
388 if ($maxfraction > 0.999 ) {
389 $maxfraction = $maxfraction * 100;
390 $errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', $maxfraction);
391 }
392 } else {
393 $totalfraction = round($totalfraction,2);
394 if ($totalfraction != 1) {
395 $totalfraction = $totalfraction * 100;
396 $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
397 }
398 }
fe6ce234 399
2d279432
PP
400 if ($answercount==0){
401 $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
402 }
403 if ($maxgrade == false) {
404 $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
405 }
fe6ce234 406
2d279432
PP
407 }
408 return $errors;
409 }
410}