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