MDL-20601 fixed fatal SQL upgrade error, missing {} around table names
[moodle.git] / question / type / multianswer / edit_multianswer_form.php
CommitLineData
4d41f4ee 1<?php // $Id$
271ffe3f 2/**
3 * Defines the editing form for the multianswer question type.
4 *
5 * @copyright &copy; 2007 Jamie Pratt
6 * @author Jamie Pratt me@jamiep.org
7 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
830e47a8 8 * @package questionbank
9 * @subpackage questiontypes
271ffe3f 10 */
11
12/**
13 * multianswer editing form definition.
14 */
15class question_edit_multianswer_form extends question_edit_form {
16
705b5874 17 // $questiondisplay will contain the qtype_multianswer_extract_question from the questiontext
f34488b2 18 var $questiondisplay ;
d3e3bd6c 19 // $savedquestiondisplay will contain the qtype_multianswer_extract_question from the questiontext in database
20 var $savedquestion ;
21 var $savedquestiondisplay ;
22 var $used_in_quiz = false ;
23 var $qtype_change = false ;
24 var $negative_diff = false ;
25 var $nb_of_quiz = 0;
26 var $nb_of_attempts = 0;
27 public $confirm = 0 ;
28 public $reload = false ;
29
30 function question_edit_multianswer_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
31 global $QTYPES, $SESSION, $CFG, $DB;
32 $this->regenerate = true;
33 if ( "1" == optional_param('reload','', PARAM_INT )) {
34 $this->reload = true ;
35 }else {
36 $this->reload = false ;
37 }
38 // $this->question = $question;
39 //$used_in_quiz =false;
40 if(! empty($question->id)){
41 $this->savedquestiondisplay =fullclone($question ) ;
42 if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $question->id))){
43 foreach($list as $key => $li){
44 $this->nb_of_quiz ++;
45 if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){
46 $this->nb_of_attempts+= count($att);
47 $this->used_in_quiz = true;
48 }
49 }
50 }
51 }
52
53
54
55
56
57 parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
58 }
59
60
705b5874 61
62 function definition_inner(&$mform) {
d3e3bd6c 63 $mform->addElement('hidden', 'reload', 1);
64 $mform->setType('reload', PARAM_INT);
e2ae84a2 65 $question_type_names = question_type_menu();
120e5cbf 66 $mform->addRule('questiontext', null, 'required', null, 'client');
705b5874 67
fe648028 68 // Remove meaningless defaultgrade field.
69 $mform->removeElement('defaultgrade');
d3e3bd6c 70 $this->confirm = optional_param('confirm','0', PARAM_RAW);
f34488b2 71
705b5874 72 // display the questions from questiontext;
73 if ( "" != optional_param('questiontext','', PARAM_RAW)) {
f34488b2 74
705b5874 75 $this->questiondisplay = fullclone(qtype_multianswer_extract_question(optional_param('questiontext','', PARAM_RAW))) ;
f34488b2 76
705b5874 77 }else {
d3e3bd6c 78 if(!$this->reload && !empty($this->savedquestiondisplay->id)){
79 // use database data as this is first pass
80 // question->id == 0 so no stored datasets
81 $this->questiondisplay = fullclone($this->savedquestiondisplay);
82 foreach($this->questiondisplay->options->questions as $subquestion){
83 if (!empty($subquestion)){
84 $subquestion->answer = array('');
85 foreach($subquestion->options->answers as $ans){
86 $subquestion->answer[]=$ans->answer ;
87 }
88 // $subquestion->answer = fullclone($subquestion->options->answers);
89 }
90 }
91 }else {
92 $this->questiondisplay = "";
93 }
f34488b2 94 }
705b5874 95
d3e3bd6c 96 if ( isset($this->savedquestiondisplay->options->questions) && is_array($this->savedquestiondisplay->options->questions) ) {
97 $countsavedsubquestions =0;
98 foreach($this->savedquestiondisplay->options->questions as $subquestion){
103a800d 99 if (!empty($subquestion)){
d3e3bd6c 100 $countsavedsubquestions++;
df79079f 101 }
f34488b2 102 }
705b5874 103 } else {
d3e3bd6c 104 $countsavedsubquestions =0;
705b5874 105 }
d3e3bd6c 106 if ($this->reload){
107 if ( isset($this->questiondisplay->options->questions) && is_array($this->questiondisplay->options->questions) ) {
108 $countsubquestions =0;
109 foreach($this->questiondisplay->options->questions as $subquestion){
110 if (!empty($subquestion)){
111 $countsubquestions++;
112 }
113 }
114 } else {
115 $countsubquestions =0;
116 }
117 }else{
118 $countsubquestions =$countsavedsubquestions ;
705b5874 119 }
d3e3bd6c 120 // echo "<p> saved question $countsavedsubquestions <pre>";print_r($this->savedquestiondisplay);echo "</pre></p>";
121 // echo "<p> saved question $countsubquestions <pre>";print_r($this->questiondisplay);echo "</pre></p>";
122
123
124 $mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer'));
125 $mform->registerNoSubmitButton('analyzequestion');
126 echo '<div class="ablock clearfix">';
127 echo '<div class=" clearfix">';
128 if ( $this->reload ){
129 for ($sub =1;$sub <=$countsubquestions ;$sub++) {
130
131 $this->editas[$sub] = 'unknown type';
132 if (isset( $this->questiondisplay->options->questions[$sub]->qtype) ) {
133 $this->editas[$sub] = $this->questiondisplay->options->questions[$sub]->qtype ;
134 } else if (optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW) != '') {
135 $this->editas[$sub] = optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW);
fd97082c 136 }
d3e3bd6c 137 $storemess = '';
138 if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
139 $this->savedquestiondisplay->options->questions[$sub]->qtype != $this->questiondisplay->options->questions[$sub]->qtype ){
140 $this->type_change = true ;
141 $storemess = "<font class=\"error\"> STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype]."</font >";
f34488b2 142 }
d3e3bd6c 143
144 $mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'quiz',
145 '{#'.$sub.'}').'&nbsp;'.$question_type_names[$this->questiondisplay->options->questions[$sub]->qtype].$storemess);
146
147 $mform->addElement('static', 'sub_'.$sub."_".'questiontext', get_string('questiondefinition','qtype_multianswer'),array('cols'=>60, 'rows'=>3));
148
149 if (isset ( $this->questiondisplay->options->questions[$sub]->questiontext)) {
150 $mform->setDefault('sub_'.$sub."_".'questiontext', $this->questiondisplay->options->questions[$sub]->questiontext);
f34488b2 151 }
d3e3bd6c 152
153 $mform->addElement('static', 'sub_'.$sub."_".'defaultgrade', get_string('defaultgrade', 'quiz'));
154 $mform->setDefault('sub_'.$sub."_".'defaultgrade',$this->questiondisplay->options->questions[$sub]->defaultgrade);
155
156 if ($this->questiondisplay->options->questions[$sub]->qtype =='shortanswer' ) {
157 $mform->addElement('static', 'sub_'.$sub."_".'usecase', get_string('casesensitive', 'quiz'));
158 }
159
160 if ($this->questiondisplay->options->questions[$sub]->qtype =='multichoice' ) {
161 $mform->addElement('static', 'sub_'.$sub."_".'layout', get_string('layout', 'qtype_multianswer'),array('cols'=>60, 'rows'=>1)) ;//, $gradeoptions);
162 }
163 foreach ($this->questiondisplay->options->questions[$sub]->answer as $key =>$ans) {
164
165 $mform->addElement('static', 'sub_'.$sub."_".'answer['.$key.']', get_string('answer', 'quiz'), array('cols'=>60, 'rows'=>1));
166
167 if ($this->questiondisplay->options->questions[$sub]->qtype =='numerical' && $key == 0 ) {
168 $mform->addElement('static', 'sub_'.$sub."_".'tolerance['.$key.']', get_string('acceptederror', 'quiz')) ;//, $gradeoptions);
169 }
170
171 $mform->addElement('static', 'sub_'.$sub."_".'fraction['.$key.']', get_string('grade')) ;//, $gradeoptions);
172
173 $mform->addElement('static', 'sub_'.$sub."_".'feedback['.$key.']', get_string('feedback', 'quiz'));
174 }
175
f34488b2 176 }
d3e3bd6c 177 echo '</div>';
90a92bd0
PP
178 $this->negative_diff =$countsavedsubquestions - $countsubquestions ;
179 if ( ($this->negative_diff > 0 ) ||$this->type_change || ($this->used_in_quiz && $this->negative_diff != 0)){
d3e3bd6c 180 $mform->addElement('header', 'additemhdr', "WARNING");
181 }
90a92bd0 182 if($this->negative_diff > 0) {
d3e3bd6c 183 //$this->used_in_quiz
90a92bd0 184
d3e3bd6c 185 $mform->addElement('static', 'alert1', "<strong>"."Question deleted"."</strong>","<strong>".$this->negative_diff.get_string(' questions less than in the multtianswer question stored in the database','qtype_multianswer')."</strong>");//$countsubquestions."-".$countsavedsubquestions
186 }
187 if($this->type_change )
188 {
189 //$this->used_in_quiz
190
191 // $mform->addElement('header', 'additemhdr', "WARNING");
192 $mform->addElement('static', 'alert1', "<strong>"."Question type change "."</strong>","<strong>".get_string(' at least one question type has been changed. Did you add,delete or move a question ? Look ahead ','qtype_multianswer')."</strong>");//$countsubquestions."-".$countsavedsubquestions
193 }
194 echo '</div>';
195 }
196 if( $this->used_in_quiz){
90a92bd0 197 if($this->negative_diff < 0) {
d3e3bd6c 198 $diff = $countsubquestions - $countsavedsubquestions;
199 $mform->addElement('static', 'alert1', "<strong>"."Question added "."</strong>","<strong>".$diff.get_string(' questions more than in the multtianswer question stored in the database','qtype_multianswer')."</strong>");//$countsubquestions."-".$countsavedsubquestions
200 }
201 $mform->addElement('header', 'additemhdr2', "This question is used in $this->nb_of_quiz quiz(s), total attempt(s) : $this->nb_of_attempts ");
202 $mform->addElement('static', 'alertas', "<strong>"."YOU SHOULD NOT "."</strong>");//$countsubquestions."-".$countsavedsubquestions
203 }
90a92bd0 204 if ( ($this->negative_diff > 0 || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->type_change ) ) && $this->reload ){
d3e3bd6c 205 $mform->addElement('header', 'additemhdr', get_string('The question will be saved as edited', 'qtype_calculatedsimple'));
206 $mform->addElement('checkbox', 'confirm','' ,get_string('I confirm that I want the question be saved as edited', 'qtype_calculatedsimple'));
207 $mform->setDefault('confirm', 0);
208 }else {
209 $mform->addElement('hidden', 'confirm',0);
705b5874 210 }
211
120e5cbf 212 }
705b5874 213
f34488b2 214
32db0d42 215 function set_data($question) {
f34488b2 216 global $DB;
705b5874 217 $default_values =array();
120e5cbf 218 if (isset($question->id) and $question->id and $question->qtype and $question->questiontext) {
c6fc9988 219
220 foreach ($question->options->questions as $key => $wrapped) {
103a800d 221 if(!empty($wrapped)){
c6fc9988 222 // The old way of restoring the definitions is kept to gradually
223 // update all multianswer questions
224 if (empty($wrapped->questiontext)) {
225 $parsableanswerdef = '{' . $wrapped->defaultgrade . ':';
226 switch ($wrapped->qtype) {
227 case 'multichoice':
228 $parsableanswerdef .= 'MULTICHOICE:';
229 break;
230 case 'shortanswer':
231 $parsableanswerdef .= 'SHORTANSWER:';
232 break;
233 case 'numerical':
234 $parsableanswerdef .= 'NUMERICAL:';
235 break;
236 default:
2471ef86 237 print_error('unknownquestiontype', 'question', '', $wrapped->qtype);
c6fc9988 238 }
239 $separator= '';
240 foreach ($wrapped->options->answers as $subanswer) {
241 $parsableanswerdef .= $separator
242 . '%' . round(100*$subanswer->fraction) . '%';
243 $parsableanswerdef .= $subanswer->answer;
244 if (!empty($wrapped->options->tolerance)) {
245 // Special for numerical answers:
246 $parsableanswerdef .= ":{$wrapped->options->tolerance}";
247 // We only want tolerance for the first alternative, it will
248 // be applied to all of the alternatives.
249 unset($wrapped->options->tolerance);
250 }
251 if ($subanswer->feedback) {
252 $parsableanswerdef .= "#$subanswer->feedback";
253 }
254 $separator = '~';
255 }
256 $parsableanswerdef .= '}';
257 // Fix the questiontext fields of old questions
f34488b2 258 $DB->set_field('question', 'questiontext', $parsableanswerdef, array('id' => $wrapped->id));
c6fc9988 259 } else {
260 $parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext);
261 }
262 $question->questiontext = str_replace("{#$key}", $parsableanswerdef, $question->questiontext);
263 }
264 }
df79079f 265 }
f34488b2 266
705b5874 267 // set default to $questiondisplay questions elements
f34488b2 268 if (isset($this->questiondisplay->options->questions)) {
269 $subquestions = fullclone($this->questiondisplay->options->questions) ;
705b5874 270 if (count($subquestions)) {
f34488b2 271 $sub =1;
272 foreach ($subquestions as $subquestion) {
705b5874 273 $prefix = 'sub_'.$sub.'_' ;
274
275 // validate parameters
276 $answercount = 0;
277 $maxgrade = false;
278 $maxfraction = -1;
fd97082c 279 if ($subquestion->qtype =='shortanswer' ) {
280 switch ($subquestion->usecase) {
281 case '1':
282 $default_values[$prefix.'usecase']= get_string('caseyes', 'quiz');
283 break;
284 case '0':
285 default :
286 $default_values[$prefix.'usecase']= get_string('caseno', 'quiz');
287 }
288 }
289
e5ebbd53 290 if ($subquestion->qtype == 'multichoice' ) {
291 $default_values[$prefix.'layout'] = $subquestion->layout ;
292 switch ($subquestion->layout) {
293 case '0':
665e82f8 294 $default_values[$prefix.'layout']= get_string('layoutselectinline', 'qtype_multianswer');
e5ebbd53 295 break;
296 case '1':
665e82f8 297 $default_values[$prefix.'layout']= get_string('layoutvertical', 'qtype_multianswer');
e5ebbd53 298 break;
299 case '2':
665e82f8 300 $default_values[$prefix.'layout']= get_string('layouthorizontal', 'qtype_multianswer');
e5ebbd53 301 break;
302 default:
665e82f8 303 $default_values[$prefix.'layout']= get_string('layoutundefined', 'qtype_multianswer');
e5ebbd53 304 }
305 }
705b5874 306 foreach ($subquestion->answer as $key=>$answer) {
307 if ( $subquestion->qtype == 'numerical' && $key == 0 ) {
308 $default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0] ;
309 }
310 $trimmedanswer = trim($answer);
311 if ($trimmedanswer !== '') {
312 $answercount++;
313 if ($subquestion->qtype == 'numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
314 $this->_form->setElementError($prefix.'answer['.$key.']' , get_string('answermustbenumberorstar', 'qtype_numerical'));
315 }
316 if ($subquestion->fraction[$key] == 1) {
317 $maxgrade = true;
f34488b2 318 }
705b5874 319 if ($subquestion->fraction[$key] > $maxfraction) {
320 $maxfraction = $subquestion->fraction[$key] ;
321 }
322 }
f34488b2 323
665e82f8 324 $default_values[$prefix.'answer['.$key.']'] = htmlspecialchars ($answer);
f34488b2 325 }
705b5874 326 if ($answercount == 0) {
327 if ($subquestion->qtype == 'multichoice' ) {
328 $this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'qtype_multichoice', 2));
329 } else {
330 $this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'quiz', 1));
331 }
332 }
333 if ($maxgrade == false) {
334 $this->_form->setElementError($prefix.'fraction[0]' ,get_string('fractionsnomax', 'question'));
f34488b2 335 }
705b5874 336 foreach ($subquestion->feedback as $key=>$answer) {
f34488b2 337
665e82f8 338 $default_values[$prefix.'feedback['.$key.']'] = htmlspecialchars ($answer);
f34488b2 339 }
705b5874 340 foreach ( $subquestion->fraction as $key=>$answer) {
341 $default_values[$prefix.'fraction['.$key.']'] = $answer;
f34488b2 342 }
343
344
345 $sub++;
705b5874 346 }
347 }
348 }
03adfa13 349 if (( $this->negative_diff || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->type_change ))&& $this->confirm == 0 ){
d3e3bd6c 350 $this->_form->setElementError('confirm',"confirm then save");
351 }
352 $default_values['alertas']= "<strong>".get_string("
353
354<ul>
355 <li>add or delete questions, </li>
356 <li>change the questions order in the text,</li>
357 <li>change their question type (numerical, shortanswer, multiple choice). </li></ul>
358",'qtype_multianswer')."</strong>";
359
360 if( $default_values != "") {
705b5874 361 $question = (object)((array)$question + $default_values);
362 }
120e5cbf 363 parent::set_data($question);
364 }
365
a78890d5 366 function validation($data, $files) {
367 $errors = parent::validation($data, $files);
705b5874 368 if (isset($this->questiondisplay->options->questions)) {
f34488b2 369
370 $subquestions = fullclone($this->questiondisplay->options->questions) ;
705b5874 371 if (count($subquestions)) {
f34488b2 372 $sub =1;
373 foreach ($subquestions as $subquestion) {
705b5874 374 $prefix = 'sub_'.$sub.'_' ;
375 $answercount = 0;
376 $maxgrade = false;
377 $maxfraction = -1;
d3e3bd6c 378 if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
379 $this->savedquestiondisplay->options->questions[$sub]->qtype != $this->questiondisplay->options->questions[$sub]->qtype ){
380 $storemess = " STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype];
381 }
382 // $errors['subhdr'.$sub]=$storemess ;
705b5874 383 foreach ( $subquestion->answer as $key=>$answer) {
384 $trimmedanswer = trim($answer);
385 if ($trimmedanswer !== '') {
386 $answercount++;
387 if ($subquestion->qtype =='numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
388 $errors[$prefix.'answer['.$key.']']= get_string('answermustbenumberorstar', 'qtype_numerical');
389 }
390 if ($subquestion->fraction[$key] == 1) {
391 $maxgrade = true;
f34488b2 392 }
705b5874 393 if ($subquestion->fraction[$key] > $maxfraction) {
394 $maxfraction = $subquestion->fraction[$key] ;
395 }
f34488b2 396 }
397 }
705b5874 398 if ($answercount==0) {
399 if ( $subquestion->qtype =='multichoice' ) {
400 $errors[$prefix.'answer[0]']= get_string('notenoughanswers', 'qtype_multichoice', 2);
401 }else {
402 $errors[$prefix.'answer[0]'] = get_string('notenoughanswers', 'quiz', 1);
403 }
404 }
405 if ($maxgrade == false) {
406 $errors[$prefix.'fraction[0]']=get_string('fractionsnomax', 'question');
f34488b2 407 }
408 $sub++;
705b5874 409 }
410 } else {
f34488b2 411 $errors['questiontext']=get_string('questionsmissing', 'qtype_multianswer');
705b5874 412 }
120e5cbf 413 }
705b5874 414
120e5cbf 415 return $errors;
c6fc9988 416 }
271ffe3f 417
418 function qtype() {
419 return 'multianswer';
420 }
421}
2471ef86 422?>