e84bfe85ddae7bdbaf8bb4e3bc37fde99d5c9422
[moodle.git] / mod / scorm / datamodels / aicclib.php
1 <?php
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 }
93 function scorm_parse_aicc($pkgdir,$scormid) {
94     $version = 'AICC';
95     $ids = array();
96     $courses = array();
97     $extaiccfiles = array('crs','des','au','cst','ort','pre','cmp');
98     if ($handle = opendir($pkgdir)) {
99         while (($file = readdir($handle)) !== false) {
100             if ($file[0] != '.') {
101                 $ext = substr($file,strrpos($file,'.'));
102                 $extension = strtolower(substr($ext,1));
103                 if (in_array($extension,$extaiccfiles)) {
104                     $id = strtolower(basename($file,$ext));
105                     $ids[$id]->$extension = $file;
106                 }
107             }
108         }
109         closedir($handle);
110     }
111     foreach ($ids as $courseid => $id) {
112         if (isset($id->crs)) {
113             if (is_file($pkgdir.'/'.$id->crs)) {
114                 $rows = file($pkgdir.'/'.$id->crs);
115                 foreach ($rows as $row) {
116                     if (preg_match("/^(.+)=(.+)$/",$row,$matches)) {
117                         switch (strtolower(trim($matches[1]))) {
118                             case 'course_id':
119                                 $courses[$courseid]->id = trim($matches[2]);
120                             break;
121                             case 'course_title':
122                                 $courses[$courseid]->title = trim($matches[2]);
123                             break;
124                             case 'version':
125                                 $courses[$courseid]->version = 'AICC_'.trim($matches[2]);
126                             break;
127                         }
128                     }
129                 }
130             }
131         }
132         if (isset($id->des)) {
133             $rows = file($pkgdir.'/'.$id->des);
134             $columns = scorm_get_aicc_columns($rows[0]);
135             $regexp = scorm_forge_cols_regexp($columns->columns);
136             for ($i=1;$i<count($rows);$i++) {
137                 if (preg_match($regexp,$rows[$i],$matches)) {
138                     for ($j=0;$j<count($columns->columns);$j++) {
139                         $column = $columns->columns[$j];
140                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
141                     }
142                 }
143             }
144         }
145         if (isset($id->au)) {
146             $rows = file($pkgdir.'/'.$id->au);
147             $columns = scorm_get_aicc_columns($rows[0]);
148             $regexp = scorm_forge_cols_regexp($columns->columns);
149             for ($i=1;$i<count($rows);$i++) {
150                 if (preg_match($regexp,$rows[$i],$matches)) {
151                     for ($j=0;$j<count($columns->columns);$j++) {
152                         $column = $columns->columns[$j];
153                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1);
154                     }
155                 }
156             }
157         }
158         if (isset($id->cst)) {
159             $rows = file($pkgdir.'/'.$id->cst);
160             $columns = scorm_get_aicc_columns($rows[0],'block');
161             $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,');
162             for ($i=1;$i<count($rows);$i++) {
163                 if (preg_match($regexp,$rows[$i],$matches)) {
164                     for ($j=0;$j<count($columns->columns);$j++) {
165                         if ($j != $columns->mastercol) {
166                             $courses[$courseid]->elements[substr(trim($matches[$j+1]),1,-1)]->parent = substr(trim($matches[$columns->mastercol+1]),1,-1);
167                         }
168                     }
169                 }
170             }
171         }
172         if (isset($id->ort)) {
173             $rows = file($pkgdir.'/'.$id->ort);
174         }
175         if (isset($id->pre)) {
176             $rows = file($pkgdir.'/'.$id->pre);
177             $columns = scorm_get_aicc_columns($rows[0],'structure_element');
178             $regexp = scorm_forge_cols_regexp($columns->columns,'(.+),');
179             for ($i=1;$i<count($rows);$i++) {
180                 if (preg_match($regexp,$rows[$i],$matches)) {
181                     $courses[$courseid]->elements[$columns->mastercol+1]->prerequisites = substr(trim($matches[1-$columns->mastercol+1]),1,-1);
182                 }
183             }
184         }
185         if (isset($id->cmp)) {
186             $rows = file($pkgdir.'/'.$id->cmp);
187         }
188     }
189     //print_r($courses);
191     $oldscoes = get_records('scorm_scoes','scorm',$scormid);
192     
193     $launch = 0;
194     if (isset($courses)) {
195         foreach ($courses as $course) {
196             unset($sco);
197             $sco->identifier = $course->id;
198             $sco->scorm = $scormid;
199             $sco->organization = '';
200             $sco->title = $course->title;
201             $sco->parent = '/';
202             $sco->launch = '';
203             $sco->scormtype = '';
205             //print_r($sco);
206             if (get_record('scorm_scoes','scorm',$scormid,'identifier',$sco->identifier)) {
207                 $id = update_record('scorm_scoes',$sco);
208                 unset($oldscoes[$id]);
209             } else {
210                 $id = insert_record('scorm_scoes',$sco);
211             }
213             if ($launch == 0) {
214                 $launch = $id;
215             }
216             if (isset($course->elements)) {
217                 foreach($course->elements as $element) {
218                     unset($sco);
219                     $sco->identifier = $element->system_id;
220                     $sco->scorm = $scormid;
221                     $sco->organization = $course->id;
222                     $sco->title = $element->title;
223                     if (strtolower($element->parent) == 'root') {
224                         $sco->parent = '/';
225                     } else {
226                         $sco->parent = $element->parent;
227                     }
228                     if (isset($element->file_name)) {
229                         $sco->launch = $element->file_name;
230                         $sco->scormtype = 'sco';
231                     } else {
232                         $element->file_name = '';
233                         $sco->scormtype = '';
234                     }
235                     if (!isset($element->prerequisites)) {
236                         $element->prerequisites = '';
237                     }
238                     $sco->prerequisites = $element->prerequisites;
239                     if (!isset($element->max_time_allowed)) {
240                         $element->max_time_allowed = '';
241                     }
242                     $sco->maxtimeallowed = $element->max_time_allowed;
243                     if (!isset($element->time_limit_action)) {
244                         $element->time_limit_action = '';
245                     }
246                     $sco->timelimitaction = $element->time_limit_action;
247                     if (!isset($element->mastery_score)) {
248                         $element->mastery_score = '';
249                     }
250                     $sco->masteryscore = $element->mastery_score;
251                     $sco->previous = 0;
252                     $sco->next = 0;
253                     if ($oldscoid = scorm_array_search('identifier',$sco->identifier,$oldscoes)) {
254                         $sco->id = $oldscoid;
255                         $id = update_record('scorm_scoes',$sco);
256                         unset($oldscoes[$oldscoid]);
257                     } else {
258                         $id = insert_record('scorm_scoes',$sco);
259                     }
260                     if ($launch==0) {
261                         $launch = $id;
262                     }
263                 }
264             }
265         }
266     }
267     if (!empty($oldscoes)) {
268         foreach($oldscoes as $oldsco) {
269             delete_records('scorm_scoes','id',$oldsco->id);
270             delete_records('scorm_scoes_track','scoid',$oldsco->id);
271         }
272     }
273     set_field('scorm','version','AICC','id',$scormid);
274     return $launch;
277 function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) {
278     global $CFG;
279     
280     // Added by Pham Minh Duc
281     $suspendscoid = scorm_get_suspendscoid($scorm->id,$user->id);
282     // End add
284     $strexpand = get_string('expcoll','scorm');
285     $modestr = '';
286     if ($mode == 'browse') {
287         $modestr = '&amp;mode='.$mode;
288     } 
289     $scormpixdir = $CFG->modpixpath.'/scorm/pix';
290     
291     $result = new stdClass();
292     $result->toc = "<ul id='0' class='$liststyle'>\n";
293     $tocmenus = array();
294     $result->prerequisites = true;
295     $incomplete = false;
296     
297     //
298     // Get the current organization infos
299     //
300     $organizationsql = '';
301     if (!empty($currentorg)) {
302         if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') {
303             $result->toc .= "\t<li>$organizationtitle</li>\n";
304             $tocmenus[] = $organizationtitle;
305         }
306         $organizationsql = "AND organization='$currentorg'";
307     }
308     //
309     // If not specified retrieve the last attempt number
310     //
311     if (empty($attempt)) {
312         $attempt = scorm_get_last_attempt($scorm->id, $user->id);
313     }
314     $result->attemptleft = $scorm->maxattempt - $attempt;
315     if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){
316         //
317         // Retrieve user tracking data for each learning object
318         // 
319         $usertracks = array();
320         foreach ($scoes as $sco) {
321             if (!empty($sco->launch)) {
322                 if ($usertrack=scorm_get_tracks($sco->id,$user->id,$attempt)) {
323                     if ($usertrack->status == '') {
324                         $usertrack->status = 'notattempted';
325                     }
326                     $usertracks[$sco->identifier] = $usertrack;
327                 }
328             }
329         }
331         $level=0;
332         $sublist=1;
333         $previd = 0;
334         $nextid = 0;
335         $findnext = false;
336         $parents[$level]='/';
337         
338         foreach ($scoes as $sco) {
339             if ($parents[$level]!=$sco->parent) {
340                 if ($newlevel = array_search($sco->parent,$parents)) {
341                     for ($i=0; $i<($level-$newlevel); $i++) {
342                         $result->toc .= "\t\t</ul></li>\n";
343                     }
344                     $level = $newlevel;
345                 } else {
346                     $i = $level;
347                     $closelist = '';
348                     while (($i > 0) && ($parents[$level] != $sco->parent)) {
349                         $closelist .= "\t\t</ul></li>\n";
350                         $i--;
351                     }
352                     if (($i == 0) && ($sco->parent != $currentorg)) {
353                         $style = '';
354                         if (isset($_COOKIE['hide:SCORMitem'.$sco->id])) {
355                             $style = ' style="display: none;"';
356                         }
357                         $result->toc .= "\t\t<li><ul id='$sublist' class='$liststyle'$style>\n";
358                         $level++;
359                     } else {
360                         $result->toc .= $closelist;
361                         $level = $i;
362                     }
363                     $parents[$level]=$sco->parent;
364                 }
365             }
366             $result->toc .= "\t\t<li>";
367             $nextsco = next($scoes);
368             if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) {
369                 $sublist++;
370                 $icon = 'minus';
371                 if (isset($_COOKIE['hide:SCORMitem'.$nextsco->id])) {
372                     $icon = 'plus';
373                 }
374                 $result->toc .= '<a href="javascript:expandCollide(img'.$sublist.','.$sublist.','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/'.$icon.'.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>';
375             } else {
376                 $result->toc .= '<img src="'.$scormpixdir.'/spacer.gif" />';
377             }
378             if (empty($sco->title)) {
379                 $sco->title = $sco->identifier;
380             }
381             if (!empty($sco->launch)) {
382                 $startbold = '';
383                 $endbold = '';
384                 $score = '';
385                 if (empty($scoid) && ($mode != 'normal')) {
386                     $scoid = $sco->id;
387                 }
388                 // Modified by Pham Minh Duc
389                 if ($suspendscoid == $sco->id){
390                     $result->toc .= '<img src="'.$scormpixdir.'/suspend.gif" alt="Dang tam dung o day" title="Dang dung o day" />';                
391                 } else {
392                     if (isset($usertracks[$sco->identifier])) {
393                         $usertrack = $usertracks[$sco->identifier];
394                         $strstatus = get_string($usertrack->status,'scorm');
395                         $result->toc .= '<img src="'.$scormpixdir.'/'.$usertrack->status.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />';
396                         
397                         if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) {
398                             $incomplete = true;
399                             if ($play && empty($scoid)) {
400                                 $scoid = $sco->id;
401                             }
402                         }
403                         if ($usertrack->score_raw != '') {
404                             $score = '('.get_string('score','scorm').':&nbsp;'.$usertrack->score_raw.')';
405                         }
406                     } else {
407                         if ($play && empty($scoid)) {
408                             $scoid = $sco->id;
409                         }
410                         if ($sco->scormtype == 'sco') {
411                             $result->toc .= '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />';
412                             $incomplete = true;
413                         } else {
414                             $result->toc .= '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />';
415                         }
416                     }
417                 }
418                 // End Modify
419                 if ($sco->id == $scoid) {
420                     $startbold = '<b>';
421                     $endbold = '</b>';
422                     $findnext = true;
423                     $shownext = $sco->next;
424                     $showprev = $sco->previous;
425                 }
426                 
427                 if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) {
428                     if (!empty($sco->launch)) {
429                         $previd = $sco->id;
430                     }
431                 }
432                 if (empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks)) {
433                     if ($sco->id == $scoid) {
434                         $result->prerequisites = true;
435                     }
436                 // Modified by Pham Minh Duc
437                     if (scorm_isChoice($scorm->id,$sco->id) == 1) {
438                         $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&amp;currentorg='.$currentorg.$modestr.'&amp;scoid='.$sco->id;
439                         $result->toc .= '&nbsp;'.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n";
440                         $tocmenus[$sco->id] = scorm_repeater('&minus;',$level) . '&gt;' . format_string($sco->title);
441                     } else {
442                         $result->toc .= '&nbsp;'.$startbold.format_string($sco->title).$score.$endbold."</li>\n";
443                         $tocmenus[$sco->id] = scorm_repeater('&minus;',$level) . '&gt;' . format_string($sco->title);                    
444                     }
445                 // End modify
446                 } else {
447                     if ($sco->id == $scoid) {
448                         $result->prerequisites = false;
449                     }
450                     $result->toc .= '&nbsp;'.$sco->title."</li>\n";
451                 }
452             } else {
453                 $result->toc .= '&nbsp;'.$sco->title."</li>\n";
454             }
455             if (($nextsco !== false) && ($nextid == 0) && ($findnext)) {
456                 if (!empty($nextsco->launch)) {
457                     $nextid = $nextsco->id;
458                 }
459             }
460         }
461         for ($i=0;$i<$level;$i++) {
462             $result->toc .= "\t\t</ul></li>\n";
463         }
464         
465         if ($play) {
466             $sco = get_record('scorm_scoes','id',$scoid);
467             $sco->previd = $previd;
468             $sco->nextid = $nextid;
469             $result->sco = $sco;
470             $result->incomplete = $incomplete;
471         } else {
472             $result->incomplete = $incomplete;
473         }
474     }
475     $result->toc .= "\t</ul>\n";
476     if ($scorm->hidetoc == 0) {
477         $result->toc .= '
478           <script language="javascript" type="text/javascript">
479           <!--
480               function expandCollide(which,list,item) {
481                   var nn=document.ids?true:false
482                   var w3c=document.getElementById?true:false
483                   var beg=nn?"document.ids.":w3c?"document.getElementById(":"document.all.";
484                   var mid=w3c?").style":".style";
486                   if (eval(beg+list+mid+".display") != "none") {
487                       which.src = "'.$scormpixdir.'/plus.gif";
488                       eval(beg+list+mid+".display=\'none\';");
489                       new cookie("hide:SCORMitem" + item, 1, 356, "/").set();
490                   } else {
491                       which.src = "'.$scormpixdir.'/minus.gif";
492                       eval(beg+list+mid+".display=\'block\';");
493                       new cookie("hide:SCORMitem" + item, 1, -1, "/").set();
494                   }
495               }
496           -->
497           </script>'."\n";
498     }
499     
500     $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&amp;currentorg='.$currentorg.$modestr.'&amp;scoid=';
501     $result->tocmenu = popup_form($url,$tocmenus, "tocmenu", $sco->id, '', '', '', true);
503     return $result;
506 ?>