3 /// Local Library of functions and constants for module scorm
6 * Create a new temporary subdirectory with a random name in the given path
8 * @param string $strpath The scorm data directory
9 * @return string/boolean
11 function scorm_datadir($strPath)
15 if (is_dir($strPath)) {
17 // Create a random string of 8 chars
21 for ($i=0; $i<$len; $i++
) {
22 $char = chr(rand(48,122));
23 while (!ereg('[a-zA-Z0-9]', $char)){
24 if ($char == $lchar) continue;
25 $char = chr(rand(48,90));
30 $datadir='/'.$randstring;
31 } while (file_exists($strPath.$datadir));
32 mkdir($strPath.$datadir, $CFG->directorypermissions
);
33 @chmod
($strPath.$datadir, $CFG->directorypermissions
); // Just in case mkdir didn't do it
34 return $strPath.$datadir;
41 * Given a package directory, this function will check if the package is valid
43 * @param string $packagedir The package directory
46 function scorm_validate($packagedir) {
47 $validation = new stdClass();
48 if (is_file($packagedir.'/imsmanifest.xml')) {
49 $validation->result
= 'found';
50 $validation->pkgtype
= 'SCORM';
52 if ($handle = opendir($packagedir)) {
53 while (($file = readdir($handle)) !== false) {
54 $ext = substr($file,strrpos($file,'.'));
55 if (strtolower($ext) == '.cst') {
56 $validation->result
= 'found';
57 $validation->pkgtype
= 'AICC';
63 if (!isset($validation)) {
64 $validation->result
= 'nomanifest';
65 $validation->pkgtype
= 'SCORM';
71 function scorm_get_user_data($userid) {
72 /// Gets user info required to display the table of scorm results
75 return get_record('user','id',$userid,'','','','','firstname, lastname, picture');
78 function scorm_string_wrap($stringa, $len=15) {
79 // Crop the given string into max $len characters lines
80 if (strlen($stringa) > $len) {
81 $words = explode(' ', $stringa);
84 foreach ($words as $word) {
85 if ((strlen($substring)+
strlen($word)+
1) < $len) {
86 $substring .= ' '.$word;
88 $newstring .= ' '.$substring.'<br />';
92 if (!empty($substring)) {
93 $newstring .= ' '.$substring;
101 function scorm_eval_prerequisites($prerequisites,$usertracks) {
105 'passed' => 'passed',
106 'completed' => 'completed',
107 'failed' => 'failed',
108 'incomplete' => 'incomplete',
109 'browsed' => 'browsed',
110 'not attempted' => 'notattempted',
116 'n' => 'notattempted'
119 while ($i<strlen($prerequisites)) {
120 $symbol = $prerequisites[$i];
132 $element = trim($element);
134 if (!empty($element)) {
135 $element = trim($element);
136 if (isset($usertracks[$element])) {
137 $element = '((\''.$usertracks[$element]->status
.'\' == \'completed\') || '.
138 '(\''.$usertracks[$element]->status
.'\' == \'passed\'))';
139 } else if (($operator = strpos($element,'=')) !== false) {
140 $item = trim(substr($element,0,$operator));
141 if (!isset($usertracks[$item])) {
145 $value = trim(trim(substr($element,$operator+
1)),'"');
146 if (isset($statuses[$value])) {
147 $status = $statuses[$value];
152 $element = '(\''.$usertracks[$item]->status
.'\' == \''.$status.'\')';
153 } else if (($operator = strpos($element,'<>')) !== false) {
154 $item = trim(substr($element,0,$operator));
155 if (!isset($usertracks[$item])) {
159 $value = trim(trim(substr($element,$operator+
2)),'"');
160 if (isset($statuses[$value])) {
161 $status = $statuses[$value];
166 $element = '(\''.$usertracks[$item]->status
.'\' != \''.$status.'\')';
167 } else if (is_numeric($element)) {
168 if ($symbol == '*') {
170 $open = strpos($prerequisites,'{',$i);
173 for ($close=$open+
1; (($opened > $closed) && ($close<strlen($prerequisites))); $close++
) {
174 if ($prerequisites[$close] == '}') {
176 } else if ($prerequisites[$close] == '{') {
182 $setelements = explode(',', substr($prerequisites, $open+
1, $close-($open+
1)-1));
184 foreach ($setelements as $setelement) {
185 if (scorm_eval_prerequisites($setelement,$usertracks)) {
190 if ($settrue >= $element) {
200 array_push($stack,$element);
203 if ($symbol == '~') {
206 if (!empty($symbol)) {
207 array_push($stack,$symbol);
216 if (!empty($element)) {
217 $element = trim($element);
218 if (isset($usertracks[$element])) {
219 $element = '((\''.$usertracks[$element]->status
.'\' == \'completed\') || '.
220 '(\''.$usertracks[$element]->status
.'\' == \'passed\'))';
221 } else if (($operator = strpos($element,'=')) !== false) {
222 $item = trim(substr($element,0,$operator));
223 if (!isset($usertracks[$item])) {
227 $value = trim(trim(substr($element,$operator+
1)),'"');
228 if (isset($statuses[$value])) {
229 $status = $statuses[$value];
234 $element = '(\''.$usertracks[$item]->status
.'\' == \''.$status.'\')';
235 } else if (($operator = strpos($element,'<>')) !== false) {
236 $item = trim(substr($element,0,$operator));
237 if (!isset($usertracks[$item])) {
241 $value = trim(trim(substr($element,$operator+
1)),'"');
242 if (isset($statuses[$value])) {
243 $status = $statuses[$value];
248 $element = '(\''.$usertracks[$item]->status
.'\' != \''.trim($status).'\')';
253 array_push($stack,$element);
255 return eval('return '.implode($stack).';');
259 function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) {
261 if ($track = get_record_select('scorm_scoes_track',"userid='$userid' AND scormid='$scormid' AND scoid='$scoid' AND attempt='$attempt' AND element='$element'")) {
262 $track->value
= $value;
263 $track->timemodified
= time();
264 $id = update_record('scorm_scoes_track',$track);
266 $track->userid
= $userid;
267 $track->scormid
= $scormid;
268 $track->scoid
= $scoid;
269 $track->attempt
= $attempt;
270 $track->element
= $element;
271 $track->value
= addslashes($value);
272 $track->timemodified
= time();
273 $id = insert_record('scorm_scoes_track',$track);
279 function scorm_add_time($a, $b) {
280 $aes = explode(':',$a);
281 $bes = explode(':',$b);
282 $aseconds = explode('.',$aes[2]);
283 $bseconds = explode('.',$bes[2]);
287 if (count($aseconds) > 1) {
288 $acents = $aseconds[1];
291 if (count($bseconds) > 1) {
292 $bcents = $bseconds[1];
294 $cents = $acents +
$bcents;
295 $change = floor($cents / 100);
296 $cents = $cents - ($change * 100);
297 if (floor($cents) < 10) {
298 $cents = '0'. $cents;
301 $secs = $aseconds[0] +
$bseconds[0] +
$change; //Seconds
302 $change = floor($secs / 60);
303 $secs = $secs - ($change * 60);
304 if (floor($secs) < 10) {
308 $mins = $aes[1] +
$bes[1] +
$change; //Minutes
309 $change = floor($mins / 60);
310 $mins = $mins - ($change * 60);
315 $hours = $aes[0] +
$bes[0] +
$change; //Hours
317 $hours = '0' . $hours;
321 return $hours . ":" . $mins . ":" . $secs . '.' . $cents;
323 return $hours . ":" . $mins . ":" . $secs;
327 function scorm_external_link($link) {
328 // check if a link is external
330 $link = strtolower($link);
331 if (substr($link,0,7) == 'http://') {
333 } else if (substr($link,0,8) == 'https://') {
335 } else if (substr($link,0,4) == 'www.') {
341 function scorm_grade_user($scoes, $userid, $grademethod=VALUESCOES
) {
352 $current = current($scoes);
353 $attempt = scorm_get_last_attempt($current->scorm
, $userid);
354 foreach ($scoes as $sco) {
355 if ($userdata=scorm_get_tracks($sco->id
, $userid,$attempt)) {
356 if (($userdata->status
== 'completed') ||
($userdata->status
== 'passed')) {
359 if (!empty($userdata->score_raw
)) {
361 $scores->sum +
= $userdata->score_raw
;
362 $scores->max
= ($userdata->score_raw
> $scores->max
)?
$userdata->score_raw
:$scores->max
;
366 switch ($grademethod) {
371 if ($scores->values
> 0) {
372 return $scores->sum
/$scores->values
;
381 return $scores->scoes
;
386 function scorm_count_launchable($scormid,$organization) {
387 return count_records_select('scorm_scoes',"scorm=$scormid AND organization='$organization' AND launch<>''");
390 function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) {
393 $strexpand = get_string('expcoll','scorm');
395 if ($mode == 'browse') {
396 $modestr = '&mode='.$mode;
398 $scormpixdir = $CFG->modpixpath
.'/scorm/pix';
400 $result = new stdClass();
401 $result->toc
= "<ul id='0' class='$liststyle'>\n";
403 $result->prerequisites
= true;
407 // Get the current organization infos
409 $organizationsql = '';
410 if (!empty($currentorg)) {
411 if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id
,'identifier',$currentorg)) != '') {
412 $result->toc
.= "\t<li>$organizationtitle</li>\n";
413 $tocmenus[] = $organizationtitle;
415 $organizationsql = "AND organization='$currentorg'";
418 // If not specified retrieve the last attempt number
420 if (empty($attempt)) {
421 $attempt = scorm_get_last_attempt($scorm->id
, $user->id
);
423 $result->attemptleft
= $scorm->maxattempt
- $attempt;
424 if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){
426 // Retrieve user tracking data for each learning object
428 $usertracks = array();
429 foreach ($scoes as $sco) {
430 if (!empty($sco->launch
)) {
431 if ($usertrack=scorm_get_tracks($sco->id
,$user->id
,$attempt)) {
432 if ($usertrack->status
== '') {
433 $usertrack->status
= 'notattempted';
435 $usertracks[$sco->identifier
] = $usertrack;
445 $parents[$level]='/';
447 foreach ($scoes as $sco) {
448 if ($parents[$level]!=$sco->parent
) {
449 if ($newlevel = array_search($sco->parent
,$parents)) {
450 for ($i=0; $i<($level-$newlevel); $i++
) {
451 $result->toc
.= "\t\t</ul></li>\n";
457 while (($i > 0) && ($parents[$level] != $sco->parent
)) {
458 $closelist .= "\t\t</ul></li>\n";
461 if (($i == 0) && ($sco->parent
!= $currentorg)) {
462 $result->toc
.= "\t\t<li><ul id='$sublist' class='$liststyle'>\n";
465 $result->toc
.= $closelist;
468 $parents[$level]=$sco->parent
;
471 $result->toc
.= "\t\t<li>";
472 $nextsco = next($scoes);
473 if (($nextsco !== false) && ($sco->parent
!= $nextsco->parent
) && (($level==0) ||
(($level>0) && ($nextsco->parent
== $sco->identifier
)))) {
475 $result->toc
.= '<a href="javascript:expandCollide(img'.$sublist.','.$sublist.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/minus.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>';
477 $result->toc
.= '<img src="'.$scormpixdir.'/spacer.gif" />';
479 if (empty($sco->title
)) {
480 $sco->title
= $sco->identifier
;
482 if (!empty($sco->launch
)) {
486 if (empty($scoid) && ($mode != 'normal')) {
489 if (isset($usertracks[$sco->identifier
])) {
490 $usertrack = $usertracks[$sco->identifier
];
491 $strstatus = get_string($usertrack->status
,'scorm');
492 $result->toc
.= '<img src="'.$scormpixdir.'/'.$usertrack->status
.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />';
493 if (($usertrack->status
== 'notattempted') ||
($usertrack->status
== 'incomplete') ||
($usertrack->status
== 'browsed')) {
495 if ($play && empty($scoid)) {
499 if ($usertrack->score_raw
!= '') {
500 $score = '('.get_string('score','scorm').': '.$usertrack->score_raw
.')';
503 if ($play && empty($scoid)) {
506 if ($sco->scormtype
== 'sco') {
507 $result->toc
.= '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />';
510 $result->toc
.= '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />';
513 if ($sco->id
== $scoid) {
517 $shownext = $sco->next
;
518 $showprev = $sco->previous
;
521 if (($nextid == 0) && (scorm_count_launchable($scorm->id
,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) {
522 if (!empty($sco->launch
)) {
526 if (empty($sco->prerequisites
) ||
scorm_eval_prerequisites($sco->prerequisites
,$usertracks)) {
527 if ($sco->id
== $scoid) {
528 $result->prerequisites
= true;
530 $url = $CFG->wwwroot
.'/mod/scorm/player.php?a='.$scorm->id
.'&currentorg='.$currentorg.$modestr.'&scoid='.$sco->id
;
531 $result->toc
.= ' '.$startbold.'<a href="'.$url.'">'.format_string($sco->title
).'</a>'.$score.$endbold."</li>\n";
532 $tocmenus[$sco->id
] = scorm_repeater('−',$level) . '>' . format_string($sco->title
);
534 if ($sco->id
== $scoid) {
535 $result->prerequisites
= false;
537 $result->toc
.= ' '.$sco->title
."</li>\n";
540 $result->toc
.= ' '.$sco->title
."</li>\n";
542 if (($nextsco !== false) && ($nextid == 0) && ($findnext)) {
543 if (!empty($nextsco->launch
)) {
544 $nextid = $nextsco->id
;
548 for ($i=0;$i<$level;$i++
) {
549 $result->toc
.= "\t\t</ul></li>\n";
553 $sco = get_record('scorm_scoes','id',$scoid);
554 $sco->previd
= $previd;
555 $sco->nextid
= $nextid;
557 $result->incomplete
= $incomplete;
559 $result->incomplete
= $incomplete;
562 $result->toc
.= "\t</ul>\n";
563 if ($scorm->hidetoc
== 0) {
565 <script language="javascript" type="text/javascript">
567 function expandCollide(which,list) {
568 var nn=document.ids?true:false
569 var w3c=document.getElementById?true:false
570 var beg=nn?"document.ids.":w3c?"document.getElementById(":"document.all.";
571 var mid=w3c?").style":".style";
573 if (eval(beg+list+mid+".display") != "none") {
574 which.src = "'.$scormpixdir.'/plus.gif";
575 eval(beg+list+mid+".display=\'none\';");
577 which.src = "'.$scormpixdir.'/minus.gif";
578 eval(beg+list+mid+".display=\'block\';");
585 $url = $CFG->wwwroot
.'/mod/scorm/player.php?a='.$scorm->id
.'&currentorg='.$currentorg.$modestr.'&scoid=';
586 $result->tocmenu
= popup_form($url,$tocmenus, "tocmenu", $sco->id
, '', '', '', true);
591 function scorm_get_last_attempt($scormid, $userid) {
592 /// Find the last attempt number for the given user id and scorm id
593 if ($lastattempt = get_record('scorm_scoes_track', 'userid', $userid, 'scormid', $scormid, '', '', 'max(attempt) as a')) {
594 if (empty($lastattempt->a
)) {
597 return $lastattempt->a
;
602 function scorm_get_tracks($scoid,$userid,$attempt='') {
603 /// Gets all tracks of specified sco and user
606 if (empty($attempt)) {
607 if ($scormid = get_field('scorm_scoes','scorm','id',$scoid)) {
608 $attempt = scorm_get_last_attempt($scormid,$userid);
613 $attemptsql = ' AND attempt=' . $attempt;
614 if ($tracks = get_records_select('scorm_scoes_track',"userid=$userid AND scoid=$scoid".$attemptsql,'element ASC')) {
615 $usertrack->userid
= $userid;
616 $usertrack->scoid
= $scoid;
617 $usertrack->score_raw
= '';
618 $usertrack->status
= '';
619 $usertrack->total_time
= '00:00:00';
620 $usertrack->session_time
= '00:00:00';
621 $usertrack->timemodified
= 0;
622 foreach ($tracks as $track) {
623 $element = $track->element
;
624 $usertrack->{$element} = $track->value
;
626 case 'cmi.core.lesson_status':
627 case 'cmi.completion_status':
628 if ($track->value
== 'not attempted') {
629 $track->value
= 'notattempted';
631 $usertrack->status
= $track->value
;
633 case 'cmi.core.score.raw':
634 case 'cmi.score.raw':
635 $usertrack->score_raw
= $track->value
;
637 case 'cmi.core.session_time':
638 case 'cmi.session_time':
639 $usertrack->session_time
= $track->value
;
641 case 'cmi.core.total_time':
642 case 'cmi.total_time':
643 $usertrack->total_time
= $track->value
;
646 if (isset($track->timemodified
) && ($track->timemodified
> $usertrack->timemodified
)) {
647 $usertrack->timemodified
= $track->timemodified
;
657 /// Library of functions and constants for parsing packages
659 function scorm_parse($scorm) {
662 // Parse scorm manifest
663 if ($scorm->pkgtype
== 'AICC') {
664 $scorm->launch
= scorm_parse_aicc($scorm->dir
.'/'.$scorm->id
,$scorm->id
);
666 if (basename($scorm->reference
) != 'imsmanifest.xml') {
667 $scorm->launch
= scorm_parse_scorm($scorm->dir
.'/'.$scorm->id
,$scorm->id
);
669 $scorm->launch
= scorm_parse_scorm($CFG->dataroot
.'/'.$scorm->course
.'/'.dirname($scorm->reference
),$scorm->id
);
673 return $scorm->launch
;
677 * Take the header row of an AICC definition file
678 * and returns sequence of columns and a pointer to
679 * the sco identifier column.
681 * @param string $row AICC header row
682 * @param string $mastername AICC sco identifier column
685 function scorm_get_aicc_columns($row,$mastername='system_id') {
686 $tok = strtok(strtolower($row),"\",\n\r");
687 $result->columns
= array();
691 $result->columns
[] = $tok;
692 if ($tok == $mastername) {
693 $result->mastercol
= $i;
697 $tok = strtok("\",\n\r");
703 * Given a colums array return a string containing the regular
704 * expression to match the columns in a text row.
706 * @param array $column The header columns
707 * @param string $remodule The regular expression module for a single column
710 function scorm_forge_cols_regexp($columns,$remodule='(".*")?,') {
712 foreach ($columns as $column) {
713 $regexp .= $remodule;
715 $regexp = substr($regexp,0,-1) . '/';
719 function scorm_parse_aicc($pkgdir,$scormid){
723 if ($handle = opendir($pkgdir)) {
724 while (($file = readdir($handle)) !== false) {
725 $ext = substr($file,strrpos($file,'.'));
726 $extension = strtolower(substr($ext,1));
727 $id = strtolower(basename($file,$ext));
728 $ids[$id]->$extension = $file;
732 foreach ($ids as $courseid => $id) {
733 if (isset($id->crs
)) {
734 if (is_file($pkgdir.'/'.$id->crs
)) {
735 $rows = file($pkgdir.'/'.$id->crs
);
736 foreach ($rows as $row) {
737 if (preg_match("/^(.+)=(.+)$/",$row,$matches)) {
738 switch (strtolower(trim($matches[1]))) {
740 $courses[$courseid]->id
= trim($matches[2]);
743 $courses[$courseid]->title
= trim($matches[2]);
746 $courses[$courseid]->version
= 'AICC_'.trim($matches[2]);
753 if (isset($id->des
)) {
754 $rows = file($pkgdir.'/'.$id->des
);
755 $columns = scorm_get_aicc_columns($rows[0]);
756 $regexp = scorm_forge_cols_regexp($columns->columns
);
757 for ($i=1;$i<count($rows);$i++
) {
758 if (preg_match($regexp,$rows[$i],$matches)) {
759 for ($j=0;$j<count($columns->columns
);$j++
) {
760 $column = $columns->columns
[$j];
761 $courses[$courseid]->elements
[substr(trim($matches[$columns->mastercol+
1]),1,-1)]->$column = substr(trim($matches[$j+
1]),1,-1);
766 if (isset($id->au
)) {
767 $rows = file($pkgdir.'/'.$id->au
);
768 $columns = scorm_get_aicc_columns($rows[0]);
769 $regexp = scorm_forge_cols_regexp($columns->columns
);
770 for ($i=1;$i<count($rows);$i++
) {
771 if (preg_match($regexp,$rows[$i],$matches)) {
772 for ($j=0;$j<count($columns->columns
);$j++
) {
773 $column = $columns->columns
[$j];
774 $courses[$courseid]->elements
[substr(trim($matches[$columns->mastercol+
1]),1,-1)]->$column = substr(trim($matches[$j+
1]),1,-1);
779 if (isset($id->cst
)) {
780 $rows = file($pkgdir.'/'.$id->cst
);
781 $columns = scorm_get_aicc_columns($rows[0],'block');
782 $regexp = scorm_forge_cols_regexp($columns->columns
,'(.+)?,');
783 for ($i=1;$i<count($rows);$i++
) {
784 if (preg_match($regexp,$rows[$i],$matches)) {
785 for ($j=0;$j<count($columns->columns
);$j++
) {
786 if ($j != $columns->mastercol
) {
787 $courses[$courseid]->elements
[substr(trim($matches[$j+
1]),1,-1)]->parent
= substr(trim($matches[$columns->mastercol+
1]),1,-1);
793 if (isset($id->ort
)) {
794 $rows = file($pkgdir.'/'.$id->ort
);
796 if (isset($id->pre
)) {
797 $rows = file($pkgdir.'/'.$id->pre
);
798 $columns = scorm_get_aicc_columns($rows[0],'structure_element');
799 $regexp = scorm_forge_cols_regexp($columns->columns
,'(.+),');
800 for ($i=1;$i<count($rows);$i++
) {
801 if (preg_match($regexp,$rows[$i],$matches)) {
802 $courses[$courseid]->elements
[$columns->mastercol+
1]->prerequisites
= substr(trim($matches[1-$columns->mastercol+
1]),1,-1);
806 if (isset($id->cmp
)) {
807 $rows = file($pkgdir.'/'.$id->cmp
);
812 if (isset($courses)) {
813 foreach ($courses as $course) {
815 $sco->identifier
= $course->id
;
816 $sco->scorm
= $scormid;
817 $sco->organization
= '';
818 $sco->title
= $course->title
;
821 $sco->scormtype
= '';
823 $id = insert_record('scorm_scoes',$sco);
827 if (isset($course->elements
)) {
828 foreach($course->elements
as $element) {
830 $sco->identifier
= $element->system_id
;
831 $sco->scorm
= $scormid;
832 $sco->organization
= $course->id
;
833 $sco->title
= $element->title
;
834 if (strtolower($element->parent
) == 'root') {
837 $sco->parent
= $element->parent
;
839 if (isset($element->file_name
)) {
840 $sco->launch
= $element->file_name
;
841 $sco->scormtype
= 'sco';
843 $element->file_name
= '';
844 $sco->scormtype
= '';
846 if (!isset($element->prerequisites
)) {
847 $element->prerequisites
= '';
849 $sco->prerequisites
= $element->prerequisites
;
850 if (!isset($element->max_time_allowed
)) {
851 $element->max_time_allowed
= '';
853 $sco->maxtimeallowed
= $element->max_time_allowed
;
854 if (!isset($element->time_limit_action
)) {
855 $element->time_limit_action
= '';
857 $sco->timelimitaction
= $element->time_limit_action
;
858 if (!isset($element->mastery_score
)) {
859 $element->mastery_score
= '';
861 $sco->masteryscore
= $element->mastery_score
;
864 $id = insert_record('scorm_scoes',$sco);
872 set_field('scorm','version','AICC','id',$scormid);
876 function scorm_get_resources($blocks) {
877 foreach ($blocks as $block) {
878 if ($block['name'] == 'RESOURCES') {
879 foreach ($block['children'] as $resource) {
880 if ($resource['name'] == 'RESOURCE') {
881 $resources[addslashes($resource['attrs']['IDENTIFIER'])] = $resource['attrs'];
889 function scorm_get_manifest($blocks,$scoes) {
890 static $parents = array();
894 static $organization;
896 if (count($blocks) > 0) {
897 foreach ($blocks as $block) {
898 switch ($block['name']) {
900 if (isset($block['children'])) {
901 foreach ($block['children'] as $metadata) {
902 if ($metadata['name'] == 'SCHEMAVERSION') {
903 if (empty($scoes->version
)) {
904 if (preg_match("/^(1\.2)$|^(CAM )?(1\.3)$/",$metadata['tagData'],$matches)) {
905 $scoes->version
= 'SCORM_'.$matches[count($matches)-1];
907 $scoes->version
= 'SCORM_1.2';
915 $manifest = addslashes($block['attrs']['IDENTIFIER']);
917 $resources = array();
918 $resources = scorm_get_resources($block['children']);
919 $scoes = scorm_get_manifest($block['children'],$scoes);
920 if (count($scoes->elements
) <= 0) {
921 foreach ($resources as $item => $resource) {
922 if (!empty($resource['HREF'])) {
923 $sco = new stdClass();
924 $sco->identifier
= $item;
927 $sco->launch
= addslashes($resource['HREF']);
928 $sco->scormtype
= addslashes($resource['ADLCP:SCORMTYPE']);
929 $scoes->elements
[$manifest][$organization][$item] = $sco;
934 case 'ORGANIZATIONS':
935 if (!isset($scoes->defaultorg
)) {
936 $scoes->defaultorg
= addslashes($block['attrs']['DEFAULT']);
938 $scoes = scorm_get_manifest($block['children'],$scoes);
941 $identifier = addslashes($block['attrs']['IDENTIFIER']);
943 $scoes->elements
[$manifest][$organization][$identifier]->identifier
= $identifier;
944 $scoes->elements
[$manifest][$organization][$identifier]->parent
= '/';
945 $scoes->elements
[$manifest][$organization][$identifier]->launch
= '';
946 $scoes->elements
[$manifest][$organization][$identifier]->scormtype
= '';
949 $parent = new stdClass();
950 $parent->identifier
= $identifier;
951 $parent->organization
= $organization;
952 array_push($parents, $parent);
953 $organization = $identifier;
955 $scoes = scorm_get_manifest($block['children'],$scoes);
960 $parent = array_pop($parents);
961 array_push($parents, $parent);
963 $identifier = addslashes($block['attrs']['IDENTIFIER']);
964 $scoes->elements
[$manifest][$organization][$identifier]->identifier
= $identifier;
965 $scoes->elements
[$manifest][$organization][$identifier]->parent
= $parent->identifier
;
966 if (!isset($block['attrs']['ISVISIBLE'])) {
967 $block['attrs']['ISVISIBLE'] = 'true';
969 $scoes->elements
[$manifest][$organization][$identifier]->isvisible
= addslashes($block['attrs']['ISVISIBLE']);
970 if (!isset($block['attrs']['PARAMETERS'])) {
971 $block['attrs']['PARAMETERS'] = '';
973 $scoes->elements
[$manifest][$organization][$identifier]->parameters
= addslashes($block['attrs']['PARAMETERS']);
974 if (!isset($block['attrs']['IDENTIFIERREF'])) {
975 $scoes->elements
[$manifest][$organization][$identifier]->launch
= '';
976 $scoes->elements
[$manifest][$organization][$identifier]->scormtype
= 'asset';
978 $idref = addslashes($block['attrs']['IDENTIFIERREF']);
980 if (isset($resources[$idref]['XML:BASE'])) {
981 $base = $resources[$idref]['XML:BASE'];
983 $scoes->elements
[$manifest][$organization][$identifier]->launch
= addslashes($base.$resources[$idref]['HREF']);
984 if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) {
985 $resources[$idref]['ADLCP:SCORMTYPE'] = 'asset';
987 $scoes->elements
[$manifest][$organization][$identifier]->scormtype
= addslashes($resources[$idref]['ADLCP:SCORMTYPE']);
990 $parent = new stdClass();
991 $parent->identifier
= $identifier;
992 $parent->organization
= $organization;
993 array_push($parents, $parent);
995 $scoes = scorm_get_manifest($block['children'],$scoes);
1000 $parent = array_pop($parents);
1001 array_push($parents, $parent);
1002 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->title
= addslashes($block['tagData']);
1004 case 'ADLCP:PREREQUISITES':
1005 if ($block['attrs']['TYPE'] == 'aicc_script') {
1006 $parent = array_pop($parents);
1007 array_push($parents, $parent);
1008 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->prerequisites
= addslashes($block['tagData']);
1011 case 'ADLCP:MAXTIMEALLOWED':
1012 $parent = array_pop($parents);
1013 array_push($parents, $parent);
1014 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->maxtimeallowed
= addslashes($block['tagData']);
1016 case 'ADLCP:TIMELIMITACTION':
1017 $parent = array_pop($parents);
1018 array_push($parents, $parent);
1019 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->timelimitaction
= addslashes($block['tagData']);
1021 case 'ADLCP:DATAFROMLMS':
1022 $parent = array_pop($parents);
1023 array_push($parents, $parent);
1024 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->datafromlms
= addslashes($block['tagData']);
1026 case 'ADLCP:MASTERYSCORE':
1027 $parent = array_pop($parents);
1028 array_push($parents, $parent);
1029 $scoes->elements
[$manifest][$parent->organization
][$parent->identifier
]->masteryscore
= addslashes($block['tagData']);
1038 function scorm_parse_scorm($pkgdir,$scormid) {
1042 $manifestfile = $pkgdir.'/imsmanifest.xml';
1044 if (is_file($manifestfile)) {
1046 $xmlstring = file_get_contents($manifestfile);
1047 $objXML = new xml2Array();
1048 $manifests = $objXML->parse($xmlstring);
1050 $scoes = new stdClass();
1051 $scoes->version
= '';
1052 $scoes = scorm_get_manifest($manifests,$scoes);
1054 if (count($scoes->elements
) > 0) {
1055 foreach ($scoes->elements
as $manifest => $organizations) {
1056 foreach ($organizations as $organization => $items) {
1057 foreach ($items as $identifier => $item) {
1058 $item->scorm
= $scormid;
1059 $item->manifest
= $manifest;
1060 $item->organization
= $organization;
1061 $id = insert_record('scorm_scoes',$item);
1063 if (($launch == 0) && ((empty($scoes->defaultorg
)) ||
($scoes->defaultorg
== $identifier))) {
1069 set_field('scorm','version',$scoes->version
,'id',$scormid);
1076 function scorm_course_format_display($user,$course) {
1079 $strupdate = get_string('update');
1080 $strmodule = get_string('modulename','scorm');
1082 echo '<div class="mod-scorm">';
1083 if ($scorms = get_all_instances_in_course('scorm', $course)) {
1084 // The module SCORM activity with the least id is the course
1085 $scorm = current($scorms);
1086 if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id
, $course->id
)) {
1087 error("Course Module ID was incorrect");
1090 $headertext = '<table width="100%"><tr><td class="title">'.get_string('name').': <b>'.format_string($scorm->name
).'</b>';
1091 if (isteacher($course->id
, $user->id
, true)) {
1092 if (isediting($course->id
)) {
1093 // Display update icon
1094 $path = $CFG->wwwroot
.'/course';
1095 $headertext .= '<span class="commands">'.
1096 '<a title="'.$strupdate.'" href="'.$path.'/mod.php?update='.$cm->id
.'&sesskey='.sesskey().'">'.
1097 '<img src="'.$CFG->pixpath
.'/t/edit.gif" hspace="2" height="11" width="11" border="0" alt="'.$strupdate.'" /></a></span>';
1099 $headertext .= '</td>';
1100 // Display report link
1101 $trackedusers = get_record('scorm_scoes_track', 'scormid', $scorm->id
, '', '', '', '', 'count(distinct(userid)) as c');
1102 if ($trackedusers->c
> 0) {
1103 $headertext .= '<td class="reportlink">'.
1104 '<a target="'.$CFG->framename
.'" href="'.$CFG->wwwroot
.'/mod/scorm/report.php?id='.$cm->id
.'">'.
1105 get_string('viewallreports','scorm',$trackedusers->c
).'</a>';
1107 $headertext .= '<td class="reportlink">'.get_string('noreports','scorm');
1109 $colspan = ' colspan="2"';
1111 $headertext .= '</td></tr><tr><td'.$colspan.'>'.format_text(get_string('summary').':<br />'.$scorm->summary
).'</td></tr></table>';
1112 print_simple_box($headertext,'','100%');
1113 scorm_view_display($user, $scorm, 'view.php?id='.$course->id
, $cm, '100%');
1115 if (isteacheredit($course->id
, $user->id
)) {
1116 // Create a new activity
1117 redirect('mod.php?id='.$course->id
.'&section=0&sesskey='.sesskey().'&add=scorm');
1119 notify('Could not find a scorm course here');
1125 function scorm_view_display ($user, $scorm, $action, $cm, $blockwidth='') {
1128 $organization = optional_param('organization', '', PARAM_INT
);
1130 print_simple_box_start('center',$blockwidth);
1132 <div
class="structurehead"><?php
print_string('coursestruct','scorm') ?
></div
>
1134 if (empty($organization)) {
1135 $organization = $scorm->launch
;
1137 if ($orgs = get_records_select_menu('scorm_scoes',"scorm='$scorm->id' AND organization='' AND launch=''",'id','id,title')) {
1138 if (count($orgs) > 1) {
1140 <div
class='center'>
1141 <?php
print_string('organizations','scorm') ?
>
1142 <form name
='changeorg' method
='post' action
='<?php echo $action ?>'>
1143 <?php
choose_from_menu($orgs, 'organization', "$organization", '','submit()') ?
>
1149 $orgidentifier = '';
1150 if ($org = get_record('scorm_scoes','id',$organization)) {
1151 if (($org->organization
== '') && ($org->launch
== '')) {
1152 $orgidentifier = $org->identifier
;
1154 $orgidentifier = $org->organization
;
1157 $result = scorm_get_toc($user,$scorm,'structlist',$orgidentifier);
1158 $incomplete = $result->incomplete
;
1160 print_simple_box_end();
1162 <div
class="center">
1163 <form name
="theform" method
="post" action
="<?php echo $CFG->wwwroot ?>/mod/scorm/player.php?id=<?php echo $cm->id ?>"<?php
echo $scorm->popup
== 1?
' target="newwin"':'' ?
>>
1165 if ($scorm->hidebrowse
== 0) {
1166 print_string("mode","scorm");
1167 echo ': <input type="radio" id="b" name="mode" value="browse" /><label for="b">'.get_string('browse','scorm').'</label>'."\n";
1168 if ($incomplete === true) {
1169 echo '<input type="radio" id="n" name="mode" value="normal" checked="checked" /><label for="n">'.get_string('normal','scorm')."</label>\n";
1171 echo '<input type="radio" id="r" name="mode" value="review" checked="checked" /><label for="r">'.get_string('review','scorm')."</label>\n";
1174 if ($incomplete === true) {
1175 echo '<input type="hidden" name="mode" value="normal" />'."\n";
1177 echo '<input type="hidden" name="mode" value="review" />'."\n";
1180 if (($incomplete === false) && (($result->attemptleft
> 0)||
($scorm->maxattempt
== 0))) {
1183 <input type
="checkbox" id
="a" name
="newattempt" />
1184 <label
for="a"><?php
print_string('newattempt','scorm') ?
></label
>
1189 <input type
="hidden" name
="scoid" />
1190 <input type
="hidden" name
="currentorg" value
="<?php echo $orgidentifier ?>" />
1191 <input type
="submit" value
="<? print_string('entercourse','scorm') ?>" />
1198 function scorm_repeater($what, $times) {
1203 for ($i=0; $i<$times;$i++
) {
1210 Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML;
1212 $objXML = new xml2Array();
1213 $arrOutput = $objXML->parse($strYourXML);
1214 print_r($arrOutput); //print it out, or do whatever!
1219 var $arrOutput = array();
1224 * Convert a utf-8 string to html entities
1226 * @param string $str The UTF-8 string
1229 function utf8_to_entities($str) {
1234 for ($i = 0; $i < strlen($str); $i++
) {
1235 $thisvalue = ord($str[$i]);
1236 if ($thisvalue < 128) {
1237 $entities .= $str[$i]; // Leave ASCII chars unchanged
1239 if (count($values) == 0) {
1240 $lookingfor = ($thisvalue < 224) ?
2 : 3;
1242 $values[] = $thisvalue;
1243 if (count($values) == $lookingfor) {
1244 $number = ($lookingfor == 3) ?
1245 (($values[0] %
16) * 4096) +
(($values[1] %
64) * 64) +
($values[2] %
64):
1246 (($values[0] %
32) * 64) +
($values[1] %
64);
1247 $entities .= '&#' . $number . ';';
1257 * Parse an XML text string and create an array tree that rapresent the XML structure
1259 * @param string $strInputXML The XML string
1262 function parse($strInputXML) {
1263 $this->resParser
= xml_parser_create ('UTF-8');
1264 xml_set_object($this->resParser
,$this);
1265 xml_set_element_handler($this->resParser
, "tagOpen", "tagClosed");
1267 xml_set_character_data_handler($this->resParser
, "tagData");
1269 $this->strXmlData
= xml_parse($this->resParser
,$strInputXML );
1270 if(!$this->strXmlData
) {
1271 die(sprintf("XML error: %s at line %d",
1272 xml_error_string(xml_get_error_code($this->resParser
)),
1273 xml_get_current_line_number($this->resParser
)));
1276 xml_parser_free($this->resParser
);
1278 return $this->arrOutput
;
1281 function tagOpen($parser, $name, $attrs) {
1282 $tag=array("name"=>$name,"attrs"=>$attrs);
1283 array_push($this->arrOutput
,$tag);
1286 function tagData($parser, $tagData) {
1287 if(trim($tagData)) {
1288 if(isset($this->arrOutput
[count($this->arrOutput
)-1]['tagData'])) {
1289 $this->arrOutput
[count($this->arrOutput
)-1]['tagData'] .= $this->utf8_to_entities($tagData);
1291 $this->arrOutput
[count($this->arrOutput
)-1]['tagData'] = $this->utf8_to_entities($tagData);
1296 function tagClosed($parser, $name) {
1297 $this->arrOutput
[count($this->arrOutput
)-2]['children'][] = $this->arrOutput
[count($this->arrOutput
)-1];
1298 array_pop($this->arrOutput
);