PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/scorm/report/basic/report.php

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