Fixed typos
[moodle.git] / mod / scorm / lib.php
CommitLineData
98ca59f6 1<?PHP // $Id$
2
3/// Library of functions and constants for module scorm
4/// (replace scorm with the name of your module and delete this line)
5
1a12b1f1 6define('VALUESCOES', '0');
7define('VALUEHIGHEST', '1');
8define('VALUEAVERAGE', '2');
9define('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"));
14
15$SCORM_WINDOW_OPTIONS = array('resizable', 'scrollbars', 'status', 'height', 'width');
98ca59f6 16
76d35423 17if (!isset($CFG->scorm_popup)) {
1a12b1f1 18 set_config('scorm_popup', '');
19}
20if (!isset($CFG->scorm_validate)) {
21 $scorm_validate = 'none';
22 if (extension_loaded('domxml')) {
23 $scorm_validate = 'domxml';
24 }
25 if (version_compare(phpversion(),'5.0.0','>=')) {
26 $scorm_validate = 'php5';
27 }
28 set_config('scorm_validate', $scorm_validate);
29}
76d35423 30
31foreach ($SCORM_WINDOW_OPTIONS as $popupoption) {
32 $popupoption = "scorm_popup$popupoption";
33 if (!isset($CFG->$popupoption)) {
1a12b1f1 34 if ($popupoption == 'scorm_popupheight') {
76d35423 35 set_config($popupoption, 450);
1a12b1f1 36 } else if ($popupoption == 'scorm_popupwidth') {
76d35423 37 set_config($popupoption, 620);
38 } else {
1a12b1f1 39 set_config($popupoption, 'checked');
76d35423 40 }
41 }
42}
43
44if (!isset($CFG->scorm_framesize)) {
1a12b1f1 45 set_config('scorm_framesize', 140);
76d35423 46}
98ca59f6 47
48function scorm_add_instance($scorm) {
49/// Given an object containing all the necessary data,
50/// (defined by the form in mod.html) this function
51/// will create a new instance and return the id number
52/// of the new instance.
53
54 $scorm->timemodified = time();
55
56 # May have to add extra stuff in here #
76d35423 57 global $SCORM_WINDOW_OPTIONS;
58
1a12b1f1 59 $scorm->popup = '';
b98eebbf 60
61 $optionlist = array();
62 foreach ($SCORM_WINDOW_OPTIONS as $option) {
63 if (isset($scorm->$option)) {
1a12b1f1 64 $optionlist[] = $option.'='.$scorm->$option;
76d35423 65 }
76d35423 66 }
b98eebbf 67 $scorm->popup = implode(',', $optionlist);
de8595db 68
b98eebbf 69
1a12b1f1 70 if ($scorm->popup != '') {
b98eebbf 71 $scorm->popup .= ',location=0,menubar=0,toolbar=0';
de8595db 72 $scorm->auto = '0';
73 }
98ca59f6 74
1a12b1f1 75 return insert_record('scorm', $scorm);
98ca59f6 76}
77
78
79function scorm_update_instance($scorm) {
80/// Given an object containing all the necessary data,
81/// (defined by the form in mod.html) this function
82/// will update an existing instance with new data.
76d35423 83
98ca59f6 84 $scorm->timemodified = time();
85 $scorm->id = $scorm->instance;
86
87 # May have to add extra stuff in here #
76d35423 88 global $SCORM_WINDOW_OPTIONS;
89
1a12b1f1 90 $scorm->popup = '';
b98eebbf 91
92 $optionlist = array();
93 foreach ($SCORM_WINDOW_OPTIONS as $option) {
94 if (isset($scorm->$option)) {
1a12b1f1 95 $optionlist[] = $option.'='.$scorm->$option;
76d35423 96 }
76d35423 97 }
b98eebbf 98 $scorm->popup = implode(',', $optionlist);
b98eebbf 99
1a12b1f1 100 if ($scorm->popup != '') {
b98eebbf 101 $scorm->popup .= ',location=0,menubar=0,toolbar=0';
de8595db 102 $scorm->auto = '0';
103 }
1a12b1f1 104 return update_record('scorm', $scorm);
98ca59f6 105}
106
107
108function scorm_delete_instance($id) {
109/// Given an ID of an instance of this module,
110/// this function will permanently delete the instance
111/// and any data that depends on it.
4a9df373 112
98ca59f6 113 require('../config.php');
114
1a12b1f1 115 if (! $scorm = get_record('scorm', 'id', $id)) {
98ca59f6 116 return false;
117 }
118
119 $result = true;
120
121 # Delete any dependent files #
1a12b1f1 122 scorm_delete_files($CFG->dataroot.'/'.$scorm->course.'/moddata/scorm'.$scorm->datadir);
98ca59f6 123
124 # Delete any dependent records here #
1a12b1f1 125 if (! delete_records('scorm_sco_users', 'scormid', $scorm->id)) {
98ca59f6 126 $result = false;
127 }
1a12b1f1 128 if (! delete_records('scorm_scoes', 'scorm', $scorm->id)) {
98ca59f6 129 $result = false;
130 }
1a12b1f1 131 if (! delete_records('scorm', 'id', $scorm->id)) {
98ca59f6 132 $result = false;
133 }
134
135
136 return $result;
137}
138
139function scorm_user_outline($course, $user, $mod, $scorm) {
140/// Return a small object with summary information about what a
141/// user has done with a given particular instance of this module
142/// Used for user activity reports.
143/// $return->time = the time they did it
144/// $return->info = a short text description
145
146 return $return;
147}
148
149function scorm_user_complete($course, $user, $mod, $scorm) {
150/// Print a detailed representation of what a user has done with
151/// a given particular instance of this module, for user activity reports.
152
153 return true;
154}
155
156function scorm_print_recent_activity(&$logs, $isteacher=false) {
157/// Given a list of logs, assumed to be those since the last login
158/// this function prints a short list of changes related to this module
159/// If isteacher is true then perhaps additional information is printed.
160/// This function is called from course/lib.php: print_recent_activity()
161
162 global $CFG, $COURSE_TEACHER_COLOR;
163
e1e108d4 164 $content = NULL;
165
98ca59f6 166 return $content; // True if anything was printed, otherwise false
167}
168
169function scorm_cron () {
170/// Function to be run periodically according to the moodle cron
171/// This function searches for things that need to be done, such
172/// as sending out mail, toggling flags etc ...
173
174 global $CFG;
175
176 return true;
177}
178
179function scorm_grades($scormid) {
180/// Must return an array of grades for a given instance of this module,
181/// indexed by user. It also returns a maximum allowed grade.
182
4a9df373 183 global $CFG;
184
1a12b1f1 185 if (!$scorm = get_record("scorm", "id", $scormid)) {
4a9df373 186 return NULL;
187 }
188
1a12b1f1 189 if ($scorm->grademethod == VALUESCOES) {
190 if (!$return->maxgrade = count_records_select('scorm_scoes',"scorm='$scormid' AND launch<>''")) {
191 return NULL;
192 }
193
194 $return->grades = NULL;
195 if ($sco_users=get_records_select('scorm_sco_users', "scormid='$scormid' GROUP BY userid")) {
196 foreach ($sco_users as $sco_user) {
197 $user_data=get_records_select('scorm_sco_users',"scormid='$scormid' AND userid='$sco_user->userid'");
198 $scores->completed=0;
199 $scores->browsed=0;
200 $scores->incomplete=0;
201 $scores->failed=0;
202 $scores->notattempted=0;
203 $result='';
204 $data = current($user_data);
205 foreach ($user_data as $data) {
206 if ($data->cmi_core_lesson_status=='passed')
207 $scores->completed++;
208 else
209 $scores->{scorm_remove_spaces($data->cmi_core_lesson_status)}++;
210 }
211 if ($scores->completed)
212 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/completed.gif\" alt=\"".get_string('completed','scorm')."\" title=\"".get_string('completed','scorm')."\"> $scores->completed ";
213 if ($scores->incomplete)
214 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/incomplete.gif\" alt=\"".get_string('incomplete','scorm')."\" title=\"".get_string('incomplete','scorm')."\"> $scores->incomplete ";
215 if ($scores->failed)
216 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/failed.gif\" alt=\"".get_string('failed','scorm')."\" title=\"".get_string('failed','scorm')."\"> $scores->failed ";
217 if ($scores->browsed)
218 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/browsed.gif\" alt=\"".get_string('browsed','scorm')."\" title=\"".get_string('browsed','scorm')."\"> $scores->browsed ";
219 if ($scores->notattempted)
220 $result.="<img src=\"$CFG->wwwroot/mod/scorm/pix/notattempted.gif\" alt=\"".get_string('notattempted','scorm')."\" title=\"".get_string('notattempted','scorm')."\"> $scores->notattempted ";
4a9df373 221
1a12b1f1 222 $return->grades[$sco_user->userid]=$result;
223 }
98ca59f6 224
1a12b1f1 225 }
226 } else {
227 $grades = get_records_select("scorm_sco_users", "scormid=$scormid AND cmi_core_score_raw>0","","id,userid,cmi_core_score_raw");
228 //$grades = get_records_menu("scorm_sco_users", "scormid",$scormid,"","userid,cmi_core_score_raw");
229 $valutations = array();
230 foreach ($grades as $grade) {
231 if (!isset($valutations[$grade->userid])) {
232 if ($scorm->grademethod == VALUEAVERAGE) {
233 $values = array();
234 $values[$grade->userid]->grade = 0;
235 $values[$grade->userid]->values = 0;
236 }
237 $valutations[$grade->userid] = 0;
238 }
239 switch ($scorm->grademethod) {
240 case VALUEHIGHEST:
241 if ($grade->cmi_core_score_raw > $valutations[$grade->userid]) {
242 $valutations[$grade->userid] = $grade->cmi_core_score_raw;
243 }
244 break;
245 case VALUEAVERAGE:
246 $values[$grade->userid]->grade += $grade->cmi_core_score_raw;
247 $values[$grade->userid]->values++;
248 break;
249 case VALUESUM:
250 $valutations[$grade->userid] += $grade->cmi_core_score_raw;
251 break;
252 }
253 }
254 if ($scorm->grademethod == VALUEAVERAGE) {
255 foreach($values as $userid => $value) {
256 $valutations[$userid] = $value->grade/$value->values;
257 }
258 }
259 //print_r($grades);
260 $return->grades = $valutations;
261 $return->maxgrade = $scorm->maxgrade;
98ca59f6 262 }
98ca59f6 263 return $return;
264}
265
266
267//////////////////////////////////////////////////////////////////////////////////////
268/// Any other scorm functions go here. Each of them must have a name that
269/// starts with scorm_
270
271
1a12b1f1 272function scorm_randstring($len = '8')
98ca59f6 273{
274 $rstring = NULL;
1a12b1f1 275 $lchar = '';
98ca59f6 276 for($i=0; $i<$len; $i++) {
277 $char = chr(rand(48,122));
1a12b1f1 278 while (!ereg('[a-zA-Z0-9]', $char)){
98ca59f6 279 if($char == $lchar) continue;
280 $char = chr(rand(48,90));
281 }
282 $rstring .= $char;
283 $lchar = $char;
284 }
285 return $rstring;
286}
287
98ca59f6 288
1a12b1f1 289function scorm_datadir($strPath, $existingdir='', $prefix = 'SCORM')
98ca59f6 290{
e23ef951 291 global $CFG;
292
1a12b1f1 293 if (($existingdir!='') && (is_dir($strPath.$existingdir)))
4a9df373 294 return $strPath.$existingdir;
295
296 if (is_dir($strPath)) {
297 do {
1a12b1f1 298 $datadir='/'.$prefix.scorm_randstring();
4a9df373 299 } while (file_exists($strPath.$datadir));
300 mkdir($strPath.$datadir, $CFG->directorypermissions);
e23ef951 301 @chmod($strPath.$datadir, $CFG->directorypermissions); // Just in case mkdir didn't do it
4a9df373 302 return $strPath.$datadir;
303 } else {
304 return false;
305 }
98ca59f6 306}
307
1a12b1f1 308if ($CFG->scorm_validate == 'domxml') {
309 require_once('validatordomxml.php');
310}
311
98ca59f6 312function scorm_validate($manifest)
313{
1a12b1f1 314 global $CFG;
315
316 global $item_idref_array;
317 global $idres_array;
318 global $def_org_array;
319 global $id_org_array;
320
98ca59f6 321 if (is_file ($manifest)) {
1a12b1f1 322 if (file_exists($manifest)) {
323 if ($CFG->scorm_validate == 'domxml') {
324 $manifest_string = file_get_contents($manifest);
325
326 /* Elimino i caratteri speciali di spaziatura e ritorno a capo dal file xml */
327
328 $spec = array('\n', '\r', '\t', '\0', '\x0B');
329 $content = str_replace($spec, '', $manifest_string);
330
331 if ($xmldoc = domxml_open_mem($content)) {
332 $root = $xmldoc->document_element();
333 if (!testRoot($root)) {
334 return 'syntax';
335 }
336 if (testNode($root)) {
337 // Nel corpo di questo if si controllano le corrispondenze fra gli attributi
338 // Nello Standard SCORM ad ogni attributo idRef di <item> deve corrispondere
339 // un attributo ID di <resource>
340 // Gli array degli attributi sono stati dichiarati globali in validator.php
341 // pertanto possono essere utilizzati direttamente all'interno di main.php
342
343 foreach($item_idref_array as $elem_it) {
344 if (array_search($elem_it, $idres_array) === false) {
345 return 'mismatch';
346 }
347 }
348
349 foreach($def_org_array as $elem_def) {
350 if (array_search($elem_it, $id_org_array) === false) {
351 return 'mismatch';
352 }
353 }
354
355 } else {
356 return 'badmanifest';
357 }
358 }
359 return 'regular';
360 } else {
361 return 'found';
362 }
363 }
98ca59f6 364 } else {
1a12b1f1 365 return 'nomanifest';
98ca59f6 366 }
367}
368
369function scorm_delete_files($directory)
370{
371 if (is_dir($directory))
372 {
373 $handle=opendir($directory);
374 while (($file = readdir($handle)) != '')
375 {
1a12b1f1 376 if ($file != '.' && $file != '..')
377 {
378 if (!is_dir($directory.'/'.$file)) {
379 //chmod($directory.'/'.$file,0777);
380 unlink($directory.'/'.$file);
381 } else {
382 scorm_delete_files($directory.'/'.$file);
383 }
384 }
98ca59f6 385 }
386 rmdir($directory);
387 }
388}
389
390function scorm_startElement($parser, $name, $attrs) {
1a12b1f1 391 global $scoes,$i,$resources,$parent,$level,$organization,$manifest,$defaultorg;
392 if ($name == 'ITEM') {
4a9df373 393 $i++;
1a12b1f1 394 $scoes[$i]['manifest'] = $manifest;
395 $scoes[$i]['organization'] = $organization;
396 $scoes[$i]['identifier'] = $attrs['IDENTIFIER'];
397 if (empty($attrs['IDENTIFIERREF']))
398 $attrs['IDENTIFIERREF'] = '';
399 $scoes[$i]['identifierref'] = $attrs['IDENTIFIERREF'];
400 if (empty($attrs['ISVISIBLE']))
401 $attrs['ISVISIBLE'] = '';
402 $scoes[$i]['isvisible'] = $attrs['ISVISIBLE'];
403 $scoes[$i]['parent'] = $parent[$level];
4a9df373 404 $level++;
1a12b1f1 405 $parent[$level] = $attrs['IDENTIFIER'];
406 }
407 if ($name == 'RESOURCE') {
408 if (!isset($attrs['HREF'])) {
409 $attrs['HREF'] = '';
410 }
411 $resources[$attrs['IDENTIFIER']]['href']=$attrs['HREF'];
412 $resources[$attrs['IDENTIFIER']]['type']=$attrs['ADLCP:SCORMTYPE'];
413 }
414 if ($name == 'ORGANIZATION') {
415 $organization = $attrs['IDENTIFIER'];
416 }
417 if ($name == 'MANIFEST') {
418 $manifest = $attrs['IDENTIFIER'];
98ca59f6 419 }
1a12b1f1 420 if ($name == 'ORGANIZATIONS') {
421 $defaultorg = $attrs['DEFAULT'];
98ca59f6 422 }
423}
424
425function scorm_endElement($parser, $name) {
1a12b1f1 426 global $scoes,$i,$level,$datacontent,$navigation;
427 if ($name == 'ITEM') {
98ca59f6 428 $level--;
429 }
1a12b1f1 430 if ($name == 'TITLE' && $level>0)
431 $scoes[$i]['title'] = $datacontent;
432 if ($name == 'ADLCP:HIDERTSUI')
433 $scoes[$i][$datacontent] = 1;
434 if ($name == 'ADLCP:DATAFROMLMS')
435 $scoes[$i]['datafromlms'] = $datacontent;
98ca59f6 436}
437
438function scorm_characterData($parser, $data) {
439 global $datacontent;
440 $datacontent = $data;
441}
442
443function scorm_parse($basedir,$file,$scorm_id) {
1a12b1f1 444 global $scoes,$i,$resources,$parent,$level,$defaultorg;
445 $datacontent = '';
446 $scoes[][] = '';
447 $resources[] = '';
448 $organization = '';
449 $defaultorg = '';
98ca59f6 450 $i = 0;
451 $level = 0;
1a12b1f1 452 $parent[$level] = '/';
98ca59f6 453
454 $xml_parser = xml_parser_create();
455 // use case-folding so we are sure to find the tag in $map_array
456 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
1a12b1f1 457 xml_set_element_handler($xml_parser, 'scorm_startElement', 'scorm_endElement');
458 xml_set_character_data_handler($xml_parser, 'scorm_characterData');
459 if (!($fp = fopen($basedir.$file, 'r'))) {
460 die('could not open XML input');
98ca59f6 461 }
462
463 while ($data = fread($fp, 4096)) {
4a9df373 464 if (!xml_parse($xml_parser, $data, feof($fp))) {
1a12b1f1 465 die(sprintf('XML error: %s at line %d',
98ca59f6 466 xml_error_string(xml_get_error_code($xml_parser)),
467 xml_get_current_line_number($xml_parser)));
468 }
469 }
470 xml_parser_free($xml_parser);
471 $launch = 0;
472
473 $sco->scorm = $scorm_id;
1a12b1f1 474 delete_records('scorm_scoes','scorm',$scorm_id);
475 delete_records('scorm_sco_users','scormid',$scorm_id);
434983ca 476
1a12b1f1 477 if (isset($scoes[1])) {
478 for ($j=1; $j<=$i; $j++) {
479 $sco->identifier = $scoes[$j]['identifier'];
480 $sco->parent = $scoes[$j]['parent'];
481 $sco->title = $scoes[$j]['title'];
482 $sco->organization = $scoes[$j]['organization'];
483 if (!isset($scoes[$j]['datafromlms'])) {
484 $scoes[$j]['datafromlms'] = '';
485 }
486 $sco->datafromlms = $scoes[$j]['datafromlms'];
434983ca 487
1a12b1f1 488 if (!isset($resources[($scoes[$j]['identifierref'])]['href'])) {
489 $resources[($scoes[$j]['identifierref'])]['href'] = '';
490 }
491 $sco->launch = $resources[($scoes[$j]['identifierref'])]['href'];
434983ca 492
1a12b1f1 493 if (!isset($resources[($scoes[$j]['identifierref'])]['type'])) {
494 $resources[($scoes[$j]['identifierref'])]['type'] = '';
495 }
496 $sco->type = $resources[($scoes[$j]['identifierref'])]['type'];
434983ca 497
1a12b1f1 498 if (!isset($scoes[$j]['previous'])) {
499 $scoes[$j]['previous'] = 0;
500 }
501 $sco->previous = $scoes[$j]['previous'];
434983ca 502
1a12b1f1 503 if (!isset($scoes[$j]['continue'])) {
504 $scoes[$j]['continue'] = 0;
505 }
506 $sco->next = $scoes[$j]['continue'];
434983ca 507
1a12b1f1 508 if (scorm_remove_spaces($scoes[$j]['isvisible']) != 'false') {
509 $id = insert_record('scorm_scoes',$sco);
510 }
511 if (($launch==0) && (isset($sco->launch)) && ($defaultorg==$sco->organization)) {
512 $launch = $id;
513 }
514 }
515 } else {
516 foreach ($resources as $label => $resource) {
517 if ((isset($resource['type'])) && ($resource['type'] == 'sco')) {
518 $sco->identifier = $label;
519 $sco->title = $label;
520 $sco->parent = '/';
521 $sco->launch = $resource['href'];
522 $sco->type = $resource['type'];
523 $id = insert_record('scorm_scoes',$sco);
524
525 if ($launch == 0) {
526 $launch = $id;
527 }
528 }
529 }
98ca59f6 530 }
531 return $launch;
532}
533
534function scorm_get_scoes_records($sco_user) {
535/// Gets all info required to display the table of scorm results
536/// for report.php
537 global $CFG;
538
539 return get_records_sql("SELECT su.*, u.firstname, u.lastname, u.picture
540 FROM {$CFG->prefix}scorm_sco_users su,
541 {$CFG->prefix}user u
542 WHERE su.scormid = '$sco_user->scormid'
543 AND su.userid = u.id
544 AND su.userid = $sco_user->userid
545 ORDER BY scoid");
546}
547
548function scorm_remove_spaces($sourcestr) {
549// Remove blank space from a string
1a12b1f1 550 $newstr='';
4a9df373 551 for( $i=0; $i<strlen($sourcestr); $i++) {
552 if ($sourcestr[$i]!=' ')
553 $newstr .=$sourcestr[$i];
554 }
555 return $newstr;
98ca59f6 556}
557
558function scorm_string_round($stringa) {
559// Crop a string to $len character and set an anchor title to the full string
560 $len=11;
561 if ( strlen($stringa)>$len ) {
1a12b1f1 562 return "<A name=\"\" title=\"$stringa\">".substr($stringa,0,$len-4).'...'.substr($stringa,strlen($stringa)-1,1).'</A>';
98ca59f6 563 } else
4a9df373 564 return $stringa;
98ca59f6 565}
566
567function scorm_external_link($link) {
568// check if a link is external
569 $result = false;
570 $link = strtolower($link);
1a12b1f1 571 if (substr($link,0,7) == 'http://')
4a9df373 572 $result = true;
1a12b1f1 573 else if (substr($link,0,8) == 'https://')
4a9df373 574 $result = true;
1a12b1f1 575 else if (substr($link,0,4) == 'www.')
4a9df373 576 $result = true;
1a12b1f1 577 /*else if (substr($link,0,7) == 'rstp://')
4a9df373 578 $result = true;
1a12b1f1 579 else if (substr($link,0,6) == 'rtp://')
4a9df373 580 $result = true;
1a12b1f1 581 else if (substr($link,0,6) == 'ftp://')
4a9df373 582 $result = true;
1a12b1f1 583 else if (substr($link,0,9) == 'gopher://')
4a9df373 584 $result = true; */
98ca59f6 585 return $result;
586}
587?>