See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 403]
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 = '&'; 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', array('id' => $olditem->id)); 713 $DB->delete_records('scorm_scoes_data', array('scoid' => $olditem->id)); 714 $DB->delete_records('scorm_scoes_track', array('scoid' => $olditem->id)); 715 $DB->delete_records('scorm_seq_objective', array('scoid' => $olditem->id)); 716 $DB->delete_records('scorm_seq_mapinfo', array('scoid' => $olditem->id)); 717 $DB->delete_records('scorm_seq_ruleconds', array('scoid' => $olditem->id)); 718 $DB->delete_records('scorm_seq_rulecond', array('scoid' => $olditem->id)); 719 $DB->delete_records('scorm_seq_rolluprule', array('scoid' => $olditem->id)); 720 $DB->delete_records('scorm_seq_rolluprulecond', array('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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body