1deef30b7d5ff5888be003b4fb0b0f478ec3a442
[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;
102         if (empty($currentgroup)) {
103             // All users who can attempt scoes.
104             if (!$students = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', 'u.id', '', '', '', '', '', false)) {
105                 echo $OUTPUT->notification(get_string('nostudentsyet'));
106                 $nostudents = true;
107                 $allowedlist = '';
108             } else {
109                 $allowedlist = array_keys($students);
110             }
111             unset($students);
112         } else {
113             // All users who can attempt scoes and who are in the currently selected group.
114             if (!$groupstudents = get_users_by_capability($contextmodule,
115                                                             'mod/scorm:savetrack', 'u.id', '', '', '',
116                                                             $currentgroup, '', false)) {
117                 echo $OUTPUT->notification(get_string('nostudentsingroup'));
118                 $nostudents = true;
119                 $groupstudents = array();
120             }
121             $allowedlist = array_keys($groupstudents);
122             unset($groupstudents);
123         }
124         if ( !$nostudents ) {
125             // Now check if asked download of data.
126             $coursecontext = \context_course::instance($course->id);
127             if ($download) {
128                 $filename = clean_filename("$course->shortname ".format_string($scorm->name, true, $formattextoptions));
129             }
131             // Define table columns.
132             $columns = array();
133             $headers = array();
134             if (!$download && $candelete) {
135                 $columns[] = 'checkbox';
136                 $headers[] = $this->generate_master_checkbox();
137             }
138             if (!$download && $CFG->grade_report_showuserimage) {
139                 $columns[] = 'picture';
140                 $headers[] = '';
141             }
142             $columns[] = 'fullname';
143             $headers[] = get_string('name');
145             $extrafields = get_extra_user_fields($coursecontext);
146             foreach ($extrafields as $field) {
147                 $columns[] = $field;
148                 $headers[] = get_user_field_name($field);
149             }
150             $columns[] = 'attempt';
151             $headers[] = get_string('attempt', 'scorm');
152             $columns[] = 'start';
153             $headers[] = get_string('started', 'scorm');
154             $columns[] = 'finish';
155             $headers[] = get_string('last', 'scorm');
156             $columns[] = 'score';
157             $headers[] = get_string('score', 'scorm');
158             $scoes = $DB->get_records('scorm_scoes', array("scorm" => $scorm->id), 'sortorder, id');
159             foreach ($scoes as $sco) {
160                 if ($sco->launch != '') {
161                     $columns[] = 'scograde'.$sco->id;
162                     $headers[] = format_string($sco->title, '', $formattextoptions);
163                 }
164             }
166             $params = array();
167             list($usql, $params) = $DB->get_in_or_equal($allowedlist, SQL_PARAMS_NAMED);
168             // Construct the SQL.
169             $select = 'SELECT DISTINCT '.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').' AS uniqueid, ';
170             $select .= 'st.scormid AS scormid, st.attempt AS attempt, ' .
171                     \user_picture::fields('u', array('idnumber'), 'userid') .
172                     get_extra_user_fields_sql($coursecontext, 'u', '', array('email', 'idnumber')) . ' ';
174             // This part is the same for all cases - join users and scorm_scoes_track tables.
175             $from = 'FROM {user} u ';
176             $from .= 'LEFT JOIN {scorm_scoes_track} st ON st.userid = u.id AND st.scormid = '.$scorm->id;
177             switch ($attemptsmode) {
178                 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH:
179                     // Show only students with attempts.
180                     $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NOT NULL';
181                     break;
182                 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
183                     // Show only students without attempts.
184                     $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NULL';
185                     break;
186                 case SCORM_REPORT_ATTEMPTS_ALL_STUDENTS:
187                     // Show all students with or without attempts.
188                     $where = ' WHERE u.id ' .$usql. ' AND (st.userid IS NOT NULL OR st.userid IS NULL)';
189                     break;
190             }
192             $countsql = 'SELECT COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').')) AS nbresults, ';
193             $countsql .= 'COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'st.attempt').')) AS nbattempts, ';
194             $countsql .= 'COUNT(DISTINCT(u.id)) AS nbusers ';
195             $countsql .= $from.$where;
196             $questioncount = get_scorm_question_count($scorm->id);
197             $nbmaincolumns = count($columns);
198             for ($id = 0; $id < $questioncount; $id++) {
199                 if ($displayoptions['qtext']) {
200                     $columns[] = 'question' . $id;
201                     $headers[] = get_string('questionx', 'scormreport_interactions', $id);
202                 }
203                 if ($displayoptions['resp']) {
204                     $columns[] = 'response' . $id;
205                     $headers[] = get_string('responsex', 'scormreport_interactions', $id);
206                 }
207                 if ($displayoptions['right']) {
208                     $columns[] = 'right' . $id;
209                     $headers[] = get_string('rightanswerx', 'scormreport_interactions', $id);
210                 }
211                 if ($displayoptions['result']) {
212                     $columns[] = 'result' . $id;
213                     $headers[] = get_string('resultx', 'scormreport_interactions', $id);
214                 }
215             }
217             if (!$download) {
218                 $table = new \flexible_table('mod-scorm-report');
220                 $table->define_columns($columns);
221                 $table->define_headers($headers);
222                 $table->define_baseurl($PAGE->url);
224                 $table->sortable(true);
225                 $table->collapsible(true);
227                 // This is done to prevent redundant data, when a user has multiple attempts.
228                 $table->column_suppress('picture');
229                 $table->column_suppress('fullname');
230                 foreach ($extrafields as $field) {
231                     $table->column_suppress($field);
232                 }
234                 $table->no_sorting('start');
235                 $table->no_sorting('finish');
236                 $table->no_sorting('score');
237                 $table->no_sorting('checkbox');
238                 $table->no_sorting('picture');
240                 for ($id = 0; $id < $questioncount; $id++) {
241                     if ($displayoptions['qtext']) {
242                         $table->no_sorting('question'.$id);
243                     }
244                     if ($displayoptions['resp']) {
245                         $table->no_sorting('response'.$id);
246                     }
247                     if ($displayoptions['right']) {
248                         $table->no_sorting('right'.$id);
249                     }
250                     if ($displayoptions['result']) {
251                         $table->no_sorting('result'.$id);
252                     }
253                 }
255                 foreach ($scoes as $sco) {
256                     if ($sco->launch != '') {
257                         $table->no_sorting('scograde'.$sco->id);
258                     }
259                 }
261                 $table->column_class('picture', 'picture');
262                 $table->column_class('fullname', 'bold');
263                 $table->column_class('score', 'bold');
265                 $table->set_attribute('cellspacing', '0');
266                 $table->set_attribute('id', 'attempts');
267                 $table->set_attribute('class', 'generaltable generalbox');
269                 // Start working -- this is necessary as soon as the niceties are over.
270                 $table->setup();
271             } else if ($download == 'ODS') {
272                 require_once("$CFG->libdir/odslib.class.php");
274                 $filename .= ".ods";
275                 // Creating a workbook.
276                 $workbook = new \MoodleODSWorkbook("-");
277                 // Sending HTTP headers.
278                 $workbook->send($filename);
279                 // Creating the first worksheet.
280                 $sheettitle = get_string('report', 'scorm');
281                 $myxls = $workbook->add_worksheet($sheettitle);
282                 // Format types.
283                 $format = $workbook->add_format();
284                 $format->set_bold(0);
285                 $formatbc = $workbook->add_format();
286                 $formatbc->set_bold(1);
287                 $formatbc->set_align('center');
288                 $formatb = $workbook->add_format();
289                 $formatb->set_bold(1);
290                 $formaty = $workbook->add_format();
291                 $formaty->set_bg_color('yellow');
292                 $formatc = $workbook->add_format();
293                 $formatc->set_align('center');
294                 $formatr = $workbook->add_format();
295                 $formatr->set_bold(1);
296                 $formatr->set_color('red');
297                 $formatr->set_align('center');
298                 $formatg = $workbook->add_format();
299                 $formatg->set_bold(1);
300                 $formatg->set_color('green');
301                 $formatg->set_align('center');
302                 // Here starts workshhet headers.
304                 $colnum = 0;
305                 foreach ($headers as $item) {
306                     $myxls->write(0, $colnum, $item, $formatbc);
307                     $colnum++;
308                 }
309                 $rownum = 1;
310             } else if ($download == 'Excel') {
311                 require_once("$CFG->libdir/excellib.class.php");
313                 $filename .= ".xls";
314                 // Creating a workbook.
315                 $workbook = new \MoodleExcelWorkbook("-");
316                 // Sending HTTP headers.
317                 $workbook->send($filename);
318                 // Creating the first worksheet.
319                 $sheettitle = get_string('report', 'scorm');
320                 $myxls = $workbook->add_worksheet($sheettitle);
321                 // Format types.
322                 $format = $workbook->add_format();
323                 $format->set_bold(0);
324                 $formatbc = $workbook->add_format();
325                 $formatbc->set_bold(1);
326                 $formatbc->set_align('center');
327                 $formatb = $workbook->add_format();
328                 $formatb->set_bold(1);
329                 $formaty = $workbook->add_format();
330                 $formaty->set_bg_color('yellow');
331                 $formatc = $workbook->add_format();
332                 $formatc->set_align('center');
333                 $formatr = $workbook->add_format();
334                 $formatr->set_bold(1);
335                 $formatr->set_color('red');
336                 $formatr->set_align('center');
337                 $formatg = $workbook->add_format();
338                 $formatg->set_bold(1);
339                 $formatg->set_color('green');
340                 $formatg->set_align('center');
342                 $colnum = 0;
343                 foreach ($headers as $item) {
344                     $myxls->write(0, $colnum, $item, $formatbc);
345                     $colnum++;
346                 }
347                 $rownum = 1;
348             } else if ($download == 'CSV') {
349                 $csvexport = new \csv_export_writer("tab");
350                 $csvexport->set_filename($filename, ".txt");
351                 $csvexport->add_data($headers);
352             }
354             if (!$download) {
355                 $sort = $table->get_sql_sort();
356             } else {
357                 $sort = '';
358             }
359             // Fix some wired sorting.
360             if (empty($sort)) {
361                 $sort = ' ORDER BY uniqueid';
362             } else {
363                 $sort = ' ORDER BY '.$sort;
364             }
366             if (!$download) {
367                 // Add extra limits due to initials bar.
368                 list($twhere, $tparams) = $table->get_sql_where();
369                 if ($twhere) {
370                     $where .= ' AND '.$twhere; // Initial bar.
371                     $params = array_merge($params, $tparams);
372                 }
374                 if (!empty($countsql)) {
375                     $count = $DB->get_record_sql($countsql, $params);
376                     $totalinitials = $count->nbresults;
377                     if ($twhere) {
378                         $countsql .= ' AND '.$twhere;
379                     }
380                     $count = $DB->get_record_sql($countsql, $params);
381                     $total  = $count->nbresults;
382                 }
384                 $table->pagesize($pagesize, $total);
386                 echo \html_writer::start_div('scormattemptcounts');
387                 if ( $count->nbresults == $count->nbattempts ) {
388                     echo get_string('reportcountattempts', 'scorm', $count);
389                 } else if ( $count->nbattempts > 0 ) {
390                     echo get_string('reportcountallattempts', 'scorm', $count);
391                 } else {
392                     echo $count->nbusers.' '.get_string('users');
393                 }
394                 echo \html_writer::end_div();
395             }
397             // Fetch the attempts.
398             if (!$download) {
399                 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params,
400                 $table->get_page_start(), $table->get_page_size());
401                 echo \html_writer::start_div('', array('id' => 'scormtablecontainer'));
402                 if ($candelete) {
403                     // Start form.
404                     $strreallydel  = addslashes_js(get_string('deleteattemptcheck', 'scorm'));
405                     echo \html_writer::start_tag('form', array('id' => 'attemptsform', 'method' => 'post',
406                                                                 'action' => $PAGE->url->out(false),
407                                                                 'onsubmit' => 'return confirm("'.$strreallydel.'");'));
408                     echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'action', 'value' => 'delete'));
409                     echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
410                     echo \html_writer::start_div('', array('style' => 'display: none;'));
411                     echo \html_writer::input_hidden_params($PAGE->url);
412                     echo \html_writer::end_div();
413                     echo \html_writer::start_div();
414                 }
415                 $table->initialbars($totalinitials > 20); // Build table rows.
416             } else {
417                 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
418             }
419             if ($attempts) {
420                 foreach ($attempts as $scouser) {
421                     $row = array();
422                     if (!empty($scouser->attempt)) {
423                         $timetracks = scorm_get_sco_runtime($scorm->id, false, $scouser->userid, $scouser->attempt);
424                     } else {
425                         $timetracks = '';
426                     }
427                     if (in_array('checkbox', $columns)) {
428                         if ($candelete && !empty($timetracks->start)) {
429                             $row[] = $this->generate_row_checkbox('attemptid[]', "{$scouser->userid}:{$scouser->attempt}");
430                         } else if ($candelete) {
431                             $row[] = '';
432                         }
433                     }
434                     if (in_array('picture', $columns)) {
435                         $user = new \stdClass();
436                         $additionalfields = explode(',', \user_picture::fields());
437                         $user = username_load_fields_from_object($user, $scouser, null, $additionalfields);
438                         $user->id = $scouser->userid;
439                         $row[] = $OUTPUT->user_picture($user, array('courseid' => $course->id));
440                     }
441                     if (!$download) {
442                         $url = new \moodle_url('/user/view.php', array('id' => $scouser->userid, 'course' => $course->id));
443                         $row[] = \html_writer::link($url, fullname($scouser));
444                     } else {
445                         $row[] = fullname($scouser);
446                     }
447                     foreach ($extrafields as $field) {
448                         $row[] = s($scouser->{$field});
449                     }
450                     if (empty($timetracks->start)) {
451                         $row[] = '-';
452                         $row[] = '-';
453                         $row[] = '-';
454                         $row[] = '-';
455                     } else {
456                         if (!$download) {
457                             $url = new \moodle_url('/mod/scorm/report/userreport.php',
458                                                     array('id' => $cm->id,
459                                                         'user' => $scouser->userid,
460                                                         'attempt' => $scouser->attempt));
461                             $row[] = \html_writer::link($url, $scouser->attempt);
462                         } else {
463                             $row[] = $scouser->attempt;
464                         }
465                         if ($download == 'ODS' || $download == 'Excel' ) {
466                             $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
467                         } else {
468                             $row[] = userdate($timetracks->start);
469                         }
470                         if ($download == 'ODS' || $download == 'Excel' ) {
471                             $row[] = userdate($timetracks->finish, get_string('strftimedatetime', 'langconfig'));
472                         } else {
473                             $row[] = userdate($timetracks->finish);
474                         }
475                         $row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
476                     }
477                     // Print out all scores of attempt.
478                     $emptyrow = $download ? '' : '&nbsp;';
479                     foreach ($scoes as $sco) {
480                         if ($sco->launch != '') {
481                             if ($trackdata = scorm_get_tracks($sco->id, $scouser->userid, $scouser->attempt)) {
482                                 if ($trackdata->status == '') {
483                                     $trackdata->status = 'notattempted';
484                                 }
485                                 $strstatus = get_string($trackdata->status, 'scorm');
486                                 // If raw score exists, print it.
487                                 if ($trackdata->score_raw != '') {
488                                     $score = $trackdata->score_raw;
489                                     // Add max score if it exists.
490                                     if (isset($trackdata->score_max)) {
491                                         $score .= '/'.$trackdata->score_max;
492                                     }
493                                 } else { // Else print out status.
494                                     $score = $strstatus;
495                                 }
496                                 if (!$download) {
497                                     $url = new \moodle_url('/mod/scorm/report/userreporttracks.php', array('id' => $cm->id,
498                                         'scoid' => $sco->id, 'user' => $scouser->userid, 'attempt' => $scouser->attempt));
499                                     $row[] = $OUTPUT->pix_icon($trackdata->status, $strstatus, 'scorm') . '<br>' .
500                                         \html_writer::link($url, $score, array('title' => get_string('details', 'scorm')));
501                                 } else {
502                                     $row[] = $score;
503                                 }
504                                 // Interaction data.
505                                 for ($i = 0; $i < $questioncount; $i++) {
506                                     if ($displayoptions['qtext']) {
507                                         $element = 'cmi.interactions_'.$i.'.id';
508                                         if (isset($trackdata->$element)) {
509                                             $row[] = s($trackdata->$element);
510                                         } else {
511                                             $row[] = $emptyrow;
512                                         }
513                                     }
514                                     if ($displayoptions['resp']) {
515                                         $element = 'cmi.interactions_'.$i.'.student_response';
516                                         if (isset($trackdata->$element)) {
517                                             $row[] = s($trackdata->$element);
518                                         } else {
519                                             $row[] = $emptyrow;
520                                         }
521                                     }
522                                     if ($displayoptions['right']) {
523                                         $j = 0;
524                                         $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
525                                         $rightans = '';
526                                         if (isset($trackdata->$element)) {
527                                             while (isset($trackdata->$element)) {
528                                                 if ($j > 0) {
529                                                     $rightans .= ',';
530                                                 }
531                                                 $rightans .= s($trackdata->$element);
532                                                 $j++;
533                                                 $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
534                                             }
535                                             $row[] = $rightans;
536                                         } else {
537                                             $row[] = $emptyrow;
538                                         }
539                                     }
540                                     if ($displayoptions['result']) {
541                                         $element = 'cmi.interactions_'.$i.'.result';
542                                         if (isset($trackdata->$element)) {
543                                             $row[] = s($trackdata->$element);
544                                         } else {
545                                             $row[] = $emptyrow;
546                                         }
547                                     }
548                                 }
549                                 // End of interaction data.
550                             } else {
551                                 // If we don't have track data, we haven't attempted yet.
552                                 $strstatus = get_string('notattempted', 'scorm');
553                                 if (!$download) {
554                                     $row[] = $OUTPUT->pix_icon('notattempted', $strstatus, 'scorm') . '<br>' . $strstatus;
555                                 } else {
556                                     $row[] = $strstatus;
557                                 }
558                                 // Complete the empty cells.
559                                 for ($i = 0; $i < count($columns) - $nbmaincolumns; $i++) {
560                                     $row[] = $emptyrow;
561                                 }
562                             }
563                         }
564                     }
566                     if (!$download) {
567                         $table->add_data($row);
568                     } else if ($download == 'Excel' or $download == 'ODS') {
569                         $colnum = 0;
570                         foreach ($row as $item) {
571                             $myxls->write($rownum, $colnum, $item, $format);
572                             $colnum++;
573                         }
574                         $rownum++;
575                     } else if ($download == 'CSV') {
576                         $csvexport->add_data($row);
577                     }
578                 }
579                 if (!$download) {
580                     $table->finish_output();
581                     if ($candelete) {
582                         echo \html_writer::start_tag('table', array('id' => 'commands'));
583                         echo \html_writer::start_tag('tr').\html_writer::start_tag('td');
584                         echo $this->generate_delete_selected_button();
585                         echo \html_writer::end_tag('td').\html_writer::end_tag('tr').\html_writer::end_tag('table');
586                         // Close form.
587                         echo \html_writer::end_tag('div');
588                         echo \html_writer::end_tag('form');
589                     }
590                     echo \html_writer::end_div();
591                     if (!empty($attempts)) {
592                         echo \html_writer::start_tag('table', array('class' => 'boxaligncenter')).\html_writer::start_tag('tr');
593                         echo \html_writer::start_tag('td');
594                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
595                                                                    array('download' => 'ODS') + $displayoptions),
596                                                                    get_string('downloadods'),
597                                                                    'post',
598                                                                    ['class' => 'mt-1']);
599                         echo \html_writer::end_tag('td');
600                         echo \html_writer::start_tag('td');
601                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
602                                                                    array('download' => 'Excel') + $displayoptions),
603                                                                    get_string('downloadexcel'),
604                                                                    'post',
605                                                                    ['class' => 'mt-1']);
606                         echo \html_writer::end_tag('td');
607                         echo \html_writer::start_tag('td');
608                         echo $OUTPUT->single_button(new \moodle_url($PAGE->url,
609                                                                    array('download' => 'CSV') + $displayoptions),
610                                                                    get_string('downloadtext'),
611                                                                    'post',
612                                                                    ['class' => 'mt-1']);
613                         echo \html_writer::end_tag('td');
614                         echo \html_writer::start_tag('td');
615                         echo \html_writer::end_tag('td');
616                         echo \html_writer::end_tag('tr').\html_writer::end_tag('table');
617                     }
618                 }
619             } else {
620                 if ($candelete && !$download) {
621                     echo \html_writer::end_div();
622                     echo \html_writer::end_tag('form');
623                     $table->finish_output();
624                 }
625                 echo \html_writer::end_div();
626             }
627             // Show preferences form irrespective of attempts are there to report or not.
628             if (!$download) {
629                 $mform->set_data(compact('pagesize', 'attemptsmode'));
630                 $mform->display();
631             }
632             if ($download == 'Excel' or $download == 'ODS') {
633                 $workbook->close();
634                 exit;
635             } else if ($download == 'CSV') {
636                 $csvexport->download_file();
637                 exit;
638             }
639         } else {
640             echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
641         }
642     }// Function ends.