MDL-68784 scorm: Removed limit on allowed scorm users in report
[moodle.git] / mod / scorm / report / interactions / classes / report.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Core Report class of basic reporting plugin
18  * @package   scormreport
19  * @subpackage interactions
20  * @author    Dan Marsden and Ankit Kumar Agarwal
21  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
24 namespace scormreport_interactions;
26 defined('MOODLE_INTERNAL') || die();
28 require_once($CFG->dirroot.'/mod/scorm/report/interactions/responsessettings_form.php');
30 class report extends \mod_scorm\report {
31     /**
32      * displays the full report
33      * @param \stdClass $scorm full SCORM object
34      * @param \stdClass $cm - full course_module object
35      * @param \stdClass $course - full course object
36      * @param string $download - type of download being requested
37      */
38     public function display($scorm, $cm, $course, $download) {
39         global $CFG, $DB, $OUTPUT, $PAGE;
41         $contextmodule = \context_module::instance($cm->id);
42         $action = optional_param('action', '', PARAM_ALPHA);
43         $attemptids = optional_param_array('attemptid', array(), PARAM_RAW);
44         $attemptsmode = optional_param('attemptsmode', SCORM_REPORT_ATTEMPTS_ALL_STUDENTS, PARAM_INT);
45         $PAGE->set_url(new \moodle_url($PAGE->url, array('attemptsmode' => $attemptsmode)));
47         if ($action == 'delete' && has_capability('mod/scorm:deleteresponses', $contextmodule) && confirm_sesskey()) {
48             if (scorm_delete_responses($attemptids, $scorm)) { // Delete responses.
49                 echo $OUTPUT->notification(get_string('scormresponsedeleted', 'scorm'), 'notifysuccess');
50             }
51         }
52         // Find out current groups mode.
53         $currentgroup = groups_get_activity_group($cm, true);
55         // Detailed report.
56         $mform = new \mod_scorm_report_interactions_settings($PAGE->url, compact('currentgroup'));
57         if ($fromform = $mform->get_data()) {
58             $pagesize = $fromform->pagesize;
59             $includeqtext = $fromform->qtext;
60             $includeresp = $fromform->resp;
61             $includeright = $fromform->right;
62             $includeresult = $fromform->result;
63             set_user_preference('scorm_report_pagesize', $pagesize);
64             set_user_preference('scorm_report_interactions_qtext', $includeqtext);
65             set_user_preference('scorm_report_interactions_resp', $includeresp);
66             set_user_preference('scorm_report_interactions_right', $includeright);
67             set_user_preference('scorm_report_interactions_result', $includeresult);
68         } else {
69             $pagesize = get_user_preferences('scorm_report_pagesize', 0);
70             $includeqtext = get_user_preferences('scorm_report_interactions_qtext', 0);
71             $includeresp = get_user_preferences('scorm_report_interactions_resp', 1);
72             $includeright = get_user_preferences('scorm_report_interactions_right', 0);
73             $includeresult = get_user_preferences('scorm_report_interactions_result', 0);
74         }
75         if ($pagesize < 1) {
76             $pagesize = SCORM_REPORT_DEFAULT_PAGE_SIZE;
77         }
79         // Select group menu.
80         $displayoptions = array();
81         $displayoptions['attemptsmode'] = $attemptsmode;
82         $displayoptions['qtext'] = $includeqtext;
83         $displayoptions['resp'] = $includeresp;
84         $displayoptions['right'] = $includeright;
85         $displayoptions['result'] = $includeresult;
87         $mform->set_data($displayoptions + array('pagesize' => $pagesize));
88         if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being used.
89             if (!$download) {
90                 groups_print_activity_menu($cm, new \moodle_url($PAGE->url, $displayoptions));
91             }
92         }
93         $formattextoptions = array('context' => \context_course::instance($course->id));
95         // We only want to show the checkbox to delete attempts
96         // if the user has permissions and if the report mode is showing attempts.
97         $candelete = has_capability('mod/scorm:deleteresponses', $contextmodule)
98                 && ($attemptsmode != SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
99         // Select the students.
100         $nostudents = false;
101         list($allowedlistsql, $params) = get_enrolled_sql($contextmodule, 'mod/scorm:savetrack', (int) $currentgroup);
102         if (empty($currentgroup)) {
103             // All users who can attempt scoes.
104             if (!$DB->record_exists_sql($allowedlistsql, $params)) {
105                 echo $OUTPUT->notification(get_string('nostudentsyet'));
106                 $nostudents = true;
107             }
108         } else {
109             // All users who can attempt scoes and who are in the currently selected group.
110             if (!$DB->record_exists_sql($allowedlistsql, $params)) {
111                 echo $OUTPUT->notification(get_string('nostudentsingroup'));
112                 $nostudents = true;
113             }
114         }
115         if ( !$nostudents ) {
116             // Now check if asked download of data.
117             $coursecontext = \context_course::instance($course->id);
118             if ($download) {
119                 $filename = clean_filename("$course->shortname ".format_string($scorm->name, true, $formattextoptions));
120             }
122             // Define table columns.
123             $columns = array();
124             $headers = array();
125             if (!$download && $candelete) {
126                 $columns[] = 'checkbox';
127                 $headers[] = $this->generate_master_checkbox();
128             }
129             if (!$download && $CFG->grade_report_showuserimage) {
130                 $columns[] = 'picture';
131                 $headers[] = '';
132             }
133             $columns[] = 'fullname';
134             $headers[] = get_string('name');
136             $extrafields = get_extra_user_fields($coursecontext);
137             foreach ($extrafields as $field) {
138                 $columns[] = $field;
139                 $headers[] = get_user_field_name($field);
140             }
141             $columns[] = 'attempt';
142             $headers[] = get_string('attempt', 'scorm');
143             $columns[] = 'start';
144             $headers[] = get_string('started', 'scorm');
145             $columns[] = 'finish';
146             $headers[] = get_string('last', 'scorm');
147             $columns[] = 'score';
148             $headers[] = get_string('score', 'scorm');
149             $scoes = $DB->get_records('scorm_scoes', array("scorm" => $scorm->id), 'sortorder, id');
150             foreach ($scoes as $sco) {
151                 if ($sco->launch != '') {
152                     $columns[] = 'scograde'.$sco->id;
153                     $headers[] = format_string($sco->title, '', $formattextoptions);
154                 }
155             }
157             // Construct the SQL.
158             $select = 'SELECT DISTINCT '.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').' AS uniqueid, ';
159             $select .= 'st.scormid AS scormid, st.attempt AS attempt, ' .
160                     \user_picture::fields('u', array('idnumber'), 'userid') .
161                     get_extra_user_fields_sql($coursecontext, 'u', '', array('email', 'idnumber')) . ' ';
163             // This part is the same for all cases - join users and scorm_scoes_track tables.
164             $from = 'FROM {user} u ';
165             $from .= 'LEFT JOIN {scorm_scoes_track} st ON st.userid = u.id AND st.scormid = '.$scorm->id;
166             switch ($attemptsmode) {
167                 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH:
168                     // Show only students with attempts.
169                     $where = " WHERE u.id IN ({$allowedlistsql}) AND st.userid IS NOT NULL";
170                     break;
171                 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
172                     // Show only students without attempts.
173                     $where = " WHERE u.id IN ({$allowedlistsql}) AND st.userid IS NULL";
174                     break;
175                 case SCORM_REPORT_ATTEMPTS_ALL_STUDENTS:
176                     // Show all students with or without attempts.
177                     $where = " WHERE u.id IN ({$allowedlistsql}) AND (st.userid IS NOT NULL OR st.userid IS NULL)";
178                     break;
179             }
181             $countsql = 'SELECT COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').')) AS nbresults, ';
182             $countsql .= 'COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'st.attempt').')) AS nbattempts, ';
183             $countsql .= 'COUNT(DISTINCT(u.id)) AS nbusers ';
184             $countsql .= $from.$where;
185             $questioncount = get_scorm_question_count($scorm->id);
186             $nbmaincolumns = count($columns);
187             for ($id = 0; $id < $questioncount; $id++) {
188                 if ($displayoptions['qtext']) {
189                     $columns[] = 'question' . $id;
190                     $headers[] = get_string('questionx', 'scormreport_interactions', $id);
191                 }
192                 if ($displayoptions['resp']) {
193                     $columns[] = 'response' . $id;
194                     $headers[] = get_string('responsex', 'scormreport_interactions', $id);
195                 }
196                 if ($displayoptions['right']) {
197                     $columns[] = 'right' . $id;
198                     $headers[] = get_string('rightanswerx', 'scormreport_interactions', $id);
199                 }
200                 if ($displayoptions['result']) {
201                     $columns[] = 'result' . $id;
202                     $headers[] = get_string('resultx', 'scormreport_interactions', $id);
203                 }
204             }
206             if (!$download) {
207                 $table = new \flexible_table('mod-scorm-report');
209                 $table->define_columns($columns);
210                 $table->define_headers($headers);
211                 $table->define_baseurl($PAGE->url);
213                 $table->sortable(true);
214                 $table->collapsible(true);
216                 // This is done to prevent redundant data, when a user has multiple attempts.
217                 $table->column_suppress('picture');
218                 $table->column_suppress('fullname');
219                 foreach ($extrafields as $field) {
220                     $table->column_suppress($field);
221                 }
223                 $table->no_sorting('start');
224                 $table->no_sorting('finish');
225                 $table->no_sorting('score');
226                 $table->no_sorting('checkbox');
227                 $table->no_sorting('picture');
229                 for ($id = 0; $id < $questioncount; $id++) {
230                     if ($displayoptions['qtext']) {
231                         $table->no_sorting('question'.$id);
232                     }
233                     if ($displayoptions['resp']) {
234                         $table->no_sorting('response'.$id);
235                     }
236                     if ($displayoptions['right']) {
237                         $table->no_sorting('right'.$id);
238                     }
239                     if ($displayoptions['result']) {
240                         $table->no_sorting('result'.$id);
241                     }
242                 }
244                 foreach ($scoes as $sco) {
245                     if ($sco->launch != '') {
246                         $table->no_sorting('scograde'.$sco->id);
247                     }
248                 }
250                 $table->column_class('picture', 'picture');
251                 $table->column_class('fullname', 'bold');
252                 $table->column_class('score', 'bold');
254                 $table->set_attribute('cellspacing', '0');
255                 $table->set_attribute('id', 'attempts');
256                 $table->set_attribute('class', 'generaltable generalbox');
258                 // Start working -- this is necessary as soon as the niceties are over.
259                 $table->setup();
260             } else if ($download == 'ODS') {
261                 require_once("$CFG->libdir/odslib.class.php");
263                 $filename .= ".ods";
264                 // Creating a workbook.
265                 $workbook = new \MoodleODSWorkbook("-");
266                 // Sending HTTP headers.
267                 $workbook->send($filename);
268                 // Creating the first worksheet.
269                 $sheettitle = get_string('report', 'scorm');
270                 $myxls = $workbook->add_worksheet($sheettitle);
271                 // Format types.
272                 $format = $workbook->add_format();
273                 $format->set_bold(0);
274                 $formatbc = $workbook->add_format();
275                 $formatbc->set_bold(1);
276                 $formatbc->set_align('center');
277                 $formatb = $workbook->add_format();
278                 $formatb->set_bold(1);
279                 $formaty = $workbook->add_format();
280                 $formaty->set_bg_color('yellow');
281                 $formatc = $workbook->add_format();
282                 $formatc->set_align('center');
283                 $formatr = $workbook->add_format();
284                 $formatr->set_bold(1);
285                 $formatr->set_color('red');
286                 $formatr->set_align('center');
287                 $formatg = $workbook->add_format();
288                 $formatg->set_bold(1);
289                 $formatg->set_color('green');
290                 $formatg->set_align('center');
291                 // Here starts workshhet headers.
293                 $colnum = 0;
294                 foreach ($headers as $item) {
295                     $myxls->write(0, $colnum, $item, $formatbc);
296                     $colnum++;
297                 }
298                 $rownum = 1;
299             } else if ($download == 'Excel') {
300                 require_once("$CFG->libdir/excellib.class.php");
302                 $filename .= ".xls";
303                 // Creating a workbook.
304                 $workbook = new \MoodleExcelWorkbook("-");
305                 // Sending HTTP headers.
306                 $workbook->send($filename);
307                 // Creating the first worksheet.
308                 $sheettitle = get_string('report', 'scorm');
309                 $myxls = $workbook->add_worksheet($sheettitle);
310                 // Format types.
311                 $format = $workbook->add_format();
312                 $format->set_bold(0);
313                 $formatbc = $workbook->add_format();
314                 $formatbc->set_bold(1);
315                 $formatbc->set_align('center');
316                 $formatb = $workbook->add_format();
317                 $formatb->set_bold(1);
318                 $formaty = $workbook->add_format();
319                 $formaty->set_bg_color('yellow');
320                 $formatc = $workbook->add_format();
321                 $formatc->set_align('center');
322                 $formatr = $workbook->add_format();
323                 $formatr->set_bold(1);
324                 $formatr->set_color('red');
325                 $formatr->set_align('center');
326                 $formatg = $workbook->add_format();
327                 $formatg->set_bold(1);
328                 $formatg->set_color('green');
329                 $formatg->set_align('center');
331                 $colnum = 0;
332                 foreach ($headers as $item) {
333                     $myxls->write(0, $colnum, $item, $formatbc);
334                     $colnum++;
335                 }
336                 $rownum = 1;
337             } else if ($download == 'CSV') {
338                 $csvexport = new \csv_export_writer("tab");
339                 $csvexport->set_filename($filename, ".txt");
340                 $csvexport->add_data($headers);
341             }
343             if (!$download) {
344                 $sort = $table->get_sql_sort();
345             } else {
346                 $sort = '';
347             }
348             // Fix some wired sorting.
349             if (empty($sort)) {
350                 $sort = ' ORDER BY uniqueid';
351             } else {
352                 $sort = ' ORDER BY '.$sort;
353             }
355             if (!$download) {
356                 // Add extra limits due to initials bar.
357                 list($twhere, $tparams) = $table->get_sql_where();
358                 if ($twhere) {
359                     $where .= ' AND '.$twhere; // Initial bar.
360                     $params = array_merge($params, $tparams);
361                 }
363                 if (!empty($countsql)) {
364                     $count = $DB->get_record_sql($countsql, $params);
365                     $totalinitials = $count->nbresults;
366                     if ($twhere) {
367                         $countsql .= ' AND '.$twhere;
368                     }
369                     $count = $DB->get_record_sql($countsql, $params);
370                     $total  = $count->nbresults;
371                 }
373                 $table->pagesize($pagesize, $total);
375                 echo \html_writer::start_div('scormattemptcounts');
376                 if ( $count->nbresults == $count->nbattempts ) {
377                     echo get_string('reportcountattempts', 'scorm', $count);
378                 } else if ( $count->nbattempts > 0 ) {
379                     echo get_string('reportcountallattempts', 'scorm', $count);
380                 } else {
381                     echo $count->nbusers.' '.get_string('users');
382                 }
383                 echo \html_writer::end_div();
384             }
386             // Fetch the attempts.
387             if (!$download) {
388                 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params,
389                 $table->get_page_start(), $table->get_page_size());
390                 echo \html_writer::start_div('', array('id' => 'scormtablecontainer'));
391                 if ($candelete) {
392                     // Start form.
393                     $strreallydel  = addslashes_js(get_string('deleteattemptcheck', 'scorm'));
394                     echo \html_writer::start_tag('form', array('id' => 'attemptsform', 'method' => 'post',
395                                                                 'action' => $PAGE->url->out(false),
396                                                                 'onsubmit' => 'return confirm("'.$strreallydel.'");'));
397                     echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'delete'));
398                     echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
399                     echo \html_writer::start_div('', array('style' => 'display: none;'));
400                     echo \html_writer::input_hidden_params($PAGE->url);
401                     echo \html_writer::end_div();
402                     echo \html_writer::start_div();
403                 }
404                 $table->initialbars($totalinitials > 20); // Build table rows.
405             } else {
406                 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
407             }
408             if ($attempts) {
409                 foreach ($attempts as $scouser) {
410                     $row = array();
411                     if (!empty($scouser->attempt)) {
412                         $timetracks = scorm_get_sco_runtime($scorm->id, false, $scouser->userid, $scouser->attempt);
413                     } else {
414                         $timetracks = '';
415                     }
416                     if (in_array('checkbox', $columns)) {
417                         if ($candelete && !empty($timetracks->start)) {
418                             $row[] = $this->generate_row_checkbox('attemptid[]', "{$scouser->userid}:{$scouser->attempt}");
419                         } else if ($candelete) {
420                             $row[] = '';
421                         }
422                     }
423                     if (in_array('picture', $columns)) {
424                         $user = new \stdClass();
425                         $additionalfields = explode(',', \user_picture::fields());
426                         $user = username_load_fields_from_object($user, $scouser, null, $additionalfields);
427                         $user->id = $scouser->userid;
428                         $row[] = $OUTPUT->user_picture($user, array('courseid' => $course->id));
429                     }
430                     if (!$download) {
431                         $url = new \moodle_url('/user/view.php', array('id' => $scouser->userid, 'course' => $course->id));
432                         $row[] = \html_writer::link($url, fullname($scouser));
433                     } else {
434                         $row[] = fullname($scouser);
435                     }
436                     foreach ($extrafields as $field) {
437                         $row[] = s($scouser->{$field});
438                     }
439                     if (empty($timetracks->start)) {
440                         $row[] = '-';
441                         $row[] = '-';
442                         $row[] = '-';
443                         $row[] = '-';
444                     } else {
445                         if (!$download) {
446                             $url = new \moodle_url('/mod/scorm/report/userreport.php',
447                                                     array('id' => $cm->id,
448                                                         'user' => $scouser->userid,
449                                                         'attempt' => $scouser->attempt));
450                             $row[] = \html_writer::link($url, $scouser->attempt);
451                         } else {
452                             $row[] = $scouser->attempt;
453                         }
454                         if ($download == 'ODS' || $download == 'Excel' ) {
455                             $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
456                         } else {
457                             $row[] = userdate($timetracks->start);
458                         }
459                         if ($download == 'ODS' || $download == 'Excel' ) {
460                             $row[] = userdate($timetracks->finish, get_string('strftimedatetime', 'langconfig'));
461                         } else {
462                             $row[] = userdate($timetracks->finish);
463                         }
464                         $row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
465                     }
466                     // Print out all scores of attempt.
467                     $emptyrow = $download ? '' : '&nbsp;';
468                     foreach ($scoes as $sco) {
469                         if ($sco->launch != '') {
470                             if ($trackdata = scorm_get_tracks($sco->id, $scouser->userid, $scouser->attempt)) {
471                                 if ($trackdata->status == '') {
472                                     $trackdata->status = 'notattempted';
473                                 }
474                                 $strstatus = get_string($trackdata->status, 'scorm');
475                                 // If raw score exists, print it.
476                                 if ($trackdata->score_raw != '') {
477                                     $score = $trackdata->score_raw;
478                                     // Add max score if it exists.
479                                     if (isset($trackdata->score_max)) {
480                                         $score .= '/'.$trackdata->score_max;
481                                     }
482                                 } else { // Else print out status.
483                                     $score = $strstatus;
484                                 }
485                                 if (!$download) {
486                                     $url = new \moodle_url('/mod/scorm/report/userreporttracks.php', array('id' => $cm->id,
487                                         'scoid' => $sco->id, 'user' => $scouser->userid, 'attempt' => $scouser->attempt));
488                                     $row[] = $OUTPUT->pix_icon($trackdata->status, $strstatus, 'scorm') . '<br>' .
489                                         \html_writer::link($url, $score, array('title' => get_string('details', 'scorm')));
490                                 } else {
491                                     $row[] = $score;
492                                 }
493                                 // Interaction data.
494                                 for ($i = 0; $i < $questioncount; $i++) {
495                                     if ($displayoptions['qtext']) {
496                                         $element = 'cmi.interactions_'.$i.'.id';
497                                         if (isset($trackdata->$element)) {
498                                             $row[] = s($trackdata->$element);
499                                         } else {
500                                             $row[] = $emptyrow;
501                                         }
502                                     }
503                                     if ($displayoptions['resp']) {
504                                         $element = 'cmi.interactions_'.$i.'.student_response';
505                                         if (isset($trackdata->$element)) {
506                                             $row[] = s($trackdata->$element);
507                                         } else {
508                                             $row[] = $emptyrow;
509                                         }
510                                     }
511                                     if ($displayoptions['right']) {
512                                         $j = 0;
513                                         $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
514                                         $rightans = '';
515                                         if (isset($trackdata->$element)) {
516                                             while (isset($trackdata->$element)) {
517                                                 if ($j > 0) {
518                                                     $rightans .= ',';
519                                                 }
520                                                 $rightans .= s($trackdata->$element);
521                                                 $j++;
522                                                 $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
523                                             }
524                                             $row[] = $rightans;
525                                         } else {
526                                             $row[] = $emptyrow;
527                                         }
528                                     }
529                                     if ($displayoptions['result']) {
530                                         $element = 'cmi.interactions_'.$i.'.result';
531                                         if (isset($trackdata->$element)) {
532                                             $row[] = s($trackdata->$element);
533                                         } else {
534                                             $row[] = $emptyrow;
535                                         }
536                                     }
537                                 }
538                                 // End of interaction data.
539                             } else {
540                                 // If we don't have track data, we haven't attempted yet.
541                                 $strstatus = get_string('notattempted', 'scorm');
542                                 if (!$download) {
543                                     $row[] = $OUTPUT->pix_icon('notattempted', $strstatus, 'scorm') . '<br>' . $strstatus;
544                                 } else {
545                                     $row[] = $strstatus;
546                                 }
547                                 // Complete the empty cells.
548                                 for ($i = 0; $i < count($columns) - $nbmaincolumns; $i++) {
549                                     $row[] = $emptyrow;
550                                 }
551                             }
552                         }
553                     }
555                     if (!$download) {
556                         $table->add_data($row);
557                     } else if ($download == 'Excel' or $download == 'ODS') {
558                         $colnum = 0;
559                         foreach ($row as $item) {
560                             $myxls->write($rownum, $colnum, $item, $format);
561                             $colnum++;
562                         }
563                         $rownum++;
564                     } else if ($download == 'CSV') {
565                         $csvexport->add_data($row);
566                     }
567                 }
568                 if (!$download) {
569                     $table->finish_output();
570                     if ($candelete) {
571                         echo \html_writer::start_tag('table', array('id' => 'commands'));
572                         echo \html_writer::start_tag('tr').\html_writer::start_tag('td');
573                         echo $this->generate_delete_selected_button();
574                         echo \html_writer::end_tag('td').\html_writer::end_tag('tr').\html_writer::end_tag('table');
575                         // Close form.
576                         echo \html_writer::end_tag('div');
577                         echo \html_writer::end_tag('form');
578                     }
579                     echo \html_writer::end_div();
580                     if (!empty($attempts)) {
581                         echo \html_writer::start_tag('table', array('class' => 'boxaligncenter')).\html_writer::start_tag('tr');
582                         echo \html_writer::start_tag('td');
583                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
584                                                                    array('download' => 'ODS') + $displayoptions),
585                                                                    get_string('downloadods'),
586                                                                    'post',
587                                                                    ['class' => 'mt-1']);
588                         echo \html_writer::end_tag('td');
589                         echo \html_writer::start_tag('td');
590                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
591                                                                    array('download' => 'Excel') + $displayoptions),
592                                                                    get_string('downloadexcel'),
593                                                                    'post',
594                                                                    ['class' => 'mt-1']);
595                         echo \html_writer::end_tag('td');
596                         echo \html_writer::start_tag('td');
597                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
598                                                                    array('download' => 'CSV') + $displayoptions),
599                                                                    get_string('downloadtext'),
600                                                                    'post',
601                                                                    ['class' => 'mt-1']);
602                         echo \html_writer::end_tag('td');
603                         echo \html_writer::start_tag('td');
604                         echo \html_writer::end_tag('td');
605                         echo \html_writer::end_tag('tr').\html_writer::end_tag('table');
606                     }
607                 }
608             } else {
609                 if ($candelete && !$download) {
610                     echo \html_writer::end_div();
611                     echo \html_writer::end_tag('form');
612                     $table->finish_output();
613                 }
614                 echo \html_writer::end_div();
615             }
616             // Show preferences form irrespective of attempts are there to report or not.
617             if (!$download) {
618                 $mform->set_data(compact('pagesize', 'attemptsmode'));
619                 $mform->display();
620             }
621             if ($download == 'Excel' or $download == 'ODS') {
622                 $workbook->close();
623                 exit;
624             } else if ($download == 'CSV') {
625                 $csvexport->download_file();
626                 exit;
627             }
628         } else {
629             echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
630         }
631     }// Function ends.