1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
|
|
|
17 |
defined('MOODLE_INTERNAL') || die();
|
|
|
18 |
|
|
|
19 |
require_once($CFG->dirroot.'/mod/scorm/datamodels/scormlib.php');
|
|
|
20 |
require_once($CFG->dirroot.'/mod/scorm/datamodels/sequencinglib.php');
|
|
|
21 |
|
|
|
22 |
function scorm_seq_overall ($scoid, $userid, $request, $attempt) {
|
|
|
23 |
$seq = scorm_seq_navigation($scoid, $userid, $request, $attempt);
|
|
|
24 |
if ($seq->navigation) {
|
|
|
25 |
if ($seq->termination != null) {
|
|
|
26 |
$seq = scorm_seq_termination($scoid, $userid, $seq);
|
|
|
27 |
}
|
|
|
28 |
if ($seq->sequencing != null) {
|
|
|
29 |
$seq = scorm_seq_sequencing($scoid, $userid, $seq);
|
|
|
30 |
if ($seq->sequencing == 'exit') { // Return the control to the LTS.
|
|
|
31 |
return 'true';
|
|
|
32 |
}
|
|
|
33 |
}
|
|
|
34 |
if ($seq->delivery != null) {
|
|
|
35 |
$seq = scorm_sequencing_delivery($scoid, $userid, $seq);
|
|
|
36 |
$seq = scorm_content_delivery_environment ($seq, $userid);
|
|
|
37 |
}
|
|
|
38 |
}
|
|
|
39 |
if ($seq->exception != null) {
|
|
|
40 |
$seq = scorm_sequencing_exception($seq);
|
|
|
41 |
}
|
|
|
42 |
return 'true';
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
function scorm_seq_navigation ($scoid, $userid, $request, $attempt=0) {
|
|
|
46 |
global $DB;
|
|
|
47 |
|
|
|
48 |
// Sequencing structure.
|
|
|
49 |
$seq = new stdClass();
|
|
|
50 |
$seq->currentactivity = scorm_get_sco($scoid);
|
|
|
51 |
$seq->traversaldir = null;
|
|
|
52 |
$seq->nextactivity = null;
|
|
|
53 |
$seq->deliveryvalid = null;
|
|
|
54 |
$seq->attempt = $attempt;
|
|
|
55 |
|
|
|
56 |
$seq->identifiedactivity = null;
|
|
|
57 |
$seq->delivery = null;
|
|
|
58 |
$seq->deliverable = false;
|
|
|
59 |
$seq->active = scorm_seq_is('active', $scoid, $userid);
|
|
|
60 |
$seq->suspended = scorm_seq_is('suspended', $scoid, $userid);
|
|
|
61 |
$seq->navigation = null;
|
|
|
62 |
$seq->termination = null;
|
|
|
63 |
$seq->sequencing = null;
|
|
|
64 |
$seq->target = null;
|
|
|
65 |
$seq->endsession = null;
|
|
|
66 |
$seq->exception = null;
|
|
|
67 |
$seq->reachable = true;
|
|
|
68 |
$seq->prevact = true;
|
|
|
69 |
|
|
|
70 |
$sco = scorm_get_sco($scoid);
|
|
|
71 |
|
|
|
72 |
switch ($request) {
|
|
|
73 |
case 'start_':
|
|
|
74 |
if (empty($seq->currentactivity)) {
|
|
|
75 |
$seq->navigation = true;
|
|
|
76 |
$seq->sequencing = 'start';
|
|
|
77 |
} else {
|
|
|
78 |
$seq->exception = 'NB.2.1-1'; // Sequencing session already begun.
|
|
|
79 |
}
|
|
|
80 |
break;
|
|
|
81 |
case 'resumeall_':
|
|
|
82 |
if (empty($seq->currentactivity)) {
|
|
|
83 |
// TODO: I think it's suspend instead of suspendedactivity.
|
|
|
84 |
if (scorm_get_sco_value($scoid, $userid, 'suspendedactivity')) {
|
|
|
85 |
|
|
|
86 |
$seq->navigation = true;
|
|
|
87 |
$seq->sequencing = 'resumeall';
|
|
|
88 |
} else {
|
|
|
89 |
$seq->exception = 'NB.2.1-3'; // No suspended activity found.
|
|
|
90 |
}
|
|
|
91 |
} else {
|
|
|
92 |
$seq->exception = 'NB.2.1-1'; // Sequencing session already begun.
|
|
|
93 |
}
|
|
|
94 |
break;
|
|
|
95 |
case 'continue_':
|
|
|
96 |
case 'previous_':
|
|
|
97 |
if (!empty($seq->currentactivity)) {
|
|
|
98 |
$sco = $seq->currentactivity;
|
|
|
99 |
if ($sco->parent != '/') {
|
|
|
100 |
if ($parentsco = scorm_get_parent($sco)) {
|
|
|
101 |
|
|
|
102 |
if (isset($parentsco->flow) && ($parentsco->flow == true)) { // I think it's parentsco.
|
|
|
103 |
// Current activity is active!
|
|
|
104 |
if (scorm_seq_is('active', $sco->id, $userid)) {
|
|
|
105 |
if ($request == 'continue_') {
|
|
|
106 |
$seq->navigation = true;
|
|
|
107 |
$seq->termination = 'exit';
|
|
|
108 |
$seq->sequencing = 'continue';
|
|
|
109 |
} else {
|
|
|
110 |
if (!isset($parentsco->forwardonly) || ($parentsco->forwardonly == false)) {
|
|
|
111 |
$seq->navigation = true;
|
|
|
112 |
$seq->termination = 'exit';
|
|
|
113 |
$seq->sequencing = 'previous';
|
|
|
114 |
} else {
|
|
|
115 |
$seq->exception = 'NB.2.1-5'; // Violates control mode.
|
|
|
116 |
}
|
|
|
117 |
}
|
|
|
118 |
}
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
}
|
|
|
122 |
}
|
|
|
123 |
} else {
|
|
|
124 |
$seq->exception = 'NB.2.1-2'; // Current activity not defined.
|
|
|
125 |
}
|
|
|
126 |
break;
|
|
|
127 |
case 'forward_':
|
|
|
128 |
case 'backward_':
|
|
|
129 |
$seq->exception = 'NB.2.1-7'; // None to be done, behavior not defined.
|
|
|
130 |
break;
|
|
|
131 |
case 'exit_':
|
|
|
132 |
case 'abandon_':
|
|
|
133 |
if (!empty($seq->currentactivity)) {
|
|
|
134 |
// Current activity is active !
|
|
|
135 |
$seq->navigation = true;
|
|
|
136 |
$seq->termination = substr($request, 0, -1);
|
|
|
137 |
$seq->sequencing = 'exit';
|
|
|
138 |
} else {
|
|
|
139 |
$seq->exception = 'NB.2.1-2'; // Current activity not defined.
|
|
|
140 |
}
|
|
|
141 |
case 'exitall_':
|
|
|
142 |
case 'abandonall_':
|
|
|
143 |
case 'suspendall_':
|
|
|
144 |
if (!empty($seq->currentactivity)) {
|
|
|
145 |
$seq->navigation = true;
|
|
|
146 |
$seq->termination = substr($request, 0, -1);
|
|
|
147 |
$seq->sequencing = 'exit';
|
|
|
148 |
} else {
|
|
|
149 |
$seq->exception = 'NB.2.1-2'; // Current activity not defined.
|
|
|
150 |
}
|
|
|
151 |
break;
|
|
|
152 |
default: // Example {target=<STRING>}choice.
|
|
|
153 |
if ($targetsco = $DB->get_record('scorm_scoes', array('scorm' => $sco->scorm, 'identifier' => $request))) {
|
|
|
154 |
if ($targetsco->parent != '/') {
|
|
|
155 |
$seq->target = $request;
|
|
|
156 |
} else {
|
|
|
157 |
if ($parentsco = scorm_get_parent($targetsco)) {
|
|
|
158 |
if (!isset($parentsco->choice) || ($parentsco->choice == true)) {
|
|
|
159 |
$seq->target = $request;
|
|
|
160 |
}
|
|
|
161 |
}
|
|
|
162 |
}
|
|
|
163 |
if ($seq->target != null) {
|
|
|
164 |
if (empty($seq->currentactivity)) {
|
|
|
165 |
$seq->navigation = true;
|
|
|
166 |
$seq->sequencing = 'choice';
|
|
|
167 |
} else {
|
|
|
168 |
if (!$sco = scorm_get_sco($scoid)) {
|
|
|
169 |
return $seq;
|
|
|
170 |
}
|
|
|
171 |
if ($sco->parent != $targetsco->parent) {
|
|
|
172 |
$ancestors = scorm_get_ancestors($sco);
|
|
|
173 |
$commonpos = scorm_find_common_ancestor($ancestors, $targetsco);
|
|
|
174 |
if ($commonpos !== false) {
|
|
|
175 |
if ($activitypath = array_slice($ancestors, 0, $commonpos)) {
|
|
|
176 |
foreach ($activitypath as $activity) {
|
|
|
177 |
if (scorm_seq_is('active', $activity->id, $userid) &&
|
|
|
178 |
(isset($activity->choiceexit) && ($activity->choiceexit == false))) {
|
|
|
179 |
$seq->navigation = false;
|
|
|
180 |
$seq->termination = null;
|
|
|
181 |
$seq->sequencing = null;
|
|
|
182 |
$seq->target = null;
|
|
|
183 |
$seq->exception = 'NB.2.1-8'; // Violates control mode.
|
|
|
184 |
return $seq;
|
|
|
185 |
}
|
|
|
186 |
}
|
|
|
187 |
} else {
|
|
|
188 |
$seq->navigation = false;
|
|
|
189 |
$seq->termination = null;
|
|
|
190 |
$seq->sequencing = null;
|
|
|
191 |
$seq->target = null;
|
|
|
192 |
$seq->exception = 'NB.2.1-9';
|
|
|
193 |
}
|
|
|
194 |
}
|
|
|
195 |
}
|
|
|
196 |
// Current activity is active !
|
|
|
197 |
$seq->navigation = true;
|
|
|
198 |
$seq->sequencing = 'choice';
|
|
|
199 |
}
|
|
|
200 |
} else {
|
|
|
201 |
$seq->exception = 'NB.2.1-10'; // Violates control mode.
|
|
|
202 |
}
|
|
|
203 |
} else {
|
|
|
204 |
$seq->exception = 'NB.2.1-11'; // Target activity does not exists.
|
|
|
205 |
}
|
|
|
206 |
break;
|
|
|
207 |
}
|
|
|
208 |
return $seq;
|
|
|
209 |
}
|
|
|
210 |
|
|
|
211 |
function scorm_seq_termination ($seq, $userid) {
|
|
|
212 |
if (empty($seq->currentactivity)) {
|
|
|
213 |
$seq->termination = false;
|
|
|
214 |
$seq->exception = 'TB.2.3-1';
|
|
|
215 |
return $seq;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
$sco = $seq->currentactivity;
|
|
|
219 |
|
|
|
220 |
if ((($seq->termination == 'exit') || ($seq->termination == 'abandon')) && !$seq->active) {
|
|
|
221 |
$seq->termination = false;
|
|
|
222 |
$seq->exception = 'TB.2.3-2';
|
|
|
223 |
return $seq;
|
|
|
224 |
}
|
|
|
225 |
switch ($seq->termination) {
|
|
|
226 |
case 'exit':
|
|
|
227 |
scorm_seq_end_attempt($sco, $userid, $seq);
|
|
|
228 |
$seq = scorm_seq_exit_action_rules($seq, $userid);
|
|
|
229 |
do {
|
|
|
230 |
$exit = false;// I think this is false. Originally this was true.
|
|
|
231 |
$seq = scorm_seq_post_cond_rules($seq, $userid);
|
|
|
232 |
if ($seq->termination == 'exitparent') {
|
|
|
233 |
if ($sco->parent != '/') {
|
|
|
234 |
$sco = scorm_get_parent($sco);
|
|
|
235 |
$seq->currentactivity = $sco;
|
|
|
236 |
$seq->active = scorm_seq_is('active', $sco->id, $userid);
|
|
|
237 |
scorm_seq_end_attempt($sco, $userid, $seq);
|
|
|
238 |
$exit = true; // I think it's true. Originally this was false.
|
|
|
239 |
} else {
|
|
|
240 |
$seq->termination = false;
|
|
|
241 |
$seq->exception = 'TB.2.3-4';
|
|
|
242 |
return $seq;
|
|
|
243 |
}
|
|
|
244 |
}
|
|
|
245 |
} while (($exit == false) && ($seq->termination == 'exit'));
|
|
|
246 |
if ($seq->termination == 'exit') {
|
|
|
247 |
$seq->termination = true;
|
|
|
248 |
return $seq;
|
|
|
249 |
}
|
|
|
250 |
case 'exitall':
|
|
|
251 |
if ($seq->active) {
|
|
|
252 |
scorm_seq_end_attempt($sco, $userid, $seq);
|
|
|
253 |
}
|
|
|
254 |
// Terminate Descendent Attempts Process.
|
|
|
255 |
|
|
|
256 |
if ($ancestors = scorm_get_ancestors($sco)) {
|
|
|
257 |
foreach ($ancestors as $ancestor) {
|
|
|
258 |
scorm_seq_end_attempt($ancestor, $userid, $seq);
|
|
|
259 |
$seq->currentactivity = $ancestor;
|
|
|
260 |
}
|
|
|
261 |
}
|
|
|
262 |
|
|
|
263 |
$seq->active = scorm_seq_is('active', $seq->currentactivity->id, $userid);
|
|
|
264 |
$seq->termination = true;
|
|
|
265 |
$seq->sequencing = 'exit';
|
|
|
266 |
break;
|
|
|
267 |
case 'suspendall':
|
|
|
268 |
if (($seq->active) || ($seq->suspended)) {
|
|
|
269 |
scorm_seq_set('suspended', $sco->id, $userid, $attempt);
|
|
|
270 |
} else {
|
|
|
271 |
if ($sco->parent != '/') {
|
|
|
272 |
$parentsco = scorm_get_parent($sco);
|
|
|
273 |
scorm_seq_set('suspended', $parentsco->id, $userid, $attempt);
|
|
|
274 |
} else {
|
|
|
275 |
$seq->termination = false;
|
|
|
276 |
$seq->exception = 'TB.2.3-3';
|
|
|
277 |
}
|
|
|
278 |
}
|
|
|
279 |
if ($ancestors = scorm_get_ancestors($sco)) {
|
|
|
280 |
foreach ($ancestors as $ancestor) {
|
|
|
281 |
scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);
|
|
|
282 |
scorm_seq_set('suspended', $ancestor->id, $userid, $attempt);
|
|
|
283 |
$seq->currentactivity = $ancestor;
|
|
|
284 |
}
|
|
|
285 |
$seq->termination = true;
|
|
|
286 |
$seq->sequencing = 'exit';
|
|
|
287 |
} else {
|
|
|
288 |
$seq->termination = false;
|
|
|
289 |
$seq->exception = 'TB.2.3-5';
|
|
|
290 |
}
|
|
|
291 |
break;
|
|
|
292 |
case 'abandon':
|
|
|
293 |
scorm_seq_set('active', $sco->id, $userid, $attempt, false);
|
|
|
294 |
$seq->active = null;
|
|
|
295 |
$seq->termination = true;
|
|
|
296 |
break;
|
|
|
297 |
case 'abandonall':
|
|
|
298 |
if ($ancestors = scorm_get_ancestors($sco)) {
|
|
|
299 |
foreach ($ancestors as $ancestor) {
|
|
|
300 |
scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);
|
|
|
301 |
$seq->currentactivity = $ancestor;
|
|
|
302 |
}
|
|
|
303 |
$seq->termination = true;
|
|
|
304 |
$seq->sequencing = 'exit';
|
|
|
305 |
} else {
|
|
|
306 |
$seq->termination = false;
|
|
|
307 |
$seq->exception = 'TB.2.3-6';
|
|
|
308 |
}
|
|
|
309 |
break;
|
|
|
310 |
default:
|
|
|
311 |
$seq->termination = false;
|
|
|
312 |
$seq->exception = 'TB.2.3-7';
|
|
|
313 |
break;
|
|
|
314 |
}
|
|
|
315 |
return $seq;
|
|
|
316 |
}
|
|
|
317 |
|
|
|
318 |
function scorm_seq_end_attempt($sco, $userid, $seq) {
|
|
|
319 |
global $DB;
|
|
|
320 |
if (scorm_is_leaf($sco)) {
|
|
|
321 |
if (!isset($sco->tracked) || ($sco->tracked == 1)) {
|
|
|
322 |
if (!scorm_seq_is('suspended', $sco->id, $userid)) {
|
|
|
323 |
if (!isset($sco->completionsetbycontent) || ($sco->completionsetbycontent == 0)) {
|
|
|
324 |
if (!scorm_seq_is('attemptprogressstatus', $sco->id, $userid, $seq->attempt)) {
|
|
|
325 |
$r = scorm_get_sco_value($sco->id, $userid, 'cmi.completion_status');
|
|
|
326 |
if ($r->value != 'incomplete') {
|
|
|
327 |
scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt);
|
|
|
328 |
scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt);
|
|
|
329 |
}
|
|
|
330 |
}
|
|
|
331 |
}
|
|
|
332 |
if (!isset($sco->objectivesetbycontent) || ($sco->objectivesetbycontent == 0)) {
|
|
|
333 |
if ($objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id))) {
|
|
|
334 |
foreach ($objectives as $objective) {
|
|
|
335 |
if ($objective->primaryobj) {
|
|
|
336 |
if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $seq->attempt)) {
|
|
|
337 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $seq->attempt);
|
|
|
338 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $seq->attempt);
|
|
|
339 |
}
|
|
|
340 |
}
|
|
|
341 |
}
|
|
|
342 |
}
|
|
|
343 |
}
|
|
|
344 |
}
|
|
|
345 |
}
|
|
|
346 |
} else if ($children = scorm_get_children($sco)) {
|
|
|
347 |
$suspended = false;
|
|
|
348 |
foreach ($children as $child) {
|
|
|
349 |
if (scorm_seq_is('suspended', $child, $userid, $seq->attempt)) {
|
|
|
350 |
$suspended = true;
|
|
|
351 |
break;
|
|
|
352 |
}
|
|
|
353 |
}
|
|
|
354 |
if ($suspended) {
|
|
|
355 |
scorm_seq_set('suspended', $sco, $userid, $seq->attempt);
|
|
|
356 |
} else {
|
|
|
357 |
scorm_seq_set('suspended', $sco, $userid, $seq->attempt, false);
|
|
|
358 |
}
|
|
|
359 |
}
|
|
|
360 |
scorm_seq_set('active', $sco->id, $userid, $seq->attempt, false);
|
|
|
361 |
scorm_seq_overall_rollup($sco, $userid, $seq);
|
|
|
362 |
}
|
|
|
363 |
|
|
|
364 |
function scorm_seq_is($what, $scoid, $userid, $attempt=0) {
|
|
|
365 |
// Check if passed activity $what is active.
|
|
|
366 |
$active = false;
|
|
|
367 |
if (scorm_get_sco_value($scoid, $userid, $what, $attempt)) {
|
|
|
368 |
$active = true;
|
|
|
369 |
}
|
|
|
370 |
return $active;
|
|
|
371 |
}
|
|
|
372 |
|
|
|
373 |
function scorm_seq_set($what, $scoid, $userid, $attempt=0, $value='true') {
|
|
|
374 |
global $DB;
|
|
|
375 |
|
|
|
376 |
$sco = scorm_get_sco($scoid);
|
|
|
377 |
|
|
|
378 |
// Set passed activity to active or not.
|
|
|
379 |
if ($value == false) {
|
|
|
380 |
$params = ['userid' => $userid, 'scormid' => $sco->scorm, 'attempt' => $attempt, 'element' => $what];
|
|
|
381 |
$sql = "WHERE scoid = :scoid AND attemptid = :attemptid AND elementid = (SELECT id
|
|
|
382 |
FROM {scorm_element}
|
|
|
383 |
WHERE element = :element)";
|
|
|
384 |
$DB->delete_records_select('scorm_scoes_value', $sql, $params);
|
|
|
385 |
} else {
|
|
|
386 |
scorm_insert_track($userid, $sco->scorm, $sco->id, $attempt, $what, $value);
|
|
|
387 |
}
|
|
|
388 |
|
|
|
389 |
// Update grades in gradebook.
|
|
|
390 |
$scorm = $DB->get_record('scorm', array('id' => $sco->scorm));
|
|
|
391 |
scorm_update_grades($scorm, $userid, true);
|
|
|
392 |
}
|
|
|
393 |
|
|
|
394 |
function scorm_evaluate_condition ($rollupruleconds, $sco, $userid) {
|
|
|
395 |
global $DB;
|
|
|
396 |
|
|
|
397 |
$res = false;
|
|
|
398 |
|
|
|
399 |
if (strpos($rollupruleconds, 'and ')) {
|
|
|
400 |
$rollupruleconds = array_filter(explode(' and ', $rollupruleconds));
|
|
|
401 |
$conditioncombination = 'all';
|
|
|
402 |
} else {
|
|
|
403 |
$rollupruleconds = array_filter(explode(' or ', $rollupruleconds));
|
|
|
404 |
$conditioncombination = 'or';
|
|
|
405 |
}
|
|
|
406 |
foreach ($rollupruleconds as $rolluprulecond) {
|
|
|
407 |
$notflag = false;
|
|
|
408 |
if (strpos($rolluprulecond, 'not') !== false) {
|
|
|
409 |
$rolluprulecond = str_replace('not', '', $rolluprulecond);
|
|
|
410 |
$notflag = true;
|
|
|
411 |
}
|
|
|
412 |
$conditionarray['condition'] = $rolluprulecond;
|
|
|
413 |
$conditionarray['notflag'] = $notflag;
|
|
|
414 |
$conditions[] = $conditionarray;
|
|
|
415 |
}
|
|
|
416 |
foreach ($conditions as $condition) {
|
|
|
417 |
$checknot = true;
|
|
|
418 |
$res = false;
|
|
|
419 |
if ($condition['notflag']) {
|
|
|
420 |
$checknot = false;
|
|
|
421 |
}
|
|
|
422 |
switch ($condition['condition']) {
|
|
|
423 |
case 'satisfied':
|
|
|
424 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectivesatisfiedstatus');
|
|
|
425 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
426 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');
|
|
|
427 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
428 |
$res = true;
|
|
|
429 |
}
|
|
|
430 |
}
|
|
|
431 |
break;
|
|
|
432 |
|
|
|
433 |
case 'objectiveStatusKnown':
|
|
|
434 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');
|
|
|
435 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
436 |
$res = true;
|
|
|
437 |
}
|
|
|
438 |
break;
|
|
|
439 |
|
|
|
440 |
case 'notobjectiveStatusKnown':
|
|
|
441 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');
|
|
|
442 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
443 |
$res = true;
|
|
|
444 |
}
|
|
|
445 |
break;
|
|
|
446 |
|
|
|
447 |
case 'objectiveMeasureKnown':
|
|
|
448 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectivemeasurestatus');
|
|
|
449 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
450 |
$res = true;
|
|
|
451 |
}
|
|
|
452 |
break;
|
|
|
453 |
|
|
|
454 |
case 'notobjectiveMeasureKnown':
|
|
|
455 |
$r = scorm_get_sco_value($sco->id, $userid, 'objectivemeasurestatus');
|
|
|
456 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
457 |
$res = true;
|
|
|
458 |
}
|
|
|
459 |
break;
|
|
|
460 |
|
|
|
461 |
case 'completed':
|
|
|
462 |
$r = scorm_get_sco_value($sco->id, $userid, 'attemptcompletionstatus');
|
|
|
463 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
464 |
$r = scorm_get_sco_value($sco->id, $userid, 'attemptprogressstatus');
|
|
|
465 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
466 |
$res = true;
|
|
|
467 |
}
|
|
|
468 |
}
|
|
|
469 |
break;
|
|
|
470 |
|
|
|
471 |
case 'attempted':
|
|
|
472 |
$r = scorm_get_sco_value($sco->id, $userid, 'x.start.time');
|
|
|
473 |
if ($checknot && $r->attempt > 0) {
|
|
|
474 |
$res = true;
|
|
|
475 |
} else if (!$checknot && $r->attempt <= 0) {
|
|
|
476 |
$res = true;
|
|
|
477 |
}
|
|
|
478 |
break;
|
|
|
479 |
|
|
|
480 |
case 'attemptLimitExceeded':
|
|
|
481 |
$r = scorm_get_sco_value($sco->id, $userid, 'activityprogressstatus');
|
|
|
482 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
483 |
$r = scorm_get_sco_value($sco->id, $userid, 'limitconditionattemptlimitcontrol');
|
|
|
484 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
485 |
$sql = "SELECT max(attempt) as attempt
|
|
|
486 |
FROM {scorm_attempt} a
|
|
|
487 |
JOIN {scorm_scoes_value} v on v.attemptid = a.id
|
|
|
488 |
WHERE v.scoid = :scoid AND a.userid = :userid";
|
|
|
489 |
$r2 = scorm_get_sco_value($sco->id, $userid, 'limitconditionattemptlimit');
|
|
|
490 |
$attempts = $DB->get_field_sql($sql, ['scoid' => $sco->id, 'userid' => $userid]);
|
|
|
491 |
if (!empty($attempts) && !empty($r2)) {
|
|
|
492 |
if ($checknot && ($attempts >= $r2->value)) {
|
|
|
493 |
$res = true;
|
|
|
494 |
} else if (!$checknot && ($attempts < $r2->value)) {
|
|
|
495 |
$res = true;
|
|
|
496 |
}
|
|
|
497 |
}
|
|
|
498 |
}
|
|
|
499 |
}
|
|
|
500 |
break;
|
|
|
501 |
|
|
|
502 |
case 'activityProgressKnown':
|
|
|
503 |
$r = scorm_get_sco_value($sco->id, $userid, 'activityprogressstatus');
|
|
|
504 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
505 |
$r = scorm_get_sco_value($sco->id, $userid, 'attemptprogressstatus');
|
|
|
506 |
if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {
|
|
|
507 |
$res = true;
|
|
|
508 |
}
|
|
|
509 |
}
|
|
|
510 |
break;
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
if ($conditioncombination == 'all' && !$res) {
|
|
|
514 |
break;
|
|
|
515 |
} else if ($conditioncombination == 'or' && $res) {
|
|
|
516 |
break;
|
|
|
517 |
}
|
|
|
518 |
}
|
|
|
519 |
|
|
|
520 |
return $res;
|
|
|
521 |
}
|
|
|
522 |
|
|
|
523 |
function scorm_check_activity ($activity, $userid) {
|
|
|
524 |
$act = scorm_seq_rules_check($activity, 'disabled');
|
|
|
525 |
if ($act != null) {
|
|
|
526 |
return true;
|
|
|
527 |
}
|
|
|
528 |
if (scorm_limit_cond_check ($activity, $userid)) {
|
|
|
529 |
return true;
|
|
|
530 |
}
|
|
|
531 |
return false;
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
function scorm_limit_cond_check ($activity, $userid) {
|
|
|
535 |
global $DB;
|
|
|
536 |
|
|
|
537 |
if (isset($activity->tracked) && ($activity->tracked == 0)) {
|
|
|
538 |
return false;
|
|
|
539 |
}
|
|
|
540 |
|
|
|
541 |
if (scorm_seq_is('active', $activity->id, $userid) || scorm_seq_is('suspended', $activity->id, $userid)) {
|
|
|
542 |
return false;
|
|
|
543 |
}
|
|
|
544 |
|
|
|
545 |
if (!isset($activity->limitcontrol) || ($activity->limitcontrol == 1)) {
|
|
|
546 |
$r = scorm_get_sco_value($activity->id, $userid, 'activityattemptcount');
|
|
|
547 |
if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattempt)) {
|
|
|
548 |
return true;
|
|
|
549 |
}
|
|
|
550 |
}
|
|
|
551 |
|
|
|
552 |
if (!isset($activity->limitabsdurcontrol) || ($activity->limitabsdurcontrol == 1)) {
|
|
|
553 |
$r = scorm_get_sco_value($activity->id, $userid, 'activityabsoluteduration');
|
|
|
554 |
if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitabsduration)) {
|
|
|
555 |
return true;
|
|
|
556 |
}
|
|
|
557 |
}
|
|
|
558 |
|
|
|
559 |
if (!isset($activity->limitexpdurcontrol) || ($activity->limitexpdurcontrol == 1)) {
|
|
|
560 |
$r = scorm_get_sco_value($activity->id, $userid, 'activityexperiencedduration');
|
|
|
561 |
if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitexpduration)) {
|
|
|
562 |
return true;
|
|
|
563 |
}
|
|
|
564 |
}
|
|
|
565 |
|
|
|
566 |
if (!isset($activity->limitattabsdurcontrol) || ($activity->limitattabsdurcontrol == 1)) {
|
|
|
567 |
$r = scorm_get_sco_value($activity->id, $userid, 'attemptabsoluteduration');
|
|
|
568 |
if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattabsduration)) {
|
|
|
569 |
return true;
|
|
|
570 |
}
|
|
|
571 |
}
|
|
|
572 |
|
|
|
573 |
if (!isset($activity->limitattexpdurcontrol) || ($activity->limitattexpdurcontrol == 1)) {
|
|
|
574 |
$r = scorm_get_sco_value($activity->id, $userid, 'attemptexperiencedduration');
|
|
|
575 |
if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattexpduration)) {
|
|
|
576 |
return true;
|
|
|
577 |
}
|
|
|
578 |
}
|
|
|
579 |
|
|
|
580 |
if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {
|
|
|
581 |
$r = scorm_get_sco_value($activity->id, $userid, 'begintime');
|
|
|
582 |
if (isset($activity->limitbegintime) && time() >= $activity->limitbegintime) {
|
|
|
583 |
return true;
|
|
|
584 |
}
|
|
|
585 |
}
|
|
|
586 |
|
|
|
587 |
if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {
|
|
|
588 |
if (isset($activity->limitbegintime) && time() < $activity->limitbegintime) {
|
|
|
589 |
return true;
|
|
|
590 |
}
|
|
|
591 |
}
|
|
|
592 |
|
|
|
593 |
if (!isset($activity->limitendcontrol) || ($activity->limitendcontrol == 1)) {
|
|
|
594 |
if (isset($activity->limitendtime) && time() > $activity->limitendtime) {
|
|
|
595 |
return true;
|
|
|
596 |
}
|
|
|
597 |
}
|
|
|
598 |
return false;
|
|
|
599 |
}
|
|
|
600 |
|
|
|
601 |
function scorm_seq_rules_check ($sco, $action) {
|
|
|
602 |
global $DB;
|
|
|
603 |
$act = null;
|
|
|
604 |
|
|
|
605 |
if ($rules = $DB->get_records('scorm_seq_ruleconds', array('scoid' => $sco->id, 'action' => $action))) {
|
|
|
606 |
foreach ($rules as $rule) {
|
|
|
607 |
if ($act = scorm_seq_rule_check($sco, $rule)) {
|
|
|
608 |
return $act;
|
|
|
609 |
}
|
|
|
610 |
}
|
|
|
611 |
}
|
|
|
612 |
return $act;
|
|
|
613 |
|
|
|
614 |
}
|
|
|
615 |
|
|
|
616 |
function scorm_seq_rule_check ($sco, $rule) {
|
|
|
617 |
global $DB;
|
|
|
618 |
|
|
|
619 |
$bag = Array();
|
|
|
620 |
$cond = '';
|
|
|
621 |
$ruleconds = $DB->get_records('scorm_seq_rulecond', array('scoid' => $sco->id, 'ruleconditionsid' => $rule->id));
|
|
|
622 |
foreach ($ruleconds as $rulecond) {
|
|
|
623 |
if ($rulecond->operator == 'not') {
|
|
|
624 |
if ($rulecond->cond != 'unknown' ) {
|
|
|
625 |
$rulecond->cond = 'not'.$rulecond->cond;
|
|
|
626 |
}
|
|
|
627 |
}
|
|
|
628 |
$bag[] = $rulecond->cond;
|
|
|
629 |
}
|
|
|
630 |
if (empty($bag)) {
|
|
|
631 |
$cond = 'unknown';
|
|
|
632 |
return $cond;
|
|
|
633 |
}
|
|
|
634 |
|
|
|
635 |
if ($rule->conditioncombination == 'all') {
|
|
|
636 |
foreach ($bag as $con) {
|
|
|
637 |
$cond = $cond.' and '.$con;
|
|
|
638 |
}
|
|
|
639 |
} else {
|
|
|
640 |
foreach ($bag as $con) {
|
|
|
641 |
$cond = $cond.' or '.$con;
|
|
|
642 |
}
|
|
|
643 |
}
|
|
|
644 |
return $cond;
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
function scorm_seq_overall_rollup($sco, $userid, $seq) {
|
|
|
648 |
if ($ancestors = scorm_get_ancestors($sco)) {
|
|
|
649 |
foreach ($ancestors as $ancestor) {
|
|
|
650 |
if (!scorm_is_leaf($ancestor)) {
|
|
|
651 |
scorm_seq_measure_rollup($sco, $userid, $seq->attempt);
|
|
|
652 |
}
|
|
|
653 |
scorm_seq_objective_rollup($sco, $userid, $seq->attempt);
|
|
|
654 |
scorm_seq_activity_progress_rollup($sco, $userid, $seq);
|
|
|
655 |
}
|
|
|
656 |
}
|
|
|
657 |
}
|
|
|
658 |
|
|
|
659 |
function scorm_seq_measure_rollup($sco, $userid, $attempt = 0) {
|
|
|
660 |
global $DB;
|
|
|
661 |
|
|
|
662 |
$totalmeasure = 0; // Check if there is something similar in the database.
|
|
|
663 |
$valid = false; // Same as in the last line.
|
|
|
664 |
$countedmeasures = 0; // Same too.
|
|
|
665 |
$targetobjective = null;
|
|
|
666 |
$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));
|
|
|
667 |
|
|
|
668 |
foreach ($objectives as $objective) {
|
|
|
669 |
if ($objective->primaryobj == true) { // Objective contributes to rollup.
|
|
|
670 |
$targetobjective = $objective;
|
|
|
671 |
break;
|
|
|
672 |
}
|
|
|
673 |
|
|
|
674 |
}
|
|
|
675 |
if ($targetobjective != null) {
|
|
|
676 |
$children = scorm_get_children($sco);
|
|
|
677 |
if (!empty ($children)) {
|
|
|
678 |
foreach ($children as $child) {
|
|
|
679 |
$child = scorm_get_sco ($child);
|
|
|
680 |
if (!isset($child->tracked) || ($child->tracked == 1)) {
|
|
|
681 |
$rolledupobjective = null;// We set the rolled up activity to undefined.
|
|
|
682 |
$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $child->id));
|
|
|
683 |
foreach ($objectives as $objective) {
|
|
|
684 |
if ($objective->primaryobj == true) {// Objective contributes to rollup I'm using primaryobj field, but not.
|
|
|
685 |
$rolledupobjective = $objective;
|
|
|
686 |
break;
|
|
|
687 |
}
|
|
|
688 |
}
|
|
|
689 |
if ($rolledupobjective != null) {
|
|
|
690 |
$child = scorm_get_sco($child->id);
|
|
|
691 |
$countedmeasures = $countedmeasures + ($child->measureweight);
|
|
|
692 |
if (!scorm_seq_is('objectivemeasurestatus', $sco->id, $userid, $attempt)) {
|
|
|
693 |
$normalizedmeasure = scorm_get_sco_value($child->id, $userid, 'objectivenormalizedmeasure');
|
|
|
694 |
$totalmeasure = $totalmeasure + (($normalizedmeasure->value) * ($child->measureweight));
|
|
|
695 |
$valid = true;
|
|
|
696 |
}
|
|
|
697 |
}
|
|
|
698 |
}
|
|
|
699 |
}
|
|
|
700 |
}
|
|
|
701 |
|
|
|
702 |
if (!$valid) {
|
|
|
703 |
scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);
|
|
|
704 |
} else {
|
|
|
705 |
if ($countedmeasures > 0) {
|
|
|
706 |
scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt);
|
|
|
707 |
$val = $totalmeasure / $countedmeasures;
|
|
|
708 |
scorm_seq_set('objectivenormalizedmeasure', $sco->id, $userid, $attempt, $val);
|
|
|
709 |
} else {
|
|
|
710 |
scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);
|
|
|
711 |
}
|
|
|
712 |
}
|
|
|
713 |
}
|
|
|
714 |
}
|
|
|
715 |
|
|
|
716 |
function scorm_seq_objective_rollup($sco, $userid, $attempt = 0) {
|
|
|
717 |
global $DB;
|
|
|
718 |
|
|
|
719 |
scorm_seq_objective_rollup_measure($sco, $userid, $attempt);
|
|
|
720 |
scorm_seq_objective_rollup_rules($sco, $userid, $attempt);
|
|
|
721 |
scorm_seq_objective_rollup_default($sco, $userid, $attempt);
|
|
|
722 |
|
|
|
723 |
/*
|
|
|
724 |
if ($targetobjective->satisfiedbymeasure) {
|
|
|
725 |
scorm_seq_objective_rollup_measure($sco, $userid);
|
|
|
726 |
}
|
|
|
727 |
else{
|
|
|
728 |
if ((scorm_seq_rollup_rule_check($sco, $userid, 'incomplete'))
|
|
|
729 |
|| (scorm_seq_rollup_rule_check($sco, $userid, 'completed'))) {
|
|
|
730 |
scorm_seq_objective_rollup_rules($sco, $userid);
|
|
|
731 |
}
|
|
|
732 |
else{
|
|
|
733 |
|
|
|
734 |
$rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid'=>$sco->id, 'userid'=>$userid));
|
|
|
735 |
foreach ($rolluprules as $rolluprule) {
|
|
|
736 |
$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid'=>$rolluprule->id));
|
|
|
737 |
foreach ($rollupruleconds as $rolluprulecond) {
|
|
|
738 |
|
|
|
739 |
switch ($rolluprulecond->cond!='satisfied'
|
|
|
740 |
&& $rolluprulecond->cond!='completed' && $rolluprulecond->cond!='attempted') {
|
|
|
741 |
|
|
|
742 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, false);
|
|
|
743 |
|
|
|
744 |
break;
|
|
|
745 |
}
|
|
|
746 |
}
|
|
|
747 |
|
|
|
748 |
|
|
|
749 |
}
|
|
|
750 |
}
|
|
|
751 |
*/
|
|
|
752 |
}
|
|
|
753 |
|
|
|
754 |
function scorm_seq_objective_rollup_measure($sco, $userid, $attempt = 0) {
|
|
|
755 |
global $DB;
|
|
|
756 |
|
|
|
757 |
$targetobjective = null;
|
|
|
758 |
|
|
|
759 |
$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));
|
|
|
760 |
foreach ($objectives as $objective) {
|
|
|
761 |
if ($objective->primaryobj == true) {
|
|
|
762 |
$targetobjective = $objective;
|
|
|
763 |
break;
|
|
|
764 |
}
|
|
|
765 |
}
|
|
|
766 |
if ($targetobjective != null) {
|
|
|
767 |
if ($targetobjective->satisfiedbymeasure) {
|
|
|
768 |
if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $attempt)) {
|
|
|
769 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt, false);
|
|
|
770 |
} else {
|
|
|
771 |
if (scorm_seq_is('active', $sco->id, $userid, $attempt)) {
|
|
|
772 |
$isactive = true;
|
|
|
773 |
} else {
|
|
|
774 |
$isactive = false;
|
|
|
775 |
}
|
|
|
776 |
$normalizedmeasure = scorm_get_sco_value($sco->id, $userid, 'objectivenormalizedmeasure');
|
|
|
777 |
|
|
|
778 |
$sco = scorm_get_sco ($sco->id);
|
|
|
779 |
|
|
|
780 |
if (!$isactive || ($isactive &&
|
|
|
781 |
(!isset($sco->measuresatisfactionifactive) || $sco->measuresatisfactionifactive == true))) {
|
|
|
782 |
if (isset($normalizedmeasure->value) && ($normalizedmeasure->value >= $targetobjective->minnormalizedmeasure)) {
|
|
|
783 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);
|
|
|
784 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt);
|
|
|
785 |
} else {
|
|
|
786 |
// TODO: handle the case where cmi.success_status is passed and objectivenormalizedmeasure undefined.
|
|
|
787 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);
|
|
|
788 |
}
|
|
|
789 |
} else {
|
|
|
790 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt, false);
|
|
|
791 |
}
|
|
|
792 |
}
|
|
|
793 |
}
|
|
|
794 |
}
|
|
|
795 |
}
|
|
|
796 |
|
|
|
797 |
function scorm_seq_objective_rollup_default($sco, $userid, $attempt = 0) {
|
|
|
798 |
global $DB;
|
|
|
799 |
|
|
|
800 |
if (!(scorm_seq_rollup_rule_check($sco, $userid, 'incomplete')) && !(scorm_seq_rollup_rule_check($sco, $userid, 'completed'))) {
|
|
|
801 |
if ($rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid' => $sco->id))) {
|
|
|
802 |
foreach ($rolluprules as $rolluprule) {
|
|
|
803 |
$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid' => $rolluprule->id));
|
|
|
804 |
foreach ($rollupruleconds as $rolluprulecond) {
|
|
|
805 |
if ($rolluprulecond->cond != 'satisfied' && $rolluprulecond->cond != 'completed' &&
|
|
|
806 |
$rolluprulecond->cond != 'attempted') {
|
|
|
807 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt, false);
|
|
|
808 |
break;
|
|
|
809 |
}
|
|
|
810 |
}
|
|
|
811 |
}
|
|
|
812 |
}
|
|
|
813 |
}
|
|
|
814 |
}
|
|
|
815 |
|
|
|
816 |
|
|
|
817 |
function scorm_seq_objective_rollup_rules($sco, $userid, $attempt = 0) {
|
|
|
818 |
global $DB;
|
|
|
819 |
|
|
|
820 |
$targetobjective = null;
|
|
|
821 |
|
|
|
822 |
$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));
|
|
|
823 |
foreach ($objectives as $objective) {
|
|
|
824 |
if ($objective->primaryobj == true) {// Objective contributes to rollup I'm using primaryobj field, but not.
|
|
|
825 |
$targetobjective = $objective;
|
|
|
826 |
break;
|
|
|
827 |
}
|
|
|
828 |
}
|
|
|
829 |
if ($targetobjective != null) {
|
|
|
830 |
|
|
|
831 |
if (scorm_seq_rollup_rule_check($sco, $userid, 'notsatisfied')) {// With not satisfied rollup for the activity.
|
|
|
832 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);
|
|
|
833 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt, false);
|
|
|
834 |
}
|
|
|
835 |
if (scorm_seq_rollup_rule_check($sco, $userid, 'satisfied')) {// With satisfied rollup for the activity.
|
|
|
836 |
scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);
|
|
|
837 |
scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt);
|
|
|
838 |
}
|
|
|
839 |
|
|
|
840 |
}
|
|
|
841 |
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
function scorm_seq_activity_progress_rollup ($sco, $userid, $seq) {
|
|
|
845 |
|
|
|
846 |
if (scorm_seq_rollup_rule_check($sco, $userid, 'incomplete')) {
|
|
|
847 |
// Incomplete rollup action.
|
|
|
848 |
scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt, false);
|
|
|
849 |
scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt, true);
|
|
|
850 |
|
|
|
851 |
}
|
|
|
852 |
if (scorm_seq_rollup_rule_check($sco, $userid, 'completed')) {
|
|
|
853 |
// Incomplete rollup action.
|
|
|
854 |
scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt, true);
|
|
|
855 |
scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt, true);
|
|
|
856 |
}
|
|
|
857 |
|
|
|
858 |
}
|
|
|
859 |
|
|
|
860 |
function scorm_seq_rollup_rule_check ($sco, $userid, $action) {
|
|
|
861 |
global $DB;
|
|
|
862 |
|
|
|
863 |
if ($rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid' => $sco->id, 'action' => $action))) {
|
|
|
864 |
$childrenbag = Array ();
|
|
|
865 |
$children = scorm_get_children ($sco);
|
|
|
866 |
|
|
|
867 |
foreach ($rolluprules as $rolluprule) {
|
|
|
868 |
foreach ($children as $child) {
|
|
|
869 |
|
|
|
870 |
$child = scorm_get_sco ($child);
|
|
|
871 |
if (!isset($child->tracked) || ($child->tracked == 1)) {
|
|
|
872 |
if (scorm_seq_check_child ($child, $action, $userid)) {
|
|
|
873 |
$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid' => $rolluprule->id));
|
|
|
874 |
$evaluate = scorm_seq_evaluate_rollupcond($child, $rolluprule->conditioncombination,
|
|
|
875 |
$rollupruleconds, $userid);
|
|
|
876 |
if ($evaluate == 'unknown') {
|
|
|
877 |
array_push($childrenbag, 'unknown');
|
|
|
878 |
} else {
|
|
|
879 |
if ($evaluate == true) {
|
|
|
880 |
array_push($childrenbag, true);
|
|
|
881 |
} else {
|
|
|
882 |
array_push($childrenbag, false);
|
|
|
883 |
}
|
|
|
884 |
}
|
|
|
885 |
}
|
|
|
886 |
}
|
|
|
887 |
|
|
|
888 |
}
|
|
|
889 |
$change = false;
|
|
|
890 |
|
|
|
891 |
switch ($rolluprule->childactivityset) {
|
|
|
892 |
|
|
|
893 |
case 'all':
|
|
|
894 |
// I think I can use this condition instead equivalent to OR.
|
|
|
895 |
if ((array_search(false, $childrenbag) === false) && (array_search('unknown', $childrenbag) === false)) {
|
|
|
896 |
$change = true;
|
|
|
897 |
}
|
|
|
898 |
break;
|
|
|
899 |
|
|
|
900 |
case 'any':
|
|
|
901 |
// I think I can use this condition instead equivalent to OR.
|
|
|
902 |
if (array_search(true, $childrenbag) !== false) {
|
|
|
903 |
$change = true;
|
|
|
904 |
}
|
|
|
905 |
break;
|
|
|
906 |
|
|
|
907 |
case 'none':
|
|
|
908 |
// I think I can use this condition instead equivalent to OR.
|
|
|
909 |
if ((array_search(true, $childrenbag) === false) && (array_search('unknown', $childrenbag) === false)) {
|
|
|
910 |
$change = true;
|
|
|
911 |
}
|
|
|
912 |
break;
|
|
|
913 |
|
|
|
914 |
case 'atleastcount':
|
|
|
915 |
// I think I can use this condition instead equivalent to OR.
|
|
|
916 |
foreach ($childrenbag as $itm) {
|
|
|
917 |
$cont = 0;
|
|
|
918 |
if ($itm === true) {
|
|
|
919 |
$cont++;
|
|
|
920 |
}
|
|
|
921 |
if ($cont >= $rolluprule->minimumcount) {
|
|
|
922 |
$change = true;
|
|
|
923 |
}
|
|
|
924 |
}
|
|
|
925 |
break;
|
|
|
926 |
|
|
|
927 |
case 'atleastcount':
|
|
|
928 |
foreach ($childrenbag as $itm) {// I think I can use this condition instead equivalent to OR.
|
|
|
929 |
$cont = 0;
|
|
|
930 |
if ($itm === true) {
|
|
|
931 |
$cont++;
|
|
|
932 |
}
|
|
|
933 |
if ($cont >= $rolluprule->minimumcount) {
|
|
|
934 |
$change = true;
|
|
|
935 |
}
|
|
|
936 |
}
|
|
|
937 |
break;
|
|
|
938 |
|
|
|
939 |
case 'atleastpercent':
|
|
|
940 |
foreach ($childrenbag as $itm) {// I think I can use this condition instead equivalent to OR.
|
|
|
941 |
$cont = 0;
|
|
|
942 |
if ($itm === true) {
|
|
|
943 |
$cont++;
|
|
|
944 |
}
|
|
|
945 |
if (($cont / count($childrenbag)) >= $rolluprule->minimumcount) {
|
|
|
946 |
$change = true;
|
|
|
947 |
}
|
|
|
948 |
}
|
|
|
949 |
break;
|
|
|
950 |
}
|
|
|
951 |
if ($change == true) {
|
|
|
952 |
return true;
|
|
|
953 |
}
|
|
|
954 |
}
|
|
|
955 |
}
|
|
|
956 |
return false;
|
|
|
957 |
}
|
|
|
958 |
|
|
|
959 |
function scorm_seq_flow_tree_traversal($activity, $direction, $childrenflag, $prevdirection, $seq, $userid, $skip = false) {
|
|
|
960 |
$revdirection = false;
|
|
|
961 |
$parent = scorm_get_parent($activity);
|
|
|
962 |
if (!empty($parent)) {
|
|
|
963 |
$children = scorm_get_available_children($parent);
|
|
|
964 |
} else {
|
|
|
965 |
$children = array();
|
|
|
966 |
}
|
|
|
967 |
$childrensize = count($children);
|
|
|
968 |
|
|
|
969 |
if (($prevdirection != null && $prevdirection == 'backward') && ($children[$childrensize - 1]->id == $activity->id)) {
|
|
|
970 |
$direction = 'backward';
|
|
|
971 |
$activity = $children[0];
|
|
|
972 |
$revdirection = true;
|
|
|
973 |
}
|
|
|
974 |
|
|
|
975 |
if ($direction == 'forward') {
|
|
|
976 |
$ancestors = scorm_get_ancestors($activity);
|
|
|
977 |
$ancestorsroot = array_reverse($ancestors);
|
|
|
978 |
$preorder = array();
|
|
|
979 |
$preorder = scorm_get_preorder($preorder, $ancestorsroot[0]);
|
|
|
980 |
$preordersize = count($preorder);
|
|
|
981 |
if (($activity->id == $preorder[$preordersize - 1]->id) || (($activity->parent == '/') && !($childrenflag))) {
|
|
|
982 |
$seq->endsession = true;
|
|
|
983 |
$seq->nextactivity = null;
|
|
|
984 |
return $seq;
|
|
|
985 |
}
|
|
|
986 |
if (scorm_is_leaf ($activity) || !$childrenflag) {
|
|
|
987 |
if ($children[$childrensize - 1]->id == $activity->id) {
|
|
|
988 |
$seq = scorm_seq_flow_tree_traversal ($parent, $direction, false, null, $seq, $userid);
|
|
|
989 |
if ($seq->nextactivity->launch == null) {
|
|
|
990 |
$seq = scorm_seq_flow_tree_traversal ($seq->nextactivity, $direction, true, null, $seq, $userid);
|
|
|
991 |
}
|
|
|
992 |
return $seq;
|
|
|
993 |
} else {
|
|
|
994 |
$position = 0;
|
|
|
995 |
foreach ($children as $sco) {
|
|
|
996 |
if ($sco->id == $activity->id) {
|
|
|
997 |
break;
|
|
|
998 |
}
|
|
|
999 |
$position++;
|
|
|
1000 |
}
|
|
|
1001 |
if ($position != ($childrensize - 1)) {
|
|
|
1002 |
$seq->nextactivity = $children[$position + 1];
|
|
|
1003 |
$seq->traversaldir = $direction;
|
|
|
1004 |
return $seq;
|
|
|
1005 |
} else {
|
|
|
1006 |
$siblings = scorm_get_siblings($activity);
|
|
|
1007 |
$children = scorm_get_children($siblings[0]);
|
|
|
1008 |
$seq->nextactivity = $children[0];
|
|
|
1009 |
return $seq;
|
|
|
1010 |
}
|
|
|
1011 |
}
|
|
|
1012 |
} else {
|
|
|
1013 |
$children = scorm_get_available_children($activity);
|
|
|
1014 |
if (!empty($children)) {
|
|
|
1015 |
$seq->traversaldir = $direction;
|
|
|
1016 |
$seq->nextactivity = $children[0];
|
|
|
1017 |
return $seq;
|
|
|
1018 |
} else {
|
|
|
1019 |
$seq->traversaldir = null;
|
|
|
1020 |
$seq->nextactivity = null;
|
|
|
1021 |
$seq->exception = 'SB.2.1-2';
|
|
|
1022 |
return $seq;
|
|
|
1023 |
}
|
|
|
1024 |
}
|
|
|
1025 |
} else if ($direction == 'backward') {
|
|
|
1026 |
if ($activity->parent == '/') {
|
|
|
1027 |
$seq->traversaldir = null;
|
|
|
1028 |
$seq->nextactivity = null;
|
|
|
1029 |
$seq->exception = 'SB.2.1-3';
|
|
|
1030 |
return $seq;
|
|
|
1031 |
}
|
|
|
1032 |
if (scorm_is_leaf ($activity) || !$childrenflag) {
|
|
|
1033 |
if (!$revdirection) {
|
|
|
1034 |
if (isset($parent->forwardonly) && ($parent->forwardonly == true && !$skip)) {
|
|
|
1035 |
$seq->traversaldir = null;
|
|
|
1036 |
$seq->nextactivity = null;
|
|
|
1037 |
$seq->exception = 'SB.2.1-4';
|
|
|
1038 |
return $seq;
|
|
|
1039 |
}
|
|
|
1040 |
}
|
|
|
1041 |
if ($children[0]->id == $activity->id) {
|
|
|
1042 |
$seq = scorm_seq_flow_tree_traversal($parent, 'backward', false, null, $seq, $userid);
|
|
|
1043 |
return $seq;
|
|
|
1044 |
} else {
|
|
|
1045 |
$ancestors = scorm_get_ancestors($activity);
|
|
|
1046 |
$ancestorsroot = array_reverse($ancestors);
|
|
|
1047 |
$preorder = array();
|
|
|
1048 |
$preorder = scorm_get_preorder($preorder, $ancestorsroot[0]);
|
|
|
1049 |
$position = 0;
|
|
|
1050 |
foreach ($preorder as $sco) {
|
|
|
1051 |
if ($sco->id == $activity->id) {
|
|
|
1052 |
break;
|
|
|
1053 |
}
|
|
|
1054 |
$position++;
|
|
|
1055 |
}
|
|
|
1056 |
if (isset($preorder[$position])) {
|
|
|
1057 |
$seq->nextactivity = $preorder[$position - 1];
|
|
|
1058 |
$seq->traversaldir = $direction;
|
|
|
1059 |
}
|
|
|
1060 |
return $seq;
|
|
|
1061 |
}
|
|
|
1062 |
} else {
|
|
|
1063 |
$children = scorm_get_available_children($activity);
|
|
|
1064 |
if (!empty($children)) {
|
|
|
1065 |
if (isset($parent->flow) && ($parent->flow == true)) {
|
|
|
1066 |
$seq->traversaldir = 'forward';
|
|
|
1067 |
$seq->nextactivity = $children[0];
|
|
|
1068 |
return $seq;
|
|
|
1069 |
} else {
|
|
|
1070 |
$seq->traversaldir = 'backward';
|
|
|
1071 |
$seq->nextactivity = $children[count($children) - 1];
|
|
|
1072 |
return $seq;
|
|
|
1073 |
}
|
|
|
1074 |
} else {
|
|
|
1075 |
$seq->traversaldir = null;
|
|
|
1076 |
$seq->nextactivity = null;
|
|
|
1077 |
$seq->exception = 'SB.2.1-2';
|
|
|
1078 |
return $seq;
|
|
|
1079 |
}
|
|
|
1080 |
}
|
|
|
1081 |
}
|
|
|
1082 |
}
|
|
|
1083 |
|
|
|
1084 |
// Returns the next activity on the tree, traversal direction, control returned to the LTS, (may) exception.
|
|
|
1085 |
function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $childrenflag, $prevdirection, $seq) {
|
|
|
1086 |
$parent = scorm_get_parent ($activity);
|
|
|
1087 |
if (!isset($parent->flow) || ($parent->flow == false)) {
|
|
|
1088 |
$seq->deliverable = false;
|
|
|
1089 |
$seq->exception = 'SB.2.2-1';
|
|
|
1090 |
$seq->nextactivity = $activity;
|
|
|
1091 |
return $seq;
|
|
|
1092 |
}
|
|
|
1093 |
|
|
|
1094 |
$rulecheck = scorm_seq_rules_check($activity, 'skip');
|
|
|
1095 |
if ($rulecheck != null) {
|
|
|
1096 |
$skip = scorm_evaluate_condition ($rulecheck, $activity, $userid);
|
|
|
1097 |
if ($skip) {
|
|
|
1098 |
$seq = scorm_seq_flow_tree_traversal($activity, $direction, false, $prevdirection, $seq, $userid, $skip);
|
|
|
1099 |
$seq = scorm_seq_flow_activity_traversal($seq->nextactivity, $userid, $direction,
|
|
|
1100 |
$childrenflag, $prevdirection, $seq);
|
|
|
1101 |
} else if (!empty($seq->identifiedactivity)) {
|
|
|
1102 |
$seq->nextactivity = $activity;
|
|
|
1103 |
}
|
|
|
1104 |
return $seq;
|
|
|
1105 |
}
|
|
|
1106 |
|
|
|
1107 |
$ch = scorm_check_activity ($activity, $userid);
|
|
|
1108 |
if ($ch) {
|
|
|
1109 |
$seq->deliverable = false;
|
|
|
1110 |
$seq->exception = 'SB.2.2-2';
|
|
|
1111 |
$seq->nextactivity = $activity;
|
|
|
1112 |
return $seq;
|
|
|
1113 |
}
|
|
|
1114 |
|
|
|
1115 |
if (!scorm_is_leaf($activity)) {
|
|
|
1116 |
$seq = scorm_seq_flow_tree_traversal ($activity, $direction, true, null, $seq, $userid);
|
|
|
1117 |
if ($seq->identifiedactivity == null) {
|
|
|
1118 |
$seq->deliverable = false;
|
|
|
1119 |
$seq->nextactivity = $activity;
|
|
|
1120 |
return $seq;
|
|
|
1121 |
} else {
|
|
|
1122 |
if ($direction == 'backward' && $seq->traversaldir == 'forward') {
|
|
|
1123 |
$seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,
|
|
|
1124 |
'forward', $childrenflag, 'backward', $seq);
|
|
|
1125 |
} else {
|
|
|
1126 |
$seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,
|
|
|
1127 |
$direction, $childrenflag, null, $seq);
|
|
|
1128 |
}
|
|
|
1129 |
return $seq;
|
|
|
1130 |
}
|
|
|
1131 |
|
|
|
1132 |
}
|
|
|
1133 |
|
|
|
1134 |
$seq->deliverable = true;
|
|
|
1135 |
$seq->nextactivity = $activity;
|
|
|
1136 |
$seq->exception = null;
|
|
|
1137 |
return $seq;
|
|
|
1138 |
|
|
|
1139 |
}
|
|
|
1140 |
|
|
|
1141 |
function scorm_seq_flow ($activity, $direction, $seq, $childrenflag, $userid) {
|
|
|
1142 |
// TODO: $PREVDIRECTION NOT DEFINED YET.
|
|
|
1143 |
$prevdirection = null;
|
|
|
1144 |
$seq = scorm_seq_flow_tree_traversal ($activity, $direction, $childrenflag, $prevdirection, $seq, $userid);
|
|
|
1145 |
if ($seq->nextactivity == null) {
|
|
|
1146 |
$seq->nextactivity = $activity;
|
|
|
1147 |
$seq->deliverable = false;
|
|
|
1148 |
return $seq;
|
|
|
1149 |
} else {
|
|
|
1150 |
$activity = $seq->nextactivity;
|
|
|
1151 |
$seq = scorm_seq_flow_activity_traversal($activity, $userid, $direction, $childrenflag, null, $seq);
|
|
|
1152 |
return $seq;
|
|
|
1153 |
}
|
|
|
1154 |
}
|
|
|
1155 |
|
|
|
1156 |
/**
|
|
|
1157 |
* Sets up $userdata array and default values for SCORM 1.3 .
|
|
|
1158 |
*
|
|
|
1159 |
* @param stdClass $userdata an empty stdClass variable that should be set up with user values
|
|
|
1160 |
* @param object $scorm package record
|
|
|
1161 |
* @param string $scoid SCO Id
|
|
|
1162 |
* @param string $attempt attempt number for the user
|
|
|
1163 |
* @param string $mode scorm display mode type
|
|
|
1164 |
* @return array The default values that should be used for SCORM 1.3 package
|
|
|
1165 |
*/
|
|
|
1166 |
function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {
|
|
|
1167 |
global $DB, $USER;
|
|
|
1168 |
|
|
|
1169 |
$userdata->student_id = $USER->username;
|
|
|
1170 |
if (empty(get_config('scorm', 'scormstandard'))) {
|
|
|
1171 |
$userdata->student_name = fullname($USER);
|
|
|
1172 |
} else {
|
|
|
1173 |
$userdata->student_name = $USER->lastname .', '. $USER->firstname;
|
|
|
1174 |
}
|
|
|
1175 |
|
|
|
1176 |
if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {
|
|
|
1177 |
// According to SCORM 2004(RTE V1, 4.2.8), only cmi.exit==suspend should allow previous datamodel elements on re-launch.
|
|
|
1178 |
if (isset($usertrack->{'cmi.exit'}) && ($usertrack->{'cmi.exit'} == 'suspend')) {
|
|
|
1179 |
foreach ($usertrack as $key => $value) {
|
|
|
1180 |
$userdata->$key = $value;
|
|
|
1181 |
}
|
|
|
1182 |
} else {
|
|
|
1183 |
$userdata->status = '';
|
|
|
1184 |
$userdata->score_raw = '';
|
|
|
1185 |
}
|
|
|
1186 |
} else {
|
|
|
1187 |
$userdata->status = '';
|
|
|
1188 |
$userdata->score_raw = '';
|
|
|
1189 |
}
|
|
|
1190 |
|
|
|
1191 |
if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
|
|
|
1192 |
foreach ($scodatas as $key => $value) {
|
|
|
1193 |
$userdata->$key = $value;
|
|
|
1194 |
}
|
|
|
1195 |
} else {
|
|
|
1196 |
throw new \moodle_exception('cannotfindsco', 'scorm');
|
|
|
1197 |
}
|
|
|
1198 |
if (!$sco = scorm_get_sco($scoid)) {
|
|
|
1199 |
throw new \moodle_exception('cannotfindsco', 'scorm');
|
|
|
1200 |
}
|
|
|
1201 |
|
|
|
1202 |
if (isset($userdata->status)) {
|
|
|
1203 |
if (!isset($userdata->{'cmi.exit'}) || $userdata->{'cmi.exit'} == 'time-out' || $userdata->{'cmi.exit'} == 'normal') {
|
|
|
1204 |
$userdata->entry = 'ab-initio';
|
|
|
1205 |
} else {
|
|
|
1206 |
if (isset($userdata->{'cmi.exit'}) && ($userdata->{'cmi.exit'} == 'suspend' || $userdata->{'cmi.exit'} == 'logout')) {
|
|
|
1207 |
$userdata->entry = 'resume';
|
|
|
1208 |
} else {
|
|
|
1209 |
$userdata->entry = '';
|
|
|
1210 |
}
|
|
|
1211 |
}
|
|
|
1212 |
}
|
|
|
1213 |
|
|
|
1214 |
$userdata->mode = 'normal';
|
|
|
1215 |
if (!empty($mode)) {
|
|
|
1216 |
$userdata->mode = $mode;
|
|
|
1217 |
}
|
|
|
1218 |
if ($userdata->mode == 'normal') {
|
|
|
1219 |
$userdata->credit = 'credit';
|
|
|
1220 |
} else {
|
|
|
1221 |
$userdata->credit = 'no-credit';
|
|
|
1222 |
}
|
|
|
1223 |
|
|
|
1224 |
$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $scoid));
|
|
|
1225 |
$index = 0;
|
|
|
1226 |
foreach ($objectives as $objective) {
|
|
|
1227 |
if (!empty($objective->minnormalizedmeasure)) {
|
|
|
1228 |
$userdata->{'cmi.scaled_passing_score'} = $objective->minnormalizedmeasure;
|
|
|
1229 |
}
|
|
|
1230 |
if (!empty($objective->objectiveid)) {
|
|
|
1231 |
$userdata->{'cmi.objectives.N'.$index.'.id'} = $objective->objectiveid;
|
|
|
1232 |
$index++;
|
|
|
1233 |
}
|
|
|
1234 |
}
|
|
|
1235 |
|
|
|
1236 |
$def = array();
|
|
|
1237 |
$def['cmi.learner_id'] = $userdata->student_id;
|
|
|
1238 |
$def['cmi.learner_name'] = $userdata->student_name;
|
|
|
1239 |
$def['cmi.mode'] = $userdata->mode;
|
|
|
1240 |
$def['cmi.entry'] = $userdata->entry;
|
|
|
1241 |
$def['cmi.exit'] = scorm_isset($userdata, 'cmi.exit');
|
|
|
1242 |
$def['cmi.credit'] = scorm_isset($userdata, 'credit');
|
|
|
1243 |
$def['cmi.completion_status'] = scorm_isset($userdata, 'cmi.completion_status', 'unknown');
|
|
|
1244 |
$def['cmi.completion_threshold'] = scorm_isset($userdata, 'threshold');
|
|
|
1245 |
$def['cmi.learner_preference.audio_level'] = scorm_isset($userdata, 'cmi.learner_preference.audio_level', 1);
|
|
|
1246 |
$def['cmi.learner_preference.language'] = scorm_isset($userdata, 'cmi.learner_preference.language');
|
|
|
1247 |
$def['cmi.learner_preference.delivery_speed'] = scorm_isset($userdata, 'cmi.learner_preference.delivery_speed');
|
|
|
1248 |
$def['cmi.learner_preference.audio_captioning'] = scorm_isset($userdata, 'cmi.learner_preference.audio_captioning', 0);
|
|
|
1249 |
$def['cmi.location'] = scorm_isset($userdata, 'cmi.location');
|
|
|
1250 |
$def['cmi.max_time_allowed'] = scorm_isset($userdata, 'attemptAbsoluteDurationLimit');
|
|
|
1251 |
$def['cmi.progress_measure'] = scorm_isset($userdata, 'cmi.progress_measure');
|
|
|
1252 |
$def['cmi.scaled_passing_score'] = scorm_isset($userdata, 'cmi.scaled_passing_score');
|
|
|
1253 |
$def['cmi.score.scaled'] = scorm_isset($userdata, 'cmi.score.scaled');
|
|
|
1254 |
$def['cmi.score.raw'] = scorm_isset($userdata, 'cmi.score.raw');
|
|
|
1255 |
$def['cmi.score.min'] = scorm_isset($userdata, 'cmi.score.min');
|
|
|
1256 |
$def['cmi.score.max'] = scorm_isset($userdata, 'cmi.score.max');
|
|
|
1257 |
$def['cmi.success_status'] = scorm_isset($userdata, 'cmi.success_status', 'unknown');
|
|
|
1258 |
$def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');
|
|
|
1259 |
$def['cmi.time_limit_action'] = scorm_isset($userdata, 'timelimitaction');
|
|
|
1260 |
$def['cmi.total_time'] = scorm_isset($userdata, 'cmi.total_time', 'PT0H0M0S');
|
|
|
1261 |
$def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');
|
|
|
1262 |
|
|
|
1263 |
return $def;
|
|
|
1264 |
}
|