From 8b074482155ad3a02de27bc689e57cd02adb5c88 Mon Sep 17 00:00:00 2001 From: piers Date: Wed, 13 Aug 2008 22:40:24 +0000 Subject: [PATCH] MDL-11188 SCORM module does not respect "prerequisites" like it used to. MDL-12342 & signs in CDATA sections are imported as &. This is a rewrite of the prerequisite handling. backport to 1.9. --- mod/scorm/datamodels/aicclib.php | 28 +-- mod/scorm/datamodels/scorm_12.js.php | 10 ++ mod/scorm/datamodels/scorm_12lib.php | 247 ++++++++++----------------- mod/scorm/datamodels/scorm_13lib.php | 32 +--- 4 files changed, 121 insertions(+), 196 deletions(-) diff --git a/mod/scorm/datamodels/aicclib.php b/mod/scorm/datamodels/aicclib.php index 2ba872a0100..2a3839305c9 100644 --- a/mod/scorm/datamodels/aicclib.php +++ b/mod/scorm/datamodels/aicclib.php @@ -322,13 +322,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n // // Get the current organization infos // - $organizationsql = ''; if (!empty($currentorg)) { if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { $result->toc .= "\t
  • $organizationtitle
  • \n"; $tocmenus[] = $organizationtitle; } - $organizationsql = "AND organization='$currentorg'"; } // // If not specified retrieve the last attempt number @@ -337,16 +335,14 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $attempt = scorm_get_last_attempt($scorm->id, $user->id); } $result->attemptleft = $scorm->maxattempt - $attempt; - if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){ - // drop keys so that we can access array sequentially - $scoes = array_values($scoes); + if ($scoes = scorm_get_scoes($scorm->id, $currentorg)){ // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { - if ($usertrack=scorm_get_tracks($sco->id,$user->id,$attempt)) { + if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } @@ -362,15 +358,10 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $findnext = false; $parents[$level]='/'; - foreach ($scoes as $pos=>$sco) { + foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = stripslashes($sco->title); - if ($optionaldatas = scorm_get_sco($sco->id, SCO_DATA)) { - if (!isset($optionaldatas->isvisible) || (isset($optionaldatas->isvisible) && ($optionaldatas->isvisible == 'true'))) { - $isvisible = true; - } - } - else { + if (!isset($sco->isvisible) || (isset($sco->isvisible) && ($sco->isvisible == 'true'))) { $isvisible = true; } if ($parents[$level]!=$sco->parent) { @@ -409,10 +400,8 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $nextsco = false; } $nextisvisible = false; - if (($nextsco !== false) && ($optionaldatas = scorm_get_sco($nextsco->id, SCO_DATA))) { - if (!isset($optionaldatas->isvisible) || (isset($optionaldatas->isvisible) && ($optionaldatas->isvisible == 'true'))) { - $nextisvisible = true; - } + if (!isset($nextsco->isvisible) || (isset($nextsco->isvisible) && ($nextsco->isvisible == 'true'))) { + $nextisvisible = true; } if ($nextisvisible && ($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { $sublist++; @@ -469,12 +458,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n } } if ($sco->id == $scoid) { - $scodata = scorm_get_sco($sco->id, SCO_DATA); $startbold = ''; $endbold = ''; $findnext = true; - $shownext = isset($scodata->next) ? $scodata->next : 0; - $showprev = isset($scodata->previous) ? $scodata->previous : 0; + $shownext = isset($sco->next) ? $sco->next : 0; + $showprev = isset($sco->previous) ? $sco->previous : 0; } if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { diff --git a/mod/scorm/datamodels/scorm_12.js.php b/mod/scorm/datamodels/scorm_12.js.php index cb7e433a0bd..3df33540846 100644 --- a/mod/scorm/datamodels/scorm_12.js.php +++ b/mod/scorm/datamodels/scorm_12.js.php @@ -37,6 +37,7 @@ function SCORMapi1_2() { cmi_children = 'core, suspend_data, launch_data, comments, objectives, student_data, student_preference, interactions'; core_children = 'student_id, student_name, lesson_location, credit, lesson_status, entry, score, total_time, lesson_mode, exit, session_time'; score_children = 'raw, min, max'; + comments_children = 'content, location, time'; objectives_children = 'id, score, status'; student_data_children = 'mastery_score, max_time_allowed, time_limit_action'; student_preference_children = 'audio, language, speed, text'; @@ -69,6 +70,12 @@ function SCORMapi1_2() { 'cmi.suspend_data':{'defaultvalue':'{'cmi.suspend_data'})?$userdata->{'cmi.suspend_data'}:'' ?>', 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'}, 'cmi.launch_data':{'defaultvalue':'datafromlms)?$userdata->datafromlms:'' ?>', 'mod':'r', 'writeerror':'403'}, 'cmi.comments':{'defaultvalue':'{'cmi.comments'})?$userdata->{'cmi.comments'}:'' ?>', 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'}, + // deprecated evaluation attributes + 'cmi.evaluation.comments._count':{'defaultvalue':'0', 'mod':'r', 'writeerror':'402'}, + 'cmi.evaluation.comments._children':{'defaultvalue':comments_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.evaluation.comments.n.content':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'}, + 'cmi.evaluation.comments.n.location':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIString256, 'mod':'rw', 'writeerror':'405'}, + 'cmi.evaluation.comments.n.time':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMITime, 'mod':'rw', 'writeerror':'405'}, 'cmi.comments_from_lms':{'mod':'r', 'writeerror':'403'}, 'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r', 'writeerror':'402'}, 'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'}, @@ -113,6 +120,9 @@ function SCORMapi1_2() { cmi.student_data = new Object(); cmi.student_preference = new Object(); cmi.interactions = new Object(); + // deprecated evaluation attributes + cmi.evaluation = new Object(); + cmi.evaluation.comments = new Object(); // Navigation Object var nav = new Object(); diff --git a/mod/scorm/datamodels/scorm_12lib.php b/mod/scorm/datamodels/scorm_12lib.php index a0889e365fc..896134a8ba9 100644 --- a/mod/scorm/datamodels/scorm_12lib.php +++ b/mod/scorm/datamodels/scorm_12lib.php @@ -1,6 +1,18 @@ 'notattempted' ); $i=0; - while ($istatus.'\' == \'completed\') || '. - '(\''.$usertracks[$element]->status.'\' == \'passed\'))'; - } else if (($operator = strpos($element,'=')) !== false) { - $item = trim(substr($element,0,$operator)); - if (!isset($usertracks[$item])) { - return false; - } - - $value = trim(trim(substr($element,$operator+1)),'"'); - if (isset($statuses[$value])) { - $status = $statuses[$value]; - } else { - return false; - } - - $element = '(\''.$usertracks[$item]->status.'\' == \''.$status.'\')'; - } else if (($operator = strpos($element,'<>')) !== false) { - $item = trim(substr($element,0,$operator)); - if (!isset($usertracks[$item])) { - return false; - } - - $value = trim(trim(substr($element,$operator+2)),'"'); - if (isset($statuses[$value])) { - $status = $statuses[$value]; - } else { - return false; - } - - $element = '(\''.$usertracks[$item]->status.'\' != \''.$status.'\')'; - } else if (is_numeric($element)) { - if ($symbol == '*') { - $symbol = ''; - $open = strpos($prerequisites,'{',$i); - $opened = 1; - $closed = 0; - for ($close=$open+1; (($opened > $closed) && ($close= $element) { - $element = 'true'; - } else { - $element = 'false'; - } - } - } else { - return false; - } - - array_push($stack,$element); - $element = ''; - } - if ($symbol == '~') { - $symbol = '!'; - } - if (!empty($symbol)) { - array_push($stack,$symbol); - } - break; - default: - $element .= $symbol; - break; - } - $i++; - } - if (!empty($element)) { + + // expand the amp entities + $prerequisites = preg_replace('/&/', '&', $prerequisites); + // find all my parsable tokens + $prerequisites = preg_replace('/(&|\||\(|\)|\~)/', '\t$1\t', $prerequisites); + // expand operators + $prerequisites = preg_replace('/&/', '&&', $prerequisites); + $prerequisites = preg_replace('/\|/', '||', $prerequisites); + // now - grab all the tokens + $elements = explode('\t', trim($prerequisites)); + + // process each token to build an expression to be evaluated + $stack = array(); + foreach ($elements as $element) { $element = trim($element); - if (isset($usertracks[$element])) { - $element = '((\''.$usertracks[$element]->status.'\' == \'completed\') || '. - '(\''.$usertracks[$element]->status.'\' == \'passed\'))'; - } else if (($operator = strpos($element,'=')) !== false) { - $item = trim(substr($element,0,$operator)); - if (!isset($usertracks[$item])) { - return false; - } - - $value = trim(trim(substr($element,$operator+1)),'"'); - if (isset($statuses[$value])) { - $status = $statuses[$value]; - } else { - return false; - } - - $element = '(\''.$usertracks[$item]->status.'\' == \''.$status.'\')'; - } else if (($operator = strpos($element,'<>')) !== false) { - $item = trim(substr($element,0,$operator)); - if (!isset($usertracks[$item])) { - return false; - } - - $value = trim(trim(substr($element,$operator+1)),'"'); - if (isset($statuses[$value])) { - $status = $statuses[$value]; - } else { - return false; - } - - $element = '(\''.$usertracks[$item]->status.'\' != \''.trim($status).'\')'; - } else { - return false; + if (empty($element)) { + continue; } - - array_push($stack,$element); + if (!preg_match('/^(&&|\|\||\(|\))$/', $element)) { + // create each individual expression + // search for ~ = <> X*{} + + // sets like 3*{S34, S36, S37, S39} + if (preg_match('/^(\d+)\*\{(.+)\}$/', $element, $matches)) { + $repeat = $matches[1]; + $set = explode(',', $matches[2]); + $count = 0; + foreach ($set as $setelement) { + if (isset($usertracks[$setelement]) && + ($usertracks[$setelement]->status == 'completed' || $usertracks[$element]->status == 'passed')) { + $count++; + } + } + if ($count >= $repeat) { + $element = 'true'; + } else { + $element = 'false'; + } + + // ~ Not + } else if ($element == '~') { + $element = '!'; + + // = | <> + } else if (preg_match('/^(.+)(\=|\<\>)(.+)$/', $element, $matches)) { + $element = trim($matches[1]); + if (isset($usertracks[$element])) { + $value = trim(preg_replace('/(\'|\")/', '', $matches[3])); + if (isset($statuses[$value])) { + $value = $statuses[$value]; + } + if ($matches[2] == '<>') { + $oper = '!='; + } else { + $oper = '=='; + } + $element = '(\''.$usertracks[$element]->status.'\' '.$oper.' \''.$value.'\')'; + } else { + $element = 'false'; + } + + // everything else must be an element defined like S45 ... + } else { + if (isset($usertracks[$element]) && + ($usertracks[$element]->status == 'completed' || $usertracks[$element]->status == 'passed')) { + $element = 'true'; + } else { + $element = 'false'; + } + } + + } + $stack []= ' '.$element.' '; } return eval('return '.implode($stack).';'); } @@ -173,13 +126,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n // // Get the current organization infos // - $organizationsql = ''; if (!empty($currentorg)) { if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { $result->toc .= "\t
  • $organizationtitle
  • \n"; $tocmenus[] = $organizationtitle; } - $organizationsql = "AND organization='$currentorg'"; } // // If not specified retrieve the last attempt number @@ -188,16 +139,14 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $attempt = scorm_get_last_attempt($scorm->id, $user->id); } $result->attemptleft = $scorm->maxattempt - $attempt; - if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){ - // drop keys so that we can access array sequentially - $scoes = array_values($scoes); + if ($scoes = scorm_get_scoes($scorm->id, $currentorg)){ // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { - if ($usertrack=scorm_get_tracks($sco->id,$user->id,$attempt)) { + if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } @@ -213,18 +162,13 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $findnext = false; $parents[$level]='/'; - foreach ($scoes as $pos=>$sco) { + foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = stripslashes($sco->title); - if ($optionaldatas = scorm_get_sco($sco->id, SCO_DATA)) { - if (!isset($optionaldatas->isvisible) || (isset($optionaldatas->isvisible) && ($optionaldatas->isvisible == 'true'))) { - $isvisible = true; - } - } - else{ + if (!isset($sco->isvisible) || (isset($sco->isvisible) && ($sco->isvisible == 'true'))) { $isvisible = true; } - if ($parents[$level]!=$sco->parent) { + if ($parents[$level] != $sco->parent) { if ($newlevel = array_search($sco->parent,$parents)) { for ($i=0; $i<($level-$newlevel); $i++) { $result->toc .= "\t\t\n"; @@ -248,7 +192,7 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $result->toc .= $closelist; $level = $i; } - $parents[$level]=$sco->parent; + $parents[$level] = $sco->parent; } } if ($isvisible) { @@ -260,10 +204,8 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $nextsco = false; } $nextisvisible = false; - if (($nextsco !== false) && ($optionaldatas = scorm_get_sco($nextsco->id, SCO_DATA))) { - if (!isset($optionaldatas->isvisible) || (isset($optionaldatas->isvisible) && ($optionaldatas->isvisible == 'true'))) { - $nextisvisible = true; - } + if (($nextsco !== false) && (!isset($nextsco->isvisible) || (isset($nextsco->isvisible) && ($nextsco->isvisible == 'true')))) { + $nextisvisible = true; } if ($nextisvisible && ($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { $sublist++; @@ -320,12 +262,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n } } if ($sco->id == $scoid) { - $scodata = scorm_get_sco($sco->id, SCO_DATA); $startbold = ''; $endbold = ''; $findnext = true; - $shownext = isset($scodata->next) ? $scodata->next : 0; - $showprev = isset($scodata->previous) ? $scodata->previous : 0; + $shownext = isset($sco->next) ? $sco->next : 0; + $showprev = isset($sco->previous) ? $sco->previous : 0; } if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { diff --git a/mod/scorm/datamodels/scorm_13lib.php b/mod/scorm/datamodels/scorm_13lib.php index 43de73bce6d..59facba388a 100644 --- a/mod/scorm/datamodels/scorm_13lib.php +++ b/mod/scorm/datamodels/scorm_13lib.php @@ -19,13 +19,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n // // Get the current organization infos // - $organizationsql = ''; if (!empty($currentorg)) { if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { $result->toc .= "\t
  • $organizationtitle
  • \n"; $tocmenus[] = $organizationtitle; } - $organizationsql = "AND organization='$currentorg'"; } // // If not specified retrieve the last attempt number @@ -34,26 +32,20 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $attempt = scorm_get_last_attempt($scorm->id, $user->id); } $result->attemptleft = $scorm->maxattempt - $attempt; - if ($scoes = get_records_select('scorm_scoes',"scorm='$scorm->id' $organizationsql order by id ASC")){ - // drop keys so that we can access array sequentially - $scoes = array_values($scoes); + if ($scoes = scorm_get_scoes($scorm->id, $currentorg)){ // // Retrieve user tracking data for each learning object // $usertracks = array(); - $optionaldatas = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { - if ($usertrack=scorm_get_tracks($sco->id,$user->id,$attempt)) { + if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } $usertracks[$sco->identifier] = $usertrack; } - if ($optionaldata = scorm_get_sco($sco->id, SCO_DATA)) { - $optionaldatas[$sco->identifier] = $optionaldata; - } } } @@ -63,14 +55,11 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $nextid = 0; $findnext = false; $parents[$level]='/'; - foreach ($scoes as $pos=>$sco) { + foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = stripslashes($sco->title); - if (isset($optionaldatas[$sco->identifier])) { - if (!isset($optionaldatas[$sco->identifier]->isvisible) || - (isset($optionaldatas[$sco->identifier]->isvisible) && ($optionaldatas[$sco->identifier]->isvisible == 'true'))) { - $isvisible = true; - } + if (!isset($sco->isvisible) || (isset($sco->isvisible) && ($sco->isvisible == 'true'))) { + $isvisible = true; } if ($parents[$level]!=$sco->parent) { if ($newlevel = array_search($sco->parent,$parents)) { @@ -105,11 +94,8 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $nextsco = false; } $nextisvisible = false; - if (($nextsco !== false) && (isset($optionaldatas[$nextsco->identifier]))) { - if (!isset($optionaldatas[$nextsco->identifier]->isvisible) || - (isset($optionaldatas[$nextsco->identifier]->isvisible) && ($optionaldatas[$nextsco->identifier]->isvisible == 'true'))) { - $nextisvisible = true; - } + if (!isset($nextsco->isvisible) || (isset($nextsco->isvisible) && ($nextsco->isvisible == 'true'))) { + $nextisvisible = true; } if ($nextisvisible && ($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { @@ -172,8 +158,8 @@ function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='n $startbold = ''; $endbold = ''; $findnext = true; - $shownext = isset($optionaldatas[$sco->identifier]->next) ? $optionaldatas[$sco->identifier]->next : 0; - $showprev = isset($optionaldatas[$sco->identifier]->prev) ? $optionaldatas[$sco->identifier]->prev : 0; + $shownext = isset($sco->next) ? $sco->next : 0; + $showprev = isset($sco->prev) ? $sco->prev : 0; } if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) {