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