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
function scorm_seq_exit_action_rules($seq, $userid) {
18
    $sco = $seq->currentactivity;
19
    $ancestors = scorm_get_ancestors($sco);
20
    $exittarget = null;
21
    foreach (array_reverse($ancestors) as $ancestor) {
22
        if (scorm_seq_rules_check($ancestor, 'exit') != null) {
23
            $exittarget = $ancestor;
24
            break;
25
        }
26
    }
27
    if ($exittarget != null) {
28
        $commons = array_slice($ancestors, 0, scorm_find_common_ancestor($ancestors, $exittarget));
29
 
30
        // Terminate Descendent Attempts Process.
31
        if ($commons) {
32
            foreach ($commons as $ancestor) {
33
                scorm_seq_end_attempt($ancestor, $userid, $seq->attempt);
34
                $seq->currentactivity = $ancestor;
35
            }
36
        }
37
    }
38
    return $seq;
39
}
40
 
41
function scorm_seq_post_cond_rules($seq, $userid) {
42
    $sco = $seq->currentactivity;
43
    if (!$seq->suspended) {
44
        if ($action = scorm_seq_rules_check($sco, 'post') != null) {
45
            switch($action) {
46
                case 'retry':
47
                case 'continue':
48
                case 'previous':
49
                    $seq->sequencing = $action;
50
                break;
51
                case 'exitparent':
52
                case 'exitall':
53
                    $seq->termination = $action;
54
                break;
55
                case 'retryall':
56
                    $seq->termination = 'exitall';
57
                    $seq->sequencing = 'retry';
58
                break;
59
            }
60
        }
61
    }
62
    return $seq;
63
}
64
 
65
 
66
function scorm_seq_evaluate_rollupcond($sco, $conditioncombination, $rollupruleconds, $userid) {
67
    $bag = Array();
68
    $con = "";
69
    $val = false;
70
    $unk = false;
71
    foreach ($rollupruleconds as $rolluprulecond) {
72
        $condit = scorm_evaluate_condition($rolluprulecond, $sco, $userid);
73
        if ($rolluprulecond->operator == 'not') { // If operator is not, negate the condition.
74
            if ($rolluprulecond->cond != 'unknown') {
75
                if ($condit) {
76
                    $condit = false;
77
                } else {
78
                    $condit = true;
79
                }
80
            } else {
81
                $condit = 'unknown';
82
            }
83
            array_push($childrenbag, $condit);
84
        }
85
    }
86
    if (empty($bag)) {
87
        return 'unknown';
88
    } else {
89
        $i = 0;
90
        foreach ($bag as $b) {
91
            if ($rolluprulecond->conditioncombination == 'all') {
92
                $val = true;
93
                if ($b == 'unknown') {
94
                    $unk = true;
95
                }
96
                if ($b === false) {
97
                    return false;
98
                }
99
            } else {
100
                $val = false;
101
 
102
                if ($b == 'unknown') {
103
                    $unk = true;
104
                }
105
                if ($b === true) {
106
                    return true;
107
                }
108
            }
109
        }
110
    }
111
    if ($unk) {
112
        return 'unknown';
113
    }
114
    return $val;
115
}
116
 
117
function scorm_seq_check_child ($sco, $action, $userid) {
118
    global $DB;
119
 
120
    $included = false;
121
    $sco = scorm_get_sco($sco->id);
122
    $r = scorm_get_sco_value($sco->id, $userid, 'activityattemptcount');
123
    if ($action == 'satisfied' || $action == 'notsatisfied') {
124
        if (!$sco->rollupobjectivesatisfied) {
125
            $included = true;
126
            if (($action == 'satisfied' && $sco->requiredforsatisfied == 'ifnotsuspended') ||
127
                ($action == 'notsatisfied' && $sco->requiredfornotsatisfied == 'ifnotsuspended')) {
128
 
129
                if (!scorm_seq_is('activityprogressstatus', $sco->id, $userid) ||
130
                    ((($r->value) > 0) && !scorm_seq_is('suspended', $sco->id, $userid))) {
131
                    $included = false;
132
                }
133
 
134
            } else {
135
                if (($action == 'satisfied' && $sco->requiredforsatisfied == 'ifattempted') ||
136
                    ($action == 'notsatisfied' && $sco->requiredfornotsatisfied == 'ifattempted')) {
137
                    if (!scorm_seq_is('activityprogressstatus', $sco->id, $userid) || (($r->value) == 0)) {
138
                        $included = false;
139
                    }
140
                } else {
141
                    if (($action == 'satisfied' && $sco->requiredforsatisfied == 'ifnotskipped') ||
142
                        ($action == 'notsatisfied' && $sco->requiredfornotsatisfied == 'ifnotskipped')) {
143
                        $rulch = scorm_seq_rules_check($sco, 'skip');
144
                        if ($rulch != null) {
145
                            $included = false;
146
                        }
147
                    }
148
                }
149
            }
150
        }
151
    }
152
    if ($action == 'completed' || $action == 'incomplete') {
153
        if (!$sco->rollupprogresscompletion) {
154
            $included = true;
155
 
156
            if (($action == 'completed' && $sco->requiredforcompleted == 'ifnotsuspended') ||
157
                ($action == 'incomplete' && $sco->requiredforincomplete == 'ifnotsuspended')) {
158
 
159
                if (!scorm_seq_is('activityprogressstatus', $sco->id, $userid) ||
160
                    ((($r->value) > 0)&& !scorm_seq_is('suspended', $sco->id, $userid))) {
161
                    $included = false;
162
                }
163
 
164
            } else {
165
 
166
                if (($action == 'completed' && $sco->requiredforcompleted == 'ifattempted') ||
167
                    ($action == 'incomplete' && $sco->requiredforincomplete == 'ifattempted')) {
168
                    if (!scorm_seq_is('activityprogressstatus', $sco->id, $userid) || (($r->value) == 0)) {
169
                        $included = false;
170
                    }
171
 
172
                } else {
173
                    if (($action == 'completed' && $sco->requiredforsatisfied == 'ifnotskipped') ||
174
                        ($action == 'incomplete' && $sco->requiredfornotsatisfied == 'ifnotskipped')) {
175
                        $rulch = scorm_seq_rules_check($sco, 'skip');
176
                        if ($rulch != null) {
177
                            $included = false;
178
                        }
179
                    }
180
                }
181
            }
182
        }
183
    }
184
    return $included;
185
}
186
 
187
function scorm_seq_sequencing ($scoid, $userid, $seq) {
188
 
189
    switch ($seq->sequencing) {
190
        case 'start':
191
            // We'll see the parameters we have to send, this should update delivery and end.
192
            $seq = scorm_seq_start_sequencing($scoid, $userid, $seq);
193
            $seq->sequencing = true;
194
            break;
195
 
196
        case 'resumeall':
197
            // We'll see the parameters we have to send, this should update delivery and end.
198
            $seq = scorm_seq_resume_all_sequencing($scoid, $userid, $seq);
199
            $seq->sequencing = true;
200
            break;
201
 
202
        case 'exit':
203
            // We'll see the parameters we have to send, this should update delivery and end.
204
            $seq = scorm_seq_exit_sequencing($scoid, $userid, $seq);
205
            $seq->sequencing = true;
206
            break;
207
 
208
        case 'retry':
209
            // We'll see the parameters we have to send, this should update delivery and end.
210
            $seq = scorm_seq_retry_sequencing($scoid, $userid, $seq);
211
            $seq->sequencing = true;
212
            break;
213
 
214
        case 'previous':
215
            // We'll see the parameters we have to send, this should update delivery and end.
216
            $seq = scorm_seq_previous_sequencing($scoid, $userid, $seq);
217
            $seq->sequencing = true;
218
            break;
219
 
220
        case 'choice':
221
            // We'll see the parameters we have to send, this should update delivery and end.
222
            $seq = scorm_seq_choice_sequencing($scoid, $userid, $seq);
223
            $seq->sequencing = true;
224
            break;
225
    }
226
 
227
    if ($seq->exception != null) {
228
        $seq->sequencing = false;
229
        return $seq;
230
    }
231
 
232
    $seq->sequencing = true;
233
    return $seq;
234
}
235
 
236
function scorm_seq_start_sequencing($scoid, $userid, $seq) {
237
    global $DB;
238
 
239
    if (!empty($seq->currentactivity)) {
240
        $seq->delivery = null;
241
        $seq->exception = 'SB.2.5-1';
242
        return $seq;
243
    }
244
    $sco = $DB->get_record('scorm_scoes', array('scoid' => $scoid, 'userid' => $userid));
245
    if (($sco->parent == '/') && scorm_is_leaf($sco)) { // If the activity is the root and is leaf.
246
        $seq->delivery = $sco;
247
    } else {
248
        $ancestors = scorm_get_ancestors($sco);
249
        $ancestorsroot = array_reverse($ancestors);
250
        $res = scorm_seq_flow($ancestorsroot[0], 'forward', $seq, true, $userid);
251
        if ($res) {
252
            return $res;
253
        }
254
    }
255
}
256
 
257
function scorm_seq_resume_all_sequencing($scoid, $userid, $seq) {
258
    global $DB;
259
 
260
    if (!empty($seq->currentactivity)) {
261
        $seq->delivery = null;
262
        $seq->exception = 'SB.2.6-1';
263
        return $seq;
264
    }
265
    $track = scorm_get_sco_value($scoid, $userid, 'suspendedactivity');
266
    if (!$track) {
267
        $seq->delivery = null;
268
        $seq->exception = 'SB.2.6-2';
269
        return $seq;
270
    }
271
    // We assign the sco to the delivery.
272
    $seq->delivery = $DB->get_record('scorm_scoes', array('scoid' => $scoid, 'userid' => $userid));
273
}
274
 
275
function scorm_seq_continue_sequencing($scoid, $userid, $seq) {
276
    if (empty($seq->currentactivity)) {
277
        $seq->delivery = null;
278
        $seq->exception = 'SB.2.7-1';
279
        return $seq;
280
    }
281
    $currentact = $seq->currentactivity;
282
    if ($currentact->parent != '/') {
283
        // If the activity is the root and is leaf.
284
        $parent = scorm_get_parent ($currentact);
285
 
286
        if (!isset($parent->flow) || ($parent->flow == false)) {
287
            $seq->delivery = null;
288
            $seq->exception = 'SB.2.7-2';
289
            return $seq;
290
        }
291
 
292
        $res = scorm_seq_flow($currentact, 'forward', $seq, false, $userid);
293
        if ($res) {
294
            return $res;
295
        }
296
    }
297
}
298
 
299
function scorm_seq_previous_sequencing($scoid, $userid, $seq) {
300
    if (empty($seq->currentactivity)) {
301
        $seq->delivery = null;
302
        $seq->exception = 'SB.2.8-1';
303
        return $seq;
304
    }
305
 
306
    $currentact = $seq->currentactivity;
307
    if ($currentact->parent != '/') { // If the activity is the root and is leaf.
308
        $parent = scorm_get_parent ($currentact);
309
        if (!isset($parent->flow) || ($parent->flow == false)) {
310
            $seq->delivery = null;
311
            $seq->exception = 'SB.2.8-2';
312
            return $seq;
313
        }
314
 
315
        $res = scorm_seq_flow($currentact, 'backward', $seq, false, $userid);
316
        if ($res) {
317
            return $res;
318
        }
319
    }
320
}
321
 
322
function scorm_seq_exit_sequencing($scoid, $userid, $seq) {
323
    if (empty($seq->currentactivity)) {
324
        $seq->delivery = null;
325
        $seq->exception = 'SB.2.11-1';
326
        return $seq;
327
    }
328
 
329
    if ($seq->active) {
330
        $seq->endsession = false;
331
        $seq->exception = 'SB.2.11-2';
332
        return $seq;
333
    }
334
    $currentact = $seq->currentactivity;
335
    if ($currentact->parent == '/') {
336
        $seq->endsession = true;
337
        return $seq;
338
    }
339
 
340
    $seq->endsession = false;
341
    return $seq;
342
}
343
 
344
function scorm_seq_retry_sequencing($scoid, $userid, $seq) {
345
    if (empty($seq->currentactivity)) {
346
        $seq->delivery = null;
347
        $seq->exception = 'SB.2.10-1';
348
        return $seq;
349
    }
350
    if ($seq->active || $seq->suspended) {
351
        $seq->delivery = null;
352
        $seq->exception = 'SB.2.10-2';
353
        return $seq;
354
    }
355
 
356
    if (!scorm_is_leaf($seq->currentactivity)) {
357
        $res = scorm_seq_flow($seq->currentactivity, 'forward', $seq, true, $userid);
358
        if ($res != null) {
359
            return $res;
360
        } else {
361
            // Return deliver.
362
            $seq->delivery = null;
363
            $seq->exception = 'SB.2.10-3';
364
            return $seq;
365
        }
366
    } else {
367
        $seq->delivery = $seq->currentactivity;
368
        return $seq;
369
    }
370
 
371
}
372
 
373
function scorm_seq_choice_sequencing($sco, $userid, $seq) {
374
 
375
    $avchildren = Array ();
376
    $comancestor = null;
377
    $traverse = null;
378
 
379
    if ($sco == null) {
380
        $seq->delivery = null;
381
        $seq->exception = 'SB.2.9-1';
382
        return $seq;
383
    }
384
 
385
    $ancestors = scorm_get_ancestors($sco);
386
    $arrpath = array_reverse($ancestors);
387
    array_push ($arrpath, $sco); // Path from the root to the target.
388
 
389
    foreach ($arrpath as $activity) {
390
        if ($activity->parent != '/') {
391
            $avchildren = scorm_get_available_children (scorm_get_parent($activity));
392
            $position = array_search($avchildren, $activity);
393
            if ($position !== false) {
394
                $seq->delivery = null;
395
                $seq->exception = 'SB.2.9-2';
396
                return $seq;
397
            }
398
        }
399
 
400
        if (scorm_seq_rules_check($activity, 'hidefromchoice' != null)) {
401
            $seq->delivery = null;
402
            $seq->exception = 'SB.2.9-3';
403
            return $seq;
404
        }
405
    }
406
 
407
    if ($sco->parent != '/') {
408
        $parent = scorm_sco_get_parent ($sco);
409
        if ( isset($parent->choice) && ($parent->choice == false)) {
410
            $seq->delivery = null;
411
            $seq->exception = 'SB.2.9-4';
412
            return $seq;
413
        }
414
    }
415
 
416
    if ($seq->currentactivity != null) {
417
        $commonpos = scorm_find_common_ancestor($ancestors, $seq->currentactivity);
418
        $comancestor = $arrpath [$commonpos];
419
    } else {
420
        $comancestor = $arrpath [0];
421
    }
422
 
423
    if ($seq->currentactivity === $sco) {
424
        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
425
        throw new \coding_exception('Unexpected state encountered');
426
    }
427
 
428
    $sib = scorm_get_siblings($seq->currentactivity);
429
    $pos = array_search($sib, $sco);
430
 
431
    if ($pos !== false) {
432
        $siblings = array_slice($sib, 0, $pos - 1);
433
        if (empty($siblings)) {
434
            $seq->delivery = null;
435
            $seq->exception = 'SB.2.9-5';
436
            return $seq;
437
        }
438
 
439
        $children = scorm_get_children (scorm_get_parent ($sco));
440
        $pos1 = array_search($children, $sco);
441
        $pos2 = array_search($seq->currentactivity, $sco);
442
        if ($pos1 > $pos2) {
443
            $traverse = 'forward';
444
        } else {
445
            $traverse = 'backward';
446
        }
447
 
448
        foreach ($siblings as $sibling) {
449
            $seq = scorm_seq_choice_activity_traversal($sibling, $userid, $seq, $traverse);
450
            if (!$seq->reachable) {
451
                $seq->delivery = null;
452
                return $seq;
453
            }
454
        }
455
        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
456
        throw new \coding_exception('Unexpected state encountered');
457
    }
458
 
459
    if ($seq->currentactivity == null || $seq->currentactivity == $comancestor) {
460
        $commonpos = scorm_find_common_ancestor($ancestors, $seq->currentactivity);
461
        // Path from the common ancestor to the target activity.
462
        $comtarget = array_slice($ancestors, 1, $commonpos - 1);
463
        $comtarget = array_reverse($comtarget);
464
 
465
        if (empty($comtarget)) {
466
            $seq->delivery = null;
467
            $seq->exception = 'SB.2.9-5';
468
            return $seq;
469
        }
470
        foreach ($comtarget as $act) {
471
            $seq = scorm_seq_choice_activity_traversal($act, $userid, $seq, 'forward');
472
            if (!$seq->reachable) {
473
                $seq->delivery = null;
474
                return $seq;
475
            }
476
            $act = scorm_get_sco ($acti->id);
477
            if (scorm_seq_is('active', $act->id, $userid) && ($act->id != $comancestor->id && $act->preventactivation)) {
478
                $seq->delivery = null;
479
                $seq->exception = 'SB.2.9-6';
480
                return $seq;
481
            }
482
        }
483
        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
484
        throw new \coding_exception('Unexpected state encountered');
485
    }
486
 
487
    if ($comancestor->id == $sco->id) {
488
 
489
        $ancestorscurrent = scorm_get_ancestors($seq->currentactivity);
490
        $possco = array_search($ancestorscurrent, $sco);
491
        // Path from the current activity to the target.
492
        $curtarget = array_slice($ancestorscurrent, 0, $possco);
493
 
494
        if (empty($curtarget)) {
495
            $seq->delivery = null;
496
            $seq->exception = 'SB.2.9-5';
497
            return $seq;
498
        }
499
        $i = 0;
500
        foreach ($curtarget as $activ) {
501
            $i++;
502
            if ($i != count($curtarget)) {
503
                if (isset($activ->choiceexit) && ($activ->choiceexit == false)) {
504
                    $seq->delivery = null;
505
                    $seq->exception = 'SB.2.9-7';
506
                    return $seq;
507
                }
508
            }
509
        }
510
        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
511
        throw new \coding_exception('Unexpected state encountered');
512
    }
513
 
514
    if (array_search($ancestors, $comancestor) !== false) {
515
        $ancestorscurrent = scorm_get_ancestors($seq->currentactivity);
516
        $commonpos = scorm_find_common_ancestor($ancestors, $sco);
517
        $curcommon = array_slice($ancestorscurrent, 0, $commonpos - 1);
518
        if (empty($curcommon)) {
519
            $seq->delivery = null;
520
            $seq->exception = 'SB.2.9-5';
521
            return $seq;
522
        }
523
 
524
        $constrained = null;
525
        foreach ($curcommon as $acti) {
526
            $acti = scorm_get_sco($acti->id);
527
            if (isset($acti->choiceexit) && ($acti->choiceexit == false)) {
528
                    $seq->delivery = null;
529
                    $seq->exception = 'SB.2.9-7';
530
                    return $seq;
531
            }
532
            if ($constrained == null) {
533
                if ($acti->constrainchoice == true) {
534
                    $constrained = $acti;
535
                }
536
            }
537
        }
538
        if ($constrained != null) {
539
            $fwdir = scorm_get_preorder($constrained);
540
 
541
            if (array_search($fwdir, $sco) !== false) {
542
                $traverse = 'forward';
543
            } else {
544
                $traverse = 'backward';
545
            }
546
            $seq = scorm_seq_choice_flow($constrained, $traverse, $seq);
547
            $actconsider = $seq->identifiedactivity;
548
            $avdescendents = Array();
549
            $avdescendents = scorm_get_available_descendents($actconsider);
550
            if (array_search ($avdescendents, $sco) !== false && $sco->id != $actconsider->id && $constrained->id != $sco->id ) {
551
                $seq->delivery = null;
552
                $seq->exception = 'SB.2.9-8';
553
                return $seq;
554
            }
555
            // CONTINUE 11.5.5 !
556
        }
557
 
558
        $commonpos = scorm_find_common_ancestor($ancestors, $seq->currentactivity);
559
        $comtarget = array_slice($ancestors, 1, $commonpos - 1);// Path from the common ancestor to the target activity.
560
        $comtarget = array_reverse($comtarget);
561
 
562
        if (empty($comtarget)) {
563
            $seq->delivery = null;
564
            $seq->exception = 'SB.2.9-5';
565
            return $seq;
566
        }
567
 
568
        $fwdir = scorm_get_preorder($seq->currentactivity);
569
 
570
        if (array_search($fwdir, $sco) !== false) {
571
            foreach ($comtarget as $act) {
572
                $seq = scorm_seq_choice_activity_traversal($act, $userid, $seq, 'forward');
573
                if (!$seq->reachable) {
574
                    $seq->delivery = null;
575
                    return $seq;
576
                }
577
                $act = scorm_get_sco($act->id);
578
                if (scorm_seq_is('active', $act->id, $userid) && ($act->id != $comancestor->id &&
579
                        ($act->preventactivation == true))) {
580
                    $seq->delivery = null;
581
                    $seq->exception = 'SB.2.9-6';
582
                    return $seq;
583
                }
584
            }
585
 
586
        } else {
587
            foreach ($comtarget as $act) {
588
                $act = scorm_get_sco($act->id);
589
                if (scorm_seq_is('active', $act->id, $userid) && $act->id != $comancestor->id && $act->preventactivation == true) {
590
                    $seq->delivery = null;
591
                    $seq->exception = 'SB.2.9-6';
592
                    return $seq;
593
                }
594
            }
595
        }
596
        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
597
        throw new \coding_exception('Unexpected state encountered');
598
    }
599
 
600
    if (scorm_is_leaf ($sco)) {
601
        $seq->delivery = $sco;
602
        $seq->exception = 'SB.2.9-6';
603
        return $seq;
604
    }
605
 
606
    $seq = scorm_seq_flow($sco, 'forward', $seq, true, $userid);
607
    if ($seq->deliverable == false) {
608
        scorm_terminate_descendent_attempts($comancestor, $userid, $seq);
609
        scorm_seq_end_attempt($comancestor, $userid, $seq->attempt);
610
        $seq->currentactivity = $sco;
611
        $seq->delivery = null;
612
        $seq->exception = 'SB.2.9-9';
613
        return $seq;
614
    } else {
615
        return $seq;
616
    }
617
 
618
}
619
 
620
function scorm_seq_choice_flow ($constrained, $traverse, $seq) {
621
    $seq = scorm_seq_choice_flow_tree ($constrained, $traverse, $seq);
622
    if ($seq->identifiedactivity == null) {
623
        $seq->identifiedactivity = $constrained;
624
        return $seq;
625
    } else {
626
        return $seq;
627
    }
628
}
629
 
630
function scorm_seq_choice_flow_tree ($constrained, $traverse, $seq) {
631
    $islast = false;
632
    $parent = scorm_get_parent ($constrained);
633
    if ($traverse == 'forward') {
634
        $preord = scorm_get_preorder($constrained);
635
        if (count($preorder) == 0 || (count($preorder) == 0 && $preorder[0]->id = $constrained->id)) {
636
            // TODO: undefined.
637
            $islast = true; // The function is the last activity available.
638
        }
639
        if ($constrained->parent == '/' || $islast) {
640
            $seq->nextactivity = null;
641
            return $seq;
642
        }
643
        $avchildren = scorm_get_available_children($parent); // Available children.
644
        if ($avchildren[count($avchildren) - 1]->id == $constrained->id) {
645
            $seq = scorm_seq_choice_flow_tree ($parent, 'forward', $seq);
646
            return $seq;
647
        } else {
648
            $i = 0;
649
            while ($i < count($avchildren)) {
650
                if ($avchildren [$i]->id == $constrained->id) {
651
                    $seq->nextactivity = $avchildren [$i + 1];
652
                    return $seq;
653
                } else {
654
                    $i++;
655
                }
656
            }
657
        }
658
    }
659
 
660
    if ($traverse == 'backward') {
661
        if ($constrained->parent == '/' ) {
662
            $seq->nextactivity = null;
663
            return $seq;
664
        }
665
 
666
        $avchildren = scorm_get_available_children($parent); // Available children.
667
        if ($avchildren [0]->id == $constrained->id) {
668
            $seq = scorm_seq_choice_flow_tree ($parent, 'backward', $seq);
669
            return $seq;
670
        } else {
671
            $i = count($avchildren) - 1;
672
            while ($i >= 0) {
673
                if ($avchildren [$i]->id == $constrained->id) {
674
                    $seq->nextactivity = $avchildren [$i - 1];
675
                    return $seq;
676
                } else {
677
                    $i--;
678
                }
679
            }
680
        }
681
    }
682
}
683
 
684
function scorm_seq_choice_activity_traversal($activity, $userid, $seq, $direction) {
685
    if ($direction == 'forward') {
686
        $act = scorm_seq_rules_check($activity, 'stopforwardtraversal');
687
 
688
        if ($act != null) {
689
            $seq->reachable = false;
690
            $seq->exception = 'SB.2.4-1';
691
            return $seq;
692
        }
693
        $seq->reachable = false;
694
        return $seq;
695
    }
696
 
697
    if ($direction == 'backward') {
698
        $parentsco = scorm_get_parent($activity);
699
        if ($parentsco != null) {
700
            if (isset($parentsco->forwardonly) && ($parentsco->forwardonly == true)) {
701
                $seq->reachable = false;
702
                $seq->exception = 'SB.2.4-2';
703
                return $seq;
704
            } else {
705
                $seq->reachable = false;
706
                $seq->exception = 'SB.2.4-3';
707
                return $seq;
708
            }
709
        }
710
    }
711
    $seq->reachable = true;
712
    return $seq;
713
}
714
 
715
// Delivery Request Process.
716
 
717
function scorm_sequencing_delivery($scoid, $userid, $seq) {
718
 
719
    if (!scorm_is_leaf($seq->delivery)) {
720
        $seq->deliveryvalid = false;
721
        $seq->exception = 'DB.1.1-1';
722
        return $seq;
723
    }
724
    $ancestors = scorm_get_ancestors($seq->delivery);
725
    $arrpath = array_reverse($ancestors);
726
    array_push ($arrpath, $seq->delivery); // Path from the root to the target.
727
 
728
    if (empty($arrpath)) {
729
        $seq->deliveryvalid = false;
730
        $seq->exception = 'DB.1.1-2';
731
        return $seq;
732
    }
733
 
734
    foreach ($arrpath as $activity) {
735
        if (scorm_check_activity($activity, $userid)) {
736
            $seq->deliveryvalid = false;
737
            $seq->exception = 'DB.1.1-3';
738
            return $seq;
739
        }
740
    }
741
 
742
    $seq->deliveryvalid = true;
743
    return $seq;
744
 
745
}
746
 
747
function scorm_content_delivery_environment($seq, $userid) {
748
    global $DB;
749
 
750
    $act = $seq->currentactivity;
751
    if (scorm_seq_is('active', $act->id, $userid)) {
752
        $seq->exception = 'DB.2-1';
753
        return $seq;
754
    }
755
    $track = scorm_get_sco_value($act->id, $userid, 'suspendedactivity');
756
    if ($track != null) {
757
        $seq = scorm_clear_suspended_activity($seq->delivery, $seq, $userid);
758
 
759
    }
760
    $attemptobject = scorm_get_attempt($userid, $track->scormid, 0);
761
    $seq = scorm_terminate_descendent_attempts ($seq->delivery, $userid, $seq);
762
    $ancestors = scorm_get_ancestors($seq->delivery);
763
    $arrpath = array_reverse($ancestors);
764
    array_push ($arrpath, $seq->delivery);
765
    foreach ($arrpath as $activity) {
766
        if (!scorm_seq_is('active', $activity->id, $userid)) {
767
            if (!isset($activity->tracked) || ($activity->tracked == 1)) {
768
                if (!scorm_seq_is('suspended', $activity->id, $userid)) {
769
                    $r = scorm_get_sco_value($activity->id, $userid, 'activityattemptcount');
770
                    $value = new stdClass();
771
                    $value->id = $r->valueid;
772
                    $value->value = ($r->value) + 1;
773
                    $DB->update_record('scorm_scoes_value', $value);
774
                    if ($r->value == 1) {
775
                        scorm_seq_set('activityprogressstatus', $activity->id, $userid, 'true');
776
                    }
777
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
778
                                       'objectiveprogressstatus', 'false');
779
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
780
                                       'objectivesatisfiedstatus', 'false');
781
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
782
                                       'objectivemeasurestatus', 'false');
783
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
784
                                       'objectivenormalizedmeasure', 0.0);
785
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
786
                                       'attemptprogressstatus', 'false');
787
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
788
                                       'attemptcompletionstatus', 'false');
789
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
790
                                       'attemptabsoluteduration', 0.0);
791
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
792
                                       'attemptexperiencedduration', 0.0);
793
                    scorm_insert_track($userid, $activity->scorm, $activity->id, $attemptobject,
794
                                       'attemptcompletionamount', 0.0);
795
                }
796
            }
797
            scorm_seq_set('active', $activity->id, $userid, 'true');
798
        }
799
    }
800
    $seq->delivery = $seq->currentactivity;
801
    scorm_seq_set('suspendedactivity', $activity->id, $userid, 'false');
802
 
803
    return $seq;
804
}
805
 
806
function scorm_clear_suspended_activity($act, $seq, $userid) {
807
    global $DB;
808
    $currentact = $seq->currentactivity;
809
    $track = scorm_get_sco_value($currentact->id, $userid, 'suspendedactivity');
810
    if ($track != null) {
811
        $ancestors = scorm_get_ancestors($act);
812
        $commonpos = scorm_find_common_ancestor($ancestors, $currentact);
813
        if ($commonpos !== false) {
814
            if ($activitypath = array_slice($ancestors, 0, $commonpos)) {
815
                if (!empty($activitypath)) {
816
 
817
                    foreach ($activitypath as $activity) {
818
                        if (scorm_is_leaf($activity)) {
819
                            scorm_seq_set('suspended', $activity->id, $userid, false);
820
                        } else {
821
                            $children = scorm_get_children($activity);
822
                            $bool = false;
823
                            foreach ($children as $child) {
824
                                if (scorm_seq_is('suspended', $child->id, $userid)) {
825
                                    $bool = true;
826
                                }
827
                            }
828
                            if (!$bool) {
829
                                scorm_seq_set('suspended', $activity->id, $userid, false);
830
                            }
831
                        }
832
                    }
833
                }
834
            }
835
        }
836
        scorm_seq_set('suspendedactivity', $act->id, $userid, false);
837
    }
838
}
839
 
840
function scorm_select_children_process($scoid, $userid) {
841
    global $DB;
842
 
843
    $sco = scorm_get_sco($scoid);
844
    if (!scorm_is_leaf($sco)) {
845
        if (!scorm_seq_is('suspended', $scoid, $userid) && !scorm_seq_is('active', $scoid, $userid)) {
846
            $r = scorm_get_sco_value($scoid, $userid, 'selectiontiming');
847
 
848
            switch ($r->value) {
849
                case 'oneachnewattempt':
850
                case 'never':
851
                break;
852
 
853
                case 'once':
854
                    if (!scorm_seq_is('activityprogressstatus', $scoid, $userid)) {
855
                        if (scorm_seq_is('selectioncountsstatus', $scoid, $userid)) {
856
                            $childlist = '';
857
                            $res = scorm_get_sco_value($scoid, $userid, 'selectioncount');
858
                            $i = ($res->value) - 1;
859
                            $children = scorm_get_children($sco);
860
 
861
                            while ($i >= 0) {
862
                                $pos = array_rand($children);
863
                                array_push($childlist, $children[$pos]);
864
                                array_splice($children, $pos, 1);
865
                                $i--;
866
                            }
867
                            sort($childlist);
868
                            $clist = serialize($childlist);
869
                            scorm_seq_set('availablechildren', $scoid, $userid, false);
870
                            scorm_seq_set('availablechildren', $scoid, $userid, $clist);
871
                        }
872
                    }
873
                break;
874
            }
875
        }
876
    }
877
}
878
 
879
function scorm_randomize_children_process($scoid, $userid) {
880
    global $DB;
881
 
882
    $sco = scorm_get_sco($scoid);
883
    if (!scorm_is_leaf($sco)) {
884
        if (!scorm_seq_is('suspended', $scoid, $userid) && !scorm_seq_is('active', $scoid, $userid)) {
885
            $r = scorm_get_sco_value($scoid, $userid, 'randomizationtiming');
886
 
887
            switch ($r->value) {
888
                case 'never':
889
                break;
890
 
891
                case 'oneachnewattempt':
892
                case 'once':
893
                    if (!scorm_seq_is('activityprogressstatus', $scoid, $userid)) {
894
                        if (scorm_seq_is('randomizechildren', $scoid, $userid)) {
895
                            $childlist = array();
896
                            $res = scorm_get_available_children($sco);
897
                            $i = count($res) - 1;
898
                            $children = $res->value;
899
 
900
                            while ($i >= 0) {
901
                                $pos = array_rand($children);
902
                                array_push($childlist, $children[$pos]);
903
                                array_splice($children, $pos, 1);
904
                                $i--;
905
                            }
906
 
907
                            $clist = serialize ($childlist);
908
                            scorm_seq_set('availablechildren', $scoid, $userid, false);
909
                            scorm_seq_set('availablechildren', $scoid, $userid, $clist);
910
                        }
911
                    }
912
                break;
913
            }
914
        }
915
    }
916
}
917
 
918
function scorm_terminate_descendent_attempts($activity, $userid, $seq) {
919
    $ancestors = scorm_get_ancestors($seq->currentactivity);
920
    $commonpos = scorm_find_common_ancestor($ancestors, $activity);
921
    if ($commonpos !== false) {
922
        if ($activitypath = array_slice($ancestors, 1, $commonpos - 2)) {
923
            if (!empty($activitypath)) {
924
                foreach ($activitypath as $sco) {
925
                    scorm_seq_end_attempt($sco, $userid, $seq->attempt);
926
                }
927
            }
928
        }
929
    }
930
}
931
 
932
function scorm_sequencing_exception($seq) {
933
    global $OUTPUT;
934
    if ($seq->exception != null) {
935
        switch ($seq->exception) {
936
            case 'NB.2.1-1':
937
                echo $OUTPUT->notification("Sequencing session has already begun");
938
            break;
939
            case 'NB.2.1-2':
940
                echo $OUTPUT->notification("Sequencing session has not begun");
941
            break;
942
            case 'NB.2.1-3':
943
                echo $OUTPUT->notification("Suspended activity is not defined");
944
            break;
945
            case 'NB.2.1-4':
946
                echo $OUTPUT->notification("Flow Sequencing Control Model Violation");
947
            break;
948
            case 'NB.2.1-5':
949
                echo $OUTPUT->notification("Flow or Forward only Sequencing Control Model Violation");
950
            break;
951
            case 'NB.2.1-6':
952
                echo $OUTPUT->notification("No activity is previous to the root");
953
            break;
954
            case 'NB.2.1-7':
955
                echo $OUTPUT->notification("Unsupported Navigation Request");
956
            break;
957
            case 'NB.2.1-8':
958
                echo $OUTPUT->notification("Choice Exit Sequencing Control Model Violation");
959
            break;
960
            case 'NB.2.1-9':
961
                echo $OUTPUT->notification("No activities to consider");
962
            break;
963
            case 'NB.2.1-10':
964
                echo $OUTPUT->notification("Choice Sequencing Control Model Violation");
965
            break;
966
            case 'NB.2.1-11':
967
                echo $OUTPUT->notification("Target Activity does not exist");
968
            break;
969
            case 'NB.2.1-12':
970
                echo $OUTPUT->notification("Current Activity already terminated");
971
            break;
972
            case 'NB.2.1-13':
973
                echo $OUTPUT->notification("Undefined Navigation Request");
974
            break;
975
 
976
            case 'TB.2.3-1':
977
                echo $OUTPUT->notification("Current Activity already terminated");
978
            break;
979
            case 'TB.2.3-2':
980
                echo $OUTPUT->notification("Current Activity already terminated");
981
            break;
982
            case 'TB.2.3-4':
983
                echo $OUTPUT->notification("Current Activity already terminated");
984
            break;
985
            case 'TB.2.3-5':
986
                echo $OUTPUT->notification("Nothing to suspend; No active activities");
987
            break;
988
            case 'TB.2.3-6':
989
                echo $OUTPUT->notification("Nothing to abandon; No active activities");
990
            break;
991
 
992
            case 'SB.2.1-1':
993
                echo $OUTPUT->notification("Last activity in the tree");
994
            break;
995
            case 'SB.2.1-2':
996
                echo $OUTPUT->notification("Cluster has no available children");
997
            break;
998
            case 'SB.2.1-3':
999
                echo $OUTPUT->notification("No activity is previous to the root");
1000
            break;
1001
            case 'SB.2.1-4':
1002
                echo $OUTPUT->notification("Forward Only Sequencing Control Model Violation");
1003
            break;
1004
 
1005
            case 'SB.2.2-1':
1006
                echo $OUTPUT->notification("Flow Sequencing Control Model Violation");
1007
            break;
1008
            case 'SB.2.2-2':
1009
                echo $OUTPUT->notification("Activity unavailable");
1010
            break;
1011
 
1012
            case 'SB.2.3-1':
1013
                echo $OUTPUT->notification("Forward Traversal Blocked");
1014
            break;
1015
            case 'SB.2.3-2':
1016
                echo $OUTPUT->notification("Forward Only Sequencing Control Model Violation");
1017
            break;
1018
            case 'SB.2.3-3':
1019
                echo $OUTPUT->notification("No activity is previous to the root");
1020
            break;
1021
 
1022
            case 'SB.2.5-1':
1023
                echo $OUTPUT->notification("Sequencing session has already begun");
1024
            break;
1025
 
1026
            case 'SB.2.6-1':
1027
                echo $OUTPUT->notification("Sequencing session has already begun");
1028
            break;
1029
            case 'SB.2.6-2':
1030
                echo $OUTPUT->notification("No Suspended activity is defined");
1031
            break;
1032
 
1033
            case 'SB.2.7-1':
1034
                echo $OUTPUT->notification("Sequencing session has not begun");
1035
            break;
1036
            case 'SB.2.7-2':
1037
                echo $OUTPUT->notification("Flow Sequencing Control Model Violation");
1038
            break;
1039
 
1040
            case 'SB.2.8-1':
1041
                echo $OUTPUT->notification("Sequencing session has not begun");
1042
            break;
1043
            case 'SB.2.8-2':
1044
                echo $OUTPUT->notification("Flow Sequencing Control Model Violation");
1045
            break;
1046
 
1047
            case 'SB.2.9-1':
1048
                echo $OUTPUT->notification("No target for Choice");
1049
            break;
1050
            case 'SB.2.9-2':
1051
                echo $OUTPUT->notification("Target Activity does not exist or is unavailable");
1052
            break;
1053
            case 'SB.2.9-3':
1054
                echo $OUTPUT->notification("Target Activity hidden from choice");
1055
            break;
1056
            case 'SB.2.9-4':
1057
                echo $OUTPUT->notification("Choice Sequencing Control Model Violation");
1058
            break;
1059
            case 'SB.2.9-5':
1060
                echo $OUTPUT->notification("No activities to consider");
1061
            break;
1062
            case 'SB.2.9-6':
1063
                echo $OUTPUT->notification("Unable to activate target; target is not a child of the Current Activity");
1064
            break;
1065
            case 'SB.2.9-7':
1066
                echo $OUTPUT->notification("Choice Exit Sequencing Control Model Violation");
1067
            break;
1068
            case 'SB.2.9-8':
1069
                echo $OUTPUT->notification("Unable to choose target activity - constrained choice");
1070
            break;
1071
            case 'SB.2.9-9':
1072
                echo $OUTPUT->notification("Choice Request Prevented by Flow-only Activity");
1073
            break;
1074
 
1075
            case 'SB.2.10-1':
1076
                echo $OUTPUT->notification("Sequencing session has not begun");
1077
            break;
1078
            case 'SB.2.10-2':
1079
                echo $OUTPUT->notification("Current Activity is active or suspended");
1080
            break;
1081
            case 'SB.2.10-3':
1082
                echo $OUTPUT->notification("Flow Sequencing Control Model Violation");
1083
            break;
1084
 
1085
            case 'SB.2.11-1':
1086
                echo $OUTPUT->notification("Sequencing session has not begun");
1087
            break;
1088
            case 'SB.2.11-2':
1089
                echo $OUTPUT->notification("Current Activity has not been terminated");
1090
            break;
1091
 
1092
            case 'SB.2.12-2':
1093
                echo $OUTPUT->notification("Undefined Sequencing Request");
1094
            break;
1095
 
1096
            case 'DB.1.1-1':
1097
                echo $OUTPUT->notification("Cannot deliver a non-leaf activity");
1098
            break;
1099
            case 'DB.1.1-2':
1100
                echo $OUTPUT->notification("Nothing to deliver");
1101
            break;
1102
            case 'DB.1.1-3':
1103
                echo $OUTPUT->notification("Activity unavailable");
1104
            break;
1105
 
1106
            case 'DB.2-1':
1107
                echo $OUTPUT->notification("Identified activity is already active");
1108
            break;
1109
 
1110
        }
1111
 
1112
    }
1113
}