MDL-16094 File storage conversion Quiz and Questions
[moodle.git] / question / type / calculatedsimple / edit_calculatedsimple_form.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Defines the editing form for the calculated simplequestion type.
20  *
21  * @copyright &copy; 2007 Jamie Pratt
22  * @author Jamie Pratt me@jamiep.org
23  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
24  * @package questionbank
25  * @subpackage questiontypes
26  */
27 class question_edit_calculatedsimple_form extends question_edit_form {
28     /**
29      * Handle to the question type for this question.
30      *
31      * @var question_calculatedsimple_qtype
32      */
33     public $qtypeobj;
35     public $wildcarddisplay ;
37     public $questiondisplay ;
39     public $datasetdefs;
41     public $reload = false ;
43     public $maxnumber = -1;
45     public $regenerate = true;
47     public $noofitems;
49     public $outsidelimit = false ;
51     public $commentanswer = array();
53     public $answer = array();
55     public $nonemptyanswer = array();
57     public $numbererrors = array();
59     public $formdata = array();
61     function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
62         global $QTYPES, $SESSION, $CFG, $DB;
63         $this->regenerate = true;
64         $this->question = $question;
66         $this->qtypeobj =& $QTYPES[$this->question->qtype];
67         //get the dataset definitions for this question
68         //coming here everytime even when using a NoSubmitButton
69         //so this will only set the values to the actual question database content which is not what we want
70         //so this should be removed from here
71         // get priority to paramdatasets
73         if ("1" == optional_param('reload','', PARAM_INT )) {
74             $this->reload = true;
75         }else {
76             $this->reload = false;
77         }
78         if (!$this->reload) { // use database data as this is first pass
79             // question->id == 0 so no stored datasets
80             // else get datasets
81             //   echo "<p>question  <pre>";print_r($question);echo "</pre></p>";
82             if (!empty($question->id)) {
84               /*  if (empty($question->options)) {
85                     $this->get_question_options($question);
86                 }*/
87                 $this->datasetdefs = $this->qtypeobj->get_dataset_definitions($question->id, array());
89                 if(!empty($this->datasetdefs)){
90                     foreach ($this->datasetdefs as $defid => $datasetdef) {
91                         // first get the items in case their number does not correspond to itemcount
92                         if (isset($datasetdef->id)) {
93                             $this->datasetdefs[$defid]->items = $this->qtypeobj->get_database_dataset_items($datasetdef->id);
94                             if ( $this->datasetdefs[$defid]->items != '') {
95                                 $datasetdef->itemcount = count($this->datasetdefs[$defid]->items);
96                             } else {
97                                 $datasetdef->itemcount = 0 ;
98                             }
99                         }
100                         // Get maxnumber
101                         if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
102                             $this->maxnumber = $datasetdef->itemcount;
103                         }
104                     }
105                 }
107                 $i = 0 ;
108                 foreach($this->question->options->answers as $answer){
109                      $this->answer[$i] = $answer ;
110                      $i++;
111                 }
112                 $this->nonemptyanswer = $this->answer ;
113             }
114             $datasettoremove = false;
115             $newdatasetvalues = false ;
116             $newdataset = false ;
117         }else {
118             // handle reload to get values from the form-elements
119             // answers, datasetdefs and data_items
120             // In any case the validation step will warn the user of any error in settings the values.
121             // Verification for the specific dataset values as the other parameters
122             // unints, feeedback etc are handled elsewhere
123             // handle request buttons :
124             //    'analyzequestion' (Identify the wild cards {x..} present in answers)
125             //    'addbutton' (create new set of datatitems)
126             //    'updatedatasets' is handled automatically on each reload
127             // The analyzequestion is done every time on reload
128             // to detect any new wild cards so that the current display reflects
129             // the mandatory (i.e. in answers) datasets
130             //  to implement : don't do any changes if the question is used in a quiz.
131             // If new datadef, new properties should erase items.
132             // Most of the data
133             $datasettoremove = false;
134             $newdatasetvalues = false ;
135             $newdataset = false ;
136             $dummyform = new stdClass();
137             $mandatorydatasets = array();
138             // should not test on adding a new answer
139             // should test if there are already olddatasets or if the 'analyzequestion' submit button has been clicked
140             if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){
142                 if  (  $dummyform->answer = optional_param('answer', '', PARAM_NOTAGS)) { // there is always at least one answer...
143                     $fraction = optional_param('fraction', '', PARAM_NUMBER);
144                     $tolerance = optional_param('tolerance', '', PARAM_NUMBER);
145                     $tolerancetype = optional_param('tolerancetype', '', PARAM_NUMBER);
146                     $correctanswerlength = optional_param('correctanswerlength', '', PARAM_INT);
147                     $correctanswerformat = optional_param('correctanswerformat', '', PARAM_INT);
149                     foreach( $dummyform->answer as $key => $answer ) {
150                         if(trim($answer) != ''){  // just look for non-empty
151                             $this->answer[$key]=new stdClass();
152                             $this->answer[$key]->answer = $answer;
153                             $this->answer[$key]->fraction = $fraction[$key];
154                             $this->answer[$key]->tolerance = $tolerance[$key];
155                             $this->answer[$key]->tolerancetype = $tolerancetype[$key];
156                             $this->answer[$key]->correctanswerlength = $correctanswerlength[$key];
157                             $this->answer[$key]->correctanswerformat = $correctanswerformat[$key];
158                             $this->nonemptyanswer[]= $this->answer[$key];
159                             $mandatorydatasets +=$this->qtypeobj->find_dataset_names($answer);
160                         }
161                     }
162                 }
163                 $this->datasetdefs = array();
164                 // rebuild datasetdefs from old values
165                 if ($olddef  = optional_param('datasetdef', '', PARAM_RAW)){
166                     $calcmin = optional_param('calcmin', '', PARAM_NUMBER);
167                     $calclength = optional_param('calclength', '', PARAM_INT);
168                     $calcmax = optional_param('calcmax', '', PARAM_NUMBER);
169                     $oldoptions  = optional_param('defoptions', '', PARAM_RAW);
170                     $newdatasetvalues = false ;
171                     for($key = 1 ; $key <= sizeof($olddef) ; $key++) {
172                         $def = $olddef[$key] ;
173                         $this->datasetdefs[$def]= new stdClass ;
174                         $this->datasetdefs[$def]->type = 1;
175                         $this->datasetdefs[$def]->category = 0;
176                       //  $this->datasets[$key]->name = $datasetname;
177                         $this->datasetdefs[$def]->options = $oldoptions[$key] ;
178                         $this->datasetdefs[$def]->calcmin = $calcmin[$key] ;
179                         $this->datasetdefs[$def]->calcmax = $calcmax[$key] ;
180                         $this->datasetdefs[$def]->calclength = $calclength[$key] ;
181                         //then compare with new values
182                         if (preg_match('~^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$~', $this->datasetdefs[$def]->options, $regs)) {
183                            if( $this->datasetdefs[$def]->calcmin != $regs[2]||
184                             $this->datasetdefs[$def]->calcmax != $regs[3] ||
185                             $this->datasetdefs[$def]->calclength != $regs[4]){
186                                  $newdatasetvalues = true ;
187                             }
188                         }
189                         $this->datasetdefs[$def]->options="uniform:".$this->datasetdefs[$def]->calcmin.":".$this->datasetdefs[$def]->calcmax.":".$this->datasetdefs[$def]->calclength;
190                     }
191                 }// if (olddef...
192                 // detect new datasets
193             $newdataset = false ;
194             foreach ($mandatorydatasets as $datasetname) {
195                 if (!isset($this->datasetdefs["1-0-$datasetname"])) {
196                     $key = "1-0-$datasetname";
197                     $this->datasetdefs[$key]=new stdClass ;//"1-0-$datasetname";
198                     $this->datasetdefs[$key]->type = 1;
199                     $this->datasetdefs[$key]->category = 0;
200                     $this->datasetdefs[$key]->name = $datasetname;
201                     $this->datasetdefs[$key]->options = "uniform:1.0:10.0:1";
202                     $newdataset = true ;
203                 }else {
204                     $this->datasetdefs["1-0-$datasetname"]->name = $datasetname ;
205                 }
206             }
207             // remove obsolete datasets
208             $datasettoremove = false;
209             foreach ($this->datasetdefs as $defkey => $datasetdef){
210                 if(!isset($datasetdef->name )){
211                     $datasettoremove = true;
212                     unset($this->datasetdefs[$defkey]);
213                 }
214             }
215         }
216         } // handle reload
217         // create items if  $newdataset and noofitems > 0 and !$newdatasetvalues
218         // eliminate any items if $newdatasetvalues
219         // eliminate any items if $datasettoremove, $newdataset, $newdatasetvalues
220         if ($datasettoremove ||$newdataset ||$newdatasetvalues ) {
221             foreach ($this->datasetdefs as $defkey => $datasetdef){
222                 $datasetdef->itemcount = 0;
223                 unset($datasetdef->items);
224             }
225         }
226         $maxnumber = -1 ;
227         if  (  "" !=optional_param('addbutton')){
228             $maxnumber = optional_param('selectadd', '', PARAM_INT); //FIXME: sloppy coding
229             foreach ($this->datasetdefs as $defid => $datasetdef) {
230                 $datasetdef->itemcount = $maxnumber;
231                 unset($datasetdef->items);
232                 for ($numberadded =1 ; $numberadded <= $maxnumber; $numberadded++){
233                     $datasetitem = new stdClass;
234                     $datasetitem->itemnumber = $numberadded;
235                     $datasetitem->id = 0;
236                     $datasetitem->value = $this->qtypeobj->generate_dataset_item($datasetdef->options);
237                     $this->datasetdefs[$defid]->items[$numberadded]=$datasetitem ;
238                 }//for number added
239             }// datasetsdefs end
240             $this->maxnumber = $maxnumber ;
241         }else {
242             // Handle reload dataset items
243             if  (  "" !=optional_param('definition')&& !($datasettoremove ||$newdataset ||$newdatasetvalues )){
244                 $i = 1;
245                 $fromformdefinition = optional_param('definition', '', PARAM_NOTAGS);
246                 $fromformnumber = optional_param('number', '', PARAM_INT);
247                 $fromformitemid = optional_param('itemid', '', PARAM_INT);
248                 ksort($fromformdefinition);
250                 foreach($fromformdefinition as $key => $defid) {
251                     $addeditem = new stdClass();
252                     $addeditem->id = $fromformitemid[$i]  ;
253                     $addeditem->value = $fromformnumber[$i];
254                     $addeditem->itemnumber = ceil($i / count($this->datasetdefs));
255                     $this->datasetdefs[$defid]->items[$addeditem->itemnumber]=$addeditem ;
256                     $this->datasetdefs[$defid]->itemcount = $i ;
257                     $i++;
258                 }
259             }
260             if (isset($addeditem->itemnumber) && $this->maxnumber < $addeditem->itemnumber){
261                 $this->maxnumber = $addeditem->itemnumber;
262                 if(!empty($this->datasetdefs)){
263                     foreach ($this->datasetdefs as $datasetdef) {
264                             $datasetdef->itemcount = $this->maxnumber ;
265                     }
266                 }
267             }
268         }
270         parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
271     }
273     function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
274         $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
275         $mform->setType('answer', PARAM_NOTAGS);
276         $addrepeated = array();
277         $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
278         $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
279         $repeatedoptions['tolerance']['default'] = 0.01;
280         $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
281         $addrepeated[] =&  $mform->createElement('select', 'correctanswerlength', get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
282         $repeatedoptions['correctanswerlength']['default'] = 2;
284         $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
285         $addrepeated[] =&  $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
286         array_splice($repeated, 3, 0, $addrepeated);
287         $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
289         return $repeated;
290     }
292     /**
293      * Add question-type specific form fields.
294      *
295      * @param MoodleQuickForm $mform the form being built.
296      */
297     function definition_inner(&$mform) {
298         global $QTYPES;
299         $this->qtypeobj =& $QTYPES[$this->qtype()];
300         $strquestionlabel = $this->qtypeobj->comment_header($this->nonemptyanswer);
301         $label = get_string("sharedwildcards", "qtype_calculated");
302         $mform->addElement('hidden', 'synchronize', 0);
303         $mform->addElement('hidden', 'initialcategory', 1);
304         $mform->setType('initialcategory', PARAM_INT);
305         $mform->addElement('hidden', 'reload', 1);
306         $mform->setType('reload', PARAM_INT);
307         $addfieldsname='updatequestion value';
308         $addstring=get_string("updatecategory", "qtype_calculated");
309         $mform->registerNoSubmitButton($addfieldsname);
310         // put a submit button to stop supplementary answers on update answers parameters
311         // $mform->insertElementBefore($mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
313         $creategrades = get_grade_options();
314         $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
315             $creategrades->gradeoptions, 1, 1);
317         $QTYPES['numerical']->add_units_options($mform,$this);
319         $QTYPES['numerical']->add_units_elements($mform,$this);
320         $label = "<div class='mdl-align'></div><div class='mdl-align'>".get_string('wildcardrole', 'qtype_calculatedsimple')."</div>";
321         $mform->addElement('html', "<div class='mdl-align'>&nbsp;</div>");
322         $mform->addElement('html', $label);// explaining the role of datasets so other strings can be shortened
324         $mform->addElement('submit', 'analyzequestion', get_string('findwildcards','qtype_calculatedsimple'));
325         $mform->registerNoSubmitButton('analyzequestion');
326         $mform->closeHeaderBefore('analyzequestion');
327         if  (  "" != optional_param('analyzequestion','', PARAM_RAW)) {
329             $this->wizarddisplay = true;
331         }else {
332             $this->wizwarddisplay = false;
333         }
334         if ($this->maxnumber != -1){
335             $this->noofitems = $this->maxnumber;
336         } else {
337             $this->noofitems = 0;
338         }
339         if(!empty($this->datasetdefs)){//So there are some datadefs
340             // we put them on the page
341             $key = 0;
342             $mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple'));
343             $idx = 1;
344             if(!empty($this->datasetdefs)){// unnecessary test
345                 $j = (($this->noofitems) * count($this->datasetdefs))+1;//
346                 foreach ($this->datasetdefs as $defkey => $datasetdef){
347                     $mform->addElement('static', "na[$j]", get_string('param', 'qtype_calculated', $datasetdef->name));
348                     $this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
349                     $mform->addElement('hidden', "datasetdef[$idx]");
350                     $mform->setType("datasetdef[$idx]", PARAM_RAW);
351                     $mform->addElement('hidden', "defoptions[$idx]");
352                     $mform->setType("defoptions[$idx]", PARAM_RAW);
353                     $idx++;
354                     $mform->addElement('static', "divider[$j]", '', '<hr />');
355                     $j++;
356                 }
357             }
358             //this should be done before the elements are created and stored as $this->formdata ;
359             //fill out all data sets and also the fields for the next item to add.
360             /*Here we do already the values error analysis so that
361              * we could force all wild cards values display if there is an error in values.
362              * as using a , in a number */
363             $this->numbererrors = array();
364             if(!empty($this->datasetdefs)){
365                 $j = $this->noofitems * count($this->datasetdefs);
366                 for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
367                     $data = array();
368                     $numbererrors = array() ;
369                     $comment = new stdClass;
370                     $comment->stranswers = array();
371                     $comment->outsidelimit = false ;
372                     $comment->answers = array();
374                     foreach ($this->datasetdefs as $defid => $datasetdef){
375                         if (isset($datasetdef->items[$itemnumber])){
376                             $this->formdata["definition[$j]"] = $defid;
377                             $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
378                             $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
379                             $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
380                             if(! is_numeric($number)){
381                                 $a = new stdClass;
382                                 $a->name = '{'.$datasetdef->name.'}' ;
383                                 $a->value = $datasetdef->items[$itemnumber]->value ;
384                                 if (stristr($number,',')){
385                                     $this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated');
386                                     $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
388                                 }else {
389                                     $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
390                                     $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
391                                     //$comment->outsidelimit = false ;
392                                 }
393                             }else if( stristr($number,'x')){ // hexa will pass the test
394                                 $a = new stdClass;
395                                 $a->name = '{'.$datasetdef->name.'}' ;
396                                 $a->value = $datasetdef->items[$itemnumber]->value ;
397                                 $this->numbererrors['number['.$j.']']= get_string('hexanotallowed','qtype_calculated',$a);
398                                 $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
399                             } else if( is_nan($number)){
400                                 $a = new stdClass;
401                                 $a->name = '{'.$datasetdef->name.'}' ;
402                                 $a->value = $datasetdef->items[$itemnumber]->value ;
403                                 $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
404                                 $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
405                                 //   $val = 1.0 ;
406                             }
407                         }
408                         $j--;
409                     }
410                     if($this->noofitems != 0 ) {
411                         if (empty($numbererrors)) {
412                             if (!isset($this->question->id)) {
413                                 $this->question->id = 0 ;
414                             }
415                             $this->question->questiontext = !empty($this->question->questiontext)?$this->question->questiontext:'';
416                             $comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $this->question->id, $this->question->questiontext, $this->nonemptyanswer, $data, $itemnumber);
417                             if ($comment->outsidelimit) {
418                                 $this->outsidelimit=$comment->outsidelimit ;
419                             }
420                             $totalcomment='';
422                             foreach ($this->nonemptyanswer as $key => $answer) {
423                                 $totalcomment .= $comment->stranswers[$key].'<br/>';
424                             }
426                             $this->formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
427                         }
428                     }
429                 }
430                 $this->formdata['selectdelete'] = '1';
431                 $this->formdata['selectadd'] = '1';
432                 $j = $this->noofitems * count($this->datasetdefs)+1;
433                 $data = array(); // data for comment_on_datasetitems later
434                 $idx =1 ;
435                 foreach ($this->datasetdefs as $defid => $datasetdef){
436                     $this->formdata["datasetdef[$idx]"] = $defid;
437                     $idx++;
438                 }
439                 $this->formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $this->formdata);
440             }
443             $addoptions = Array();
444             $addoptions['1']='1';
445             for ($i=10; $i<=100 ; $i+=10){
446                 $addoptions["$i"]="$i";
447             }
448             $showoptions = Array();
449             $showoptions['1']='1';
450             $showoptions['2']='2';
451             $showoptions['5']='5';
452             for ($i=10; $i<=100 ; $i+=10){
453                 $showoptions["$i"]="$i";
454             }
455             $mform->closeHeaderBefore('additemhdr');
456             $addgrp = array();
457             $addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('generatenewitemsset', 'qtype_calculatedsimple'));
458             $addgrp[] =& $mform->createElement('select', "selectadd", '', $addoptions);
459             $addgrp[] = & $mform->createElement('static',"stat",'',get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
460             $mform->addGroup($addgrp, 'addgrp', '', '   ', false);
461             $mform->registerNoSubmitButton('addbutton');
462             $mform->closeHeaderBefore('addgrp');
463             $addgrp1 = array();
464             $addgrp1[] =& $mform->createElement('submit', 'showbutton', get_string('showitems', 'qtype_calculatedsimple'));
465             $addgrp1[] =& $mform->createElement('select', "selectshow",'' , $showoptions);
466             $addgrp1[] = & $mform->createElement('static',"stat",'',get_string('setwildcardvalues', 'qtype_calculatedsimple'));
467             $mform->addGroup($addgrp1, 'addgrp1', '', '   ', false);
468             $mform->registerNoSubmitButton('showbutton');
469             $mform->closeHeaderBefore('addgrp1');
470             $mform->addElement('static', "divideradd", '', '');
471             if ($this->noofitems == 0) {
472                 $mform->addElement('static','warningnoitems','','<span class="error">'.get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple').'</span>');
473                 $mform->closeHeaderBefore('warningnoitems');
474             }else {
475                 $mform->addElement('header', 'additemhdr1', get_string('wildcardvalues', 'qtype_calculatedsimple'));
476                 $mform->closeHeaderBefore('additemhdr1');
477                 //   $mform->addElement('header', '', get_string('itemno', 'qtype_calculated', ""));
478                 if( !empty($this->numbererrors) || $this->outsidelimit) {
479                     $mform->addElement('static', "alert", '', '<span class="error">'.get_string('useadvance', 'qtype_calculatedsimple').'</span>');
480                 }
482                 $mform->addElement('submit', 'updatedatasets', get_string('updatewildcardvalues', 'qtype_calculatedsimple'));
483                 $mform->registerNoSubmitButton('updatedatasets');
484                 $mform->setAdvanced("updatedatasets",true);
486                 //------------------------------------------------------------------------------------------------------------------------------
487                 $j = $this->noofitems * count($this->datasetdefs);
488                 $k = 1 ;
489                 if ("" != optional_param('selectshow')){
490                     $k = optional_param('selectshow', '', PARAM_INT);
491                 }
493                 for ($i = $this->noofitems; $i >= 1 ; $i--){
494                     foreach ($this->datasetdefs as $defkey => $datasetdef){
495                         if($k > 0 ||  $this->outsidelimit || !empty($this->numbererrors ) ){
496                             $mform->addElement('text',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
497                             $mform->setAdvanced("number[$j]",true);
498                             if(!empty($this->numbererrors['number['.$j.']']) ){
499                                 $mform->addElement('static', "numbercomment[$j]",'','<span class="error">'.$this->numbererrors['number['.$j.']'].'</span>');
500                                 $mform->setAdvanced("numbercomment[$j]",true);
501                             }
502                         }else {
503                             $mform->addElement('hidden',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
504                         }
505                         $mform->setType("number[$j]", PARAM_NUMBER);
507                         $mform->addElement('hidden', "itemid[$j]");
508                         $mform->setType("itemid[$j]", PARAM_INT);
510                         $mform->addElement('hidden', "definition[$j]");
511                         $mform->setType("definition[$j]", PARAM_NOTAGS);
513                         $j--;
514                     }
515                     if (!empty( $strquestionlabel) && ($k > 0 ||  $this->outsidelimit || !empty($this->numbererrors ) ) ){
516                         //   $repeated[] =& $mform->addElement('static', "answercomment[$i]", $strquestionlabel);
517                         $mform->addElement('static', "answercomment[$i]", "<b>".get_string('setno', 'qtype_calculatedsimple', $i)."</b>&nbsp;&nbsp;".$strquestionlabel);
519                     }
520                     if($k > 0 ||  $this->outsidelimit || !empty($this->numbererrors )){
521                         $mform->addElement('static', "divider1[$j]", '', '<hr />');
523                     }
524                     $k-- ;
525                 }
526             }
527             //  if ($this->outsidelimit){
528             //   $mform->addElement('static','outsidelimit','','');
529             //  }
530         }else {
531             $mform->addElement('static','warningnowildcards','','<span class="error">'.get_string('atleastonewildcard', 'qtype_calculatedsimple').'</span>');
532             $mform->closeHeaderBefore('warningnowildcards');
533         }
536         //------------------------------------------------------------------------------------------------------------------------------
537         //non standard name for button element needed so not using add_action_buttons
538         //hidden elements
540         $mform->addElement('hidden', 'id');
541         $mform->setType('id', PARAM_INT);
543         $mform->addElement('hidden', 'courseid');
544         $mform->setType('courseid', PARAM_INT);
545         $mform->setDefault('courseid', 0);
547         $mform->addElement('hidden', 'cmid');
548         $mform->setType('cmid', PARAM_INT);
549         $mform->setDefault('cmid', 0);
550         if (!empty($this->question->id)){
551             if ($this->question->formoptions->cansaveasnew){
552                 $mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple'));
553                 $mform->closeHeaderBefore('additemhdr');
555                 $mform->addElement('checkbox', 'convert','' ,get_string('willconverttocalculated', 'qtype_calculatedsimple'));
556                 $mform->setDefault('convert', 0);
558             }
559         }
560     }
562     function data_preprocessing($question) {
563         global $QTYPES;
564         $answer = $this->answer;
565         $default_values = array();
566         if (count($answer)) {
567             $key = 0;
568             foreach ($answer as $answer){
569                 $default_values['answer['.$key.']'] = $answer->answer; //  is necessary ? to-do test it
570                 $default_values['fraction['.$key.']'] = $answer->fraction;
571                 $default_values['tolerance['.$key.']'] = $answer->tolerance;
572                 $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
573                 $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
574                 $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
576                 // prepare draft files
577                 $draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
578                 $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
579                     $draftid,       // draftid
580                     $this->context->id,    // context
581                     'question', // component
582                     'answerfeedback',             // filarea
583                     !empty($answer->id)?(int)$answer->id:null, // itemid
584                     $this->fileoptions,    // options
585                     !empty($answer->feedback)?$answer->feedback:''      // text
586                 );
587                 $default_values['feedback['.$key.']']['format'] = !empty($answer->feedbackformat)?$answer->feedbackformat:editors_get_preferred_format();
588                 $default_values['feedback['.$key.']']['itemid'] = $draftid;
590                 $key++;
591             }
592         }
593         $default_values['synchronize'] = 0 ;
594         $QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
595         /*  if (isset($question->options)){
596                 $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
597                 $default_values['unitpenalty'] = $question->options->unitpenalty ;
598                 switch ($question->options->showunits){
599                     case 'O' :
600                     case '1' :
601                         $default_values['showunits0'] = $question->options->showunits ;
602                         $default_values['unitrole'] = 0 ;
603                         break;
604                     case '2' :
605                     case '3' :
606                         $default_values['showunits1'] = $question->options->showunits ;
607                         $default_values['unitrole'] = 1 ;
608                         break;
609                 }
610                 $default_values['unitsleft'] = $question->options->unitsleft ;
611                 $default_values['instructions'] = $question->options->instructions  ;
613                 if (isset($question->options->units)){
614                     $units  = array_values($question->options->units);
615                     if (!empty($units)) {
616                         foreach ($units as $key => $unit){
617                             $default_values['unit['.$key.']'] = $unit->unit;
618                             $default_values['multiplier['.$key.']'] = $unit->multiplier;
619                         }
620                     }
621                 }
622             }
623            */
624         $key = 0 ;
626         $formdata = array();
627         $fromform = new stdClass();
628         //this should be done before the elements are created and stored as $this->formdata ;
629         //fill out all data sets and also the fields for the next item to add.
630         /* if(!empty($this->datasetdefs)){
631         $j = $this->noofitems * count($this->datasetdefs);
632         for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
633             $data = array();
634             foreach ($this->datasetdefs as $defid => $datasetdef){
635                 if (isset($datasetdef->items[$itemnumber])){
636                     $formdata["number[$j]"] = $datasetdef->items[$itemnumber]->value;
637                     $formdata["definition[$j]"] = $defid;
638                     $formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
639                     $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
640                 }
641                 $j--;
642             }
643             // echo "<p>answers avant  comment <pre>";print_r($answer);echo"</pre></p>";
644             // echo "<p>data avant  comment <pre>";print_r($data);echo"</pre></p>";
646             if($this->noofitems != 0 ) {
647                 if(!isset($question->id)) $question->id = 0 ;
648                 $comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this->
649                  if ($comment->outsidelimit) {
650                      $this->outsidelimit=$comment->outsidelimit ;
651                 }
652                 $totalcomment='';
653                 // echo "<p> comment <pre>";print_r($comment);echo"</pre></p>";
655                 foreach ($this->nonemptyanswer as $key => $answer) {
656                     $totalcomment .= $comment->stranswers[$key].'<br/>';
657                 }
659                 $formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
660             }
661         }
662         // $formdata['reload'] = '1';
663         // $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
664         $formdata['selectdelete'] = '1';
665         $formdata['selectadd'] = '1';
666         $j = $this->noofitems * count($this->datasetdefs)+1;
667         $data = array(); // data for comment_on_datasetitems later
668            $idx =1 ;
669             foreach ($this->datasetdefs as $defid => $datasetdef){
670                $formdata["datasetdef[$idx]"] = $defid;
671                 $idx++;
672             }
673         $formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
674         }*/
675         $question = (object)((array)$question + $default_values+$this->formdata );
677         return $question;
678     }
680     function qtype() {
681         return 'calculatedsimple';
682     }
684     function validation($data, $files) {
685         global $QTYPES;
686         $errors = parent::validation($data, $files);
687         //verifying for errors in {=...} in question text;
688         $qtext = "";
689         $qtextremaining = $data['questiontext']['text'];
690         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
691         foreach ($possibledatasets as $name => $value) {
692             $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
693         }
694         while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
695             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
696             $qtext =$qtext.$qtextsplits[0];
697             $qtextremaining = $qtextsplits[1];
698             if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
699                 if(!isset($errors['questiontext'])){
700                     $errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
701                 }else {
702                     $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
703                 }
704             }
705         }
706         $answers = $data['answer'];
707         $answercount = 0;
708         $maxgrade = false;
709         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
710         $mandatorydatasets = array();
711         foreach ($answers as $key => $answer){
712             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
713         }
714         if ( count($mandatorydatasets )==0){
715              foreach ($answers as $key => $answer){
716                 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_calculated');
717             }
718         }
719         foreach ($answers as $key => $answer){
720             //check no of choices
721             // the * for everykind of answer not actually implemented
722             $trimmedanswer = trim($answer);
723             if (($trimmedanswer!='')||$answercount==0){
724                 $eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
725                 if (FALSE !== $eqerror){
726                     $errors['answer['.$key.']'] = $eqerror;
727                 }
728             }
729             if ($trimmedanswer!=''){
730                 if ('2' == $data['correctanswerformat'][$key]
731                         && '0' == $data['correctanswerlength'][$key]) {
732                     $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
733                 }
734                 if (!is_numeric($data['tolerance'][$key])){
735                     $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
736                 }
737                 if ($data['fraction'][$key] == 1) {
738                    $maxgrade = true;
739                 }
741                 $answercount++;
742             }
743             //check grades
745             //TODO how should grade checking work here??
746             /*if ($answer != '') {
747                 if ($data['fraction'][$key] > 0) {
748                     $totalfraction += $data['fraction'][$key];
749                 }
750                 if ($data['fraction'][$key] > $maxfraction) {
751                     $maxfraction = $data['fraction'][$key];
752                 }
753             }*/
754         }
755         //grade checking :
756         /// Perform sanity checks on fractional grades
757         /*if ( ) {
758             if ($maxfraction != 1) {
759                 $maxfraction = $maxfraction * 100;
760                 $errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', $maxfraction);
761             }
762         } else {
763             $totalfraction = round($totalfraction,2);
764             if ($totalfraction != 1) {
765                 $totalfraction = $totalfraction * 100;
766                 $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
767             }
768         }*/
769             $QTYPES['numerical']->validate_numerical_options($data, $errors) ;
770         $units  = $data['unit'];
771         if (count($units)) {
772             foreach ($units as $key => $unit){
773                 if (is_numeric($unit)){
774                     $errors['unit['.$key.']'] = get_string('mustnotbenumeric', 'qtype_calculated');
775                 }
776                 $trimmedunit = trim($unit);
777                 $trimmedmultiplier = trim($data['multiplier'][$key]);
778                 if (!empty($trimmedunit)){
779                     if (empty($trimmedmultiplier)){
780                         $errors['multiplier['.$key.']'] = get_string('youmustenteramultiplierhere', 'qtype_calculated');
781                     }
782                     if (!is_numeric($trimmedmultiplier)){
783                         $errors['multiplier['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
784                     }
786                 }
787             }
788         }
789         if ($answercount==0){
790             $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
791         }
792         if ($maxgrade == false) {
793             $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
794         }
795         if (isset($data['backtoquiz']) && ($this->noofitems==0) ){
796             $errors['warning'] = get_string('warning', 'mnet');
797         }
798         if ($this->outsidelimit){
799          //   if(!isset($errors['warning'])) $errors['warning']=' ';
800            $errors['outsidelimits'] = get_string('oneanswertrueansweroutsidelimits','qtype_calculated');
801         }
802                 /*Here we use the already done the error analysis so that
803         * we could force all wild cards values display if there is an error in values.
804         * as using a , in a number *//*
805         $numbers = $data['number'];
806         foreach ($numbers as $key => $number){
807             if(! is_numeric($number)){
808                 if (stristr($number,',')){
809                     $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
810                 }else {
811                     $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
812                 }
813             }else if( stristr($number,'x')){
814                 $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
815             } else if( is_nan($number)){
816                 $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
817             }
818         }
819         */
820         if ( $this->noofitems==0  ){
821             $errors['warning'] = get_string('warning', 'mnet');
822         }
824         return $errors;
825     }