642042a045dfecf336276645d3b9d134da0b6450
[moodle.git] / mod / scorm / lib.php
1 <?php  // $Id$
3 /// Library of functions and constants for module scorm
4 /// (replace scorm with the name of your module and delete this line)
6 define('VALUESCOES', '0');
7 define('VALUEHIGHEST', '1');
8 define('VALUEAVERAGE', '2');
9 define('VALUESUM', '3');
10 $SCORM_GRADE_METHOD = array (VALUESCOES => get_string('gradescoes', 'scorm'), 
11                              VALUEHIGHEST => get_string('gradehighest', 'scorm'),
12                              VALUEAVERAGE => get_string('gradeaverage', 'scorm'),
13                              VALUESUM => get_string('gradesum', 'scorm'));
15 if (!isset($CFG->scorm_validate)) {
16     $scormvalidate = 'none';
17     if (extension_loaded('domxml') && version_compare(phpversion(),'5.0.0','<')) {
18         $scormvalidate = 'domxml';
19     }
20     if (version_compare(phpversion(),'5.0.0','>=')) {
21         $scormvalidate = 'php5';
22     }
23     set_config('scorm_validate', $scormvalidate);
24 }
26 if (!isset($CFG->scorm_frameheight)) {
27     set_config('scorm_frameheight','600');
28 }
30 if (!isset($CFG->scorm_framewidth)) {
31     set_config('scorm_framewidth','800');
32 }
34 function scorm_add_instance($scorm) {
35 /// Given an object containing all the necessary data, 
36 /// (defined by the form in mod.html) this function 
37 /// will create a new instance and return the id number 
38 /// of the new instance.
40     $scorm->timemodified = time();
42     # May have to add extra stuff in here #
43     global $CFG;
44     
45     $id = insert_record('scorm', $scorm);
46     
47     //
48     // Rename temp scorm dir to scorm id
49     //
50     
51     $scormdir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm';
52     rename($scormdir.$scorm->datadir,$scormdir.'/'.$id);
53     //
54     // Parse scorm manifest
55     //
56     if ($scorm->launch == 0) {
57     $scorm->launch = scorm_parse($scormdir.'/'.$id,$scorm->pkgtype,$id);
58         set_field('scorm','launch',$scorm->launch,'id',$id);
59     }
60     
61     return $id;
62 }
65 function scorm_update_instance($scorm) {
66 /// Given an object containing all the necessary data, 
67 /// (defined by the form in mod.html) this function 
68 /// will update an existing instance with new data.
69     
70     $scorm->timemodified = time();
71     $scorm->id = $scorm->instance;
73     # May have to add extra stuff in here #
74     global $CFG;
75     
76     
77     //
78     // Check if scorm manifest needs to be reparsed
79     //
80     if ($scorm->launch == 0) {
81         $scormdir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm';
82         if (isset($scorm->datadir) && ($scorm->datadir != $scorm->id)) {
83             scorm_delete_files($scormdir.'/'.$scorm->id);
84             rename($scormdir.$scorm->datadir,$scormdir.'/'.$scorm->id);
85         }
86         $scorm->launch = scorm_parse($scormdir.'/'.$scorm->id,$scorm->pkgtype,$scorm->id);
87     }
88     
89     return update_record('scorm', $scorm);
90 }
93 function scorm_delete_instance($id) {
94 /// Given an ID of an instance of this module, 
95 /// this function will permanently delete the instance 
96 /// and any data that depends on it.  
97     
98     require('../config.php');
100     if (! $scorm = get_record('scorm', 'id', $id)) {
101         return false;
102     }
104     $result = true;
106     # Delete any dependent files #
107     scorm_delete_files($CFG->dataroot.'/'.$scorm->course.'/moddata/scorm/'.$scorm->id);
109     # Delete any dependent records here #
110     if (! delete_records('scorm_scoes_track', 'scormid', $scorm->id)) {
111         $result = false;
112     }
113     if (! delete_records('scorm_scoes', 'scorm', $scorm->id)) {
114         $result = false;
115     }
116     if (! delete_records('scorm', 'id', $scorm->id)) {
117         $result = false;
118     }
119     
121     return $result;
124 function scorm_user_outline($course, $user, $mod, $scorm) {
125 /// Return a small object with summary information about what a 
126 /// user has done with a given particular instance of this module
127 /// Used for user activity reports.
128 /// $return->time = the time they did it
129 /// $return->info = a short text description
131     return NULL;   // TO FIX
134 function scorm_user_complete($course, $user, $mod, $scorm) {
135 /// Print a detailed representation of what a  user has done with 
136 /// a given particular instance of this module, for user activity reports.
138     return true;   // TO FIX
141 function scorm_print_recent_activity(&$logs, $isteacher=false) {
142 /// Given a list of logs, assumed to be those since the last login 
143 /// this function prints a short list of changes related to this module
144 /// If isteacher is true then perhaps additional information is printed.
145 /// This function is called from course/lib.php: print_recent_activity()
147     return false;  // True if anything was printed, otherwise false
150 function scorm_cron () {
151 /// Function to be run periodically according to the moodle cron
152 /// This function searches for things that need to be done, such 
153 /// as sending out mail, toggling flags etc ... 
155     global $CFG;
157     return true;
160 function scorm_grades($scormid) {
161 /// Must return an array of grades for a given instance of this module, 
162 /// indexed by user.  It also returns a maximum allowed grade.
164     global $CFG;
165     
166     if (!$scorm = get_record('scorm', 'id', $scormid)) {
167         return NULL;
168     }
169     if (!$scoes = get_records('scorm_scoes','scorm',$scormid)) {
170     return NULL;
171     }
173     if ($scorm->grademethod == VALUESCOES) {
174         if (!$return->maxgrade = count_records_select('scorm_scoes',"scorm='$scormid' AND launch<>''")) {
175             return NULL;
176         }
177     } else {
178         $return->maxgrade = $scorm->maxgrade;
179     }
180     
181     $return->grades = NULL;
182     if ($scousers=get_records_select('scorm_scoes_track', "scormid='$scormid' GROUP BY userid", "", "userid,null")) {
183         foreach ($scousers as $scouser) {
184             $scores = NULL;
185             $scores->scoes = 0;
186             $scores->values = 0;
187         $scores->max = 0;
188             $scores->sum = 0;
189    
190             foreach ($scoes as $sco) {
191                 $userdata=scorm_get_tracks($sco->id, $scouser->userid);
192                 if (($userdata->status == 'completed') || ($userdata->status == 'passed')) { 
193                     $scores->scoes++;
194                 }
195                 if (!empty($userdata->score_raw)) {
196                     $scores->values++;
197                     $scores->sum += $userdata->score_raw;
198                     $scores->max = ($userdata->score_raw > $scores->max)?$userdata->score_raw:$scores->max;
199                 }
200             }
201             switch ($scorm->grademethod) {
202                 case VALUEHIGHEST:
203                     $return->grades[$scouser->userid] = $scores->max;
204                     break;
205                 case VALUEAVERAGE:
206                     if ($score->values > 0) {
207                         $return->grades[$scouser->userid] = $scores->sum/$scores->values;
208                     } else {
209                         $return->grades[$scouser->userid] = 0;
210                     }
211                     break;
212                 case VALUESUM:
213                     $return->grades[$scouser->userid] = $scores->sum;
214                     break;
215                 case VALUESCOES:
216                     $return->grades[$scouser->userid] = $scores->scoes;
217                     break;
218             }
219        }
220     }
221     return $return;
225 //////////////////////////////////////////////////////////////////////////////////////
226 /// Any other scorm functions go here.  Each of them must have a name that 
227 /// starts with scorm_
230 function scorm_randstring($len = '8')
232         $rstring = NULL;
233         $lchar = '';
234         for($i=0; $i<$len; $i++) {
235                 $char = chr(rand(48,122));
236                 while (!ereg('[a-zA-Z0-9]', $char)){
237                         if($char == $lchar) continue;
238                         $char = chr(rand(48,90));
239                 }
240                 $rstring .= $char;
241                 $lchar = $char;
242         }
243         return $rstring;
246  
247 function scorm_datadir($strPath, $existingdir='')
249     global $CFG;
251     if (($existingdir!='') && (is_dir($strPath.'/'.$existingdir)))
252         return $strPath.'/'.$existingdir;
253         
254     if (is_dir($strPath)) {
255         do {
256             $datadir='/'.scorm_randstring();
257         } while (file_exists($strPath.$datadir));
258         mkdir($strPath.$datadir, $CFG->directorypermissions);
259         @chmod($strPath.$datadir, $CFG->directorypermissions);  // Just in case mkdir didn't do it
260         return $strPath.$datadir;
261     } else {
262         return false;
263     }
264
266 function scorm_validate($packagedir)
268     if (is_file($packagedir.'/imsmanifest.xml')) {
269         $validation->result = 'found';
270         $validation->pkgtype = 'SCORM';
271     } else {
272         if ($handle = opendir($packagedir)) {
273             while (($file = readdir($handle)) !== false) {
274                 $ext = substr($file,strrpos($file,'.'));
275                 if (strtolower($ext) == '.cst') {
276                     $validation->result = 'found';
277                     $validation->pkgtype = 'AICC';
278                     break;
279                 }
280             }
281             closedir($handle);
282         }
283         if (!isset($validation)) {
284             $validation->result = 'nomanifest';
285             $validation->pkgtype = 'SCORM';
286         }
287     }
288     return $validation;
291 function scorm_delete_files($directory) {
292     if (is_dir($directory)) {
293         $files=scorm_scandir($directory);
294         //print_r($files);
295         foreach($files as $file) {
296             if (($file != '.') && ($file != '..')) {
297                 if (!is_dir($directory.'/'.$file)) {
298                     //chmod($directory.'/'.$file,0777);
299                     unlink($directory.'/'.$file);
300                 } else {
301                     scorm_delete_files($directory.'/'.$file);
302                 }
303             }
304         }
305         rmdir($directory);
306     }
309 function scorm_scandir($directory) {
310     if (version_compare(phpversion(),'5.0.0','>=')) {
311         return scandir($directory);
312     } else {
313         $files = null;
314         if ($dh = opendir($directory)) {
315             while (($file = readdir($dh)) !== false) {
316                $files[] = $file;
317             }
318             closedir($dh);
319         }
320         return $files;
321     }
324 function scorm_startElement($parser, $name, $attrs) {
326     global $scoes,$i,$resources,$parent,$level,$organization,$manifest,$defaultorg;
327     
328     switch ($name) {
329     case 'ITEM':
330         $i++;
331         $scoes[$i]['manifest'] = $manifest;
332         $scoes[$i]['organization'] = $organization;
333         $scoes[$i]['identifier'] = $attrs['IDENTIFIER'];
334         if (empty($attrs['IDENTIFIERREF']))
335             $attrs['IDENTIFIERREF'] = '';
336         $scoes[$i]['identifierref'] = $attrs['IDENTIFIERREF'];
337         if (empty($attrs['ISVISIBLE']))
338             $attrs['ISVISIBLE'] = '';
339         $scoes[$i]['isvisible'] = $attrs['ISVISIBLE'];
340         if (empty($attrs['PARAMETERS']))
341             $attrs['PARAMETERS'] = '';
342         $scoes[$i]['parameters'] = $attrs['PARAMETERS'];
343         $scoes[$i]['parent'] = $parent[$level];
344         $level++;
345         $parent[$level] = $attrs['IDENTIFIER'];
346     break;
347     case 'RESOURCE':
348         if (!isset($attrs['HREF'])) {
349             $attrs['HREF'] = '';
350         }
351         $resources[$attrs['IDENTIFIER']]['href']=$attrs['HREF'];
352         if (!isset($attrs['ADLCP:SCORMTYPE'])) {
353             $attrs['ADLCP:SCORMTYPE'] = 'sco'; //Default scorm type
354         }
355         $resources[$attrs['IDENTIFIER']]['scormtype']=$attrs['ADLCP:SCORMTYPE'];
356     break;
357     case 'ORGANIZATION':
358         $i++;
359         $scoes[$i]['manifest'] = $manifest;
360         $scoes[$i]['organization'] = '';
361         $scoes[$i]['identifier'] = $attrs['IDENTIFIER'];
362         $scoes[$i]['identifierref'] = '';
363         $scoes[$i]['isvisible'] = '';
364         $scoes[$i]['parent'] = $parent[$level];
365         $level++;
366         $parent[$level] = $attrs['IDENTIFIER'];
367         $organization = $attrs['IDENTIFIER'];
368     break;
369     case 'MANIFEST':
370         $manifest = $attrs['IDENTIFIER'];
371     break;
372     case 'ORGANIZATIONS':
373         if (!isset($attrs['DEFAULT'])) {
374             $attrs['DEFAULT'] = '';
375         }
376         $defaultorg = $attrs['DEFAULT'];
377     break;
378     }
381 function scorm_endElement($parser, $name) {
382     global $scoes,$i,$level,$datacontent,$manifest,$organization,$version;
383     if ($name == 'ITEM') {
384         $level--;
385     }
386     //if ($name == 'TITLE' && $level>0) {
387     if ($name == 'TITLE') {
388         $scoes[$i]['title'] = $datacontent;
389     }
390     if ($name == 'ADLCP:HIDERTSUI') {
391         $scoes[$i][$datacontent] = 1;
392     }
393     if ($name == 'ADLCP:DATAFROMLMS') {
394         $scoes[$i]['datafromlms'] = $datacontent;
395     }
396     if ($name == 'ADLCP:PREREQUISITES') {
397         $scoes[$i]['prerequisites'] = $datacontent;
398     }
399     if ($name == 'ADLCP:MAXTIMEALLOWED') {
400         $scoes[$i]['maxtimeallowed'] = $datacontent;
401     }
402     if ($name == 'ADLCP:TIMELIMITACTION') {
403         $scoes[$i]['timelimitaction'] = $datacontent;
404     }
405     if ($name == 'ADLCP:MASTERYSCORE') {
406         $scoes[$i]['masteryscore'] = $datacontent;
407     }
408     if ($name == 'ORGANIZATION') {
409         $organization = '';
410         $level--;
411     }
412     if ($name == 'MANIFEST') {
413         $manifest = '';
414     }
415     if ($name == 'SCHEMAVERSION') {
416         if (preg_match("/^(1\.2)$|^(CAM )?(1\.3)$/",$datacontent,$matches)) {
417             $version = 'SCORM_'.$matches[count($matches)-1];
418         } else {
419             $version = 'SCORM_1.2';
420         }
421     }
424 function scorm_characterData($parser, $data) {
425     global $datacontent;
426     $datacontent = utf8_decode($data);
429 function scorm_parse($pkgdir,$pkgtype,$scormid){
430     if ($pkgtype == 'AICC') {
431         return scorm_parse_aicc($pkgdir,$scormid);
432     } else {
433         return scorm_parse_scorm($pkgdir,'/imsmanifest.xml',$scormid);
434     }
437 function scorm_get_aicc_columns($row,$mastername='system_id') {
438     $tok = strtok(strtolower($row),"\",\n\r");
439     $result->columns = array();
440     $i=0;
441     while ($tok) {
442         if ($tok !='') {
443             $result->columns[] = $tok;
444             if ($tok == $mastername) {
445                 $result->mastercol = $i;
446             }
447             $i++;
448         }
449         $tok = strtok("\",\n\r");
450     }
451     return $result;
454 function scorm_forge_cols_regexp($columns,$remodule='(".*")?,') {
455     $regexp = '/^';
456     foreach ($columns as $column) {
457         $regexp .= $remodule;
458     }
459     $regexp = substr($regexp,0,-1) . '/';
460     return $regexp;
463 function scorm_parse_aicc($pkgdir,$scormid){
464     $version = 'AICC';
465     $ids = array();
466     $courses = array();
467     if ($handle = opendir($pkgdir)) {
468         while (($file = readdir($handle)) !== false) {
469             $ext = substr($file,strrpos($file,'.'));
470             $extension = strtolower(substr($ext,1));
471             $id = strtolower(basename($file,$ext));
472             $ids[$id]->$extension = $file;
473         }
474         closedir($handle);
475     }
476     foreach ($ids as $courseid => $id) {
477         if (isset($id->crs)) {
478             if (is_file($pkgdir.'/'.$id->crs)) {
479                 $rows = file($pkgdir.'/'.$id->crs);
480                 foreach ($rows as $row) {
481                     if (preg_match("/^(.+)=(.+)$/",$row,$matches)) {
482                         switch (strtolower(trim($matches[1]))) {
483                             case 'course_id':
484                                 $courses[$courseid]->id = trim($matches[2]); 
485                                 break;
486                             case 'course_title':
487                                 $courses[$courseid]->title = trim($matches[2]);
488                                 break;
489                             case 'version':
490                                 $courses[$courseid]->version = 'AICC_'.trim($matches[2]);
491                                 break;
492                         }
493                     }
494                 }
495             }
496         }
497         if (isset($id->des)) {
498             $rows = file($pkgdir.'/'.$id->des);
499             $columns = scorm_get_aicc_columns($rows[0]);
500             $regexp = scorm_forge_cols_regexp($columns->columns);
501             for ($i=1;$i<count($rows);$i++) {
502                 if (preg_match($regexp,$rows[$i],$matches)) {
503                     for ($j=0;$j<count($columns->columns);$j++) {
504                         $column = $columns->columns[$j];
505                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
506                     }
507                 }
508             } 
509         }
510         if (isset($id->au)) {
511             $rows = file($pkgdir.'/'.$id->au);
512             $columns = scorm_get_aicc_columns($rows[0]);
513         $regexp = scorm_forge_cols_regexp($columns->columns);
514         for ($i=1;$i<count($rows);$i++) {
515         if (preg_match($regexp,$rows[$i],$matches)) {
516             for ($j=0;$j<count($columns->columns);$j++) {
517             $column = $columns->columns[$j];
518             $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
519             }
520         }
521         }
522     }
523     if (isset($id->cst)) {
524         $rows = file($pkgdir.'/'.$id->cst);
525         $columns = scorm_get_aicc_columns($rows[0],'block');
526         $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,');
527         for ($i=1;$i<count($rows);$i++) {
528         if (preg_match($regexp,$rows[$i],$matches)) {
529             for ($j=0;$j<count($columns->columns);$j++) {
530             if ($j != $columns->mastercol) {
531                 $courses[$courseid]->elements[substr(trim($matches[$j+1]),1,-1)]->parent = substr(trim($matches[$columns->mastercol+1]),1,-1);
532             }
533             }
534         }
535         }
536     }
537     if (isset($id->ort)) {
538         $rows = file($pkgdir.'/'.$id->ort);
539     }
540     if (isset($id->pre)) {
541         $rows = file($pkgdir.'/'.$id->pre);
542         $columns = scorm_get_aicc_columns($rows[0],'structure_element');
543         $regexp = scorm_forge_cols_regexp($columns->columns,'(.+),');
544         for ($i=1;$i<count($rows);$i++) {
545         if (preg_match($regexp,$rows[$i],$matches)) {
546             $courses[$courseid]->elements[$columns->mastercol+1]->prerequisites = substr(trim($matches[1-$columns->mastercol+1]),1,-1);
547         }
548         }
549     }
550     if (isset($id->cmp)) {
551         $rows = file($pkgdir.'/'.$id->cmp);
552     }
553     }
554     //print_r($courses);
555     $launch = 0;
556     if (isset($courses)) {
557     
558     foreach ($courses as $course) {
559         unset($sco);
560         $sco->identifier = $course->id;
561         $sco->scorm = $scormid;
562         $sco->organization = '';
563         $sco->title = $course->title;
564         $sco->parent = '/';
565         $sco->launch = '';
566             $sco->scormtype = '';
567         //print_r($sco);
568             $id = insert_record('scorm_scoes',$sco);
569             if ($launch == 0) {
570             $launch = $id;
571             }
572         if (isset($course->elements)) {
573         foreach($course->elements as $element) {
574             unset($sco);
575             $sco->identifier = $element->system_id;
576             $sco->scorm = $scormid;
577                 $sco->organization = $course->id;
578             $sco->title = $element->title;
579             if (strtolower($element->parent) == 'root') {
580             $sco->parent = '/';
581             } else {
582             $sco->parent = $element->parent;
583             }
584             if (isset($element->file_name)) {
585                 $sco->launch = $element->file_name;
586             $sco->scormtype = 'sco';
587             } else {
588             $element->file_name = '';
589             $sco->scormtype = '';
590             }
591             if (!isset($element->prerequisites)) {
592                 $element->prerequisites = '';
593                     } 
594                     $sco->prerequisites = $element->prerequisites;
595             if (!isset($element->max_time_allowed)) {
596                 $element->max_time_allowed = '';
597                     } 
598                     $sco->maxtimeallowed = $element->max_time_allowed;
599                     if (!isset($element->time_limit_action)) {
600                 $element->time_limit_action = '';
601                     } 
602                     $sco->timelimitaction = $element->time_limit_action;
603                     if (!isset($element->mastery_score)) {
604                 $element->mastery_score = '';
605                     } 
606                     $sco->masteryscore = $element->mastery_score;
607                     $sco->previous = 0;
608                     $sco->next = 0;
609         
610                 //print_r($sco);
611                 $id = insert_record('scorm_scoes',$sco);
613             if ($launch==0) {
614                 $launch = $id;
615             }
616         }
617         }
618     }
619     }
620     if (isset($courses[0]->version)) {
621     $version = $courses[0]->version;
622     }
623     set_field('scorm','version',$version,'id',$scormid);
624     return $launch;
627 function scorm_parse_scorm($pkgdir,$file,$scormid) {
628     global $scoes,$i,$resources,$parent,$level,$defaultorg,$version,$organization,$manifest;
629     $datacontent = '';
630     $scoes[][] = '';
631     $resources[] = '';
632     $manifest = '';
633     $version = 'SCORM';
634     $organization = '';
635     $defaultorg = '';
636     $i = 0;
637     $level = 0;
638     $parent[$level] = '/';
640     $xmlparser = xml_parser_create('UTF-8');
641     // use case-folding so we are sure to find the tag in $map_array
642     xml_parser_set_option($xmlparser, XML_OPTION_CASE_FOLDING, true);
643     xml_set_element_handler($xmlparser, 'scorm_startElement', 'scorm_endElement');
644     xml_set_character_data_handler($xmlparser, 'scorm_characterData');
645     if (!($fp = fopen($pkgdir.$file, 'r'))) {
646        die('could not open XML input');
647     }
649     while ($data = fread($fp, 4096)) {
650         if (!xml_parse($xmlparser, $data, feof($fp))) {
651             die(sprintf('XML error: %s at line %d',
652                     xml_error_string(xml_get_error_code($xmlparser)),
653                     xml_get_current_line_number($xmlparser)));
654         }
655     }
656     xml_parser_free($xmlparser);
657     $launch = 0;
659     $sco->scorm = $scormid;
660     delete_records('scorm_scoes','scorm',$scormid);
661     delete_records('scorm_scoes_track','scormid',$scormid);
662     
663     if (isset($scoes[1])) {
664         for ($j=1; $j<=$i; $j++) {
665             $sco->identifier = $scoes[$j]['identifier'];
666             $sco->parent = $scoes[$j]['parent'];
667             $sco->title = $scoes[$j]['title'];
668             $sco->manifest = $scoes[$j]['manifest'];
669             $sco->organization = $scoes[$j]['organization'];
670             if (!isset($scoes[$j]['datafromlms'])) {
671             $scoes[$j]['datafromlms'] = '';
672             } 
673             $sco->datafromlms = $scoes[$j]['datafromlms'];
674             if (!isset($scoes[$j]['prerequisites'])) {
675             $scoes[$j]['prerequisites'] = '';
676             } 
677             $sco->prerequisites = $scoes[$j]['prerequisites'];
678             if (!isset($scoes[$j]['maxtimeallowed'])) {
679             $scoes[$j]['maxtimeallowed'] = '';
680             } 
681             $sco->maxtimeallowed = $scoes[$j]['maxtimeallowed'];
682             if (!isset($scoes[$j]['timelimitaction'])) {
683             $scoes[$j]['timelimitaction'] = '';
684             } 
685             $sco->timelimitaction = $scoes[$j]['timelimitaction'];
686             if (!isset($scoes[$j]['masteryscore'])) {
687             $scoes[$j]['masteryscore'] = '';
688             } 
689             $sco->masteryscore = $scoes[$j]['masteryscore'];
690         
691             if (!isset($resources[($scoes[$j]['identifierref'])]['href'])) {
692             $resources[($scoes[$j]['identifierref'])]['href'] = '';
693             }
694             $sco->launch = $resources[($scoes[$j]['identifierref'])]['href'];
695             
696             if (!isset($scoes[$j]['parameters'])) {
697             $scoes[$j]['parameters'] = '';
698             } 
699             $sco->parameters = $scoes[$j]['parameters'];
700         
701             if (!isset($resources[($scoes[$j]['identifierref'])]['scormtype'])) {
702             $resources[($scoes[$j]['identifierref'])]['scormtype'] = '';
703             }
704             $sco->scormtype = $resources[($scoes[$j]['identifierref'])]['scormtype'];
705         
706             if (!isset($scoes[$j]['previous'])) {
707             $scoes[$j]['previous'] = 0;
708             }
709             $sco->previous = $scoes[$j]['previous'];
710         
711             if (!isset($scoes[$j]['continue'])) {
712             $scoes[$j]['continue'] = 0;
713             }
714             $sco->next = $scoes[$j]['continue'];
715         
716             if (scorm_remove_spaces($scoes[$j]['isvisible']) != 'false') {
717             $id = insert_record('scorm_scoes',$sco);
718             }
719             if (($launch==0) && ($defaultorg==$sco->identifier)) {
720             $launch = $id;
721             }
722     }
723     } else {
724         foreach ($resources as $label => $resource) {
725             if (!empty($resource['href'])) {
726                 $sco->identifier = $label;
727                 $sco->title = $label;
728                 $sco->parent = '/';
729                 $sco->launch = $resource['href'];
730                 $sco->scormtype = $resource['type'];
731                 $id = insert_record('scorm_scoes',$sco);
732                 
733                 if ($launch == 0) {
734                 $launch = $id;
735                 }
736             }
737         }
738     }
739     set_field('scorm','version',$version,'id',$scormid);
740     return $launch;
743 function scorm_get_tracks($scoid,$userid) {
744 /// Gets all tracks of specified sco and user 
745     global $CFG;
746     
747     if ($tracks = get_records_select('scorm_scoes_track',"userid=$userid AND scoid=$scoid",'element ASC')) {
748         //print_r($tracks);
749         $usertrack->userid = $userid;
750         $usertrack->scoid = $scoid;
751         $usertrack->score_raw = '';
752         $usertrack->status = '';
753         $usertrack->total_time = '00:00:00';
754         $usertrack->session_time = '00:00:00';
755     foreach ($tracks as $track) {
756         //$element = str_replace('.','_',$track->element);
757         $element = $track->element;
758         $usertrack->{$element} = $track->value;
759         switch ($element) {
760         case 'cmi.core.lesson_status':
761         case 'cmi.completition_status':
762             if ($track->value == 'not attempted') {
763             $track->value = 'notattempted';
764             }
765             $usertrack->status = $track->value;
766         break;
767         case 'cmi.core.score.raw':
768         case 'cmi.score.raw':
769             $usertrack->score_raw = $track->value;
770         break;
771         case 'cmi.core.session_time':
772         case 'cmi.session_time':
773             $usertrack->session_time = $track->value;
774         break;
775         case 'cmi.core.total_time':
776         case 'cmi.total_time':
777             $usertrack->total_time = $track->value;
778         break;
779         }
780     }
781     //print_r($usertrack);
782     return $usertrack;
783     } else {
784     return false;
785     }
788 function scorm_get_user_data($userid) {
789 /// Gets user info required to display the table of scorm results
790 /// for report.php
792     return get_record('user','id',$userid,'','','','','firstname, lastname, picture'); 
795 function scorm_remove_spaces($sourcestr) {
796 // Remove blank space from a string
797     $newstr='';
798     for( $i=0; $i<strlen($sourcestr); $i++) {
799         if ($sourcestr[$i]!=' ')
800             $newstr .=$sourcestr[$i];
801     }
802     return $newstr;
805 function scorm_string_round($stringa, $len=11) {
806 // Crop a string to $len character and set an anchor title to the full string
807     if ( strlen($stringa)>$len ) {
808     return '<a name="none" title="'.$stringa.'">'.substr($stringa,0,$len-4).'...'.substr($stringa,strlen($stringa)-1,1).'</a>';
809     } else
810     return $stringa;
813 function scorm_external_link($link) {
814 // check if a link is external
815     $result = false;
816     $link = strtolower($link);
817     if (substr($link,0,7) == 'http://')
818         $result = true;
819     else if (substr($link,0,8) == 'https://')
820         $result = true;
821     else if (substr($link,0,4) == 'www.')
822         $result = true;
823     return $result;
826 function scorm_count_launchable($scormid,$organization) {
827     return count_records_select('scorm_scoes',"scorm=$scormid AND organization='$organization' AND launch<>''");
830 function scorm_display_structure($scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$play=false) {
831     global $USER;
832     
833     $strexpand = get_string('expcoll','scorm');
834     
835     echo "<ul id='0' class='$liststyle'>";
836     $incomplete = false;
837     $organizationsql = '';
838     if (empty($currentorg)) {
839         //
840     } else {
841         $organizationsql = "AND organization='$currentorg'";
842     }
843     if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){
844         $level=0;
845         $sublist=1;
846         $previd = 0;
847         $nextid = 0; 
848         $parents[$level]='/';
849         foreach ($scoes as $sco) {
850             if ($parents[$level]!=$sco->parent) {
851             if ($level>0 && $parents[$level-1]==$sco->parent) {
852                 echo "\t\t</ul></li>\n";
853                 $level--;
854             } else {
855                 $i = $level;
856                 $closelist = '';
857                 while (($i > 0) && ($parents[$level] != $sco->parent)) {
858                  $closelist .= "\t\t</ul></li>\n";
859                  $i--;
860              }
861              if (($i == 0) && ($sco->parent != $currentorg)) {
862                  echo "\t\t<li><ul id='$sublist' class='$liststyle'>\n";
863                     $level++;
864                 } else {
865                     echo $closelist;
866                     $level = $i;
867                 }
868                 $parents[$level]=$sco->parent;
869             }
870             }
871             echo "\t\t<li>";
872             $nextsco = next($scoes);
873             if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) {
874             $sublist++;
875             echo '<a href="#" onClick="expandCollide(img'.$sublist.','.$sublist.');"><img id="img'.$sublist.'" src="pix/minus.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>';
876             } else {
877             echo '<img src="pix/spacer.gif" />';
878             }
879             
880             if ($sco->launch) {
881                 $startbold = '';
882                 $endbold = '';
883                 $score = '';
884                 if (empty($scoid) && ($mode != 'normal')) {
885                 $scoid = $sco->id;
886             }
887             if ($usertrack=scorm_get_tracks($sco->id,$USER->id)) {
888                 if ( $usertrack->status == '') {
889                     $usertrack->status = 'notattempted';
890                     }
891                     $strstatus = get_string($usertrack->status,'scorm');
892                 echo "<img src='pix/".$usertrack->status.".gif' alt='$strstatus' title='$strstatus' />";
893              if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) {
894              $incomplete = true;
895              if ($play && empty($scoid)) {
896                  $scoid = $sco->id;
897                 }
898              }
899              if ($usertrack->score_raw != '') {
900                 $score = '('.get_string('score','scorm').':&nbsp;'.$usertrack->score_raw.')';
901             }
902             } else {
903                 if ($play && empty($scoid)) {
904                     $scoid = $sco->id;
905                     }
906                 if ($sco->scormtype == 'sco') {
907                 echo '<img src="pix/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />';
908                 $incomplete = true;
909                 } else {
910                 echo '<img src="pix/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />';
911                 }
912             }
914             if ($sco->id == $scoid) {
915                 $startbold = '<b>';
916                 $endbold = '</b>';
917                 if ($nextsco !== false) {
918                 $nextid = $nextsco->id;
919                 } else {
920                 $nextid = 0;
921                 }
922             $shownext = $sco->next;
923             $showprev = $sco->previous;
924                 }
925             if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1)) {
926                     $previd = $sco->id;
927                 }
928                 
929             echo "&nbsp;$startbold<a href='javascript:playSCO(".$sco->id.");'>$sco->title</a> $score$endbold</li>\n";
930             } else {
931         echo "&nbsp;$sco->title</li>\n";
932         }
933     }
934     for ($i=0;$i<$level;$i++) {
935         echo "\t\t</ul></li>\n";
936     }
937     }
938     echo "\t</ul>\n";
939     if ($play) {
940         $result->id = $scoid;
941         $result->prev = $previd;
942         $result->next = $nextid;
943     $result->showprev = $showprev;
944     $result->shownext = $shownext;
945     return $result;
946     } else {
947         return $incomplete;
948     }
950 ?>