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