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 $SCORM_WINDOW_OPTIONS = array('resizable', 'scrollbars', 'status', 'height', 'width');
17 if (!isset($CFG->scorm_popup
)) {
18 set_config('scorm_popup', '');
20 if (!isset($CFG->scorm_validate
)) {
21 $scorm_validate = 'none';
22 //I've commented this out for Moodle 1.4, as I've seen errors in
23 //SCORM packages even though the actual package worked fine. -- Martin Dougiamas
24 //if (extension_loaded('domxml')) {
25 // $scorm_validate = 'domxml';
27 //if (version_compare(phpversion(),'5.0.0','>=')) {
28 // $scorm_validate = 'php5';
30 set_config('scorm_validate', $scorm_validate);
33 foreach ($SCORM_WINDOW_OPTIONS as $popupoption) {
34 $popupoption = "scorm_popup$popupoption";
35 if (!isset($CFG->$popupoption)) {
36 if ($popupoption == 'scorm_popupheight') {
37 set_config($popupoption, 450);
38 } else if ($popupoption == 'scorm_popupwidth') {
39 set_config($popupoption, 620);
41 set_config($popupoption, 'checked');
46 if (!isset($CFG->scorm_framesize
)) {
47 set_config('scorm_framesize', 140);
50 function scorm_add_instance($scorm) {
51 /// Given an object containing all the necessary data,
52 /// (defined by the form in mod.html) this function
53 /// will create a new instance and return the id number
54 /// of the new instance.
56 $scorm->timemodified
= time();
58 # May have to add extra stuff in here #
59 global $SCORM_WINDOW_OPTIONS;
63 $optionlist = array();
64 foreach ($SCORM_WINDOW_OPTIONS as $option) {
65 if (isset($scorm->$option)) {
66 $optionlist[] = $option.'='.$scorm->$option;
69 $scorm->popup
= implode(',', $optionlist);
72 if ($scorm->popup
!= '') {
73 $scorm->popup
.= ',location=0,menubar=0,toolbar=0';
77 return insert_record('scorm', $scorm);
81 function scorm_update_instance($scorm) {
82 /// Given an object containing all the necessary data,
83 /// (defined by the form in mod.html) this function
84 /// will update an existing instance with new data.
86 $scorm->timemodified
= time();
87 $scorm->id
= $scorm->instance
;
89 # May have to add extra stuff in here #
90 global $SCORM_WINDOW_OPTIONS;
94 $optionlist = array();
95 foreach ($SCORM_WINDOW_OPTIONS as $option) {
96 if (isset($scorm->$option)) {
97 $optionlist[] = $option.'='.$scorm->$option;
100 $scorm->popup
= implode(',', $optionlist);
102 if ($scorm->popup
!= '') {
103 $scorm->popup
.= ',location=0,menubar=0,toolbar=0';
106 return update_record('scorm', $scorm);
110 function scorm_delete_instance($id) {
111 /// Given an ID of an instance of this module,
112 /// this function will permanently delete the instance
113 /// and any data that depends on it.
115 require('../config.php');
117 if (! $scorm = get_record('scorm', 'id', $id)) {
123 # Delete any dependent files #
124 scorm_delete_files($CFG->dataroot
.'/'.$scorm->course
.'/moddata/scorm'.$scorm->datadir
);
126 # Delete any dependent records here #
127 if (! delete_records('scorm_sco_users', 'scormid', $scorm->id
)) {
130 if (! delete_records('scorm_scoes', 'scorm', $scorm->id
)) {
133 if (! delete_records('scorm', 'id', $scorm->id
)) {
141 function scorm_user_outline($course, $user, $mod, $scorm) {
142 /// Return a small object with summary information about what a
143 /// user has done with a given particular instance of this module
144 /// Used for user activity reports.
145 /// $return->time = the time they did it
146 /// $return->info = a short text description
151 function scorm_user_complete($course, $user, $mod, $scorm) {
152 /// Print a detailed representation of what a user has done with
153 /// a given particular instance of this module, for user activity reports.
158 function scorm_print_recent_activity(&$logs, $isteacher=false) {
159 /// Given a list of logs, assumed to be those since the last login
160 /// this function prints a short list of changes related to this module
161 /// If isteacher is true then perhaps additional information is printed.
162 /// This function is called from course/lib.php: print_recent_activity()
164 global $CFG, $COURSE_TEACHER_COLOR;
168 return $content; // True if anything was printed, otherwise false
171 function scorm_cron () {
172 /// Function to be run periodically according to the moodle cron
173 /// This function searches for things that need to be done, such
174 /// as sending out mail, toggling flags etc ...
181 function scorm_grades($scormid) {
182 /// Must return an array of grades for a given instance of this module,
183 /// indexed by user. It also returns a maximum allowed grade.
187 if (!$scorm = get_record("scorm", "id", $scormid)) {
191 if ($scorm->grademethod
== VALUESCOES
) {
192 if (!$return->maxgrade
= count_records_select('scorm_scoes',"scorm='$scormid' AND launch<>''")) {
196 $return->grades
= NULL;
197 if ($sco_users=get_records_select('scorm_sco_users', "scormid='$scormid' GROUP BY userid")) {
198 foreach ($sco_users as $sco_user) {
199 $user_data=get_records_select('scorm_sco_users',"scormid='$scormid' AND userid='$sco_user->userid'");
200 $scores->completed
=0;
202 $scores->incomplete
=0;
204 $scores->notattempted
=0;
206 $data = current($user_data);
207 foreach ($user_data as $data) {
208 if ($data->cmi_core_lesson_status
=='passed')
209 $scores->completed++
;
211 $scores->{scorm_remove_spaces($data->cmi_core_lesson_status
)}++
;
213 if ($scores->completed
)
214 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/completed.gif\" alt=\"".get_string('completed','scorm')."\" title=\"".get_string('completed','scorm')."\"> $scores->completed ";
215 if ($scores->incomplete
)
216 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/incomplete.gif\" alt=\"".get_string('incomplete','scorm')."\" title=\"".get_string('incomplete','scorm')."\"> $scores->incomplete ";
218 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/failed.gif\" alt=\"".get_string('failed','scorm')."\" title=\"".get_string('failed','scorm')."\"> $scores->failed ";
219 if ($scores->browsed
)
220 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/browsed.gif\" alt=\"".get_string('browsed','scorm')."\" title=\"".get_string('browsed','scorm')."\"> $scores->browsed ";
221 if ($scores->notattempted
)
222 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/notattempted.gif\" alt=\"".get_string('notattempted','scorm')."\" title=\"".get_string('notattempted','scorm')."\"> $scores->notattempted ";
224 $return->grades
[$sco_user->userid
]=$result;
229 $grades = get_records_select("scorm_sco_users", "scormid=$scormid AND cmi_core_score_raw>0","","id,userid,cmi_core_score_raw");
230 //$grades = get_records_menu("scorm_sco_users", "scormid",$scormid,"","userid,cmi_core_score_raw");
231 $valutations = array();
232 foreach ($grades as $grade) {
233 if (!isset($valutations[$grade->userid
])) {
234 if ($scorm->grademethod
== VALUEAVERAGE
) {
236 $values[$grade->userid
]->grade
= 0;
237 $values[$grade->userid
]->values
= 0;
239 $valutations[$grade->userid
] = 0;
241 switch ($scorm->grademethod
) {
243 if ($grade->cmi_core_score_raw
> $valutations[$grade->userid
]) {
244 $valutations[$grade->userid
] = $grade->cmi_core_score_raw
;
248 $values[$grade->userid
]->grade +
= $grade->cmi_core_score_raw
;
249 $values[$grade->userid
]->values++
;
252 $valutations[$grade->userid
] +
= $grade->cmi_core_score_raw
;
256 if ($scorm->grademethod
== VALUEAVERAGE
) {
257 foreach($values as $userid => $value) {
258 $valutations[$userid] = $value->grade
/$value->values
;
262 $return->grades
= $valutations;
263 $return->maxgrade
= $scorm->maxgrade
;
269 //////////////////////////////////////////////////////////////////////////////////////
270 /// Any other scorm functions go here. Each of them must have a name that
271 /// starts with scorm_
274 function scorm_randstring($len = '8')
278 for($i=0; $i<$len; $i++
) {
279 $char = chr(rand(48,122));
280 while (!ereg('[a-zA-Z0-9]', $char)){
281 if($char == $lchar) continue;
282 $char = chr(rand(48,90));
291 function scorm_datadir($strPath, $existingdir='', $prefix = 'SCORM')
295 if (($existingdir!='') && (is_dir($strPath.$existingdir)))
296 return $strPath.$existingdir;
298 if (is_dir($strPath)) {
300 $datadir='/'.$prefix.scorm_randstring();
301 } while (file_exists($strPath.$datadir));
302 mkdir($strPath.$datadir, $CFG->directorypermissions
);
303 @chmod
($strPath.$datadir, $CFG->directorypermissions
); // Just in case mkdir didn't do it
304 return $strPath.$datadir;
310 if ($CFG->scorm_validate
== 'domxml') {
311 require_once('validatordomxml.php');
314 function scorm_validate($manifest)
318 global $item_idref_array;
320 global $def_org_array;
321 global $id_org_array;
323 if (is_file ($manifest)) {
324 if (file_exists($manifest)) {
325 if ($CFG->scorm_validate
== 'domxml') {
326 $manifest_string = file_get_contents($manifest);
328 /* Elimino i caratteri speciali di spaziatura e ritorno a capo dal file xml */
330 $spec = array('\n', '\r', '\t', '\0', '\x0B');
331 $content = str_replace($spec, '', $manifest_string);
333 if ($xmldoc = domxml_open_mem($content)) {
334 $root = $xmldoc->document_element();
335 if (!testRoot($root)) {
338 if (testNode($root)) {
339 // Nel corpo di questo if si controllano le corrispondenze fra gli attributi
340 // Nello Standard SCORM ad ogni attributo idRef di <item> deve corrispondere
341 // un attributo ID di <resource>
342 // Gli array degli attributi sono stati dichiarati globali in validator.php
343 // pertanto possono essere utilizzati direttamente all'interno di main.php
345 foreach($item_idref_array as $elem_it) {
346 if (array_search($elem_it, $idres_array) === false) {
351 foreach($def_org_array as $elem_def) {
352 if (array_search($elem_it, $id_org_array) === false) {
358 return 'badmanifest';
371 function scorm_delete_files($directory)
373 if (is_dir($directory))
375 $handle=opendir($directory);
376 while (($file = readdir($handle)) != '')
378 if ($file != '.' && $file != '..')
380 if (!is_dir($directory.'/'.$file)) {
381 //chmod($directory.'/'.$file,0777);
382 unlink($directory.'/'.$file);
384 scorm_delete_files($directory.'/'.$file);
392 function scorm_startElement($parser, $name, $attrs) {
394 global $scoes,$i,$resources,$parent,$level,$organization,$manifest,$defaultorg;
396 if ($name == 'ITEM') {
398 $scoes[$i]['manifest'] = $manifest;
399 $scoes[$i]['organization'] = $organization;
400 $scoes[$i]['identifier'] = $attrs['IDENTIFIER'];
401 if (empty($attrs['IDENTIFIERREF']))
402 $attrs['IDENTIFIERREF'] = '';
403 $scoes[$i]['identifierref'] = $attrs['IDENTIFIERREF'];
404 if (empty($attrs['ISVISIBLE']))
405 $attrs['ISVISIBLE'] = '';
406 $scoes[$i]['isvisible'] = $attrs['ISVISIBLE'];
407 $scoes[$i]['parent'] = $parent[$level];
409 $parent[$level] = $attrs['IDENTIFIER'];
411 if ($name == 'RESOURCE') {
412 if (!isset($attrs['HREF'])) {
415 $resources[$attrs['IDENTIFIER']]['href']=$attrs['HREF'];
416 if (!isset($attrs['ADLCP:SCORMTYPE'])) {
417 $attrs['ADLCP:SCORMTYPE'] = '';
419 $resources[$attrs['IDENTIFIER']]['type']=$attrs['ADLCP:SCORMTYPE'];
421 if ($name == 'ORGANIZATION') {
423 $scoes[$i]['manifest'] = $manifest;
424 $scoes[$i]['organization'] = '';
425 $scoes[$i]['identifier'] = $attrs['IDENTIFIER'];
426 $scoes[$i]['identifierref'] = '';
427 $scoes[$i]['isvisible'] = '';
428 $scoes[$i]['parent'] = $parent[$level];
430 $parent[$level] = $attrs['IDENTIFIER'];
431 $organization = $attrs['IDENTIFIER'];
433 if ($name == 'MANIFEST') {
434 $manifest = $attrs['IDENTIFIER'];
436 if ($name == 'ORGANIZATIONS') {
437 if (!isset($attrs['DEFAULT'])) {
438 $attrs['DEFAULT'] = '';
440 $defaultorg = $attrs['DEFAULT'];
444 function scorm_endElement($parser, $name) {
445 global $scoes,$i,$level,$datacontent,$navigation;
446 if ($name == 'ITEM') {
449 //if ($name == 'TITLE' && $level>0) {
450 if ($name == 'TITLE') {
451 $scoes[$i]['title'] = $datacontent;
453 if ($name == 'ADLCP:HIDERTSUI') {
454 $scoes[$i][$datacontent] = 1;
456 if ($name == 'ADLCP:DATAFROMLMS') {
457 $scoes[$i]['datafromlms'] = $datacontent;
459 if ($name == 'ORGANIZATION') {
463 if ($name == 'MANIFEST') {
468 function scorm_characterData($parser, $data) {
470 $datacontent = $data;
473 function scorm_parse($basedir,$file,$scorm_id) {
474 global $scoes,$i,$resources,$parent,$level,$defaultorg;
482 $parent[$level] = '/';
484 $xml_parser = xml_parser_create('UTF-8');
485 // use case-folding so we are sure to find the tag in $map_array
486 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING
, true);
487 xml_set_element_handler($xml_parser, 'scorm_startElement', 'scorm_endElement');
488 xml_set_character_data_handler($xml_parser, 'scorm_characterData');
489 if (!($fp = fopen($basedir.$file, 'r'))) {
490 die('could not open XML input');
493 while ($data = fread($fp, 4096)) {
494 if (!xml_parse($xml_parser, $data, feof($fp))) {
495 die(sprintf('XML error: %s at line %d',
496 xml_error_string(xml_get_error_code($xml_parser)),
497 xml_get_current_line_number($xml_parser)));
500 xml_parser_free($xml_parser);
503 $sco->scorm
= $scorm_id;
504 delete_records('scorm_scoes','scorm',$scorm_id);
505 delete_records('scorm_sco_users','scormid',$scorm_id);
507 if (isset($scoes[1])) {
508 for ($j=1; $j<=$i; $j++
) {
509 $sco->identifier
= $scoes[$j]['identifier'];
510 $sco->parent
= $scoes[$j]['parent'];
511 $sco->title
= $scoes[$j]['title'];
512 $sco->organization
= $scoes[$j]['organization'];
513 if (!isset($scoes[$j]['datafromlms'])) {
514 $scoes[$j]['datafromlms'] = '';
516 $sco->datafromlms
= $scoes[$j]['datafromlms'];
518 if (!isset($resources[($scoes[$j]['identifierref'])]['href'])) {
519 $resources[($scoes[$j]['identifierref'])]['href'] = '';
521 $sco->launch
= $resources[($scoes[$j]['identifierref'])]['href'];
523 if (!isset($resources[($scoes[$j]['identifierref'])]['type'])) {
524 $resources[($scoes[$j]['identifierref'])]['type'] = '';
526 $sco->type
= $resources[($scoes[$j]['identifierref'])]['type'];
528 if (!isset($scoes[$j]['previous'])) {
529 $scoes[$j]['previous'] = 0;
531 $sco->previous
= $scoes[$j]['previous'];
533 if (!isset($scoes[$j]['continue'])) {
534 $scoes[$j]['continue'] = 0;
536 $sco->next
= $scoes[$j]['continue'];
538 if (scorm_remove_spaces($scoes[$j]['isvisible']) != 'false') {
539 $id = insert_record('scorm_scoes',$sco);
541 //if (($launch==0) && (isset($sco->launch)) && ($defaultorg==$sco->organization)) {
542 if (($launch==0) && ($defaultorg==$sco->identifier
)) {
547 foreach ($resources as $label => $resource) {
548 if (!empty($resource['href'])) {
549 $sco->identifier
= $label;
550 $sco->title
= $label;
552 $sco->launch
= $resource['href'];
553 $sco->type
= $resource['type'];
554 $id = insert_record('scorm_scoes',$sco);
565 function scorm_get_scoes_records($sco_user) {
566 /// Gets all info required to display the table of scorm results
570 return get_records_sql("SELECT su.*, u.firstname, u.lastname, u.picture
571 FROM {$CFG->prefix}scorm_sco_users su,
573 WHERE su.scormid = '$sco_user->scormid'
575 AND su.userid = '$sco_user->userid'
579 function scorm_remove_spaces($sourcestr) {
580 // Remove blank space from a string
582 for( $i=0; $i<strlen($sourcestr); $i++
) {
583 if ($sourcestr[$i]!=' ')
584 $newstr .=$sourcestr[$i];
589 function scorm_string_round($stringa) {
590 // Crop a string to $len character and set an anchor title to the full string
592 if ( strlen($stringa)>$len ) {
593 return "<A name=\"\" title=\"$stringa\">".substr($stringa,0,$len-4).'...'.substr($stringa,strlen($stringa)-1,1).'</A>';
598 function scorm_external_link($link) {
599 // check if a link is external
601 $link = strtolower($link);
602 if (substr($link,0,7) == 'http://')
604 else if (substr($link,0,8) == 'https://')
606 else if (substr($link,0,4) == 'www.')
608 /*else if (substr($link,0,7) == 'rstp://')
610 else if (substr($link,0,6) == 'rtp://')
612 else if (substr($link,0,6) == 'ftp://')
614 else if (substr($link,0,9) == 'gopher://')