XHTML Striiict:MDL-17705
[moodle.git] / mod / scorm / datamodels / aicclib.php
1 <?php // $Id$
2 function scorm_add_time($a, $b) {
3     $aes = explode(':',$a);
4     $bes = explode(':',$b);
5     $aseconds = explode('.',$aes[2]);
6     $bseconds = explode('.',$bes[2]);
7     $change = 0;
9     $acents = 0;  //Cents
10     if (count($aseconds) > 1) {
11         $acents = $aseconds[1];
12     }
13     $bcents = 0;
14     if (count($bseconds) > 1) {
15         $bcents = $bseconds[1];
16     }
17     $cents = $acents + $bcents;
18     $change = floor($cents / 100);
19     $cents = $cents - ($change * 100);
20     if (floor($cents) < 10) {
21         $cents = '0'. $cents;
22     }
24     $secs = $aseconds[0] + $bseconds[0] + $change;  //Seconds
25     $change = floor($secs / 60);
26     $secs = $secs - ($change * 60);
27     if (floor($secs) < 10) {
28         $secs = '0'. $secs;
29     }
31     $mins = $aes[1] + $bes[1] + $change;   //Minutes
32     $change = floor($mins / 60);
33     $mins = $mins - ($change * 60);
34     if ($mins < 10) {
35         $mins = '0' .  $mins;
36     }
38     $hours = $aes[0] + $bes[0] + $change;  //Hours
39     if ($hours < 10) {
40         $hours = '0' . $hours;
41     }
43     if ($cents != '0') {
44         return $hours . ":" . $mins . ":" . $secs . '.' . $cents;
45     } else {
46         return $hours . ":" . $mins . ":" . $secs;
47     }
48 }
50 /**
51 * Take the header row of an AICC definition file
52 * and returns sequence of columns and a pointer to
53 * the sco identifier column.
54 *
55 * @param string $row AICC header row
56 * @param string $mastername AICC sco identifier column
57 * @return mixed
58 */
59 function scorm_get_aicc_columns($row,$mastername='system_id') {
60     $tok = strtok(strtolower($row),"\",\n\r");
61     $result->columns = array();
62     $i=0;
63     while ($tok) {
64         if ($tok !='') {
65             $result->columns[] = $tok;
66             if ($tok == $mastername) {
67                 $result->mastercol = $i;
68             }
69             $i++;
70         }
71         $tok = strtok("\",\n\r");
72     }
73     return $result;
74 }
76 /**
77 * Given a colums array return a string containing the regular
78 * expression to match the columns in a text row.
79 *
80 * @param array $column The header columns
81 * @param string $remodule The regular expression module for a single column
82 * @return string
83 */
84 function scorm_forge_cols_regexp($columns,$remodule='(".*")?,') {
85     $regexp = '/^';
86     foreach ($columns as $column) {
87         $regexp .= $remodule;
88     }
89     $regexp = substr($regexp,0,-1) . '/';
90     return $regexp;
91 }
94 function scorm_parse_aicc($scorm) {
95     global $DB;
97     if (!isset($scorm->cmid)) {
98         $cm = get_coursemodule_from_instance('scorm', $scorm->id);
99         $scorm->cmid = $cm->id;
100     }
101     $context = get_context_instance(COURSE_MODULE, $scorm->cmid);
103     $fs = get_file_storage();
105     $files = $fs->get_area_files($context->id, 'scorm_content', 0, '', false);
108     $version = 'AICC';
109     $ids = array();
110     $courses = array();
111     $extaiccfiles = array('crs','des','au','cst','ort','pre','cmp');
113     foreach ($files as $file) {
114         $filename = $file->get_filename();
115         $ext = substr($filename,strrpos($filename,'.'));
116         $extension = strtolower(substr($ext,1));
117         if (in_array($extension,$extaiccfiles)) {
118             $id = strtolower(basename($filename,$ext));
119             $ids[$id]->$extension = $file;
120         }
121     }
123     foreach ($ids as $courseid => $id) {
124         if (isset($id->crs)) {
125             $rows = $id->crs->get_content();
126             foreach ($rows as $row) {
127                 if (preg_match("/^(.+)=(.+)$/",$row,$matches)) {
128                     switch (strtolower(trim($matches[1]))) {
129                         case 'course_id':
130                             $courses[$courseid]->id = trim($matches[2]);
131                         break;
132                         case 'course_title':
133                             $courses[$courseid]->title = trim($matches[2]);
134                         break;
135                         case 'version':
136                             $courses[$courseid]->version = 'AICC_'.trim($matches[2]);
137                         break;
138                     }
139                 }
140             }
141         }
142         if (isset($id->des)) {
143             $rows = $id->des->get_content();
144             $columns = scorm_get_aicc_columns($rows[0]);
145             $regexp = scorm_forge_cols_regexp($columns->columns);
146             for ($i=1;$i<count($rows);$i++) {
147                 if (preg_match($regexp,$rows[$i],$matches)) {
148                     for ($j=0;$j<count($columns->columns);$j++) {
149                         $column = $columns->columns[$j];
150                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
151                     }
152                 }
153             }
154         }
155         if (isset($id->au)) {
156             $rows = $id->au->get_content();
157             $columns = scorm_get_aicc_columns($rows[0]);
158             $regexp = scorm_forge_cols_regexp($columns->columns);
159             for ($i=1;$i<count($rows);$i++) {
160                 if (preg_match($regexp,$rows[$i],$matches)) {
161                     for ($j=0;$j<count($columns->columns);$j++) {
162                         $column = $columns->columns[$j];
163                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
164                     }
165                 }
166             }
167         }
168         if (isset($id->cst)) {
169             $rows = $id->cst->get_content();
170             $columns = scorm_get_aicc_columns($rows[0],'block');
171             $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,');
172             for ($i=1;$i<count($rows);$i++) {
173                 if (preg_match($regexp,$rows[$i],$matches)) {
174                     for ($j=0;$j<count($columns->columns);$j++) {
175                         if ($j != $columns->mastercol) {
176                             $courses[$courseid]->elements[substr(trim($matches[$j+1]),1,-1)]->parent = substr(trim($matches[$columns->mastercol+1]),1,-1);
177                         }
178                     }
179                 }
180             }
181         }
182         if (isset($id->ort)) {
183             $rows = $id->ort->get_content();
184             $columns = scorm_get_aicc_columns($rows[0],'course_element');
185             $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,');
186             for ($i=1;$i<count($rows);$i++) {
187                 if (preg_match($regexp,$rows[$i],$matches)) {
188                     for ($j=0;$j<count($matches)-1;$j++) {
189                         if ($j != $columns->mastercol) {
190                             $courses[$courseid]->elements[substr(trim($matches[$j+1]),1,-1)]->parent = substr(trim($matches[$columns->mastercol+1]),1,-1);
191                         }
192                     }
193                 }
194             }
195         }
196         if (isset($id->pre)) {
197             $rows = $id->pre->get_content();
198             $columns = scorm_get_aicc_columns($rows[0],'structure_element');
199             $regexp = scorm_forge_cols_regexp($columns->columns,'(.+),');
200             for ($i=1;$i<count($rows);$i++) {
201                 if (preg_match($regexp,$rows[$i],$matches)) {
202                     $courses[$courseid]->elements[$columns->mastercol+1]->prerequisites = substr(trim($matches[1-$columns->mastercol+1]),1,-1);
203                 }
204             }
205         }
206         if (isset($id->cmp)) {
207             $rows = $id->cmp->get_content();
208         }
209     }
210     //print_r($courses);
212     $oldscoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id));
213     
214     $launch = 0;
215     if (isset($courses)) {
216         foreach ($courses as $course) {
217             $sco = new object();
218             $sco->identifier = $course->id;
219             $sco->scorm = $scorm->id;
220             $sco->organization = '';
221             $sco->title = $course->title;
222             $sco->parent = '/';
223             $sco->launch = '';
224             $sco->scormtype = '';
226             //print_r($sco);
227             if ($ss = $DB->get_record('scorm_scoes', array('scorm'=>$scorm->id,'identifier'=>$sco->identifier))) {
228                 $id = $ss->id;
229                 $DB->update_record('scorm_scoes',$sco);
230                 unset($oldscoes[$id]);
231             } else {
232                 $id = $DB->insert_record('scorm_scoes',$sco);
233             }
235             if ($launch == 0) {
236                 $launch = $id;
237             }
238             if (isset($course->elements)) {
239                 foreach($course->elements as $element) {
240                     unset($sco);
241                     $sco->identifier = $element->system_id;
242                     $sco->scorm = $scorm->id;
243                     $sco->organization = $course->id;
244                     $sco->title = $element->title;
245                  
246                     if (!isset($element->parent) || strtolower($element->parent) == 'root') {
247                         $sco->parent = '/';
248                     } else {
249                         $sco->parent = $element->parent;
250                     }
251                     if (isset($element->file_name)) {
252                         $sco->launch = $element->file_name;
253                         $sco->scormtype = 'sco';
254                         $sco->previous = 0;
255                         $sco->next = 0;
256                         $id = null;
257                         if ($oldscoid = scorm_array_search('identifier',$sco->identifier,$oldscoes)) {
258                             $sco->id = $oldscoid;
259                             $id = $DB->update_record('scorm_scoes',$sco);
260                             $DB->delete_records('scorm_scoes_data', array('scoid'=>$oldscoid));
261                             unset($oldscoes[$oldscoid]);
262                         } else {
263                             $id = $DB->insert_record('scorm_scoes',$sco);
264                         }
265                         if (!empty($id)) {
266                             unset($scodata);
267                             $scodata->scoid = $id;
268                             if (isset($element->web_launch)) {
269                                 $scodata->name = 'parameters';
270                                 $scodata->value = $element->web_launch;
271                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
272                             }
273                             if (isset($element->prerequisites)) {
274                                 $scodata->name = 'prerequisites';
275                                 $scodata->value = $element->prerequisites;
276                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
277                             }
278                             if (isset($element->max_time_allowed)) {
279                                 $scodata->name = 'max_time_allowed';
280                                 $scodata->value = $element->max_time_allowed;
281                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
282                             }
283                             if (isset($element->time_limit_action)) {
284                                 $scodata->name = 'time_limit_action';
285                                 $scodata->value = $element->time_limit_action;
286                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
287                             }
288                             if (isset($element->mastery_score)) {
289                                 $scodata->name = 'mastery_score';
290                                 $scodata->value = $element->mastery_score;
291                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
292                             }
293                             if (isset($element->core_vendor)) {
294                                 $scodata->name = 'datafromlms';
295                                 $scodata->value = eregi_replace('<cr>', "\r\n", $element->core_vendor);
296                                 $dataid = $DB->insert_record('scorm_scoes_data',$scodata);
297                             }
298                         }
299                         if ($launch==0) {
300                             $launch = $id;
301                         }
302                     }
303                 }
304             }
305         }
306     }
307     if (!empty($oldscoes)) {
308         foreach($oldscoes as $oldsco) {
309             $DB->delete_records('scorm_scoes', array('id'=>$oldsco->id));
310             $DB->delete_records('scorm_scoes_track', array('scoid'=>$oldsco->id));
311         }
312     }
314     $scorm->version = 'AICC';
316     $scorm->launch = $launch;
318     return true;
321 function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) {
322     global $CFG, $DB;
323     
324     $strexpand = get_string('expcoll','scorm');
325     $modestr = '';
326     if ($mode == 'browse') {
327         $modestr = '&amp;mode='.$mode;
328     } 
329     $scormpixdir = $CFG->modpixpath.'/scorm/pix';
330     
331     $result = new stdClass();
332     $result->toc = "<ul id='s0' class='$liststyle'>\n";
333     $tocmenus = array();
334     $result->prerequisites = true;
335     $incomplete = false;
336     
337     //
338     // Get the current organization infos
339     //
340     if (!empty($currentorg)) {
341         if (($organizationtitle = $DB->get_field('scorm_scoes','title', array('scorm'=>$scorm->id,'identifier'=>$currentorg))) != '') {
342             $result->toc .= "\t<li>$organizationtitle</li>\n";
343             $tocmenus[] = $organizationtitle;
344         }
345     }
346     //
347     // If not specified retrieve the last attempt number
348     //
349     if (empty($attempt)) {
350         $attempt = scorm_get_last_attempt($scorm->id, $user->id);
351     }
352     $result->attemptleft = $scorm->maxattempt - $attempt;
353     if ($scoes = scorm_get_scoes($scorm->id, $currentorg)){
354         //
355         // Retrieve user tracking data for each learning object
356         // 
357         $usertracks = array();
358         foreach ($scoes as $sco) {
359             if (!empty($sco->launch)) {
360                 if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) {
361                     if ($usertrack->status == '') {
362                         $usertrack->status = 'notattempted';
363                     }
364                     $usertracks[$sco->identifier] = $usertrack;
365                 }
366             }
367         }
369         $level=0;
370         $sublist=1;
371         $previd = 0;
372         $nextid = 0;
373         $findnext = false;
374         $parents[$level]='/';
375         
376         foreach ($scoes as $pos => $sco) {
377             $isvisible = false;
378             $sco->title = $sco->title;
379             if (!isset($sco->isvisible) || (isset($sco->isvisible) && ($sco->isvisible == 'true'))) {
380                 $isvisible = true;
381             }
382             if ($parents[$level]!=$sco->parent) {
383                 if ($newlevel = array_search($sco->parent,$parents)) {
384                     for ($i=0; $i<($level-$newlevel); $i++) {
385                         $result->toc .= "\t\t</ul></li>\n";
386                     }
387                     $level = $newlevel;
388                 } else {
389                     $i = $level;
390                     $closelist = '';
391                     while (($i > 0) && ($parents[$level] != $sco->parent)) {
392                         $closelist .= "\t\t</ul></li>\n";
393                         $i--;
394                     }
395                     if (($i == 0) && ($sco->parent != $currentorg)) {
396                         $style = '';
397                         if (isset($_COOKIE['hide:SCORMitem'.$sco->id])) {
398                             $style = ' style="display: none;"';
399                         }
400                         $result->toc .= "\t\t<li><ul id='s$sublist' class='$liststyle'$style>\n";
401                         $level++;
402                     } else {
403                         $result->toc .= $closelist;
404                         $level = $i;
405                     }
406                     $parents[$level]=$sco->parent;
407                 }
408             }
409             if ($isvisible) {
410                 $result->toc .= "\t\t<li>";
411             }
412             if (isset($scoes[$pos+1])) {
413                 $nextsco = $scoes[$pos+1];
414             } else {
415                 $nextsco = false;
416             }
417             $nextisvisible = false;
418             if (!isset($nextsco->isvisible) || (isset($nextsco->isvisible) && ($nextsco->isvisible == 'true'))) {
419                 $nextisvisible = true;
420             }
421             if ($nextisvisible && ($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) {
422                 $sublist++;
423                 $icon = 'minus';
424                 if (isset($_COOKIE['hide:SCORMitem'.$nextsco->id])) {
425                     $icon = 'plus';
426                 }
427                 $result->toc .= '<a href="javascript:expandCollide(\'img'.$sublist.'\',\'s'.$sublist.'\','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/'.$icon.'.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>';
428             } else if ($isvisible) {
429                 $result->toc .= '<img src="'.$scormpixdir.'/spacer.gif" alt="spacer" />';
430             }
431             if (empty($sco->title)) {
432                 $sco->title = $sco->identifier;
433             }
434             if (!empty($sco->launch)) {
435                 if ($isvisible) {
436                     $startbold = '';
437                     $endbold = '';
438                     $score = '';
439                     if (empty($scoid) && ($mode != 'normal')) {
440                         $scoid = $sco->id;
441                     }
442                     if (isset($usertracks[$sco->identifier])) {
443                         $usertrack = $usertracks[$sco->identifier];
444                         $strstatus = get_string($usertrack->status,'scorm');
445                         if ($sco->scormtype == 'sco') {
446                             $statusicon = '<img src="'.$scormpixdir.'/'.$usertrack->status.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />';
447                         } else {
448                             $statusicon = '<img src="'.$scormpixdir.'/assetc.gif" alt="'.get_string('assetlaunched','scorm').'" title="'.get_string('assetlaunched','scorm').'" />';
449                         }
450                     
451                         if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) {
452                             $incomplete = true;
453                             if ($play && empty($scoid)) {
454                                 $scoid = $sco->id;
455                             }
456                         }
457                         if ($usertrack->score_raw != '') {
458                             $score = '('.get_string('score','scorm').':&nbsp;'.$usertrack->score_raw.')';
459                         }
460                         $strsuspended = get_string('suspended','scorm');
461                         if (isset($usertrack->{'cmi.core.exit'}) && ($usertrack->{'cmi.core.exit'} == 'suspend')) {
462                             $statusicon = '<img src="'.$scormpixdir.'/suspend.gif" alt="'.$strstatus.' - '.$strsuspended.'" title="'.$strstatus.' - '.$strsuspended.'" />';
463                         }
464                     } else {
465                         if ($play && empty($scoid)) {
466                             $scoid = $sco->id;
467                         }
468                         $incomplete = true;
469                         if ($sco->scormtype == 'sco') {
470                             $statusicon = '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />';
471                         } else {
472                             $statusicon = '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />';
473                         }
474                     }
475                     if ($sco->id == $scoid) {
476                         $startbold = '<b>';
477                         $endbold = '</b>';
478                         $findnext = true;
479                         $shownext = isset($sco->next) ? $sco->next : 0;
480                         $showprev = isset($sco->previous) ? $sco->previous : 0;
481                     }
482                 
483                     if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) {
484                         if (!empty($sco->launch)) {
485                             $previd = $sco->id;
486                         }
487                     }
488                     if (empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks)) {
489                         if ($sco->id == $scoid) {
490                             $result->prerequisites = true;
491                         }
492                         $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&amp;currentorg='.$currentorg.$modestr.'&amp;scoid='.$sco->id;
493                         $result->toc .= $statusicon.'&nbsp;'.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n";
494                         $tocmenus[$sco->id] = scorm_repeater('&minus;',$level) . '&gt;' . format_string($sco->title);
495                     } else {
496                         if ($sco->id == $scoid) {
497                             $result->prerequisites = false;
498                         }
499                         $result->toc .= $statusicon.'&nbsp;'.format_string($sco->title)."</li>\n";
500                     }
501                 }
502             } else {
503                 $result->toc .= '&nbsp;'.format_string($sco->title)."</li>\n";
504             }
505             if (($nextsco !== false) && ($nextid == 0) && ($findnext)) {
506                 if (!empty($nextsco->launch)) {
507                     $nextid = $nextsco->id;
508                 }
509             }
510         }
511         for ($i=0;$i<$level;$i++) {
512             $result->toc .= "\t\t</ul></li>\n";
513         }
514         
515         if ($play) {
516             $sco = scorm_get_sco($scoid);
517             $sco->previd = $previd;
518             $sco->nextid = $nextid;
519             $result->sco = $sco;
520             $result->incomplete = $incomplete;
521         } else {
522             $result->incomplete = $incomplete;
523         }
524     }
525     $result->toc .= "\t</ul>\n";
526     if ($scorm->hidetoc == 0) {
527         $result->toc .= '
528           <script type="text/javascript">
529           //<![CDATA[
530               function expandCollide(which,list,item) {
531                   var el = document.ids ? document.ids[list] : document.getElementById ? document.getElementById(list) : document.all[list];
532                   which = which.substring(0,(which.length));
533                   var el2 = document.ids ? document.ids[which] : document.getElementById ? document.getElementById(which) : document.all[which];
534                   if (el.style.display != "none") {
535                       el2.src = "'.$scormpixdir.'/plus.gif";
536                       el.style.display=\'none\';
537                       new cookie("hide:SCORMitem" + item, 1, 356, "/").set();
538                   } else {
539                       el2.src = "'.$scormpixdir.'/minus.gif";
540                       el.style.display=\'block\';
541                       new cookie("hide:SCORMitem" + item, 1, -1, "/").set();
542                   }
543               }
544           //]]>
545           </script>'."\n";
546     }
547     
548     $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&amp;currentorg='.$currentorg.$modestr.'&amp;scoid=';
549     $result->tocmenu = popup_form($url,$tocmenus, "tocmenu", $sco->id, '', '', '', true);
551     return $result;
554 ?>