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