AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.defined('MOODLE_INTERNAL') || die();require_once($CFG->dirroot.'/mod/scorm/datamodels/scormlib.php');require_once($CFG->dirroot.'/mod/scorm/datamodels/sequencinglib.php');function scorm_seq_overall ($scoid, $userid, $request, $attempt) {$seq = scorm_seq_navigation($scoid, $userid, $request, $attempt);if ($seq->navigation) {if ($seq->termination != null) {$seq = scorm_seq_termination($scoid, $userid, $seq);}if ($seq->sequencing != null) {$seq = scorm_seq_sequencing($scoid, $userid, $seq);if ($seq->sequencing == 'exit') { // Return the control to the LTS.return 'true';}}if ($seq->delivery != null) {$seq = scorm_sequencing_delivery($scoid, $userid, $seq);$seq = scorm_content_delivery_environment ($seq, $userid);}}if ($seq->exception != null) {$seq = scorm_sequencing_exception($seq);}return 'true';}function scorm_seq_navigation ($scoid, $userid, $request, $attempt=0) {global $DB;// Sequencing structure.$seq = new stdClass();$seq->currentactivity = scorm_get_sco($scoid);$seq->traversaldir = null;$seq->nextactivity = null;$seq->deliveryvalid = null;$seq->attempt = $attempt;$seq->identifiedactivity = null;$seq->delivery = null;$seq->deliverable = false;$seq->active = scorm_seq_is('active', $scoid, $userid);$seq->suspended = scorm_seq_is('suspended', $scoid, $userid);$seq->navigation = null;$seq->termination = null;$seq->sequencing = null;$seq->target = null;$seq->endsession = null;$seq->exception = null;$seq->reachable = true;$seq->prevact = true;$sco = scorm_get_sco($scoid);switch ($request) {case 'start_':if (empty($seq->currentactivity)) {$seq->navigation = true;$seq->sequencing = 'start';} else {$seq->exception = 'NB.2.1-1'; // Sequencing session already begun.}break;case 'resumeall_':if (empty($seq->currentactivity)) {// TODO: I think it's suspend instead of suspendedactivity.if (scorm_get_sco_value($scoid, $userid, 'suspendedactivity')) {$seq->navigation = true;$seq->sequencing = 'resumeall';} else {$seq->exception = 'NB.2.1-3'; // No suspended activity found.}} else {$seq->exception = 'NB.2.1-1'; // Sequencing session already begun.}break;case 'continue_':case 'previous_':if (!empty($seq->currentactivity)) {$sco = $seq->currentactivity;if ($sco->parent != '/') {if ($parentsco = scorm_get_parent($sco)) {if (isset($parentsco->flow) && ($parentsco->flow == true)) { // I think it's parentsco.// Current activity is active!if (scorm_seq_is('active', $sco->id, $userid)) {if ($request == 'continue_') {$seq->navigation = true;$seq->termination = 'exit';$seq->sequencing = 'continue';} else {if (!isset($parentsco->forwardonly) || ($parentsco->forwardonly == false)) {$seq->navigation = true;$seq->termination = 'exit';$seq->sequencing = 'previous';} else {$seq->exception = 'NB.2.1-5'; // Violates control mode.}}}}}}} else {$seq->exception = 'NB.2.1-2'; // Current activity not defined.}break;case 'forward_':case 'backward_':$seq->exception = 'NB.2.1-7'; // None to be done, behavior not defined.break;case 'exit_':case 'abandon_':if (!empty($seq->currentactivity)) {// Current activity is active !$seq->navigation = true;$seq->termination = substr($request, 0, -1);$seq->sequencing = 'exit';} else {$seq->exception = 'NB.2.1-2'; // Current activity not defined.}case 'exitall_':case 'abandonall_':case 'suspendall_':if (!empty($seq->currentactivity)) {$seq->navigation = true;$seq->termination = substr($request, 0, -1);$seq->sequencing = 'exit';} else {$seq->exception = 'NB.2.1-2'; // Current activity not defined.}break;default: // Example {target=<STRING>}choice.if ($targetsco = $DB->get_record('scorm_scoes', array('scorm' => $sco->scorm, 'identifier' => $request))) {if ($targetsco->parent != '/') {$seq->target = $request;} else {if ($parentsco = scorm_get_parent($targetsco)) {if (!isset($parentsco->choice) || ($parentsco->choice == true)) {$seq->target = $request;}}}if ($seq->target != null) {if (empty($seq->currentactivity)) {$seq->navigation = true;$seq->sequencing = 'choice';} else {if (!$sco = scorm_get_sco($scoid)) {return $seq;}if ($sco->parent != $targetsco->parent) {$ancestors = scorm_get_ancestors($sco);$commonpos = scorm_find_common_ancestor($ancestors, $targetsco);if ($commonpos !== false) {if ($activitypath = array_slice($ancestors, 0, $commonpos)) {foreach ($activitypath as $activity) {if (scorm_seq_is('active', $activity->id, $userid) &&(isset($activity->choiceexit) && ($activity->choiceexit == false))) {$seq->navigation = false;$seq->termination = null;$seq->sequencing = null;$seq->target = null;$seq->exception = 'NB.2.1-8'; // Violates control mode.return $seq;}}} else {$seq->navigation = false;$seq->termination = null;$seq->sequencing = null;$seq->target = null;$seq->exception = 'NB.2.1-9';}}}// Current activity is active !$seq->navigation = true;$seq->sequencing = 'choice';}} else {$seq->exception = 'NB.2.1-10'; // Violates control mode.}} else {$seq->exception = 'NB.2.1-11'; // Target activity does not exists.}break;}return $seq;}function scorm_seq_termination ($seq, $userid) {if (empty($seq->currentactivity)) {$seq->termination = false;$seq->exception = 'TB.2.3-1';return $seq;}$sco = $seq->currentactivity;if ((($seq->termination == 'exit') || ($seq->termination == 'abandon')) && !$seq->active) {$seq->termination = false;$seq->exception = 'TB.2.3-2';return $seq;}switch ($seq->termination) {case 'exit':scorm_seq_end_attempt($sco, $userid, $seq);$seq = scorm_seq_exit_action_rules($seq, $userid);do {$exit = false;// I think this is false. Originally this was true.$seq = scorm_seq_post_cond_rules($seq, $userid);if ($seq->termination == 'exitparent') {if ($sco->parent != '/') {$sco = scorm_get_parent($sco);$seq->currentactivity = $sco;$seq->active = scorm_seq_is('active', $sco->id, $userid);scorm_seq_end_attempt($sco, $userid, $seq);$exit = true; // I think it's true. Originally this was false.} else {$seq->termination = false;$seq->exception = 'TB.2.3-4';return $seq;}}} while (($exit == false) && ($seq->termination == 'exit'));if ($seq->termination == 'exit') {$seq->termination = true;return $seq;}case 'exitall':if ($seq->active) {scorm_seq_end_attempt($sco, $userid, $seq);}// Terminate Descendent Attempts Process.if ($ancestors = scorm_get_ancestors($sco)) {foreach ($ancestors as $ancestor) {scorm_seq_end_attempt($ancestor, $userid, $seq);$seq->currentactivity = $ancestor;}}$seq->active = scorm_seq_is('active', $seq->currentactivity->id, $userid);$seq->termination = true;$seq->sequencing = 'exit';break;case 'suspendall':if (($seq->active) || ($seq->suspended)) {scorm_seq_set('suspended', $sco->id, $userid, $attempt);} else {if ($sco->parent != '/') {$parentsco = scorm_get_parent($sco);scorm_seq_set('suspended', $parentsco->id, $userid, $attempt);} else {$seq->termination = false;$seq->exception = 'TB.2.3-3';}}if ($ancestors = scorm_get_ancestors($sco)) {foreach ($ancestors as $ancestor) {scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);scorm_seq_set('suspended', $ancestor->id, $userid, $attempt);$seq->currentactivity = $ancestor;}$seq->termination = true;$seq->sequencing = 'exit';} else {$seq->termination = false;$seq->exception = 'TB.2.3-5';}break;case 'abandon':scorm_seq_set('active', $sco->id, $userid, $attempt, false);$seq->active = null;$seq->termination = true;break;case 'abandonall':if ($ancestors = scorm_get_ancestors($sco)) {foreach ($ancestors as $ancestor) {scorm_seq_set('active', $ancestor->id, $userid, $attempt, false);$seq->currentactivity = $ancestor;}$seq->termination = true;$seq->sequencing = 'exit';} else {$seq->termination = false;$seq->exception = 'TB.2.3-6';}break;default:$seq->termination = false;$seq->exception = 'TB.2.3-7';break;}return $seq;}function scorm_seq_end_attempt($sco, $userid, $seq) {global $DB;if (scorm_is_leaf($sco)) {if (!isset($sco->tracked) || ($sco->tracked == 1)) {if (!scorm_seq_is('suspended', $sco->id, $userid)) {if (!isset($sco->completionsetbycontent) || ($sco->completionsetbycontent == 0)) {if (!scorm_seq_is('attemptprogressstatus', $sco->id, $userid, $seq->attempt)) {$r = scorm_get_sco_value($sco->id, $userid, 'cmi.completion_status');if ($r->value != 'incomplete') {scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt);scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt);}}}if (!isset($sco->objectivesetbycontent) || ($sco->objectivesetbycontent == 0)) {if ($objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id))) {foreach ($objectives as $objective) {if ($objective->primaryobj) {if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $seq->attempt)) {scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $seq->attempt);scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $seq->attempt);}}}}}}}} else if ($children = scorm_get_children($sco)) {$suspended = false;foreach ($children as $child) {if (scorm_seq_is('suspended', $child, $userid, $seq->attempt)) {$suspended = true;break;}}if ($suspended) {scorm_seq_set('suspended', $sco, $userid, $seq->attempt);} else {scorm_seq_set('suspended', $sco, $userid, $seq->attempt, false);}}scorm_seq_set('active', $sco->id, $userid, $seq->attempt, false);scorm_seq_overall_rollup($sco, $userid, $seq);}function scorm_seq_is($what, $scoid, $userid, $attempt=0) {// Check if passed activity $what is active.$active = false;if (scorm_get_sco_value($scoid, $userid, $what, $attempt)) {$active = true;}return $active;}function scorm_seq_set($what, $scoid, $userid, $attempt=0, $value='true') {global $DB;$sco = scorm_get_sco($scoid);// Set passed activity to active or not.if ($value == false) {$params = ['userid' => $userid, 'scormid' => $sco->scorm, 'attempt' => $attempt, 'element' => $what];$sql = "WHERE scoid = :scoid AND attemptid = :attemptid AND elementid = (SELECT idFROM {scorm_element}WHERE element = :element)";$DB->delete_records_select('scorm_scoes_value', $sql, $params);} else {scorm_insert_track($userid, $sco->scorm, $sco->id, $attempt, $what, $value);}// Update grades in gradebook.$scorm = $DB->get_record('scorm', array('id' => $sco->scorm));scorm_update_grades($scorm, $userid, true);}function scorm_evaluate_condition ($rollupruleconds, $sco, $userid) {global $DB;$res = false;if (strpos($rollupruleconds, 'and ')) {$rollupruleconds = array_filter(explode(' and ', $rollupruleconds));$conditioncombination = 'all';} else {$rollupruleconds = array_filter(explode(' or ', $rollupruleconds));$conditioncombination = 'or';}foreach ($rollupruleconds as $rolluprulecond) {$notflag = false;if (strpos($rolluprulecond, 'not') !== false) {$rolluprulecond = str_replace('not', '', $rolluprulecond);$notflag = true;}$conditionarray['condition'] = $rolluprulecond;$conditionarray['notflag'] = $notflag;$conditions[] = $conditionarray;}foreach ($conditions as $condition) {$checknot = true;$res = false;if ($condition['notflag']) {$checknot = false;}switch ($condition['condition']) {case 'satisfied':$r = scorm_get_sco_value($sco->id, $userid, 'objectivesatisfiedstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}}break;case 'objectiveStatusKnown':$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}break;case 'notobjectiveStatusKnown':$r = scorm_get_sco_value($sco->id, $userid, 'objectiveprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}break;case 'objectiveMeasureKnown':$r = scorm_get_sco_value($sco->id, $userid, 'objectivemeasurestatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}break;case 'notobjectiveMeasureKnown':$r = scorm_get_sco_value($sco->id, $userid, 'objectivemeasurestatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}break;case 'completed':$r = scorm_get_sco_value($sco->id, $userid, 'attemptcompletionstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$r = scorm_get_sco_value($sco->id, $userid, 'attemptprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}}break;case 'attempted':$r = scorm_get_sco_value($sco->id, $userid, 'x.start.time');if ($checknot && $r->attempt > 0) {$res = true;} else if (!$checknot && $r->attempt <= 0) {$res = true;}break;case 'attemptLimitExceeded':$r = scorm_get_sco_value($sco->id, $userid, 'activityprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$r = scorm_get_sco_value($sco->id, $userid, 'limitconditionattemptlimitcontrol');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$sql = "SELECT max(attempt) as attemptFROM {scorm_attempt} aJOIN {scorm_scoes_value} v on v.attemptid = a.idWHERE v.scoid = :scoid AND a.userid = :userid";$r2 = scorm_get_sco_value($sco->id, $userid, 'limitconditionattemptlimit');$attempts = $DB->get_field_sql($sql, ['scoid' => $sco->id, 'userid' => $userid]);if (!empty($attempts) && !empty($r2)) {if ($checknot && ($attempts >= $r2->value)) {$res = true;} else if (!$checknot && ($attempts < $r2->value)) {$res = true;}}}}break;case 'activityProgressKnown':$r = scorm_get_sco_value($sco->id, $userid, 'activityprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$r = scorm_get_sco_value($sco->id, $userid, 'attemptprogressstatus');if ((!isset($r->value) && !$checknot) || (isset($r->value) && ($r->value == $checknot))) {$res = true;}}break;}if ($conditioncombination == 'all' && !$res) {break;} else if ($conditioncombination == 'or' && $res) {break;}}return $res;}function scorm_check_activity ($activity, $userid) {$act = scorm_seq_rules_check($activity, 'disabled');if ($act != null) {return true;}if (scorm_limit_cond_check ($activity, $userid)) {return true;}return false;}function scorm_limit_cond_check ($activity, $userid) {global $DB;if (isset($activity->tracked) && ($activity->tracked == 0)) {return false;}if (scorm_seq_is('active', $activity->id, $userid) || scorm_seq_is('suspended', $activity->id, $userid)) {return false;}if (!isset($activity->limitcontrol) || ($activity->limitcontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'activityattemptcount');if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattempt)) {return true;}}if (!isset($activity->limitabsdurcontrol) || ($activity->limitabsdurcontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'activityabsoluteduration');if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitabsduration)) {return true;}}if (!isset($activity->limitexpdurcontrol) || ($activity->limitexpdurcontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'activityexperiencedduration');if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitexpduration)) {return true;}}if (!isset($activity->limitattabsdurcontrol) || ($activity->limitattabsdurcontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'attemptabsoluteduration');if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattabsduration)) {return true;}}if (!isset($activity->limitattexpdurcontrol) || ($activity->limitattexpdurcontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'attemptexperiencedduration');if (scorm_seq_is('activityprogressstatus', $activity->id, $userid) && ($r->value >= $activity->limitattexpduration)) {return true;}}if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {$r = scorm_get_sco_value($activity->id, $userid, 'begintime');if (isset($activity->limitbegintime) && time() >= $activity->limitbegintime) {return true;}}if (!isset($activity->limitbegincontrol) || ($activity->limitbegincontrol == 1)) {if (isset($activity->limitbegintime) && time() < $activity->limitbegintime) {return true;}}if (!isset($activity->limitendcontrol) || ($activity->limitendcontrol == 1)) {if (isset($activity->limitendtime) && time() > $activity->limitendtime) {return true;}}return false;}function scorm_seq_rules_check ($sco, $action) {global $DB;$act = null;if ($rules = $DB->get_records('scorm_seq_ruleconds', array('scoid' => $sco->id, 'action' => $action))) {foreach ($rules as $rule) {if ($act = scorm_seq_rule_check($sco, $rule)) {return $act;}}}return $act;}function scorm_seq_rule_check ($sco, $rule) {global $DB;$bag = Array();$cond = '';$ruleconds = $DB->get_records('scorm_seq_rulecond', array('scoid' => $sco->id, 'ruleconditionsid' => $rule->id));foreach ($ruleconds as $rulecond) {if ($rulecond->operator == 'not') {if ($rulecond->cond != 'unknown' ) {$rulecond->cond = 'not'.$rulecond->cond;}}$bag[] = $rulecond->cond;}if (empty($bag)) {$cond = 'unknown';return $cond;}if ($rule->conditioncombination == 'all') {foreach ($bag as $con) {$cond = $cond.' and '.$con;}} else {foreach ($bag as $con) {$cond = $cond.' or '.$con;}}return $cond;}function scorm_seq_overall_rollup($sco, $userid, $seq) {if ($ancestors = scorm_get_ancestors($sco)) {foreach ($ancestors as $ancestor) {if (!scorm_is_leaf($ancestor)) {scorm_seq_measure_rollup($sco, $userid, $seq->attempt);}scorm_seq_objective_rollup($sco, $userid, $seq->attempt);scorm_seq_activity_progress_rollup($sco, $userid, $seq);}}}function scorm_seq_measure_rollup($sco, $userid, $attempt = 0) {global $DB;$totalmeasure = 0; // Check if there is something similar in the database.$valid = false; // Same as in the last line.$countedmeasures = 0; // Same too.$targetobjective = null;$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));foreach ($objectives as $objective) {if ($objective->primaryobj == true) { // Objective contributes to rollup.$targetobjective = $objective;break;}}if ($targetobjective != null) {$children = scorm_get_children($sco);if (!empty ($children)) {foreach ($children as $child) {$child = scorm_get_sco ($child);if (!isset($child->tracked) || ($child->tracked == 1)) {$rolledupobjective = null;// We set the rolled up activity to undefined.$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $child->id));foreach ($objectives as $objective) {if ($objective->primaryobj == true) {// Objective contributes to rollup I'm using primaryobj field, but not.$rolledupobjective = $objective;break;}}if ($rolledupobjective != null) {$child = scorm_get_sco($child->id);$countedmeasures = $countedmeasures + ($child->measureweight);if (!scorm_seq_is('objectivemeasurestatus', $sco->id, $userid, $attempt)) {$normalizedmeasure = scorm_get_sco_value($child->id, $userid, 'objectivenormalizedmeasure');$totalmeasure = $totalmeasure + (($normalizedmeasure->value) * ($child->measureweight));$valid = true;}}}}}if (!$valid) {scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);} else {if ($countedmeasures > 0) {scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt);$val = $totalmeasure / $countedmeasures;scorm_seq_set('objectivenormalizedmeasure', $sco->id, $userid, $attempt, $val);} else {scorm_seq_set('objectivemeasurestatus', $sco->id, $userid, $attempt, false);}}}}function scorm_seq_objective_rollup($sco, $userid, $attempt = 0) {global $DB;scorm_seq_objective_rollup_measure($sco, $userid, $attempt);scorm_seq_objective_rollup_rules($sco, $userid, $attempt);scorm_seq_objective_rollup_default($sco, $userid, $attempt);/*if ($targetobjective->satisfiedbymeasure) {scorm_seq_objective_rollup_measure($sco, $userid);}else{if ((scorm_seq_rollup_rule_check($sco, $userid, 'incomplete'))|| (scorm_seq_rollup_rule_check($sco, $userid, 'completed'))) {scorm_seq_objective_rollup_rules($sco, $userid);}else{$rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid'=>$sco->id, 'userid'=>$userid));foreach ($rolluprules as $rolluprule) {$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid'=>$rolluprule->id));foreach ($rollupruleconds as $rolluprulecond) {switch ($rolluprulecond->cond!='satisfied'&& $rolluprulecond->cond!='completed' && $rolluprulecond->cond!='attempted') {scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, false);break;}}}}*/}function scorm_seq_objective_rollup_measure($sco, $userid, $attempt = 0) {global $DB;$targetobjective = null;$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));foreach ($objectives as $objective) {if ($objective->primaryobj == true) {$targetobjective = $objective;break;}}if ($targetobjective != null) {if ($targetobjective->satisfiedbymeasure) {if (!scorm_seq_is('objectiveprogressstatus', $sco->id, $userid, $attempt)) {scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt, false);} else {if (scorm_seq_is('active', $sco->id, $userid, $attempt)) {$isactive = true;} else {$isactive = false;}$normalizedmeasure = scorm_get_sco_value($sco->id, $userid, 'objectivenormalizedmeasure');$sco = scorm_get_sco ($sco->id);if (!$isactive || ($isactive &&(!isset($sco->measuresatisfactionifactive) || $sco->measuresatisfactionifactive == true))) {if (isset($normalizedmeasure->value) && ($normalizedmeasure->value >= $targetobjective->minnormalizedmeasure)) {scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt);} else {// TODO: handle the case where cmi.success_status is passed and objectivenormalizedmeasure undefined.scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);}} else {scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt, false);}}}}}function scorm_seq_objective_rollup_default($sco, $userid, $attempt = 0) {global $DB;if (!(scorm_seq_rollup_rule_check($sco, $userid, 'incomplete')) && !(scorm_seq_rollup_rule_check($sco, $userid, 'completed'))) {if ($rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid' => $sco->id))) {foreach ($rolluprules as $rolluprule) {$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid' => $rolluprule->id));foreach ($rollupruleconds as $rolluprulecond) {if ($rolluprulecond->cond != 'satisfied' && $rolluprulecond->cond != 'completed' &&$rolluprulecond->cond != 'attempted') {scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt, false);break;}}}}}}function scorm_seq_objective_rollup_rules($sco, $userid, $attempt = 0) {global $DB;$targetobjective = null;$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $sco->id));foreach ($objectives as $objective) {if ($objective->primaryobj == true) {// Objective contributes to rollup I'm using primaryobj field, but not.$targetobjective = $objective;break;}}if ($targetobjective != null) {if (scorm_seq_rollup_rule_check($sco, $userid, 'notsatisfied')) {// With not satisfied rollup for the activity.scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt, false);}if (scorm_seq_rollup_rule_check($sco, $userid, 'satisfied')) {// With satisfied rollup for the activity.scorm_seq_set('objectiveprogressstatus', $sco->id, $userid, $attempt);scorm_seq_set('objectivesatisfiedstatus', $sco->id, $userid, $attempt);}}}function scorm_seq_activity_progress_rollup ($sco, $userid, $seq) {if (scorm_seq_rollup_rule_check($sco, $userid, 'incomplete')) {// Incomplete rollup action.scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt, false);scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt, true);}if (scorm_seq_rollup_rule_check($sco, $userid, 'completed')) {// Incomplete rollup action.scorm_seq_set('attemptcompletionstatus', $sco->id, $userid, $seq->attempt, true);scorm_seq_set('attemptprogressstatus', $sco->id, $userid, $seq->attempt, true);}}function scorm_seq_rollup_rule_check ($sco, $userid, $action) {global $DB;if ($rolluprules = $DB->get_record('scorm_seq_rolluprule', array('scoid' => $sco->id, 'action' => $action))) {$childrenbag = Array ();$children = scorm_get_children ($sco);foreach ($rolluprules as $rolluprule) {foreach ($children as $child) {$child = scorm_get_sco ($child);if (!isset($child->tracked) || ($child->tracked == 1)) {if (scorm_seq_check_child ($child, $action, $userid)) {$rollupruleconds = $DB->get_records('scorm_seq_rolluprulecond', array('rollupruleid' => $rolluprule->id));$evaluate = scorm_seq_evaluate_rollupcond($child, $rolluprule->conditioncombination,$rollupruleconds, $userid);if ($evaluate == 'unknown') {array_push($childrenbag, 'unknown');} else {if ($evaluate == true) {array_push($childrenbag, true);} else {array_push($childrenbag, false);}}}}}$change = false;switch ($rolluprule->childactivityset) {case 'all':// I think I can use this condition instead equivalent to OR.if ((array_search(false, $childrenbag) === false) && (array_search('unknown', $childrenbag) === false)) {$change = true;}break;case 'any':// I think I can use this condition instead equivalent to OR.if (array_search(true, $childrenbag) !== false) {$change = true;}break;case 'none':// I think I can use this condition instead equivalent to OR.if ((array_search(true, $childrenbag) === false) && (array_search('unknown', $childrenbag) === false)) {$change = true;}break;case 'atleastcount':// I think I can use this condition instead equivalent to OR.foreach ($childrenbag as $itm) {$cont = 0;if ($itm === true) {$cont++;}if ($cont >= $rolluprule->minimumcount) {$change = true;}}break;case 'atleastcount':foreach ($childrenbag as $itm) {// I think I can use this condition instead equivalent to OR.$cont = 0;if ($itm === true) {$cont++;}if ($cont >= $rolluprule->minimumcount) {$change = true;}}break;case 'atleastpercent':foreach ($childrenbag as $itm) {// I think I can use this condition instead equivalent to OR.$cont = 0;if ($itm === true) {$cont++;}if (($cont / count($childrenbag)) >= $rolluprule->minimumcount) {$change = true;}}break;}if ($change == true) {return true;}}}return false;}function scorm_seq_flow_tree_traversal($activity, $direction, $childrenflag, $prevdirection, $seq, $userid, $skip = false) {$revdirection = false;$parent = scorm_get_parent($activity);if (!empty($parent)) {$children = scorm_get_available_children($parent);} else {$children = array();}$childrensize = count($children);if (($prevdirection != null && $prevdirection == 'backward') && ($children[$childrensize - 1]->id == $activity->id)) {$direction = 'backward';$activity = $children[0];$revdirection = true;}if ($direction == 'forward') {$ancestors = scorm_get_ancestors($activity);$ancestorsroot = array_reverse($ancestors);$preorder = array();$preorder = scorm_get_preorder($preorder, $ancestorsroot[0]);$preordersize = count($preorder);if (($activity->id == $preorder[$preordersize - 1]->id) || (($activity->parent == '/') && !($childrenflag))) {$seq->endsession = true;$seq->nextactivity = null;return $seq;}if (scorm_is_leaf ($activity) || !$childrenflag) {if ($children[$childrensize - 1]->id == $activity->id) {$seq = scorm_seq_flow_tree_traversal ($parent, $direction, false, null, $seq, $userid);if ($seq->nextactivity->launch == null) {$seq = scorm_seq_flow_tree_traversal ($seq->nextactivity, $direction, true, null, $seq, $userid);}return $seq;} else {$position = 0;foreach ($children as $sco) {if ($sco->id == $activity->id) {break;}$position++;}if ($position != ($childrensize - 1)) {$seq->nextactivity = $children[$position + 1];$seq->traversaldir = $direction;return $seq;} else {$siblings = scorm_get_siblings($activity);$children = scorm_get_children($siblings[0]);$seq->nextactivity = $children[0];return $seq;}}} else {$children = scorm_get_available_children($activity);if (!empty($children)) {$seq->traversaldir = $direction;$seq->nextactivity = $children[0];return $seq;} else {$seq->traversaldir = null;$seq->nextactivity = null;$seq->exception = 'SB.2.1-2';return $seq;}}} else if ($direction == 'backward') {if ($activity->parent == '/') {$seq->traversaldir = null;$seq->nextactivity = null;$seq->exception = 'SB.2.1-3';return $seq;}if (scorm_is_leaf ($activity) || !$childrenflag) {if (!$revdirection) {if (isset($parent->forwardonly) && ($parent->forwardonly == true && !$skip)) {$seq->traversaldir = null;$seq->nextactivity = null;$seq->exception = 'SB.2.1-4';return $seq;}}if ($children[0]->id == $activity->id) {$seq = scorm_seq_flow_tree_traversal($parent, 'backward', false, null, $seq, $userid);return $seq;} else {$ancestors = scorm_get_ancestors($activity);$ancestorsroot = array_reverse($ancestors);$preorder = array();$preorder = scorm_get_preorder($preorder, $ancestorsroot[0]);$position = 0;foreach ($preorder as $sco) {if ($sco->id == $activity->id) {break;}$position++;}if (isset($preorder[$position])) {$seq->nextactivity = $preorder[$position - 1];$seq->traversaldir = $direction;}return $seq;}} else {$children = scorm_get_available_children($activity);if (!empty($children)) {if (isset($parent->flow) && ($parent->flow == true)) {$seq->traversaldir = 'forward';$seq->nextactivity = $children[0];return $seq;} else {$seq->traversaldir = 'backward';$seq->nextactivity = $children[count($children) - 1];return $seq;}} else {$seq->traversaldir = null;$seq->nextactivity = null;$seq->exception = 'SB.2.1-2';return $seq;}}}}// Returns the next activity on the tree, traversal direction, control returned to the LTS, (may) exception.function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $childrenflag, $prevdirection, $seq) {$parent = scorm_get_parent ($activity);if (!isset($parent->flow) || ($parent->flow == false)) {$seq->deliverable = false;$seq->exception = 'SB.2.2-1';$seq->nextactivity = $activity;return $seq;}$rulecheck = scorm_seq_rules_check($activity, 'skip');if ($rulecheck != null) {$skip = scorm_evaluate_condition ($rulecheck, $activity, $userid);if ($skip) {$seq = scorm_seq_flow_tree_traversal($activity, $direction, false, $prevdirection, $seq, $userid, $skip);$seq = scorm_seq_flow_activity_traversal($seq->nextactivity, $userid, $direction,$childrenflag, $prevdirection, $seq);} else if (!empty($seq->identifiedactivity)) {$seq->nextactivity = $activity;}return $seq;}$ch = scorm_check_activity ($activity, $userid);if ($ch) {$seq->deliverable = false;$seq->exception = 'SB.2.2-2';$seq->nextactivity = $activity;return $seq;}if (!scorm_is_leaf($activity)) {$seq = scorm_seq_flow_tree_traversal ($activity, $direction, true, null, $seq, $userid);if ($seq->identifiedactivity == null) {$seq->deliverable = false;$seq->nextactivity = $activity;return $seq;} else {if ($direction == 'backward' && $seq->traversaldir == 'forward') {$seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,'forward', $childrenflag, 'backward', $seq);} else {$seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,$direction, $childrenflag, null, $seq);}return $seq;}}$seq->deliverable = true;$seq->nextactivity = $activity;$seq->exception = null;return $seq;}function scorm_seq_flow ($activity, $direction, $seq, $childrenflag, $userid) {// TODO: $PREVDIRECTION NOT DEFINED YET.$prevdirection = null;$seq = scorm_seq_flow_tree_traversal ($activity, $direction, $childrenflag, $prevdirection, $seq, $userid);if ($seq->nextactivity == null) {$seq->nextactivity = $activity;$seq->deliverable = false;return $seq;} else {$activity = $seq->nextactivity;$seq = scorm_seq_flow_activity_traversal($activity, $userid, $direction, $childrenflag, null, $seq);return $seq;}}/*** Sets up $userdata array and default values for SCORM 1.3 .** @param stdClass $userdata an empty stdClass variable that should be set up with user values* @param object $scorm package record* @param string $scoid SCO Id* @param string $attempt attempt number for the user* @param string $mode scorm display mode type* @return array The default values that should be used for SCORM 1.3 package*/function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {global $DB, $USER;$userdata->student_id = $USER->username;if (empty(get_config('scorm', 'scormstandard'))) {$userdata->student_name = fullname($USER);} else {$userdata->student_name = $USER->lastname .', '. $USER->firstname;}if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {// According to SCORM 2004(RTE V1, 4.2.8), only cmi.exit==suspend should allow previous datamodel elements on re-launch.if (isset($usertrack->{'cmi.exit'}) && ($usertrack->{'cmi.exit'} == 'suspend')) {foreach ($usertrack as $key => $value) {$userdata->$key = $value;}} else {$userdata->status = '';$userdata->score_raw = '';}} else {$userdata->status = '';$userdata->score_raw = '';}if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {foreach ($scodatas as $key => $value) {$userdata->$key = $value;}} else {throw new \moodle_exception('cannotfindsco', 'scorm');}if (!$sco = scorm_get_sco($scoid)) {throw new \moodle_exception('cannotfindsco', 'scorm');}if (isset($userdata->status)) {if (!isset($userdata->{'cmi.exit'}) || $userdata->{'cmi.exit'} == 'time-out' || $userdata->{'cmi.exit'} == 'normal') {$userdata->entry = 'ab-initio';} else {if (isset($userdata->{'cmi.exit'}) && ($userdata->{'cmi.exit'} == 'suspend' || $userdata->{'cmi.exit'} == 'logout')) {$userdata->entry = 'resume';} else {$userdata->entry = '';}}}$userdata->mode = 'normal';if (!empty($mode)) {$userdata->mode = $mode;}if ($userdata->mode == 'normal') {$userdata->credit = 'credit';} else {$userdata->credit = 'no-credit';}$objectives = $DB->get_records('scorm_seq_objective', array('scoid' => $scoid));$index = 0;foreach ($objectives as $objective) {if (!empty($objective->minnormalizedmeasure)) {$userdata->{'cmi.scaled_passing_score'} = $objective->minnormalizedmeasure;}if (!empty($objective->objectiveid)) {$userdata->{'cmi.objectives.N'.$index.'.id'} = $objective->objectiveid;$index++;}}$def = array();$def['cmi.learner_id'] = $userdata->student_id;$def['cmi.learner_name'] = $userdata->student_name;$def['cmi.mode'] = $userdata->mode;$def['cmi.entry'] = $userdata->entry;$def['cmi.exit'] = scorm_isset($userdata, 'cmi.exit');$def['cmi.credit'] = scorm_isset($userdata, 'credit');$def['cmi.completion_status'] = scorm_isset($userdata, 'cmi.completion_status', 'unknown');$def['cmi.completion_threshold'] = scorm_isset($userdata, 'threshold');$def['cmi.learner_preference.audio_level'] = scorm_isset($userdata, 'cmi.learner_preference.audio_level', 1);$def['cmi.learner_preference.language'] = scorm_isset($userdata, 'cmi.learner_preference.language');$def['cmi.learner_preference.delivery_speed'] = scorm_isset($userdata, 'cmi.learner_preference.delivery_speed');$def['cmi.learner_preference.audio_captioning'] = scorm_isset($userdata, 'cmi.learner_preference.audio_captioning', 0);$def['cmi.location'] = scorm_isset($userdata, 'cmi.location');$def['cmi.max_time_allowed'] = scorm_isset($userdata, 'attemptAbsoluteDurationLimit');$def['cmi.progress_measure'] = scorm_isset($userdata, 'cmi.progress_measure');$def['cmi.scaled_passing_score'] = scorm_isset($userdata, 'cmi.scaled_passing_score');$def['cmi.score.scaled'] = scorm_isset($userdata, 'cmi.score.scaled');$def['cmi.score.raw'] = scorm_isset($userdata, 'cmi.score.raw');$def['cmi.score.min'] = scorm_isset($userdata, 'cmi.score.min');$def['cmi.score.max'] = scorm_isset($userdata, 'cmi.score.max');$def['cmi.success_status'] = scorm_isset($userdata, 'cmi.success_status', 'unknown');$def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');$def['cmi.time_limit_action'] = scorm_isset($userdata, 'timelimitaction');$def['cmi.total_time'] = scorm_isset($userdata, 'cmi.total_time', 'PT0H0M0S');$def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');return $def;}