MDL-28933 SCORM use a global function to check SCORM version to make sure all methods...
[moodle.git] / mod / scorm / report / basic / report.php
CommitLineData
62b82cbc
AKA
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
5e85f7b5
PS
18 * @package scormreport
19 * @subpackage basic
20 * @author Dan Marsden and Ankit Kumar Agarwal
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
62b82cbc
AKA
22 */
23
b2614417
DM
24defined('MOODLE_INTERNAL') || die();
25
62b82cbc
AKA
26class scorm_basic_report extends scorm_default_report {
27 /**
b2614417
DM
28 * displays the full report
29 * @param stdClass $scorm full SCORM object
30 * @param stdClass $cm - full course_module object
31 * @param stdClass $course - full course object
32 * @param string $download - type of download being requested
62b82cbc 33 */
b2614417
DM
34 function display($scorm, $cm, $course, $download) {
35 global $CFG, $DB, $OUTPUT, $PAGE;
62b82cbc 36 $contextmodule= get_context_instance(CONTEXT_MODULE, $cm->id);
b2614417
DM
37 $action = optional_param('action', '', PARAM_ALPHA);
38 $attemptids = optional_param('attemptid', array(), PARAM_RAW);
62b82cbc 39
b2614417
DM
40 if ($action == 'delete' && has_capability('mod/scorm:deleteresponses', $contextmodule) && confirm_sesskey()) {
41 if (scorm_delete_responses($attemptids, $scorm)) { //delete responses.
42 add_to_log($course->id, 'scorm', 'delete attempts', 'report.php?id=' . $cm->id, implode(",", $attemptids), $cm->id);
43 echo $OUTPUT->notification(get_string('scormresponsedeleted', 'scorm'), 'notifysuccess');
44 }
45 }
62b82cbc
AKA
46
47 // detailed report
b2614417 48 $mform = new mod_scorm_report_settings($PAGE->url, compact('currentgroup'));
62b82cbc
AKA
49 if ($fromform = $mform->get_data()) {
50 $detailedrep = $fromform->detailedrep;
51 $pagesize = $fromform->pagesize;
b2614417 52 $attemptsmode = !empty($fromform->attemptsmode) ? $fromform->attemptsmode : SCORM_REPORT_ATTEMPTS_ALL_STUDENTS;
62b82cbc
AKA
53 set_user_preference('scorm_report_detailed', $detailedrep);
54 set_user_preference('scorm_report_pagesize', $pagesize);
55 } else {
56 $detailedrep = get_user_preferences('scorm_report_detailed', false);
57 $pagesize = get_user_preferences('scorm_report_pagesize', 0);
58 $attemptsmode = optional_param('attemptsmode', SCORM_REPORT_ATTEMPTS_STUDENTS_WITH, PARAM_INT);
59 }
60 if ($pagesize < 1) {
61 $pagesize = SCORM_REPORT_DEFAULT_PAGE_SIZE;
62 }
63
64 // select group menu
5e3014df
DM
65 $displayoptions = array();
66 $displayoptions['attemptsmode'] = $attemptsmode;
62b82cbc
AKA
67 if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
68 if (!$download) {
5e3014df 69 groups_print_activity_menu($cm, new moodle_url($PAGE->url, $displayoptions));
62b82cbc
AKA
70 }
71 }
72
73 // We only want to show the checkbox to delete attempts
74 // if the user has permissions and if the report mode is showing attempts.
75 $candelete = has_capability('mod/scorm:deleteresponses', $contextmodule)
76 && ($attemptsmode!= SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
77 // select the students
78 $nostudents = false;
79
80 if (empty($currentgroup)) {
81 // all users who can attempt scoes
82 if (!$students = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', '', '', '', '', '', '', false)) {
83 echo $OUTPUT->notification(get_string('nostudentsyet'));
84 $nostudents = true;
85 $allowedlist = '';
86 } else {
87 $allowedlist = join(',', array_keys($students));
88 }
89 } else {
90 // all users who can attempt scoes and who are in the currently selected group
91 if (!$groupstudents = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', '', '', '', '', $currentgroup, '', false)) {
92 echo $OUTPUT->notification(get_string('nostudentsingroup'));
93 $nostudents = true;
94 $groupstudents = array();
95 }
96 $allowedlist = join(',', array_keys($groupstudents));
97 }
98
99 if ( !$nostudents ) {
100 // Now check if asked download of data
101 if ($download) {
102 $filename = clean_filename("$course->shortname ".format_string($scorm->name, true));
103 }
104
105 // Define table columns
106 $columns = array();
107 $headers = array();
108 if (!$download && $candelete) {
109 $columns[]= 'checkbox';
110 $headers[]= null;
111 }
112 if (!$download && $CFG->grade_report_showuserimage) {
113 $columns[]= 'picture';
114 $headers[]= '';
115 }
116 $columns[]= 'fullname';
117 $headers[]= get_string('name');
118 if ($CFG->grade_report_showuseridnumber) {
119 $columns[]= 'idnumber';
120 $headers[]= get_string('idnumber');
121 }
122 $columns[]= 'attempt';
123 $headers[]= get_string('attempt', 'scorm');
124 $columns[]= 'start';
125 $headers[]= get_string('started', 'scorm');
126 $columns[]= 'finish';
127 $headers[]= get_string('last', 'scorm');
128 $columns[]= 'score';
129 $headers[]= get_string('score', 'scorm');
130 if ($detailedrep && $scoes = $DB->get_records('scorm_scoes', array("scorm"=>$scorm->id), 'id')) {
131 foreach ($scoes as $sco) {
132 if ($sco->launch!='') {
133 $columns[]= 'scograde'.$sco->id;
134 $headers[]= format_string($sco->title);
135 $table->head[]= format_string($sco->title);
136 }
137 }
138 } else {
139 $scoes = null;
140 }
141
142 if (!$download) {
143 $table = new flexible_table('mod-scorm-report');
144
145 $table->define_columns($columns);
146 $table->define_headers($headers);
b2614417 147 $table->define_baseurl($PAGE->url);
62b82cbc
AKA
148
149 $table->sortable(true);
150 $table->collapsible(true);
151
152 $table->column_suppress('picture');
153 $table->column_suppress('fullname');
154 $table->column_suppress('idnumber');
155
156 $table->no_sorting('start');
157 $table->no_sorting('finish');
158 $table->no_sorting('score');
159 if ( $scoes ) {
160 foreach ($scoes as $sco) {
161 if ($sco->launch!='') {
162 $table->no_sorting('scograde'.$sco->id);
163 }
164 }
165 }
166
167 $table->column_class('picture', 'picture');
168 $table->column_class('fullname', 'bold');
169 $table->column_class('score', 'bold');
170
171 $table->set_attribute('cellspacing', '0');
172 $table->set_attribute('id', 'attempts');
173 $table->set_attribute('class', 'generaltable generalbox');
174
175 // Start working -- this is necessary as soon as the niceties are over
176 $table->setup();
177 } else if ($download =='ODS') {
178 require_once("$CFG->libdir/odslib.class.php");
179
180 $filename .= ".ods";
181 // Creating a workbook
182 $workbook = new MoodleODSWorkbook("-");
183 // Sending HTTP headers
184 $workbook->send($filename);
185 // Creating the first worksheet
186 $sheettitle = get_string('report', 'scorm');
187 $myxls =& $workbook->add_worksheet($sheettitle);
188 // format types
189 $format =& $workbook->add_format();
190 $format->set_bold(0);
191 $formatbc =& $workbook->add_format();
192 $formatbc->set_bold(1);
193 $formatbc->set_align('center');
194 $formatb =& $workbook->add_format();
195 $formatb->set_bold(1);
196 $formaty =& $workbook->add_format();
197 $formaty->set_bg_color('yellow');
198 $formatc =& $workbook->add_format();
199 $formatc->set_align('center');
200 $formatr =& $workbook->add_format();
201 $formatr->set_bold(1);
202 $formatr->set_color('red');
203 $formatr->set_align('center');
204 $formatg =& $workbook->add_format();
205 $formatg->set_bold(1);
206 $formatg->set_color('green');
207 $formatg->set_align('center');
208 // Here starts workshhet headers
209
210 $colnum = 0;
211 foreach ($headers as $item) {
212 $myxls->write(0, $colnum, $item, $formatbc);
213 $colnum++;
214 }
215 $rownum=1;
216 } else if ($download =='Excel') {
217 require_once("$CFG->libdir/excellib.class.php");
218
219 $filename .= ".xls";
220 // Creating a workbook
221 $workbook = new MoodleExcelWorkbook("-");
222 // Sending HTTP headers
223 $workbook->send($filename);
224 // Creating the first worksheet
225 $sheettitle = get_string('report', 'scorm');
226 $myxls =& $workbook->add_worksheet($sheettitle);
227 // format types
228 $format =& $workbook->add_format();
229 $format->set_bold(0);
230 $formatbc =& $workbook->add_format();
231 $formatbc->set_bold(1);
232 $formatbc->set_align('center');
233 $formatb =& $workbook->add_format();
234 $formatb->set_bold(1);
235 $formaty =& $workbook->add_format();
236 $formaty->set_bg_color('yellow');
237 $formatc =& $workbook->add_format();
238 $formatc->set_align('center');
239 $formatr =& $workbook->add_format();
240 $formatr->set_bold(1);
241 $formatr->set_color('red');
242 $formatr->set_align('center');
243 $formatg =& $workbook->add_format();
244 $formatg->set_bold(1);
245 $formatg->set_color('green');
246 $formatg->set_align('center');
247
248 $colnum = 0;
249 foreach ($headers as $item) {
250 $myxls->write(0, $colnum, $item, $formatbc);
251 $colnum++;
252 }
253 $rownum=1;
254 } else if ($download=='CSV') {
255 $filename .= ".txt";
256 header("Content-Type: application/download\n");
257 header("Content-Disposition: attachment; filename=\"$filename\"");
258 header("Expires: 0");
259 header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
260 header("Pragma: public");
261 echo implode("\t", $headers)." \n";
262 }
263
264 // Construct the SQL
265 $select = 'SELECT DISTINCT '.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').' AS uniqueid, ';
266 $select .= 'st.scormid AS scormid, st.attempt AS attempt, ' .
267 'u.id AS userid, u.idnumber, u.firstname, u.lastname, u.picture, u.imagealt, u.email ';
268
269 // This part is the same for all cases - join users and scorm_scoes_track tables
270 $from = 'FROM {user} u ';
271 $from .= 'LEFT JOIN {scorm_scoes_track} st ON st.userid = u.id AND st.scormid = '.$scorm->id;
272 switch ($attemptsmode) {
273 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH:
274 // Show only students with attempts
275 $where = ' WHERE u.id IN (' .$allowedlist. ') AND st.userid IS NOT NULL';
276 break;
277 case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
278 // Show only students without attempts
279 $where = ' WHERE u.id IN (' .$allowedlist. ') AND st.userid IS NULL';
280 break;
281 case SCORM_REPORT_ATTEMPTS_ALL_STUDENTS:
282 // Show all students with or without attempts
283 $where = ' WHERE u.id IN (' .$allowedlist. ') AND (st.userid IS NOT NULL OR st.userid IS NULL)';
284 break;
285 }
286
287 $countsql = 'SELECT COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').')) AS nbresults, ';
288 $countsql .= 'COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'st.attempt').')) AS nbattempts, ';
289 $countsql .= 'COUNT(DISTINCT(u.id)) AS nbusers ';
290 $countsql .= $from.$where;
291 $params = array();
292
293 if (!$download) {
294 $sort = $table->get_sql_sort();
295 } else {
296 $sort = '';
297 }
298 // Fix some wired sorting
299 if (empty($sort)) {
300 $sort = ' ORDER BY uniqueid';
301 } else {
302 $sort = ' ORDER BY '.$sort;
303 }
304
305 if (!$download) {
306 // Add extra limits due to initials bar
307 list($twhere, $tparams) = $table->get_sql_where();
308 if ($twhere) {
309 $where .= ' AND '.$twhere; //initial bar
310 $params = array_merge($params, $tparams);
311 }
312
313 if (!empty($countsql)) {
314 $count = $DB->get_record_sql($countsql);
315 $totalinitials = $count->nbresults;
316 if ($twhere) {
317 $countsql .= ' AND '.$twhere;
318 }
319 $count = $DB->get_record_sql($countsql, $params);
320 $total = $count->nbresults;
321 }
322
323 $table->pagesize($pagesize, $total);
324
325 echo '<div class="quizattemptcounts">';
326 if ( $count->nbresults == $count->nbattempts ) {
327 echo get_string('reportcountattempts', 'scorm', $count);
328 } else if ( $count->nbattempts>0 ) {
329 echo get_string('reportcountallattempts', 'scorm', $count);
330 } else {
331 echo $count->nbusers.' '.get_string('users');
332 }
333 echo '</div>';
334 }
335
336 // Fetch the attempts
337 if (!$download) {
338 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params,
339 $table->get_page_start(), $table->get_page_size());
340 echo '<div id="scormtablecontainer">';
341 if ($candelete) {
342 // Start form
343 $strreallydel = addslashes_js(get_string('deleteattemptcheck', 'scorm'));
5e3014df 344 echo '<form id="attemptsform" method="post" action="' . $PAGE->url->out(false) .
b2614417 345 '" onsubmit="return confirm(\''.$strreallydel.'\');">';
62b82cbc
AKA
346 echo '<input type="hidden" name="action" value="delete"/>';
347 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
348 echo '<div style="display: none;">';
b2614417 349 echo html_writer::input_hidden_params($PAGE->url);
62b82cbc
AKA
350 echo '</div>';
351 echo '<div>';
352 }
353 $table->initialbars($totalinitials>20); // Build table rows
354 } else {
355 $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
356 }
357
358 if ($attempts) {
359 foreach ($attempts as $scouser) {
360 $row = array();
361 if (!empty($scouser->attempt)) {
362 $timetracks = scorm_get_sco_runtime($scorm->id, false, $scouser->userid, $scouser->attempt);
363 }
364 if (in_array('checkbox', $columns)) {
365 if ($candelete && !empty($timetracks->start)) {
366 $row[] = '<input type="checkbox" name="attemptid[]" value="'. $scouser->userid . ':' . $scouser->attempt . '" />';
367 } else if ($candelete) {
368 $row[] = '';
369 }
370 }
371 if (in_array('picture', $columns)) {
372 $user = (object)array(
373 'id'=>$scouser->userid,
374 'picture'=>$scouser->picture,
375 'imagealt'=>$scouser->imagealt,
376 'email'=>$scouser->email,
377 'firstname'=>$scouser->firstname,
378 'lastname'=>$scouser->lastname);
379 $row[] = $OUTPUT->user_picture($user, array('courseid'=>$course->id));
380 }
381 if (!$download) {
382 $row[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$scouser->userid.'&amp;course='.$course->id.'">'.fullname($scouser).'</a>';
383 } else {
384 $row[] = fullname($scouser);
385 }
386 if (in_array('idnumber', $columns)) {
387 $row[] = $scouser->idnumber;
388 }
389 if (empty($timetracks->start)) {
390 $row[] = '-';
391 $row[] = '-';
392 $row[] = '-';
393 $row[] = '-';
394 } else {
395 if (!$download) {
396 $row[] = '<a href="userreport.php?a='.$scorm->id.'&amp;user='.$scouser->userid.'&amp;attempt='.$scouser->attempt.'">'.$scouser->attempt.'</a>';
397 } else {
398 $row[] = $scouser->attempt;
399 }
400 if ($download =='ODS' || $download =='Excel' ) {
401 $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
402 } else {
403 $row[] = userdate($timetracks->start);
404 }
405 if ($download =='ODS' || $download =='Excel' ) {
406 $row[] = userdate($timetracks->finish, get_string('strftimedatetime', 'langconfig'));
407 } else {
408 $row[] = userdate($timetracks->finish);
409 }
410 $row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
411 }
412 // print out all scores of attempt
413 if ($scoes) {
414 foreach ($scoes as $sco) {
415 if ($sco->launch!='') {
416 if ($trackdata = scorm_get_tracks($sco->id, $scouser->userid, $scouser->attempt)) {
417 if ($trackdata->status == '') {
418 $trackdata->status = 'notattempted';
419 }
420 $strstatus = get_string($trackdata->status, 'scorm');
421 // if raw score exists, print it
422 if ($trackdata->score_raw != '') {
423 $score = $trackdata->score_raw;
424 // add max score if it exists
e6402b54 425 if (scorm_version_check($scorm->version, SCORM_13)) {
62b82cbc
AKA
426 $maxkey = 'cmi.score.max';
427 } else {
428 $maxkey = 'cmi.core.score.max';
429 }
430 if (isset($trackdata->$maxkey)) {
431 $score .= '/'.$trackdata->$maxkey;
432 }
433 // else print out status
434 } else {
435 $score = $strstatus;
436 }
437 if (!$download) {
438 $row[] = '<img src="'.$OUTPUT->pix_url($trackdata->status, 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>
439 <a href="userreport.php?b='.$sco->id.'&amp;user='.$scouser->userid.'&amp;attempt='.$scouser->attempt.
440 '" title="'.get_string('details', 'scorm').'">'.$score.'</a>';
441 } else {
442 $row[] = $score;
443 }
444 } else {
445 // if we don't have track data, we haven't attempted yet
446 $strstatus = get_string('notattempted', 'scorm');
447 if (!$download) {
448 $row[] = '<img src="'.$OUTPUT->pix_url('notattempted', 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>'.$strstatus;
449 } else {
450 $row[] = $strstatus;
451 }
452 }
453 }
454 }
455 }
456
457 if (!$download) {
458 $table->add_data($row);
459 } else if ($download == 'Excel' or $download == 'ODS') {
460 $colnum = 0;
461 foreach ($row as $item) {
462 $myxls->write($rownum, $colnum, $item, $format);
463 $colnum++;
464 }
465 $rownum++;
466 } else if ($download=='CSV') {
467 $text = implode("\t", $row);
468 echo $text." \n";
469 }
470 }
471 if (!$download) {
472 $table->finish_output();
473 if ($candelete) {
474 echo '<table id="commands">';
475 echo '<tr><td>';
476 echo '<a href="javascript:select_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
477 get_string('selectall', 'scorm').'</a> / ';
478 echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
479 get_string('selectnone', 'scorm').'</a> ';
480 echo '&nbsp;&nbsp;';
481 echo '<input type="submit" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
482 echo '</td></tr></table>';
483 // Close form
484 echo '</div>';
485 echo '</form>';
486 }
487 echo '</div>';
488 if (!empty($attempts)) {
489 echo '<table class="boxaligncenter"><tr>';
490 echo '<td>';
5e3014df
DM
491 echo $OUTPUT->single_button(new moodle_url($PAGE->url,
492 array('download'=>'ODS') + $displayoptions),
493 get_string('downloadods'));
62b82cbc
AKA
494 echo "</td>\n";
495 echo '<td>';
5e3014df
DM
496 echo $OUTPUT->single_button(new moodle_url($PAGE->url,
497 array('download'=>'Excel') + $displayoptions),
498 get_string('downloadexcel'));
62b82cbc
AKA
499 echo "</td>\n";
500 echo '<td>';
5e3014df
DM
501 echo $OUTPUT->single_button(new moodle_url($PAGE->url,
502 array('download'=>'CSV') + $displayoptions),
503 get_string('downloadtext'));
62b82cbc
AKA
504 echo "</td>\n";
505 echo "<td>";
506 echo "</td>\n";
507 echo '</tr></table>';
508 }
509 }
510 if (!$download) {
b2614417 511 $mform->set_data(compact('detailedrep', 'pagesize'));
62b82cbc
AKA
512 $mform->display();
513 }
514 } else {
515 echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
516 }
517 if ($download == 'Excel' or $download == 'ODS') {
518 $workbook->close();
519 exit;
520 } else if ($download == 'CSV') {
521 exit;
522 }
523 } else {
524 echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
525 }
62b82cbc 526 }// function ends
62b82cbc 527}