Merge branch 'MDL-61530-master' of git://github.com/andrewnicols/moodle
[moodle.git] / mod / lesson / pagetypes / matching.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  * Matching
20  *
21  * @package mod_lesson
22  * @copyright  2009 Sam Hemelryk
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  **/
26 defined('MOODLE_INTERNAL') || die();
28 /** Matching question type */
29 define("LESSON_PAGE_MATCHING",      "5");
31 class lesson_page_type_matching extends lesson_page {
33     protected $type = lesson_page::TYPE_QUESTION;
34     protected $typeid = LESSON_PAGE_MATCHING;
35     protected $typeidstring = 'matching';
36     protected $string = null;
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);
58         // Trigger an event question viewed.
59         $eventparams = array(
60             'context' => context_module::instance($PAGE->cm->id),
61             'objectid' => $this->properties->id,
62             'other' => array(
63                     'pagetype' => $this->get_typestring()
64                 )
65             );
67         $event = \mod_lesson\event\question_viewed::create($eventparams);
68         $event->trigger();
69         return $mform->display();
70     }
72     protected function make_answer_form($attempt=null) {
73         global $USER, $CFG;
74         // don't shuffle answers (could be an option??)
75         $getanswers = array_slice($this->get_answers(), 2);
77         $answers = array();
78         foreach ($getanswers as $getanswer) {
79             $answers[$getanswer->id] = $getanswer;
80         }
82         $responses = array();
83         foreach ($answers as $answer) {
84             // get all the response
85             if ($answer->response != null) {
86                 $responses[] = trim($answer->response);
87             }
88         }
90         $responseoptions = array(''=>get_string('choosedots'));
91         if (!empty($responses)) {
92             shuffle($responses);
93             foreach ($responses as  $response) {
94                 $responseoptions[htmlspecialchars($response)] = $response;
95             }
96         }
97         if (isset($USER->modattempts[$this->lesson->id]) && !empty($attempt->useranswer)) {
98             $useranswers = explode(',', $attempt->useranswer);
99             $t = 0;
100         } else {
101             $useranswers = array();
102         }
104         $action = $CFG->wwwroot.'/mod/lesson/continue.php';
105         $params = array('answers'=>$answers, 'useranswers'=>$useranswers, 'responseoptions'=>$responseoptions, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
106         $mform = new lesson_display_answer_form_matching($action, $params);
107         return $mform;
108     }
110     public function create_answers($properties) {
111         global $DB, $PAGE;
112         // now add the answers
113         $newanswer = new stdClass;
114         $newanswer->lessonid = $this->lesson->id;
115         $newanswer->pageid = $this->properties->id;
116         $newanswer->timecreated = $this->properties->timecreated;
118         $cm = get_coursemodule_from_instance('lesson', $this->lesson->id, $this->lesson->course);
119         $context = context_module::instance($cm->id);
121         // Check for duplicate response format.
122         $duplicateresponse = array();
123         if (is_array($properties->response_editor) &&             // If there are response_editors to iterate.
124                 is_array(reset($properties->response_editor))) {  // And they come split into text & format array.
125             foreach ($properties->response_editor as $response) { // Iterate over all them.
126                 $duplicateresponse[] = $response['text'];         // Picking the text only. This pagetype is that way.
127             }
128             $properties->response_editor = $duplicateresponse;
129         }
131         $answers = array();
133         // need to add two to offset correct response and wrong response
134         $this->lesson->maxanswers = $this->lesson->maxanswers + 2;
135         for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
136             $answer = clone($newanswer);
137             if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
138                 $answer->answer = $properties->answer_editor[$i]['text'];
139                 $answer->answerformat = $properties->answer_editor[$i]['format'];
140             }
141             if (!empty($properties->response_editor[$i])) {
142                 $answer->response = $properties->response_editor[$i];
143                 $answer->responseformat = 0;
144             }
146             if (isset($properties->jumpto[$i])) {
147                 $answer->jumpto = $properties->jumpto[$i];
148             }
149             if ($this->lesson->custom && isset($properties->score[$i])) {
150                 $answer->score = $properties->score[$i];
151             }
153             if (isset($answer->answer) && $answer->answer != '') {
154                 $answer->id = $DB->insert_record("lesson_answers", $answer);
155                 $this->save_answers_files($context, $PAGE->course->maxbytes,
156                         $answer, $properties->answer_editor[$i]);
157                 $answers[$answer->id] = new lesson_page_answer($answer);
158             } else if ($i < 2) {
159                 $answer->id = $DB->insert_record("lesson_answers", $answer);
160                 $answers[$answer->id] = new lesson_page_answer($answer);
161             } else {
162                 break;
163             }
164         }
165         $this->answers = $answers;
166         return $answers;
167     }
169     public function check_answer() {
170         global $CFG, $PAGE;
172         $formattextdefoptions = new stdClass();
173         $formattextdefoptions->noclean = true;
174         $formattextdefoptions->para = false;
176         $result = parent::check_answer();
178         $mform = $this->make_answer_form();
180         $data = $mform->get_data();
181         require_sesskey();
183         if (!$data) {
184             $result->inmediatejump = true;
185             $result->newpageid = $this->properties->id;
186             return $result;
187         }
189         $response = $data->response;
190         $getanswers = $this->get_answers();
191         foreach ($getanswers as $key => $answer) {
192             $getanswers[$key] = parent::rewrite_answers_urls($answer);
193         }
195         $correct = array_shift($getanswers);
196         $wrong   = array_shift($getanswers);
198         $answers = array();
199         foreach ($getanswers as $key => $answer) {
200             if ($answer->answer !== '' or $answer->response !== '') {
201                 $answers[$answer->id] = $answer;
202             }
203         }
205         // get the user's exact responses for record keeping
206         $hits = 0;
207         $userresponse = array();
208         $result->studentanswerformat = FORMAT_HTML;
209         foreach ($response as $id => $value) {
210             if ($value == '') {
211                 $result->noanswer = true;
212                 return $result;
213             }
214             $value = htmlspecialchars_decode($value);
215             $userresponse[] = $value;
216             // Make sure the user's answer exists in question's answer
217             if (array_key_exists($id, $answers)) {
218                 $answer = $answers[$id];
219                 $result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions).' = '.$value;
220                 if (trim($answer->response) == trim($value)) {
221                     $hits++;
222                 }
223             }
224         }
226         $result->userresponse = implode(",", $userresponse);
228         if ($hits == count($answers)) {
229             $result->correctanswer = true;
230             $result->response      = format_text($correct->answer, $correct->answerformat, $formattextdefoptions);
231             $result->answerid      = $correct->id;
232             $result->newpageid     = $correct->jumpto;
233         } else {
234             $result->correctanswer = false;
235             $result->response      = format_text($wrong->answer, $wrong->answerformat, $formattextdefoptions);
236             $result->answerid      = $wrong->id;
237             $result->newpageid     = $wrong->jumpto;
238         }
240         return $result;
241     }
243     public function option_description_string() {
244         return get_string("firstanswershould", "lesson");
245     }
247     public function display_answers(html_table $table) {
248         $answers = $this->get_answers();
249         $options = new stdClass;
250         $options->noclean = true;
251         $options->para = false;
252         $i = 1;
253         $n = 0;
255         foreach ($answers as $answer) {
256             $answer = parent::rewrite_answers_urls($answer);
257             if ($n < 2) {
258                 if ($answer->answer != null) {
259                     $cells = array();
260                     if ($n == 0) {
261                         $cells[] = '<label>' . get_string('correctresponse', 'lesson') . '</label>';
262                     } else {
263                         $cells[] = '<label>' . get_string('wrongresponse', 'lesson') . '</label>';
264                     }
265                     $cells[] = format_text($answer->answer, $answer->answerformat, $options);
266                     $table->data[] = new html_table_row($cells);
267                 }
269                 if ($n == 0) {
270                     $cells = array();
271                     $cells[] = '<label>' . get_string('correctanswerscore', 'lesson') . '</label>';
272                     $cells[] = $answer->score;
273                     $table->data[] = new html_table_row($cells);
275                     $cells = array();
276                     $cells[] = '<label>' . get_string('correctanswerjump', 'lesson') . '</label>';
277                     $cells[] = $this->get_jump_name($answer->jumpto);
278                     $table->data[] = new html_table_row($cells);
279                 } elseif ($n == 1) {
280                     $cells = array();
281                     $cells[] = '<label>' . get_string('wronganswerscore', 'lesson') . '</label>';
282                     $cells[] = $answer->score;
283                     $table->data[] = new html_table_row($cells);
285                     $cells = array();
286                     $cells[] = '<label>' . get_string('wronganswerjump', 'lesson') . '</label>';
287                     $cells[] = $this->get_jump_name($answer->jumpto);
288                     $table->data[] = new html_table_row($cells);
289                 }
291                 if ($n === 0){
292                     $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
293                 }
294                 $n++;
295                 $i--;
296             } else {
297                 $cells = array();
298                 if ($this->lesson->custom && $answer->score > 0) {
299                     // if the score is > 0, then it is correct
300                     $cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>';
301                 } else if ($this->lesson->custom) {
302                     $cells[] = '<label>' . get_string('answer', 'lesson') . ' ' . $i . '</label>';
303                 } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
304                     $cells[] = '<label class="correct">' . get_string('answer', 'lesson') . ' ' . $i . '</label>';
305                 } else {
306                     $cells[] = '<label>' . get_string('answer', 'lesson') . ' ' . $i . '</label>';
307                 }
308                 $cells[] = format_text($answer->answer, $answer->answerformat, $options);
309                 $table->data[] = new html_table_row($cells);
311                 $cells = array();
312                 $cells[] = '<label>' . get_string('matchesanswer', 'lesson') . ' ' . $i . '</label>';
313                 $cells[] = format_text($answer->response, $answer->responseformat, $options);
314                 $table->data[] = new html_table_row($cells);
315             }
316             $i++;
317         }
318         return $table;
319     }
320     /**
321      * Updates the page and its answers
322      *
323      * @global moodle_database $DB
324      * @global moodle_page $PAGE
325      * @param stdClass $properties
326      * @return bool
327      */
328     public function update($properties, $context = null, $maxbytes = null) {
329         global $DB, $PAGE;
330         $answers  = $this->get_answers();
331         $properties->id = $this->properties->id;
332         $properties->lessonid = $this->lesson->id;
333         $properties->timemodified = time();
334         $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);
335         $DB->update_record("lesson_pages", $properties);
337         // Trigger an event: page updated.
338         \mod_lesson\event\page_updated::create_from_lesson_page($this, $context)->trigger();
340         // need to add two to offset correct response and wrong response
341         $this->lesson->maxanswers += 2;
342         for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
343             if (!array_key_exists($i, $this->answers)) {
344                 $this->answers[$i] = new stdClass;
345                 $this->answers[$i]->lessonid = $this->lesson->id;
346                 $this->answers[$i]->pageid = $this->id;
347                 $this->answers[$i]->timecreated = $this->timecreated;
348             }
350             if (!empty($properties->answer_editor[$i]) && is_array($properties->answer_editor[$i])) {
351                 $this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
352                 $this->answers[$i]->answerformat = $properties->answer_editor[$i]['format'];
353             }
354             if (!empty($properties->response_editor[$i])) {
355                 $this->answers[$i]->response = $properties->response_editor[$i];
356                 $this->answers[$i]->responseformat = 0;
357             }
359             if (isset($properties->jumpto[$i])) {
360                 $this->answers[$i]->jumpto = $properties->jumpto[$i];
361             }
362             if ($this->lesson->custom && isset($properties->score[$i])) {
363                 $this->answers[$i]->score = $properties->score[$i];
364             }
366             // we don't need to check for isset here because properties called it's own isset method.
367             if ($this->answers[$i]->answer != '') {
368                 if (!isset($this->answers[$i]->id)) {
369                     $this->answers[$i]->id =  $DB->insert_record("lesson_answers", $this->answers[$i]);
370                 } else {
371                     $DB->update_record("lesson_answers", $this->answers[$i]->properties());
372                 }
373                 // Save files in answers (no response_editor for matching questions).
374                 $this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]);
375             } else if ($i < 2) {
376                 if (!isset($this->answers[$i]->id)) {
377                     $this->answers[$i]->id =  $DB->insert_record("lesson_answers", $this->answers[$i]);
378                 } else {
379                     $DB->update_record("lesson_answers", $this->answers[$i]->properties());
380                 }
382                 // Save files in answers (no response_editor for matching questions).
383                 $this->save_answers_files($context, $maxbytes, $this->answers[$i], $properties->answer_editor[$i]);
384             } else if (isset($this->answers[$i]->id)) {
385                 $DB->delete_records('lesson_answers', array('id'=>$this->answers[$i]->id));
386                 unset($this->answers[$i]);
387             }
388         }
389         return true;
390     }
391     public function stats(array &$pagestats, $tries) {
392         if(count($tries) > $this->lesson->maxattempts) { // if there are more tries than the max that is allowed, grab the last "legal" attempt
393             $temp = $tries[$this->lesson->maxattempts - 1];
394         } else {
395             // else, user attempted the question less than the max, so grab the last one
396             $temp = end($tries);
397         }
398         if ($temp->correct) {
399             if (isset($pagestats[$temp->pageid]["correct"])) {
400                 $pagestats[$temp->pageid]["correct"]++;
401             } else {
402                 $pagestats[$temp->pageid]["correct"] = 1;
403             }
404         }
405         if (isset($pagestats[$temp->pageid]["total"])) {
406             $pagestats[$temp->pageid]["total"]++;
407         } else {
408             $pagestats[$temp->pageid]["total"] = 1;
409         }
410         return true;
411     }
412     public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
413         $answers = array();
414         foreach ($this->get_answers() as $answer) {
415             $answers[$answer->id] = $answer;
416         }
417         $formattextdefoptions = new stdClass;
418         $formattextdefoptions->para = false;  //I'll use it widely in this page
419         foreach ($answers as $answer) {
420             if ($n == 0 && $useranswer != null && $useranswer->correct) {
421                 if ($answer->response == null && $useranswer != null) {
422                     $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
423                 } else {
424                     $answerdata->response = $answer->response;
425                 }
426                 if ($this->lesson->custom) {
427                     $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
428                 } else {
429                     $answerdata->score = get_string("receivedcredit", "lesson");
430                 }
431             } elseif ($n == 1 && $useranswer != null && !$useranswer->correct) {
432                 if ($answer->response == null && $useranswer != null) {
433                     $answerdata->response = get_string("thatsthewronganswer", "lesson");
434                 } else {
435                     $answerdata->response = $answer->response;
436                 }
437                 if ($this->lesson->custom) {
438                     $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
439                 } else {
440                     $answerdata->score = get_string("didnotreceivecredit", "lesson");
441                 }
442             } elseif ($n > 1) {
443                 $data = '<label class="accesshide" for="answer_' . $n . '">' . get_string('answer', 'lesson') . '</label>';
444                 $data .= strip_tags(format_string($answer->answer)) . ' ';
445                 if ($useranswer != null) {
446                     $userresponse = explode(",", $useranswer->useranswer);
447                     $data .= '<label class="accesshide" for="stu_answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
448                     $data .= "<select class=\"custom-select\" id=\"stu_answer_response_" . $n . "\" " .
449                              "disabled=\"disabled\"><option selected=\"selected\">";
450                     if (array_key_exists($i, $userresponse)) {
451                         $data .= $userresponse[$i];
452                     }
453                     $data .= "</option></select>";
454                 } else {
455                     $data .= '<label class="accesshide" for="answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
456                     $data .= "<select class=\"custom-select\" id=\"answer_response_" . $n . "\" " .
457                              "disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
458                 }
460                 if ($n == 2) {
461                     if (isset($pagestats[$this->properties->id])) {
462                         if (!array_key_exists('correct', $pagestats[$this->properties->id])) {
463                             $pagestats[$this->properties->id]["correct"] = 0;
464                         }
465                         $percent = $pagestats[$this->properties->id]["correct"] / $pagestats[$this->properties->id]["total"] * 100;
466                         $percent = round($percent, 2);
467                         $percent .= "% ".get_string("answeredcorrectly", "lesson");
468                     } else {
469                         $percent = get_string("nooneansweredthisquestion", "lesson");
470                     }
471                 } else {
472                     $percent = '';
473                 }
475                 $answerdata->answers[] = array($data, $percent);
476                 $i++;
477             }
478             $n++;
479             $answerpage->answerdata = $answerdata;
480         }
481         return $answerpage;
482     }
483     public function get_jumps() {
484         global $DB;
485         // The jumps for matching question type are stored in the 1st and 2nd answer record.
486         $jumps = array();
487         if ($answers = $DB->get_records("lesson_answers", array("lessonid" => $this->lesson->id, "pageid" => $this->properties->id), 'id', '*', 0, 2)) {
488             foreach ($answers as $answer) {
489                 $jumps[] = $this->get_jump_name($answer->jumpto);
490             }
491         } else {
492             $jumps[] = $this->get_jump_name($this->properties->nextpageid);
493         }
494         return $jumps;
495     }
498 class lesson_add_page_form_matching extends lesson_add_page_form_base {
500     public $qtype = 'matching';
501     public $qtypestring = 'matching';
502     protected $answerformat = LESSON_ANSWER_HTML;
503     protected $responseformat = '';
505     public function custom_definition() {
507         $this->_form->addElement('header', 'correctresponse', get_string('correctresponse', 'lesson'));
508         $this->_form->addElement('editor', 'answer_editor[0]', get_string('correctresponse', 'lesson'),
509                 array('rows' => '4', 'columns' => '80'),
510                 array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes']));
511         $this->_form->setType('answer_editor[0]', PARAM_RAW);
512         $this->_form->setDefault('answer_editor[0]', array('text' => '', 'format' => FORMAT_HTML));
513         $this->add_jumpto(0, get_string('correctanswerjump','lesson'), LESSON_NEXTPAGE);
514         $this->add_score(0, get_string("correctanswerscore", "lesson"), 1);
516         $this->_form->addElement('header', 'wrongresponse', get_string('wrongresponse', 'lesson'));
517         $this->_form->addElement('editor', 'answer_editor[1]', get_string('wrongresponse', 'lesson'),
518                 array('rows' => '4', 'columns' => '80'),
519                 array('noclean' => true, 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $this->_customdata['maxbytes']));
520         $this->_form->setType('answer_editor[1]', PARAM_RAW);
521         $this->_form->setDefault('answer_editor[1]', array('text' => '', 'format' => FORMAT_HTML));
523         $this->add_jumpto(1, get_string('wronganswerjump','lesson'), LESSON_THISPAGE);
524         $this->add_score(1, get_string("wronganswerscore", "lesson"), 0);
526         for ($i = 2; $i < $this->_customdata['lesson']->maxanswers+2; $i++) {
527             $this->_form->addElement('header', 'matchingpair'.($i-1), get_string('matchingpair', 'lesson', $i-1));
528             $this->add_answer($i, null, ($i < 4), LESSON_ANSWER_HTML);
529             $required = ($i < 4);
530             $label = get_string('matchesanswer','lesson');
531             $count = $i;
532             $this->_form->addElement('text', 'response_editor['.$count.']', $label, array('size'=>'50'));
533             $this->_form->setType('response_editor['.$count.']', PARAM_NOTAGS);
534             $this->_form->setDefault('response_editor['.$count.']', '');
535             if ($required) {
536                 $this->_form->addRule('response_editor['.$count.']', get_string('required'), 'required', null, 'client');
537             }
538         }
539     }
542 class lesson_display_answer_form_matching extends moodleform {
544     public function definition() {
545         global $USER, $OUTPUT, $PAGE;
546         $mform = $this->_form;
547         $answers = $this->_customdata['answers'];
548         $useranswers = $this->_customdata['useranswers'];
549         $responseoptions = $this->_customdata['responseoptions'];
550         $lessonid = $this->_customdata['lessonid'];
551         $contents = $this->_customdata['contents'];
553         // Disable shortforms.
554         $mform->setDisableShortforms();
556         $mform->addElement('header', 'pageheader');
558         $mform->addElement('html', $OUTPUT->container($contents, 'contents'));
560         $hasattempt = false;
561         $disabled = '';
562         if (isset($useranswers) && !empty($useranswers)) {
563             $hasattempt = true;
564             $disabled = array('disabled' => 'disabled');
565         }
567         $options = new stdClass;
568         $options->para = false;
569         $options->noclean = true;
571         $mform->addElement('hidden', 'id');
572         $mform->setType('id', PARAM_INT);
574         $mform->addElement('hidden', 'pageid');
575         $mform->setType('pageid', PARAM_INT);
577         $i = 0;
579         foreach ($answers as $answer) {
580             $mform->addElement('html', '<div class="answeroption">');
581             if ($answer->response != null) {
582                 $responseid = 'response['.$answer->id.']';
583                 if ($hasattempt) {
584                     $responseid = 'response_'.$answer->id;
585                     $mform->addElement('hidden', 'response['.$answer->id.']', htmlspecialchars($useranswers[$i]));
586                     // Temporary fixed until MDL-38885 gets integrated
587                     $mform->setType('response', PARAM_TEXT);
588                 }
589                 $answer = lesson_page_type_matching::rewrite_answers_urls($answer);
590                 $mform->addElement('select', $responseid, format_text($answer->answer,$answer->answerformat,$options), $responseoptions, $disabled);
591                 $mform->setType($responseid, PARAM_TEXT);
592                 if ($hasattempt) {
593                     $mform->setDefault($responseid, htmlspecialchars(trim($useranswers[$i])));
594                 } else {
595                     $mform->setDefault($responseid, 'answeroption');
596                 }
597             }
598             $mform->addElement('html', '</div>');
599             $i++;
600         }
601         if ($hasattempt) {
602             $this->add_action_buttons(null, get_string("nextpage", "lesson"));
603         } else {
604             $this->add_action_buttons(null, get_string("submit", "lesson"));
605         }
606     }