MDL-23486 fixed regression, sorrrrry
[moodle.git] / mod / lesson / pagetypes / matching.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 * Matching
20 *
21 * @package lesson
22 * @copyright 2009 Sam Hemelryk
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 **/
25
1e7f8ea2
PS
26defined('MOODLE_INTERNAL') || die();
27
0a4abb73
SH
28/** Matching question type */
29define("LESSON_PAGE_MATCHING", "5");
30
31class lesson_page_type_matching extends lesson_page {
32
33 protected $type = lesson_page::TYPE_QUESTION;
34 protected $typeid = LESSON_PAGE_MATCHING;
35 protected $typeidstring = 'matching';
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 $mform = $this->make_answer_form($attempt);
53 $data = new stdClass;
54 $data->id = $PAGE->cm->id;
55 $data->pageid = $this->properties->id;
56 $mform->set_data($data);
57 return $mform->display();
58 }
59
60 protected function make_answer_form($attempt=null) {
61 global $USER, $CFG;
62 // don't suffle answers (could be an option??)
63 $answers = array_slice($this->get_answers(), 2);
64 $responses = array();
65 foreach ($answers as $answer) {
66 // get all the response
67 if ($answer->response != NULL) {
68 $responses[] = trim($answer->response);
69 }
70 }
71
72 $responseoptions = array();
73 if (!empty($responses)) {
74 shuffle($responses);
75 $responses = array_unique($responses);
76 foreach ($responses as $response) {
77 $responseoptions[htmlspecialchars(trim($response))] = $response;
78 }
79 }
80 if (isset($USER->modattempts[$this->lesson->id]) && !empty($attempt->useranswer)) {
81 $useranswers = explode(',', $attempt->useranswer);
82 $t = 0;
83 } else {
84 $useranswers = array();
85 }
86
87 $action = $CFG->wwwroot.'/mod/lesson/continue.php';
88 $params = array('answers'=>$answers, 'useranswers'=>$useranswers, 'responseoptions'=>$responseoptions, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
89 $mform = new lesson_display_answer_form_matching($action, $params);
90 return $mform;
91 }
92
93 public function create_answers($properties) {
94 global $DB;
95 // now add the answers
96 $newanswer = new stdClass;
97 $newanswer->lessonid = $this->lesson->id;
98 $newanswer->pageid = $this->properties->id;
99 $newanswer->timecreated = $this->properties->timecreated;
100
101 $answers = array();
102
103 // need to add two to offset correct response and wrong response
104 $this->lesson->maxanswers = $this->lesson->maxanswers + 2;
105 for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
106 $answer = clone($newanswer);
01c37ef1
PS
107 if (!empty($properties->answer_editor[$i])) {
108 $answer->answer = $properties->answer_editor[$i]['text'];
109 $answer->answerformat = $properties->answer_editor[$i]['format'];
110 if (isset($properties->response_editor[$i])) {
111 $answer->response = $properties->response_editor[$i]['text'];
112 $answer->responseformat = $properties->response_editor[$i]['format'];
0a4abb73
SH
113 }
114 if (isset($properties->jumpto[$i])) {
115 $answer->jumpto = $properties->jumpto[$i];
116 }
117 if ($this->lesson->custom && isset($properties->score[$i])) {
118 $answer->score = $properties->score[$i];
119 }
120 $answer->id = $DB->insert_record("lesson_answers", $answer);
121 $answers[$answer->id] = new lesson_page_answer($answer);
122 } else if ($i < 2) {
123 $answer->id = $DB->insert_record("lesson_answers", $answer);
124 $answers[$answer->id] = new lesson_page_answer($answer);
125 } else {
126 break;
127 }
128 }
129 $this->answers = $answers;
130 return $answers;
131 }
132
133 public function check_answer() {
134 global $CFG;
01c37ef1
PS
135
136 $formattextdefoptions = new object();
137 $formattextdefoptions->noclean = true;
138 $formattextdefoptions->para = false;
139
0a4abb73
SH
140 $result = parent::check_answer();
141
142 $mform = $this->make_answer_form();
143
144 $data = $mform->get_data();
145 require_sesskey();
146
147 if (!$data) {
a6855934 148 redirect(new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$this->properties->id)));
0a4abb73 149 }
2f67a9b3 150
0a4abb73
SH
151 $response = $data->response;
152 if (!is_array($response)) {
153 $result->noanswer = true;
154 return $result;
155 }
156 $answers = $this->get_answers();
0a4abb73
SH
157 $ncorrect = 0;
158 $i = 0;
159 foreach ($answers as $answer) {
160 if ($i < 2) {
161 // ignore first two answers, they are correct response
162 // and wrong response
163 $i++;
164 continue;
165 }
166 if ($answer->response == $response[$answer->id]) {
167 $ncorrect++;
168 }
169 if ($i == 2) {
170 $correctpageid = $answer->jumpto;
171 $correctanswerid = $answer->id;
172 }
173 if ($i == 3) {
174 $wrongpageid = $answer->jumpto;
175 $wronganswerid = $answer->id;
176 }
177 $i++;
178 }
179 // get he users exact responses for record keeping
180 $userresponse = array();
181 foreach ($response as $key => $value) {
182 foreach($answers as $answer) {
183 if ($value == $answer->response) {
184 $userresponse[] = $answer->id;
185 }
186 if ((int)$answer->id === (int)$key) {
01c37ef1 187 $result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions).' = '.$value;
0a4abb73
SH
188 }
189 }
190 }
191 $result->userresponse = implode(",", $userresponse);
192
193 if ($ncorrect == count($answers)-2) { // dont count correct/wrong responses in the total.
194 foreach ($answers as $answer) {
195 if ($answer->response == NULL && $answer->answer != NULL) {
01c37ef1 196 $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
197 break;
198 }
199 }
200 if (isset($correctpageid)) {
201 $result->newpageid = $correctpageid;
202 }
203 if (isset($correctanswerid)) {
204 $result->answerid = $correctanswerid;
205 }
206 $result->correctanswer = true;
207 } else {
208 $t = 0;
209 foreach ($answers as $answer) {
210 if ($answer->response == NULL && $answer->answer != NULL) {
211 if ($t == 1) {
01c37ef1 212 $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
0a4abb73
SH
213 break;
214 }
215 $t++;
216 }
217 }
218 if (isset($wrongpageid)) {
219 $result->newpageid = $wrongpageid;
220 }
221 if (isset($wronganswerid)) {
222 $result->answerid = $wronganswerid;
223 }
224 }
225 return $result;
226 }
227
228 public function option_description_string() {
229 return get_string("firstanswershould", "lesson");
230 }
231
232 public function display_answers(html_table $table) {
233 $answers = $this->get_answers();
234 $options = new stdClass;
235 $options->noclean = true;
236 $options->para = false;
237 $i = 1;
238 $n = 0;
2f67a9b3 239
0a4abb73
SH
240 foreach ($answers as $answer) {
241 if ($n < 2) {
242 if ($answer->answer != NULL) {
243 $cells = array();
244 if ($n == 0) {
245 $cells[] = "<span class=\"label\">".get_string("correctresponse", "lesson").'</span>';
246 } else {
247 $cells[] = "<span class=\"label\">".get_string("wrongresponse", "lesson").'</span>';
248 }
01c37ef1 249 $cells[] = format_text($answer->answer, $answer->answerformat, $options);
8cea545e 250 $table->data[] = new html_table_row($cells);
0a4abb73
SH
251 }
252 $n++;
253 $i--;
254 } else {
255 $cells = array();
256 if ($this->lesson->custom && $answer->score > 0) {
257 // if the score is > 0, then it is correct
258 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
259 } else if ($this->lesson->custom) {
260 $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
261 } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
262 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
263 } else {
264 $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
265 }
01c37ef1 266 $cells[] = format_text($answer->answer, $answer->answerformat, $options);
8cea545e 267 $table->data[] = new html_table_row($cells);
0a4abb73
SH
268
269 $cells = array();
270 $cells[] = '<span class="label">'.get_string("matchesanswer", "lesson")." $i</span>: ";
01c37ef1 271 $cells[] = format_text($answer->response, $answer->responseformat, $options);
8cea545e 272 $table->data[] = new html_table_row($cells);
0a4abb73
SH
273 }
274
275 if ($i == 1) {
276 $cells = array();
277 $cells[] = '<span class="label">'.get_string("correctanswerscore", "lesson")." $i</span>: ";
278 $cells[] = $answer->score;
8cea545e 279 $table->data[] = new html_table_row($cells);
0a4abb73
SH
280
281 $cells = array();
282 $cells[] = '<span class="label">'.get_string("correctanswerjump", "lesson")." $i</span>: ";
283 $cells[] = $this->get_jump_name($answer->jumpto);
8cea545e 284 $table->data[] = new html_table_row($cells);
0a4abb73
SH
285 } elseif ($i == 2) {
286 $cells = array();
287 $cells[] = '<span class="label">'.get_string("wronganswerscore", "lesson")." $i</span>: ";
288 $cells[] = $answer->score;
8cea545e 289 $table->data[] = new html_table_row($cells);
0a4abb73
SH
290
291 $cells = array();
292 $cells[] = '<span class="label">'.get_string("wronganswerjump", "lesson")." $i</span>: ";
293 $cells[] = $this->get_jump_name($answer->jumpto);
8cea545e 294 $table->data[] = new html_table_row($cells);
0a4abb73
SH
295 }
296
297 if ($i === 1){
298 $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
299 }
300
301 $i++;
302 }
303 return $table;
304 }
305 public function update($properties) {
306 global $DB, $PAGE;
307 $answers = $this->get_answers();
308 $properties->id = $this->properties->id;
309 $properties->lessonid = $this->lesson->id;
64f93798 310 $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), get_context_instance(CONTEXT_MODULE, $PAGE->cm->id), 'mod_lesson', 'page_contents', $properties->id);
0a4abb73
SH
311 $DB->update_record("lesson_pages", $properties);
312
313 // need to add two to offset correct response and wrong response
314 $this->lesson->maxanswers += 2;
315 for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
316 if (!array_key_exists($i, $this->answers)) {
317 $this->answers[$i] = new stdClass;
318 $this->answers[$i]->lessonid = $this->lesson->id;
319 $this->answers[$i]->pageid = $this->id;
320 $this->answers[$i]->timecreated = $this->timecreated;
321 }
01c37ef1
PS
322 if (!empty($properties->answer_editor[$i])) {
323 $this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
324 $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format'];
325 if (isset($properties->response_editor[$i])) {
01310dfb
PS
326 $this->answers[$i]->response = $properties->response_editor[$i]['text'];
327 $this->answers[$i]->responseformat = $properties->response_editor[$i]['format'];
0a4abb73
SH
328 }
329 if (isset($properties->jumpto[$i])) {
330 $this->answers[$i]->jumpto = $properties->jumpto[$i];
331 }
332 if ($this->lesson->custom && isset($properties->score[$i])) {
333 $this->answers[$i]->score = $properties->score[$i];
334 }
335 if (!isset($this->answers[$i]->id)) {
336 $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]);
337 } else {
338 $DB->update_record("lesson_answers", $this->answers[$i]->properties());
339 }
340
341 } else if ($i < 2) {
342 if (!isset($this->answers[$i]->id)) {
343 $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]);
344 } else {
345 $DB->update_record("lesson_answers", $this->answers[$i]->properties());
346 }
347
348 } else {
349 break;
350 }
351 }
352 return true;
353 }
354 public function stats(array &$pagestats, $tries) {
355 if(count($tries) > $this->lesson->maxattempts) { // if there are more tries than the max that is allowed, grab the last "legal" attempt
356 $temp = $tries[$this->lesson->maxattempts - 1];
357 } else {
358 // else, user attempted the question less than the max, so grab the last one
359 $temp = end($tries);
360 }
361 if ($temp->correct) {
362 if (isset($pagestats[$temp->pageid]["correct"])) {
363 $pagestats[$temp->pageid]["correct"]++;
364 } else {
365 $pagestats[$temp->pageid]["correct"] = 1;
366 }
367 }
368 if (isset($pagestats[$temp->pageid]["total"])) {
369 $pagestats[$temp->pageid]["total"]++;
370 } else {
371 $pagestats[$temp->pageid]["total"] = 1;
372 }
373 return true;
374 }
375 public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
376 $answers = array();
377 foreach ($this->get_answers() as $answer) {
378 $answers[$answer->id] = $answer;
379 }
380 $formattextdefoptions = new stdClass;
381 $formattextdefoptions->para = false; //I'll use it widely in this page
382 foreach ($answers as $answer) {
383 if ($n == 0 && $useranswer != NULL && $useranswer->correct) {
384 if ($answer->response == NULL && $useranswer != NULL) {
385 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
386 } else {
387 $answerdata->response = $answer->response;
388 }
389 } elseif ($n == 1 && $useranswer != NULL && !$useranswer->correct) {
390 if ($answer->response == NULL && $useranswer != NULL) {
391 $answerdata->response = get_string("thatsthewronganswer", "lesson");
392 } else {
393 $answerdata->response = $answer->response;
394 }
395 } elseif ($n > 1) {
396 if ($n == 2 && $useranswer != NULL && $useranswer->correct) {
397 if ($this->lesson->custom) {
398 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
399 } else {
400 $answerdata->score = get_string("receivedcredit", "lesson");
401 }
402 } elseif ($n == 3 && $useranswer != NULL && !$useranswer->correct) {
403 if ($this->lesson->custom) {
404 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
405 } else {
406 $answerdata->score = get_string("didnotreceivecredit", "lesson");
407 }
408 }
409 $data = "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->answer))."</option></select>";
410 if ($useranswer != NULL) {
411 $userresponse = explode(",", $useranswer->useranswer);
412 $data .= "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answers[$userresponse[$i]]->response))."</option></select>";
413 } else {
414 $data .= "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
415 }
416
417 if ($n == 2) {
418 if (isset($pagestats[$this->properties->id])) {
419 if (!array_key_exists('correct', $pagestats[$this->properties->id])) {
420 $pagestats[$this->properties->id]["correct"] = 0;
421 }
422 $percent = $pagestats[$this->properties->id]["correct"] / $pagestats[$this->properties->id]["total"] * 100;
423 $percent = round($percent, 2);
424 $percent .= "% ".get_string("answeredcorrectly", "lesson");
425 } else {
426 $percent = get_string("nooneansweredthisquestion", "lesson");
427 }
428 } else {
429 $percent = "";
430 }
431
432 $answerdata->answers[] = array($data, $percent);
433 $i++;
434 }
435 $n++;
436 $answerpage->answerdata = $answerdata;
437 }
438 return $answerpage;
439 }
440 public function get_jumps() {
441 global $DB;
442 // The jumps for matching question type is stored
443 // in the 3rd and 4rth answer record.
444 $jumps = array();
445 $params = array ("lessonid" => $this->lesson->id, "pageid" => $this->properties->id);
446 if ($answers = $DB->get_records_select("lesson_answers", "lessonid = :lessonid and pageid = :pageid", $params, 'id', '*', '2', '2')) {
447 foreach ($answers as $answer) {
448 $jumps[] = $this->get_jump_name($answer->jumpto);
449 }
450 }
451 return $jumps;
452 }
453}
454
455class lesson_add_page_form_matching extends lesson_add_page_form_base {
456
457 public $qtype = 'matching';
458 public $qtypestring = 'matching';
459
460 public function custom_definition() {
461
462 $this->_form->addElement('header', 'correctresponse', get_string('correctresponse', 'lesson'));
088d4006
PS
463 $this->_form->addElement('editor', 'answer_editor[0]', get_string('correctresponse', 'lesson'), null, array('noclean'=>true));
464 $this->_form->setDefault('answer_editor[0]', array('text'=>'', 'format'=>FORMAT_MOODLE));
0a4abb73
SH
465 $this->add_jumpto(2, get_string('correctanswerjump','lesson'));
466 $this->add_score(2, get_string("correctanswerscore", "lesson"));
467
468 $this->_form->addElement('header', 'wrongresponse', get_string('wrongresponse', 'lesson'));
088d4006
PS
469 $this->_form->addElement('editor', 'answer_editor[1]', get_string('wrongresponse', 'lesson'), null, array('noclean'=>true));
470 $this->_form->setDefault('answer_editor[1]', array('text'=>'', 'format'=>FORMAT_MOODLE));
0a4abb73
SH
471 $this->add_jumpto(3, get_string('wronganswerjump','lesson'));
472 $this->add_score(3, get_string("wronganswerscore", "lesson"));
473
474 for ($i = 2; $i < $this->_customdata['lesson']->maxanswers+2; $i++) {
475 $this->_form->addElement('header', 'matchingpair'.($i-1), get_string('matchingpair', 'lesson', $i-1));
476 $this->add_answer($i);
477 $this->add_response($i, get_string('matchesanswer','lesson'));
478 }
479 }
480}
481
482
483class lesson_display_answer_form_matching extends moodleform {
484
485 public function definition() {
486 global $USER, $OUTPUT;
487 $mform = $this->_form;
488 $answers = $this->_customdata['answers'];
489 $useranswers = $this->_customdata['useranswers'];
490 $responseoptions = $this->_customdata['responseoptions'];
491 $lessonid = $this->_customdata['lessonid'];
492 $contents = $this->_customdata['contents'];
493
494 $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
495
496 $options = new stdClass;
497 $options->para = false;
498 $options->noclean = true;
499
500 $mform->addElement('hidden', 'id');
501 $mform->setType('id', PARAM_INT);
502
503 $mform->addElement('hidden', 'pageid');
504 $mform->setType('pageid', PARAM_INT);
505
506 $i = 0;
507 foreach ($answers as $answer) {
508 $mform->addElement('html', '<div class="answeroption">');
509 if ($answer->response != NULL) {
01c37ef1 510 $mform->addElement('select', 'response['.$answer->id.']', format_text($answer->answer,$answer->answerformat,$options), $responseoptions);
0a4abb73
SH
511 $mform->setType('response['.$answer->id.']', PARAM_TEXT);
512 if (isset($USER->modattempts[$lessonid])) {
513 $mform->setDefault('response['.$answer->id.']', htmlspecialchars(trim($answers[$useranswers[$t]]->response)));
514 } else {
515 $mform->setDefault('response['.$answer->id.']', 'answeroption');
516 }
517 }
518 $mform->addElement('html', '</div>');
519 $i++;
520 }
521
522 $this->add_action_buttons(null, get_string("pleasematchtheabovepairs", "lesson"));
523 }
524
525}