MDL-15799 LDAP - user data mapping doesn't work. Merged from MOODLE_18_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
252 if (strstr($element, '.score.raw')) {
bf347041 253 $scorm = $DB->get_record('scorm', array('id'=>$scormid));
d23121ab 254 include_once('lib.php');
255 scorm_update_grades($scorm, $userid);
256 }
257
e4aa175a 258 return $id;
259}
260
e4aa175a 261function scorm_get_tracks($scoid,$userid,$attempt='') {
e4aa175a 262/// Gets all tracks of specified sco and user
bf347041 263 global $CFG, $DB;
e4aa175a 264
265 if (empty($attempt)) {
bf347041 266 if ($scormid = $DB->get_field('scorm_scoes','scorm', array('id'=>$scoid))) {
e4aa175a 267 $attempt = scorm_get_last_attempt($scormid,$userid);
268 } else {
269 $attempt = 1;
270 }
271 }
bf347041 272 if ($tracks = $DB->get_records('scorm_scoes_track', array('userid'=>$userid, 'scoid'=>$scoid, 'attempt'=>$attempt),'element ASC')) {
e4aa175a 273 $usertrack->userid = $userid;
274 $usertrack->scoid = $scoid;
a30b6819 275 // Defined in order to unify scorm1.2 and scorm2004
e4aa175a 276 $usertrack->score_raw = '';
e4aa175a 277 $usertrack->status = '';
e4aa175a 278 $usertrack->total_time = '00:00:00';
279 $usertrack->session_time = '00:00:00';
280 $usertrack->timemodified = 0;
281 foreach ($tracks as $track) {
282 $element = $track->element;
283 $usertrack->{$element} = $track->value;
284 switch ($element) {
f69db63e 285 case 'cmi.core.lesson_status':
286 case 'cmi.completion_status':
287 if ($track->value == 'not attempted') {
288 $track->value = 'notattempted';
289 }
290 $usertrack->status = $track->value;
291 break;
e4aa175a 292 case 'cmi.core.score.raw':
293 case 'cmi.score.raw':
294 $usertrack->score_raw = $track->value;
295 break;
e4aa175a 296 case 'cmi.core.session_time':
297 case 'cmi.session_time':
298 $usertrack->session_time = $track->value;
299 break;
300 case 'cmi.core.total_time':
301 case 'cmi.total_time':
302 $usertrack->total_time = $track->value;
303 break;
304 }
305 if (isset($track->timemodified) && ($track->timemodified > $usertrack->timemodified)) {
306 $usertrack->timemodified = $track->timemodified;
307 }
3505e82b 308 }
07b905ae 309 if (is_array($usertrack)) {
310 ksort($usertrack);
311 }
e4aa175a 312 return $usertrack;
313 } else {
314 return false;
315 }
316}
317
2b3447c3 318function scorm_get_user_data($userid) {
bf347041 319 global $DB;
2b3447c3 320/// Gets user info required to display the table of scorm results
321/// for report.php
e4aa175a 322
bf347041 323 return $DB->get_record('user', array('id'=>$userid),'firstname, lastname, picture');
2b3447c3 324}
e4aa175a 325
a30b6819 326function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) {
bf347041 327 global $DB;
a30b6819 328 $attemptscore = NULL;
329 $attemptscore->scoes = 0;
330 $attemptscore->values = 0;
331 $attemptscore->max = 0;
332 $attemptscore->sum = 0;
333 $attemptscore->lastmodify = 0;
334
bf347041 335 if (!$scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) {
a30b6819 336 return NULL;
e4aa175a 337 }
e4aa175a 338
7ce6eb87 339 // this treatment is necessary as the whatgrade field was not in the DB
340 // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade
341 // and 1s are grademethod
a30b6819 342 $grademethod = $scorm->grademethod % 10;
343
2b3447c3 344 foreach ($scoes as $sco) {
345 if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) {
346 if (($userdata->status == 'completed') || ($userdata->status == 'passed')) {
a30b6819 347 $attemptscore->scoes++;
2b3447c3 348 }
349 if (!empty($userdata->score_raw)) {
a30b6819 350 $attemptscore->values++;
351 $attemptscore->sum += $userdata->score_raw;
352 $attemptscore->max = ($userdata->score_raw > $attemptscore->max)?$userdata->score_raw:$attemptscore->max;
353 if (isset($userdata->timemodified) && ($userdata->timemodified > $attemptscore->lastmodify)) {
354 $attemptscore->lastmodify = $userdata->timemodified;
355 } else {
356 $attemptscore->lastmodify = 0;
357 }
2b3447c3 358 }
359 }
e4aa175a 360 }
2b3447c3 361 switch ($grademethod) {
a30b6819 362 case GRADEHIGHEST:
363 $score = $attemptscore->max;
2b3447c3 364 break;
a30b6819 365 case GRADEAVERAGE:
366 if ($attemptscore->values > 0) {
367 $score = $attemptscore->sum/$attemptscore->values;
5c1ac70c 368 } else {
a30b6819 369 $score = 0;
2b3447c3 370 }
371 break;
a30b6819 372 case GRADESUM:
373 $score = $attemptscore->sum;
2b3447c3 374 break;
a30b6819 375 case GRADESCOES:
376 $score = $attemptscore->scoes;
7ef16bf9 377 break;
378 default:
379 $score = $attemptscore->max; // Remote Learner GRADEHIGHEST is default
5c1ac70c 380 }
a30b6819 381
382 if ($time) {
383 $result = new stdClass();
384 $result->score = $score;
385 $result->time = $attemptscore->lastmodify;
386 } else {
387 $result = $score;
388 }
389
390 return $result;
391}
392
393function scorm_grade_user($scorm, $userid, $time=false) {
7ce6eb87 394 // this treatment is necessary as the whatgrade field was not in the DB
395 // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade
396 // and 1s are grademethod
a30b6819 397 $whatgrade = intval($scorm->grademethod / 10);
398
7ce6eb87 399 // insure we dont grade user beyond $scorm->maxattempt settings
400 $lastattempt = scorm_get_last_attempt($scorm->id, $userid);
401 if($scorm->maxattempt != 0 && $lastattempt >= $scorm->maxattempt){
402 $lastattempt = $scorm->maxattempt;
403 }
404
a30b6819 405 switch ($whatgrade) {
406 case FIRSTATTEMPT:
407 return scorm_grade_user_attempt($scorm, $userid, 1, $time);
408 break;
409 case LASTATTEMPT:
410 return scorm_grade_user_attempt($scorm, $userid, scorm_get_last_attempt($scorm->id, $userid), $time);
411 break;
412 case HIGHESTATTEMPT:
a30b6819 413 $maxscore = 0;
414 $attempttime = 0;
415 for ($attempt = 1; $attempt <= $lastattempt; $attempt++) {
416 $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time);
417 if ($time) {
418 if ($attemptscore->score > $maxscore) {
419 $maxscore = $attemptscore->score;
420 $attempttime = $attemptscore->time;
421 }
422 } else {
423 $maxscore = $attemptscore > $maxscore ? $attemptscore: $maxscore;
424 }
425 }
426 if ($time) {
427 $result = new stdClass();
428 $result->score = $maxscore;
429 $result->time = $attempttime;
430 return $result;
431 } else {
432 return $maxscore;
433 }
434 break;
435 case AVERAGEATTEMPT:
436 $lastattempt = scorm_get_last_attempt($scorm->id, $userid);
437 $sumscore = 0;
438 for ($attempt = 1; $attempt <= $lastattempt; $attempt++) {
439 $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time);
440 if ($time) {
441 $sumscore += $attemptscore->score;
442 } else {
443 $sumscore += $attemptscore;
444 }
445 }
446
447 if ($lastattempt > 0) {
448 $score = $sumscore / $lastattempt;
449 } else {
450 $score = 0;
451 }
452
453 if ($time) {
454 $result = new stdClass();
455 $result->score = $score;
456 $result->time = $attemptscore->time;
457 return $result;
458 } else {
459 return $score;
460 }
461 break;
462 }
e4aa175a 463}
464
8e45ba45 465function scorm_count_launchable($scormid,$organization='') {
bf347041 466 global $DB;
467
468 $sqlorganization = '';
469 $params = array($scormid);
8e45ba45 470 if (!empty($organization)) {
bf347041 471 $sqlorganization = " AND organization=?";
472 $params[] = $organization;
8e45ba45 473 }
6280b17a 474 $params []= ''; // empty launch
475 return $DB->count_records_select('scorm_scoes',"scorm = ? $sqlorganization AND launch <> ?", $params);
e4aa175a 476}
477
2b3447c3 478function scorm_get_last_attempt($scormid, $userid) {
bf347041 479 global $DB;
480
2b3447c3 481/// Find the last attempt number for the given user id and scorm id
bf347041 482 if ($lastattempt = $DB->get_record('scorm_scoes_track', array('userid'=>$userid, 'scormid'=>$scormid), 'max(attempt) as a')) {
2b3447c3 483 if (empty($lastattempt->a)) {
484 return '1';
485 } else {
486 return $lastattempt->a;
e4aa175a 487 }
488 }
e4aa175a 489}
490
e4aa175a 491function scorm_course_format_display($user,$course) {
bf347041 492 global $CFG, $DB;
e4aa175a 493
494 $strupdate = get_string('update');
495 $strmodule = get_string('modulename','scorm');
77bf0c29 496 $context = get_context_instance(CONTEXT_COURSE,$course->id);
e4aa175a 497
498 echo '<div class="mod-scorm">';
499 if ($scorms = get_all_instances_in_course('scorm', $course)) {
500 // The module SCORM activity with the least id is the course
501 $scorm = current($scorms);
502 if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id)) {
08b56f93 503 print_error('invalidcoursemodule');
e4aa175a 504 }
505 $colspan = '';
506 $headertext = '<table width="100%"><tr><td class="title">'.get_string('name').': <b>'.format_string($scorm->name).'</b>';
2b3447c3 507 if (has_capability('moodle/course:manageactivities', $context)) {
e4aa175a 508 if (isediting($course->id)) {
509 // Display update icon
510 $path = $CFG->wwwroot.'/course';
511 $headertext .= '<span class="commands">'.
512 '<a title="'.$strupdate.'" href="'.$path.'/mod.php?update='.$cm->id.'&amp;sesskey='.sesskey().'">'.
0d905d9f 513 '<img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$strupdate.'" /></a></span>';
e4aa175a 514 }
515 $headertext .= '</td>';
516 // Display report link
bf347041 517 $trackedusers = $DB->get_record('scorm_scoes_track', array('scormid'=>$scorm->id), 'count(distinct(userid)) as c');
e4aa175a 518 if ($trackedusers->c > 0) {
519 $headertext .= '<td class="reportlink">'.
fa738731 520 '<a '.$CFG->frametarget.'" href="'.$CFG->wwwroot.'/mod/scorm/report.php?id='.$cm->id.'">'.
e4aa175a 521 get_string('viewallreports','scorm',$trackedusers->c).'</a>';
522 } else {
523 $headertext .= '<td class="reportlink">'.get_string('noreports','scorm');
524 }
525 $colspan = ' colspan="2"';
526 }
527 $headertext .= '</td></tr><tr><td'.$colspan.'>'.format_text(get_string('summary').':<br />'.$scorm->summary).'</td></tr></table>';
528 print_simple_box($headertext,'','100%');
529 scorm_view_display($user, $scorm, 'view.php?id='.$course->id, $cm, '100%');
530 } else {
0d699c24 531 if (has_capability('moodle/course:update', $context)) {
e4aa175a 532 // Create a new activity
2b3447c3 533 redirect($CFG->wwwroot.'/course/mod.php?id='.$course->id.'&amp;section=0&sesskey='.sesskey().'&amp;add=scorm');
e4aa175a 534 } else {
535 notify('Could not find a scorm course here');
536 }
537 }
538 echo '</div>';
539}
540
2b3447c3 541function scorm_view_display ($user, $scorm, $action, $cm, $boxwidth='') {
534792cd 542 global $CFG, $DB;
ab3b00e1 543
544 if ($scorm->updatefreq == UPDATE_EVERYTIME){
5d2b8013 545 require_once($CFG->dirroot.'/mod/scorm/lib.php');
546
ab3b00e1 547 $scorm->instance = $scorm->id;
548 scorm_update_instance($scorm);
549 }
550
e4aa175a 551 $organization = optional_param('organization', '', PARAM_INT);
552
2b3447c3 553 print_simple_box_start('center',$boxwidth);
e4aa175a 554?>
29a43013 555 <div class="structurehead"><?php print_string('contents','scorm') ?></div>
e4aa175a 556<?php
557 if (empty($organization)) {
558 $organization = $scorm->launch;
559 }
07b905ae 560 if ($orgs = $DB->get_records_menu('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''), 'id', 'id,title')) {
e4aa175a 561 if (count($orgs) > 1) {
562 ?>
52a9a9b5 563 <div class='scorm-center'>
e4aa175a 564 <?php print_string('organizations','scorm') ?>
b7dc2256 565 <form id='changeorg' method='post' action='<?php echo $action ?>'>
e4aa175a 566 <?php choose_from_menu($orgs, 'organization', "$organization", '','submit()') ?>
567 </form>
568 </div>
569<?php
570 }
571 }
572 $orgidentifier = '';
b3659259 573 if ($sco = scorm_get_sco($organization, SCO_ONLY)) {
574 if (($sco->organization == '') && ($sco->launch == '')) {
575 $orgidentifier = $sco->identifier;
e4aa175a 576 } else {
b3659259 577 $orgidentifier = $sco->organization;
e4aa175a 578 }
579 }
28ffbff3 580
581/*
582 $orgidentifier = '';
bf347041 583 if ($org = $DB->get_record('scorm_scoes', array('id'=>$organization))) {
28ffbff3 584 if (($org->organization == '') && ($org->launch == '')) {
585 $orgidentifier = $org->identifier;
586 } else {
587 $orgidentifier = $org->organization;
588 }
589 }*/
590
2b3447c3 591 $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe
dbe7e6f6 592 if (!file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php')) {
593 $scorm->version = 'scorm_12';
594 }
2b3447c3 595 require_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php');
596
e4aa175a 597 $result = scorm_get_toc($user,$scorm,'structlist',$orgidentifier);
598 $incomplete = $result->incomplete;
e4aa175a 599 echo $result->toc;
e4aa175a 600 print_simple_box_end();
28ffbff3 601
e4aa175a 602?>
52a9a9b5 603 <div class="scorm-center">
1436d553 604 <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 605 <?php
e4aa175a 606 if ($scorm->hidebrowse == 0) {
8949f8df 607 print_string('mode','scorm');
97be0533 608 echo '<input type="hidden" name="scoid" value="'.$sco->id.'" />'."\n";
e4aa175a 609 echo ': <input type="radio" id="b" name="mode" value="browse" /><label for="b">'.get_string('browse','scorm').'</label>'."\n";
76ea4fb4 610 echo '<input type="radio" id="n" name="mode" value="normal" checked="checked" /><label for="n">'.get_string('normal','scorm')."</label>\n";
e4aa175a 611 } else {
76ea4fb4 612 echo '<input type="hidden" name="mode" value="normal" />'."\n";
e4aa175a 613 }
614 if (($incomplete === false) && (($result->attemptleft > 0)||($scorm->maxattempt == 0))) {
615?>
616 <br />
617 <input type="checkbox" id="a" name="newattempt" />
618 <label for="a"><?php print_string('newattempt','scorm') ?></label>
619<?php
620 }
621 ?>
622 <br />
28ffbff3 623 <input type="hidden" name="scoid"/>
e4aa175a 624 <input type="hidden" name="currentorg" value="<?php echo $orgidentifier ?>" />
2b600d16 625 <input type="submit" value="<?php print_string('enter','scorm') ?>" />
e4aa175a 626 </form>
627 </div>
628<?php
629}
6280b17a 630
28ffbff3 631function scorm_simple_play($scorm,$user) {
bf347041 632 global $DB;
633
28ffbff3 634 $result = false;
635
5b4b959b 636 $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty()));
28ffbff3 637
fa6eb1dc 638 if ($scoes && (count($scoes) == 1)) {
28ffbff3 639 if ($scorm->skipview >= 1) {
640 $sco = current($scoes);
641 if (scorm_get_tracks($sco->id,$user->id) === false) {
45698041 642 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
28ffbff3 643 $result = true;
644 } else if ($scorm->skipview == 2) {
45698041 645 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
28ffbff3 646 $result = true;
647 }
648 }
649 }
28ffbff3 650 return $result;
651}
652/*
8e45ba45 653function scorm_simple_play($scorm,$user) {
bf347041 654 global $DB;
8e45ba45 655 $result = false;
bf347041 656 if ($scoes = $DB->get_records_select('scorm_scoes','scorm=? AND launch<>""', array($scorm->id))) {
76ea4fb4 657 if (count($scoes) == 1) {
658 if ($scorm->skipview >= 1) {
659 $sco = current($scoes);
660 if (scorm_get_tracks($sco->id,$user->id) === false) {
661 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
662 $result = true;
663 } else if ($scorm->skipview == 2) {
664 header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id);
665 $result = true;
666 }
8e45ba45 667 }
668 }
669 }
670 return $result;
671}
28ffbff3 672*/
a30b6819 673function scorm_parse($scorm) {
d9397d51 674 global $CFG;
45698041 675
8949f8df 676 if ($scorm->reference[0] == '#') {
d9397d51 677 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
45698041 678 $referencedir = $CFG->repository.substr($scorm->reference,1);
679 }
8949f8df 680 } else {
45698041 681 if ((!scorm_external_link($scorm->reference)) && (basename($scorm->reference) == 'imsmanifest.xml')) {
682 $referencedir = $CFG->dataroot.'/'.$scorm->course.'/'.$scorm->datadir;
683 } else {
684 $referencedir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm/'.$scorm->id;
685 }
8949f8df 686 }
45698041 687
a30b6819 688 // Parse scorm manifest
689 if ($scorm->pkgtype == 'AICC') {
690 require_once('datamodels/aicclib.php');
45698041 691 $scorm->launch = scorm_parse_aicc($referencedir, $scorm->id);
a30b6819 692 } else {
693 require_once('datamodels/scormlib.php');
45698041 694 $scorm->launch = scorm_parse_scorm($referencedir,$scorm->id);
a30b6819 695 }
696 return $scorm->launch;
697}
698
76ea4fb4 699/**
700* Given a manifest path, this function will check if the manifest is valid
701*
702* @param string $manifest The manifest file
703* @return object
704*/
705function scorm_validate_manifest($manifest) {
706 $validation = new stdClass();
707 if (is_file($manifest)) {
a679d64d 708 $validation->result = true;
76ea4fb4 709 } else {
a679d64d 710 $validation->result = false;
711 $validation->errors['reference'] = get_string('nomanifest','scorm');
76ea4fb4 712 }
713 return $validation;
714}
715
716/**
717* Given a aicc package directory, this function will check if the course structure is valid
718*
719* @param string $packagedir The aicc package directory path
720* @return object
721*/
722function scorm_validate_aicc($packagedir) {
723 $validation = new stdClass();
a679d64d 724 $validation->result = false;
76ea4fb4 725 if (is_dir($packagedir)) {
726 if ($handle = opendir($packagedir)) {
727 while (($file = readdir($handle)) !== false) {
728 $ext = substr($file,strrpos($file,'.'));
729 if (strtolower($ext) == '.cst') {
a679d64d 730 $validation->result = true;
76ea4fb4 731 break;
732 }
733 }
734 closedir($handle);
735 }
736 }
a679d64d 737 if ($validation->result == false) {
738 $validation->errors['reference'] = get_string('nomanifest','scorm');
76ea4fb4 739 }
740 return $validation;
741}
742
743
744function scorm_validate($data) {
bf347041 745 global $CFG, $DB;
76ea4fb4 746
a679d64d 747 $validation = new stdClass();
748 $validation->errors = array();
749
750 if (!isset($data['course']) || empty($data['course'])) {
751 $validation->errors['reference'] = get_string('missingparam','scorm');
752 $validation->result = false;
753 return $validation;
754 }
755 $courseid = $data['course']; // Course Module ID
756
757 if (!isset($data['reference']) || empty($data['reference'])) {
758 $validation->errors['reference'] = get_string('packagefile','scorm');
759 $validation->result = false;
760 return $validation;
761 }
762 $reference = $data['reference']; // Package/manifest path/location
763
764 $scormid = $data['instance']; // scorm ID
765 $scorm = new stdClass();
766 if (!empty($scormid)) {
bf347041 767 if (!$scorm = $DB->get_record('scorm', array('id'=>$scormid))) {
a679d64d 768 $validation->errors['reference'] = get_string('missingparam','scorm');
769 $validation->result = false;
770 return $validation;
771 }
772 }
773
774 if ($reference[0] == '#') {
d9397d51 775 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
a679d64d 776 $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml';
777 } else {
778 $validation->errors['reference'] = get_string('badpackage','scorm');
779 $validation->result = false;
780 return $validation;
781 }
782 } else if (!scorm_external_link($reference)) {
783 $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference;
784 }
785
786 // Create a temporary directory to unzip package or copy manifest and validate package
787 $tempdir = '';
788 $scormdir = '';
789 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
790 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 791 $localreference = $tempdir.'/'.basename($reference);
a679d64d 792 copy ("$reference", $localreference);
793 if (!is_file($localreference)) {
794 $validation->errors['reference'] = get_string('badpackage','scorm');
795 $validation->result = false;
796 } else {
797 $ext = strtolower(substr(basename($localreference),strrpos(basename($localreference),'.')));
798 switch ($ext) {
799 case '.pif':
800 case '.zip':
801 if (!unzip_file($localreference, $tempdir, false)) {
802 $validation->errors['reference'] = get_string('unziperror','scorm');
803 $validation->result = false;
804 } else {
805 unlink ($localreference);
806 if (is_file($tempdir.'/imsmanifest.xml')) {
807 $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml');
808 $validation->pkgtype = 'SCORM';
809 } else {
810 $validation = scorm_validate_aicc($tempdir);
811 if (($validation->result == 'regular') || ($validation->result == 'found')) {
812 $validation->pkgtype = 'AICC';
813 } else {
814 $validation->errors['reference'] = get_string('nomanifest','scorm');
815 $validation->result = false;
816 }
817 }
818 }
819 break;
820 case '.xml':
821 if (basename($localreference) == 'imsmanifest.xml') {
822 $validation = scorm_validate_manifest($localreference);
823 } else {
824 $validation->errors['reference'] = get_string('nomanifest','scorm');
825 $validation->result = false;
826 }
827 break;
828 default:
829 $validation->errors['reference'] = get_string('badpackage','scorm');
830 $validation->result = false;
831 break;
832 }
833 }
834 if (is_dir($tempdir)) {
835 // Delete files and temporary directory
836 scorm_delete_files($tempdir);
837 }
838 } else {
839 $validation->errors['reference'] = get_string('packagedir','scorm');
840 $validation->result = false;
841 }
842 } else {
843 $validation->errors['reference'] = get_string('datadir','scorm');
844 $validation->result = false;
845 }
846 return $validation;
847}
848
849function scorm_check_package($data) {
bf347041 850 global $CFG, $COURSE, $DB;
a679d64d 851
76ea4fb4 852 $courseid = $data->course; // Course Module ID
853 $reference = $data->reference; // Package path
854 $scormid = $data->instance; // scorm ID
855
856 $validation = new stdClass();
a679d64d 857
858 if (!empty($courseid) && !empty($reference)) {
859 $externalpackage = scorm_external_link($reference);
860
76ea4fb4 861 $validation->launch = 0;
76ea4fb4 862 $referencefield = $reference;
863 if (empty($reference)) {
a679d64d 864 $validation = null;
76ea4fb4 865 } else if ($reference[0] == '#') {
d9397d51 866 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
658e2574 867 $referencefield = $reference.'/imsmanifest.xml';
76ea4fb4 868 $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml';
869 } else {
a679d64d 870 $validation = null;
76ea4fb4 871 }
a679d64d 872 } else if (!$externalpackage) {
76ea4fb4 873 $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference;
874 }
875
876 if (!empty($scormid)) {
877 //
878 // SCORM Update
879 //
a679d64d 880 if ((!empty($validation)) && (is_file($reference) || $externalpackage)){
8aee93f1 881
a679d64d 882 if (!$externalpackage) {
883 $mdcheck = md5_file($reference);
884 } else if ($externalpackage){
885 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
886 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 887 copy ("$reference", $tempdir.'/'.basename($reference));
888 $mdcheck = md5_file($tempdir.'/'.basename($reference));
a679d64d 889 scorm_delete_files($tempdir);
890 }
891 }
892 }
8aee93f1 893
bf347041 894 if ($scorm = $DB->get_record('scorm', array('id'=>$scormid))) {
76ea4fb4 895 if ($scorm->reference[0] == '#') {
d9397d51 896 if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) {
76ea4fb4 897 $oldreference = $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml';
898 } else {
899 $oldreference = $scorm->reference;
900 }
a679d64d 901 } else if (!scorm_external_link($scorm->reference)) {
76ea4fb4 902 $oldreference = $CFG->dataroot.'/'.$courseid.'/'.$scorm->reference;
a679d64d 903 } else {
904 $oldreference = $scorm->reference;
905 }
76ea4fb4 906 $validation->launch = $scorm->launch;
a679d64d 907 if ((($oldreference == $reference) && ($mdcheck != $scorm->md5hash)) || ($oldreference != $reference)) {
76ea4fb4 908 // This is a new or a modified package
909 $validation->launch = 0;
910 } else {
911 // Old package already validated
76ea4fb4 912 if (strpos($scorm->version,'AICC') !== false) {
913 $validation->pkgtype = 'AICC';
914 } else {
915 $validation->pkgtype = 'SCORM';
916 }
917 }
918 } else {
a679d64d 919 $validation = null;
76ea4fb4 920 }
921 } else {
a679d64d 922 $validation = null;
76ea4fb4 923 }
924 }
925 //$validation->launch = 0;
a679d64d 926 if (($validation != null) && ($validation->launch == 0)) {
76ea4fb4 927 //
928 // Package must be validated
929 //
930 $ext = strtolower(substr(basename($reference),strrpos(basename($reference),'.')));
a679d64d 931 $tempdir = '';
76ea4fb4 932 switch ($ext) {
933 case '.pif':
934 case '.zip':
935 // Create a temporary directory to unzip package and validate package
76ea4fb4 936 $scormdir = '';
937 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
a679d64d 938 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 939 copy ("$reference", $tempdir.'/'.basename($reference));
940 unzip_file($tempdir.'/'.basename($reference), $tempdir, false);
82e34576 941 if (!$externalpackage) {
8949f8df 942 unlink ($tempdir.'/'.basename($reference));
82e34576 943 }
76ea4fb4 944 if (is_file($tempdir.'/imsmanifest.xml')) {
945 $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml');
946 $validation->pkgtype = 'SCORM';
947 } else {
948 $validation = scorm_validate_aicc($tempdir);
949 $validation->pkgtype = 'AICC';
950 }
951 } else {
a679d64d 952 $validation = null;
76ea4fb4 953 }
954 } else {
a679d64d 955 $validation = null;
76ea4fb4 956 }
957 break;
958 case '.xml':
959 if (basename($reference) == 'imsmanifest.xml') {
a679d64d 960 if ($externalpackage) {
961 if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) {
962 if ($tempdir = scorm_tempdir($scormdir)) {
8949f8df 963 copy ("$reference", $tempdir.'/'.basename($reference));
964 if (is_file($tempdir.'/'.basename($reference))) {
ab3b00e1 965 $validation = scorm_validate_manifest($tempdir.'/'.basename($reference));
966 } else {
967 $validation = null;
968 }
a679d64d 969 }
970 }
971 } else {
45698041 972 $validation = scorm_validate_manifest($reference);
a679d64d 973 }
ab3b00e1 974 $validation->pkgtype = 'SCORM';
76ea4fb4 975 } else {
a679d64d 976 $validation = null;
76ea4fb4 977 }
978 break;
979 default:
a679d64d 980 $validation = null;
76ea4fb4 981 break;
982 }
a679d64d 983 if ($validation == null) {
76ea4fb4 984 if (is_dir($tempdir)) {
985 // Delete files and temporary directory
986 scorm_delete_files($tempdir);
987 }
76ea4fb4 988 } else {
ab3b00e1 989 if (($ext == '.xml') && (!$externalpackage)) {
76ea4fb4 990 $validation->datadir = dirname($referencefield);
991 } else {
992 $validation->datadir = substr($tempdir,strlen($scormdir));
993 }
994 $validation->launch = 0;
995 }
996 }
997 } else {
a679d64d 998 $validation = null;
76ea4fb4 999 }
1000 return $validation;
1001}
1002
d8c9d8a1 1003
1004function scorm_get_count_users($scormid, $groupingid=null) {
bf347041 1005 global $CFG, $DB;
d8c9d8a1 1006
1007 if (!empty($CFG->enablegroupings) && !empty($groupingid)) {
1008 $sql = "SELECT COUNT(DISTINCT st.userid)
bf347041 1009 FROM {scorm_scoes_track} st
1010 INNER JOIN {groups_members} gm ON st.userid = gm.userid
1011 INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid
1012 WHERE st.scormid = ? AND gg.groupingid = ?
d8c9d8a1 1013 ";
bf347041 1014 $params = array($scormid, $groupingid);
d8c9d8a1 1015 } else {
1016 $sql = "SELECT COUNT(DISTINCT st.userid)
bf347041 1017 FROM {scorm_scoes_track} st
1018 WHERE st.scormid = ?
d8c9d8a1 1019 ";
bf347041 1020 $params = array($scormid);
d8c9d8a1 1021 }
1022
bf347041 1023 return ($DB->count_records_sql($sql, $params));
d8c9d8a1 1024}
1025
b44c704f 1026?>