XHTML-ize course categories list. MDL-16358 ; merged from 19_STABLE
[moodle.git] / mod / scorm / locallib.php
CommitLineData
e4aa175a 1<?php // $Id$
f69db63e 2
a30b6819 3/// Constants and settings for module scorm
a679d64d 4define('UPDATE_NEVER', '0');
5define('UPDATE_ONCHANGE', '1');
6define('UPDATE_EVERYDAY', '2');
7define('UPDATE_EVERYTIME', '3');
8
b3659259 9define('SCO_ALL', 0);
10define('SCO_DATA', 1);
11define('SCO_ONLY', 2);
a30b6819 12
13define('GRADESCOES', '0');
14define('GRADEHIGHEST', '1');
15define('GRADEAVERAGE', '2');
16define('GRADESUM', '3');
17$SCORM_GRADE_METHOD = array (GRADESCOES => get_string('gradescoes', 'scorm'),
18 GRADEHIGHEST => get_string('gradehighest', 'scorm'),
19 GRADEAVERAGE => get_string('gradeaverage', 'scorm'),
20 GRADESUM => get_string('gradesum', 'scorm'));
21
22define('HIGHESTATTEMPT', '0');
23define('AVERAGEATTEMPT', '1');
24define('FIRSTATTEMPT', '2');
25define('LASTATTEMPT', '3');
26$SCORM_WHAT_GRADE = array (HIGHESTATTEMPT => get_string('highestattempt', 'scorm'),
27 AVERAGEATTEMPT => get_string('averageattempt', 'scorm'),
28 FIRSTATTEMPT => get_string('firstattempt', 'scorm'),
29 LASTATTEMPT => get_string('lastattempt', 'scorm'));
30
31$SCORM_POPUP_OPTIONS = array('resizable'=>1,
32 'scrollbars'=>1,
33 'directories'=>0,
34 'location'=>0,
35 'menubar'=>0,
36 'toolbar'=>0,
37 'status'=>0);
38$stdoptions = '';
39foreach ($SCORM_POPUP_OPTIONS as $popupopt => $value) {
40 $stdoptions .= $popupopt.'='.$value;
41 if ($popupopt != 'status') {
42 $stdoptions .= ',';
43 }
44}
45
46if (!isset($CFG->scorm_maxattempts)) {
47 set_config('scorm_maxattempts','6');
48}
49
50if (!isset($CFG->scorm_frameheight)) {
51 set_config('scorm_frameheight','500');
52}
53
54if (!isset($CFG->scorm_framewidth)) {
55 set_config('scorm_framewidth','100%');
56}
57
8aee93f1 58if (!isset($CFG->scorm_updatetime)) {
59 set_config('scorm_updatetime','2');
60}
61
a30b6819 62if (!isset($CFG->scorm_advancedsettings)) {
63 set_config('scorm_advancedsettings','0');
64}
65
66if (!isset($CFG->scorm_windowsettings)) {
67 set_config('scorm_windowsettings','0');
68}
69
2b3447c3 70/// Local Library of functions for module scorm
f69db63e 71
5c1ac70c 72/**
73* This function will permanently delete the given
74* directory and all files and subdirectories.
75*
76* @param string $directory The directory to remove
77* @return boolean
78*/
79function scorm_delete_files($directory) {
80 if (is_dir($directory)) {
0567b5af 81 $files=scandir($directory);
bd3523a5 82 set_time_limit(0);
5c1ac70c 83 foreach($files as $file) {
84 if (($file != '.') && ($file != '..')) {
85 if (!is_dir($directory.'/'.$file)) {
86 unlink($directory.'/'.$file);
87 } else {
88 scorm_delete_files($directory.'/'.$file);
89 }
f69db63e 90 }
91 }
5c1ac70c 92 rmdir($directory);
93 return true;
f69db63e 94 }
5c1ac70c 95 return false;
f69db63e 96}
97
e4aa175a 98/**
99* Create a new temporary subdirectory with a random name in the given path
100*
101* @param string $strpath The scorm data directory
102* @return string/boolean
103*/
a679d64d 104function scorm_tempdir($strPath)
e4aa175a 105{
106 global $CFG;
107
108 if (is_dir($strPath)) {
109 do {
110 // Create a random string of 8 chars
111 $randstring = NULL;
112 $lchar = '';
113 $len = 8;
114 for ($i=0; $i<$len; $i++) {
115 $char = chr(rand(48,122));
116 while (!ereg('[a-zA-Z0-9]', $char)){
117 if ($char == $lchar) continue;
118 $char = chr(rand(48,90));
119 }
120 $randstring .= $char;
121 $lchar = $char;
122 }
123 $datadir='/'.$randstring;
124 } while (file_exists($strPath.$datadir));
125 mkdir($strPath.$datadir, $CFG->directorypermissions);
126 @chmod($strPath.$datadir, $CFG->directorypermissions); // Just in case mkdir didn't do it
127 return $strPath.$datadir;
128 } else {
129 return false;
130 }
131}
132
2b3447c3 133function scorm_array_search($item, $needle, $haystacks, $strict=false) {
134 if (!empty($haystacks)) {
135 foreach ($haystacks as $key => $element) {
136 if ($strict) {
137 if ($element->{$item} === $needle) {
138 return $key;
139 }
140 } else {
141 if ($element->{$item} == $needle) {
142 return $key;
e4aa175a 143 }
144 }
e4aa175a 145 }
146 }
2b3447c3 147 return false;
e4aa175a 148}
149
2b3447c3 150function scorm_repeater($what, $times) {
151 if ($times <= 0) {
152 return null;
153 }
154 $return = '';
155 for ($i=0; $i<$times;$i++) {
156 $return .= $what;
157 }
158 return $return;
159}
e4aa175a 160
2b3447c3 161function scorm_external_link($link) {
162// check if a link is external
163 $result = false;
164 $link = strtolower($link);
165 if (substr($link,0,7) == 'http://') {
166 $result = true;
167 } else if (substr($link,0,8) == 'https://') {
168 $result = true;
169 } else if (substr($link,0,4) == 'www.') {
170 $result = true;
171 }
172 return $result;
e4aa175a 173}
174
b3659259 175/**
176* Returns an object containing all datas relative to the given sco ID
177*
178* @param integer $id The sco ID
179* @return mixed (false if sco id does not exists)
180*/
bd3523a5 181
b3659259 182function scorm_get_sco($id,$what=SCO_ALL) {
bf347041 183 global $DB;
184
185 if ($sco = $DB->get_record('scorm_scoes', array('id'=>$id))) {
b3659259 186 $sco = ($what == SCO_DATA) ? new stdClass() : $sco;
bf347041 187 if (($what != SCO_ONLY) && ($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id)))) {
b3659259 188 foreach ($scodatas as $scodata) {
c31f631b 189 $sco->{$scodata->name} = $scodata->value;
b3659259 190 }
bf347041 191 } else if (($what != SCO_ONLY) && (!($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id))))) {
bd3523a5 192 $sco->parameters = '';
b3659259 193 }
194 return $sco;
195 } else {
196 return false;
197 }
198}
82605bea 199
200/**
201* Returns an object (array) containing all the scoes data related to the given sco ID
202*
203* @param integer $id The sco ID
204* @param integer $organisation an organisation ID - defaults to false if not required
205* @return mixed (false if there are no scoes or an array)
206*/
207
208function scorm_get_scoes($id,$organisation=false) {
b44c704f 209 global $DB;
210
82605bea 211 $organizationsql = '';
b44c704f 212 $queryarray = array('scorm'=>$id);
82605bea 213 if (!empty($organisation)) {
b44c704f 214 $queryarray['organization'] = $organisation;
82605bea 215 }
b44c704f 216 if ($scoes = $DB->get_records('scorm_scoes', $queryarray, 'id ASC')) {
82605bea 217 // drop keys so that it is a simple array as expected
218 $scoes = array_values($scoes);
219 foreach ($scoes as $sco) {
b44c704f 220 if ($scodatas = $DB->get_records('scorm_scoes_data',array('scoid'=>$sco->id))) {
82605bea 221 foreach ($scodatas as $scodata) {
222 $sco->{$scodata->name} = stripslashes_safe($scodata->value);
223 }
224 }
225 }
226 return $scoes;
227 } else {
228 return false;
229 }
230}
231
e4aa175a 232function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) {
bf347041 233 global $DB;
234
e4aa175a 235 $id = null;
bf347041 236 if ($track = $DB->get_record('scorm_scoes_track',array('userid'=>$userid, 'scormid'=>$scormid, 'scoid'=>$scoid, 'attempt'=>$attempt, 'element'=>$element))) {
e4aa175a 237 $track->value = $value;
238 $track->timemodified = time();
bf347041 239 $id = $DB->update_record('scorm_scoes_track',$track);
e4aa175a 240 } else {
241 $track->userid = $userid;
242 $track->scormid = $scormid;
243 $track->scoid = $scoid;
244 $track->attempt = $attempt;
245 $track->element = $element;
bf347041 246 $track->value = $value;
e4aa175a 247 $track->timemodified = time();
bf347041 248 $id = $DB->insert_record('scorm_scoes_track',$track);
e4aa175a 249 }
d23121ab 250
251 // MDL-9552, update the gradebook everything raw score is sent
a0b36684 252 // Scoring by learning objects also needs to be included in the gradebook update
253 if (strstr($element, '.score.raw') ||
254 (($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && ($track->value == 'completed' || $track->value == 'passed'))) {
255 $scorm = $DB->get_record('scorm', array('id' => $scormid));
256 $grademethod = $scorm->grademethod % 10;
257 if (strstr($element, '.score.raw') || $grademethod == GRADESCOES) {
258 include_once('lib.php');
259 scorm_update_grades($scorm, $userid);
260 }
d23121ab 261 }
262
e4aa175a 263 return $id;
264}
265
e4aa175a 266function scorm_get_tracks($scoid,$userid,$attempt='') {
e4aa175a 267/// Gets all tracks of specified sco and user
bf347041 268 global $CFG, $DB;
e4aa175a 269
270 if (empty($attempt)) {
bf347041 271 if ($scormid = $DB->get_field('scorm_scoes','scorm', array('id'=>$scoid))) {
e4aa175a 272 $attempt = scorm_get_last_attempt($scormid,$userid);
273 } else {
274 $attempt = 1;
275 }
276 }
bf347041 277 if ($tracks = $DB->get_records('scorm_scoes_track', array('userid'=>$userid, 'scoid'=>$scoid, 'attempt'=>$attempt),'element ASC')) {
e4aa175a 278 $usertrack->userid = $userid;
279 $usertrack->scoid = $scoid;
a30b6819 280 // Defined in order to unify scorm1.2 and scorm2004
e4aa175a 281 $usertrack->score_raw = '';
e4aa175a 282 $usertrack->status = '';
e4aa175a 283 $usertrack->total_time = '00:00:00';
284 $usertrack->session_time = '00:00:00';
285 $usertrack->timemodified = 0;
286 foreach ($tracks as $track) {
287 $element = $track->element;
288 $usertrack->{$element} = $track->value;
289 switch ($element) {
f69db63e 290 case 'cmi.core.lesson_status':
291 case 'cmi.completion_status':
292 if ($track->value == 'not attempted') {
293 $track->value = 'notattempted';
294 }
295 $usertrack->status = $track->value;
296 break;
e4aa175a 297 case 'cmi.core.score.raw':
298 case 'cmi.score.raw':
299 $usertrack->score_raw = $track->value;
300 break;
e4aa175a 301 case 'cmi.core.session_time':
302 case 'cmi.session_time':
303 $usertrack->session_time = $track->value;
304 break;
305 case 'cmi.core.total_time':
306 case 'cmi.total_time':
307 $usertrack->total_time = $track->value;
308 break;
309 }
310 if (isset($track->timemodified) && ($track->timemodified > $usertrack->timemodified)) {
311 $usertrack->timemodified = $track->timemodified;
312 }
3505e82b 313 }
07b905ae 314 if (is_array($usertrack)) {
315 ksort($usertrack);
316 }
e4aa175a 317 return $usertrack;
318 } else {
319 return false;
320 }
321}
322
2b3447c3 323function scorm_get_user_data($userid) {
bf347041 324 global $DB;
2b3447c3 325/// Gets user info required to display the table of scorm results
326/// for report.php
e4aa175a 327
bf347041 328 return $DB->get_record('user', array('id'=>$userid),'firstname, lastname, picture');
2b3447c3 329}
e4aa175a 330
a30b6819 331function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) {
bf347041 332 global $DB;
a30b6819 333 $attemptscore = NULL;
334 $attemptscore->scoes = 0;
335 $attemptscore->values = 0;
336 $attemptscore->max = 0;
337 $attemptscore->sum = 0;
338 $attemptscore->lastmodify = 0;
339
bf347041 340 if (!$scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) {
a30b6819 341 return NULL;
e4aa175a 342 }
e4aa175a 343
7ce6eb87 344 // this treatment is necessary as the whatgrade field was not in the DB
345 // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade
346 // and 1s are grademethod
a30b6819 347 $grademethod = $scorm->grademethod % 10;
348
2b3447c3 349 foreach ($scoes as $sco) {
350 if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) {
351 if (($userdata->status == 'completed') || ($userdata->status == 'passed')) {
a30b6819 352 $attemptscore->scoes++;
2b3447c3 353 }
354 if (!empty($userdata->score_raw)) {
a30b6819 355 $attemptscore->values++;
356 $attemptscore->sum += $userdata->score_raw;
357 $attemptscore->max = ($userdata->score_raw > $attemptscore->max)?$userdata->score_raw:$attemptscore->max;
358 if (isset($userdata->timemodified) && ($userdata->timemodified > $attemptscore->lastmodify)) {
359 $attemptscore->lastmodify = $userdata->timemodified;
360 } else {
361 $attemptscore->lastmodify = 0;
362 }
2b3447c3 363 }
364 }
e4aa175a 365 }
2b3447c3 366 switch ($grademethod) {
a30b6819 367 case GRADEHIGHEST:
368 $score = $attemptscore->max;
2b3447c3 369 break;
a30b6819 370 case GRADEAVERAGE:
371 if ($attemptscore->values > 0) {
372 $score = $attemptscore->sum/$attemptscore->values;
5c1ac70c 373 } else {
a30b6819 374 $score = 0;
2b3447c3 375 }
376 break;
a30b6819 377 case GRADESUM:
378 $score = $attemptscore->sum;
2b3447c3 379 break;
a30b6819 380 case GRADESCOES:
381 $score = $attemptscore->scoes;
7ef16bf9 382 break;
383 default:
384 $score = $attemptscore->max; // Remote Learner GRADEHIGHEST is default
5c1ac70c 385 }
a30b6819 386
387 if ($time) {
388 $result = new stdClass();
389 $result->score = $score;
390 $result->time = $attemptscore->lastmodify;
391 } else {
392 $result = $score;
393 }
394
395 return $result;
396}
397
398function scorm_grade_user($scorm, $userid, $time=false) {
7ce6eb87 399 // this treatment is necessary as the whatgrade field was not in the DB
400 // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade
401 // and 1s are grademethod
a30b6819 402 $whatgrade = intval($scorm->grademethod / 10);
403
7ce6eb87 404 // insure we dont grade user beyond $scorm->maxattempt settings
405 $lastattempt = scorm_get_last_attempt($scorm->id, $userid);
406 if($scorm->maxattempt != 0 && $lastattempt >= $scorm->maxattempt){
407 $lastattempt = $scorm->maxattempt;
408 }
409
a30b6819 410 switch ($whatgrade) {
411 case FIRSTATTEMPT:
412 return scorm_grade_user_attempt($scorm, $userid, 1, $time);
413 break;
414 case LASTATTEMPT:
415 return scorm_grade_user_attempt($scorm, $userid, scorm_get_last_attempt($scorm->id, $userid), $time);
416 break;
417 case HIGHESTATTEMPT:
a30b6819 418 $maxscore = 0;
419 $attempttime = 0;
420 for ($attempt = 1; $attempt <= $lastattempt; $attempt++) {
421 $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time);
422 if ($time) {
423 if ($attemptscore->score > $maxscore) {
424 $maxscore = $attemptscore->score;
425 $attempttime = $attemptscore->time;
426 }
427 } else {
428 $maxscore = $attemptscore > $maxscore ? $attemptscore: $maxscore;
429 }
430 }
431 if ($time) {
432 $result = new stdClass();
433 $result->score = $maxscore;
434 $result->time = $attempttime;
435 return $result;
436 } else {
437 return $maxscore;
438 }
439 break;
440 case AVERAGEATTEMPT:
441 $lastattempt = scorm_get_last_attempt($scorm->id, $userid);
442 $sumscore = 0;
443 for ($attempt = 1; $attempt <= $lastattempt; $attempt++) {
444 $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time);
445 if ($time) {
446 $sumscore += $attemptscore->score;
447 } else {
448 $sumscore += $attemptscore;
449 }
450 }
451
452 if ($lastattempt > 0) {
453 $score = $sumscore / $lastattempt;
454 } else {
455 $score = 0;
456 }
457
458 if ($time) {
459 $result = new stdClass();
460 $result->score = $score;
461 $result->time = $attemptscore->time;
462 return $result;
463 } else {
464 return $score;
465 }
466 break;
467 }
e4aa175a 468}
469
8e45ba45 470function scorm_count_launchable($scormid,$organization='') {
bf347041 471 global $DB;
472
473 $sqlorganization = '';
474 $params = array($scormid);
8e45ba45 475 if (!empty($organization)) {
bf347041 476 $sqlorganization = " AND organization=?";
477 $params[] = $organization;
8e45ba45 478 }
6280b17a 479 $params []= ''; // empty launch
480 return $DB->count_records_select('scorm_scoes',"scorm = ? $sqlorganization AND launch <> ?", $params);
e4aa175a 481}
482
2b3447c3 483function scorm_get_last_attempt($scormid, $userid) {
bf347041 484 global $DB;
485
2b3447c3 486/// Find the last attempt number for the given user id and scorm id
bf347041 487 if ($lastattempt = $DB->get_record('scorm_scoes_track', array('userid'=>$userid, 'scormid'=>$scormid), 'max(attempt) as a')) {
2b3447c3 488 if (empty($lastattempt->a)) {
489 return '1';
490 } else {
491 return $lastattempt->a;
e4aa175a 492 }
493 }
e4aa175a 494}
495
e4aa175a 496function scorm_course_format_display($user,$course) {
bf347041 497 global $CFG, $DB;
e4aa175a 498
499 $strupdate = get_string('update');
500 $strmodule = get_string('modulename','scorm');
77bf0c29 501 $context = get_context_instance(CONTEXT_COURSE,$course->id);
e4aa175a 502
503 echo '<div class="mod-scorm">';
504 if ($scorms = get_all_instances_in_course('scorm', $course)) {
505 // The module SCORM activity with the least id is the course
506 $scorm = current($scorms);
507 if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id)) {
08b56f93 508 print_error('invalidcoursemodule');
e4aa175a 509 }
510 $colspan = '';
511 $headertext = '<table width="100%"><tr><td class="title">'.get_string('name').': <b>'.format_string($scorm->name).'</b>';
2b3447c3 512 if (has_capability('moodle/course:manageactivities', $context)) {
e4aa175a 513 if (isediting($course->id)) {
514 // Display update icon
515 $path = $CFG->wwwroot.'/course';
516 $headertext .= '<span class="commands">'.
517 '<a title="'.$strupdate.'" href="'.$path.'/mod.php?update='.$cm->id.'&amp;sesskey='.sesskey().'">'.
0d905d9f 518 '<img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$strupdate.'" /></a></span>';
e4aa175a 519 }
520 $headertext .= '</td>';
521 // Display report link
bf347041 522 $trackedusers = $DB->get_record('scorm_scoes_track', array('scormid'=>$scorm->id), 'count(distinct(userid)) as c');
e4aa175a 523 if ($trackedusers->c > 0) {
524 $headertext .= '<td class="reportlink">'.
fa738731 525 '<a '.$CFG->frametarget.'" href="'.$CFG->wwwroot.'/mod/scorm/report.php?id='.$cm->id.'">'.
e4aa175a 526 get_string('viewallreports','scorm',$trackedusers->c).'</a>';
527 } else {
528 $headertext .= '<td class="reportlink">'.get_string('noreports','scorm');
529 }
530 $colspan = ' colspan="2"';
531 }
532 $headertext .= '</td></tr><tr><td'.$colspan.'>'.format_text(get_string('summary').':<br />'.$scorm->summary).'</td></tr></table>';
533 print_simple_box($headertext,'','100%');
534 scorm_view_display($user, $scorm, 'view.php?id='.$course->id, $cm, '100%');
535 } else {
0d699c24 536 if (has_capability('moodle/course:update', $context)) {
e4aa175a 537 // Create a new activity
2b3447c3 538 redirect($CFG->wwwroot.'/course/mod.php?id='.$course->id.'&amp;section=0&sesskey='.sesskey().'&amp;add=scorm');
e4aa175a 539 } else {
540 notify('Could not find a scorm course here');
541 }
542 }
543 echo '</div>';
544}
545
2b3447c3 546function scorm_view_display ($user, $scorm, $action, $cm, $boxwidth='') {
534792cd 547 global $CFG, $DB;
ab3b00e1 548
549 if ($scorm->updatefreq == UPDATE_EVERYTIME){
5d2b8013 550 require_once($CFG->dirroot.'/mod/scorm/lib.php');
551
ab3b00e1 552 $scorm->instance = $scorm->id;
553 scorm_update_instance($scorm);
554 }
555
e4aa175a 556 $organization = optional_param('organization', '', PARAM_INT);
557
2b3447c3 558 print_simple_box_start('center',$boxwidth);
e4aa175a 559?>
29a43013 560 <div class="structurehead"><?php print_string('contents','scorm') ?></div>
e4aa175a 561<?php
562 if (empty($organization)) {
563 $organization = $scorm->launch;
564 }
07b905ae 565 if ($orgs = $DB->get_records_menu('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''), 'id', 'id,title')) {
e4aa175a 566 if (count($orgs) > 1) {
567 ?>
52a9a9b5 568 <div class='scorm-center'>
e4aa175a 569 <?php print_string('organizations','scorm') ?>
b7dc2256 570 <form id='changeorg' method='post' action='<?php echo $action ?>'>
e4aa175a 571 <?php choose_from_menu($orgs, 'organization', "$organization", '','submit()') ?>
572 </form>
573 </div>
574<?php
575 }
576 }
577 $orgidentifier = '';
b3659259 578 if ($sco = scorm_get_sco($organization, SCO_ONLY)) {
579 if (($sco->organization == '') && ($sco->launch == '')) {
580 $orgidentifier = $sco->identifier;
e4aa175a 581 } else {
b3659259 582 $orgidentifier = $sco->organization;
e4aa175a 583 }
584 }
28ffbff3 585
586/*
587 $orgidentifier = '';
bf347041 588 if ($org = $DB->get_record('scorm_scoes', array('id'=>$organization))) {
28ffbff3 589 if (($org->organization == '') && ($org->launch == '')) {
590 $orgidentifier = $org->identifier;
591 } else {
592 $orgidentifier = $org->organization;
593 }
594 }*/
595
2b3447c3 596 $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe
dbe7e6f6 597 if (!file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php')) {
598 $scorm->version = 'scorm_12';
599 }
2b3447c3 600 require_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php');
601
e4aa175a 602 $result = scorm_get_toc($user,$scorm,'structlist',$orgidentifier);
603 $incomplete = $result->incomplete;
e4aa175a 604 echo $result->toc;
e4aa175a 605 print_simple_box_end();
28ffbff3 606
e4aa175a 607?>
52a9a9b5 608 <div class="scorm-center">
4675994a 609 <form id="theform" method="post" action="<?php echo $CFG->wwwroot ?>/mod/scorm/player.php">
e4aa175a 610 <?php
e4aa175a 611 if ($scorm->hidebrowse == 0) {
8949f8df 612 print_string('mode','scorm');
e4aa175a 613 echo ': <input type="radio" id="b" name="mode" value="browse" /><label for="b">'.get_string('browse','scorm').'</label>'."\n";
76ea4fb4 614 echo '<input type="radio" id="n" name="mode" value="normal" checked="checked" /><label for="n">'.get_string('normal','scorm')."</label>\n";
e4aa175a 615 } else {
76ea4fb4 616 echo '<input type="hidden" name="mode" value="normal" />'."\n";
e4aa175a 617 }
618 if (($incomplete === false) && (($result->attemptleft > 0)||($scorm->maxattempt == 0))) {
619?>
620 <br />
621 <input type="checkbox" id="a" name="newattempt" />
622 <label for="a"><?php print_string('newattempt','scorm') ?></label>
623<?php
624 }
625 ?>
626 <br />
4675994a 627 <input type="hidden" name="scoid"/>
df7ba610 628 <input type="hidden" name="id" value="<?php echo $cm->id ?>"/>
e4aa175a 629 <input type="hidden" name="currentorg" value="<?php echo $orgidentifier ?>" />
2b600d16 630 <input type="submit" value="<?php print_string('enter','scorm') ?>" />
e4aa175a 631 </form>
632 </div>
633<?php
634}
6280b17a 635
28ffbff3 636function scorm_simple_play($scorm,$user) {
bf347041 637 global $DB;
638
28ffbff3 639 $result = false;
640
5b4b959b 641 $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty()));
28ffbff3 642
fa6eb1dc 643 if ($scoes && (count($scoes) == 1)) {
28ffbff3 644 if ($scorm->skipview >= 1) {
645 $sco = current($scoes);
646 if (scorm_get_tracks($sco->id,$user->id) === false) {
45698041 647 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
28ffbff3 648 $result = true;
649 } else if ($scorm->skipview == 2) {
45698041 650 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
28ffbff3 651 $result = true;
652 }
653 }
654 }
28ffbff3 655 return $result;
656}
657/*
8e45ba45 658function scorm_simple_play($scorm,$user) {
bf347041 659 global $DB;
8e45ba45 660 $result = false;
bf347041 661 if ($scoes = $DB->get_records_select('scorm_scoes','scorm=? AND launch<>""', array($scorm->id))) {
76ea4fb4 662 if (count($scoes) == 1) {
663 if ($scorm->skipview >= 1) {
664 $sco = current($scoes);
665 if (scorm_get_tracks($sco->id,$user->id) === false) {
666 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
667 $result = true;
668 } else if ($scorm->skipview == 2) {
669 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
670 $result = true;
671 }
8e45ba45 672 }
673 }
674 }
675 return $result;
676}
28ffbff3 677*/
a30b6819 678function scorm_parse($scorm) {
d9397d51 679 global $CFG;
45698041 680
8949f8df 681 if ($scorm->reference[0] == '#') {
d9397d51 682 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
45698041 683 $referencedir = $CFG->repository.substr($scorm->reference,1);
684 }
8949f8df 685 } else {
45698041 686 if ((!scorm_external_link($scorm->reference)) && (basename($scorm->reference) == 'imsmanifest.xml')) {
687 $referencedir = $CFG->dataroot.'/'.$scorm->course.'/'.$scorm->datadir;
688 } else {
689 $referencedir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm/'.$scorm->id;
690 }
8949f8df 691 }
45698041 692
a30b6819 693 // Parse scorm manifest
694 if ($scorm->pkgtype == 'AICC') {
695 require_once('datamodels/aicclib.php');
45698041 696 $scorm->launch = scorm_parse_aicc($referencedir, $scorm->id);
a30b6819 697 } else {
698 require_once('datamodels/scormlib.php');
45698041 699 $scorm->launch = scorm_parse_scorm($referencedir,$scorm->id);
a30b6819 700 }
701 return $scorm->launch;
702}
703
76ea4fb4 704/**
705* Given a manifest path, this function will check if the manifest is valid
706*
707* @param string $manifest The manifest file
708* @return object
709*/
710function scorm_validate_manifest($manifest) {
711 $validation = new stdClass();
712 if (is_file($manifest)) {
a679d64d 713 $validation->result = true;
76ea4fb4 714 } else {
a679d64d 715 $validation->result = false;
716 $validation->errors['reference'] = get_string('nomanifest','scorm');
76ea4fb4 717 }
718 return $validation;
719}
720
721/**
722* Given a aicc package directory, this function will check if the course structure is valid
723*
724* @param string $packagedir The aicc package directory path
725* @return object
726*/
727function scorm_validate_aicc($packagedir) {
728 $validation = new stdClass();
a679d64d 729 $validation->result = false;
76ea4fb4 730 if (is_dir($packagedir)) {
731 if ($handle = opendir($packagedir)) {
732 while (($file = readdir($handle)) !== false) {
733 $ext = substr($file,strrpos($file,'.'));
734 if (strtolower($ext) == '.cst') {
a679d64d 735 $validation->result = true;
76ea4fb4 736 break;
737 }
738 }
739 closedir($handle);
740 }
741 }
a679d64d 742 if ($validation->result == false) {
743 $validation->errors['reference'] = get_string('nomanifest','scorm');
76ea4fb4 744 }
745 return $validation;
746}
747
748
749function scorm_validate($data) {
bf347041 750 global $CFG, $DB;
76ea4fb4 751
a679d64d 752 $validation = new stdClass();
753 $validation->errors = array();
754
755 if (!isset($data['course']) || empty($data['course'])) {
756 $validation->errors['reference'] = get_string('missingparam','scorm');
757 $validation->result = false;
758 return $validation;
759 }
760 $courseid = $data['course']; // Course Module ID
761
762 if (!isset($data['reference']) || empty($data['reference'])) {
763 $validation->errors['reference'] = get_string('packagefile','scorm');
764 $validation->result = false;
765 return $validation;
766 }
767 $reference = $data['reference']; // Package/manifest path/location
768
769 $scormid = $data['instance']; // scorm ID
770 $scorm = new stdClass();
771 if (!empty($scormid)) {
bf347041 772 if (!$scorm = $DB->get_record('scorm', array('id'=>$scormid))) {
a679d64d 773 $validation->errors['reference'] = get_string('missingparam','scorm');
774 $validation->result = false;
775 return $validation;
776 }
777 }
778
779 if ($reference[0] == '#') {
d9397d51 780 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
a679d64d 781 $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml';
782 } else {
783 $validation->errors['reference'] = get_string('badpackage','scorm');
784 $validation->result = false;
785 return $validation;
786 }
787 } else if (!scorm_external_link($reference)) {
788 $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference;
789 }
790
791 // Create a temporary directory to unzip package or copy manifest and validate package
792 $tempdir = '';
793 $scormdir = '';
794 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
795 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 796 $localreference = $tempdir.'/'.basename($reference);
a679d64d 797 copy ("$reference", $localreference);
798 if (!is_file($localreference)) {
799 $validation->errors['reference'] = get_string('badpackage','scorm');
800 $validation->result = false;
801 } else {
802 $ext = strtolower(substr(basename($localreference),strrpos(basename($localreference),'.')));
803 switch ($ext) {
804 case '.pif':
805 case '.zip':
806 if (!unzip_file($localreference, $tempdir, false)) {
807 $validation->errors['reference'] = get_string('unziperror','scorm');
808 $validation->result = false;
809 } else {
810 unlink ($localreference);
811 if (is_file($tempdir.'/imsmanifest.xml')) {
812 $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml');
813 $validation->pkgtype = 'SCORM';
814 } else {
815 $validation = scorm_validate_aicc($tempdir);
816 if (($validation->result == 'regular') || ($validation->result == 'found')) {
817 $validation->pkgtype = 'AICC';
818 } else {
819 $validation->errors['reference'] = get_string('nomanifest','scorm');
820 $validation->result = false;
821 }
822 }
823 }
824 break;
825 case '.xml':
826 if (basename($localreference) == 'imsmanifest.xml') {
827 $validation = scorm_validate_manifest($localreference);
828 } else {
829 $validation->errors['reference'] = get_string('nomanifest','scorm');
830 $validation->result = false;
831 }
832 break;
833 default:
834 $validation->errors['reference'] = get_string('badpackage','scorm');
835 $validation->result = false;
836 break;
837 }
838 }
839 if (is_dir($tempdir)) {
840 // Delete files and temporary directory
841 scorm_delete_files($tempdir);
842 }
843 } else {
844 $validation->errors['reference'] = get_string('packagedir','scorm');
845 $validation->result = false;
846 }
847 } else {
848 $validation->errors['reference'] = get_string('datadir','scorm');
849 $validation->result = false;
850 }
851 return $validation;
852}
853
854function scorm_check_package($data) {
bf347041 855 global $CFG, $COURSE, $DB;
a679d64d 856
76ea4fb4 857 $courseid = $data->course; // Course Module ID
858 $reference = $data->reference; // Package path
859 $scormid = $data->instance; // scorm ID
860
861 $validation = new stdClass();
a679d64d 862
863 if (!empty($courseid) && !empty($reference)) {
864 $externalpackage = scorm_external_link($reference);
865
76ea4fb4 866 $validation->launch = 0;
76ea4fb4 867 $referencefield = $reference;
868 if (empty($reference)) {
a679d64d 869 $validation = null;
76ea4fb4 870 } else if ($reference[0] == '#') {
d9397d51 871 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
658e2574 872 $referencefield = $reference.'/imsmanifest.xml';
76ea4fb4 873 $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml';
874 } else {
a679d64d 875 $validation = null;
76ea4fb4 876 }
a679d64d 877 } else if (!$externalpackage) {
76ea4fb4 878 $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference;
879 }
880
881 if (!empty($scormid)) {
882 //
883 // SCORM Update
884 //
a679d64d 885 if ((!empty($validation)) && (is_file($reference) || $externalpackage)){
8aee93f1 886
a679d64d 887 if (!$externalpackage) {
888 $mdcheck = md5_file($reference);
889 } else if ($externalpackage){
890 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
891 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 892 copy ("$reference", $tempdir.'/'.basename($reference));
893 $mdcheck = md5_file($tempdir.'/'.basename($reference));
a679d64d 894 scorm_delete_files($tempdir);
895 }
896 }
897 }
8aee93f1 898
bf347041 899 if ($scorm = $DB->get_record('scorm', array('id'=>$scormid))) {
76ea4fb4 900 if ($scorm->reference[0] == '#') {
d9397d51 901 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
76ea4fb4 902 $oldreference = $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml';
903 } else {
904 $oldreference = $scorm->reference;
905 }
a679d64d 906 } else if (!scorm_external_link($scorm->reference)) {
76ea4fb4 907 $oldreference = $CFG->dataroot.'/'.$courseid.'/'.$scorm->reference;
a679d64d 908 } else {
909 $oldreference = $scorm->reference;
910 }
76ea4fb4 911 $validation->launch = $scorm->launch;
a679d64d 912 if ((($oldreference == $reference) && ($mdcheck != $scorm->md5hash)) || ($oldreference != $reference)) {
76ea4fb4 913 // This is a new or a modified package
914 $validation->launch = 0;
915 } else {
916 // Old package already validated
76ea4fb4 917 if (strpos($scorm->version,'AICC') !== false) {
918 $validation->pkgtype = 'AICC';
919 } else {
920 $validation->pkgtype = 'SCORM';
921 }
922 }
923 } else {
a679d64d 924 $validation = null;
76ea4fb4 925 }
926 } else {
a679d64d 927 $validation = null;
76ea4fb4 928 }
929 }
930 //$validation->launch = 0;
a679d64d 931 if (($validation != null) && ($validation->launch == 0)) {
76ea4fb4 932 //
933 // Package must be validated
934 //
935 $ext = strtolower(substr(basename($reference),strrpos(basename($reference),'.')));
a679d64d 936 $tempdir = '';
76ea4fb4 937 switch ($ext) {
938 case '.pif':
939 case '.zip':
940 // Create a temporary directory to unzip package and validate package
76ea4fb4 941 $scormdir = '';
942 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
a679d64d 943 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 944 copy ("$reference", $tempdir.'/'.basename($reference));
945 unzip_file($tempdir.'/'.basename($reference), $tempdir, false);
82e34576 946 if (!$externalpackage) {
8949f8df 947 unlink ($tempdir.'/'.basename($reference));
82e34576 948 }
76ea4fb4 949 if (is_file($tempdir.'/imsmanifest.xml')) {
950 $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml');
951 $validation->pkgtype = 'SCORM';
952 } else {
953 $validation = scorm_validate_aicc($tempdir);
954 $validation->pkgtype = 'AICC';
955 }
956 } else {
a679d64d 957 $validation = null;
76ea4fb4 958 }
959 } else {
a679d64d 960 $validation = null;
76ea4fb4 961 }
962 break;
963 case '.xml':
964 if (basename($reference) == 'imsmanifest.xml') {
a679d64d 965 if ($externalpackage) {
966 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
967 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 968 copy ("$reference", $tempdir.'/'.basename($reference));
969 if (is_file($tempdir.'/'.basename($reference))) {
ab3b00e1 970 $validation = scorm_validate_manifest($tempdir.'/'.basename($reference));
971 } else {
972 $validation = null;
973 }
a679d64d 974 }
975 }
976 } else {
45698041 977 $validation = scorm_validate_manifest($reference);
a679d64d 978 }
ab3b00e1 979 $validation->pkgtype = 'SCORM';
76ea4fb4 980 } else {
a679d64d 981 $validation = null;
76ea4fb4 982 }
983 break;
984 default:
a679d64d 985 $validation = null;
76ea4fb4 986 break;
987 }
a679d64d 988 if ($validation == null) {
76ea4fb4 989 if (is_dir($tempdir)) {
990 // Delete files and temporary directory
991 scorm_delete_files($tempdir);
992 }
76ea4fb4 993 } else {
ab3b00e1 994 if (($ext == '.xml') && (!$externalpackage)) {
76ea4fb4 995 $validation->datadir = dirname($referencefield);
996 } else {
997 $validation->datadir = substr($tempdir,strlen($scormdir));
998 }
999 $validation->launch = 0;
1000 }
1001 }
1002 } else {
a679d64d 1003 $validation = null;
76ea4fb4 1004 }
1005 return $validation;
1006}
1007
d8c9d8a1 1008
1009function scorm_get_count_users($scormid, $groupingid=null) {
bf347041 1010 global $CFG, $DB;
d8c9d8a1 1011
1012 if (!empty($CFG->enablegroupings) && !empty($groupingid)) {
1013 $sql = "SELECT COUNT(DISTINCT st.userid)
bf347041 1014 FROM {scorm_scoes_track} st
1015 INNER JOIN {groups_members} gm ON st.userid = gm.userid
1016 INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid
1017 WHERE st.scormid = ? AND gg.groupingid = ?
d8c9d8a1 1018 ";
bf347041 1019 $params = array($scormid, $groupingid);
d8c9d8a1 1020 } else {
1021 $sql = "SELECT COUNT(DISTINCT st.userid)
bf347041 1022 FROM {scorm_scoes_track} st
1023 WHERE st.scormid = ?
d8c9d8a1 1024 ";
bf347041 1025 $params = array($scormid);
d8c9d8a1 1026 }
1027
bf347041 1028 return ($DB->count_records_sql($sql, $params));
d8c9d8a1 1029}
1030
b44c704f 1031?>