Merge branch 'MDL-70065_310' of https://github.com/timhunt/moodle into MOODLE_310_STABLE
[moodle.git] / question / type / calculatedsimple / edit_calculatedsimple_form.php
1 <?php
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/>.
17 /**
18  * Defines the editing form for the calculated simplequestion type.
19  *
20  * @package    qtype
21  * @subpackage calculatedsimple
22  * @copyright  2007 Jamie Pratt me@jamiep.org
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->dirroot . '/question/type/calculated/edit_calculated_form.php');
32 /**
33  * Editing form for the calculated simplequestion type.
34  *
35  * @copyright  2007 Jamie Pratt me@jamiep.org
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
39     /**
40      * Handle to the question type for this question.
41      *
42      * @var question_calculatedsimple_qtype
43      */
44     public $qtypeobj;
46     public $wildcarddisplay;
48     public $questiondisplay;
50     public $datasetdefs;
52     public $reload = false;
54     public $maxnumber = -1;
56     public $regenerate = true;
58     public $noofitems;
60     public $outsidelimit = false;
62     public $commentanswer = array();
64     public $answer = array();
66     public $nonemptyanswer = array();
68     public $numbererrors = array();
70     public $formdata = array();
72     public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
73         $this->regenerate = true;
74         $this->question = $question;
76         $this->qtypeobj = question_bank::get_qtype($this->question->qtype);
77         // Get the dataset definitions for this question.
78         // Coming here everytime even when using a NoSubmitButton.
79         // This will only set the values to the actual question database content
80         // which is not what we want, so this should be removed from here.
81         // Get priority to paramdatasets.
83         $this->reload = optional_param('reload', false, PARAM_BOOL);
84         if (!$this->reload) { // Use database data as this is first pass
85             // Question->id == 0 so no stored datasets.
86             if (!empty($question->id)) {
88                 $this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
89                         $question->id, array());
91                 if (!empty($this->datasetdefs)) {
92                     foreach ($this->datasetdefs as $defid => $datasetdef) {
93                         // First get the items in case their number does not correspond to itemcount.
94                         if (isset($datasetdef->id)) {
95                             $this->datasetdefs[$defid]->items =
96                                     $this->qtypeobj->get_database_dataset_items($datasetdef->id);
97                             if ($this->datasetdefs[$defid]->items != '') {
98                                 $datasetdef->itemcount = count($this->datasetdefs[$defid]->items);
99                             } else {
100                                 $datasetdef->itemcount = 0;
101                             }
102                         }
103                         // Get maxnumber.
104                         if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
105                             $this->maxnumber = $datasetdef->itemcount;
106                         }
107                     }
108                 }
110                 $i = 0;
111                 foreach ($this->question->options->answers as $answer) {
112                      $this->answer[$i] = $answer;
113                      $i++;
114                 }
115                 $this->nonemptyanswer = $this->answer;
116             }
117             $datasettoremove = false;
118             $newdatasetvalues = false;
119             $newdataset = false;
120         } else {
121             // Handle reload to get values from the form-elements
122             // answers, datasetdefs and data_items. In any case the validation
123             // step will warn the user of any error in settings the values.
124             // Verification for the specific dataset values as the other parameters
125             // units, feeedback etc are handled elsewhere.
126             // Handle request buttons :
127             //    'analyzequestion' (Identify the wild cards {x..} present in answers).
128             //    'addbutton' (create new set of datatitems).
129             //    'updatedatasets' is handled automatically on each reload.
130             // The analyzequestion is done every time on reload
131             // to detect any new wild cards so that the current display reflects
132             // the mandatory (i.e. in answers) datasets.
133             // To implement : don't do any changes if the question is used in a quiz.
134             // If new datadef, new properties should erase items.
135             // most of the data.
136             $datasettoremove = false;
137             $newdatasetvalues = false;
138             $newdataset = false;
139             $dummyform = new stdClass();
140             $mandatorydatasets = array();
141             // Should not test on adding a new answer.
142             // Should test if there are already olddatasets or if the 'analyzequestion'.
143             // submit button has been clicked.
144             if (optional_param_array('datasetdef', false, PARAM_BOOL) ||
145                     optional_param('analyzequestion', false, PARAM_BOOL)) {
147                 if ($dummyform->answer = optional_param_array('answer', '', PARAM_NOTAGS)) {
148                     // There is always at least one answer...
149                     $fraction = optional_param_array('fraction', '', PARAM_FLOAT);
150                     $tolerance = optional_param_array('tolerance', '', PARAM_LOCALISEDFLOAT);
151                     $tolerancetype = optional_param_array('tolerancetype', '', PARAM_FLOAT);
152                     $correctanswerlength = optional_param_array('correctanswerlength', '', PARAM_INT);
153                     $correctanswerformat = optional_param_array('correctanswerformat', '', PARAM_INT);
155                     foreach ($dummyform->answer as $key => $answer) {
156                         if (trim($answer) != '') {  // Just look for non-empty.
157                             $this->answer[$key] = new stdClass();
158                             $this->answer[$key]->answer = $answer;
159                             $this->answer[$key]->fraction = $fraction[$key];
160                             $this->answer[$key]->tolerance = $tolerance[$key];
161                             $this->answer[$key]->tolerancetype = $tolerancetype[$key];
162                             $this->answer[$key]->correctanswerlength = $correctanswerlength[$key];
163                             $this->answer[$key]->correctanswerformat = $correctanswerformat[$key];
164                             $this->nonemptyanswer[]= $this->answer[$key];
165                             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
166                         }
167                     }
168                 }
169                 $this->datasetdefs = array();
170                 // Rebuild datasetdefs from old values.
171                 if ($olddef = optional_param_array('datasetdef', '', PARAM_RAW)) {
172                     $calcmin = optional_param_array('calcmin', '', PARAM_LOCALISEDFLOAT);
173                     $calclength = optional_param_array('calclength', '', PARAM_INT);
174                     $calcmax = optional_param_array('calcmax', '', PARAM_LOCALISEDFLOAT);
175                     $oldoptions  = optional_param_array('defoptions', '', PARAM_RAW);
176                     $newdatasetvalues = false;
177                     $sizeofolddef = count($olddef);
178                     for ($key = 1; $key <= $sizeofolddef; $key++) {
179                         $def = $olddef[$key];
180                         $this->datasetdefs[$def]= new stdClass();
181                         $this->datasetdefs[$def]->type = 1;
182                         $this->datasetdefs[$def]->category = 0;
183                         $this->datasetdefs[$def]->options = $oldoptions[$key];
184                         $this->datasetdefs[$def]->calcmin = $calcmin[$key];
185                         $this->datasetdefs[$def]->calcmax = $calcmax[$key];
186                         $this->datasetdefs[$def]->calclength = $calclength[$key];
187                         // Then compare with new values.
188                         if (preg_match('~^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$~',
189                                 $this->datasetdefs[$def]->options, $regs)) {
190                             if ($this->datasetdefs[$def]->calcmin != $regs[2]||
191                                     $this->datasetdefs[$def]->calcmax != $regs[3] ||
192                                     $this->datasetdefs[$def]->calclength != $regs[4]) {
193                                 $newdatasetvalues = true;
194                             }
195                         }
196                         $this->datasetdefs[$def]->options = "uniform:" .
197                                 $this->datasetdefs[$def]->calcmin . ":" .
198                                 $this->datasetdefs[$def]->calcmax . ":" .
199                                 $this->datasetdefs[$def]->calclength;
200                     }
201                 }
202                 // Detect new datasets.
203                 $newdataset = false;
204                 foreach ($mandatorydatasets as $datasetname) {
205                     if (!isset($this->datasetdefs["1-0-{$datasetname}"])) {
206                         $key = "1-0-{$datasetname}";
207                         $this->datasetdefs[$key] = new stdClass();
208                         $this->datasetdefs[$key]->type = 1;
209                         $this->datasetdefs[$key]->category = 0;
210                         $this->datasetdefs[$key]->name = $datasetname;
211                         $this->datasetdefs[$key]->options = "uniform:1.0:10.0:1";
212                         $newdataset = true;
213                     } else {
214                         $this->datasetdefs["1-0-{$datasetname}"]->name = $datasetname;
215                     }
216                 }
217                 // Remove obsolete datasets.
218                 $datasettoremove = false;
219                 foreach ($this->datasetdefs as $defkey => $datasetdef) {
220                     if (!isset($datasetdef->name)) {
221                         $datasettoremove = true;
222                         unset($this->datasetdefs[$defkey]);
223                     }
224                 }
225             }
226         } // Handle reload.
227         // Create items if  $newdataset and noofitems > 0 and !$newdatasetvalues.
228         // Eliminate any items if $newdatasetvalues.
229         // Eliminate any items if $datasettoremove, $newdataset, $newdatasetvalues.
230         if ($datasettoremove ||$newdataset ||$newdatasetvalues) {
231             foreach ($this->datasetdefs as $defkey => $datasetdef) {
232                 $datasetdef->itemcount = 0;
233                 unset($datasetdef->items);
234             }
235         }
236         $maxnumber = -1;
237         if (optional_param('addbutton', false, PARAM_BOOL)) {
238             $maxnumber = optional_param('selectadd', '', PARAM_INT); // FIXME: sloppy coding.
239             foreach ($this->datasetdefs as $defid => $datasetdef) {
240                 $datasetdef->itemcount = $maxnumber;
241                 unset($datasetdef->items);
242                 for ($numberadded = 1; $numberadded <= $maxnumber; $numberadded++) {
243                     $datasetitem = new stdClass();
244                     $datasetitem->itemnumber = $numberadded;
245                     $datasetitem->id = 0;
246                     $datasetitem->value = $this->qtypeobj->generate_dataset_item(
247                             $datasetdef->options);
248                     $this->datasetdefs[$defid]->items[$numberadded] = $datasetitem;
249                 }
250             }
251             $this->maxnumber = $maxnumber;
252         } else {
253             // Handle reload dataset items.
254             if (optional_param_array('definition', '', PARAM_NOTAGS) &&
255                     !($datasettoremove ||$newdataset ||$newdatasetvalues)) {
256                 $i = 1;
257                 $fromformdefinition = optional_param_array('definition', '', PARAM_NOTAGS);
258                 $fromformnumber = optional_param_array('number', '', PARAM_LOCALISEDFLOAT);
259                 $fromformitemid = optional_param_array('itemid', '', PARAM_INT);
260                 ksort($fromformdefinition);
262                 foreach ($fromformdefinition as $key => $defid) {
263                     $addeditem = new stdClass();
264                     $addeditem->id = $fromformitemid[$i];
265                     $addeditem->value = $fromformnumber[$i];
266                     $addeditem->itemnumber = ceil($i / count($this->datasetdefs));
267                     $this->datasetdefs[$defid]->items[$addeditem->itemnumber] = $addeditem;
268                     $this->datasetdefs[$defid]->itemcount = $i;
269                     $i++;
270                 }
271             }
272             if (isset($addeditem->itemnumber) && $this->maxnumber < $addeditem->itemnumber) {
273                 $this->maxnumber = $addeditem->itemnumber;
274                 if (!empty($this->datasetdefs)) {
275                     foreach ($this->datasetdefs as $datasetdef) {
276                             $datasetdef->itemcount = $this->maxnumber;
277                     }
278                 }
279             }
280         }
282         parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
283     }
285     /**
286      * Add question-type specific form fields.
287      *
288      * @param MoodleQuickForm $mform the form being built.
289      */
290     protected function definition_inner($mform) {
291         $strquestionlabel = $this->qtypeobj->comment_header($this->nonemptyanswer);
292         $label = get_string("sharedwildcards", "qtype_calculated");
293         $mform->addElement('hidden', 'synchronize', 0);
294         $mform->addElement('hidden', 'initialcategory', 1);
295         $mform->setType('synchronize', PARAM_BOOL);
296         $mform->setType('initialcategory', PARAM_INT);
297         $mform->addElement('hidden', 'reload', 1);
298         $mform->setType('reload', PARAM_INT);
299         $addfieldsname = 'updatequestion value';
300         $addstring = get_string("updatecategory", "qtype_calculated");
301         $mform->registerNoSubmitButton($addfieldsname);
303         $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
304                 question_bank::fraction_options(), 1, 1);
306         $this->add_unit_options($mform, $this);
307         $this->add_unit_fields($mform, $this);
308         $this->add_interactive_settings();
310         $label = "<div class='mdl-align'></div><div class='mdl-align'>" .
311                 get_string('wildcardrole', 'qtype_calculatedsimple') . "</div>";
312         $mform->addElement('html', "<div class='mdl-align'>&nbsp;</div>");
313         // Explaining the role of datasets so other strings can be shortened.
314         $mform->addElement('html', $label);
316         $mform->addElement('submit', 'analyzequestion',
317                 get_string('findwildcards', 'qtype_calculatedsimple'));
318         $mform->registerNoSubmitButton('analyzequestion');
319         $mform->closeHeaderBefore('analyzequestion');
320         $this->wizarddisplay = optional_param('analyzequestion', false, PARAM_BOOL);
321         if ($this->maxnumber != -1) {
322             $this->noofitems = $this->maxnumber;
323         } else {
324             $this->noofitems = 0;
325         }
326         if (!empty($this->datasetdefs)) {// So there are some datadefs.
327             // We put them on the page.
328             $key = 0;
329             $mform->addElement('header', 'additemhdr',
330                     get_string('wildcardparam', 'qtype_calculatedsimple'));
331             $idx = 1;
332             if (!empty($this->datasetdefs)) {// Unnecessary test.
333                 $j = (($this->noofitems) * count($this->datasetdefs))+1;//
334                 foreach ($this->datasetdefs as $defkey => $datasetdef) {
335                     $mform->addElement('static', "na[{$j}]",
336                             get_string('param', 'qtype_calculated', $datasetdef->name));
337                     $this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
338                     $mform->addElement('hidden', "datasetdef[{$idx}]");
339                     $mform->setType("datasetdef[{$idx}]", PARAM_RAW);
340                     $mform->addElement('hidden', "defoptions[{$idx}]");
341                     $mform->setType("defoptions[{$idx}]", PARAM_RAW);
342                     $idx++;
343                     $mform->addElement('static', "divider[{$j}]", '', '<hr />');
344                     $j++;
345                 }
346             }
347             // This should be done before the elements are created and stored as $this->formdata.
348             // Fill out all data sets and also the fields for the next item to add.
349             /*Here we do already the values error analysis so that
350              * we could force all wild cards values display if there is an error in values.
351              * as using a , in a number */
352             $this->numbererrors = array();
353             if (!empty($this->datasetdefs)) {
354                 $j = $this->noofitems * count($this->datasetdefs);
355                 for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--) {
356                     $data = array();
357                     $numbererrors = '';
358                     $comment = new stdClass();
359                     $comment->stranswers = array();
360                     $comment->outsidelimit = false;
361                     $comment->answers = array();
363                     foreach ($this->datasetdefs as $defid => $datasetdef) {
364                         if (isset($datasetdef->items[$itemnumber])) {
365                             $this->formdata["definition[{$j}]"] = $defid;
366                             $this->formdata["itemid[{$j}]"] =
367                                     $datasetdef->items[$itemnumber]->id;
368                             $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
369                             $this->formdata["number[{$j}]"] = $number =
370                                     $datasetdef->items[$itemnumber]->value;
371                             if (! is_numeric($number)) {
372                                 $a = new stdClass();
373                                 $a->name = '{'.$datasetdef->name.'}';
374                                 $a->value = $datasetdef->items[$itemnumber]->value;
375                                 if (stristr($number, ',')) {
376                                     $this->numbererrors["number[{$j}]"] =
377                                             get_string('nocommaallowed', 'qtype_calculated');
378                                     $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
380                                 } else {
381                                     $this->numbererrors["number[{$j}]"] =
382                                             get_string('notvalidnumber', 'qtype_calculated', $a);
383                                     $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
384                                 }
385                             } else if (stristr($number, 'x')) { // Hexa will pass the test.
386                                 $a = new stdClass();
387                                 $a->name = '{'.$datasetdef->name.'}';
388                                 $a->value = $datasetdef->items[$itemnumber]->value;
389                                 $this->numbererrors['number['.$j.']'] =
390                                         get_string('hexanotallowed', 'qtype_calculated', $a);
391                                 $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
392                             } else if (is_nan($number)) {
393                                 $a = new stdClass();
394                                 $a->name = '{'.$datasetdef->name.'}';
395                                 $a->value = $datasetdef->items[$itemnumber]->value;
396                                 $this->numbererrors["number[{$j}]"] =
397                                         get_string('notvalidnumber', 'qtype_calculated', $a);
398                                 $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
399                             }
400                         }
401                         $j--;
402                     }
403                     if ($this->noofitems != 0) {
404                         if (empty($numbererrors)) {
405                             if (!isset($this->question->id)) {
406                                 $this->question->id = 0;
407                             }
408                             $this->question->questiontext = !empty($this->question->questiontext) ?
409                                     $this->question->questiontext : '';
410                             $comment = $this->qtypeobj->comment_on_datasetitems(
411                                     $this->qtypeobj, $this->question->id,
412                                     $this->question->questiontext, $this->nonemptyanswer,
413                                     $data, $itemnumber);
414                             if ($comment->outsidelimit) {
415                                 $this->outsidelimit = $comment->outsidelimit;
416                             }
417                             $totalcomment = '';
419                             foreach ($this->nonemptyanswer as $key => $answer) {
420                                 $totalcomment .= $comment->stranswers[$key].'<br/>';
421                             }
423                             $this->formdata['answercomment['.$itemnumber.']'] = $totalcomment;
424                         }
425                     }
426                 }
427                 $this->formdata['selectdelete'] = '1';
428                 $this->formdata['selectadd'] = '1';
429                 $j = $this->noofitems * count($this->datasetdefs)+1;
430                 $data = array(); // Data for comment_on_datasetitems later.
431                 $idx = 1;
432                 foreach ($this->datasetdefs as $defid => $datasetdef) {
433                     $this->formdata["datasetdef[{$idx}]"] = $defid;
434                     $idx++;
435                 }
436                 $this->formdata = $this->qtypeobj->custom_generator_set_data(
437                         $this->datasetdefs, $this->formdata);
438             }
440             $addoptions = Array();
441             $addoptions['1'] = '1';
442             for ($i = 10; $i <= 100; $i += 10) {
443                 $addoptions["{$i}"] = "{$i}";
444             }
445             $showoptions = Array();
446             $showoptions['1'] = '1';
447             $showoptions['2'] = '2';
448             $showoptions['5'] = '5';
449             for ($i = 10; $i <= 100; $i += 10) {
450                 $showoptions["{$i}"] = "{$i}";
451             }
452             $mform->closeHeaderBefore('additemhdr');
453             $addgrp = array();
454             $addgrp[] = $mform->createElement('submit', 'addbutton',
455                     get_string('generatenewitemsset', 'qtype_calculatedsimple'));
456             $addgrp[] = $mform->createElement('select', "selectadd", '', $addoptions);
457             $addgrp[] = $mform->createElement('static', "stat", '',
458                     get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
459             $mform->addGroup($addgrp, 'addgrp', '', '   ', false);
460             $mform->registerNoSubmitButton('addbutton');
461             $mform->closeHeaderBefore('addgrp');
462             $addgrp1 = array();
463             $addgrp1[] = $mform->createElement('submit', 'showbutton',
464                     get_string('showitems', 'qtype_calculatedsimple'));
465             $addgrp1[] = $mform->createElement('select', "selectshow", '', $showoptions);
466             $addgrp1[] = $mform->createElement('static', "stat", '',
467                     get_string('setwildcardvalues', 'qtype_calculatedsimple'));
468             $mform->addGroup($addgrp1, 'addgrp1', '', '   ', false);
469             $mform->registerNoSubmitButton('showbutton');
470             $mform->closeHeaderBefore('addgrp1');
471             $mform->addElement('static', "divideradd", '', '');
472             if ($this->noofitems == 0) {
473                 $mform->addElement('static', 'warningnoitems', '', '<span class="error">' .
474                         get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple') .
475                         '</span>');
476                 $mform->closeHeaderBefore('warningnoitems');
477             } else {
478                 $mform->addElement('header', 'additemhdr1',
479                         get_string('wildcardvalues', 'qtype_calculatedsimple'));
480                 $mform->closeHeaderBefore('additemhdr1');
481                 if (!empty($this->numbererrors) || $this->outsidelimit) {
482                     $mform->addElement('static', "alert", '', '<span class="error">' .
483                             get_string('useadvance', 'qtype_calculatedsimple').'</span>');
484                 }
486                 $mform->addElement('submit', 'updatedatasets',
487                         get_string('updatewildcardvalues', 'qtype_calculatedsimple'));
488                 $mform->registerNoSubmitButton('updatedatasets');
489                 $mform->setAdvanced("updatedatasets", true);
491                 // ...--------------------------------------------------------------.
492                 $j = $this->noofitems * count($this->datasetdefs);
493                 $k = optional_param('selectshow', 1, PARAM_INT);
495                 for ($i = $this->noofitems; $i >= 1; $i--) {
496                     foreach ($this->datasetdefs as $defkey => $datasetdef) {
497                         if ($k > 0 ||  $this->outsidelimit || !empty($this->numbererrors)) {
498                             $mform->addElement('float', "number[{$j}]", get_string(
499                                     'wildcard', 'qtype_calculatedsimple', $datasetdef->name));
500                             $mform->setAdvanced("number[{$j}]", true);
501                             if (!empty($this->numbererrors['number['.$j.']'])) {
502                                 $mform->addElement('static', "numbercomment[{$j}]", '',
503                                         '<span class="error">' .
504                                         $this->numbererrors['number['.$j.']'] . '</span>');
505                                 $mform->setAdvanced("numbercomment[{$j}]", true);
506                             }
507                         } else {
508                             $mform->addElement('hidden', "number[{$j}]", '');
509                             $mform->setType("number[{$j}]", PARAM_LOCALISEDFLOAT); // Localisation handling has to be done manually.
510                             if (isset($this->formdata["number[{$j}]"])) {
511                                 $this->formdata["number[{$j}]"] = format_float($this->formdata["number[{$j}]"], -1);
512                             }
513                         }
515                         $mform->addElement('hidden', "itemid[{$j}]");
516                         $mform->setType("itemid[{$j}]", PARAM_INT);
518                         $mform->addElement('hidden', "definition[{$j}]");
519                         $mform->setType("definition[{$j}]", PARAM_NOTAGS);
521                         $j--;
522                     }
523                     if (!empty($strquestionlabel) && ($k > 0 ||  $this->outsidelimit ||
524                             !empty($this->numbererrors))) {
525                         $mform->addElement('static', "answercomment[{$i}]", "<b>" .
526                                 get_string('setno', 'qtype_calculatedsimple', $i) .
527                                 "</b>&nbsp;&nbsp;" . $strquestionlabel);
529                     }
530                     if ($k > 0 ||  $this->outsidelimit || !empty($this->numbererrors)) {
531                         $mform->addElement('static', "divider1[{$j}]", '', '<hr />');
533                     }
534                     $k--;
535                 }
536             }
537         } else {
538             $mform->addElement('static', 'warningnowildcards', '', '<span class="error">' .
539                     get_string('atleastonewildcard', 'qtype_calculatedsimple') . '</span>');
540             $mform->closeHeaderBefore('warningnowildcards');
541         }
543         // ...----------------------------------------------------------------------.
544         // Non standard name for button element needed so not using add_action_buttons.
545         // Hidden elements.
547         $mform->addElement('hidden', 'id');
548         $mform->setType('id', PARAM_INT);
550         $mform->addElement('hidden', 'courseid');
551         $mform->setType('courseid', PARAM_INT);
552         $mform->setDefault('courseid', 0);
554         $mform->addElement('hidden', 'cmid');
555         $mform->setType('cmid', PARAM_INT);
556         $mform->setDefault('cmid', 0);
557         if (!empty($this->question->id)) {
558             if ($this->question->formoptions->cansaveasnew) {
559                 $mform->addElement('header', 'additemhdr',
560                         get_string('converttocalculated', 'qtype_calculatedsimple'));
561                 $mform->closeHeaderBefore('additemhdr');
563                 $mform->addElement('checkbox', 'convert', '',
564                         get_string('willconverttocalculated', 'qtype_calculatedsimple'));
565                 $mform->setDefault('convert', 0);
567             }
568         }
569     }
571     protected function can_preview() {
572         return empty($this->question->beingcopied) && !empty($this->question->id) &&
573                 $this->question->formoptions->canedit && $this->noofitems > 0;
574     }
576     public function data_preprocessing($question) {
577         $question = parent::data_preprocessing($question);
578         $question = $this->data_preprocessing_answers($question);
579         $question = $this->data_preprocessing_hints($question);
580         $question = $this->data_preprocessing_units($question);
581         $question = $this->data_preprocessing_unit_options($question);
583         // This is a bit ugly, but it loads all the dataset values.
584         $question = (object)((array)$question + $this->formdata);
586         return $question;
587     }
589     public function qtype() {
590         return 'calculatedsimple';
591     }
593     public function validation($data, $files) {
594         $errors = parent::validation($data, $files);
596         if (array_key_exists('number', $data)) {
597             $numbers = $data['number'];
598         } else {
599             $numbers = array();
600         }
601         foreach ($numbers as $key => $number) {
602             if (! is_numeric($number)) {
603                 if (stristr($number, ',')) {
604                     $errors['number['.$key.']'] = get_string('nocommaallowed', 'qtype_calculated');
605                 } else {
606                     $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
607                 }
608             } else if (stristr($number, 'x')) {
609                 $a = new stdClass();
610                 $a->name = '';
611                 $a->value = $number;
612                 $errors['number['.$key.']'] = get_string('hexanotallowed', 'qtype_calculated', $a);
613             } else if (is_nan($number)) {
614                 $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
615             }
616         }
618         if (empty($data['definition'])) {
619             $errors['selectadd'] = get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple');
620         }
622         return $errors;
623     }