e4aa175a |
1 | <?php // $Id$ |
f69db63e |
2 | |
a30b6819 |
3 | /// Constants and settings for module scorm |
a679d64d |
4 | define('UPDATE_NEVER', '0'); |
5 | define('UPDATE_ONCHANGE', '1'); |
6 | define('UPDATE_EVERYDAY', '2'); |
7 | define('UPDATE_EVERYTIME', '3'); |
8 | |
b3659259 |
9 | define('SCO_ALL', 0); |
10 | define('SCO_DATA', 1); |
11 | define('SCO_ONLY', 2); |
a30b6819 |
12 | |
13 | define('GRADESCOES', '0'); |
14 | define('GRADEHIGHEST', '1'); |
15 | define('GRADEAVERAGE', '2'); |
16 | define('GRADESUM', '3'); |
17 | $SCORM_GRADE_METHOD = array (GRADESCOES => get_string('gradescoes', 'scorm'), |
18 | GRADEHIGHEST => get_string('gradehighest', 'scorm'), |
19 | GRADEAVERAGE => get_string('gradeaverage', 'scorm'), |
20 | GRADESUM => get_string('gradesum', 'scorm')); |
21 | |
22 | define('HIGHESTATTEMPT', '0'); |
23 | define('AVERAGEATTEMPT', '1'); |
24 | define('FIRSTATTEMPT', '2'); |
25 | define('LASTATTEMPT', '3'); |
26 | $SCORM_WHAT_GRADE = array (HIGHESTATTEMPT => get_string('highestattempt', 'scorm'), |
27 | AVERAGEATTEMPT => get_string('averageattempt', 'scorm'), |
28 | FIRSTATTEMPT => get_string('firstattempt', 'scorm'), |
29 | LASTATTEMPT => get_string('lastattempt', 'scorm')); |
30 | |
31 | $SCORM_POPUP_OPTIONS = array('resizable'=>1, |
32 | 'scrollbars'=>1, |
33 | 'directories'=>0, |
34 | 'location'=>0, |
35 | 'menubar'=>0, |
36 | 'toolbar'=>0, |
37 | 'status'=>0); |
38 | $stdoptions = ''; |
39 | foreach ($SCORM_POPUP_OPTIONS as $popupopt => $value) { |
40 | $stdoptions .= $popupopt.'='.$value; |
41 | if ($popupopt != 'status') { |
42 | $stdoptions .= ','; |
43 | } |
44 | } |
45 | |
46 | if (!isset($CFG->scorm_maxattempts)) { |
47 | set_config('scorm_maxattempts','6'); |
48 | } |
49 | |
50 | if (!isset($CFG->scorm_frameheight)) { |
51 | set_config('scorm_frameheight','500'); |
52 | } |
53 | |
54 | if (!isset($CFG->scorm_framewidth)) { |
55 | set_config('scorm_framewidth','100%'); |
56 | } |
57 | |
8aee93f1 |
58 | if (!isset($CFG->scorm_updatetime)) { |
59 | set_config('scorm_updatetime','2'); |
60 | } |
61 | |
a30b6819 |
62 | if (!isset($CFG->scorm_advancedsettings)) { |
63 | set_config('scorm_advancedsettings','0'); |
64 | } |
65 | |
66 | if (!isset($CFG->scorm_windowsettings)) { |
67 | set_config('scorm_windowsettings','0'); |
68 | } |
69 | |
2b3447c3 |
70 | /// Local Library of functions for module scorm |
f69db63e |
71 | |
5c1ac70c |
72 | /** |
73 | * This function will permanently delete the given |
74 | * directory and all files and subdirectories. |
75 | * |
76 | * @param string $directory The directory to remove |
77 | * @return boolean |
78 | */ |
79 | function scorm_delete_files($directory) { |
80 | if (is_dir($directory)) { |
0567b5af |
81 | $files=scandir($directory); |
bd3523a5 |
82 | set_time_limit(0); |
5c1ac70c |
83 | foreach($files as $file) { |
84 | if (($file != '.') && ($file != '..')) { |
85 | if (!is_dir($directory.'/'.$file)) { |
86 | unlink($directory.'/'.$file); |
87 | } else { |
88 | scorm_delete_files($directory.'/'.$file); |
89 | } |
f69db63e |
90 | } |
91 | } |
5c1ac70c |
92 | rmdir($directory); |
93 | return true; |
f69db63e |
94 | } |
5c1ac70c |
95 | return false; |
f69db63e |
96 | } |
97 | |
e4aa175a |
98 | /** |
99 | * Create a new temporary subdirectory with a random name in the given path |
100 | * |
101 | * @param string $strpath The scorm data directory |
102 | * @return string/boolean |
103 | */ |
a679d64d |
104 | function scorm_tempdir($strPath) |
e4aa175a |
105 | { |
106 | global $CFG; |
107 | |
108 | if (is_dir($strPath)) { |
109 | do { |
110 | // Create a random string of 8 chars |
111 | $randstring = NULL; |
112 | $lchar = ''; |
113 | $len = 8; |
114 | for ($i=0; $i<$len; $i++) { |
115 | $char = chr(rand(48,122)); |
116 | while (!ereg('[a-zA-Z0-9]', $char)){ |
117 | if ($char == $lchar) continue; |
118 | $char = chr(rand(48,90)); |
119 | } |
120 | $randstring .= $char; |
121 | $lchar = $char; |
122 | } |
123 | $datadir='/'.$randstring; |
124 | } while (file_exists($strPath.$datadir)); |
125 | mkdir($strPath.$datadir, $CFG->directorypermissions); |
126 | @chmod($strPath.$datadir, $CFG->directorypermissions); // Just in case mkdir didn't do it |
127 | return $strPath.$datadir; |
128 | } else { |
129 | return false; |
130 | } |
131 | } |
132 | |
2b3447c3 |
133 | function scorm_array_search($item, $needle, $haystacks, $strict=false) { |
134 | if (!empty($haystacks)) { |
135 | foreach ($haystacks as $key => $element) { |
136 | if ($strict) { |
137 | if ($element->{$item} === $needle) { |
138 | return $key; |
139 | } |
140 | } else { |
141 | if ($element->{$item} == $needle) { |
142 | return $key; |
e4aa175a |
143 | } |
144 | } |
e4aa175a |
145 | } |
146 | } |
2b3447c3 |
147 | return false; |
e4aa175a |
148 | } |
149 | |
2b3447c3 |
150 | function scorm_repeater($what, $times) { |
151 | if ($times <= 0) { |
152 | return null; |
153 | } |
154 | $return = ''; |
155 | for ($i=0; $i<$times;$i++) { |
156 | $return .= $what; |
157 | } |
158 | return $return; |
159 | } |
e4aa175a |
160 | |
2b3447c3 |
161 | function scorm_external_link($link) { |
162 | // check if a link is external |
163 | $result = false; |
164 | $link = strtolower($link); |
165 | if (substr($link,0,7) == 'http://') { |
166 | $result = true; |
167 | } else if (substr($link,0,8) == 'https://') { |
168 | $result = true; |
169 | } else if (substr($link,0,4) == 'www.') { |
170 | $result = true; |
171 | } |
172 | return $result; |
e4aa175a |
173 | } |
174 | |
b3659259 |
175 | /** |
176 | * Returns an object containing all datas relative to the given sco ID |
177 | * |
178 | * @param integer $id The sco ID |
179 | * @return mixed (false if sco id does not exists) |
180 | */ |
bd3523a5 |
181 | |
b3659259 |
182 | function scorm_get_sco($id,$what=SCO_ALL) { |
bf347041 |
183 | global $DB; |
184 | |
185 | if ($sco = $DB->get_record('scorm_scoes', array('id'=>$id))) { |
b3659259 |
186 | $sco = ($what == SCO_DATA) ? new stdClass() : $sco; |
bf347041 |
187 | if (($what != SCO_ONLY) && ($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id)))) { |
b3659259 |
188 | foreach ($scodatas as $scodata) { |
c31f631b |
189 | $sco->{$scodata->name} = $scodata->value; |
b3659259 |
190 | } |
bf347041 |
191 | } else if (($what != SCO_ONLY) && (!($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id))))) { |
bd3523a5 |
192 | $sco->parameters = ''; |
b3659259 |
193 | } |
194 | return $sco; |
195 | } else { |
196 | return false; |
197 | } |
198 | } |
82605bea |
199 | |
200 | /** |
201 | * Returns an object (array) containing all the scoes data related to the given sco ID |
202 | * |
203 | * @param integer $id The sco ID |
204 | * @param integer $organisation an organisation ID - defaults to false if not required |
205 | * @return mixed (false if there are no scoes or an array) |
206 | */ |
207 | |
208 | function scorm_get_scoes($id,$organisation=false) { |
b44c704f |
209 | global $DB; |
210 | |
82605bea |
211 | $organizationsql = ''; |
b44c704f |
212 | $queryarray = array('scorm'=>$id); |
82605bea |
213 | if (!empty($organisation)) { |
b44c704f |
214 | $queryarray['organization'] = $organisation; |
82605bea |
215 | } |
b44c704f |
216 | if ($scoes = $DB->get_records('scorm_scoes', $queryarray, 'id ASC')) { |
82605bea |
217 | // drop keys so that it is a simple array as expected |
218 | $scoes = array_values($scoes); |
219 | foreach ($scoes as $sco) { |
b44c704f |
220 | if ($scodatas = $DB->get_records('scorm_scoes_data',array('scoid'=>$sco->id))) { |
82605bea |
221 | foreach ($scodatas as $scodata) { |
222 | $sco->{$scodata->name} = stripslashes_safe($scodata->value); |
223 | } |
224 | } |
225 | } |
226 | return $scoes; |
227 | } else { |
228 | return false; |
229 | } |
230 | } |
231 | |
e4aa175a |
232 | function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) { |
bf347041 |
233 | global $DB; |
234 | |
e4aa175a |
235 | $id = null; |
bf347041 |
236 | if ($track = $DB->get_record('scorm_scoes_track',array('userid'=>$userid, 'scormid'=>$scormid, 'scoid'=>$scoid, 'attempt'=>$attempt, 'element'=>$element))) { |
e4aa175a |
237 | $track->value = $value; |
238 | $track->timemodified = time(); |
bf347041 |
239 | $id = $DB->update_record('scorm_scoes_track',$track); |
e4aa175a |
240 | } else { |
241 | $track->userid = $userid; |
242 | $track->scormid = $scormid; |
243 | $track->scoid = $scoid; |
244 | $track->attempt = $attempt; |
245 | $track->element = $element; |
bf347041 |
246 | $track->value = $value; |
e4aa175a |
247 | $track->timemodified = time(); |
bf347041 |
248 | $id = $DB->insert_record('scorm_scoes_track',$track); |
e4aa175a |
249 | } |
d23121ab |
250 | |
251 | // MDL-9552, update the gradebook everything raw score is sent |
a0b36684 |
252 | // Scoring by learning objects also needs to be included in the gradebook update |
253 | if (strstr($element, '.score.raw') || |
254 | (($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && ($track->value == 'completed' || $track->value == 'passed'))) { |
255 | $scorm = $DB->get_record('scorm', array('id' => $scormid)); |
256 | $grademethod = $scorm->grademethod % 10; |
257 | if (strstr($element, '.score.raw') || $grademethod == GRADESCOES) { |
258 | include_once('lib.php'); |
259 | scorm_update_grades($scorm, $userid); |
260 | } |
d23121ab |
261 | } |
262 | |
e4aa175a |
263 | return $id; |
264 | } |
265 | |
e4aa175a |
266 | function scorm_get_tracks($scoid,$userid,$attempt='') { |
e4aa175a |
267 | /// Gets all tracks of specified sco and user |
bf347041 |
268 | global $CFG, $DB; |
e4aa175a |
269 | |
270 | if (empty($attempt)) { |
bf347041 |
271 | if ($scormid = $DB->get_field('scorm_scoes','scorm', array('id'=>$scoid))) { |
e4aa175a |
272 | $attempt = scorm_get_last_attempt($scormid,$userid); |
273 | } else { |
274 | $attempt = 1; |
275 | } |
276 | } |
bf347041 |
277 | if ($tracks = $DB->get_records('scorm_scoes_track', array('userid'=>$userid, 'scoid'=>$scoid, 'attempt'=>$attempt),'element ASC')) { |
e4aa175a |
278 | $usertrack->userid = $userid; |
279 | $usertrack->scoid = $scoid; |
a30b6819 |
280 | // Defined in order to unify scorm1.2 and scorm2004 |
e4aa175a |
281 | $usertrack->score_raw = ''; |
e4aa175a |
282 | $usertrack->status = ''; |
e4aa175a |
283 | $usertrack->total_time = '00:00:00'; |
284 | $usertrack->session_time = '00:00:00'; |
285 | $usertrack->timemodified = 0; |
286 | foreach ($tracks as $track) { |
287 | $element = $track->element; |
288 | $usertrack->{$element} = $track->value; |
289 | switch ($element) { |
f69db63e |
290 | case 'cmi.core.lesson_status': |
291 | case 'cmi.completion_status': |
292 | if ($track->value == 'not attempted') { |
293 | $track->value = 'notattempted'; |
294 | } |
295 | $usertrack->status = $track->value; |
296 | break; |
e4aa175a |
297 | case 'cmi.core.score.raw': |
298 | case 'cmi.score.raw': |
299 | $usertrack->score_raw = $track->value; |
300 | break; |
e4aa175a |
301 | case 'cmi.core.session_time': |
302 | case 'cmi.session_time': |
303 | $usertrack->session_time = $track->value; |
304 | break; |
305 | case 'cmi.core.total_time': |
306 | case 'cmi.total_time': |
307 | $usertrack->total_time = $track->value; |
308 | break; |
309 | } |
310 | if (isset($track->timemodified) && ($track->timemodified > $usertrack->timemodified)) { |
311 | $usertrack->timemodified = $track->timemodified; |
312 | } |
3505e82b |
313 | } |
07b905ae |
314 | if (is_array($usertrack)) { |
315 | ksort($usertrack); |
316 | } |
e4aa175a |
317 | return $usertrack; |
318 | } else { |
319 | return false; |
320 | } |
321 | } |
322 | |
2b3447c3 |
323 | function scorm_get_user_data($userid) { |
bf347041 |
324 | global $DB; |
2b3447c3 |
325 | /// Gets user info required to display the table of scorm results |
326 | /// for report.php |
e4aa175a |
327 | |
bf347041 |
328 | return $DB->get_record('user', array('id'=>$userid),'firstname, lastname, picture'); |
2b3447c3 |
329 | } |
e4aa175a |
330 | |
a30b6819 |
331 | function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) { |
bf347041 |
332 | global $DB; |
a30b6819 |
333 | $attemptscore = NULL; |
334 | $attemptscore->scoes = 0; |
335 | $attemptscore->values = 0; |
336 | $attemptscore->max = 0; |
337 | $attemptscore->sum = 0; |
338 | $attemptscore->lastmodify = 0; |
339 | |
bf347041 |
340 | if (!$scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) { |
a30b6819 |
341 | return NULL; |
e4aa175a |
342 | } |
e4aa175a |
343 | |
7ce6eb87 |
344 | // this treatment is necessary as the whatgrade field was not in the DB |
345 | // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade |
346 | // and 1s are grademethod |
a30b6819 |
347 | $grademethod = $scorm->grademethod % 10; |
348 | |
2b3447c3 |
349 | foreach ($scoes as $sco) { |
350 | if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { |
351 | if (($userdata->status == 'completed') || ($userdata->status == 'passed')) { |
a30b6819 |
352 | $attemptscore->scoes++; |
2b3447c3 |
353 | } |
354 | if (!empty($userdata->score_raw)) { |
a30b6819 |
355 | $attemptscore->values++; |
356 | $attemptscore->sum += $userdata->score_raw; |
357 | $attemptscore->max = ($userdata->score_raw > $attemptscore->max)?$userdata->score_raw:$attemptscore->max; |
358 | if (isset($userdata->timemodified) && ($userdata->timemodified > $attemptscore->lastmodify)) { |
359 | $attemptscore->lastmodify = $userdata->timemodified; |
360 | } else { |
361 | $attemptscore->lastmodify = 0; |
362 | } |
2b3447c3 |
363 | } |
364 | } |
e4aa175a |
365 | } |
2b3447c3 |
366 | switch ($grademethod) { |
a30b6819 |
367 | case GRADEHIGHEST: |
368 | $score = $attemptscore->max; |
2b3447c3 |
369 | break; |
a30b6819 |
370 | case GRADEAVERAGE: |
371 | if ($attemptscore->values > 0) { |
372 | $score = $attemptscore->sum/$attemptscore->values; |
5c1ac70c |
373 | } else { |
a30b6819 |
374 | $score = 0; |
2b3447c3 |
375 | } |
376 | break; |
a30b6819 |
377 | case GRADESUM: |
378 | $score = $attemptscore->sum; |
2b3447c3 |
379 | break; |
a30b6819 |
380 | case GRADESCOES: |
381 | $score = $attemptscore->scoes; |
7ef16bf9 |
382 | break; |
383 | default: |
384 | $score = $attemptscore->max; // Remote Learner GRADEHIGHEST is default |
5c1ac70c |
385 | } |
a30b6819 |
386 | |
387 | if ($time) { |
388 | $result = new stdClass(); |
389 | $result->score = $score; |
390 | $result->time = $attemptscore->lastmodify; |
391 | } else { |
392 | $result = $score; |
393 | } |
394 | |
395 | return $result; |
396 | } |
397 | |
398 | function scorm_grade_user($scorm, $userid, $time=false) { |
7ce6eb87 |
399 | // this treatment is necessary as the whatgrade field was not in the DB |
400 | // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade |
401 | // and 1s are grademethod |
a30b6819 |
402 | $whatgrade = intval($scorm->grademethod / 10); |
403 | |
7ce6eb87 |
404 | // insure we dont grade user beyond $scorm->maxattempt settings |
405 | $lastattempt = scorm_get_last_attempt($scorm->id, $userid); |
406 | if($scorm->maxattempt != 0 && $lastattempt >= $scorm->maxattempt){ |
407 | $lastattempt = $scorm->maxattempt; |
408 | } |
409 | |
a30b6819 |
410 | switch ($whatgrade) { |
411 | case FIRSTATTEMPT: |
412 | return scorm_grade_user_attempt($scorm, $userid, 1, $time); |
413 | break; |
414 | case LASTATTEMPT: |
415 | return scorm_grade_user_attempt($scorm, $userid, scorm_get_last_attempt($scorm->id, $userid), $time); |
416 | break; |
417 | case HIGHESTATTEMPT: |
a30b6819 |
418 | $maxscore = 0; |
419 | $attempttime = 0; |
420 | for ($attempt = 1; $attempt <= $lastattempt; $attempt++) { |
421 | $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time); |
422 | if ($time) { |
423 | if ($attemptscore->score > $maxscore) { |
424 | $maxscore = $attemptscore->score; |
425 | $attempttime = $attemptscore->time; |
426 | } |
427 | } else { |
428 | $maxscore = $attemptscore > $maxscore ? $attemptscore: $maxscore; |
429 | } |
430 | } |
431 | if ($time) { |
432 | $result = new stdClass(); |
433 | $result->score = $maxscore; |
434 | $result->time = $attempttime; |
435 | return $result; |
436 | } else { |
437 | return $maxscore; |
438 | } |
439 | break; |
440 | case AVERAGEATTEMPT: |
441 | $lastattempt = scorm_get_last_attempt($scorm->id, $userid); |
442 | $sumscore = 0; |
443 | for ($attempt = 1; $attempt <= $lastattempt; $attempt++) { |
444 | $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time); |
445 | if ($time) { |
446 | $sumscore += $attemptscore->score; |
447 | } else { |
448 | $sumscore += $attemptscore; |
449 | } |
450 | } |
451 | |
452 | if ($lastattempt > 0) { |
453 | $score = $sumscore / $lastattempt; |
454 | } else { |
455 | $score = 0; |
456 | } |
457 | |
458 | if ($time) { |
459 | $result = new stdClass(); |
460 | $result->score = $score; |
461 | $result->time = $attemptscore->time; |
462 | return $result; |
463 | } else { |
464 | return $score; |
465 | } |
466 | break; |
467 | } |
e4aa175a |
468 | } |
469 | |
8e45ba45 |
470 | function scorm_count_launchable($scormid,$organization='') { |
bf347041 |
471 | global $DB; |
472 | |
473 | $sqlorganization = ''; |
474 | $params = array($scormid); |
8e45ba45 |
475 | if (!empty($organization)) { |
bf347041 |
476 | $sqlorganization = " AND organization=?"; |
477 | $params[] = $organization; |
8e45ba45 |
478 | } |
6280b17a |
479 | $params []= ''; // empty launch |
480 | return $DB->count_records_select('scorm_scoes',"scorm = ? $sqlorganization AND launch <> ?", $params); |
e4aa175a |
481 | } |
482 | |
2b3447c3 |
483 | function scorm_get_last_attempt($scormid, $userid) { |
bf347041 |
484 | global $DB; |
485 | |
2b3447c3 |
486 | /// Find the last attempt number for the given user id and scorm id |
bf347041 |
487 | if ($lastattempt = $DB->get_record('scorm_scoes_track', array('userid'=>$userid, 'scormid'=>$scormid), 'max(attempt) as a')) { |
2b3447c3 |
488 | if (empty($lastattempt->a)) { |
489 | return '1'; |
490 | } else { |
491 | return $lastattempt->a; |
e4aa175a |
492 | } |
493 | } |
e4aa175a |
494 | } |
495 | |
e4aa175a |
496 | function scorm_course_format_display($user,$course) { |
bf347041 |
497 | global $CFG, $DB; |
e4aa175a |
498 | |
499 | $strupdate = get_string('update'); |
500 | $strmodule = get_string('modulename','scorm'); |
77bf0c29 |
501 | $context = get_context_instance(CONTEXT_COURSE,$course->id); |
e4aa175a |
502 | |
503 | echo '<div class="mod-scorm">'; |
504 | if ($scorms = get_all_instances_in_course('scorm', $course)) { |
505 | // The module SCORM activity with the least id is the course |
506 | $scorm = current($scorms); |
507 | if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id)) { |
08b56f93 |
508 | print_error('invalidcoursemodule'); |
e4aa175a |
509 | } |
510 | $colspan = ''; |
511 | $headertext = '<table width="100%"><tr><td class="title">'.get_string('name').': <b>'.format_string($scorm->name).'</b>'; |
2b3447c3 |
512 | if (has_capability('moodle/course:manageactivities', $context)) { |
e4aa175a |
513 | if (isediting($course->id)) { |
514 | // Display update icon |
515 | $path = $CFG->wwwroot.'/course'; |
516 | $headertext .= '<span class="commands">'. |
517 | '<a title="'.$strupdate.'" href="'.$path.'/mod.php?update='.$cm->id.'&sesskey='.sesskey().'">'. |
0d905d9f |
518 | '<img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$strupdate.'" /></a></span>'; |
e4aa175a |
519 | } |
520 | $headertext .= '</td>'; |
521 | // Display report link |
bf347041 |
522 | $trackedusers = $DB->get_record('scorm_scoes_track', array('scormid'=>$scorm->id), 'count(distinct(userid)) as c'); |
e4aa175a |
523 | if ($trackedusers->c > 0) { |
524 | $headertext .= '<td class="reportlink">'. |
fa738731 |
525 | '<a '.$CFG->frametarget.'" href="'.$CFG->wwwroot.'/mod/scorm/report.php?id='.$cm->id.'">'. |
e4aa175a |
526 | get_string('viewallreports','scorm',$trackedusers->c).'</a>'; |
527 | } else { |
528 | $headertext .= '<td class="reportlink">'.get_string('noreports','scorm'); |
529 | } |
530 | $colspan = ' colspan="2"'; |
531 | } |
532 | $headertext .= '</td></tr><tr><td'.$colspan.'>'.format_text(get_string('summary').':<br />'.$scorm->summary).'</td></tr></table>'; |
533 | print_simple_box($headertext,'','100%'); |
534 | scorm_view_display($user, $scorm, 'view.php?id='.$course->id, $cm, '100%'); |
535 | } else { |
0d699c24 |
536 | if (has_capability('moodle/course:update', $context)) { |
e4aa175a |
537 | // Create a new activity |
2b3447c3 |
538 | redirect($CFG->wwwroot.'/course/mod.php?id='.$course->id.'&section=0&sesskey='.sesskey().'&add=scorm'); |
e4aa175a |
539 | } else { |
540 | notify('Could not find a scorm course here'); |
541 | } |
542 | } |
543 | echo '</div>'; |
544 | } |
545 | |
2b3447c3 |
546 | function scorm_view_display ($user, $scorm, $action, $cm, $boxwidth='') { |
534792cd |
547 | global $CFG, $DB; |
ab3b00e1 |
548 | |
549 | if ($scorm->updatefreq == UPDATE_EVERYTIME){ |
5d2b8013 |
550 | require_once($CFG->dirroot.'/mod/scorm/lib.php'); |
551 | |
ab3b00e1 |
552 | $scorm->instance = $scorm->id; |
553 | scorm_update_instance($scorm); |
554 | } |
555 | |
e4aa175a |
556 | $organization = optional_param('organization', '', PARAM_INT); |
557 | |
2b3447c3 |
558 | print_simple_box_start('center',$boxwidth); |
e4aa175a |
559 | ?> |
29a43013 |
560 | <div class="structurehead"><?php print_string('contents','scorm') ?></div> |
e4aa175a |
561 | <?php |
562 | if (empty($organization)) { |
563 | $organization = $scorm->launch; |
564 | } |
07b905ae |
565 | if ($orgs = $DB->get_records_menu('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''), 'id', 'id,title')) { |
e4aa175a |
566 | if (count($orgs) > 1) { |
567 | ?> |
52a9a9b5 |
568 | <div class='scorm-center'> |
e4aa175a |
569 | <?php print_string('organizations','scorm') ?> |
b7dc2256 |
570 | <form id='changeorg' method='post' action='<?php echo $action ?>'> |
e4aa175a |
571 | <?php choose_from_menu($orgs, 'organization', "$organization", '','submit()') ?> |
572 | </form> |
573 | </div> |
574 | <?php |
575 | } |
576 | } |
577 | $orgidentifier = ''; |
b3659259 |
578 | if ($sco = scorm_get_sco($organization, SCO_ONLY)) { |
579 | if (($sco->organization == '') && ($sco->launch == '')) { |
580 | $orgidentifier = $sco->identifier; |
e4aa175a |
581 | } else { |
b3659259 |
582 | $orgidentifier = $sco->organization; |
e4aa175a |
583 | } |
584 | } |
28ffbff3 |
585 | |
586 | /* |
587 | $orgidentifier = ''; |
bf347041 |
588 | if ($org = $DB->get_record('scorm_scoes', array('id'=>$organization))) { |
28ffbff3 |
589 | if (($org->organization == '') && ($org->launch == '')) { |
590 | $orgidentifier = $org->identifier; |
591 | } else { |
592 | $orgidentifier = $org->organization; |
593 | } |
594 | }*/ |
595 | |
2b3447c3 |
596 | $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe |
dbe7e6f6 |
597 | if (!file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php')) { |
598 | $scorm->version = 'scorm_12'; |
599 | } |
2b3447c3 |
600 | require_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php'); |
601 | |
e4aa175a |
602 | $result = scorm_get_toc($user,$scorm,'structlist',$orgidentifier); |
603 | $incomplete = $result->incomplete; |
e4aa175a |
604 | echo $result->toc; |
e4aa175a |
605 | print_simple_box_end(); |
28ffbff3 |
606 | |
e4aa175a |
607 | ?> |
52a9a9b5 |
608 | <div class="scorm-center"> |
4675994a |
609 | <form id="theform" method="post" action="<?php echo $CFG->wwwroot ?>/mod/scorm/player.php"> |
e4aa175a |
610 | <?php |
e4aa175a |
611 | if ($scorm->hidebrowse == 0) { |
8949f8df |
612 | print_string('mode','scorm'); |
e4aa175a |
613 | echo ': <input type="radio" id="b" name="mode" value="browse" /><label for="b">'.get_string('browse','scorm').'</label>'."\n"; |
76ea4fb4 |
614 | echo '<input type="radio" id="n" name="mode" value="normal" checked="checked" /><label for="n">'.get_string('normal','scorm')."</label>\n"; |
e4aa175a |
615 | } else { |
76ea4fb4 |
616 | echo '<input type="hidden" name="mode" value="normal" />'."\n"; |
e4aa175a |
617 | } |
618 | if (($incomplete === false) && (($result->attemptleft > 0)||($scorm->maxattempt == 0))) { |
619 | ?> |
620 | <br /> |
621 | <input type="checkbox" id="a" name="newattempt" /> |
622 | <label for="a"><?php print_string('newattempt','scorm') ?></label> |
623 | <?php |
624 | } |
625 | ?> |
626 | <br /> |
4675994a |
627 | <input type="hidden" name="scoid"/> |
df7ba610 |
628 | <input type="hidden" name="id" value="<?php echo $cm->id ?>"/> |
e4aa175a |
629 | <input type="hidden" name="currentorg" value="<?php echo $orgidentifier ?>" /> |
2b600d16 |
630 | <input type="submit" value="<?php print_string('enter','scorm') ?>" /> |
e4aa175a |
631 | </form> |
632 | </div> |
633 | <?php |
634 | } |
6280b17a |
635 | |
28ffbff3 |
636 | function scorm_simple_play($scorm,$user) { |
bf347041 |
637 | global $DB; |
638 | |
28ffbff3 |
639 | $result = false; |
640 | |
5b4b959b |
641 | $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty())); |
28ffbff3 |
642 | |
fa6eb1dc |
643 | if ($scoes && (count($scoes) == 1)) { |
28ffbff3 |
644 | if ($scorm->skipview >= 1) { |
645 | $sco = current($scoes); |
646 | if (scorm_get_tracks($sco->id,$user->id) === false) { |
45698041 |
647 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
28ffbff3 |
648 | $result = true; |
649 | } else if ($scorm->skipview == 2) { |
45698041 |
650 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
28ffbff3 |
651 | $result = true; |
652 | } |
653 | } |
654 | } |
28ffbff3 |
655 | return $result; |
656 | } |
657 | /* |
8e45ba45 |
658 | function scorm_simple_play($scorm,$user) { |
bf347041 |
659 | global $DB; |
8e45ba45 |
660 | $result = false; |
bf347041 |
661 | if ($scoes = $DB->get_records_select('scorm_scoes','scorm=? AND launch<>""', array($scorm->id))) { |
76ea4fb4 |
662 | if (count($scoes) == 1) { |
663 | if ($scorm->skipview >= 1) { |
664 | $sco = current($scoes); |
665 | if (scorm_get_tracks($sco->id,$user->id) === false) { |
666 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
667 | $result = true; |
668 | } else if ($scorm->skipview == 2) { |
669 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
670 | $result = true; |
671 | } |
8e45ba45 |
672 | } |
673 | } |
674 | } |
675 | return $result; |
676 | } |
28ffbff3 |
677 | */ |
a30b6819 |
678 | function scorm_parse($scorm) { |
d9397d51 |
679 | global $CFG; |
45698041 |
680 | |
8949f8df |
681 | if ($scorm->reference[0] == '#') { |
d9397d51 |
682 | if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { |
45698041 |
683 | $referencedir = $CFG->repository.substr($scorm->reference,1); |
684 | } |
8949f8df |
685 | } else { |
45698041 |
686 | if ((!scorm_external_link($scorm->reference)) && (basename($scorm->reference) == 'imsmanifest.xml')) { |
687 | $referencedir = $CFG->dataroot.'/'.$scorm->course.'/'.$scorm->datadir; |
688 | } else { |
689 | $referencedir = $CFG->dataroot.'/'.$scorm->course.'/moddata/scorm/'.$scorm->id; |
690 | } |
8949f8df |
691 | } |
45698041 |
692 | |
a30b6819 |
693 | // Parse scorm manifest |
694 | if ($scorm->pkgtype == 'AICC') { |
695 | require_once('datamodels/aicclib.php'); |
45698041 |
696 | $scorm->launch = scorm_parse_aicc($referencedir, $scorm->id); |
a30b6819 |
697 | } else { |
698 | require_once('datamodels/scormlib.php'); |
45698041 |
699 | $scorm->launch = scorm_parse_scorm($referencedir,$scorm->id); |
a30b6819 |
700 | } |
701 | return $scorm->launch; |
702 | } |
703 | |
76ea4fb4 |
704 | /** |
705 | * Given a manifest path, this function will check if the manifest is valid |
706 | * |
707 | * @param string $manifest The manifest file |
708 | * @return object |
709 | */ |
710 | function scorm_validate_manifest($manifest) { |
711 | $validation = new stdClass(); |
712 | if (is_file($manifest)) { |
a679d64d |
713 | $validation->result = true; |
76ea4fb4 |
714 | } else { |
a679d64d |
715 | $validation->result = false; |
716 | $validation->errors['reference'] = get_string('nomanifest','scorm'); |
76ea4fb4 |
717 | } |
718 | return $validation; |
719 | } |
720 | |
721 | /** |
722 | * Given a aicc package directory, this function will check if the course structure is valid |
723 | * |
724 | * @param string $packagedir The aicc package directory path |
725 | * @return object |
726 | */ |
727 | function scorm_validate_aicc($packagedir) { |
728 | $validation = new stdClass(); |
a679d64d |
729 | $validation->result = false; |
76ea4fb4 |
730 | if (is_dir($packagedir)) { |
731 | if ($handle = opendir($packagedir)) { |
732 | while (($file = readdir($handle)) !== false) { |
733 | $ext = substr($file,strrpos($file,'.')); |
734 | if (strtolower($ext) == '.cst') { |
a679d64d |
735 | $validation->result = true; |
76ea4fb4 |
736 | break; |
737 | } |
738 | } |
739 | closedir($handle); |
740 | } |
741 | } |
a679d64d |
742 | if ($validation->result == false) { |
743 | $validation->errors['reference'] = get_string('nomanifest','scorm'); |
76ea4fb4 |
744 | } |
745 | return $validation; |
746 | } |
747 | |
748 | |
749 | function scorm_validate($data) { |
bf347041 |
750 | global $CFG, $DB; |
76ea4fb4 |
751 | |
a679d64d |
752 | $validation = new stdClass(); |
753 | $validation->errors = array(); |
754 | |
755 | if (!isset($data['course']) || empty($data['course'])) { |
756 | $validation->errors['reference'] = get_string('missingparam','scorm'); |
757 | $validation->result = false; |
758 | return $validation; |
759 | } |
760 | $courseid = $data['course']; // Course Module ID |
761 | |
762 | if (!isset($data['reference']) || empty($data['reference'])) { |
763 | $validation->errors['reference'] = get_string('packagefile','scorm'); |
764 | $validation->result = false; |
765 | return $validation; |
766 | } |
767 | $reference = $data['reference']; // Package/manifest path/location |
768 | |
769 | $scormid = $data['instance']; // scorm ID |
770 | $scorm = new stdClass(); |
771 | if (!empty($scormid)) { |
bf347041 |
772 | if (!$scorm = $DB->get_record('scorm', array('id'=>$scormid))) { |
a679d64d |
773 | $validation->errors['reference'] = get_string('missingparam','scorm'); |
774 | $validation->result = false; |
775 | return $validation; |
776 | } |
777 | } |
778 | |
779 | if ($reference[0] == '#') { |
d9397d51 |
780 | if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { |
a679d64d |
781 | $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml'; |
782 | } else { |
783 | $validation->errors['reference'] = get_string('badpackage','scorm'); |
784 | $validation->result = false; |
785 | return $validation; |
786 | } |
787 | } else if (!scorm_external_link($reference)) { |
788 | $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference; |
789 | } |
790 | |
791 | // Create a temporary directory to unzip package or copy manifest and validate package |
792 | $tempdir = ''; |
793 | $scormdir = ''; |
794 | if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { |
795 | if ($tempdir = scorm_tempdir($scormdir)) { |
8949f8df |
796 | $localreference = $tempdir.'/'.basename($reference); |
a679d64d |
797 | copy ("$reference", $localreference); |
798 | if (!is_file($localreference)) { |
799 | $validation->errors['reference'] = get_string('badpackage','scorm'); |
800 | $validation->result = false; |
801 | } else { |
802 | $ext = strtolower(substr(basename($localreference),strrpos(basename($localreference),'.'))); |
803 | switch ($ext) { |
804 | case '.pif': |
805 | case '.zip': |
806 | if (!unzip_file($localreference, $tempdir, false)) { |
807 | $validation->errors['reference'] = get_string('unziperror','scorm'); |
808 | $validation->result = false; |
809 | } else { |
810 | unlink ($localreference); |
811 | if (is_file($tempdir.'/imsmanifest.xml')) { |
812 | $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml'); |
813 | $validation->pkgtype = 'SCORM'; |
814 | } else { |
815 | $validation = scorm_validate_aicc($tempdir); |
816 | if (($validation->result == 'regular') || ($validation->result == 'found')) { |
817 | $validation->pkgtype = 'AICC'; |
818 | } else { |
819 | $validation->errors['reference'] = get_string('nomanifest','scorm'); |
820 | $validation->result = false; |
821 | } |
822 | } |
823 | } |
824 | break; |
825 | case '.xml': |
826 | if (basename($localreference) == 'imsmanifest.xml') { |
827 | $validation = scorm_validate_manifest($localreference); |
828 | } else { |
829 | $validation->errors['reference'] = get_string('nomanifest','scorm'); |
830 | $validation->result = false; |
831 | } |
832 | break; |
833 | default: |
834 | $validation->errors['reference'] = get_string('badpackage','scorm'); |
835 | $validation->result = false; |
836 | break; |
837 | } |
838 | } |
839 | if (is_dir($tempdir)) { |
840 | // Delete files and temporary directory |
841 | scorm_delete_files($tempdir); |
842 | } |
843 | } else { |
844 | $validation->errors['reference'] = get_string('packagedir','scorm'); |
845 | $validation->result = false; |
846 | } |
847 | } else { |
848 | $validation->errors['reference'] = get_string('datadir','scorm'); |
849 | $validation->result = false; |
850 | } |
851 | return $validation; |
852 | } |
853 | |
854 | function scorm_check_package($data) { |
bf347041 |
855 | global $CFG, $COURSE, $DB; |
a679d64d |
856 | |
76ea4fb4 |
857 | $courseid = $data->course; // Course Module ID |
858 | $reference = $data->reference; // Package path |
859 | $scormid = $data->instance; // scorm ID |
860 | |
861 | $validation = new stdClass(); |
a679d64d |
862 | |
863 | if (!empty($courseid) && !empty($reference)) { |
864 | $externalpackage = scorm_external_link($reference); |
865 | |
76ea4fb4 |
866 | $validation->launch = 0; |
76ea4fb4 |
867 | $referencefield = $reference; |
868 | if (empty($reference)) { |
a679d64d |
869 | $validation = null; |
76ea4fb4 |
870 | } else if ($reference[0] == '#') { |
d9397d51 |
871 | if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { |
658e2574 |
872 | $referencefield = $reference.'/imsmanifest.xml'; |
76ea4fb4 |
873 | $reference = $CFG->repository.substr($reference,1).'/imsmanifest.xml'; |
874 | } else { |
a679d64d |
875 | $validation = null; |
76ea4fb4 |
876 | } |
a679d64d |
877 | } else if (!$externalpackage) { |
76ea4fb4 |
878 | $reference = $CFG->dataroot.'/'.$courseid.'/'.$reference; |
879 | } |
880 | |
881 | if (!empty($scormid)) { |
882 | // |
883 | // SCORM Update |
884 | // |
a679d64d |
885 | if ((!empty($validation)) && (is_file($reference) || $externalpackage)){ |
8aee93f1 |
886 | |
a679d64d |
887 | if (!$externalpackage) { |
888 | $mdcheck = md5_file($reference); |
889 | } else if ($externalpackage){ |
890 | if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { |
891 | if ($tempdir = scorm_tempdir($scormdir)) { |
8949f8df |
892 | copy ("$reference", $tempdir.'/'.basename($reference)); |
893 | $mdcheck = md5_file($tempdir.'/'.basename($reference)); |
a679d64d |
894 | scorm_delete_files($tempdir); |
895 | } |
896 | } |
897 | } |
8aee93f1 |
898 | |
bf347041 |
899 | if ($scorm = $DB->get_record('scorm', array('id'=>$scormid))) { |
76ea4fb4 |
900 | if ($scorm->reference[0] == '#') { |
d9397d51 |
901 | if (isset($CFG->repositoryactivate) && $CFG->repositoryactivate) { |
76ea4fb4 |
902 | $oldreference = $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml'; |
903 | } else { |
904 | $oldreference = $scorm->reference; |
905 | } |
a679d64d |
906 | } else if (!scorm_external_link($scorm->reference)) { |
76ea4fb4 |
907 | $oldreference = $CFG->dataroot.'/'.$courseid.'/'.$scorm->reference; |
a679d64d |
908 | } else { |
909 | $oldreference = $scorm->reference; |
910 | } |
76ea4fb4 |
911 | $validation->launch = $scorm->launch; |
a679d64d |
912 | if ((($oldreference == $reference) && ($mdcheck != $scorm->md5hash)) || ($oldreference != $reference)) { |
76ea4fb4 |
913 | // This is a new or a modified package |
914 | $validation->launch = 0; |
915 | } else { |
916 | // Old package already validated |
76ea4fb4 |
917 | if (strpos($scorm->version,'AICC') !== false) { |
918 | $validation->pkgtype = 'AICC'; |
919 | } else { |
920 | $validation->pkgtype = 'SCORM'; |
921 | } |
922 | } |
923 | } else { |
a679d64d |
924 | $validation = null; |
76ea4fb4 |
925 | } |
926 | } else { |
a679d64d |
927 | $validation = null; |
76ea4fb4 |
928 | } |
929 | } |
930 | //$validation->launch = 0; |
a679d64d |
931 | if (($validation != null) && ($validation->launch == 0)) { |
76ea4fb4 |
932 | // |
933 | // Package must be validated |
934 | // |
935 | $ext = strtolower(substr(basename($reference),strrpos(basename($reference),'.'))); |
a679d64d |
936 | $tempdir = ''; |
76ea4fb4 |
937 | switch ($ext) { |
938 | case '.pif': |
939 | case '.zip': |
940 | // Create a temporary directory to unzip package and validate package |
76ea4fb4 |
941 | $scormdir = ''; |
942 | if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { |
a679d64d |
943 | if ($tempdir = scorm_tempdir($scormdir)) { |
8949f8df |
944 | copy ("$reference", $tempdir.'/'.basename($reference)); |
945 | unzip_file($tempdir.'/'.basename($reference), $tempdir, false); |
82e34576 |
946 | if (!$externalpackage) { |
8949f8df |
947 | unlink ($tempdir.'/'.basename($reference)); |
82e34576 |
948 | } |
76ea4fb4 |
949 | if (is_file($tempdir.'/imsmanifest.xml')) { |
950 | $validation = scorm_validate_manifest($tempdir.'/imsmanifest.xml'); |
951 | $validation->pkgtype = 'SCORM'; |
952 | } else { |
953 | $validation = scorm_validate_aicc($tempdir); |
954 | $validation->pkgtype = 'AICC'; |
955 | } |
956 | } else { |
a679d64d |
957 | $validation = null; |
76ea4fb4 |
958 | } |
959 | } else { |
a679d64d |
960 | $validation = null; |
76ea4fb4 |
961 | } |
962 | break; |
963 | case '.xml': |
964 | if (basename($reference) == 'imsmanifest.xml') { |
a679d64d |
965 | if ($externalpackage) { |
966 | if ($scormdir = make_upload_directory("$courseid/$CFG->moddata/scorm")) { |
967 | if ($tempdir = scorm_tempdir($scormdir)) { |
8949f8df |
968 | copy ("$reference", $tempdir.'/'.basename($reference)); |
969 | if (is_file($tempdir.'/'.basename($reference))) { |
ab3b00e1 |
970 | $validation = scorm_validate_manifest($tempdir.'/'.basename($reference)); |
971 | } else { |
972 | $validation = null; |
973 | } |
a679d64d |
974 | } |
975 | } |
976 | } else { |
45698041 |
977 | $validation = scorm_validate_manifest($reference); |
a679d64d |
978 | } |
ab3b00e1 |
979 | $validation->pkgtype = 'SCORM'; |
76ea4fb4 |
980 | } else { |
a679d64d |
981 | $validation = null; |
76ea4fb4 |
982 | } |
983 | break; |
984 | default: |
a679d64d |
985 | $validation = null; |
76ea4fb4 |
986 | break; |
987 | } |
a679d64d |
988 | if ($validation == null) { |
76ea4fb4 |
989 | if (is_dir($tempdir)) { |
990 | // Delete files and temporary directory |
991 | scorm_delete_files($tempdir); |
992 | } |
76ea4fb4 |
993 | } else { |
ab3b00e1 |
994 | if (($ext == '.xml') && (!$externalpackage)) { |
76ea4fb4 |
995 | $validation->datadir = dirname($referencefield); |
996 | } else { |
997 | $validation->datadir = substr($tempdir,strlen($scormdir)); |
998 | } |
999 | $validation->launch = 0; |
1000 | } |
1001 | } |
1002 | } else { |
a679d64d |
1003 | $validation = null; |
76ea4fb4 |
1004 | } |
1005 | return $validation; |
1006 | } |
1007 | |
d8c9d8a1 |
1008 | |
1009 | function scorm_get_count_users($scormid, $groupingid=null) { |
bf347041 |
1010 | global $CFG, $DB; |
d8c9d8a1 |
1011 | |
1012 | if (!empty($CFG->enablegroupings) && !empty($groupingid)) { |
1013 | $sql = "SELECT COUNT(DISTINCT st.userid) |
bf347041 |
1014 | FROM {scorm_scoes_track} st |
1015 | INNER JOIN {groups_members} gm ON st.userid = gm.userid |
1016 | INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid |
1017 | WHERE st.scormid = ? AND gg.groupingid = ? |
d8c9d8a1 |
1018 | "; |
bf347041 |
1019 | $params = array($scormid, $groupingid); |
d8c9d8a1 |
1020 | } else { |
1021 | $sql = "SELECT COUNT(DISTINCT st.userid) |
bf347041 |
1022 | FROM {scorm_scoes_track} st |
1023 | WHERE st.scormid = ? |
d8c9d8a1 |
1024 | "; |
bf347041 |
1025 | $params = array($scormid); |
d8c9d8a1 |
1026 | } |
1027 | |
bf347041 |
1028 | return ($DB->count_records_sql($sql, $params)); |
d8c9d8a1 |
1029 | } |
1030 | |
b44c704f |
1031 | ?> |