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