e4aa175a |
1 | <?php // $Id$ |
f69db63e |
2 | |
9528568b |
3 | require_once("$CFG->dirroot/mod/scorm/lib.php"); |
4 | |
a30b6819 |
5 | /// Constants and settings for module scorm |
a679d64d |
6 | define('UPDATE_NEVER', '0'); |
7 | define('UPDATE_ONCHANGE', '1'); |
8 | define('UPDATE_EVERYDAY', '2'); |
9 | define('UPDATE_EVERYTIME', '3'); |
10 | |
b3659259 |
11 | define('SCO_ALL', 0); |
12 | define('SCO_DATA', 1); |
13 | define('SCO_ONLY', 2); |
a30b6819 |
14 | |
15 | define('GRADESCOES', '0'); |
16 | define('GRADEHIGHEST', '1'); |
17 | define('GRADEAVERAGE', '2'); |
18 | define('GRADESUM', '3'); |
a30b6819 |
19 | |
20 | define('HIGHESTATTEMPT', '0'); |
21 | define('AVERAGEATTEMPT', '1'); |
22 | define('FIRSTATTEMPT', '2'); |
23 | define('LASTATTEMPT', '3'); |
a30b6819 |
24 | |
a30b6819 |
25 | |
9528568b |
26 | /// Local Library of functions for module scorm |
a30b6819 |
27 | |
9528568b |
28 | /** |
29 | * Extracts scrom package, sets up all variables. |
30 | * Called whenever scorm changes |
31 | * @param object $scorm instance - fields are updated and changes saved into database |
32 | * @param bool $full force full update if true |
33 | * @return void |
34 | */ |
35 | function scorm_parse($scorm, $full) { |
36 | global $CFG, $DB; |
8aee93f1 |
37 | |
9528568b |
38 | if (!isset($scorm->cmid)) { |
39 | $cm = get_coursemodule_from_instance('scorm', $scorm->id); |
40 | $scorm->cmid = $cm->id; |
41 | } |
42 | $context = get_context_instance(CONTEXT_MODULE, $scorm->cmid); |
43 | $newhash = $scorm->sha1hash; |
a30b6819 |
44 | |
9528568b |
45 | if ($scorm->scormtype === SCORM_TYPE_LOCAL or $scorm->scormtype === SCORM_TYPE_LOCALSYNC) { |
a30b6819 |
46 | |
9528568b |
47 | $fs = get_file_storage(); |
48 | $packagefile = false; |
f69db63e |
49 | |
9528568b |
50 | if ($scorm->scormtype === SCORM_TYPE_LOCAL) { |
51 | if ($packagefile = $fs->get_file($context->id, 'scorm_package', 0, '/', $scorm->reference)) { |
52 | $newhash = $packagefile->get_contenthash(); |
53 | } else { |
54 | $newhash = null; |
55 | } |
56 | } else { |
57 | if (!$CFG->scorm_allowtypelocalsync) { |
58 | // sorry - localsync disabled |
59 | return; |
60 | } |
61 | if ($scorm->reference !== '' and (!$full or $scorm->sha1hash !== sha1($scorm->reference))) { |
62 | $fs->delete_area_files($context->id, 'scorm_package'); |
63 | $file_record = array('contextid'=>$context->id, 'filearea'=>'scorm_package', 'itemid'=>0, 'filepath'=>'/'); |
64 | if ($packagefile = $fs->create_file_from_url($file_record, $scorm->reference)) { |
65 | $newhash = sha1($scorm->reference); |
5c1ac70c |
66 | } else { |
9528568b |
67 | $newhash = null; |
5c1ac70c |
68 | } |
f69db63e |
69 | } |
70 | } |
f69db63e |
71 | |
9528568b |
72 | if ($packagefile) { |
73 | if (!$full and $packagefile and $scorm->sha1hash === $newhash) { |
74 | if (strpos($scorm->version, 'SCORM') !== false) { |
75 | if ($fs->get_file($context->id, 'scorm_content', 0, '/', 'imsmanifest.xml')) { |
76 | // no need to update |
77 | return; |
e4aa175a |
78 | } |
9528568b |
79 | } else if (strpos($scorm->version, 'AICC') !== false) { |
80 | // TODO: add more sanity checks - something really exists in scorm_content area |
81 | return; |
82 | } |
83 | } |
84 | |
85 | // now extract files |
86 | $fs->delete_area_files($context->id, 'scorm_content'); |
87 | |
88 | $packer = get_file_packer('application/zip'); |
89 | $packagefile->extract_to_storage($packer, $context->id, 'scorm_content', 0, '/'); |
90 | |
91 | } else if (!$full) { |
92 | return; |
93 | } |
94 | |
95 | |
96 | if ($manifest = $fs->get_file($context->id, 'scorm_content', 0, '/', 'imsmanifest.xml')) { |
97 | require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); |
98 | // SCORM |
99 | if (!scorm_parse_scorm($scorm, $manifest)) { |
100 | $scorm->version = 'ERROR'; |
101 | } |
102 | } else { |
103 | require_once("$CFG->dirroot/mod/scorm/datamodels/aicclib.php"); |
104 | // AICC |
105 | if (!scorm_parse_aicc($scorm)) { |
106 | $scorm->version = 'ERROR'; |
107 | } |
108 | } |
109 | |
110 | } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL and $CFG->scorm_allowtypeexternal) { |
111 | if (!$full and $scorm->sha1hash === sha1($scorm->reference)) { |
112 | return; |
113 | } |
114 | require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); |
115 | // SCORM only, AICC can not be external |
116 | if (!scorm_parse_scorm($scorm, $scorm->reference)) { |
117 | $scorm->version = 'ERROR'; |
118 | } |
119 | $newhash = sha1($scorm->reference); |
120 | |
121 | } else if ($scorm->scormtype === SCORM_TYPE_IMSREPOSITORY and !empty($CFG->repositoryactivate) and $CFG->scorm_allowtypeimsrepository) { |
122 | if (!$full and $scorm->sha1hash === sha1($scorm->reference)) { |
123 | return; |
124 | } |
125 | require_once("$CFG->dirroot/mod/scorm/datamodels/scormlib.php"); |
126 | if (!scorm_parse_scorm($scorm, $CFG->repository.substr($scorm->reference,1).'/imsmanifest.xml')) { |
127 | $scorm->version = 'ERROR'; |
128 | } |
129 | $newhash = sha1($scorm->reference); |
130 | |
e4aa175a |
131 | } else { |
9528568b |
132 | // sorry, disabled type |
133 | return; |
e4aa175a |
134 | } |
9528568b |
135 | |
136 | $scorm->revision++; |
137 | $scorm->sha1hash = $newhash; |
138 | $DB->update_record('scorm', $scorm); |
e4aa175a |
139 | } |
140 | |
9528568b |
141 | |
2b3447c3 |
142 | function scorm_array_search($item, $needle, $haystacks, $strict=false) { |
143 | if (!empty($haystacks)) { |
144 | foreach ($haystacks as $key => $element) { |
145 | if ($strict) { |
146 | if ($element->{$item} === $needle) { |
147 | return $key; |
148 | } |
149 | } else { |
150 | if ($element->{$item} == $needle) { |
151 | return $key; |
e4aa175a |
152 | } |
153 | } |
e4aa175a |
154 | } |
155 | } |
2b3447c3 |
156 | return false; |
e4aa175a |
157 | } |
158 | |
2b3447c3 |
159 | function scorm_repeater($what, $times) { |
160 | if ($times <= 0) { |
161 | return null; |
162 | } |
163 | $return = ''; |
164 | for ($i=0; $i<$times;$i++) { |
165 | $return .= $what; |
166 | } |
167 | return $return; |
168 | } |
e4aa175a |
169 | |
2b3447c3 |
170 | function scorm_external_link($link) { |
171 | // check if a link is external |
172 | $result = false; |
173 | $link = strtolower($link); |
174 | if (substr($link,0,7) == 'http://') { |
175 | $result = true; |
176 | } else if (substr($link,0,8) == 'https://') { |
177 | $result = true; |
178 | } else if (substr($link,0,4) == 'www.') { |
179 | $result = true; |
180 | } |
181 | return $result; |
e4aa175a |
182 | } |
183 | |
b3659259 |
184 | /** |
185 | * Returns an object containing all datas relative to the given sco ID |
186 | * |
187 | * @param integer $id The sco ID |
188 | * @return mixed (false if sco id does not exists) |
189 | */ |
bd3523a5 |
190 | |
b3659259 |
191 | function scorm_get_sco($id,$what=SCO_ALL) { |
bf347041 |
192 | global $DB; |
193 | |
194 | if ($sco = $DB->get_record('scorm_scoes', array('id'=>$id))) { |
b3659259 |
195 | $sco = ($what == SCO_DATA) ? new stdClass() : $sco; |
bf347041 |
196 | if (($what != SCO_ONLY) && ($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id)))) { |
b3659259 |
197 | foreach ($scodatas as $scodata) { |
c31f631b |
198 | $sco->{$scodata->name} = $scodata->value; |
b3659259 |
199 | } |
bf347041 |
200 | } else if (($what != SCO_ONLY) && (!($scodatas = $DB->get_records('scorm_scoes_data', array('scoid'=>$id))))) { |
9528568b |
201 | $sco->parameters = ''; |
b3659259 |
202 | } |
203 | return $sco; |
204 | } else { |
205 | return false; |
206 | } |
207 | } |
82605bea |
208 | |
209 | /** |
210 | * Returns an object (array) containing all the scoes data related to the given sco ID |
211 | * |
212 | * @param integer $id The sco ID |
213 | * @param integer $organisation an organisation ID - defaults to false if not required |
214 | * @return mixed (false if there are no scoes or an array) |
215 | */ |
216 | |
217 | function scorm_get_scoes($id,$organisation=false) { |
b44c704f |
218 | global $DB; |
219 | |
82605bea |
220 | $organizationsql = ''; |
b44c704f |
221 | $queryarray = array('scorm'=>$id); |
82605bea |
222 | if (!empty($organisation)) { |
b44c704f |
223 | $queryarray['organization'] = $organisation; |
82605bea |
224 | } |
b44c704f |
225 | if ($scoes = $DB->get_records('scorm_scoes', $queryarray, 'id ASC')) { |
82605bea |
226 | // drop keys so that it is a simple array as expected |
227 | $scoes = array_values($scoes); |
228 | foreach ($scoes as $sco) { |
b44c704f |
229 | if ($scodatas = $DB->get_records('scorm_scoes_data',array('scoid'=>$sco->id))) { |
82605bea |
230 | foreach ($scodatas as $scodata) { |
231 | $sco->{$scodata->name} = stripslashes_safe($scodata->value); |
232 | } |
233 | } |
234 | } |
235 | return $scoes; |
236 | } else { |
237 | return false; |
238 | } |
239 | } |
240 | |
e4aa175a |
241 | function scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value) { |
bf347041 |
242 | global $DB; |
243 | |
e4aa175a |
244 | $id = null; |
bf347041 |
245 | if ($track = $DB->get_record('scorm_scoes_track',array('userid'=>$userid, 'scormid'=>$scormid, 'scoid'=>$scoid, 'attempt'=>$attempt, 'element'=>$element))) { |
e4aa175a |
246 | $track->value = $value; |
247 | $track->timemodified = time(); |
bf347041 |
248 | $id = $DB->update_record('scorm_scoes_track',$track); |
e4aa175a |
249 | } else { |
250 | $track->userid = $userid; |
251 | $track->scormid = $scormid; |
252 | $track->scoid = $scoid; |
253 | $track->attempt = $attempt; |
254 | $track->element = $element; |
bf347041 |
255 | $track->value = $value; |
e4aa175a |
256 | $track->timemodified = time(); |
bf347041 |
257 | $id = $DB->insert_record('scorm_scoes_track',$track); |
e4aa175a |
258 | } |
9528568b |
259 | |
d23121ab |
260 | // MDL-9552, update the gradebook everything raw score is sent |
a0b36684 |
261 | // Scoring by learning objects also needs to be included in the gradebook update |
9528568b |
262 | if (strstr($element, '.score.raw') || |
a0b36684 |
263 | (($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && ($track->value == 'completed' || $track->value == 'passed'))) { |
264 | $scorm = $DB->get_record('scorm', array('id' => $scormid)); |
265 | $grademethod = $scorm->grademethod % 10; |
266 | if (strstr($element, '.score.raw') || $grademethod == GRADESCOES) { |
267 | include_once('lib.php'); |
268 | scorm_update_grades($scorm, $userid); |
269 | } |
d23121ab |
270 | } |
9528568b |
271 | |
e4aa175a |
272 | return $id; |
273 | } |
274 | |
e4aa175a |
275 | function scorm_get_tracks($scoid,$userid,$attempt='') { |
e4aa175a |
276 | /// Gets all tracks of specified sco and user |
bf347041 |
277 | global $CFG, $DB; |
e4aa175a |
278 | |
279 | if (empty($attempt)) { |
bf347041 |
280 | if ($scormid = $DB->get_field('scorm_scoes','scorm', array('id'=>$scoid))) { |
e4aa175a |
281 | $attempt = scorm_get_last_attempt($scormid,$userid); |
282 | } else { |
283 | $attempt = 1; |
284 | } |
285 | } |
bf347041 |
286 | if ($tracks = $DB->get_records('scorm_scoes_track', array('userid'=>$userid, 'scoid'=>$scoid, 'attempt'=>$attempt),'element ASC')) { |
e4aa175a |
287 | $usertrack->userid = $userid; |
9528568b |
288 | $usertrack->scoid = $scoid; |
a30b6819 |
289 | // Defined in order to unify scorm1.2 and scorm2004 |
e4aa175a |
290 | $usertrack->score_raw = ''; |
e4aa175a |
291 | $usertrack->status = ''; |
e4aa175a |
292 | $usertrack->total_time = '00:00:00'; |
293 | $usertrack->session_time = '00:00:00'; |
294 | $usertrack->timemodified = 0; |
295 | foreach ($tracks as $track) { |
296 | $element = $track->element; |
297 | $usertrack->{$element} = $track->value; |
298 | switch ($element) { |
f69db63e |
299 | case 'cmi.core.lesson_status': |
300 | case 'cmi.completion_status': |
301 | if ($track->value == 'not attempted') { |
302 | $track->value = 'notattempted'; |
9528568b |
303 | } |
f69db63e |
304 | $usertrack->status = $track->value; |
9528568b |
305 | break; |
e4aa175a |
306 | case 'cmi.core.score.raw': |
307 | case 'cmi.score.raw': |
308 | $usertrack->score_raw = $track->value; |
9528568b |
309 | break; |
e4aa175a |
310 | case 'cmi.core.session_time': |
311 | case 'cmi.session_time': |
312 | $usertrack->session_time = $track->value; |
9528568b |
313 | break; |
e4aa175a |
314 | case 'cmi.core.total_time': |
315 | case 'cmi.total_time': |
316 | $usertrack->total_time = $track->value; |
9528568b |
317 | break; |
318 | } |
e4aa175a |
319 | if (isset($track->timemodified) && ($track->timemodified > $usertrack->timemodified)) { |
320 | $usertrack->timemodified = $track->timemodified; |
9528568b |
321 | } |
3505e82b |
322 | } |
9528568b |
323 | if (is_array($usertrack)) { |
07b905ae |
324 | ksort($usertrack); |
325 | } |
e4aa175a |
326 | return $usertrack; |
327 | } else { |
328 | return false; |
329 | } |
330 | } |
331 | |
2b3447c3 |
332 | function scorm_get_user_data($userid) { |
bf347041 |
333 | global $DB; |
2b3447c3 |
334 | /// Gets user info required to display the table of scorm results |
335 | /// for report.php |
e4aa175a |
336 | |
bf347041 |
337 | return $DB->get_record('user', array('id'=>$userid),'firstname, lastname, picture'); |
2b3447c3 |
338 | } |
e4aa175a |
339 | |
a30b6819 |
340 | function scorm_grade_user_attempt($scorm, $userid, $attempt=1, $time=false) { |
bf347041 |
341 | global $DB; |
9528568b |
342 | $attemptscore = NULL; |
a30b6819 |
343 | $attemptscore->scoes = 0; |
344 | $attemptscore->values = 0; |
345 | $attemptscore->max = 0; |
346 | $attemptscore->sum = 0; |
347 | $attemptscore->lastmodify = 0; |
9528568b |
348 | |
bf347041 |
349 | if (!$scoes = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id))) { |
a30b6819 |
350 | return NULL; |
e4aa175a |
351 | } |
e4aa175a |
352 | |
7ce6eb87 |
353 | // this treatment is necessary as the whatgrade field was not in the DB |
354 | // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade |
355 | // and 1s are grademethod |
a30b6819 |
356 | $grademethod = $scorm->grademethod % 10; |
357 | |
9528568b |
358 | foreach ($scoes as $sco) { |
2b3447c3 |
359 | if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { |
360 | if (($userdata->status == 'completed') || ($userdata->status == 'passed')) { |
a30b6819 |
361 | $attemptscore->scoes++; |
9528568b |
362 | } |
2b3447c3 |
363 | if (!empty($userdata->score_raw)) { |
a30b6819 |
364 | $attemptscore->values++; |
365 | $attemptscore->sum += $userdata->score_raw; |
366 | $attemptscore->max = ($userdata->score_raw > $attemptscore->max)?$userdata->score_raw:$attemptscore->max; |
367 | if (isset($userdata->timemodified) && ($userdata->timemodified > $attemptscore->lastmodify)) { |
368 | $attemptscore->lastmodify = $userdata->timemodified; |
369 | } else { |
370 | $attemptscore->lastmodify = 0; |
371 | } |
9528568b |
372 | } |
373 | } |
e4aa175a |
374 | } |
2b3447c3 |
375 | switch ($grademethod) { |
a30b6819 |
376 | case GRADEHIGHEST: |
377 | $score = $attemptscore->max; |
9528568b |
378 | break; |
a30b6819 |
379 | case GRADEAVERAGE: |
380 | if ($attemptscore->values > 0) { |
381 | $score = $attemptscore->sum/$attemptscore->values; |
5c1ac70c |
382 | } else { |
a30b6819 |
383 | $score = 0; |
9528568b |
384 | } |
385 | break; |
a30b6819 |
386 | case GRADESUM: |
387 | $score = $attemptscore->sum; |
9528568b |
388 | break; |
a30b6819 |
389 | case GRADESCOES: |
390 | $score = $attemptscore->scoes; |
7ef16bf9 |
391 | break; |
392 | default: |
393 | $score = $attemptscore->max; // Remote Learner GRADEHIGHEST is default |
5c1ac70c |
394 | } |
a30b6819 |
395 | |
396 | if ($time) { |
397 | $result = new stdClass(); |
398 | $result->score = $score; |
399 | $result->time = $attemptscore->lastmodify; |
400 | } else { |
401 | $result = $score; |
402 | } |
403 | |
404 | return $result; |
405 | } |
406 | |
407 | function scorm_grade_user($scorm, $userid, $time=false) { |
7ce6eb87 |
408 | // this treatment is necessary as the whatgrade field was not in the DB |
409 | // and so whatgrade and grademethod are combined in grademethod 10s are whatgrade |
410 | // and 1s are grademethod |
a30b6819 |
411 | $whatgrade = intval($scorm->grademethod / 10); |
412 | |
7ce6eb87 |
413 | // insure we dont grade user beyond $scorm->maxattempt settings |
414 | $lastattempt = scorm_get_last_attempt($scorm->id, $userid); |
415 | if($scorm->maxattempt != 0 && $lastattempt >= $scorm->maxattempt){ |
416 | $lastattempt = $scorm->maxattempt; |
417 | } |
418 | |
a30b6819 |
419 | switch ($whatgrade) { |
420 | case FIRSTATTEMPT: |
421 | return scorm_grade_user_attempt($scorm, $userid, 1, $time); |
9528568b |
422 | break; |
a30b6819 |
423 | case LASTATTEMPT: |
424 | return scorm_grade_user_attempt($scorm, $userid, scorm_get_last_attempt($scorm->id, $userid), $time); |
425 | break; |
426 | case HIGHESTATTEMPT: |
a30b6819 |
427 | $maxscore = 0; |
428 | $attempttime = 0; |
429 | for ($attempt = 1; $attempt <= $lastattempt; $attempt++) { |
430 | $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time); |
431 | if ($time) { |
432 | if ($attemptscore->score > $maxscore) { |
433 | $maxscore = $attemptscore->score; |
434 | $attempttime = $attemptscore->time; |
435 | } |
436 | } else { |
437 | $maxscore = $attemptscore > $maxscore ? $attemptscore: $maxscore; |
438 | } |
439 | } |
440 | if ($time) { |
441 | $result = new stdClass(); |
442 | $result->score = $maxscore; |
443 | $result->time = $attempttime; |
444 | return $result; |
445 | } else { |
446 | return $maxscore; |
447 | } |
448 | break; |
449 | case AVERAGEATTEMPT: |
450 | $lastattempt = scorm_get_last_attempt($scorm->id, $userid); |
451 | $sumscore = 0; |
452 | for ($attempt = 1; $attempt <= $lastattempt; $attempt++) { |
453 | $attemptscore = scorm_grade_user_attempt($scorm, $userid, $attempt, $time); |
454 | if ($time) { |
455 | $sumscore += $attemptscore->score; |
456 | } else { |
457 | $sumscore += $attemptscore; |
458 | } |
459 | } |
460 | |
461 | if ($lastattempt > 0) { |
462 | $score = $sumscore / $lastattempt; |
463 | } else { |
464 | $score = 0; |
465 | } |
466 | |
467 | if ($time) { |
468 | $result = new stdClass(); |
469 | $result->score = $score; |
470 | $result->time = $attemptscore->time; |
471 | return $result; |
472 | } else { |
473 | return $score; |
474 | } |
475 | break; |
476 | } |
e4aa175a |
477 | } |
478 | |
8e45ba45 |
479 | function scorm_count_launchable($scormid,$organization='') { |
bf347041 |
480 | global $DB; |
481 | |
482 | $sqlorganization = ''; |
483 | $params = array($scormid); |
8e45ba45 |
484 | if (!empty($organization)) { |
bf347041 |
485 | $sqlorganization = " AND organization=?"; |
486 | $params[] = $organization; |
8e45ba45 |
487 | } |
6280b17a |
488 | $params []= ''; // empty launch |
489 | return $DB->count_records_select('scorm_scoes',"scorm = ? $sqlorganization AND launch <> ?", $params); |
e4aa175a |
490 | } |
491 | |
2b3447c3 |
492 | function scorm_get_last_attempt($scormid, $userid) { |
bf347041 |
493 | global $DB; |
494 | |
2b3447c3 |
495 | /// Find the last attempt number for the given user id and scorm id |
bf347041 |
496 | if ($lastattempt = $DB->get_record('scorm_scoes_track', array('userid'=>$userid, 'scormid'=>$scormid), 'max(attempt) as a')) { |
2b3447c3 |
497 | if (empty($lastattempt->a)) { |
498 | return '1'; |
499 | } else { |
500 | return $lastattempt->a; |
e4aa175a |
501 | } |
502 | } |
e4aa175a |
503 | } |
504 | |
e4aa175a |
505 | function scorm_course_format_display($user,$course) { |
bf347041 |
506 | global $CFG, $DB; |
e4aa175a |
507 | |
508 | $strupdate = get_string('update'); |
509 | $strmodule = get_string('modulename','scorm'); |
77bf0c29 |
510 | $context = get_context_instance(CONTEXT_COURSE,$course->id); |
e4aa175a |
511 | |
512 | echo '<div class="mod-scorm">'; |
513 | if ($scorms = get_all_instances_in_course('scorm', $course)) { |
9528568b |
514 | // The module SCORM activity with the least id is the course |
e4aa175a |
515 | $scorm = current($scorms); |
516 | if (! $cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id)) { |
08b56f93 |
517 | print_error('invalidcoursemodule'); |
e4aa175a |
518 | } |
519 | $colspan = ''; |
520 | $headertext = '<table width="100%"><tr><td class="title">'.get_string('name').': <b>'.format_string($scorm->name).'</b>'; |
2b3447c3 |
521 | if (has_capability('moodle/course:manageactivities', $context)) { |
e4aa175a |
522 | if (isediting($course->id)) { |
523 | // Display update icon |
524 | $path = $CFG->wwwroot.'/course'; |
525 | $headertext .= '<span class="commands">'. |
526 | '<a title="'.$strupdate.'" href="'.$path.'/mod.php?update='.$cm->id.'&sesskey='.sesskey().'">'. |
0d905d9f |
527 | '<img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$strupdate.'" /></a></span>'; |
e4aa175a |
528 | } |
529 | $headertext .= '</td>'; |
530 | // Display report link |
bf347041 |
531 | $trackedusers = $DB->get_record('scorm_scoes_track', array('scormid'=>$scorm->id), 'count(distinct(userid)) as c'); |
e4aa175a |
532 | if ($trackedusers->c > 0) { |
533 | $headertext .= '<td class="reportlink">'. |
fa738731 |
534 | '<a '.$CFG->frametarget.'" href="'.$CFG->wwwroot.'/mod/scorm/report.php?id='.$cm->id.'">'. |
e4aa175a |
535 | get_string('viewallreports','scorm',$trackedusers->c).'</a>'; |
536 | } else { |
537 | $headertext .= '<td class="reportlink">'.get_string('noreports','scorm'); |
538 | } |
539 | $colspan = ' colspan="2"'; |
9528568b |
540 | } |
e4aa175a |
541 | $headertext .= '</td></tr><tr><td'.$colspan.'>'.format_text(get_string('summary').':<br />'.$scorm->summary).'</td></tr></table>'; |
542 | print_simple_box($headertext,'','100%'); |
543 | scorm_view_display($user, $scorm, 'view.php?id='.$course->id, $cm, '100%'); |
544 | } else { |
0d699c24 |
545 | if (has_capability('moodle/course:update', $context)) { |
e4aa175a |
546 | // Create a new activity |
2b3447c3 |
547 | redirect($CFG->wwwroot.'/course/mod.php?id='.$course->id.'&section=0&sesskey='.sesskey().'&add=scorm'); |
e4aa175a |
548 | } else { |
549 | notify('Could not find a scorm course here'); |
550 | } |
551 | } |
552 | echo '</div>'; |
553 | } |
554 | |
2b3447c3 |
555 | function scorm_view_display ($user, $scorm, $action, $cm, $boxwidth='') { |
534792cd |
556 | global $CFG, $DB; |
ab3b00e1 |
557 | |
9528568b |
558 | if ($scorm->updatefreq == UPDATE_EVERYTIME) { |
559 | scorm_parse($scorm, false); |
ab3b00e1 |
560 | } |
561 | |
e4aa175a |
562 | $organization = optional_param('organization', '', PARAM_INT); |
563 | |
2b3447c3 |
564 | print_simple_box_start('center',$boxwidth); |
e4aa175a |
565 | ?> |
29a43013 |
566 | <div class="structurehead"><?php print_string('contents','scorm') ?></div> |
e4aa175a |
567 | <?php |
568 | if (empty($organization)) { |
569 | $organization = $scorm->launch; |
570 | } |
07b905ae |
571 | if ($orgs = $DB->get_records_menu('scorm_scoes', array('scorm'=>$scorm->id, 'organization'=>'', 'launch'=>''), 'id', 'id,title')) { |
e4aa175a |
572 | if (count($orgs) > 1) { |
573 | ?> |
52a9a9b5 |
574 | <div class='scorm-center'> |
e4aa175a |
575 | <?php print_string('organizations','scorm') ?> |
b7dc2256 |
576 | <form id='changeorg' method='post' action='<?php echo $action ?>'> |
e4aa175a |
577 | <?php choose_from_menu($orgs, 'organization', "$organization", '','submit()') ?> |
578 | </form> |
579 | </div> |
580 | <?php |
581 | } |
582 | } |
583 | $orgidentifier = ''; |
b3659259 |
584 | if ($sco = scorm_get_sco($organization, SCO_ONLY)) { |
585 | if (($sco->organization == '') && ($sco->launch == '')) { |
586 | $orgidentifier = $sco->identifier; |
e4aa175a |
587 | } else { |
b3659259 |
588 | $orgidentifier = $sco->organization; |
e4aa175a |
589 | } |
590 | } |
28ffbff3 |
591 | |
592 | /* |
593 | $orgidentifier = ''; |
bf347041 |
594 | if ($org = $DB->get_record('scorm_scoes', array('id'=>$organization))) { |
28ffbff3 |
595 | if (($org->organization == '') && ($org->launch == '')) { |
596 | $orgidentifier = $org->identifier; |
597 | } else { |
598 | $orgidentifier = $org->organization; |
599 | } |
600 | }*/ |
601 | |
2b3447c3 |
602 | $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); // Just to be safe |
dbe7e6f6 |
603 | if (!file_exists($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php')) { |
604 | $scorm->version = 'scorm_12'; |
605 | } |
2b3447c3 |
606 | require_once($CFG->dirroot.'/mod/scorm/datamodels/'.$scorm->version.'lib.php'); |
607 | |
e4aa175a |
608 | $result = scorm_get_toc($user,$scorm,'structlist',$orgidentifier); |
609 | $incomplete = $result->incomplete; |
e4aa175a |
610 | echo $result->toc; |
e4aa175a |
611 | print_simple_box_end(); |
28ffbff3 |
612 | |
e4aa175a |
613 | ?> |
52a9a9b5 |
614 | <div class="scorm-center"> |
4675994a |
615 | <form id="theform" method="post" action="<?php echo $CFG->wwwroot ?>/mod/scorm/player.php"> |
e4aa175a |
616 | <?php |
e4aa175a |
617 | if ($scorm->hidebrowse == 0) { |
8949f8df |
618 | print_string('mode','scorm'); |
e4aa175a |
619 | echo ': <input type="radio" id="b" name="mode" value="browse" /><label for="b">'.get_string('browse','scorm').'</label>'."\n"; |
76ea4fb4 |
620 | echo '<input type="radio" id="n" name="mode" value="normal" checked="checked" /><label for="n">'.get_string('normal','scorm')."</label>\n"; |
e4aa175a |
621 | } else { |
76ea4fb4 |
622 | echo '<input type="hidden" name="mode" value="normal" />'."\n"; |
e4aa175a |
623 | } |
624 | if (($incomplete === false) && (($result->attemptleft > 0)||($scorm->maxattempt == 0))) { |
625 | ?> |
626 | <br /> |
627 | <input type="checkbox" id="a" name="newattempt" /> |
628 | <label for="a"><?php print_string('newattempt','scorm') ?></label> |
629 | <?php |
630 | } |
631 | ?> |
632 | <br /> |
4675994a |
633 | <input type="hidden" name="scoid"/> |
df7ba610 |
634 | <input type="hidden" name="id" value="<?php echo $cm->id ?>"/> |
e4aa175a |
635 | <input type="hidden" name="currentorg" value="<?php echo $orgidentifier ?>" /> |
2b600d16 |
636 | <input type="submit" value="<?php print_string('enter','scorm') ?>" /> |
e4aa175a |
637 | </form> |
638 | </div> |
639 | <?php |
640 | } |
6280b17a |
641 | |
28ffbff3 |
642 | function scorm_simple_play($scorm,$user) { |
bf347041 |
643 | global $DB; |
644 | |
28ffbff3 |
645 | $result = false; |
9528568b |
646 | |
5b4b959b |
647 | $scoes = $DB->get_records_select('scorm_scoes', 'scorm = ? AND launch <> ?', array($scorm->id, $DB->sql_empty())); |
9528568b |
648 | |
fa6eb1dc |
649 | if ($scoes && (count($scoes) == 1)) { |
28ffbff3 |
650 | if ($scorm->skipview >= 1) { |
651 | $sco = current($scoes); |
652 | if (scorm_get_tracks($sco->id,$user->id) === false) { |
45698041 |
653 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
28ffbff3 |
654 | $result = true; |
655 | } else if ($scorm->skipview == 2) { |
45698041 |
656 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
28ffbff3 |
657 | $result = true; |
658 | } |
659 | } |
660 | } |
28ffbff3 |
661 | return $result; |
662 | } |
663 | /* |
8e45ba45 |
664 | function scorm_simple_play($scorm,$user) { |
bf347041 |
665 | global $DB; |
8e45ba45 |
666 | $result = false; |
bf347041 |
667 | if ($scoes = $DB->get_records_select('scorm_scoes','scorm=? AND launch<>""', array($scorm->id))) { |
76ea4fb4 |
668 | if (count($scoes) == 1) { |
669 | if ($scorm->skipview >= 1) { |
670 | $sco = current($scoes); |
671 | if (scorm_get_tracks($sco->id,$user->id) === false) { |
672 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
673 | $result = true; |
674 | } else if ($scorm->skipview == 2) { |
675 | header('Location: player.php?a='.$scorm->id.'&scoid='.$sco->id); |
676 | $result = true; |
677 | } |
8e45ba45 |
678 | } |
679 | } |
680 | } |
681 | return $result; |
682 | } |
28ffbff3 |
683 | */ |
d8c9d8a1 |
684 | |
685 | function scorm_get_count_users($scormid, $groupingid=null) { |
bf347041 |
686 | global $CFG, $DB; |
9528568b |
687 | |
d8c9d8a1 |
688 | if (!empty($CFG->enablegroupings) && !empty($groupingid)) { |
689 | $sql = "SELECT COUNT(DISTINCT st.userid) |
bf347041 |
690 | FROM {scorm_scoes_track} st |
691 | INNER JOIN {groups_members} gm ON st.userid = gm.userid |
9528568b |
692 | INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid |
bf347041 |
693 | WHERE st.scormid = ? AND gg.groupingid = ? |
d8c9d8a1 |
694 | "; |
bf347041 |
695 | $params = array($scormid, $groupingid); |
d8c9d8a1 |
696 | } else { |
697 | $sql = "SELECT COUNT(DISTINCT st.userid) |
9528568b |
698 | FROM {scorm_scoes_track} st |
bf347041 |
699 | WHERE st.scormid = ? |
d8c9d8a1 |
700 | "; |
bf347041 |
701 | $params = array($scormid); |
d8c9d8a1 |
702 | } |
9528568b |
703 | |
bf347041 |
704 | return ($DB->count_records_sql($sql, $params)); |
d8c9d8a1 |
705 | } |
706 | |
527af457 |
707 | /** |
708 | * Build up the JavaScript representation of an array element |
709 | * |
710 | * @param string $sversion SCORM API version |
711 | * @param array $userdata User track data |
712 | * @param string $element_name Name of array element to get values for |
713 | * @param array $children list of sub elements of this array element that also need instantiating |
714 | * @return None |
715 | */ |
716 | function scorm_reconstitute_array_element($sversion, $userdata, $element_name, $children) { |
717 | // reconstitute comments_from_learner and comments_from_lms |
718 | $current = ''; |
719 | $count = 0; |
720 | |
721 | // filter out the ones we want |
722 | $element_list = array(); |
723 | foreach($userdata as $element => $value){ |
724 | if (substr($element,0,strlen($element_name)) == $element_name) { |
725 | $element_list[$element] = $value; |
726 | } |
727 | } |
728 | |
729 | // sort elements in .n array order |
730 | uksort($element_list, "scorm_element_cmp"); |
731 | |
732 | // generate JavaScript |
733 | foreach($element_list as $element => $value){ |
734 | if ($sversion == 'scorm_13') { |
735 | $element = preg_replace('/\.(\d+)\./', ".N\$1.", $element); |
736 | preg_match('/\.(N\d+)\./', $element, $matches); |
737 | } else { |
738 | $element = preg_replace('/\.(\d+)\./', "_\$1.", $element); |
739 | preg_match('/\_(\d+)\./', $element, $matches); |
740 | } |
741 | if (count($matches) > 0 && $current != $matches[1]) { |
742 | $current = $matches[1]; |
743 | $count++; |
744 | $end = strpos($element,$matches[1])+strlen($matches[1]); |
745 | $subelement = substr($element,0,$end); |
746 | echo ' '.$subelement." = new Object();\n"; |
747 | // now add the children |
748 | foreach ($children as $child) { |
749 | echo ' '.$subelement.".".$child." = new Object();\n"; |
750 | echo ' '.$subelement.".".$child."._children = ".$child."_children;\n"; |
751 | } |
752 | } |
753 | echo ' '.$element.' = \''.$value."';\n"; |
754 | } |
755 | if ($count > 0) { |
756 | echo ' '.$element_name.'._count = '.$count.";\n"; |
757 | } |
758 | } |
759 | |
760 | /** |
761 | * Build up the JavaScript representation of an array element |
762 | * |
763 | * @param string $a left array element |
764 | * @param string $b right array element |
765 | * @return comparator - 0,1,-1 |
766 | */ |
767 | function scorm_element_cmp($a, $b) { |
768 | preg_match('/(\d+)\./', $a, $matches); |
769 | $left = intval($matches[1]); |
770 | preg_match('/(\d+)\./', $b, $matches); |
771 | $right = intval($matches[1]); |
772 | if ($left < $right) { |
773 | return -1; // smaller |
774 | } elseif ($left > $right) { |
775 | return 1; // bigger |
776 | } else { |
777 | return 0; // equal to |
778 | } |
779 | } |
780 | ?> |