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