MDL-53966 lesson: Allow maximum number of attempts to be unlimited
[moodle.git] / mod / lesson / pagetypes / truefalse.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 * True/false
20 *
9b24f68b 21 * @package mod_lesson
cc3dbaaa
PS
22 * @copyright 2009 Sam Hemelryk
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
0a4abb73
SH
24 **/
25
1e7f8ea2
PS
26defined('MOODLE_INTERNAL') || die();
27
28/** True/False question type */
0a4abb73
SH
29define("LESSON_PAGE_TRUEFALSE", "2");
30
31class lesson_page_type_truefalse extends lesson_page {
32
33 protected $type = lesson_page::TYPE_QUESTION;
34 protected $typeidstring = 'truefalse';
35 protected $typeid = LESSON_PAGE_TRUEFALSE;
36 protected $string = null;
37
38 public function get_typeid() {
39 return $this->typeid;
40 }
41 public function get_typestring() {
42 if ($this->string===null) {
43 $this->string = get_string($this->typeidstring, 'lesson');
44 }
45 return $this->string;
46 }
47 public function get_idstring() {
48 return $this->typeidstring;
49 }
50 public function display($renderer, $attempt) {
51 global $USER, $CFG, $PAGE;
52 $answers = $this->get_answers();
0abc18cf
JMV
53 foreach ($answers as $key => $answer) {
54 $answers[$key] = parent::rewrite_answers_urls($answer);
55 }
0a4abb73
SH
56 shuffle($answers);
57
1112c9ed 58 $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
0a4abb73
SH
59 $mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params);
60 $data = new stdClass;
61 $data->id = $PAGE->cm->id;
62 $data->pageid = $this->properties->id;
63 $mform->set_data($data);
8101328a
SB
64
65 // Trigger an event question viewed.
66 $eventparams = array(
67 'context' => context_module::instance($PAGE->cm->id),
68 'objectid' => $this->properties->id,
69 'other' => array(
70 'pagetype' => $this->get_typestring()
71 )
72 );
73
74 $event = \mod_lesson\event\question_viewed::create($eventparams);
75 $event->trigger();
0a4abb73
SH
76 return $mform->display();
77 }
78 public function check_answer() {
79 global $DB, $CFG;
39790bd8 80 $formattextdefoptions = new stdClass();
01c37ef1
PS
81 $formattextdefoptions->noclean = true;
82 $formattextdefoptions->para = false;
83
0a4abb73
SH
84 $answers = $this->get_answers();
85 shuffle($answers);
86 $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
87 $mform = new lesson_display_answer_form_truefalse($CFG->wwwroot.'/mod/lesson/continue.php', $params);
88 $data = $mform->get_data();
89 require_sesskey();
90
91 $result = parent::check_answer();
92
fb6e1b8f 93 if (empty($data->answerid)) {
0a4abb73
SH
94 $result->noanswer = true;
95 return $result;
96 }
fb6e1b8f 97 $result->answerid = $data->answerid;
01c37ef1 98 $answer = $DB->get_record("lesson_answers", array("id" => $result->answerid), '*', MUST_EXIST);
0abc18cf 99 $answer = parent::rewrite_answers_urls($answer);
0a4abb73
SH
100 if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
101 $result->correctanswer = true;
102 }
103 if ($this->lesson->custom) {
104 if ($answer->score > 0) {
105 $result->correctanswer = true;
106 } else {
107 $result->correctanswer = false;
108 }
109 }
110 $result->newpageid = $answer->jumpto;
01c37ef1 111 $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
548a878b 112 $result->studentanswer = $result->userresponse = $answer->answer;
0a4abb73
SH
113 return $result;
114 }
115
116 public function display_answers(html_table $table) {
117 $answers = $this->get_answers();
39790bd8 118 $options = new stdClass();
0a4abb73
SH
119 $options->noclean = true;
120 $options->para = false;
121 $i = 1;
122 foreach ($answers as $answer) {
0abc18cf 123 $answer = parent::rewrite_answers_urls($answer);
0a4abb73
SH
124 $cells = array();
125 if ($this->lesson->custom && $answer->score > 0) {
126 // if the score is > 0, then it is correct
195480e9 127 $cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
0a4abb73 128 } else if ($this->lesson->custom) {
195480e9 129 $cells[] = '<label>' . get_string('answer', 'lesson') . " {$i}</label>: \n";
0a4abb73
SH
130 } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
131 // underline correct answers
195480e9 132 $cells[] = '<span class="correct">' . get_string('answer', 'lesson') . " {$i}</span>: \n";
0a4abb73 133 } else {
195480e9 134 $cells[] = '<label class="correct">' . get_string('answer', 'lesson') . " {$i}</label>: \n";
0a4abb73 135 }
01c37ef1 136 $cells[] = format_text($answer->answer, $answer->answerformat, $options);
8cea545e 137 $table->data[] = new html_table_row($cells);
0a4abb73
SH
138
139 $cells = array();
195480e9 140 $cells[] = '<label>' . get_string('response', 'lesson') . ' ' . $i . '</label>:';
01c37ef1 141 $cells[] = format_text($answer->response, $answer->responseformat, $options);
8cea545e 142 $table->data[] = new html_table_row($cells);
0a4abb73
SH
143
144 $cells = array();
195480e9 145 $cells[] = '<label>' . get_string('score', 'lesson') . '</label>:';
0a4abb73 146 $cells[] = $answer->score;
8cea545e 147 $table->data[] = new html_table_row($cells);
0a4abb73
SH
148
149 $cells = array();
195480e9 150 $cells[] = '<label>' . get_string('jump', 'lesson') . '</label>:';
0a4abb73 151 $cells[] = $this->get_jump_name($answer->jumpto);
8cea545e 152 $table->data[] = new html_table_row($cells);
2f67a9b3 153
0a4abb73
SH
154 if ($i === 1){
155 $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
156 }
157
158 $i++;
159 }
160 return $table;
161 }
6d84e82f
RW
162
163 /**
164 * Updates the page and its answers
165 *
166 * @global moodle_database $DB
167 * @global moodle_page $PAGE
168 * @param stdClass $properties
169 * @return bool
170 */
0ed6f712 171 public function update($properties, $context = null, $maxbytes = null) {
6d84e82f
RW
172 global $DB, $PAGE;
173 $answers = $this->get_answers();
174 $properties->id = $this->properties->id;
175 $properties->lessonid = $this->lesson->id;
9ac3d686 176 $properties->timemodified = time();
5918e371 177 $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), context_module::instance($PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
6d84e82f
RW
178 $DB->update_record("lesson_pages", $properties);
179
1cf8ca34
SB
180 // Trigger an event: page updated.
181 \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
182
6d84e82f
RW
183 // need to reset offset for correct and wrong responses
184 $this->lesson->maxanswers = 2;
185 for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
186 if (!array_key_exists($i, $this->answers)) {
187 $this->answers[$i] = new stdClass;
188 $this->answers[$i]->lessonid = $this->lesson->id;
189 $this->answers[$i]->pageid = $this->id;
190 $this->answers[$i]->timecreated = $this->timecreated;
191 }
192
193 if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
194 $this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
195 $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format'];
196 }
197
198 if (!empty($properties->response_editor[$i]) && is_array($properties->response_editor[$i])) {
199 $this->answers[$i]->response = $properties->response_editor[$i]['text'];
200 $this->answers[$i]->responseformat = $properties->response_editor[$i]['format'];
201 }
202
203 // we don't need to check for isset here because properties called it's own isset method.
204 if ($this->answers[$i]->answer != '') {
205 if (isset($properties->jumpto[$i])) {
206 $this->answers[$i]->jumpto = $properties->jumpto[$i];
207 }
208 if ($this->lesson->custom && isset($properties->score[$i])) {
209 $this->answers[$i]->score = $properties->score[$i];
210 }
211 if (!isset($this->answers[$i]->id)) {
212 $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]);
213 } else {
214 $DB->update_record("lesson_answers", $this->answers[$i]->properties());
215 }
0abc18cf
JMV
216 // Save files in answers and responses.
217 $this->save_answers_files($context, $maxbytes, $this->answers[$i],
218 $properties->answer_editor[$i], $properties->response_editor[$i]);
6d84e82f
RW
219 } else if (isset($this->answers[$i]->id)) {
220 $DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id));
221 unset($this->answers[$i]);
222 }
223 }
224 return true;
225 }
226
0a4abb73 227 public function stats(array &$pagestats, $tries) {
252e85be 228 $temp = $this->lesson->get_last_attempt($tries);
0a4abb73
SH
229 if ($this->properties->qoption) {
230 $userresponse = explode(",", $temp->useranswer);
231 foreach ($userresponse as $response) {
232 if (isset($pagestats[$temp->pageid][$response])) {
233 $pagestats[$temp->pageid][$response]++;
234 } else {
235 $pagestats[$temp->pageid][$response] = 1;
236 }
237 }
238 } else {
239 if (isset($pagestats[$temp->pageid][$temp->answerid])) {
240 $pagestats[$temp->pageid][$temp->answerid]++;
241 } else {
242 $pagestats[$temp->pageid][$temp->answerid] = 1;
243 }
244 }
245 if (isset($pagestats[$temp->pageid]["total"])) {
246 $pagestats[$temp->pageid]["total"]++;
247 } else {
248 $pagestats[$temp->pageid]["total"] = 1;
249 }
250 return true;
251 }
252
253 public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
254 $answers = $this->get_answers();
39790bd8 255 $formattextdefoptions = new stdClass(); //I'll use it widely in this page
01c37ef1
PS
256 $formattextdefoptions->para = false;
257 $formattextdefoptions->noclean = true;
59f1c9f5
JMV
258 $formattextdefoptions->context = $answerpage->context;
259
0a4abb73 260 foreach ($answers as $answer) {
0abc18cf 261 $answer = parent::rewrite_answers_urls($answer);
0a4abb73 262 if ($this->properties->qoption) {
ecea65ca 263 if ($useranswer == null) {
0a4abb73
SH
264 $userresponse = array();
265 } else {
266 $userresponse = explode(",", $useranswer->useranswer);
267 }
268 if (in_array($answer->id, $userresponse)) {
269 // make checked
270 $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
271 if (!isset($answerdata->response)) {
ecea65ca 272 if ($answer->response == null) {
0a4abb73
SH
273 if ($useranswer->correct) {
274 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
275 } else {
276 $answerdata->response = get_string("thatsthewronganswer", "lesson");
277 }
278 } else {
01c37ef1 279 $answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
280 }
281 }
282 if (!isset($answerdata->score)) {
283 if ($this->lesson->custom) {
284 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
285 } elseif ($useranswer->correct) {
286 $answerdata->score = get_string("receivedcredit", "lesson");
287 } else {
288 $answerdata->score = get_string("didnotreceivecredit", "lesson");
289 }
290 }
291 } else {
292 // unchecked
293 $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
294 }
295 if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
01c37ef1 296 $data .= "<div class=highlight>".format_text($answer->answer, $answer->answerformat, $formattextdefoptions)."</div>";
0a4abb73 297 } else {
01c37ef1 298 $data .= format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
0a4abb73
SH
299 }
300 } else {
ecea65ca 301 if ($useranswer != null and $answer->id == $useranswer->answerid) {
0a4abb73
SH
302 // make checked
303 $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
ecea65ca 304 if ($answer->response == null) {
0a4abb73
SH
305 if ($useranswer->correct) {
306 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
307 } else {
308 $answerdata->response = get_string("thatsthewronganswer", "lesson");
309 }
310 } else {
01c37ef1 311 $answerdata->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
312 }
313 if ($this->lesson->custom) {
314 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
315 } elseif ($useranswer->correct) {
316 $answerdata->score = get_string("receivedcredit", "lesson");
317 } else {
318 $answerdata->score = get_string("didnotreceivecredit", "lesson");
319 }
320 } else {
321 // unchecked
322 $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
323 }
324 if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
01c37ef1 325 $data .= "<div class=\"highlight\">".format_text($answer->answer, $answer->answerformat, $formattextdefoptions)."</div>";
0a4abb73 326 } else {
01c37ef1 327 $data .= format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
0a4abb73
SH
328 }
329 }
330 if (isset($pagestats[$this->properties->id][$answer->id])) {
331 $percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100;
332 $percent = round($percent, 2);
333 $percent .= "% ".get_string("checkedthisone", "lesson");
334 } else {
335 $percent = get_string("noonecheckedthis", "lesson");
336 }
337
338 $answerdata->answers[] = array($data, $percent);
339 $answerpage->answerdata = $answerdata;
340 }
341 return $answerpage;
342 }
343}
344
345class lesson_add_page_form_truefalse extends lesson_add_page_form_base {
346
347 public $qtype = 'truefalse';
348 public $qtypestring = 'truefalse';
ceeab150
RT
349 protected $answerformat = LESSON_ANSWER_HTML;
350 protected $responseformat = LESSON_ANSWER_HTML;
0a4abb73
SH
351
352 public function custom_definition() {
353 $this->_form->addElement('header', 'answertitle0', get_string('correctresponse', 'lesson'));
ceeab150 354 $this->add_answer(0, null, true, $this->get_answer_format());
0a4abb73 355 $this->add_response(0);
4d5ab13e 356 $this->add_jumpto(0, get_string('correctanswerjump', 'lesson'), LESSON_NEXTPAGE);
0a4abb73
SH
357 $this->add_score(0, get_string('correctanswerscore', 'lesson'), 1);
358
359 $this->_form->addElement('header', 'answertitle1', get_string('wrongresponse', 'lesson'));
ceeab150 360 $this->add_answer(1, null, true, $this->get_answer_format());
0a4abb73 361 $this->add_response(1);
4d5ab13e 362 $this->add_jumpto(1, get_string('wronganswerjump', 'lesson'), LESSON_THISPAGE);
0a4abb73
SH
363 $this->add_score(1, get_string('wronganswerscore', 'lesson'), 0);
364 }
365}
366
367class lesson_display_answer_form_truefalse extends moodleform {
368
369 public function definition() {
370 global $USER, $OUTPUT;
371 $mform = $this->_form;
372 $answers = $this->_customdata['answers'];
373 $lessonid = $this->_customdata['lessonid'];
374 $contents = $this->_customdata['contents'];
1112c9ed
AB
375 if (array_key_exists('attempt', $this->_customdata)) {
376 $attempt = $this->_customdata['attempt'];
377 } else {
378 $attempt = new stdClass();
379 $attempt->answerid = null;
380 }
0a4abb73 381
5b0af6e4
JMV
382 // Disable shortforms.
383 $mform->setDisableShortforms();
384
ffdf7f8a
DM
385 $mform->addElement('header', 'pageheader');
386
387 $mform->addElement('html', $OUTPUT->container($contents, 'contents'));
0a4abb73 388
abd5c24e
RW
389 $hasattempt = false;
390 $disabled = '';
391 if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
392 $hasattempt = true;
393 $disabled = array('disabled' => 'disabled');
394 }
395
39790bd8 396 $options = new stdClass();
0a4abb73
SH
397 $options->para = false;
398 $options->noclean = true;
399
400 $mform->addElement('hidden', 'id');
401 $mform->setType('id', PARAM_INT);
402
403 $mform->addElement('hidden', 'pageid');
404 $mform->setType('pageid', PARAM_INT);
405
406 $i = 0;
2aacd415 407 $radiobuttons = array();
0a4abb73 408 foreach ($answers as $answer) {
abd5c24e
RW
409 $ansid = 'answerid';
410 if ($hasattempt) {
411 $ansid = 'answer_id';
412 }
413
bd576de8 414 $answer = lesson_page_type_truefalse::rewrite_answers_urls($answer);
2aacd415
RT
415 $radiobuttons[] = $mform->createElement('radio', $ansid, null,
416 format_text($answer->answer, $answer->answerformat, $options), $answer->id, $disabled);
417
abd5c24e
RW
418 $mform->setType($ansid, PARAM_INT);
419 if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
420 $mform->setDefault($ansid, $attempt->answerid);
421 $mform->addElement('hidden', 'answerid', $answer->id);
422 $mform->setType('answerid', PARAM_INT);
0a4abb73 423 }
0a4abb73
SH
424 $i++;
425 }
426
2aacd415
RT
427 $radiogroup = $mform->addGroup($radiobuttons, $ansid, '', array(''), false);
428 $radiogroup->setAttributes(array('class' => 'answeroptiongroup'));
429
abd5c24e
RW
430 if ($hasattempt) {
431 $this->add_action_buttons(null, get_string("nextpage", "lesson"));
432 } else {
433 $this->add_action_buttons(null, get_string("submit", "lesson"));
434 }
435
0a4abb73 436 }
2f67a9b3 437
ffdf7f8a 438}