MDL-51704 survey: Fix display problems in report page
[moodle.git] / mod / survey / report.php
CommitLineData
a5cb6242 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 * This file is responsible for producing the survey reports
20 *
da9bddbe 21 * @package mod_survey
a5cb6242 22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
f9903ed0 25
b0e3a925 26 require_once("../../config.php");
27 require_once("lib.php");
f9903ed0 28
29// Check that all the parameters have been provided.
ec81373f 30
8f414ab8 31 $id = required_param('id', PARAM_INT); // Course Module ID
32 $action = optional_param('action', '', PARAM_ALPHA); // What to look at
031c8ae5 33 $qid = optional_param('qid', 0, PARAM_RAW); // Question IDs comma-separated list
8f414ab8 34 $student = optional_param('student', 0, PARAM_INT); // Student ID
35 $notes = optional_param('notes', '', PARAM_RAW); // Save teachers notes
f9903ed0 36
031c8ae5 37 $qids = explode(',', $qid);
ea4721f0 38 $qids = clean_param_array($qids, PARAM_INT);
031c8ae5 39 $qid = implode (',', $qids);
40
f9d5371b 41 if (! $cm = get_coursemodule_from_id('survey', $id)) {
83b10e57 42 print_error('invalidcoursemodule');
f9903ed0 43 }
44
deb3a60d 45 if (! $course = $DB->get_record("course", array("id"=>$cm->course))) {
83b10e57 46 print_error('coursemisconf');
f9903ed0 47 }
48
a6855934 49 $url = new moodle_url('/mod/survey/report.php', array('id'=>$id));
a5cb6242 50 if ($action !== '') {
51 $url->param('action', $action);
52 }
53 if ($qid !== 0) {
54 $url->param('qid', $qid);
55 }
56 if ($student !== 0) {
57 $url->param('student', $student);
58 }
59 if ($notes !== '') {
60 $url->param('notes', $notes);
61 }
62 $PAGE->set_url($url);
63
cdbea7ee 64 require_login($course, false, $cm);
e5dd8e3b 65
1c969116 66 $context = context_module::instance($cm->id);
0468976c 67
68 require_capability('mod/survey:readresponses', $context);
f9903ed0 69
deb3a60d 70 if (! $survey = $DB->get_record("survey", array("id"=>$cm->instance))) {
83b10e57 71 print_error('invalidsurveyid', 'survey');
f9903ed0 72 }
73
deb3a60d 74 if (! $template = $DB->get_record("survey", array("id"=>$survey->template))) {
83b10e57 75 print_error('invalidtmptid', 'survey');
7c8c335f 76 }
77
78 $showscales = ($template->name != 'ciqname');
f9903ed0 79
f9903ed0 80
e137027f 81 $strreport = get_string("report", "survey");
7c8c335f 82 $strsurvey = get_string("modulename", "survey");
e137027f 83 $strsurveys = get_string("modulenameplural", "survey");
84 $strsummary = get_string("summary", "survey");
85 $strscales = get_string("scales", "survey");
86 $strquestion = get_string("question", "survey");
87 $strquestions = get_string("questions", "survey");
88 $strdownload = get_string("download", "survey");
89 $strallscales = get_string("allscales", "survey");
90 $strallquestions = get_string("allquestions", "survey");
91 $strselectedquestions = get_string("selectedquestions", "survey");
92 $strseemoredetail = get_string("seemoredetail", "survey");
93 $strnotes = get_string("notes", "survey");
94
a5cb6242 95 switch ($action) {
96 case 'download':
97 $PAGE->navbar->add(get_string('downloadresults', 'survey'));
98 break;
99 case 'summary':
100 case 'scales':
101 case 'questions':
102 $PAGE->navbar->add($strreport);
103 $PAGE->navbar->add(${'str'.$action});
104 break;
105 case 'students':
106 $PAGE->navbar->add($strreport);
107 $PAGE->navbar->add(get_string('participants'));
108 break;
109 case '':
110 $PAGE->navbar->add($strreport);
111 $PAGE->navbar->add($strsummary);
112 break;
113 default:
114 $PAGE->navbar->add($strreport);
115 break;
116 }
c0549ef5 117
118 $PAGE->set_title("$course->shortname: ".format_string($survey->name));
119 $PAGE->set_heading($course->fullname);
c0549ef5 120 echo $OUTPUT->header();
5def24cf 121 echo $OUTPUT->heading($survey->name);
7c8c335f 122
a9ccbf60 123/// Check to see if groups are being used in this survey
07aa2057 124 if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
a30f997e 125 $menuaction = $action == "student" ? "students" : $action;
07aa2057 126 $currentgroup = groups_get_activity_group($cm, true);
f1035deb 127 groups_print_activity_menu($cm, $CFG->wwwroot . "/mod/survey/report.php?id=$cm->id&amp;action=$menuaction&amp;qid=$qid");
a9ccbf60 128 } else {
129 $currentgroup = 0;
130 }
131
9e176aa2
RT
132 $params = array(
133 'objectid' => $survey->id,
134 'context' => $context,
135 'courseid' => $course->id,
136 'relateduserid' => $student,
137 'other' => array('action' => $action, 'groupid' => $currentgroup)
138 );
139 $event = \mod_survey\event\report_viewed::create($params);
140 $event->trigger();
141
a9ccbf60 142 if ($currentgroup) {
3c268f25 143 $users = get_users_by_capability($context, 'mod/survey:participate', '', '', '', '', $currentgroup, null, false);
98da6021 144 } else if (!empty($cm->groupingid)) {
3c268f25 145 $groups = groups_get_all_groups($courseid, 0, $cm->groupingid);
146 $groups = array_keys($groups);
147 $users = get_users_by_capability($context, 'mod/survey:participate', '', '', '', '', $groups, null, false);
a9ccbf60 148 } else {
3c268f25 149 $users = get_users_by_capability($context, 'mod/survey:participate', '', '', '', '', '', null, false);
150 $group = false;
a9ccbf60 151 }
3c268f25 152
07aa2057 153 $groupingid = $cm->groupingid;
e5dd8e3b 154
8b1b0382 155 echo $OUTPUT->box_start("generalbox boxaligncenter");
7c8c335f 156 if ($showscales) {
839f2456 157 echo "<a href=\"report.php?action=summary&amp;id=$id\">$strsummary</a>";
158 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=scales&amp;id=$id\">$strscales</a>";
159 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=questions&amp;id=$id\">$strquestions</a>";
9101efd3 160 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=students&amp;id=$id\">".get_string('participants')."</a>";
0468976c 161 if (has_capability('mod/survey:download', $context)) {
bee7ee38 162 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=download&amp;id=$id\">$strdownload</a>";
bbbf2d40 163 }
a9ccbf60 164 if (empty($action)) {
165 $action = "summary";
166 }
7c8c335f 167 } else {
839f2456 168 echo "<a href=\"report.php?action=questions&amp;id=$id\">$strquestions</a>";
9101efd3 169 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=students&amp;id=$id\">".get_string('participants')."</a>";
0468976c 170 if (has_capability('mod/survey:download', $context)) {
bee7ee38 171 echo "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"report.php?action=download&amp;id=$id\">$strdownload</a>";
172 }
a9ccbf60 173 if (empty($action)) {
174 $action = "questions";
175 }
f9903ed0 176 }
8b1b0382 177 echo $OUTPUT->box_end();
f9903ed0 178
1ba862ec 179 echo $OUTPUT->spacer(array('height'=>30, 'width'=>30, 'br'=>true)); // should be done with CSS instead
e137027f 180
7c8c335f 181
182/// Print the menu across the top
183
8f414ab8 184 $virtualscales = false;
185
7c8c335f 186 switch ($action) {
f9903ed0 187
188 case "summary":
5def24cf 189 echo $OUTPUT->heading($strsummary, 3);
f9903ed0 190
07aa2057 191 if (survey_count_responses($survey->id, $currentgroup, $groupingid)) {
795b56b3 192 echo "<div class='reportsummary'><a href=\"report.php?action=scales&amp;id=$id\">";
839f2456 193 survey_print_graph("id=$id&amp;group=$currentgroup&amp;type=overall.png");
795b56b3 194 echo "</a></div>";
77f4a40b 195 } else {
8b1b0382 196 echo $OUTPUT->notification(get_string("nobodyyet","survey"));
77f4a40b 197 }
f9903ed0 198 break;
199
200 case "scales":
5def24cf 201 echo $OUTPUT->heading($strscales, 3);
f9903ed0 202
07aa2057 203 if (! $results = survey_get_responses($survey->id, $currentgroup, $groupingid) ) {
8b1b0382 204 echo $OUTPUT->notification(get_string("nobodyyet","survey"));
f9903ed0 205
ba163fa2 206 } else {
f9903ed0 207
44e1b7d7 208 $questions = $DB->get_records_list("survey_questions", "id", explode(',', $survey->questions));
ba163fa2 209 $questionorder = explode(",", $survey->questions);
210
211 foreach ($questionorder as $key => $val) {
212 $question = $questions[$val];
213 if ($question->type < 0) { // We have some virtual scales. Just show them.
214 $virtualscales = true;
215 break;
f9903ed0 216 }
ba163fa2 217 }
218
219 foreach ($questionorder as $key => $val) {
220 $question = $questions[$val];
221 if ($question->multi) {
222fd710 222 if (!empty($virtualscales) && $question->type > 0) { // Don't show non-virtual scales if virtual
ba163fa2 223 continue;
224 }
795b56b3 225 echo "<p class=\"centerpara\"><a title=\"$strseemoredetail\" href=\"report.php?action=questions&amp;id=$id&amp;qid=$question->multi\">";
839f2456 226 survey_print_graph("id=$id&amp;qid=$question->id&amp;group=$currentgroup&amp;type=multiquestion.png");
a9ef4a63 227 echo "</a></p><br />";
ec81373f 228 }
ba163fa2 229 }
f9903ed0 230 }
231
f9903ed0 232 break;
233
234 case "questions":
f9903ed0 235
236 if ($qid) { // just get one multi-question
b83bdae6 237 $questions = $DB->get_records_select("survey_questions", "id in ($qid)");
f9903ed0 238 $questionorder = explode(",", $qid);
239
deb3a60d 240 if ($scale = $DB->get_records("survey_questions", array("multi"=>$qid))) {
000331f5 241 $scale = array_pop($scale);
5def24cf 242 echo $OUTPUT->heading("$scale->text - $strselectedquestions", 3);
57f14b34 243 } else {
5def24cf 244 echo $OUTPUT->heading($strselectedquestions, 3);
000331f5 245 }
246
f9903ed0 247 } else { // get all top-level questions
44e1b7d7 248 $questions = $DB->get_records_list("survey_questions", "id", explode(',',$survey->questions));
f9903ed0 249 $questionorder = explode(",", $survey->questions);
250
5def24cf 251 echo $OUTPUT->heading($strallquestions, 3);
f9903ed0 252 }
253
07aa2057 254 if (! $results = survey_get_responses($survey->id, $currentgroup, $groupingid) ) {
8b1b0382 255 echo $OUTPUT->notification(get_string("nobodyyet","survey"));
f9903ed0 256
ba163fa2 257 } else {
f9903ed0 258
ba163fa2 259 foreach ($questionorder as $key => $val) {
260 $question = $questions[$val];
261 if ($question->type < 0) { // We have some virtual scales. DON'T show them.
262 $virtualscales = true;
263 break;
f9903ed0 264 }
ba163fa2 265 }
607809b3 266
ba163fa2 267 foreach ($questionorder as $key => $val) {
268 $question = $questions[$val];
269
270 if ($question->type < 0) { // We have some virtual scales. DON'T show them.
271 continue;
f9903ed0 272 }
ba163fa2 273 $question->text = get_string($question->text, "survey");
274
275 if ($question->multi) {
5def24cf 276 echo $OUTPUT->heading($question->text . ':', 4);
ba163fa2 277
f772b515
JL
278 $subquestions = survey_get_subquestions($question);
279 foreach ($subquestions as $subquestion) {
ba163fa2 280 if ($subquestion->type > 0) {
795b56b3 281 echo "<p class=\"centerpara\">";
839f2456 282 echo "<a title=\"$strseemoredetail\" href=\"report.php?action=question&amp;id=$id&amp;qid=$subquestion->id\">";
283 survey_print_graph("id=$id&amp;qid=$subquestion->id&amp;group=$currentgroup&amp;type=question.png");
ba163fa2 284 echo "</a></p>";
285 }
286 }
287 } else if ($question->type > 0 ) {
795b56b3 288 echo "<p class=\"centerpara\">";
839f2456 289 echo "<a title=\"$strseemoredetail\" href=\"report.php?action=question&amp;id=$id&amp;qid=$question->id\">";
290 survey_print_graph("id=$id&amp;qid=$question->id&amp;group=$currentgroup&amp;type=question.png");
ba163fa2 291 echo "</a></p>";
292
293 } else {
02f78a26 294 $table = new html_table();
ba163fa2 295 $table->head = array($question->text);
296 $table->align = array ("left");
297
298 $contents = '<table cellpadding="15" width="100%">';
299
300 if ($aaa = survey_get_user_answers($survey->id, $question->id, $currentgroup, "sa.time ASC")) {
301 foreach ($aaa as $a) {
302 $contents .= "<tr>";
795b56b3 303 $contents .= '<td class="fullnamecell">'.fullname($a).'</td>';
45ae1da2 304 $contents .= '<td valign="top">'.s($a->answer1).'</td>';
ba163fa2 305 $contents .= "</tr>";
306 }
307 }
308 $contents .= "</table>";
7c8c335f 309
ba163fa2 310 $table->data[] = array($contents);
7c8c335f 311
16be8974 312 echo html_writer::table($table);
e5dd8e3b 313
1ba862ec 314 echo $OUTPUT->spacer(array('height'=>30)); // should be done with CSS instead
ba163fa2 315 }
f9903ed0 316 }
317 }
318
f9903ed0 319 break;
320
321 case "question":
deb3a60d 322 if (!$question = $DB->get_record("survey_questions", array("id"=>$qid))) {
83b10e57 323 print_error('cannotfindquestion', 'survey');
f9903ed0 324 }
b6c38cec 325 $question->text = get_string($question->text, "survey");
f9903ed0 326
f762c448 327 $answers = explode(",", get_string($question->options, "survey"));
f9903ed0 328
5def24cf 329 echo $OUTPUT->heading("$strquestion: $question->text", 3);
f9903ed0 330
f9903ed0 331
e137027f 332 $strname = get_string("name", "survey");
333 $strtime = get_string("time", "survey");
334 $stractual = get_string("actual", "survey");
335 $strpreferred = get_string("preferred", "survey");
dcde9f02 336 $strdateformat = get_string("strftimedatetime");
e137027f 337
02f78a26 338 $table = new html_table();
7c8c335f 339 $table->head = array("", $strname, $strtime, $stractual, $strpreferred);
340 $table->align = array ("left", "left", "left", "left", "right");
341 $table->size = array (35, "", "", "", "");
f9903ed0 342
a9ccbf60 343 if ($aaa = survey_get_user_answers($survey->id, $question->id, $currentgroup)) {
e323955d 344 foreach ($aaa as $a) {
e323955d 345 if ($a->answer1) {
7c8c335f 346 $answer1 = "$a->answer1 - ".$answers[$a->answer1 - 1];
e323955d 347 } else {
7c8c335f 348 $answer1 = "&nbsp;";
e323955d 349 }
e323955d 350 if ($a->answer2) {
7c8c335f 351 $answer2 = "$a->answer2 - ".$answers[$a->answer2 - 1];
e323955d 352 } else {
7c8c335f 353 $answer2 = "&nbsp;";
e323955d 354 }
7c8c335f 355 $table->data[] = array(
812dbaf7 356 $OUTPUT->user_picture($a, array('courseid'=>$course->id)),
839f2456 357 "<a href=\"report.php?id=$id&amp;action=student&amp;student=$a->userid\">".fullname($a)."</a>",
ec81373f 358 userdate($a->time),
31d0bf81 359 s($answer1), s($answer2));
ec81373f 360
e323955d 361 }
f9903ed0 362 }
f9903ed0 363
16be8974 364 echo html_writer::table($table);
f9903ed0 365
f9903ed0 366 break;
367
368 case "students":
369
5def24cf 370 echo $OUTPUT->heading(get_string("analysisof", "survey", get_string('participants')), 3);
ec81373f 371
07aa2057 372 if (! $results = survey_get_responses($survey->id, $currentgroup, $groupingid) ) {
8b1b0382 373 echo $OUTPUT->notification(get_string("nobodyyet","survey"));
f9903ed0 374 } else {
7c8c335f 375 survey_print_all_responses($cm->id, $results, $course->id);
f9903ed0 376 }
377
f9903ed0 378 break;
379
380 case "student":
deb3a60d 381 if (!$user = $DB->get_record("user", array("id"=>$student))) {
83b10e57 382 print_error('invaliduserid');
f9903ed0 383 }
384
5def24cf 385 echo $OUTPUT->heading(get_string("analysisof", "survey", fullname($user)), 3);
f9903ed0 386
8f414ab8 387 if ($notes != '' and confirm_sesskey()) {
551b0b98 388 if (survey_get_analysis($survey->id, $user->id)) {
389 if (! survey_update_analysis($survey->id, $user->id, $notes)) {
8b1b0382 390 echo $OUTPUT->notification("An error occurred while saving your notes. Sorry.");
e137027f 391 } else {
8b1b0382 392 echo $OUTPUT->notification(get_string("savednotes", "survey"));
f9903ed0 393 }
394 } else {
551b0b98 395 if (! survey_add_analysis($survey->id, $user->id, $notes)) {
8b1b0382 396 echo $OUTPUT->notification("An error occurred while saving your notes. Sorry.");
e137027f 397 } else {
8b1b0382 398 echo $OUTPUT->notification(get_string("savednotes", "survey"));
f9903ed0 399 }
400 }
f9903ed0 401 }
402
156de651 403 echo "<p class=\"centerpara\">";
812dbaf7 404 echo $OUTPUT->user_picture($user, array('courseid'=>$course->id));
7c8c335f 405 echo "</p>";
f9903ed0 406
44e1b7d7 407 $questions = $DB->get_records_list("survey_questions", "id", explode(',', $survey->questions));
f9903ed0 408 $questionorder = explode(",", $survey->questions);
7c8c335f 409
410 if ($showscales) {
411 // Print overall summary
156de651 412 echo "<p class=\"centerpara\">";
839f2456 413 survey_print_graph("id=$id&amp;sid=$student&amp;type=student.png");
7c8c335f 414 echo "</p>";
ec81373f 415
7c8c335f 416 // Print scales
ec81373f 417
7c8c335f 418 foreach ($questionorder as $key => $val) {
419 $question = $questions[$val];
420 if ($question->type < 0) { // We have some virtual scales. Just show them.
421 $virtualscales = true;
422 break;
423 }
f9903ed0 424 }
ec81373f 425
7c8c335f 426 foreach ($questionorder as $key => $val) {
427 $question = $questions[$val];
428 if ($question->multi) {
429 if ($virtualscales && $question->type > 0) { // Don't show non-virtual scales if virtual
430 continue;
431 }
795b56b3 432 echo "<p class=\"centerpara\">";
839f2456 433 echo "<a title=\"$strseemoredetail\" href=\"report.php?action=questions&amp;id=$id&amp;qid=$question->multi\">";
434 survey_print_graph("id=$id&amp;qid=$question->id&amp;sid=$student&amp;type=studentmultiquestion.png");
a9ef4a63 435 echo "</a></p><br />";
ec81373f 436 }
7c8c335f 437 }
ec81373f 438 }
7c8c335f 439
440 // Print non-scale questions
441
f9903ed0 442 foreach ($questionorder as $key => $val) {
443 $question = $questions[$val];
7c8c335f 444 if ($question->type == 0 or $question->type == 1) {
445 if ($answer = survey_get_user_answer($survey->id, $question->id, $user->id)) {
02f78a26 446 $table = new html_table();
7c8c335f 447 $table->head = array(get_string($question->text, "survey"));
448 $table->align = array ("left");
156de651
DP
449 if (!empty($question->options) && $answer->answer1 > 0) {
450 $answers = explode(',', get_string($question->options, 'survey'));
451 if ($answer->answer1 <= count($answers)) {
452 $table->data[] = array(s($answers[$answer->answer1 - 1])); // No html here, just plain text.
453 } else {
454 $table->data[] = array(s($answer->answer1)); // No html here, just plain text.
455 }
456 } else {
457 $table->data[] = array(s($answer->answer1)); // No html here, just plain text.
458 }
16be8974 459 echo html_writer::table($table);
b83bdae6 460 echo $OUTPUT->spacer(array('height'=>30));
f9903ed0 461 }
7c8c335f 462 }
f9903ed0 463 }
464
551b0b98 465 if ($rs = survey_get_analysis($survey->id, $user->id)) {
f9903ed0 466 $notes = $rs->notes;
467 } else {
468 $notes = "";
469 }
d1290cec 470 echo "<hr noshade=\"noshade\" size=\"1\" />";
795b56b3 471 echo "<div class='studentreport'>";
b7dc2256 472 echo "<form action=\"report.php\" method=\"post\">";
7c8c335f 473 echo "<h3>$strnotes:</h3>";
474 echo "<blockquote>";
d1290cec 475 echo "<textarea name=\"notes\" rows=\"10\" cols=\"60\">";
f9903ed0 476 p($notes);
a9ef4a63 477 echo "</textarea><br />";
d1290cec 478 echo "<input type=\"hidden\" name=\"action\" value=\"student\" />";
8f414ab8 479 echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />";
d1290cec 480 echo "<input type=\"hidden\" name=\"student\" value=\"$student\" />";
481 echo "<input type=\"hidden\" name=\"id\" value=\"$cm->id\" />";
482 echo "<input type=\"submit\" value=\"".get_string("savechanges")."\" />";
7c8c335f 483 echo "</blockquote>";
484 echo "</form>";
795b56b3 485 echo "</div>";
ec81373f 486
f9903ed0 487
f9903ed0 488 break;
489
490 case "download":
5def24cf 491 echo $OUTPUT->heading($strdownload, 3);
e137027f 492
0468976c 493 require_capability('mod/survey:download', $context);
494
4257ae82
AD
495 $numusers = survey_count_responses($survey->id, $currentgroup, $groupingid);
496 if ($numusers > 0) {
497 echo html_writer::tag('p', get_string("downloadinfo", "survey"), array('class' => 'centerpara'));
f9903ed0 498
4257ae82
AD
499 echo $OUTPUT->container_start('reportbuttons');
500 $options = array();
501 $options["id"] = "$cm->id";
502 $options["group"] = $currentgroup;
d81b7ffb 503
4257ae82
AD
504 $options["type"] = "ods";
505 echo $OUTPUT->single_button(new moodle_url("download.php", $options), get_string("downloadods"));
d81b7ffb 506
4257ae82
AD
507 $options["type"] = "xls";
508 echo $OUTPUT->single_button(new moodle_url("download.php", $options), get_string("downloadexcel"));
f9903ed0 509
4257ae82
AD
510 $options["type"] = "txt";
511 echo $OUTPUT->single_button(new moodle_url("download.php", $options), get_string("downloadtext"));
512 echo $OUTPUT->container_end();
513
514 } else {
515 echo html_writer::tag('p', get_string("nobodyyet", "survey"), array('class' => 'centerpara'));
516 }
ec81373f 517
f9903ed0 518 break;
519
520 }
641f16ba 521 echo $OUTPUT->footer();
e5dd8e3b 522