lesson MDL-23525 more bugs fixed to pagetypes.
[moodle.git] / mod / lesson / pagetypes / multichoice.php
CommitLineData
0a4abb73
SH
1<?php
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
18/**
19 * Multichoice
20 *
cc3dbaaa
PS
21 * @package mod
22 * @subpackage lesson
23 * @copyright 2009 Sam Hemelryk
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0a4abb73
SH
25 **/
26
1e7f8ea2
PS
27defined('MOODLE_INTERNAL') || die();
28
0a4abb73
SH
29/** Multichoice question type */
30define("LESSON_PAGE_MULTICHOICE", "3");
31
32class lesson_page_type_multichoice extends lesson_page {
33
34 protected $type = lesson_page::TYPE_QUESTION;
35 protected $typeidstring = 'multichoice';
36 protected $typeid = LESSON_PAGE_MULTICHOICE;
37 protected $string = null;
38
39 public function get_typeid() {
40 return $this->typeid;
41 }
42 public function get_typestring() {
43 if ($this->string===null) {
44 $this->string = get_string($this->typeidstring, 'lesson');
45 }
46 return $this->string;
47 }
48 public function get_idstring() {
49 return $this->typeidstring;
50 }
51
a07dedff
PS
52 /**
53 * Gets an array of the jumps used by the answers of this page
54 *
55 * @return array
56 */
57 public function get_jumps() {
58 global $DB;
59 $jumps = array();
60 $params = array ("lessonid" => $this->lesson->id, "pageid" => $this->properties->id);
61 if ($answers = $this->get_answers()) {
62 foreach ($answers as $answer) {
63 if ($answer->answer === '') {
64 // show only jumps for real branches (==have description)
65 continue;
66 }
67 $jumps[] = $this->get_jump_name($answer->jumpto);
68 }
69 }
70 return $jumps;
71 }
72
cc217823
PS
73 public function get_used_answers() {
74 $answers = $this->get_answers();
75 foreach ($answers as $key=>$answer) {
6eb2138a 76 if ($answer->answer === '') {
cc217823
PS
77 unset($answers[$key]);
78 }
79 }
80 return $answers;
81 }
82
0a4abb73
SH
83 public function display($renderer, $attempt) {
84 global $CFG, $PAGE;
cc217823 85 $answers = $this->get_used_answers();
0a4abb73
SH
86 shuffle($answers);
87 $action = $CFG->wwwroot.'/mod/lesson/continue.php';
ac3d7791 88 $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
0a4abb73
SH
89 if ($this->properties->qoption) {
90 $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
91 } else {
92 $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
93 }
94 $data = new stdClass;
95 $data->id = $PAGE->cm->id;
96 $data->pageid = $this->properties->id;
97 $mform->set_data($data);
98 return $mform->display();
99 }
100
101 public function check_answer() {
102 global $DB, $CFG;
103 $result = parent::check_answer();
104
6eb2138a
PS
105 $formattextdefoptions = new object();
106 $formattextdefoptions->noclean = true;
107 $formattextdefoptions->para = false;
108
cc217823 109 $answers = $this->get_used_answers();
0a4abb73
SH
110 shuffle($answers);
111 $action = $CFG->wwwroot.'/mod/lesson/continue.php';
112 $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
113 if ($this->properties->qoption) {
114 $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
115 } else {
116 $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
117 }
118 $data = $mform->get_data();
119 require_sesskey();
120
121 if (!$data) {
a6855934 122 redirect(new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$this->properties->id)));
0a4abb73 123 }
2f67a9b3 124
0a4abb73
SH
125 if ($this->properties->qoption) {
126 // MULTIANSWER allowed, user's answer is an array
127
128 if (empty($data->answer) || !is_array($data->answer)) {
129 $result->noanswer = true;
130 return $result;
131 }
132
6eb2138a
PS
133 $studentanswers = array();
134 foreach ($data->answer as $key=>$value) {
135 $studentanswers[] = (int)$key;
0a4abb73 136 }
2f67a9b3 137
0a4abb73
SH
138 // get what the user answered
139 $result->userresponse = implode(",", $studentanswers);
140
141 // get the answers in a set order, the id order
cc217823 142 $answers = $this->get_used_answers();
0a4abb73
SH
143 $ncorrect = 0;
144 $nhits = 0;
145 $correctresponse = '';
146 $wrongresponse = '';
147 $correctanswerid = 0;
148 $wronganswerid = 0;
149 // store student's answers for displaying on feedback page
6eb2138a 150 $result->studentanswer = '';
0a4abb73 151 foreach ($answers as $answer) {
6eb2138a 152 foreach ($studentanswers as $answerid) {
0a4abb73 153 if ($answerid == $answer->id) {
6eb2138a 154 $result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
0a4abb73
SH
155 }
156 }
157 }
158 // this is for custom scores. If score on answer is positive, it is correct
159 if ($this->lesson->custom) {
160 $ncorrect = 0;
161 $nhits = 0;
162 foreach ($answers as $answer) {
163 if ($answer->score > 0) {
164 $ncorrect++;
165
6eb2138a 166 foreach ($studentanswers as $answerid) {
0a4abb73
SH
167 if ($answerid == $answer->id) {
168 $nhits++;
169 }
170 }
171 // save the first jumpto page id, may be needed!...
172 if (!isset($correctpageid)) {
173 // leave in its "raw" state - will converted into a proper page id later
174 $correctpageid = $answer->jumpto;
175 }
176 // save the answer id for scoring
177 if ($correctanswerid == 0) {
178 $correctanswerid = $answer->id;
179 }
180 // ...also save any response from the correct answers...
181 if (trim(strip_tags($answer->response))) {
6eb2138a 182 $correctresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
183 }
184 } else {
185 // save the first jumpto page id, may be needed!...
186 if (!isset($wrongpageid)) {
187 // leave in its "raw" state - will converted into a proper page id later
188 $wrongpageid = $answer->jumpto;
189 }
190 // save the answer id for scoring
191 if ($wronganswerid == 0) {
192 $wronganswerid = $answer->id;
193 }
194 // ...and from the incorrect ones, don't know which to use at this stage
195 if (trim(strip_tags($answer->response))) {
6eb2138a 196 $wrongresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
197 }
198 }
199 }
200 } else {
201 foreach ($answers as $answer) {
202 if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
203 $ncorrect++;
6eb2138a 204 foreach ($studentanswers as $answerid) {
0a4abb73
SH
205 if ($answerid == $answer->id) {
206 $nhits++;
207 }
208 }
209 // save the first jumpto page id, may be needed!...
210 if (!isset($correctpageid)) {
211 // leave in its "raw" state - will converted into a proper page id later
212 $correctpageid = $answer->jumpto;
213 }
214 // save the answer id for scoring
215 if ($correctanswerid == 0) {
216 $correctanswerid = $answer->id;
217 }
218 // ...also save any response from the correct answers...
219 if (trim(strip_tags($answer->response))) {
6eb2138a 220 $correctresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
221 }
222 } else {
223 // save the first jumpto page id, may be needed!...
224 if (!isset($wrongpageid)) {
225 // leave in its "raw" state - will converted into a proper page id later
226 $wrongpageid = $answer->jumpto;
227 }
228 // save the answer id for scoring
229 if ($wronganswerid == 0) {
230 $wronganswerid = $answer->id;
231 }
232 // ...and from the incorrect ones, don't know which to use at this stage
233 if (trim(strip_tags($answer->response))) {
6eb2138a 234 $wrongresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
235 }
236 }
237 }
238 }
239 if ((count($studentanswers) == $ncorrect) and ($nhits == $ncorrect)) {
240 $result->correctanswer = true;
241 $result->response = $correctresponse;
242 $result->newpageid = $correctpageid;
243 $result->answerid = $correctanswerid;
244 } else {
245 $result->response = $wrongresponse;
246 $result->newpageid = $wrongpageid;
247 $result->answerid = $wronganswerid;
248 }
249 } else {
250 // only one answer allowed
0a4abb73
SH
251 if (empty($data->answerid) && !is_int($data->answerid)) {
252 $result->noanswer = true;
253 return $result;
254 }
255 $result->answerid = $data->answerid;
256 if (!$answer = $DB->get_record("lesson_answers", array("id" => $result->answerid))) {
257 print_error("Continue: answer record not found");
258 }
259 if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
260 $result->correctanswer = true;
261 }
262 if ($this->lesson->custom) {
263 if ($answer->score > 0) {
264 $result->correctanswer = true;
265 } else {
266 $result->correctanswer = false;
267 }
268 }
269 $result->newpageid = $answer->jumpto;
01c37ef1 270 $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
ece2c44f 271 $result->userresponse = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
5e110d86 272 $result->studentanswer = $result->userresponse;
0a4abb73
SH
273 }
274 return $result;
275 }
276
277 public function option_description_string() {
278 if ($this->properties->qoption) {
279 return " - ".get_string("multianswer", "lesson");
280 }
281 return parent::option_description_string();
282 }
283
284 public function display_answers(html_table $table) {
cc217823 285 $answers = $this->get_used_answers();
0a4abb73
SH
286 $options = new stdClass;
287 $options->noclean = true;
288 $options->para = false;
289 $i = 1;
290 foreach ($answers as $answer) {
291 $cells = array();
292 if ($this->lesson->custom && $answer->score > 0) {
293 // if the score is > 0, then it is correct
294 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
295 } else if ($this->lesson->custom) {
296 $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
297 } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
298 // underline correct answers
299 $cells[] = '<span class="correct">'.get_string("answer", "lesson")." $i</span>: \n";
300 } else {
301 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
302 }
01c37ef1 303 $cells[] = format_text($answer->answer, $answer->answerformat, $options);
8cea545e 304 $table->data[] = new html_table_row($cells);
0a4abb73
SH
305
306 $cells = array();
307 $cells[] = "<span class=\"label\">".get_string("response", "lesson")." $i</span>";
01c37ef1 308 $cells[] = format_text($answer->response, $answer->responseformat, $options);
8cea545e 309 $table->data[] = new html_table_row($cells);
2f67a9b3 310
0a4abb73
SH
311 $cells = array();
312 $cells[] = "<span class=\"label\">".get_string("score", "lesson").'</span>';
313 $cells[] = $answer->score;
8cea545e 314 $table->data[] = new html_table_row($cells);
0a4abb73
SH
315
316 $cells = array();
317 $cells[] = "<span class=\"label\">".get_string("jump", "lesson").'</span>';
318 $cells[] = $this->get_jump_name($answer->jumpto);
8cea545e 319 $table->data[] = new html_table_row($cells);
0a4abb73
SH
320 if ($i === 1){
321 $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
322 }
323 $i++;
324 }
325 return $table;
326 }
327 public function stats(array &$pagestats, $tries) {
328 if(count($tries) > $this->lesson->maxattempts) { // if there are more tries than the max that is allowed, grab the last "legal" attempt
329 $temp = $tries[$this->lesson->maxattempts - 1];
330 } else {
331 // else, user attempted the question less than the max, so grab the last one
332 $temp = end($tries);
333 }
334 if ($this->properties->qoption) {
335 $userresponse = explode(",", $temp->useranswer);
336 foreach ($userresponse as $response) {
337 if (isset($pagestats[$temp->pageid][$response])) {
338 $pagestats[$temp->pageid][$response]++;
339 } else {
340 $pagestats[$temp->pageid][$response] = 1;
341 }
342 }
343 } else {
344 if (isset($pagestats[$temp->pageid][$temp->answerid])) {
345 $pagestats[$temp->pageid][$temp->answerid]++;
346 } else {
347 $pagestats[$temp->pageid][$temp->answerid] = 1;
348 }
349 }
350 if (isset($pagestats[$temp->pageid]["total"])) {
351 $pagestats[$temp->pageid]["total"]++;
352 } else {
353 $pagestats[$temp->pageid]["total"] = 1;
354 }
355 return true;
356 }
357
358 public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
cc217823 359 $answers = $this->get_used_answers();
0a4abb73
SH
360 $formattextdefoptions = new stdClass;
361 $formattextdefoptions->para = false; //I'll use it widely in this page
362 foreach ($answers as $answer) {
363 if ($this->properties->qoption) {
364 if ($useranswer == NULL) {
365 $userresponse = array();
366 } else {
367 $userresponse = explode(",", $useranswer->useranswer);
368 }
369 if (in_array($answer->id, $userresponse)) {
370 // make checked
371 $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
372 if (!isset($answerdata->response)) {
373 if ($answer->response == NULL) {
374 if ($useranswer->correct) {
375 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
376 } else {
377 $answerdata->response = get_string("thatsthewronganswer", "lesson");
378 }
379 } else {
380 $answerdata->response = $answer->response;
381 }
382 }
383 if (!isset($answerdata->score)) {
384 if ($this->lesson->custom) {
385 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
386 } elseif ($useranswer->correct) {
387 $answerdata->score = get_string("receivedcredit", "lesson");
388 } else {
389 $answerdata->score = get_string("didnotreceivecredit", "lesson");
390 }
391 }
392 } else {
393 // unchecked
394 $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
395 }
396 if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
01c37ef1 397 $data = "<div class=highlight>".$data.' '.format_text($answer->answer,$answer->answerformat,$formattextdefoptions)."</div>";
0a4abb73 398 } else {
01c37ef1 399 $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
0a4abb73
SH
400 }
401 } else {
402 if ($useranswer != NULL and $answer->id == $useranswer->answerid) {
403 // make checked
404 $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
405 if ($answer->response == NULL) {
406 if ($useranswer->correct) {
407 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
408 } else {
409 $answerdata->response = get_string("thatsthewronganswer", "lesson");
410 }
411 } else {
412 $answerdata->response = $answer->response;
413 }
414 if ($this->lesson->custom) {
415 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
416 } elseif ($useranswer->correct) {
417 $answerdata->score = get_string("receivedcredit", "lesson");
418 } else {
419 $answerdata->score = get_string("didnotreceivecredit", "lesson");
420 }
421 } else {
422 // unchecked
423 $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
424 }
425 if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
426 $data = "<div class=\"highlight\">".$data.' '.format_text($answer->answer,FORMAT_MOODLE,$formattextdefoptions)."</div>";
427 } else {
01c37ef1 428 $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
0a4abb73
SH
429 }
430 }
431 if (isset($pagestats[$this->properties->id][$answer->id])) {
432 $percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100;
433 $percent = round($percent, 2);
434 $percent .= "% ".get_string("checkedthisone", "lesson");
435 } else {
436 $percent = get_string("noonecheckedthis", "lesson");
437 }
438
439 $answerdata->answers[] = array($data, $percent);
440 $answerpage->answerdata = $answerdata;
441 }
442 return $answerpage;
443 }
444}
445
446
447class lesson_add_page_form_multichoice extends lesson_add_page_form_base {
448
449 public $qtype = 'multichoice';
450 public $qtypestring = 'multichoice';
451
452 public function custom_definition() {
453
454 $this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('multianswer', 'lesson'));
fbda6256 455 $this->_form->setDefault('qoption', 0);
4c80a990 456 $this->_form->addHelpButton('qoption', 'multianswer', 'lesson');
0a4abb73
SH
457
458 for ($i = 0; $i < $this->_customdata['lesson']->maxanswers; $i++) {
459 $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
c284a770 460 $this->add_answer($i, NULL, ($i<2));
0a4abb73 461 $this->add_response($i);
94282e8f 462 $this->add_jumpto($i, NULL, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
0a4abb73
SH
463 $this->add_score($i, null, ($i===0)?1:0);
464 }
465 }
466}
467
468class lesson_display_answer_form_multichoice_singleanswer extends moodleform {
469
470 public function definition() {
471 global $USER, $OUTPUT;
472 $mform = $this->_form;
473 $answers = $this->_customdata['answers'];
474 $lessonid = $this->_customdata['lessonid'];
475 $contents = $this->_customdata['contents'];
ac3d7791
AB
476 if (array_key_exists('attempt', $this->_customdata)) {
477 $attempt = $this->_customdata['attempt'];
1112c9ed
AB
478 } else {
479 $attempt = new stdClass();
480 $attempt->answerid = null;
ac3d7791 481 }
0a4abb73
SH
482 $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
483
484 $options = new stdClass;
485 $options->para = false;
486 $options->noclean = true;
487
488 $mform->addElement('hidden', 'id');
489 $mform->setType('id', PARAM_INT);
490
491 $mform->addElement('hidden', 'pageid');
492 $mform->setType('pageid', PARAM_INT);
493
494 $i = 0;
495 foreach ($answers as $answer) {
496 $mform->addElement('html', '<div class="answeroption">');
01c37ef1 497 $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id);
0a4abb73 498 $mform->setType('answer'.$i, PARAM_INT);
1112c9ed 499 if (isset($USER->modattempts[$lessonid]) && $answer->id == $attempt->answerid) {
0a4abb73
SH
500 $mform->setDefault('answerid', true);
501 }
502 $mform->addElement('html', '</div>');
503 $i++;
504 }
505
506 $this->add_action_buttons(null, get_string("pleasecheckoneanswer", "lesson"));
507 }
508
509}
510
511class lesson_display_answer_form_multichoice_multianswer extends moodleform {
512
513 public function definition() {
514 global $USER, $OUTPUT;
515 $mform = $this->_form;
516 $answers = $this->_customdata['answers'];
6eb2138a 517
0a4abb73
SH
518 $lessonid = $this->_customdata['lessonid'];
519 $contents = $this->_customdata['contents'];
520
521 $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
522
523 $options = new stdClass;
524 $options->para = false;
525 $options->noclean = true;
526
527 $mform->addElement('hidden', 'id');
528 $mform->setType('id', PARAM_INT);
529
530 $mform->addElement('hidden', 'pageid');
531 $mform->setType('pageid', PARAM_INT);
532
0a4abb73
SH
533 foreach ($answers as $answer) {
534 $mform->addElement('html', '<div class="answeroption">');
6eb2138a
PS
535 // NOTE: our silly checkbox supports only value '1' - we can not use it like the radiobox above!!!!!!
536 $mform->addElement('checkbox','answer['.$answer->id.']',null,format_text($answer->answer, $answer->answerformat, $options));
537 $mform->setType('answer['.$answer->id.']', PARAM_INT);
0a4abb73 538 if (isset($USER->modattempts[$lessonid]) && $answer->id == $attempt->answerid) {
6eb2138a 539 $mform->setDefault('answer['.$answer->id.']', true);
0a4abb73
SH
540 }
541 $mform->addElement('html', '</div>');
0a4abb73
SH
542 }
543
544 $this->add_action_buttons(null, get_string("pleasecheckoneormoreanswers", "lesson"));
545 }
546
4c80a990 547}