Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
}