more globals fixed
[moodle.git] / mod / lesson / pagetypes / multichoice.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  * Multichoice
20  *
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
25  **/
27 defined('MOODLE_INTERNAL') || die();
29 /** Multichoice question type */
30 define("LESSON_PAGE_MULTICHOICE",   "3");
32 class lesson_page_type_multichoice extends lesson_page {
34     protected $type = lesson_page::TYPE_QUESTION;
35     protected $typeidstring = 'multichoice';
36     protected $typeid = LESSON_PAGE_MULTICHOICE;
37     protected $string = null;
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     }
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     }
73     public function get_used_answers() {
74         $answers = $this->get_answers();
75         foreach ($answers as $key=>$answer) {
76             if ($answer->answer === '') {
77                 unset($answers[$key]);
78             }
79         }
80         return $answers;
81     }
83     public function display($renderer, $attempt) {
84         global $CFG, $PAGE;
85         $answers = $this->get_used_answers();
86         shuffle($answers);
87         $action = $CFG->wwwroot.'/mod/lesson/continue.php';
88         $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
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     }
101     public function check_answer() {
102         global $DB, $CFG, $PAGE;
103         $result = parent::check_answer();
105         $formattextdefoptions = new object();
106         $formattextdefoptions->noclean = true;
107         $formattextdefoptions->para = false;
109         $answers = $this->get_used_answers();
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();
121         if (!$data) {
122             redirect(new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$this->properties->id)));
123         }
125         if ($this->properties->qoption) {
126             // MULTIANSWER allowed, user's answer is an array
128             if (empty($data->answer) || !is_array($data->answer)) {
129                 $result->noanswer = true;
130                 return $result;
131             }
133             $studentanswers = array();
134             foreach ($data->answer as $key=>$value) {
135                 $studentanswers[] = (int)$key;
136             }
138             // get what the user answered
139             $result->userresponse = implode(",", $studentanswers);
141             // get the answers in a set order, the id order
142             $answers = $this->get_used_answers();
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
150             $result->studentanswer = '';
151             foreach ($answers as $answer) {
152                 foreach ($studentanswers as $answerid) {
153                     if ($answerid == $answer->id) {
154                         $result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
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++;
166                         foreach ($studentanswers as $answerid) {
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))) {
182                             $correctresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
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))) {
196                             $wrongresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
197                         }
198                     }
199                 }
200             } else {
201                 foreach ($answers as $answer) {
202                     if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
203                         $ncorrect++;
204                         foreach ($studentanswers as $answerid) {
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))) {
220                             $correctresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
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))) {
234                             $wrongresponse = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
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
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;
270             $result->response  = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
271             $result->userresponse = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
272             $result->studentanswer = $result->userresponse;
273         }
274         return $result;
275     }
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     }
284     public function display_answers(html_table $table) {
285         $answers = $this->get_used_answers();
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             }
303             $cells[] = format_text($answer->answer, $answer->answerformat, $options);
304             $table->data[] = new html_table_row($cells);
306             $cells = array();
307             $cells[] = "<span class=\"label\">".get_string("response", "lesson")." $i</span>";
308             $cells[] = format_text($answer->response, $answer->responseformat, $options);
309             $table->data[] = new html_table_row($cells);
311             $cells = array();
312             $cells[] = "<span class=\"label\">".get_string("score", "lesson").'</span>';
313             $cells[] = $answer->score;
314             $table->data[] = new html_table_row($cells);
316             $cells = array();
317             $cells[] = "<span class=\"label\">".get_string("jump", "lesson").'</span>';
318             $cells[] = $this->get_jump_name($answer->jumpto);
319             $table->data[] = new html_table_row($cells);
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     }
358     public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
359         $answers = $this->get_used_answers();
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)) {
397                     $data = "<div class=highlight>".$data.' '.format_text($answer->answer,$answer->answerformat,$formattextdefoptions)."</div>";
398                 } else {
399                     $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
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 {
428                     $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
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             }
439             $answerdata->answers[] = array($data, $percent);
440             $answerpage->answerdata = $answerdata;
441         }
442         return $answerpage;
443     }
447 class lesson_add_page_form_multichoice extends lesson_add_page_form_base {
449     public $qtype = 'multichoice';
450     public $qtypestring = 'multichoice';
452     public function custom_definition() {
454         $this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('multianswer', 'lesson'));
455         $this->_form->setDefault('qoption', 0);
456         $this->_form->addHelpButton('qoption', 'multianswer', 'lesson');
458         for ($i = 0; $i < $this->_customdata['lesson']->maxanswers; $i++) {
459             $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
460             $this->add_answer($i, NULL, ($i<2));
461             $this->add_response($i);
462             $this->add_jumpto($i, NULL, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
463             $this->add_score($i, null, ($i===0)?1:0);
464         }
465     }
468 class lesson_display_answer_form_multichoice_singleanswer extends moodleform {
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'];
476         if (array_key_exists('attempt', $this->_customdata)) {
477             $attempt = $this->_customdata['attempt'];
478         } else {
479             $attempt = new stdClass();
480             $attempt->answerid = null;
481         }
482         $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
484         $options = new stdClass;
485         $options->para = false;
486         $options->noclean = true;
488         $mform->addElement('hidden', 'id');
489         $mform->setType('id', PARAM_INT);
491         $mform->addElement('hidden', 'pageid');
492         $mform->setType('pageid', PARAM_INT);
494         $i = 0;
495         foreach ($answers as $answer) {
496             $mform->addElement('html', '<div class="answeroption">');
497             $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id);
498             $mform->setType('answer'.$i, PARAM_INT);
499             if (isset($USER->modattempts[$lessonid]) && $answer->id == $attempt->answerid) {
500                 $mform->setDefault('answerid', true);
501             }
502             $mform->addElement('html', '</div>');
503             $i++;
504         }
506         $this->add_action_buttons(null, get_string("pleasecheckoneanswer", "lesson"));
507     }
511 class lesson_display_answer_form_multichoice_multianswer extends moodleform {
513     public function definition() {
514         global $USER, $OUTPUT;
515         $mform = $this->_form;
516         $answers = $this->_customdata['answers'];
518         $lessonid = $this->_customdata['lessonid'];
519         $contents = $this->_customdata['contents'];
521         $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
523         $options = new stdClass;
524         $options->para = false;
525         $options->noclean = true;
527         $mform->addElement('hidden', 'id');
528         $mform->setType('id', PARAM_INT);
530         $mform->addElement('hidden', 'pageid');
531         $mform->setType('pageid', PARAM_INT);
533         foreach ($answers as $answer) {
534             $mform->addElement('html', '<div class="answeroption">');
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);
538             if (isset($USER->modattempts[$lessonid]) && $answer->id == $attempt->answerid) {
539                 $mform->setDefault('answer['.$answer->id.']', true);
540             }
541             $mform->addElement('html', '</div>');
542         }
544         $this->add_action_buttons(null, get_string("pleasecheckoneormoreanswers", "lesson"));
545     }