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
/**
18
 * functions used by SCORM 1.2/2004 packages.
19
 *
20
 * @package    mod_scorm
21
 * @copyright 1999 onwards Roberto Pinna
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
function scorm_get_resources($blocks) {
26
    $resources = array();
27
    foreach ($blocks as $block) {
28
        if ($block['name'] == 'RESOURCES' && isset($block['children'])) {
29
            foreach ($block['children'] as $resource) {
30
                if ($resource['name'] == 'RESOURCE') {
31
                    $resources[addslashes_js($resource['attrs']['IDENTIFIER'])] = $resource['attrs'];
32
                }
33
            }
34
        }
35
    }
36
    return $resources;
37
}
38
 
39
function scorm_get_manifest($blocks, $scoes) {
40
    global $OUTPUT;
41
    static $parents = array();
42
    static $resources;
43
 
44
    static $manifest;
45
    static $organization;
46
 
47
    $manifestresourcesnotfound = array();
48
    if (count($blocks) > 0) {
49
        foreach ($blocks as $block) {
50
            switch ($block['name']) {
51
                case 'METADATA':
52
                    if (isset($block['children'])) {
53
                        foreach ($block['children'] as $metadata) {
54
                            if ($metadata['name'] == 'SCHEMAVERSION') {
55
                                if (empty($scoes->version)) {
56
                                    $isversionset = (preg_match("/^(1\.2)$|^(CAM )?(1\.3)$/", $metadata['tagData'], $matches));
57
                                    if (isset($metadata['tagData']) && $isversionset) {
58
                                        $scoes->version = 'SCORM_'.$matches[count($matches) - 1];
59
                                    } else {
60
                                        $isversionset = (preg_match("/^2004 (3rd|4th) Edition$/", $metadata['tagData'], $matches));
61
                                        if (isset($metadata['tagData']) && $isversionset) {
62
                                            $scoes->version = 'SCORM_1.3';
63
                                        } else {
64
                                            $scoes->version = 'SCORM_1.2';
65
                                        }
66
                                    }
67
                                }
68
                            }
69
                        }
70
                    }
71
                break;
72
                case 'MANIFEST':
73
                    $manifest = $block['attrs']['IDENTIFIER'];
74
                    $organization = '';
75
                    $resources = array();
76
                    $resources = scorm_get_resources($block['children']);
77
                    $scoes = scorm_get_manifest($block['children'], $scoes);
78
                    if (empty($scoes->elements) || count($scoes->elements) <= 0) {
79
                        foreach ($resources as $item => $resource) {
80
                            if (!empty($resource['HREF'])) {
81
                                $sco = new stdClass();
82
                                $sco->identifier = $item;
83
                                $sco->title = $item;
84
                                $sco->parent = '/';
85
                                $sco->launch = $resource['HREF'];
86
                                $sco->scormtype = $resource['ADLCP:SCORMTYPE'];
87
                                $scoes->elements[$manifest][$organization][$item] = $sco;
88
                            }
89
                        }
90
                    }
91
                break;
92
                case 'ORGANIZATIONS':
93
                    if (!isset($scoes->defaultorg) && isset($block['attrs']['DEFAULT'])) {
94
                        $scoes->defaultorg = $block['attrs']['DEFAULT'];
95
                    }
96
                    if (!empty($block['children'])) {
97
                        $scoes = scorm_get_manifest($block['children'], $scoes);
98
                    }
99
                break;
100
                case 'ORGANIZATION':
101
                    $identifier = $block['attrs']['IDENTIFIER'];
102
                    $organization = '';
103
                    $scoes->elements[$manifest][$organization][$identifier] = new stdClass();
104
                    $scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier;
105
                    $scoes->elements[$manifest][$organization][$identifier]->parent = '/';
106
                    $scoes->elements[$manifest][$organization][$identifier]->launch = '';
107
                    $scoes->elements[$manifest][$organization][$identifier]->scormtype = '';
108
 
109
                    $parents = array();
110
                    $parent = new stdClass();
111
                    $parent->identifier = $identifier;
112
                    $parent->organization = $organization;
113
                    array_push($parents, $parent);
114
                    $organization = $identifier;
115
 
116
                    if (!empty($block['children'])) {
117
                        $scoes = scorm_get_manifest($block['children'], $scoes);
118
                    }
119
 
120
                    array_pop($parents);
121
                break;
122
                case 'ITEM':
123
                    $parent = array_pop($parents);
124
                    array_push($parents, $parent);
125
 
126
                    $identifier = $block['attrs']['IDENTIFIER'];
127
                    $scoes->elements[$manifest][$organization][$identifier] = new stdClass();
128
                    $scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier;
129
                    $scoes->elements[$manifest][$organization][$identifier]->parent = $parent->identifier;
130
                    if (!isset($block['attrs']['ISVISIBLE'])) {
131
                        $block['attrs']['ISVISIBLE'] = 'true';
132
                    }
133
                    $scoes->elements[$manifest][$organization][$identifier]->isvisible = $block['attrs']['ISVISIBLE'];
134
                    if (!isset($block['attrs']['PARAMETERS'])) {
135
                        $block['attrs']['PARAMETERS'] = '';
136
                    }
137
                    $scoes->elements[$manifest][$organization][$identifier]->parameters = $block['attrs']['PARAMETERS'];
138
                    if (!isset($block['attrs']['IDENTIFIERREF'])) {
139
                        $scoes->elements[$manifest][$organization][$identifier]->launch = '';
140
                        $scoes->elements[$manifest][$organization][$identifier]->scormtype = 'asset';
141
                    } else {
142
                        $idref = addslashes_js($block['attrs']['IDENTIFIERREF']);
143
                        $base = '';
144
                        if (isset($resources[$idref]['XML:BASE'])) {
145
                            $base = $resources[$idref]['XML:BASE'];
146
                        }
147
                        if (!isset($resources[$idref])) {
148
                            $manifestresourcesnotfound[] = $idref;
149
                            $scoes->elements[$manifest][$organization][$identifier]->launch = '';
150
                        } else {
151
                            $scoes->elements[$manifest][$organization][$identifier]->launch = $base.$resources[$idref]['HREF'];
152
                            if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) {
153
                                $resources[$idref]['ADLCP:SCORMTYPE'] = 'asset';
154
                            }
155
                            $scoes->elements[$manifest][$organization][$identifier]->scormtype = $resources[$idref]['ADLCP:SCORMTYPE'];
156
                        }
157
                    }
158
 
159
                    $parent = new stdClass();
160
                    $parent->identifier = $identifier;
161
                    $parent->organization = $organization;
162
                    array_push($parents, $parent);
163
 
164
                    if (!empty($block['children'])) {
165
                        $scoes = scorm_get_manifest($block['children'], $scoes);
166
                    }
167
 
168
                    array_pop($parents);
169
                break;
170
                case 'TITLE':
171
                    $parent = array_pop($parents);
172
                    array_push($parents, $parent);
173
                    if (!isset($block['tagData'])) {
174
                        $block['tagData'] = '';
175
                    }
176
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->title = $block['tagData'];
177
                break;
178
                case 'ADLCP:PREREQUISITES':
179
                    if ($block['attrs']['TYPE'] == 'aicc_script') {
180
                        $parent = array_pop($parents);
181
                        array_push($parents, $parent);
182
                        if (!isset($block['tagData'])) {
183
                            $block['tagData'] = '';
184
                        }
185
                        $scoes->elements[$manifest][$parent->organization][$parent->identifier]->prerequisites = $block['tagData'];
186
                    }
187
                break;
188
                case 'ADLCP:MAXTIMEALLOWED':
189
                    $parent = array_pop($parents);
190
                    array_push($parents, $parent);
191
                    if (!isset($block['tagData'])) {
192
                        $block['tagData'] = '';
193
                    }
194
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->maxtimeallowed = $block['tagData'];
195
                break;
196
                case 'ADLCP:TIMELIMITACTION':
197
                    $parent = array_pop($parents);
198
                    array_push($parents, $parent);
199
                    if (!isset($block['tagData'])) {
200
                        $block['tagData'] = '';
201
                    }
202
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->timelimitaction = $block['tagData'];
203
                break;
204
                case 'ADLCP:DATAFROMLMS':
205
                    $parent = array_pop($parents);
206
                    array_push($parents, $parent);
207
                    if (!isset($block['tagData'])) {
208
                        $block['tagData'] = '';
209
                    }
210
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->datafromlms = $block['tagData'];
211
                break;
212
                case 'ADLCP:MASTERYSCORE':
213
                    $parent = array_pop($parents);
214
                    array_push($parents, $parent);
215
                    if (!isset($block['tagData'])) {
216
                        $block['tagData'] = '';
217
                    }
218
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->masteryscore = $block['tagData'];
219
                break;
220
                case 'ADLCP:COMPLETIONTHRESHOLD':
221
                    $parent = array_pop($parents);
222
                    array_push($parents, $parent);
223
                    if (!isset($block['attrs']['MINPROGRESSMEASURE'])) {
224
                        $block['attrs']['MINPROGRESSMEASURE'] = '1.0';
225
                    }
226
                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->threshold = $block['attrs']['MINPROGRESSMEASURE'];
227
                break;
228
                case 'ADLNAV:PRESENTATION':
229
                    $parent = array_pop($parents);
230
                    array_push($parents, $parent);
231
                    if (!empty($block['children'])) {
232
                        foreach ($block['children'] as $adlnav) {
233
                            if ($adlnav['name'] == 'ADLNAV:NAVIGATIONINTERFACE') {
234
                                foreach ($adlnav['children'] as $adlnavinterface) {
235
                                    if ($adlnavinterface['name'] == 'ADLNAV:HIDELMSUI') {
236
                                        if ($adlnavinterface['tagData'] == 'continue') {
237
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hidecontinue = 1;
238
                                        }
239
                                        if ($adlnavinterface['tagData'] == 'previous') {
240
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideprevious = 1;
241
                                        }
242
                                        if ($adlnavinterface['tagData'] == 'exit') {
243
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideexit = 1;
244
                                        }
245
                                        if ($adlnavinterface['tagData'] == 'exitAll') {
246
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideexitall = 1;
247
                                        }
248
                                        if ($adlnavinterface['tagData'] == 'abandon') {
249
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideabandon = 1;
250
                                        }
251
                                        if ($adlnavinterface['tagData'] == 'abandonAll') {
252
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hideabandonall = 1;
253
                                        }
254
                                        if ($adlnavinterface['tagData'] == 'suspendAll') {
255
                                            $scoes->elements[$manifest][$parent->organization][$parent->identifier]->hidesuspendall = 1;
256
                                        }
257
                                    }
258
                                }
259
                            }
260
                        }
261
                    }
262
                break;
263
                case 'IMSSS:SEQUENCING':
264
                    $parent = array_pop($parents);
265
                    array_push($parents, $parent);
266
                    if (!empty($block['children'])) {
267
                        foreach ($block['children'] as $sequencing) {
268
                            if ($sequencing['name'] == 'IMSSS:CONTROLMODE') {
269
                                if (isset($sequencing['attrs']['CHOICE'])) {
270
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->choice =
271
                                        $sequencing['attrs']['CHOICE'] == 'true' ? 1 : 0;
272
                                }
273
                                if (isset($sequencing['attrs']['CHOICEEXIT'])) {
274
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->choiceexit =
275
                                        $sequencing['attrs']['CHOICEEXIT'] == 'true' ? 1 : 0;
276
                                }
277
                                if (isset($sequencing['attrs']['FLOW'])) {
278
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->flow =
279
                                        $sequencing['attrs']['FLOW'] == 'true' ? 1 : 0;
280
                                }
281
                                if (isset($sequencing['attrs']['FORWARDONLY'])) {
282
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->forwardonly =
283
                                        $sequencing['attrs']['FORWARDONLY'] == 'true' ? 1 : 0;
284
                                }
285
                                if (isset($sequencing['attrs']['USECURRENTATTEMPTOBJECTINFO'])) {
286
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptobjectinfo =
287
                                        $sequencing['attrs']['USECURRENTATTEMPTOBJECTINFO'] == 'true' ? 1 : 0;
288
                                }
289
                                if (isset($sequencing['attrs']['USECURRENTATTEMPTPROGRESSINFO'])) {
290
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptprogressinfo =
291
                                        $sequencing['attrs']['USECURRENTATTEMPTPROGRESSINFO'] == 'true' ? 1 : 0;
292
                                }
293
                            }
294
                            if ($sequencing['name'] == 'IMSSS:DELIVERYCONTROLS') {
295
                                if (isset($sequencing['attrs']['TRACKED'])) {
296
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->tracked =
297
                                        $sequencing['attrs']['TRACKED'] == 'true' ? 1 : 0;
298
                                }
299
                                if (isset($sequencing['attrs']['COMPLETIONSETBYCONTENT'])) {
300
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->completionsetbycontent =
301
                                        $sequencing['attrs']['COMPLETIONSETBYCONTENT'] == 'true' ? 1 : 0;
302
                                }
303
                                if (isset($sequencing['attrs']['OBJECTIVESETBYCONTENT'])) {
304
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectivesetbycontent =
305
                                        $sequencing['attrs']['OBJECTIVESETBYCONTENT'] == 'true' ? 1 : 0;
306
                                }
307
                            }
308
                            if ($sequencing['name'] == 'ADLSEQ:CONSTRAINEDCHOICECONSIDERATIONS') {
309
                                if (isset($sequencing['attrs']['CONSTRAINCHOICE'])) {
310
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->constrainChoice =
311
                                        $sequencing['attrs']['CONSTRAINCHOICE'] == 'true' ? 1 : 0;
312
                                }
313
                                if (isset($sequencing['attrs']['PREVENTACTIVATION'])) {
314
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->preventactivation =
315
                                        $sequencing['attrs']['PREVENTACTIVATION'] == 'true' ? 1 : 0;
316
                                }
317
                            }
318
                            if ($sequencing['name'] == 'IMSSS:OBJECTIVES') {
319
                                $objectives = array();
320
                                foreach ($sequencing['children'] as $objective) {
321
                                    $objectivedata = new stdClass();
322
                                    $objectivedata->primaryobj = 0;
323
                                    switch ($objective['name']) {
324
                                        case 'IMSSS:PRIMARYOBJECTIVE':
325
                                            $objectivedata->primaryobj = 1;
326
                                        case 'IMSSS:OBJECTIVE':
327
                                            $objectivedata->satisfiedbymeasure = 0;
328
                                            if (isset($objective['attrs']['SATISFIEDBYMEASURE'])) {
329
                                                $objectivedata->satisfiedbymeasure =
330
                                                    $objective['attrs']['SATISFIEDBYMEASURE'] == 'true' ? 1 : 0;
331
                                            }
332
                                            $objectivedata->objectiveid = '';
333
                                            if (isset($objective['attrs']['OBJECTIVEID'])) {
334
                                                $objectivedata->objectiveid = $objective['attrs']['OBJECTIVEID'];
335
                                            }
336
                                            $objectivedata->minnormalizedmeasure = 1.0;
337
                                            if (!empty($objective['children'])) {
338
                                                $mapinfos = array();
339
                                                foreach ($objective['children'] as $objectiveparam) {
340
                                                    if ($objectiveparam['name'] == 'IMSSS:MINNORMALIZEDMEASURE') {
341
                                                        if (isset($objectiveparam['tagData'])) {
342
                                                            $objectivedata->minnormalizedmeasure = $objectiveparam['tagData'];
343
                                                        } else {
344
                                                            $objectivedata->minnormalizedmeasure = 0;
345
                                                        }
346
                                                    }
347
                                                    if ($objectiveparam['name'] == 'IMSSS:MAPINFO') {
348
                                                        $mapinfo = new stdClass();
349
                                                        $mapinfo->targetobjectiveid = '';
350
                                                        if (isset($objectiveparam['attrs']['TARGETOBJECTIVEID'])) {
351
                                                            $mapinfo->targetobjectiveid =
352
                                                                $objectiveparam['attrs']['TARGETOBJECTIVEID'];
353
                                                        }
354
                                                        $mapinfo->readsatisfiedstatus = 1;
355
                                                        if (isset($objectiveparam['attrs']['READSATISFIEDSTATUS'])) {
356
                                                            $mapinfo->readsatisfiedstatus =
357
                                                                $objectiveparam['attrs']['READSATISFIEDSTATUS'] == 'true' ? 1 : 0;
358
                                                        }
359
                                                        $mapinfo->writesatisfiedstatus = 0;
360
                                                        if (isset($objectiveparam['attrs']['WRITESATISFIEDSTATUS'])) {
361
                                                            $mapinfo->writesatisfiedstatus =
362
                                                                $objectiveparam['attrs']['WRITESATISFIEDSTATUS'] == 'true' ? 1 : 0;
363
                                                        }
364
                                                        $mapinfo->readnormalizemeasure = 1;
365
                                                        if (isset($objectiveparam['attrs']['READNORMALIZEDMEASURE'])) {
366
                                                            $mapinfo->readnormalizemeasure =
367
                                                                $objectiveparam['attrs']['READNORMALIZEDMEASURE'] == 'true' ? 1 : 0;
368
                                                        }
369
                                                        $mapinfo->writenormalizemeasure = 0;
370
                                                        if (isset($objectiveparam['attrs']['WRITENORMALIZEDMEASURE'])) {
371
                                                            $mapinfo->writenormalizemeasure =
372
                                                                $objectiveparam['attrs']['WRITENORMALIZEDMEASURE'] == 'true' ? 1 : 0;
373
                                                        }
374
                                                        array_push($mapinfos, $mapinfo);
375
                                                    }
376
                                                }
377
                                                if (!empty($mapinfos)) {
378
                                                    $objectivesdata->mapinfos = $mapinfos;
379
                                                }
380
                                            }
381
                                        break;
382
                                    }
383
                                    array_push($objectives, $objectivedata);
384
                                }
385
                                $scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectives = $objectives;
386
                            }
387
                            if ($sequencing['name'] == 'IMSSS:LIMITCONDITIONS') {
388
                                if (isset($sequencing['attrs']['ATTEMPTLIMIT'])) {
389
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptLimit =
390
                                        $sequencing['attrs']['ATTEMPTLIMIT'];
391
                                }
392
                                if (isset($sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT'])) {
393
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptAbsoluteDurationLimit =
394
                                        $sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT'];
395
                                }
396
                            }
397
                            if ($sequencing['name'] == 'IMSSS:ROLLUPRULES') {
398
                                if (isset($sequencing['attrs']['ROLLUPOBJECTIVESATISFIED'])) {
399
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupobjectivesatisfied =
400
                                        $sequencing['attrs']['ROLLUPOBJECTIVESATISFIED'] == 'true' ? 1 : 0;
401
                                }
402
                                if (isset($sequencing['attrs']['ROLLUPPROGRESSCOMPLETION'])) {
403
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupprogresscompletion =
404
                                        $sequencing['attrs']['ROLLUPPROGRESSCOMPLETION'] == 'true' ? 1 : 0;
405
                                }
406
                                if (isset($sequencing['attrs']['OBJECTIVEMEASUREWEIGHT'])) {
407
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectivemeasureweight =
408
                                        $sequencing['attrs']['OBJECTIVEMEASUREWEIGHT'];
409
                                }
410
 
411
                                if (!empty($sequencing['children'])) {
412
                                    $rolluprules = array();
413
                                    foreach ($sequencing['children'] as $sequencingrolluprule) {
414
                                        if ($sequencingrolluprule['name'] == 'IMSSS:ROLLUPRULE' ) {
415
                                            $rolluprule = new stdClass();
416
                                            $rolluprule->childactivityset = 'all';
417
                                            if (isset($sequencingrolluprule['attrs']['CHILDACTIVITYSET'])) {
418
                                                $rolluprule->childactivityset = $sequencingrolluprule['attrs']['CHILDACTIVITYSET'];
419
                                            }
420
                                            $rolluprule->minimumcount = 0;
421
                                            if (isset($sequencingrolluprule['attrs']['MINIMUMCOUNT'])) {
422
                                                $rolluprule->minimumcount = $sequencingrolluprule['attrs']['MINIMUMCOUNT'];
423
                                            }
424
                                            $rolluprule->minimumpercent = 0.0000;
425
                                            if (isset($sequencingrolluprule['attrs']['MINIMUMPERCENT'])) {
426
                                                $rolluprule->minimumpercent = $sequencingrolluprule['attrs']['MINIMUMPERCENT'];
427
                                            }
428
                                            if (!empty($sequencingrolluprule['children'])) {
429
                                                foreach ($sequencingrolluprule['children'] as $rolluproleconditions) {
430
                                                    if ($rolluproleconditions['name'] == 'IMSSS:ROLLUPCONDITIONS') {
431
                                                        $conditions = array();
432
                                                        $rolluprule->conditioncombination = 'all';
433
                                                        if (isset($rolluproleconditions['attrs']['CONDITIONCOMBINATION'])) {
434
                                                            $rolluprule->CONDITIONCOMBINATION = $rolluproleconditions['attrs']['CONDITIONCOMBINATION'];
435
                                                        }
436
                                                        foreach ($rolluproleconditions['children'] as $rolluprulecondition) {
437
                                                            if ($rolluprulecondition['name'] == 'IMSSS:ROLLUPCONDITION') {
438
                                                                $condition = new stdClass();
439
                                                                if (isset($rolluprulecondition['attrs']['CONDITION'])) {
440
                                                                    $condition->cond = $rolluprulecondition['attrs']['CONDITION'];
441
                                                                }
442
                                                                $condition->operator = 'noOp';
443
                                                                if (isset($rolluprulecondition['attrs']['OPERATOR'])) {
444
                                                                    $condition->operator = $rolluprulecondition['attrs']['OPERATOR'];
445
                                                                }
446
                                                                array_push($conditions, $condition);
447
                                                            }
448
                                                        }
449
                                                        $rolluprule->conditions = $conditions;
450
                                                    }
451
                                                    if ($rolluproleconditions['name'] == 'IMSSS:ROLLUPACTION') {
452
                                                        $rolluprule->rollupruleaction = $rolluproleconditions['attrs']['ACTION'];
453
                                                    }
454
                                                }
455
                                            }
456
                                            array_push($rolluprules, $rolluprule);
457
                                        }
458
                                    }
459
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rolluprules = $rolluprules;
460
                                }
461
                            }
462
 
463
                            if ($sequencing['name'] == 'IMSSS:SEQUENCINGRULES') {
464
                                if (!empty($sequencing['children'])) {
465
                                    $sequencingrules = array();
466
                                    foreach ($sequencing['children'] as $conditionrules) {
467
                                        $conditiontype = -1;
468
                                        switch($conditionrules['name']) {
469
                                            case 'IMSSS:PRECONDITIONRULE':
470
                                                $conditiontype = 0;
471
                                            break;
472
                                            case 'IMSSS:POSTCONDITIONRULE':
473
                                                $conditiontype = 1;
474
                                            break;
475
                                            case 'IMSSS:EXITCONDITIONRULE':
476
                                                $conditiontype = 2;
477
                                            break;
478
                                        }
479
                                        if (!empty($conditionrules['children'])) {
480
                                            $sequencingrule = new stdClass();
481
                                            foreach ($conditionrules['children'] as $conditionrule) {
482
                                                if ($conditionrule['name'] == 'IMSSS:RULECONDITIONS') {
483
                                                    $ruleconditions = array();
484
                                                    $sequencingrule->conditioncombination = 'all';
485
                                                    if (isset($conditionrule['attrs']['CONDITIONCOMBINATION'])) {
486
                                                        $sequencingrule->conditioncombination = $conditionrule['attrs']['CONDITIONCOMBINATION'];
487
                                                    }
488
                                                    foreach ($conditionrule['children'] as $rulecondition) {
489
                                                        if ($rulecondition['name'] == 'IMSSS:RULECONDITION') {
490
                                                            $condition = new stdClass();
491
                                                            if (isset($rulecondition['attrs']['CONDITION'])) {
492
                                                                $condition->cond = $rulecondition['attrs']['CONDITION'];
493
                                                            }
494
                                                            $condition->operator = 'noOp';
495
                                                            if (isset($rulecondition['attrs']['OPERATOR'])) {
496
                                                                $condition->operator = $rulecondition['attrs']['OPERATOR'];
497
                                                            }
498
                                                            $condition->measurethreshold = 0.0000;
499
                                                            if (isset($rulecondition['attrs']['MEASURETHRESHOLD'])) {
500
                                                                $condition->measurethreshold = $rulecondition['attrs']['MEASURETHRESHOLD'];
501
                                                            }
502
                                                            $condition->referencedobjective = '';
503
                                                            if (isset($rulecondition['attrs']['REFERENCEDOBJECTIVE'])) {
504
                                                                $condition->referencedobjective = $rulecondition['attrs']['REFERENCEDOBJECTIVE'];
505
                                                            }
506
                                                            array_push($ruleconditions, $condition);
507
                                                        }
508
                                                    }
509
                                                    $sequencingrule->ruleconditions = $ruleconditions;
510
                                                }
511
                                                if ($conditionrule['name'] == 'IMSSS:RULEACTION') {
512
                                                    $sequencingrule->action = $conditionrule['attrs']['ACTION'];
513
                                                }
514
                                                $sequencingrule->type = $conditiontype;
515
                                            }
516
                                            array_push($sequencingrules, $sequencingrule);
517
                                        }
518
                                    }
519
                                    $scoes->elements[$manifest][$parent->organization][$parent->identifier]->sequencingrules = $sequencingrules;
520
                                }
521
                            }
522
                        }
523
                    }
524
                break;
525
            }
526
        }
527
    }
528
    if (!empty($manifestresourcesnotfound)) {
529
        // Throw warning to user to let them know manifest contains references to resources that don't appear to exist.
530
        if (!defined('DEBUGGING_PRINTED')) {
531
            // Prevent redirect and display warning.
532
            define('DEBUGGING_PRINTED', 1);
533
        }
534
        echo $OUTPUT->notification(get_string('invalidmanifestresource', 'scorm').' '. implode(', ', $manifestresourcesnotfound));
535
    }
536
    return $scoes;
537
}
538
 
539
/**
540
 * Sets up SCORM 1.2/2004 packages using the manifest file.
541
 * Called whenever SCORM changes
542
 * @param object $scorm instance - fields are updated and changes saved into database
543
 * @param stored_file|string $manifest - path to manifest file or stored_file.
544
 * @return bool
545
 */
546
function scorm_parse_scorm(&$scorm, $manifest) {
547
    global $CFG, $DB;
548
 
549
    // Load manifest into string.
550
    if ($manifest instanceof stored_file) {
551
        $xmltext = $manifest->get_content();
552
    } else {
553
        require_once("$CFG->libdir/filelib.php");
554
        $xmltext = download_file_content($manifest);
555
    }
556
 
557
    $defaultorgid = 0;
558
    $firstinorg = 0;
559
 
560
    $pattern = '/&(?!\w{2,6};)/';
561
    $replacement = '&amp;';
562
    $xmltext = preg_replace($pattern, $replacement, $xmltext);
563
 
564
    $objxml = new xml2Array();
565
    $manifests = $objxml->parse($xmltext);
566
    $scoes = new stdClass();
567
    $scoes->version = '';
568
    $scoes = scorm_get_manifest($manifests, $scoes);
569
    $newscoes = array();
570
    $sortorder = 0;
571
    if (!empty($scoes->elements) && is_iterable($scoes->elements)) {
572
        $olditems = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id));
573
        foreach ($scoes->elements as $manifest => $organizations) {
574
            foreach ($organizations as $organization => $items) {
575
                foreach ($items as $identifier => $item) {
576
                    $sortorder++;
577
                    // This new db mngt will support all SCORM future extensions.
578
                    $newitem = new stdClass();
579
                    $newitem->scorm = $scorm->id;
580
                    $newitem->manifest = $manifest;
581
                    $newitem->organization = $organization;
582
                    $newitem->sortorder = $sortorder;
583
                    $standarddatas = array('parent', 'identifier', 'launch', 'scormtype', 'title');
584
                    foreach ($standarddatas as $standarddata) {
585
                        if (isset($item->$standarddata)) {
586
                            $newitem->$standarddata = $item->$standarddata;
587
                        } else {
588
                            $newitem->$standarddata = '';
589
                        }
590
                    }
591
 
592
                    if (!empty($defaultorgid) && !empty($scoes->defaultorg) && empty($firstinorg) &&
593
                        $newitem->parent == $scoes->defaultorg) {
594
 
595
                        $firstinorg = $sortorder;
596
                    }
597
 
598
                    if (!empty($olditems) && ($olditemid = scorm_array_search('identifier', $newitem->identifier, $olditems))) {
599
                        $newitem->id = $olditemid;
600
                        // Update the Sco sortorder but keep id so that user tracks are kept against the same ids.
601
                        $DB->update_record('scorm_scoes', $newitem);
602
                        $id = $olditemid;
603
                        // Remove all old data so we don't duplicate it.
604
                        $DB->delete_records('scorm_scoes_data', array('scoid' => $olditemid));
605
                        $DB->delete_records('scorm_seq_objective', array('scoid' => $olditemid));
606
                        $DB->delete_records('scorm_seq_mapinfo', array('scoid' => $olditemid));
607
                        $DB->delete_records('scorm_seq_ruleconds', array('scoid' => $olditemid));
608
                        $DB->delete_records('scorm_seq_rulecond', array('scoid' => $olditemid));
609
                        $DB->delete_records('scorm_seq_rolluprule', array('scoid' => $olditemid));
610
                        $DB->delete_records('scorm_seq_rolluprulecond', array('scoid' => $olditemid));
611
 
612
                        // Now remove this SCO from the olditems object as we have dealt with it.
613
                        unset($olditems[$olditemid]);
614
                    } else {
615
                        // Insert the new SCO, and retain the link between the old and new for later adjustment.
616
                        $id = $DB->insert_record('scorm_scoes', $newitem);
617
                    }
618
                    // Save this sco in memory so we can use it later.
619
                    $newscoes[$id] = $newitem;
620
 
621
                    if ($optionaldatas = scorm_optionals_data($item, $standarddatas)) {
622
                        $data = new stdClass();
623
                        $data->scoid = $id;
624
                        foreach ($optionaldatas as $optionaldata) {
625
                            if (isset($item->$optionaldata)) {
626
                                $data->name = $optionaldata;
627
                                $data->value = $item->$optionaldata;
628
                                $dataid = $DB->insert_record('scorm_scoes_data', $data);
629
                            }
630
                        }
631
                    }
632
 
633
                    if (isset($item->sequencingrules)) {
634
                        foreach ($item->sequencingrules as $sequencingrule) {
635
                            $rule = new stdClass();
636
                            $rule->scoid = $id;
637
                            $rule->ruletype = $sequencingrule->type;
638
                            $rule->conditioncombination = $sequencingrule->conditioncombination;
639
                            $rule->action = $sequencingrule->action;
640
                            $ruleid = $DB->insert_record('scorm_seq_ruleconds', $rule);
641
                            if (isset($sequencingrule->ruleconditions)) {
642
                                foreach ($sequencingrule->ruleconditions as $rulecondition) {
643
                                    $rulecond = new stdClass();
644
                                    $rulecond->scoid = $id;
645
                                    $rulecond->ruleconditionsid = $ruleid;
646
                                    $rulecond->referencedobjective = $rulecondition->referencedobjective;
647
                                    $rulecond->measurethreshold = $rulecondition->measurethreshold;
648
                                    $rulecond->operator = $rulecondition->operator;
649
                                    $rulecond->cond = $rulecondition->cond;
650
                                    $rulecondid = $DB->insert_record('scorm_seq_rulecond', $rulecond);
651
                                }
652
                            }
653
                        }
654
                    }
655
 
656
                    if (isset($item->rolluprules)) {
657
                        foreach ($item->rolluprules as $rolluprule) {
658
                            $rollup = new stdClass();
659
                            $rollup->scoid = $id;
660
                            $rollup->childactivityset = $rolluprule->childactivityset;
661
                            $rollup->minimumcount = $rolluprule->minimumcount;
662
                            $rollup->minimumpercent = $rolluprule->minimumpercent;
663
                            $rollup->rollupruleaction = $rolluprule->rollupruleaction;
664
                            $rollup->conditioncombination = $rolluprule->conditioncombination;
665
 
666
                            $rollupruleid = $DB->insert_record('scorm_seq_rolluprule', $rollup);
667
                            if (isset($rollup->conditions)) {
668
                                foreach ($rollup->conditions as $condition) {
669
                                    $cond = new stdClass();
670
                                    $cond->scoid = $rollup->scoid;
671
                                    $cond->rollupruleid = $rollupruleid;
672
                                    $cond->operator = $condition->operator;
673
                                    $cond->cond = $condition->cond;
674
                                    $conditionid = $DB->insert_record('scorm_seq_rolluprulecond', $cond);
675
                                }
676
                            }
677
                        }
678
                    }
679
 
680
                    if (isset($item->objectives)) {
681
                        foreach ($item->objectives as $objective) {
682
                            $obj = new stdClass();
683
                            $obj->scoid = $id;
684
                            $obj->primaryobj = $objective->primaryobj;
685
                            $obj->satisfiedbumeasure = $objective->satisfiedbymeasure;
686
                            $obj->objectiveid = $objective->objectiveid;
687
                            $obj->minnormalizedmeasure = trim($objective->minnormalizedmeasure);
688
                            $objectiveid = $DB->insert_record('scorm_seq_objective', $obj);
689
                            if (isset($objective->mapinfos)) {
690
                                foreach ($objective->mapinfos as $objmapinfo) {
691
                                    $mapinfo = new stdClass();
692
                                    $mapinfo->scoid = $id;
693
                                    $mapinfo->objectiveid = $objectiveid;
694
                                    $mapinfo->targetobjectiveid = $objmapinfo->targetobjectiveid;
695
                                    $mapinfo->readsatisfiedstatus = $objmapinfo->readsatisfiedstatus;
696
                                    $mapinfo->writesatisfiedstatus = $objmapinfo->writesatisfiedstatus;
697
                                    $mapinfo->readnormalizedmeasure = $objmapinfo->readnormalizedmeasure;
698
                                    $mapinfo->writenormalizedmeasure = $objmapinfo->writenormalizedmeasure;
699
                                    $mapinfoid = $DB->insert_record('scorm_seq_mapinfo', $mapinfo);
700
                                }
701
                            }
702
                        }
703
                    }
704
                    if (empty($defaultorgid) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) {
705
                        $defaultorgid = $id;
706
                    }
707
                }
708
            }
709
        }
710
        if (!empty($olditems)) {
711
            foreach ($olditems as $olditem) {
712
                $DB->delete_records('scorm_scoes', ['id' => $olditem->id]);
713
                $DB->delete_records('scorm_scoes_data', ['scoid' => $olditem->id]);
714
                scorm_delete_tracks($scorm->id, $olditem->id);
715
                $DB->delete_records('scorm_seq_objective', ['scoid' => $olditem->id]);
716
                $DB->delete_records('scorm_seq_mapinfo', ['scoid' => $olditem->id]);
717
                $DB->delete_records('scorm_seq_ruleconds', ['scoid' => $olditem->id]);
718
                $DB->delete_records('scorm_seq_rulecond', ['scoid' => $olditem->id]);
719
                $DB->delete_records('scorm_seq_rolluprule', ['scoid' => $olditem->id]);
720
                $DB->delete_records('scorm_seq_rolluprulecond', ['scoid' => $olditem->id]);
721
            }
722
        }
723
        if (empty($scoes->version)) {
724
            $scoes->version = 'SCORM_1.2';
725
        }
726
        $DB->set_field('scorm', 'version', $scoes->version, array('id' => $scorm->id));
727
        $scorm->version = $scoes->version;
728
    }
729
    $scorm->launch = 0;
730
    // Check launch sco is valid.
731
    if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && !empty($newscoes[$defaultorgid]->launch)) {
732
        // Launch param is valid - do nothing.
733
        $scorm->launch = $defaultorgid;
734
    } else if (!empty($defaultorgid) && isset($newscoes[$defaultorgid]) && empty($newscoes[$defaultorgid]->launch)) {
735
        // The launch is probably the default org so we need to find the first launchable item inside this org.
736
        $sqlselect = 'scorm = ? AND sortorder >= ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
737
        // We use get_records here as we need to pass a limit in the query that works cross db.
738
        $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id, $firstinorg), 'sortorder', 'id', 0, 1);
739
        if (!empty($scoes)) {
740
            $sco = reset($scoes); // We only care about the first record - the above query only returns one.
741
            $scorm->launch = $sco->id;
742
        }
743
    }
744
    if (empty($scorm->launch)) {
745
        // No valid Launch is specified - find the first launchable sco instead.
746
        $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
747
        // We use get_records here as we need to pass a limit in the query that works cross db.
748
        $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1);
749
        if (!empty($scoes)) {
750
            $sco = reset($scoes); // We only care about the first record - the above query only returns one.
751
            $scorm->launch = $sco->id;
752
        }
753
    }
754
 
755
    return true;
756
}
757
 
758
function scorm_optionals_data($item, $standarddata) {
759
    $result = array();
760
    $sequencingdata = array('sequencingrules', 'rolluprules', 'objectives');
761
    foreach ($item as $element => $value) {
762
        if (! in_array($element, $standarddata)) {
763
            if (! in_array($element, $sequencingdata)) {
764
                $result[] = $element;
765
            }
766
        }
767
    }
768
    return $result;
769
}
770
 
771
function scorm_is_leaf($sco) {
772
    global $DB;
773
 
774
    if ($DB->record_exists('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier))) {
775
        return false;
776
    }
777
    return true;
778
}
779
 
780
function scorm_get_parent($sco) {
781
    global $DB;
782
 
783
    if ($sco->parent != '/') {
784
        if ($parent = $DB->get_record('scorm_scoes', array('scorm' => $sco->scorm, 'identifier' => $sco->parent))) {
785
            return scorm_get_sco($parent->id);
786
        }
787
    }
788
    return null;
789
}
790
 
791
function scorm_get_children($sco) {
792
    global $DB;
793
 
794
    $children = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier), 'sortorder, id');
795
    if (!empty($children)) {
796
        return $children;
797
    }
798
    return null;
799
}
800
 
801
function scorm_get_available_children($sco) {
802
    global $DB;
803
 
804
    $res = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->identifier), 'sortorder, id');
805
    if (!$res || $res == null) {
806
        return false;
807
    } else {
808
        foreach ($res as $sco) {
809
            $result[] = $sco;
810
        }
811
        return $result;
812
    }
813
}
814
 
815
function scorm_get_available_descendent($descend, $sco) {
816
    if ($sco == null) {
817
        return $descend;
818
    } else {
819
        $avchildren = scorm_get_available_children($sco);
820
        foreach ($avchildren as $avchild) {
821
            array_push($descend, $avchild);
822
        }
823
        foreach ($avchildren as $avchild) {
824
            scorm_get_available_descendent($descend, $avchild);
825
        }
826
    }
827
}
828
 
829
function scorm_get_siblings($sco) {
830
    global $DB;
831
 
832
    if ($siblings = $DB->get_records('scorm_scoes', array('scorm' => $sco->scorm, 'parent' => $sco->parent), 'sortorder, id')) {
833
        unset($siblings[$sco->id]);
834
        if (!empty($siblings)) {
835
            return $siblings;
836
        }
837
    }
838
    return null;
839
}
840
// Get an array that contains all the parent scos for this sco.
841
function scorm_get_ancestors($sco) {
842
    $ancestors = array();
843
    $continue = true;
844
    while ($continue) {
845
        $ancestor = scorm_get_parent($sco);
846
        if (!empty($ancestor) && $ancestor->id !== $sco->id) {
847
            $sco = $ancestor;
848
            $ancestors[] = $ancestor;
849
            if ($sco->parent == '/') {
850
                $continue = false;
851
            }
852
        } else {
853
            $continue = false;
854
        }
855
    }
856
    return $ancestors;
857
}
858
 
859
function scorm_get_preorder(&$preorder = array(), $sco = null) {
860
    if ($sco != null) {
861
        array_push($preorder, $sco);
862
        if ($children = scorm_get_children($sco)) {
863
            foreach ($children as $child) {
864
                scorm_get_preorder($preorder, $child);
865
            }
866
        }
867
    }
868
    return $preorder;
869
}
870
 
871
function scorm_find_common_ancestor($ancestors, $sco) {
872
    $pos = scorm_array_search('identifier', $sco->parent, $ancestors);
873
    if ($sco->parent != '/') {
874
        if ($pos === false) {
875
            return scorm_find_common_ancestor($ancestors, scorm_get_parent($sco));
876
        }
877
    }
878
    return $pos;
879
}
880
 
881
/* Usage
882
 Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML;
883
 
884
 $objXML = new xml2Array();
885
 $arroutput = $objXML->parse($strYourXML);
886
 print_r($arroutput); //print it out, or do whatever!
887
 
888
*/
889
class xml2Array {
890
 
891
    public $arroutput = array();
892
    public $resparser;
893
    public $strxmldata;
894
 
895
    /**
896
     * Convert a utf-8 string to html entities
897
     *
898
     * @param string $str The UTF-8 string
899
     * @return string
900
     */
901
    public function utf8_to_entities($str) {
902
        global $CFG;
903
 
904
        $entities = '';
905
        $values = array();
906
        $lookingfor = 1;
907
 
908
        return $str;
909
    }
910
 
911
    /**
912
     * Parse an XML text string and create an array tree that rapresent the XML structure
913
     *
914
     * @param string $strinputxml The XML string
915
     * @return array
916
     */
917
    public function parse($strinputxml) {
918
        $this->resparser = xml_parser_create ('UTF-8');
919
        xml_set_object($this->resparser, $this);
920
        xml_set_element_handler($this->resparser, "tagopen", "tagclosed");
921
 
922
        xml_set_character_data_handler($this->resparser, "tagdata");
923
 
924
        $this->strxmldata = xml_parse($this->resparser, $strinputxml );
925
        if (!$this->strxmldata) {
926
            die(sprintf("XML error: %s at line %d",
927
            xml_error_string(xml_get_error_code($this->resparser)),
928
            xml_get_current_line_number($this->resparser)));
929
        }
930
 
931
        xml_parser_free($this->resparser);
932
 
933
        return $this->arroutput;
934
    }
935
 
936
    public function tagopen($parser, $name, $attrs) {
937
        $tag = array("name" => $name, "attrs" => $attrs);
938
        array_push($this->arroutput, $tag);
939
    }
940
 
941
    public function tagdata($parser, $tagdata) {
942
        if (trim($tagdata)) {
943
            if (isset($this->arroutput[count($this->arroutput) - 1]['tagData'])) {
944
                $this->arroutput[count($this->arroutput) - 1]['tagData'] .= $this->utf8_to_entities($tagdata);
945
            } else {
946
                $this->arroutput[count($this->arroutput) - 1]['tagData'] = $this->utf8_to_entities($tagdata);
947
            }
948
        }
949
    }
950
 
951
    public function tagclosed($parser, $name) {
952
        $this->arroutput[count($this->arroutput) - 2]['children'][] = $this->arroutput[count($this->arroutput) - 1];
953
        array_pop($this->arroutput);
954
    }
955
 
956
}