diff --git a/mod/scorm/datamodels/aicclib.php b/mod/scorm/datamodels/aicclib.php new file mode 100644 index 00000000000..e84bfe85dda --- /dev/null +++ b/mod/scorm/datamodels/aicclib.php @@ -0,0 +1,506 @@ +<?php +function scorm_add_time($a, $b) { + $aes = explode(':',$a); + $bes = explode(':',$b); + $aseconds = explode('.',$aes[2]); + $bseconds = explode('.',$bes[2]); + $change = 0; + + $acents = 0; //Cents + if (count($aseconds) > 1) { + $acents = $aseconds[1]; + } + $bcents = 0; + if (count($bseconds) > 1) { + $bcents = $bseconds[1]; + } + $cents = $acents + $bcents; + $change = floor($cents / 100); + $cents = $cents - ($change * 100); + if (floor($cents) < 10) { + $cents = '0'. $cents; + } + + $secs = $aseconds[0] + $bseconds[0] + $change; //Seconds + $change = floor($secs / 60); + $secs = $secs - ($change * 60); + if (floor($secs) < 10) { + $secs = '0'. $secs; + } + + $mins = $aes[1] + $bes[1] + $change; //Minutes + $change = floor($mins / 60); + $mins = $mins - ($change * 60); + if ($mins < 10) { + $mins = '0' . $mins; + } + + $hours = $aes[0] + $bes[0] + $change; //Hours + if ($hours < 10) { + $hours = '0' . $hours; + } + + if ($cents != '0') { + return $hours . ":" . $mins . ":" . $secs . '.' . $cents; + } else { + return $hours . ":" . $mins . ":" . $secs; + } +} + +/** +* Take the header row of an AICC definition file +* and returns sequence of columns and a pointer to +* the sco identifier column. +* +* @param string $row AICC header row +* @param string $mastername AICC sco identifier column +* @return mixed +*/ +function scorm_get_aicc_columns($row,$mastername='system_id') { + $tok = strtok(strtolower($row),"\",\n\r"); + $result->columns = array(); + $i=0; + while ($tok) { + if ($tok !='') { + $result->columns[] = $tok; + if ($tok == $mastername) { + $result->mastercol = $i; + } + $i++; + } + $tok = strtok("\",\n\r"); + } + return $result; +} + +/** +* Given a colums array return a string containing the regular +* expression to match the columns in a text row. +* +* @param array $column The header columns +* @param string $remodule The regular expression module for a single column +* @return string +*/ +function scorm_forge_cols_regexp($columns,$remodule='(".*")?,') { + $regexp = '/^'; + foreach ($columns as $column) { + $regexp .= $remodule; + } + $regexp = substr($regexp,0,-1) . '/'; + return $regexp; +} + +function scorm_parse_aicc($pkgdir,$scormid) { + $version = 'AICC'; + $ids = array(); + $courses = array(); + $extaiccfiles = array('crs','des','au','cst','ort','pre','cmp'); + if ($handle = opendir($pkgdir)) { + while (($file = readdir($handle)) !== false) { + if ($file[0] != '.') { + $ext = substr($file,strrpos($file,'.')); + $extension = strtolower(substr($ext,1)); + if (in_array($extension,$extaiccfiles)) { + $id = strtolower(basename($file,$ext)); + $ids[$id]->$extension = $file; + } + } + } + closedir($handle); + } + foreach ($ids as $courseid => $id) { + if (isset($id->crs)) { + if (is_file($pkgdir.'/'.$id->crs)) { + $rows = file($pkgdir.'/'.$id->crs); + foreach ($rows as $row) { + if (preg_match("/^(.+)=(.+)$/",$row,$matches)) { + switch (strtolower(trim($matches[1]))) { + case 'course_id': + $courses[$courseid]->id = trim($matches[2]); + break; + case 'course_title': + $courses[$courseid]->title = trim($matches[2]); + break; + case 'version': + $courses[$courseid]->version = 'AICC_'.trim($matches[2]); + break; + } + } + } + } + } + if (isset($id->des)) { + $rows = file($pkgdir.'/'.$id->des); + $columns = scorm_get_aicc_columns($rows[0]); + $regexp = scorm_forge_cols_regexp($columns->columns); + for ($i=1;$i<count($rows);$i++) { + if (preg_match($regexp,$rows[$i],$matches)) { + for ($j=0;$j<count($columns->columns);$j++) { + $column = $columns->columns[$j]; + $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1); + } + } + } + } + if (isset($id->au)) { + $rows = file($pkgdir.'/'.$id->au); + $columns = scorm_get_aicc_columns($rows[0]); + $regexp = scorm_forge_cols_regexp($columns->columns); + for ($i=1;$i<count($rows);$i++) { + if (preg_match($regexp,$rows[$i],$matches)) { + for ($j=0;$j<count($columns->columns);$j++) { + $column = $columns->columns[$j]; + $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol+1]),1,-1)]->$column = substr(trim($matches[$j+1]),1,-1); + } + } + } + } + if (isset($id->cst)) { + $rows = file($pkgdir.'/'.$id->cst); + $columns = scorm_get_aicc_columns($rows[0],'block'); + $regexp = scorm_forge_cols_regexp($columns->columns,'(.+)?,'); + for ($i=1;$i<count($rows);$i++) { + if (preg_match($regexp,$rows[$i],$matches)) { + for ($j=0;$j<count($columns->columns);$j++) { + if ($j != $columns->mastercol) { + $courses[$courseid]->elements[substr(trim($matches[$j+1]),1,-1)]->parent = substr(trim($matches[$columns->mastercol+1]),1,-1); + } + } + } + } + } + if (isset($id->ort)) { + $rows = file($pkgdir.'/'.$id->ort); + } + if (isset($id->pre)) { + $rows = file($pkgdir.'/'.$id->pre); + $columns = scorm_get_aicc_columns($rows[0],'structure_element'); + $regexp = scorm_forge_cols_regexp($columns->columns,'(.+),'); + for ($i=1;$i<count($rows);$i++) { + if (preg_match($regexp,$rows[$i],$matches)) { + $courses[$courseid]->elements[$columns->mastercol+1]->prerequisites = substr(trim($matches[1-$columns->mastercol+1]),1,-1); + } + } + } + if (isset($id->cmp)) { + $rows = file($pkgdir.'/'.$id->cmp); + } + } + //print_r($courses); + + $oldscoes = get_records('scorm_scoes','scorm',$scormid); + + $launch = 0; + if (isset($courses)) { + foreach ($courses as $course) { + unset($sco); + $sco->identifier = $course->id; + $sco->scorm = $scormid; + $sco->organization = ''; + $sco->title = $course->title; + $sco->parent = '/'; + $sco->launch = ''; + $sco->scormtype = ''; + + //print_r($sco); + if (get_record('scorm_scoes','scorm',$scormid,'identifier',$sco->identifier)) { + $id = update_record('scorm_scoes',$sco); + unset($oldscoes[$id]); + } else { + $id = insert_record('scorm_scoes',$sco); + } + + if ($launch == 0) { + $launch = $id; + } + if (isset($course->elements)) { + foreach($course->elements as $element) { + unset($sco); + $sco->identifier = $element->system_id; + $sco->scorm = $scormid; + $sco->organization = $course->id; + $sco->title = $element->title; + if (strtolower($element->parent) == 'root') { + $sco->parent = '/'; + } else { + $sco->parent = $element->parent; + } + if (isset($element->file_name)) { + $sco->launch = $element->file_name; + $sco->scormtype = 'sco'; + } else { + $element->file_name = ''; + $sco->scormtype = ''; + } + if (!isset($element->prerequisites)) { + $element->prerequisites = ''; + } + $sco->prerequisites = $element->prerequisites; + if (!isset($element->max_time_allowed)) { + $element->max_time_allowed = ''; + } + $sco->maxtimeallowed = $element->max_time_allowed; + if (!isset($element->time_limit_action)) { + $element->time_limit_action = ''; + } + $sco->timelimitaction = $element->time_limit_action; + if (!isset($element->mastery_score)) { + $element->mastery_score = ''; + } + $sco->masteryscore = $element->mastery_score; + $sco->previous = 0; + $sco->next = 0; + if ($oldscoid = scorm_array_search('identifier',$sco->identifier,$oldscoes)) { + $sco->id = $oldscoid; + $id = update_record('scorm_scoes',$sco); + unset($oldscoes[$oldscoid]); + } else { + $id = insert_record('scorm_scoes',$sco); + } + if ($launch==0) { + $launch = $id; + } + } + } + } + } + if (!empty($oldscoes)) { + foreach($oldscoes as $oldsco) { + delete_records('scorm_scoes','id',$oldsco->id); + delete_records('scorm_scoes_track','scoid',$oldsco->id); + } + } + set_field('scorm','version','AICC','id',$scormid); + return $launch; +} + +function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) { + global $CFG; + + // Added by Pham Minh Duc + $suspendscoid = scorm_get_suspendscoid($scorm->id,$user->id); + // End add + + $strexpand = get_string('expcoll','scorm'); + $modestr = ''; + if ($mode == 'browse') { + $modestr = '&mode='.$mode; + } + $scormpixdir = $CFG->modpixpath.'/scorm/pix'; + + $result = new stdClass(); + $result->toc = "<ul id='0' class='$liststyle'>\n"; + $tocmenus = array(); + $result->prerequisites = true; + $incomplete = false; + + // + // Get the current organization infos + // + $organizationsql = ''; + if (!empty($currentorg)) { + if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { + $result->toc .= "\t<li>$organizationtitle</li>\n"; + $tocmenus[] = $organizationtitle; + } + $organizationsql = "AND organization='$currentorg'"; + } + // + // If not specified retrieve the last attempt number + // + if (empty($attempt)) { + $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")){ + // + // 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->status == '') { + $usertrack->status = 'notattempted'; + } + $usertracks[$sco->identifier] = $usertrack; + } + } + } + + $level=0; + $sublist=1; + $previd = 0; + $nextid = 0; + $findnext = false; + $parents[$level]='/'; + + foreach ($scoes as $sco) { + if ($parents[$level]!=$sco->parent) { + if ($newlevel = array_search($sco->parent,$parents)) { + for ($i=0; $i<($level-$newlevel); $i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + $level = $newlevel; + } else { + $i = $level; + $closelist = ''; + while (($i > 0) && ($parents[$level] != $sco->parent)) { + $closelist .= "\t\t</ul></li>\n"; + $i--; + } + if (($i == 0) && ($sco->parent != $currentorg)) { + $style = ''; + if (isset($_COOKIE['hide:SCORMitem'.$sco->id])) { + $style = ' style="display: none;"'; + } + $result->toc .= "\t\t<li><ul id='$sublist' class='$liststyle'$style>\n"; + $level++; + } else { + $result->toc .= $closelist; + $level = $i; + } + $parents[$level]=$sco->parent; + } + } + $result->toc .= "\t\t<li>"; + $nextsco = next($scoes); + if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { + $sublist++; + $icon = 'minus'; + if (isset($_COOKIE['hide:SCORMitem'.$nextsco->id])) { + $icon = 'plus'; + } + $result->toc .= '<a href="javascript:expandCollide(img'.$sublist.','.$sublist.','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/'.$icon.'.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>'; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/spacer.gif" />'; + } + if (empty($sco->title)) { + $sco->title = $sco->identifier; + } + if (!empty($sco->launch)) { + $startbold = ''; + $endbold = ''; + $score = ''; + if (empty($scoid) && ($mode != 'normal')) { + $scoid = $sco->id; + } + // Modified by Pham Minh Duc + if ($suspendscoid == $sco->id){ + $result->toc .= '<img src="'.$scormpixdir.'/suspend.gif" alt="Dang tam dung o day" title="Dang dung o day" />'; + } else { + if (isset($usertracks[$sco->identifier])) { + $usertrack = $usertracks[$sco->identifier]; + $strstatus = get_string($usertrack->status,'scorm'); + $result->toc .= '<img src="'.$scormpixdir.'/'.$usertrack->status.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />'; + + if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) { + $incomplete = true; + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + } + if ($usertrack->score_raw != '') { + $score = '('.get_string('score','scorm').': '.$usertrack->score_raw.')'; + } + } else { + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + if ($sco->scormtype == 'sco') { + $result->toc .= '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />'; + $incomplete = true; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />'; + } + } + } + // End Modify + if ($sco->id == $scoid) { + $startbold = '<b>'; + $endbold = '</b>'; + $findnext = true; + $shownext = $sco->next; + $showprev = $sco->previous; + } + + if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { + if (!empty($sco->launch)) { + $previd = $sco->id; + } + } + if (empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks)) { + if ($sco->id == $scoid) { + $result->prerequisites = true; + } + // Modified by Pham Minh Duc + if (scorm_isChoice($scorm->id,$sco->id) == 1) { + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='.$sco->id; + $result->toc .= ' '.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n"; + $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); + } else { + $result->toc .= ' '.$startbold.format_string($sco->title).$score.$endbold."</li>\n"; + $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); + } + // End modify + } else { + if ($sco->id == $scoid) { + $result->prerequisites = false; + } + $result->toc .= ' '.$sco->title."</li>\n"; + } + } else { + $result->toc .= ' '.$sco->title."</li>\n"; + } + if (($nextsco !== false) && ($nextid == 0) && ($findnext)) { + if (!empty($nextsco->launch)) { + $nextid = $nextsco->id; + } + } + } + for ($i=0;$i<$level;$i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + + if ($play) { + $sco = get_record('scorm_scoes','id',$scoid); + $sco->previd = $previd; + $sco->nextid = $nextid; + $result->sco = $sco; + $result->incomplete = $incomplete; + } else { + $result->incomplete = $incomplete; + } + } + $result->toc .= "\t</ul>\n"; + if ($scorm->hidetoc == 0) { + $result->toc .= ' + <script language="javascript" type="text/javascript"> + <!-- + function expandCollide(which,list,item) { + var nn=document.ids?true:false + var w3c=document.getElementById?true:false + var beg=nn?"document.ids.":w3c?"document.getElementById(":"document.all."; + var mid=w3c?").style":".style"; + + if (eval(beg+list+mid+".display") != "none") { + which.src = "'.$scormpixdir.'/plus.gif"; + eval(beg+list+mid+".display=\'none\';"); + new cookie("hide:SCORMitem" + item, 1, 356, "/").set(); + } else { + which.src = "'.$scormpixdir.'/minus.gif"; + eval(beg+list+mid+".display=\'block\';"); + new cookie("hide:SCORMitem" + item, 1, -1, "/").set(); + } + } + --> + </script>'."\n"; + } + + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='; + $result->tocmenu = popup_form($url,$tocmenus, "tocmenu", $sco->id, '', '', '', true); + + return $result; +} + +?> diff --git a/mod/scorm/datamodels/scorm_12.js.php b/mod/scorm/datamodels/scorm_12.js.php new file mode 100644 index 00000000000..3b1afd800ea --- /dev/null +++ b/mod/scorm/datamodels/scorm_12.js.php @@ -0,0 +1,561 @@ +<?php + if (isset($userdata->status)) { + if ($userdata->status == '') { + $userdata->entry = 'ab-initio'; + } else { + if (isset($userdata->{'cmi.core.exit'}) && ($userdata->{'cmi.core.exit'} == 'suspend')) { + $userdata->entry = 'resume'; + } else { + $userdata->entry = ''; + } + } + } +?> +// +// SCORM 1.2 API Implementation +// +function SCORMapi1_2() { + // Standard Data Type Definition + CMIString256 = '^.{0,255}$'; + //CMIString4096 = '^[.|\\n|\\r]{0,4095}$'; + CMIString4096 = '^.{0,4096}$'; + CMITime = '^([0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\.[0-9]{1,2})?$'; + CMITimespan = '^([0-9]{2,4}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,2})?$'; + CMIInteger = '^\\d+$'; + CMISInteger = '^-?([0-9]+)$'; + CMIDecimal = '^-?([0-9]{0,3})(\.[0-9]{1,2})?$'; + CMIIdentifier = '^\\w{1,255}$'; + CMIFeedback = CMIString256; // This must be redefined + CMIIndex = '[._](\\d+).'; + // Vocabulary Data Type Definition + CMIStatus = '^passed$|^completed$|^failed$|^incomplete$|^browsed$'; + CMIStatus2 = '^passed$|^completed$|^failed$|^incomplete$|^browsed$|^not attempted$'; + CMIExit = '^time-out$|^suspend$|^logout$|^$'; + CMIType = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$'; + CMIResult = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\.[0-9]{1,2})?$'; + NAVEvent = '^previous$|^continue$'; + // Children lists + 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'; + objectives_children = 'id, score, status'; + student_data_children = 'mastery_score, max_time_allowed, time_limit_action'; + student_preference_children = 'audio, language, speed, text'; + interactions_children = 'id, objectives, time, type, correct_responses, weighting, student_response, result, latency'; + // Data ranges + score_range = '0#100'; + audio_range = '-1#100'; + speed_range = '-100#100'; + weighting_range = '-100#100'; + text_range = '-1#1'; + // The SCORM 1.2 data model + var datamodel = { + 'cmi._children':{'defaultvalue':cmi_children, 'mod':'r', 'writeerror':'402'}, + 'cmi._version':{'defaultvalue':'3.4', 'mod':'r', 'writeerror':'402'}, + 'cmi.core._children':{'defaultvalue':core_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.core.student_id':{'defaultvalue':'<?php echo $userdata->student_id ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.student_name':{'defaultvalue':'<?php echo addslashes($userdata->student_name) ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.lesson_location':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.lesson_location'})?$userdata->{'cmi.core.lesson_location'}:'' ?>', 'format':CMIString256, 'mod':'rw', 'writeerror':'405'}, + 'cmi.core.credit':{'defaultvalue':'<?php echo $userdata->credit ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.lesson_status':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.lesson_status'})?$userdata->{'cmi.core.lesson_status'}:'' ?>', 'format':CMIStatus, 'mod':'rw', 'writeerror':'405'}, + 'cmi.core.entry':{'defaultvalue':'<?php echo $userdata->entry ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.score._children':{'defaultvalue':score_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.core.score.raw':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.score.raw'})?$userdata->{'cmi.core.score.raw'}:'' ?>', 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.core.score.max':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.score.max'})?$userdata->{'cmi.core.score.max'}:'' ?>', 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.core.score.min':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.score.min'})?$userdata->{'cmi.core.score.min'}:'' ?>', 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.core.total_time':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.total_time'})?$userdata->{'cmi.core.total_time'}:'00:00:00' ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.lesson_mode':{'defaultvalue':'<?php echo $userdata->mode ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.core.exit':{'defaultvalue':'<?php echo isset($userdata->{'cmi.core.exit'})?$userdata->{'cmi.core.exit'}:'' ?>', 'format':CMIExit, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.core.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'00:00:00', 'readerror':'404', 'writeerror':'405'}, + 'cmi.suspend_data':{'defaultvalue':'<?php echo isset($userdata->{'cmi.suspend_data'})?$userdata->{'cmi.suspend_data'}:'' ?>', 'format':CMIString4096, 'mod':'rw', 'writeerror':'405'}, + 'cmi.launch_data':{'defaultvalue':'<?php echo $userdata->datafromlms ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.comments':{'defaultvalue':'<?php echo isset($userdata->{'cmi.comments'})?$userdata->{'cmi.comments'}:'' ?>', 'format':CMIString4096, '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'}, + 'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'rw', 'writeerror':'405'}, + 'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'mod':'r', 'writeerror':'402'}, + 'cmi.objectives.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.objectives.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.objectives.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':score_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.objectives.n.status':{'pattern':CMIIndex, 'format':CMIStatus2, 'mod':'rw', 'writeerror':'405'}, + 'cmi.student_data._children':{'defaultvalue':student_data_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.student_data.mastery_score':{'defaultvalue':'<?php echo $userdata->masteryscore ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.student_data.max_time_allowed':{'defaultvalue':'<?php echo $userdata->maxtimeallowed ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.student_data.time_limit_action':{'defaultvalue':'<?php echo $userdata->timelimitaction ?>', 'mod':'r', 'writeerror':'403'}, + 'cmi.student_preference._children':{'defaultvalue':student_preference_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.student_preference.audio':{'defaultvalue':'0', 'format':CMISInteger, 'range':audio_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.student_preference.language':{'defaultvalue':'', 'format':CMIString256, 'mod':'rw', 'writeerror':'405'}, + 'cmi.student_preference.speed':{'defaultvalue':'0', 'format':CMISInteger, 'range':speed_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.student_preference.text':{'defaultvalue':'0', 'format':CMISInteger, 'range':text_range, 'mod':'rw', 'writeerror':'405'}, + 'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r', 'writeerror':'402'}, + 'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0', 'writeerror':'402'}, + 'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'}, + 'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMIIdentifier, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.time':{'pattern':CMIIndex, 'format':CMITime, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.correct_responses._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0', 'writeerror':'402'}, + 'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'range':weighting_range, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.student_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'w', 'readerror':'404', 'writeerror':'405'}, + 'nav.event':{'defaultvalue':'', 'format':NAVEvent, 'mod':'w', 'readerror':'404', 'writeerror':'405'} + }; + // + // Datamodel inizialization + // + var cmi = new Object(); + cmi.core = new Object(); + cmi.core.score = new Object(); + cmi.objectives = new Object(); + cmi.student_data = new Object(); + cmi.student_preference = new Object(); + cmi.interactions = new Object(); + + // Navigation Object + var nav = new Object(); + + for (element in datamodel) { + if (element.match(/\.n\./) == null) { + if ((typeof eval('datamodel["'+element+'"].defaultvalue')) != 'undefined') { + eval(element+' = datamodel["'+element+'"].defaultvalue;'); + } else { + eval(element+' = "";'); + } + } + } + +<?php + $count = 0; + $objectives = ''; + foreach($userdata as $element => $value){ + if (substr($element,0,14) == 'cmi.objectives') { + preg_match('/.(\d+)./',$element,$matches); + $element = preg_replace('/.(\d+)./',"_\$1.",$element); + if ($matches[1] == $count) { + $count++; + $end = strpos($element,$matches[1])+strlen($matches[1]); + $subelement = substr($element,0,$end); + echo ' '.$subelement." = new Object();\n"; + echo ' '.$subelement.".score = new Object();\n"; + echo ' '.$subelement.".score._children = score_children;\n"; + echo ' '.$subelement.".score.raw = '';\n"; + echo ' '.$subelement.".score.min = '';\n"; + echo ' '.$subelement.".score.max = '';\n"; + } + echo ' '.$element.' = \''.$value."';\n"; + } + } + if ($count > 0) { + echo ' cmi.objectives._count = '.$count.";\n"; + } +?> + + if (cmi.core.lesson_status == '') { + cmi.core.lesson_status = 'not attempted'; + } + + // + // API Methods definition + // + var Initialized = false; + + function LMSInitialize (param) { + errorCode = "0"; + if (param == "") { + if (!Initialized) { + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Initialized SCORM 1.2");'; + } + ?> + Initialized = true; + errorCode = "0"; + return "true"; + } else { + errorCode = "101"; + } + } else { + errorCode = "201"; + } + return "false"; + } + + function LMSFinish (param) { + errorCode = "0"; + if (param == "") { + if (Initialized) { + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Finished SCORM 1.2");'; + } + ?> + Initialized = false; + result = StoreData(cmi,true); + if (nav.event != '') { + if (nav.event == 'continue') { + setTimeout('top.document.location=top.next;',500); + } else { + setTimeout('top.document.location=top.prev;',500); + } + } else { + if (<?php echo $scorm->auto ?> == 1) { + setTimeout('top.document.location=top.next;',500); + } + } + return "true"; + } else { + errorCode = "301"; + } + } else { + errorCode = "201"; + } + return "false"; + } + + function LMSGetValue (element) { + errorCode = "0"; + if (Initialized) { + if (element !="") { + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'w') { + element = element.replace(expression, "_$1."); + elementIndexes = element.split('.'); + subelement = 'cmi'; + i = 1; + while ((i < elementIndexes.length) && (typeof eval(subelement) != "undefined")) { + subelement += '.'+elementIndexes[i++]; + } + if (subelement == element) { + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+": "+eval(element));'; + } + ?> + return eval(element); + } else { + errorCode = "0"; // Need to check if it is the right errorCode + } + } else { + errorCode = eval('datamodel["'+elementmodel+'"].readerror'); + } + } else { + childrenstr = '._children'; + countstr = '._count'; + if (elementmodel.substr(elementmodel.length-childrenstr.length,elementmodel.length) == childrenstr) { + parentmodel = elementmodel.substr(0,elementmodel.length-childrenstr.length); + if ((typeof eval('datamodel["'+parentmodel+'"]')) != "undefined") { + errorCode = "202"; + } else { + errorCode = "201"; + } + } else if (elementmodel.substr(elementmodel.length-countstr.length,elementmodel.length) == countstr) { + parentmodel = elementmodel.substr(0,elementmodel.length-countstr.length); + if ((typeof eval('datamodel["'+parentmodel+'"]')) != "undefined") { + errorCode = "203"; + } else { + errorCode = "201"; + } + } else { + errorCode = "201"; + } + } + } else { + errorCode = "201"; + } + } else { + errorCode = "301"; + } + return ""; + } + + function LMSSetValue (element,value) { + errorCode = "0"; + if (Initialized) { + if (element != "") { + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'r') { + expression = new RegExp(eval('datamodel["'+elementmodel+'"].format')); + value = value+''; + matches = value.match(expression); + if (matches != null) { + //Create dynamic data model element + if (element != elementmodel) { + elementIndexes = element.split('.'); + subelement = 'cmi'; + for (i=1;i < elementIndexes.length-1;i++) { + elementIndex = elementIndexes[i]; + if (elementIndexes[i+1].match(/^\d+$/)) { + if ((typeof eval(subelement+'.'+elementIndex)) == "undefined") { + eval(subelement+'.'+elementIndex+' = new Object();'); + eval(subelement+'.'+elementIndex+'._count = 0;'); + } + if (elementIndexes[i+1] == eval(subelement+'.'+elementIndex+'._count')) { + eval(subelement+'.'+elementIndex+'._count++;'); + } + if (elementIndexes[i+1] > eval(subelement+'.'+elementIndex+'._count')) { + errorCode = "201"; + } + subelement = subelement.concat('.'+elementIndex+'_'+elementIndexes[i+1]); + i++; + } else { + subelement = subelement.concat('.'+elementIndex); + } + if ((typeof eval(subelement)) == "undefined") { + eval(subelement+' = new Object();'); + if (subelement.substr(0,14) == 'cmi.objectives') { + eval(subelement+'.score = new Object();'); + eval(subelement+'.score._children = score_children;'); + eval(subelement+'.score.raw = "";'); + eval(subelement+'.score.min = "";'); + eval(subelement+'.score.max = "";'); + } + if (subelement.substr(0,16) == 'cmi.interactions') { + eval(subelement+'.objectives = new Object();'); + eval(subelement+'.objectives._count = 0;'); + eval(subelement+'.correct_responses = new Object();'); + eval(subelement+'.correct_responses._count = 0;'); + } + } + } + element = subelement.concat('.'+elementIndexes[elementIndexes.length-1]); + } + //Store data + if (errorCode == "0") { + if ((typeof eval('datamodel["'+elementmodel+'"].range')) != "undefined") { + range = eval('datamodel["'+elementmodel+'"].range'); + ranges = range.split('#'); + value = value*1.0; + if ((value >= ranges[0]) && (value <= ranges[1])) { + eval(element+'="'+value+'";'); + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+":= "+value);'; + } + ?> + return "true"; + } else { + errorCode = eval('datamodel["'+elementmodel+'"].writeerror'); + } + } else { + if (element == 'cmi.comments') { + eval(element+'+="'+value+'";'); + } else { + eval(element+'="'+value+'";'); + } + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+":= "+value);'; + } + ?> + return "true"; + } + } + } else { + errorCode = eval('datamodel["'+elementmodel+'"].writeerror'); + } + } else { + errorCode = eval('datamodel["'+elementmodel+'"].writeerror'); + } + } else { + errorCode = "201" + } + } else { + errorCode = "201"; + } + } else { + errorCode = "301"; + } + return "false"; + } + + function LMSCommit (param) { + errorCode = "0"; + if (param == "") { + if (Initialized) { + result = StoreData(cmi,false); + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Data Commited");'; + } + ?> + return "true"; + } else { + errorCode = "301"; + } + } else { + errorCode = "201"; + } + return "false"; + } + + function LMSGetLastError () { + return errorCode; + } + + function LMSGetErrorString (param) { + if (param != "") { + var errorString = new Array(); + errorString["0"] = "No error"; + errorString["101"] = "General exception"; + errorString["201"] = "Invalid argument error"; + errorString["202"] = "Element cannot have children"; + errorString["203"] = "Element not an array - cannot have count"; + errorString["301"] = "Not initialized"; + errorString["401"] = "Not implemented error"; + errorString["402"] = "Invalid set value, element is a keyword"; + errorString["403"] = "Element is read only"; + errorString["404"] = "Element is write only"; + errorString["405"] = "Incorrect data type"; + return errorString[param]; + } else { + return ""; + } + } + + function LMSGetDiagnostic (param) { + if (param == "") { + param = errorCode; + } + return param; + } + + function AddTime (first, second) { + var sFirst = first.split(":"); + var sSecond = second.split(":"); + var cFirst = sFirst[2].split("."); + var cSecond = sSecond[2].split("."); + var change = 0; + + FirstCents = 0; //Cents + if (cFirst.length > 1) { + FirstCents = parseInt(cFirst[1],10); + } + SecondCents = 0; + if (cSecond.length > 1) { + SecondCents = parseInt(cSecond[1],10); + } + var cents = FirstCents + SecondCents; + change = Math.floor(cents / 100); + cents = cents - (change * 100); + if (Math.floor(cents) < 10) { + cents = "0" + cents.toString(); + } + + var secs = parseInt(cFirst[0],10)+parseInt(cSecond[0],10)+change; //Seconds + change = Math.floor(secs / 60); + secs = secs - (change * 60); + if (Math.floor(secs) < 10) { + secs = "0" + secs.toString(); + } + + mins = parseInt(sFirst[1],10)+parseInt(sSecond[1],10)+change; //Minutes + change = Math.floor(mins / 60); + mins = mins - (change * 60); + if (mins < 10) { + mins = "0" + mins.toString(); + } + + hours = parseInt(sFirst[0],10)+parseInt(sSecond[0],10)+change; //Hours + if (hours < 10) { + hours = "0" + hours.toString(); + } + + if (cents != '0') { + return hours + ":" + mins + ":" + secs + '.' + cents; + } else { + return hours + ":" + mins + ":" + secs; + } + } + + function TotalTime() { + total_time = AddTime(cmi.core.total_time, cmi.core.session_time); + return '&'+underscore('cmi.core.total_time')+'='+escape(total_time); + } + + function CollectData(data,parent) { + var datastring = ''; + for (property in data) { + if (typeof data[property] == 'object') { + datastring += CollectData(data[property],parent+'.'+property); + } else { + element = parent+'.'+property; + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if (elementmodel != "cmi.core.session_time") { + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'r') { + elementstring = '&'+underscore(element)+'='+escape(data[property]); + if ((typeof eval('datamodel["'+elementmodel+'"].defaultvalue')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].defaultvalue') != data[property]) { + datastring += elementstring; + } + } else { + datastring += elementstring; + } + } + } + } + } + } + return datastring; + } + + function StoreData(data,storetotaltime) { + if (storetotaltime) { + if (cmi.core.lesson_status == 'not attempted') { + cmi.core.lesson_status = 'completed'; + } + if (cmi.core.lesson_mode == 'normal') { + if (cmi.core.credit == 'credit') { + if (cmi.core.lesson_status == 'completed') { + if (cmi.student_data.mastery_score != '') { + if (parseFloat(cmi.core.score.raw) >= parseFloat(cmi.student_data.mastery_score)) { + cmi.core.lesson_status = 'passed'; + } else { + cmi.core.lesson_status = 'failed'; + } + } + } + } + } + if (cmi.core.lesson_mode == 'browse') { + if (datamodel['cmi.core.lesson_status'].defaultvalue == '') { + cmi.core.lesson_status = 'browsed'; + } + } + datastring = CollectData(data,'cmi'); + datastring += TotalTime(); + } else { + datastring = CollectData(data,'cmi'); + } + datastring += '&attempt=<?php echo $attempt ?>'; + datastring += '&scoid=<?php echo $sco->id ?>'; + + var myRequest = NewHttpReq(); + result = DoRequest(myRequest,"<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php","id=<?php p($id) ?>&sesskey=<?php p($USER->sesskey) ?>"+datastring); + results = result.split('\n'); + errorCode = results[1]; + return results[0]; + } + + this.LMSInitialize = LMSInitialize; + this.LMSFinish = LMSFinish; + this.LMSGetValue = LMSGetValue; + this.LMSSetValue = LMSSetValue; + this.LMSCommit = LMSCommit; + this.LMSGetLastError = LMSGetLastError; + this.LMSGetErrorString = LMSGetErrorString; + this.LMSGetDiagnostic = LMSGetDiagnostic; +} + +var API = new SCORMapi1_2(); diff --git a/mod/scorm/datamodels/scorm_12lib.php b/mod/scorm/datamodels/scorm_12lib.php new file mode 100644 index 00000000000..1adde9c0752 --- /dev/null +++ b/mod/scorm/datamodels/scorm_12lib.php @@ -0,0 +1,369 @@ +<?php + +function scorm_eval_prerequisites($prerequisites,$usertracks) { + $element = ''; + $stack = array(); + $statuses = array( + 'passed' => 'passed', + 'completed' => 'completed', + 'failed' => 'failed', + 'incomplete' => 'incomplete', + 'browsed' => 'browsed', + 'not attempted' => 'notattempted', + 'p' => 'passed', + 'c' => 'completed', + 'f' => 'failed', + 'i' => 'incomplete', + 'b' => 'browsed', + 'n' => 'notattempted' + ); + $i=0; + while ($i<strlen($prerequisites)) { + $symbol = $prerequisites[$i]; + switch ($symbol) { + case '&': + case '|': + $symbol .= $symbol; + case '~': + case '(': + case ')': + case '*': + $element = trim($element); + + if (!empty($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+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<strlen($prerequisites))); $close++) { + if ($prerequisites[$close] == '}') { + $closed++; + } else if ($prerequisites[$close] == '{') { + $opened++; + } + } + $i = $close; + + $setelements = explode(',', substr($prerequisites, $open+1, $close-($open+1)-1)); + $settrue = 0; + foreach ($setelements as $setelement) { + if (eval_prerequisites($setelement,$usertracks)) { + $settrue++; + } + } + + if ($settrue >= $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)) { + $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; + } + + array_push($stack,$element); + } + return eval('return '.implode($stack).';'); +} + +function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) { + global $CFG; + + $strexpand = get_string('expcoll','scorm'); + $modestr = ''; + if ($mode == 'browse') { + $modestr = '&mode='.$mode; + } + $scormpixdir = $CFG->modpixpath.'/scorm/pix'; + + $result = new stdClass(); + $result->toc = "<ul id='0' class='$liststyle'>\n"; + $tocmenus = array(); + $result->prerequisites = true; + $incomplete = false; + + // + // Get the current organization infos + // + $organizationsql = ''; + if (!empty($currentorg)) { + if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { + $result->toc .= "\t<li>$organizationtitle</li>\n"; + $tocmenus[] = $organizationtitle; + } + $organizationsql = "AND organization='$currentorg'"; + } + // + // If not specified retrieve the last attempt number + // + if (empty($attempt)) { + $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")){ + // + // 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->status == '') { + $usertrack->status = 'notattempted'; + } + $usertracks[$sco->identifier] = $usertrack; + } + } + } + + $level=0; + $sublist=1; + $previd = 0; + $nextid = 0; + $findnext = false; + $parents[$level]='/'; + + foreach ($scoes as $sco) { + if ($parents[$level]!=$sco->parent) { + if ($newlevel = array_search($sco->parent,$parents)) { + for ($i=0; $i<($level-$newlevel); $i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + $level = $newlevel; + } else { + $i = $level; + $closelist = ''; + while (($i > 0) && ($parents[$level] != $sco->parent)) { + $closelist .= "\t\t</ul></li>\n"; + $i--; + } + if (($i == 0) && ($sco->parent != $currentorg)) { + $style = ''; + if (isset($_COOKIE['hide:SCORMitem'.$sco->id])) { + $style = ' style="display: none;"'; + } + $result->toc .= "\t\t<li><ul id='$sublist' class='$liststyle'$style>\n"; + $level++; + } else { + $result->toc .= $closelist; + $level = $i; + } + $parents[$level]=$sco->parent; + } + } + $result->toc .= "\t\t<li>"; + $nextsco = next($scoes); + if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { + $sublist++; + $icon = 'minus'; + if (isset($_COOKIE['hide:SCORMitem'.$nextsco->id])) { + $icon = 'plus'; + } + $result->toc .= '<a href="javascript:expandCollide(img'.$sublist.','.$sublist.','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/'.$icon.'.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>'; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/spacer.gif" />'; + } + if (empty($sco->title)) { + $sco->title = $sco->identifier; + } + if (!empty($sco->launch)) { + $startbold = ''; + $endbold = ''; + $score = ''; + if (empty($scoid) && ($mode != 'normal')) { + $scoid = $sco->id; + } + if (isset($usertracks[$sco->identifier])) { + $usertrack = $usertracks[$sco->identifier]; + $strstatus = get_string($usertrack->status,'scorm'); + $result->toc .= '<img src="'.$scormpixdir.'/'.$usertrack->status.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />'; + + if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) { + $incomplete = true; + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + } + if ($usertrack->score_raw != '') { + $score = '('.get_string('score','scorm').': '.$usertrack->score_raw.')'; + } + } else { + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + if ($sco->scormtype == 'sco') { + $result->toc .= '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />'; + $incomplete = true; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />'; + } + } + if ($sco->id == $scoid) { + $startbold = '<b>'; + $endbold = '</b>'; + $findnext = true; + $shownext = $sco->next; + $showprev = $sco->previous; + } + + if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { + if (!empty($sco->launch)) { + $previd = $sco->id; + } + } + if (empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks)) { + if ($sco->id == $scoid) { + $result->prerequisites = true; + } + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='.$sco->id; + $result->toc .= ' '.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n"; + $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); + } else { + if ($sco->id == $scoid) { + $result->prerequisites = false; + } + $result->toc .= ' '.$sco->title."</li>\n"; + } + } else { + $result->toc .= ' '.$sco->title."</li>\n"; + } + if (($nextsco !== false) && ($nextid == 0) && ($findnext)) { + if (!empty($nextsco->launch)) { + $nextid = $nextsco->id; + } + } + } + for ($i=0;$i<$level;$i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + + if ($play) { + $sco = get_record('scorm_scoes','id',$scoid); + $sco->previd = $previd; + $sco->nextid = $nextid; + $result->sco = $sco; + $result->incomplete = $incomplete; + } else { + $result->incomplete = $incomplete; + } + } + $result->toc .= "\t</ul>\n"; + if ($scorm->hidetoc == 0) { + $result->toc .= ' + <script language="javascript" type="text/javascript"> + <!-- + function expandCollide(which,list,item) { + var nn=document.ids?true:false + var w3c=document.getElementById?true:false + var beg=nn?"document.ids.":w3c?"document.getElementById(":"document.all."; + var mid=w3c?").style":".style"; + + if (eval(beg+list+mid+".display") != "none") { + which.src = "'.$scormpixdir.'/plus.gif"; + eval(beg+list+mid+".display=\'none\';"); + new cookie("hide:SCORMitem" + item, 1, 356, "/").set(); + } else { + which.src = "'.$scormpixdir.'/minus.gif"; + eval(beg+list+mid+".display=\'block\';"); + new cookie("hide:SCORMitem" + item, 1, -1, "/").set(); + } + } + --> + </script>'."\n"; + } + + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='; + $result->tocmenu = popup_form($url,$tocmenus, "tocmenu", $sco->id, '', '', '', true); + + return $result; +} + +?> diff --git a/mod/scorm/datamodels/scorm_13.js.php b/mod/scorm/datamodels/scorm_13.js.php new file mode 100644 index 00000000000..309c505bb0f --- /dev/null +++ b/mod/scorm/datamodels/scorm_13.js.php @@ -0,0 +1,615 @@ +<?php + if (isset($userdata->status)) { + if ($userdata->status == '') { + $userdata->entry = 'ab-initio'; + } else { + if (isset($userdata->{'cmi.exit'}) && ($userdata->{'cmi.exit'} == 'suspend')) { + $userdata->entry = 'resume'; + } else { + $userdata->entry = ''; + } + } + } +?> +// +// SCORM 1.3 API Implementation +// +function SCORMapi1_3() { + // Standard Data Type Definition + CMIString200 = '^.{0,200}$'; + CMIString250 = '^.{0,250}$'; + CMIString1000 = '^.{0,1000}$'; + CMIString4000 = '^.{0,4000}$'; + CMITime = '^(\\d{4})(-\\d{2})(-\\d{2})(T[0-2]{1}[0-9]{1}):([0-5]{1}[0-9]{1}):([0-5]{1}[0-9]{1})(\\.[0-9]{1,2})?(Z|(-?[0-1]{1}[0-9]{1})(:[0-5]{1}[0-9]{1})?)?$'; + CMITimespan = '^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+(\.\\d{1,2})?S)?)?$'; + CMIInteger = '^\\d+$'; + CMISInteger = '^-?([0-9]+)$'; + CMIDecimal = '^-?([0-9]{0,3})(\\.[0-9]{1,7})?$'; + CMIIdentifier = '^\\w{1,200}$'; + CMILongIdentifier = '^\\w{1,4000}$'; + CMIFeedback = CMIString200; // This must be redefined + CMIIndex = '[._](\\d+).'; + // Vocabulary Data Type Definition + CMICStatus = '^completed$|^incomplete$|^not attempted$|^unknown$'; + CMISStatus = '^passed$|^failed$|^unknown$'; + CMIExit = '^time-out$|^suspend$|^logout$|^$'; + CMIType = '^true-false$|^choice$|^fill-in$|^matching$|^performance$|^sequencing$|^likert$|^numeric$'; + CMIResult = '^correct$|^wrong$|^unanticipated$|^neutral$|^([0-9]{0,3})?(\\.[0-9]{1,2})?$'; + NAVEvent = '^previous$|^continue$'; + // Children lists + cmi_children = 'version, comments_from_learner, comments_from_lms, completion_status, credit, entry, exit, interactions, launch_data, learner_id, learner_name, learner_preference, location, max_time_allowed, mode, objectives, progress_measure, scaled_passing_score, score, session_time, success_status, suspend_data, time_limit_action, total_time'; + comments_children = 'comment, location, date_time'; + score_children = 'scaled, raw, min, max'; + objectives_children = 'id, score, success_status, completion_status, description'; + student_data_children = 'mastery_score, max_time_allowed, time_limit_action'; + student_preference_children = 'audio_level, language, delivery_speed, audio_caption'; + interactions_children = 'id, type, objectives, timestamp, correct_responses, weighting, learner_response, result, latency, description'; + // Data ranges + scaled_range = '-1#1'; + audio_range = '0#*'; + speed_range = '0#*'; + text_range = '-1#1'; + progress_range = '0#1'; + // The SCORM 1.3 data model + var datamodel = { + 'cmi._children':{'defaultvalue':cmi_children, 'mod':'r'}, + 'cmi.version':{'defaultvalue':'1.0', 'mod':'r'}, + 'cmi.comments_from_learner._children':{'defaultvalue':comments_children, 'mod':'r'}, + 'cmi.comments_from_learner._count':{'mod':'r', 'defaultvalue':'0'}, + 'cmi.comments_from_learner.n.comment':{'format':CMIString4000, 'mod':'rw'}, + 'cmi.comments_from_learner.n.location':{'format':CMIString250, 'mod':'rw'}, + 'cmi.comments_from_learner.n.date_time':{'format':CMITime, 'mod':'rw'}, + 'cmi.comments_from_lms._children':{'defaultvalue':comments_children, 'mod':'r'}, + 'cmi.comments_from_lms._count':{'mod':'r', 'defaultvalue':'0'}, + 'cmi.comments_from_lms.n.comment':{'format':CMIString4000, 'mod':'r'}, + 'cmi.comments_from_lms.n.location':{'format':CMIString250, 'mod':'r'}, + 'cmi.comments_from_lms.n.date_time':{'format':CMITime, 'mod':'r'}, + 'cmi.completion_status':{'defaultvalue':'<?php echo isset($userdata->{'cmi.completion_status'})?$userdata->{'cmi.completion_status'}:'unknown' ?>', 'format':CMICStatus, 'mod':'rw'}, + 'cmi.completion_threshold':{'defaultvalue':<?php echo isset($userdata->threshold)?'\''.$userdata->threshold.'\'':'null' ?>, 'mod':'r'}, + 'cmi.credit':{'defaultvalue':'<?php echo isset($userdata->credit)?$userdata->credit:'' ?>', 'mod':'r'}, + 'cmi.entry':{'defaultvalue':'<?php echo $userdata->entry ?>', 'mod':'r'}, + 'cmi.exit':{'defaultvalue':'<?php echo isset($userdata->{'cmi.exit'})?$userdata->{'cmi.exit'}:'' ?>', 'format':CMIExit, 'mod':'w'}, + 'cmi.interactions._children':{'defaultvalue':interactions_children, 'mod':'r'}, + 'cmi.interactions._count':{'mod':'r', 'defaultvalue':'0'}, + 'cmi.interactions.n.id':{'pattern':CMIIndex, 'format':CMILongIdentifier, 'mod':'rw'}, + 'cmi.interactions.n.type':{'pattern':CMIIndex, 'format':CMIType, 'mod':'rw'}, + 'cmi.interactions.n.objectives._count':{'pattern':CMIIndex, 'mod':'r', 'defaultvalue':'0'}, + 'cmi.interactions.n.objectives.n.id':{'pattern':CMIIndex, 'format':CMILongIdentifier, 'mod':'rw'}, + 'cmi.interactions.n.timestamp':{'pattern':CMIIndex, 'format':CMITime, 'mod':'rw'}, + 'cmi.interactions.n.correct_responses._count':{'defaultvalue':'0', 'pattern':CMIIndex, 'mod':'r'}, + 'cmi.interactions.n.correct_responses.n.pattern':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'rw'}, + 'cmi.interactions.n.weighting':{'pattern':CMIIndex, 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.interactions.n.learner_response':{'pattern':CMIIndex, 'format':CMIFeedback, 'mod':'rw'}, + 'cmi.interactions.n.result':{'pattern':CMIIndex, 'format':CMIResult, 'mod':'rw'}, + 'cmi.interactions.n.latency':{'pattern':CMIIndex, 'format':CMITimespan, 'mod':'rw'}, + 'cmi.interactions.n.description':{'pattern':CMIIndex, 'format':CMIString250, 'mod':'rw'}, + 'cmi.launch_data':{'defaultvalue':<?php echo isset($userdata->datafromlms)?'\''.$userdata->datafromlms.'\'':'null' ?>, 'mod':'r'}, + 'cmi.learner_id':{'defaultvalue':'<?php echo $userdata->student_id ?>', 'mod':'r'}, + 'cmi.learner_name':{'defaultvalue':'<?php echo addslashes($userdata->student_name) ?>', 'mod':'r'}, + 'cmi.learner_preference._children':{'defaultvalue':student_preference_children, 'mod':'r'}, + 'cmi.learner_preference.audio_level':{'defaultvalue':'0', 'format':CMIDecimal, 'range':audio_range, 'mod':'rw'}, + 'cmi.learner_preference.language':{'defaultvalue':'', 'format':CMIString250, 'mod':'rw'}, + 'cmi.learner_preference.delivery_speed':{'defaultvalue':'0', 'format':CMIDecimal, 'range':speed_range, 'mod':'rw'}, + 'cmi.learner_preference.audio_caption':{'defaultvalue':'0', 'format':CMISInteger, 'range':text_range, 'mod':'rw'}, + 'cmi.location':{'defaultvalue':<?php echo isset($userdata->{'cmi.location'})?'\''.$userdata->{'cmi.location'}.'\'':'null' ?>, 'format':CMIString1000, 'mod':'rw'}, + 'cmi.max_time_allowed':{'defaultvalue':<?php echo isset($userdata->maxtimeallowed)?'\''.$userdata->maxtimeallowed.'\'':'null' ?>, 'mod':'r'}, + 'cmi.mode':{'defaultvalue':'<?php echo $userdata->mode ?>', 'mod':'r'}, + 'cmi.objectives._children':{'defaultvalue':objectives_children, 'mod':'r'}, + 'cmi.objectives._count':{'mod':'r', 'defaultvalue':'0'}, + 'cmi.objectives.n.id':{'pattern':CMIIndex, 'format':CMILongIdentifier, 'mod':'rw'}, + 'cmi.objectives.n.score._children':{'pattern':CMIIndex, 'mod':'r'}, + 'cmi.objectives.n.score.scaled':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'range':scaled_range, 'mod':'rw'}, + 'cmi.objectives.n.score.raw':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.objectives.n.score.min':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.objectives.n.score.max':{'defaultvalue':'', 'pattern':CMIIndex, 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.objectives.n.success_status':{'defaultvalue':'unknown', 'pattern':CMIIndex, 'format':CMISStatus, 'mod':'rw'}, + 'cmi.objectives.n.completion_status':{'defaultvalue':'unknown', 'pattern':CMIIndex, 'format':CMISStatus, 'mod':'rw'}, + 'cmi.objectives.n.description':{'pattern':CMIIndex, 'format':CMIString250, 'mod':'rw'}, + 'cmi.progress_measure':{'defaultvalue':'<?php echo isset($userdata->{'cmi.progess_measure'})?$userdata->{'cmi.progress_measure'}:'' ?>', 'format':CMIDecimal, 'range':progress_range, 'mod':'rw'}, + 'cmi.scaled_passing_score':{'defaultvalue':<?php echo isset($userdata->mnm)?'\''.$userdata->mnm.'\'':'null' ?>, 'format':CMIDecimal, 'range':scaled_range, 'mod':'r'}, + 'cmi.score._children':{'pattern':CMIIndex, 'mod':'r'}, + 'cmi.score.scaled':{'defaultvalue':'<?php echo isset($userdata->{'cmi.score.scaled'})?$userdata->{'cmi.score.scaled'}:'' ?>', 'format':CMIDecimal, 'range':scaled_range, 'mod':'rw'}, + 'cmi.score.raw':{'defaultvalue':'<?php echo isset($userdata->{'cmi.score.raw'})?$userdata->{'cmi.score.raw'}:'' ?>', 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.score.min':{'defaultvalue':'<?php echo isset($userdata->{'cmi.score.min'})?$userdata->{'cmi.score.min'}:'' ?>', 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.score.max':{'defaultvalue':'<?php echo isset($userdata->{'cmi.score.max'})?$userdata->{'cmi.score.max'}:'' ?>', 'format':CMIDecimal, 'mod':'rw'}, + 'cmi.session_time':{'format':CMITimespan, 'mod':'w', 'defaultvalue':'PT0H0M0S'}, + 'cmi.success_status':{'defaultvalue':'<?php echo isset($userdata->{'cmi.success_status'})?$userdata->{'cmi.success_status'}:'unknown' ?>', 'format':CMISStatus, 'mod':'rw'}, + 'cmi.suspend_data':{'defaultvalue':<?php echo isset($userdata->{'cmi.suspend_data'})?'\''.$userdata->{'cmi.suspend_data'}.'\'':'null' ?>, 'format':CMIString4000, 'mod':'rw'}, + 'cmi.time_limit_action':{'defaultvalue':<?php echo isset($userdata->timelimitaction)?'\''.$userdata->timelimitaction.'\'':'null' ?>, 'mod':'r'}, + 'cmi.total_time':{'defaultvalue':'<?php echo isset($userdata->{'cmi.total_time'})?$userdata->{'cmi.total_time'}:'PT0H0M0S' ?>', 'mod':'r'}, + 'nav.event':{'defaultvalue':'', 'format':NAVEvent, 'mod':'w'} + }; + // + // Datamodel inizialization + // + var cmi = new Object(); + cmi.comments_from_learner = new Object(); + cmi.comments_from_lms = new Object(); + cmi.interactions = new Object(); + cmi.learner_preference = new Object(); + cmi.objectives = new Object(); + cmi.score = new Object(); + + // Navigation Object + var nav = new Object(); + + for (element in datamodel) { + if (element.match(/\.n\./) == null) { + if ((typeof eval('datamodel["'+element+'"].defaultvalue')) != 'undefined') { + eval(element+' = datamodel["'+element+'"].defaultvalue;'); + } else { + eval(element+' = "";'); + } + } + } + +<?php + $count = 0; + $objectives = ''; + foreach($userdata as $element => $value){ + if (substr($element,0,14) == 'cmi.objectives') { + preg_match('/.(\d+)./',$element,$matches); + $element = preg_replace('/.(\d+)./',"_\$1.",$element); + if ($matches[1] == $count) { + $count++; + $end = strpos($element,$matches[1])+strlen($matches[1]); + $subelement = substr($element,0,$end); + echo ' '.$subelement." = new Object();\n"; + echo ' '.$subelement.".score = new Object();\n"; + echo ' '.$subelement.".score._children = score_children;\n"; + echo ' '.$subelement.".score.scaled = '';\n"; + echo ' '.$subelement.".score.raw = '';\n"; + echo ' '.$subelement.".score.min = '';\n"; + echo ' '.$subelement.".score.max = '';\n"; + } + echo ' '.$element.' = \''.$value."';\n"; + } + } + if ($count > 0) { + echo ' cmi.objectives._count = '.$count.";\n"; + } +?> + + if (cmi.completion_status == '') { + cmi.completion_status = 'not attempted'; + } + + // + // API Methods definition + // + var Initialized = false; + var Terminated = false; + var diagnostic = ""; + + function Initialize (param) { + errorCode = "0"; + if (param == "") { + if ((!Initialized) && (!Terminated)) { + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Initialized SCORM 1.3");'; + } + ?> + Initialized = true; + errorCode = "0"; + return "true"; + } else { + if (Initializated) { + errorCode = "103"; + } else { + errorCode = "104"; + } + } + } else { + errorCode = "201"; + } + return "false"; + } + + function Terminate (param) { + errorCode = "0"; + if (param == "") { + if ((Initialized) && (!Terminated)) { + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Terminated SCORM 1.3");'; + } + ?> + Initialized = false; + Terminated = true; + result = StoreData(cmi,true); + if (nav.event != '') { + if (nav.event == 'continue') { + setTimeout('top.nextSCO();',500); + } else { + setTimeout('top.prevSCO();',500); + } + } else { + if (<?php echo $scorm->auto ?> == 1) { + setTimeout('top.nextSCO();',500); + } + } + return "true"; + } else { + if (Terminated) { + errorCode = "113"; + } else { + errorCode = "112"; + } + } + } else { + errorCode = "201"; + } + return "false"; + } + + function GetValue (element) { + errorCode = "0"; + diagnostic = ""; + if ((Initialized) && (!Terminated)) { + if (element !="") { + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'w') { + element = element.replace(expression, "_$1."); + elementIndexes = element.split('.'); + subelement = 'cmi'; + i = 1; + while ((i < elementIndexes.length) && (typeof eval(subelement) != "undefined")) { + subelement += '.'+elementIndexes[i++]; + } + if (subelement == element) { + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+": "+eval(element));'; + } + ?> + return eval(element); + } else { + errorCode = "0"; // Need to check if it is the right errorCode + } + } else { + //errorCode = eval('datamodel["'+elementmodel+'"].readerror'); + errorCode = "405"; + } + } else { + childrenstr = '._children'; + countstr = '._count'; + if (elementmodel.substr(elementmodel.length-childrenstr.length,elementmodel.length) == childrenstr) { + parentmodel = elementmodel.substr(0,elementmodel.length-childrenstr.length); + if ((typeof eval('datamodel["'+parentmodel+'"]')) != "undefined") { + errorCode = "301"; + diagnostic = "Data Model Element Does Not Have Children"; + } else { + errorCode = "401"; + } + } else if (elementmodel.substr(elementmodel.length-countstr.length,elementmodel.length) == countstr) { + parentmodel = elementmodel.substr(0,elementmodel.length-countstr.length); + if ((typeof eval('datamodel["'+parentmodel+'"]')) != "undefined") { + errorCode = "301"; + diagnostic = "Data Model Element Cannot Have Count"; + } else { + errorCode = "401"; + } + } else { + errorCode = "401"; + } + } + } else { + errorCode = "301"; + } + } else { + if (Terminated) { + errorCode = "123"; + } else { + errorCode = "122"; + } + } + return ""; + } + + function SetValue (element,value) { + errorCode = "0"; + diagnostic = ""; + if ((Initialized) && (!Terminated)) { + if (element != "") { + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'r') { + expression = new RegExp(eval('datamodel["'+elementmodel+'"].format')); + value = value+''; + matches = value.match(expression); + if (matches != null) { + //Create dynamic data model element + if (element != elementmodel) { + elementIndexes = element.split('.'); + subelement = 'cmi'; + for (i=1;i < elementIndexes.length-1;i++) { + elementIndex = elementIndexes[i]; + if (elementIndexes[i+1].match(/^\d+$/)) { + if ((typeof eval(subelement+'.'+elementIndex)) == "undefined") { + eval(subelement+'.'+elementIndex+' = new Object();'); + eval(subelement+'.'+elementIndex+'._count = 0;'); + } + if (elementIndexes[i+1] == eval(subelement+'.'+elementIndex+'._count')) { + eval(subelement+'.'+elementIndex+'._count++;'); + } + if (elementIndexes[i+1] > eval(subelement+'.'+elementIndex+'._count')) { + errorCode = "351"; + diagnostic = "Data Model Element Collection Set Out Of Order"; + } + subelement = subelement.concat('.'+elementIndex+'_'+elementIndexes[i+1]); + i++; + } else { + subelement = subelement.concat('.'+elementIndex); + } + if ((typeof eval(subelement)) == "undefined") { + eval(subelement+' = new Object();'); + if (subelement.substr(0,14) == 'cmi.objectives') { + eval(subelement+'.score = new Object();'); + eval(subelement+'.score._children = score_children;'); + eval(subelement+'.score.scaled = "";'); + eval(subelement+'.score.raw = "";'); + eval(subelement+'.score.min = "";'); + eval(subelement+'.score.max = "";'); + } + if (subelement.substr(0,16) == 'cmi.interactions') { + eval(subelement+'.objectives = new Object();'); + eval(subelement+'.objectives._count = 0;'); + eval(subelement+'.correct_responses = new Object();'); + eval(subelement+'.correct_responses._count = 0;'); + } + } + } + element = subelement.concat('.'+elementIndexes[elementIndexes.length-1]); + } + //Store data + if (errorCode == "0") { + if ((typeof eval('datamodel["'+elementmodel+'"].range')) != "undefined") { + range = eval('datamodel["'+elementmodel+'"].range'); + ranges = range.split('#'); + value = value*1.0; + if (value >= ranges[0]) { + if ((ranges[1] == '*') || (value <= ranges[1])) { + eval(element+'="'+value+'";'); + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+":= "+value);'; + } + ?> + return "true"; + } else { + errorCode = '407'; + } + } else { + errorCode = '407'; + } + } else { + eval(element+'="'+value+'";'); + errorCode = "0"; + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert(element+":= "+value);'; + } + ?> + return "true"; + } + } + } else { + //errorCode = eval('datamodel["'+elementmodel+'"].writeerror'); + errorCode = "406"; + } + } else { + //errorCode = eval('datamodel["'+elementmodel+'"].writeerror'); + errorCode = "404"; + } + } else { + errorCode = "401" + } + } else { + errorCode = "351"; + } + } else { + if (Terminated) { + errorCode = "133"; + } else { + errorCode = "132"; + } + } + return "false"; + } + + function Commit (param) { + errorCode = "0"; + if (param == "") { + if ((Initialized) && (!Terminated)) { + result = StoreData(cmi,false); + <?php + if (($CFG->debug > 7) && (isadmin())) { + echo 'alert("Data Commited");'; + } + ?> + return "true"; + } else { + if (Terminated) { + errorCode = "143"; + } else { + errorCode = "142"; + } + } + } else { + errorCode = "201"; + } + return "false"; + } + + function GetLastError () { + return errorCode; + } + + function GetErrorString (param) { + if (param != "") { + var errorString = new Array(); + errorString["0"] = "No error"; + errorString["101"] = "General exception"; + errorString["102"] = "General Inizialization Failure"; + errorString["103"] = "Already Initialized"; + errorString["104"] = "Content Instance Terminated"; + errorString["111"] = "General Termination Failure"; + errorString["112"] = "Termination Before Inizialization"; + errorString["113"] = "Termination After Termination"; + errorString["122"] = "Retrieve Data Before Initialization"; + errorString["123"] = "Retrieve Data After Termination"; + errorString["132"] = "Store Data Before Inizialization"; + errorString["133"] = "Store Data After Termination"; + errorString["142"] = "Commit Before Inizialization"; + errorString["143"] = "Commit After Termination"; + errorString["201"] = "General Argument Error"; + errorString["301"] = "General Get Failure"; + errorString["351"] = "General Set Failure"; + errorString["391"] = "General Commit Failure"; + errorString["401"] = "Undefinited Data Model"; + errorString["402"] = "Unimplemented Data Model Element"; + errorString["403"] = "Data Model Element Value Not Initialized"; + errorString["404"] = "Data Model Element Is Read Only"; + errorString["405"] = "Data Model Element Is Write Only"; + errorString["406"] = "Data Model Element Type Mismatch"; + errorString["407"] = "Data Model Element Value Out Of Range"; + errorString["408"] = "Data Model Dependency Not Established"; + return errorString[param]; + } else { + return ""; + } + } + + function GetDiagnostic (param) { + if (param == "") { + return diagnostic; + } + return param; + } + + function AddTime (first, second) { + var timestring = 'P'; + var matchexpr = /^P((\d+)Y)?((\d+)M)?((\d+)D)?(T((\d+)H)?((\d+)M)?((\d+(\.\d{1,2})?)S)?)?$/; + var firstarray = first.match(matchexpr); + var secondarray = second.match(matchexpr); + if ((firstarray != null) && (secondarray != null)) { + var secs = parseFloat(firstarray[13],10)+parseFloat(secondarray[13],10); //Seconds + change = Math.floor(secs / 60); + secs = secs - (change * 60); + mins = parseInt(firstarray[11],10)+parseInt(secondarray[11],10)+change; //Minutes + change = Math.floor(mins / 60); + mins = mins - (change * 60); + hours = parseInt(firstarray[10],10)+parseInt(secondarray[10],10)+change; //Hours + change = Math.floor(hours / 24); + hours = hours - (change * 24); + days = parseInt(firstarray[6],10)+parseInt(secondarray[6],10)+change; // Days + months = parseInt(firstarray[4],10)+parseInt(secondarray[4],10) + years = parseInt(firstarray[2],10)+parseInt(secondarray[2],10) + } + if (years > 0) { + timestring += years + 'Y'; + } + if (months > 0) { + timestring += months + 'M'; + } + if (days > 0) { + timestring += days + 'D'; + } + if ((hours > 0) || (mins > 0) || (secs > 0)) { + timestring += 'T'; + if (hours > 0) { + timestring += hours + 'H'; + } + if (mins > 0) { + timestring += mins + 'M'; + } + if (secs > 0) { + timestring += secs + 'S'; + } + } + return timestring; + } + + function TotalTime() { + total_time = AddTime(cmi.total_time, cmi.session_time); + return '&'+underscore('cmi.total_time')+'='+escape(total_time); + } + + function CollectData(data,parent) { + var datastring = ''; + for (property in data) { + if (typeof data[property] == 'object') { + datastring += CollectData(data[property],parent+'.'+property); + } else { + element = parent+'.'+property; + expression = new RegExp(CMIIndex,'g'); + elementmodel = element.replace(expression,'.n.'); + if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].mod') != 'r') { + elementstring = '&'+underscore(element)+'='+escape(data[property]); + if ((typeof eval('datamodel["'+elementmodel+'"].defaultvalue')) != "undefined") { + if (eval('datamodel["'+elementmodel+'"].defaultvalue') != data[property]) { + datastring += elementstring; + } + } else { + datastring += elementstring; + } + } + } + } + } + return datastring; + } + + function StoreData(data,storetotaltime) { + if (storetotaltime) { + if (cmi.mode == 'normal') { + if (cmi.credit == 'credit') { + if ((cmi.completion_threshold != null) && (cmi.progress_measure != '')) { + if (cmi.progress_measure >= cmi.completion_threshold) { + cmi.completion_status = 'completed'; + } else { + cmi.completion_status = 'incomplete'; + } + } + if ((cmi.scaled_passed_score != null) && (cmi.score.scaled != '')) { + if (cmi.score.scaled >= cmi.scaled_passed_score) { + cmi.success_status = 'passed'; + } else { + cmi.success_status = 'failed'; + } + } + } + } + datastring = CollectData(data,'cmi'); + datastring += TotalTime(); + } else { + datastring = CollectData(data,'cmi'); + } + datastring += '&attempt=<?php echo $attempt ?>'; + datastring += '&scoid=<?php echo $sco->id ?>'; + //popupwin(datastring); + var myRequest = NewHttpReq(); + result = DoRequest(myRequest,"<?php p($CFG->wwwroot) ?>/mod/scorm/datamodel.php","id=<?php p($id) ?>&sesskey=<?php p($USER->sesskey) ?>"+datastring); + results = result.split('\n'); + errorCode = results[1]; + return results[0]; + } + + this.Initialize = Initialize; + this.Terminate = Terminate; + this.GetValue = GetValue; + this.SetValue = SetValue; + this.Commit = Commit; + this.GetLastError = GetLastError; + this.GetErrorString = GetErrorString; + this.GetDiagnostic = GetDiagnostic; +} + +var API_1484_11 = new SCORMapi1_3(); diff --git a/mod/scorm/datamodels/scorm_13lib.php b/mod/scorm/datamodels/scorm_13lib.php new file mode 100644 index 00000000000..f3c9adc43cb --- /dev/null +++ b/mod/scorm/datamodels/scorm_13lib.php @@ -0,0 +1,784 @@ +<?php +/* // Added by Pham Minh Duc + case 'ADLNAV:PRESENTATION': + $parent = array_pop($parents); + array_push($parents, $parent); + foreach ($block['children'] as $adlnav) { + if ($adlnav['name'] == 'ADLNAV:NAVIGATIONINTERFACE') { + foreach ($adlnav['children'] as $adlnavInterface) { + if ($adlnavInterface['name'] == 'ADLNAV:HIDELMSUI') { + if ($adlnavInterface['tagData'] == 'continue') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->next = 1; + } + if ($adlnavInterface['tagData'] == 'previous') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->previous = 1; + } + } + + } + } + } + break; + case 'IMSSS:SEQUENCING': + $parent = array_pop($parents); + array_push($parents, $parent); + foreach ($block['children'] as $sequencing) { + if ($sequencing['name']=='IMSSS:CONTROLMODE') { + if ($sequencing['attrs']['CHOICE'] == 'false') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->choice = 0; + } + if ($sequencing['attrs']['CHOICEEXIT'] == 'false') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->choiceexit = 0; + } + if ($sequencing['attrs']['FLOW'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->flow = 1; + } + if ($sequencing['attrs']['FORWARDONLY'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->forwardonly = 1; + } + if ($sequencing['attrs']['USECURRENTATTEMPTOBJECTINFO'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptobjectinfo = 1; + } + if ($sequencing['attrs']['USECURRENTATTEMPTPROGRESSINFO'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->usecurrentattemptprogressinfo = 1; + } + } + if ($sequencing['name']=='ADLSEQ:CONSTRAINEDCHOICECONSIDERATIONS') { + if ($sequencing['attrs']['CONSTRAINCHOICE'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->constrainChoice = 1; + } + if ($sequencing['attrs']['PREVENTACTIVATION'] == 'true') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->preventactivation = 1; + } + + } + if ($sequencing['name']=='IMSSS:OBJECTIVES') { + foreach ($sequencing['children'] as $objective) { + if ($objective['name']=='IMSSS:PRIMARYOBJECTIVE') { + foreach ($objective['children'] as $primaryobjective) { + if ($primaryobjective['name']=='IMSSS:MINNORMALIZEDMEASURE') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->minnormalizedmeasure = $primaryobjective['tagData']; + } + } + } + } + } + if ($sequencing['name']=='IMSSS:LIMITCONDITIONS') { + if (!empty($sequencing['attrs']['ATTEMPTLIMIT'])) { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptLimit = $sequencing['attrs']['ATTEMPTLIMIT']; + } + if (!empty($sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT'])) { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->attemptAbsoluteDurationLimit = $sequencing['attrs']['ATTEMPTABSOLUTEDURATIONLIMIT']; + } + } + if ($sequencing['name']=='IMSSS:ROLLUPRULES') { + $rolluprules = array(); + if (!empty($sequencing['attrs']['ROLLUPOBJECTIVESATISFIED'])) { + if ($sequencing['attrs']['ROLLUPOBJECTIVESATISFIED']== 'false') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupobjectivesatisfied = 0; + } + } + if (!empty($sequencing['attrs']['ROLLUPPROGRESSCOMPLETION'])) { + if ($sequencing['attrs']['ROLLUPPROGRESSCOMPLETION']== 'false') { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rollupprogresscompletion = 0; + } + } + if (!empty($sequencing['attrs']['OBJECTIVEMEASUREWEIGHT'])) { + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->objectivemeasureweight = $sequencing['attrs']['OBJECTIVEMEASUREWEIGHT']; + } + + if (!empty($sequencing['children'])){ + foreach ($sequencing['children'] as $sequencingrolluprule) { + if ($sequencingrolluprule['name']=='IMSSS:ROLLUPRULE' ) { + $rolluprule = new stdClass(); + if ($sequencingrolluprule['attrs']['CHILDACTIVITYSET'] !=' ') { + $rolluprule->childactivityset = $sequencingrolluprule['attrs']['CHILDACTIVITYSET']; + if (!empty($sequencingrolluprule['children'])) { + foreach ($sequencingrolluprule['children'] as $rolluproleconditions) { + if ($rolluproleconditions['name']=='IMSSS:ROLLUPCONDITIONS') { + $conditions = array(); + if (!empty($rolluproleconditions['attrs']['conditionCombination'])) { + $rolluprule->conditionCombination = $rolluproleconditions['attrs']['conditionCombination']; + } + foreach ($rolluproleconditions['children'] as $rolluprulecondition) { + if ($rolluprulecondition['name']=='IMSSS:ROLLUPCONDITION') { + $condition = new stdClass(); + if (!empty($rolluprulecondition['attrs']['OPERATOR'])) { + $condition->operator = $rolluprulecondition['attrs']['OPERATOR']; + } + if (!empty($rolluprulecondition['attrs']['CONDITION'])) { + $condition->condition = $rolluprulecondition['attrs']['CONDITION']; + } + array_push($conditions,$condition); + } + } + $rolluprule->conditions = $conditions; + } + if ($rolluproleconditions['name']=='IMSSS:ROLLUPACTION') { + $rolluprule->rollupruleaction = $rolluproleconditions['attrs']['ACTION']; + } + } + } + } + array_push($rolluprules, $rolluprule); + } + } + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->rolluprules = $rolluprules; + } + + if ($sequencing['name']=='IMSSS:SEQUENCINGRULES') { + $sequencingrules = array(); + foreach ($sequencing['children'] as $conditionrules) { + if ($conditionrules['name']=='IMSSS:EXITCONDITIONRULE') { + $sequencingrule = new stdClass(); + if (!empty($conditionrules['children'])) { + foreach ($conditionrules['children'] as $conditionrule) { + if ($conditionrule['name']=='IMSSS:RULECONDITIONS') { + $ruleconditions = array(); + if (!empty($conditionrule['attrs']['conditionCombination'])) { + $sequencingrule->conditionCombination = $conditionrule['attrs']['conditionCombination']; + } + foreach ($conditionrule['children'] as $rulecondition) { + if ($rulecondition['name']=='IMSSS:RULECONDITION') { + $condition = new stdClass(); + if (!empty($rulecondition['attrs']['OPERATOR'])) { + $condition->operator = $rulecondition['attrs']['OPERATOR']; + } + if (!empty($rulecondition['attrs']['CONDITION'])) { + $condition->condition = $rulecondition['attrs']['CONDITION']; + } + if (!empty($rulecondition['attrs']['MEASURETHRESHOLD'])) { + $condition->measurethreshold = $rulecondition['attrs']['MEASURETHRESHOLD']; + } + if (!empty($rulecondition['attrs']['REFERENCEDOBJECTIVE'])) { + $condition->referencedobjective = $rulecondition['attrs']['REFERENCEDOBJECTIVE']; + } + array_push($ruleconditions,$condition); + } + } + $sequencingrule->ruleconditions = $ruleconditions; + } + if ($conditionrule['name']=='IMSSS:RULEACTION') { + $sequencingrule->exitconditionruleaction = $conditionrule['attrs']['ACTION']; + } + } + } + array_push($sequencingrules,$sequencingrule); + } + if ($conditionrules['name']=='IMSSS:PRECONDITIONRULE') { + $sequencingrule = new stdClass(); + if (!empty($conditionrules['children'])) { + foreach ($conditionrules['children'] as $conditionrule) { + if ($conditionrule['name']=='IMSSS:RULECONDITIONS') { + $ruleconditions = array(); + if (!empty($conditionrule['attrs']['conditionCombination'])) { + $sequencingrule->conditionCombination = $conditionrule['attrs']['conditionCombination']; + } + foreach ($conditionrule['children'] as $rulecondition) { + if ($rulecondition['name']=='IMSSS:RULECONDITION') { + $condition = new stdClass(); + if (!empty($rulecondition['attrs']['OPERATOR'])) { + $condition->operator = $rulecondition['attrs']['OPERATOR']; + } + if (!empty($rulecondition['attrs']['CONDITION'])) { + $condition->condition = $rulecondition['attrs']['CONDITION']; + } + if (!empty($rulecondition['attrs']['MEASURETHRESHOLD'])) { + $condition->measurethreshold = $rulecondition['attrs']['MEASURETHRESHOLD']; + } + if (!empty($rulecondition['attrs']['REFERENCEDOBJECTIVE'])) { + $condition->referencedobjective = $rulecondition['attrs']['REFERENCEDOBJECTIVE']; + } + array_push($ruleconditions,$condition); + } + } + $sequencingrule->ruleconditions = $ruleconditions; + } + if ($conditionrule['name']=='IMSSS:RULEACTION') { + $sequencingrule->preconditionruleaction = $conditionrule['attrs']['ACTION']; + } + } + } + array_push($sequencingrules,$sequencingrule); + } + if ($conditionrules['name']=='IMSSS:POSTCONDITIONRULE') { + $sequencingrule = new stdClass(); + if (!empty($conditionrules['children'])) { + foreach ($conditionrules['children'] as $conditionrule) { + if ($conditionrule['name']=='IMSSS:RULECONDITIONS'){ + $ruleconditions = array(); + if (!empty($conditionrule['attrs']['conditionCombination'])){ + $sequencingrule->conditionCombination = $conditionrule['attrs']['conditionCombination']; + } + foreach ($conditionrule['children'] as $rulecondition){ + if ($rulecondition['name']=='IMSSS:RULECONDITION'){ + $condition = new stdClass(); + if (!empty($rulecondition['attrs']['OPERATOR'])){ + $condition->operator = $rulecondition['attrs']['OPERATOR']; + } + if (!empty($rulecondition['attrs']['CONDITION'])){ + $condition->condition = $rulecondition['attrs']['CONDITION']; + } + if (!empty($rulecondition['attrs']['MEASURETHRESHOLD'])){ + $condition->measurethreshold = $rulecondition['attrs']['MEASURETHRESHOLD']; + } + if (!empty($rulecondition['attrs']['REFERENCEDOBJECTIVE'])){ + $condition->referencedobjective = $rulecondition['attrs']['REFERENCEDOBJECTIVE']; + } + array_push($ruleconditions,$condition); + } + } + $sequencingrule->ruleconditions = $ruleconditions; + } + if ($conditionrule['name']=='IMSSS:RULEACTION'){ + $sequencingrule->postconditionruleaction = $conditionrule['attrs']['ACTION']; + } + } + } + array_push($sequencingrules,$sequencingrule); + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->sequencingrules = $sequencingrules; + } + } + } + break; +*/ + +function scorm_parse_scorm($pkgdir,$scormid) { + global $CFG; + + $launch = 0; + $manifestfile = $pkgdir.'/imsmanifest.xml'; + + if (is_file($manifestfile)) { + + $xmlstring = file_get_contents($manifestfile); + $objXML = new xml2Array(); + $manifests = $objXML->parse($xmlstring); + // print_r($manifests); + $scoes = new stdClass(); + $scoes->version = ''; + $scoes = scorm_get_manifest($manifests,$scoes); + + if (count($scoes->elements) > 0) { + $olditems = get_records('scorm_scoes','scorm',$scormid); + foreach ($scoes->elements as $manifest => $organizations) { + foreach ($organizations as $organization => $items) { + foreach ($items as $identifier => $item) { + $item->scorm = $scormid; + $item->manifest = $manifest; + $item->organization = $organization; + if ($olditemid = scorm_array_search('identifier',$item->identifier,$olditems)) { + $item->id = $olditemid; + $id = update_record('scorm_scoes',$item); + unset($olditems[$olditemid]); + } else { + $id = insert_record('scorm_scoes',$item); + } + // Added by Pham Minh Duc + $item->scormid = $scormid; + $item->scoid = $id; + $idControlMode = insert_record('scorm_sequencing_controlmode',$item); + + if (!empty($item->sequencingrules)) { + foreach($item->sequencingrules as $sequencingrule) { + $sequencingrule->scormid = $scormid; + $sequencingrule->scoid = $item->scoid; + $idruleconditions = insert_record('scorm_sequencing_ruleconditions',$sequencingrule); + foreach($sequencingrule->ruleconditions as $rulecondition) { + $rulecondition->scormid = $sequencingrule->scormid; + $rulecondition->scoid = $sequencingrule->scoid; + $rulecondition->ruleconditionsid = $idruleconditions; + $idrulecondition = insert_record('scorm_sequencing_rulecondition',$rulecondition); + } + } + } + + if (!empty($item->rolluprules)) { + $idControlMode = insert_record('scorm_sequencing_rolluprules',$item); + foreach($item->rolluprules as $rollup) { + $rollup->rolluprulesid =$idControlMode; + $rollup->scormid = $scormid; + $rollup->scoid = $item->scoid; + + $idRollupRule = insert_record('scorm_sequencing_rolluprule',$rollup); + $rollup->rollupruleid = $idRollupRule; + $idconditions = insert_record('scorm_sequencing_rollupruleconditions',$rollup); + foreach($rollup->conditions as $condition){ + $condition->ruleconditionsid = $idconditions; + $condition->scormid = $rollup->scormid; + $condition->scoid = $rollup->scoid; + $idcondition = insert_record('scorm_sequencing_rolluprulecondition',$condition); + } + } + } + // End Add + if (($launch == 0) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { + $launch = $id; + } + } + } + } + if (!empty($olditems)) { + foreach($olditems as $olditem) { + delete_records('scorm_scoes','id',$olditem->id); + delete_records('scorm_scoes_track','scoid',$olditem->id); + } + } + set_field('scorm','version',$scoes->version,'id',$scormid); + } + } + + return $launch; +} + +function scorm_get_toc($user,$scorm,$liststyle,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false) { + global $CFG; + + // Added by Pham Minh Duc + $suspendscoid = scorm_get_suspendscoid($scorm->id,$user->id); + // End add + + $strexpand = get_string('expcoll','scorm'); + $modestr = ''; + if ($mode == 'browse') { + $modestr = '&mode='.$mode; + } + $scormpixdir = $CFG->modpixpath.'/scorm/pix'; + + $result = new stdClass(); + $result->toc = "<ul id='0' class='$liststyle'>\n"; + $tocmenus = array(); + $result->prerequisites = true; + $incomplete = false; + + // + // Get the current organization infos + // + $organizationsql = ''; + if (!empty($currentorg)) { + if (($organizationtitle = get_field('scorm_scoes','title','scorm',$scorm->id,'identifier',$currentorg)) != '') { + $result->toc .= "\t<li>$organizationtitle</li>\n"; + $tocmenus[] = $organizationtitle; + } + $organizationsql = "AND organization='$currentorg'"; + } + // + // If not specified retrieve the last attempt number + // + if (empty($attempt)) { + $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")){ + // + // 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->status == '') { + $usertrack->status = 'notattempted'; + } + $usertracks[$sco->identifier] = $usertrack; + } + } + } + + $level=0; + $sublist=1; + $previd = 0; + $nextid = 0; + $findnext = false; + $parents[$level]='/'; + + foreach ($scoes as $sco) { + if ($parents[$level]!=$sco->parent) { + if ($newlevel = array_search($sco->parent,$parents)) { + for ($i=0; $i<($level-$newlevel); $i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + $level = $newlevel; + } else { + $i = $level; + $closelist = ''; + while (($i > 0) && ($parents[$level] != $sco->parent)) { + $closelist .= "\t\t</ul></li>\n"; + $i--; + } + if (($i == 0) && ($sco->parent != $currentorg)) { + $style = ''; + if (isset($_COOKIE['hide:SCORMitem'.$sco->id])) { + $style = ' style="display: none;"'; + } + $result->toc .= "\t\t<li><ul id='$sublist' class='$liststyle'$style>\n"; + $level++; + } else { + $result->toc .= $closelist; + $level = $i; + } + $parents[$level]=$sco->parent; + } + } + $result->toc .= "\t\t<li>"; + $nextsco = next($scoes); + if (($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { + $sublist++; + $icon = 'minus'; + if (isset($_COOKIE['hide:SCORMitem'.$nextsco->id])) { + $icon = 'plus'; + } + $result->toc .= '<a href="javascript:expandCollide(img'.$sublist.','.$sublist.','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$scormpixdir.'/'.$icon.'.gif" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>'; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/spacer.gif" />'; + } + if (empty($sco->title)) { + $sco->title = $sco->identifier; + } + if (!empty($sco->launch)) { + $startbold = ''; + $endbold = ''; + $score = ''; + if (empty($scoid) && ($mode != 'normal')) { + $scoid = $sco->id; + } + // Modified by Pham Minh Duc + if ($suspendscoid == $sco->id){ + $result->toc .= '<img src="'.$scormpixdir.'/suspend.gif" alt="Dang tam dung o day" title="Dang dung o day" />'; + } else { + if (isset($usertracks[$sco->identifier])) { + $usertrack = $usertracks[$sco->identifier]; + $strstatus = get_string($usertrack->status,'scorm'); + $result->toc .= '<img src="'.$scormpixdir.'/'.$usertrack->status.'.gif" alt="'.$strstatus.'" title="'.$strstatus.'" />'; + + if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) { + $incomplete = true; + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + } + if ($usertrack->score_raw != '') { + $score = '('.get_string('score','scorm').': '.$usertrack->score_raw.')'; + } + } else { + if ($play && empty($scoid)) { + $scoid = $sco->id; + } + if ($sco->scormtype == 'sco') { + $result->toc .= '<img src="'.$scormpixdir.'/notattempted.gif" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />'; + $incomplete = true; + } else { + $result->toc .= '<img src="'.$scormpixdir.'/asset.gif" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />'; + } + } + } + // End Modify + if ($sco->id == $scoid) { + $startbold = '<b>'; + $endbold = '</b>'; + $findnext = true; + $shownext = $sco->next; + $showprev = $sco->previous; + } + + if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { + if (!empty($sco->launch)) { + $previd = $sco->id; + } + } + if (empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks)) { + if ($sco->id == $scoid) { + $result->prerequisites = true; + } + // Modified by Pham Minh Duc + if (scorm_isChoice($scorm->id,$sco->id) == 1) { + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='.$sco->id; + $result->toc .= ' '.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n"; + $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); + } else { + $result->toc .= ' '.$startbold.format_string($sco->title).$score.$endbold."</li>\n"; + $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); + } + // End modify + } else { + if ($sco->id == $scoid) { + $result->prerequisites = false; + } + $result->toc .= ' '.$sco->title."</li>\n"; + } + } else { + $result->toc .= ' '.$sco->title."</li>\n"; + } + if (($nextsco !== false) && ($nextid == 0) && ($findnext)) { + if (!empty($nextsco->launch)) { + $nextid = $nextsco->id; + } + } + } + for ($i=0;$i<$level;$i++) { + $result->toc .= "\t\t</ul></li>\n"; + } + + if ($play) { + $sco = get_record('scorm_scoes','id',$scoid); + $sco->previd = $previd; + $sco->nextid = $nextid; + $result->sco = $sco; + $result->incomplete = $incomplete; + } else { + $result->incomplete = $incomplete; + } + } + $result->toc .= "\t</ul>\n"; + if ($scorm->hidetoc == 0) { + $result->toc .= ' + <script language="javascript" type="text/javascript"> + <!-- + function expandCollide(which,list,item) { + var nn=document.ids?true:false + var w3c=document.getElementById?true:false + var beg=nn?"document.ids.":w3c?"document.getElementById(":"document.all."; + var mid=w3c?").style":".style"; + + if (eval(beg+list+mid+".display") != "none") { + which.src = "'.$scormpixdir.'/plus.gif"; + eval(beg+list+mid+".display=\'none\';"); + new cookie("hide:SCORMitem" + item, 1, 356, "/").set(); + } else { + which.src = "'.$scormpixdir.'/minus.gif"; + eval(beg+list+mid+".display=\'block\';"); + new cookie("hide:SCORMitem" + item, 1, -1, "/").set(); + } + } + --> + </script>'."\n"; + } + + $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='; + $result->tocmenu = popup_form($url,$tocmenus, "tocmenu", $sco->id, '', '', '', true); + + return $result; +} + +// +// Functions added by Pham Minh Duc +// +function scorm_get_score_from_parent($sco,$userid,$grademethod=VALUESCOES) { + $scores = NULL; + $scores->scoes = 0; + $scores->values = 0; + $scores->scaled = 0; + $scores->max = 0; + $scores->sum = 0; + + $scoes_total = 0; + $scoes_count = 0; + $attempt = scorm_get_last_attempt($sco->scorm, $userid); + $scoes = get_records('scorm_scoes', 'parent', $sco->identifier); + foreach ($scoes as $sco) + { + $scoes_total++; + if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { + if (($userdata->status == 'completed') || ($userdata->success_status == 'passed')) { + $scoes_count++; + } + + $scoreraw = $userdata->score_raw; + if (!empty($userdata->score_raw)) { + $scores->values++; + $scores->sum += $userdata->score_raw; + $scores->max = ($userdata->score_raw > $scores->max)?$userdata->score_raw:$scores->max; + } + if (!empty($userdata->score_scaled)) { + $scores->scaled = $scores->scaled + $userdata->score_scaled; + } + } + } + if ($scoes_count > 0) { + $scores->scaled = ($scores->scaled)/($scoes_count); + } + switch ($grademethod) { + case VALUEHIGHEST: + return $scores->max; + break; + case VALUEAVERAGE: + if ($scores->values > 0) { + return $scores->sum/$scores->values; + } else { + return 0; + } + break; + case VALUESUM: + return $scores->sum; + break; + case VALUESCOES: + return $scores->scaled; + break; + } +} + +function scorm_get_user_sco_count($scormid, $userid) { + $scoes_count = 0; + $attempt = scorm_get_last_attempt($current->scorm, $userid); + $scoes = get_records('scorm_scoes', 'scorm', $scormid); + + foreach ($scoes as $sco) { + if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { + if (($userdata->status == 'completed') || ($userdata->success_status == 'passed')) { + $scoes_count++; + } + } + } + return $scoes_count; +} + +function scorm_grade_user_new($scoes, $userid, $grademethod=VALUESCOES) { + $scores = NULL; + $scores->scoes = 0; + $scores->values = 0; + $scores->scaled = 0; + $scores->max = 0; + $scores->sum = 0; + + if (!$scoes) { + return ''; + } + $current = current($scoes); + $attempt = scorm_get_last_attempt($current->scorm, $userid); + foreach ($scoes as $sco) { + if ($userdata=scorm_get_tracks($sco->id, $userid,$attempt)) { + if (($userdata->status == 'completed') || ($userdata->success_status == 'passed')) { + $scores->scoes++; + } + $scaled = $userdata->score_scaled; + $scoreraw = $userdata->score_raw; + if ($scaled ==0){ + $scores->scaled = $scores->scaled / $scores->scoes; + } + if (!empty($userdata->score_raw)) { + $scores->values++; + $scores->sum += $userdata->score_raw; + $scores->max = ($userdata->score_raw > $scores->max)?$userdata->score_raw:$scores->max; + } + if (!empty($scaled)) { + $scores->scaled = (($scores->scaled) * ($scores->scoes-1) + $scaled)/($scores->scoes); + } + } + } + switch ($grademethod) { + case VALUEHIGHEST: + return $scores->max; + break; + case VALUEAVERAGE: + if ($scores->values > 0) { + return $scores->sum/$scores->values; + } else { + return 0; + } + break; + case VALUESUM: + return $scores->sum; + break; + case VALUESCOES: + return $scores->scaled; + break; + } +} + +function scorm_insert_statistic($statisticInput){ + $id = null; + if ($statistic = get_record_select('scorm_statistic',"userid='$statisticInput->userid' AND scormid='$statisticInput->scormid'")) { + + $statistic->durationtime = $statisticInput->duration; + $statistic->accesstime = $statisticInput->accesstime; + $statistic->status = $statisticInput->status; + $statistic->attemptnumber = $statisticInput->attemptnumber; + $id = update_record('scorm_statistic',$statistic); + } else { + $id = insert_record('scorm_statistic',$statisticInput); + } + return $id; +} + +function scorm_insert_trackmodel($userid,$scormid,$scoid,$attempt) { + $id = null; + if ($suspendtrack = get_record_select('scorm_suspendtrack',"userid='$userid' AND scormid='$scormid'")) { + $suspendtrack->suspendscoid = $scoid; + $suspendtrack->attempt = $attempt; + $id = update_record('scorm_suspendtrack',$suspendtrack); + } else { + $suspendtrack->scormid = $scormid; + $suspendtrack->suspendscoid = $scoid; + $suspendtrack->userid = $userid; + $suspendtrack->attempt = $attempt; + $id = insert_record('scorm_suspendtrack',$suspendtrack); + } + return $id; +} + +function scorm_get_suspendscoid($scormid,$userid) { + if ($sco = get_record("scorm_suspendtrack","scormid",$scormid,"userid",$userid)) { + $suspendscoid = $sco->suspendscoid; + return $suspendscoid; + } else { + return 0; + } +} + +function scorm_set_attempt($scoid,$userid) { + if ($scormid = get_field('scorm_scoes','scorm','id',$scoid)) { + $attempt = scorm_get_last_attempt($scormid,$userid); + } else { + $attempt = 1; + } + $scormtype = get_field('scorm_scoes','scormtype','id',$scoid) ; + if ($scormtype == 'sco'){ + $element = 'cmi.attempt_status'; + $value = 'attempted'; + scorm_insert_track($userid,$scormid,$scoid,$attempt,$element,$value); + } +} + +function scorm_get_AbsoluteTimeLimit($scoid){ + $sco = get_record("scorm_scoes","id",$scoid); + if (!empty($sco)){ + return $sco->attemptAbsoluteDurationLimit; + } + return 0; +} + +function scorm_update_status($scormid,$scoid) +{ + +} + +function scorm_get_nextsco($scormid,$scoid) +{ + +} + +function scorm_get_presco($scormid,$scoid) +{ + +} + +function scorm_isChoice($scormid,$scoid) +{ + $sco = get_record("scorm_sequencing_controlmode","scormid",$scormid,"scoid",$scoid); + $scoparent = get_record("scorm_sequencing_controlmode","scormid",$scormid,"identifier",$sco->parent); + + return $scoparent->choice; +} + +function scorm_isChoiceexit($scormid,$scoid) +{ + $sco = get_record("scorm_sequencing_controlmode","scormid",$scormid,"scoid",$scoid); + $scoparent = get_record("scorm_sequencing_controlmode","scormid",$scormid,"identifier",$sco->parent); + + return $scoparent->choiceexit; +} +// End add + +?> diff --git a/mod/scorm/datamodels/scormlib.php b/mod/scorm/datamodels/scormlib.php new file mode 100644 index 00000000000..54d419f9f0a --- /dev/null +++ b/mod/scorm/datamodels/scormlib.php @@ -0,0 +1,354 @@ +<?php +function scorm_get_resources($blocks) { + $resources = array(); + foreach ($blocks as $block) { + if ($block['name'] == 'RESOURCES') { + foreach ($block['children'] as $resource) { + if ($resource['name'] == 'RESOURCE') { + $resources[addslashes($resource['attrs']['IDENTIFIER'])] = $resource['attrs']; + } + } + } + } + return $resources; +} + +function scorm_get_manifest($blocks,$scoes) { + static $parents = array(); + static $resources; + + static $manifest; + static $organization; + + if (count($blocks) > 0) { + foreach ($blocks as $block) { + switch ($block['name']) { + case 'METADATA': + if (isset($block['children'])) { + foreach ($block['children'] as $metadata) { + if ($metadata['name'] == 'SCHEMAVERSION') { + if (empty($scoes->version)) { + if (isset($metadata['tagData']) && (preg_match("/^(1\.2)$|^(CAM )?(1\.3)$/",$metadata['tagData'],$matches))) { + $scoes->version = 'SCORM_'.$matches[count($matches)-1]; + } else { + $scoes->version = 'SCORM_1.2'; + } + } + } + } + } + break; + case 'MANIFEST': + $manifest = addslashes($block['attrs']['IDENTIFIER']); + $organization = ''; + $resources = array(); + $resources = scorm_get_resources($block['children']); + $scoes = scorm_get_manifest($block['children'],$scoes); + if (count($scoes->elements) <= 0) { + foreach ($resources as $item => $resource) { + if (!empty($resource['HREF'])) { + $sco = new stdClass(); + $sco->identifier = $item; + $sco->title = $item; + $sco->parent = '/'; + $sco->launch = addslashes($resource['HREF']); + $sco->scormtype = addslashes($resource['ADLCP:SCORMTYPE']); + $scoes->elements[$manifest][$organization][$item] = $sco; + } + } + } + break; + case 'ORGANIZATIONS': + if (!isset($scoes->defaultorg)) { + $scoes->defaultorg = addslashes($block['attrs']['DEFAULT']); + } + $scoes = scorm_get_manifest($block['children'],$scoes); + break; + case 'ORGANIZATION': + $identifier = addslashes($block['attrs']['IDENTIFIER']); + $organization = ''; + $scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier; + $scoes->elements[$manifest][$organization][$identifier]->parent = '/'; + $scoes->elements[$manifest][$organization][$identifier]->launch = ''; + $scoes->elements[$manifest][$organization][$identifier]->scormtype = ''; + + $parents = array(); + $parent = new stdClass(); + $parent->identifier = $identifier; + $parent->organization = $organization; + array_push($parents, $parent); + $organization = $identifier; + + $scoes = scorm_get_manifest($block['children'],$scoes); + + array_pop($parents); + break; + case 'ITEM': + $parent = array_pop($parents); + array_push($parents, $parent); + + $identifier = addslashes($block['attrs']['IDENTIFIER']); + $scoes->elements[$manifest][$organization][$identifier]->identifier = $identifier; + $scoes->elements[$manifest][$organization][$identifier]->parent = $parent->identifier; + if (!isset($block['attrs']['ISVISIBLE'])) { + $block['attrs']['ISVISIBLE'] = 'true'; + } + $scoes->elements[$manifest][$organization][$identifier]->isvisible = addslashes($block['attrs']['ISVISIBLE']); + if (!isset($block['attrs']['PARAMETERS'])) { + $block['attrs']['PARAMETERS'] = ''; + } + $scoes->elements[$manifest][$organization][$identifier]->parameters = addslashes($block['attrs']['PARAMETERS']); + if (!isset($block['attrs']['IDENTIFIERREF'])) { + $scoes->elements[$manifest][$organization][$identifier]->launch = ''; + $scoes->elements[$manifest][$organization][$identifier]->scormtype = 'asset'; + } else { + $idref = addslashes($block['attrs']['IDENTIFIERREF']); + $base = ''; + if (isset($resources[$idref]['XML:BASE'])) { + $base = $resources[$idref]['XML:BASE']; + } + $scoes->elements[$manifest][$organization][$identifier]->launch = addslashes($base.$resources[$idref]['HREF']); + if (empty($resources[$idref]['ADLCP:SCORMTYPE'])) { + $resources[$idref]['ADLCP:SCORMTYPE'] = 'asset'; + } + $scoes->elements[$manifest][$organization][$identifier]->scormtype = addslashes($resources[$idref]['ADLCP:SCORMTYPE']); + } + + $parent = new stdClass(); + $parent->identifier = $identifier; + $parent->organization = $organization; + array_push($parents, $parent); + + $scoes = scorm_get_manifest($block['children'],$scoes); + + array_pop($parents); + break; + case 'TITLE': + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->title = addslashes($block['tagData']); + break; + case 'ADLCP:PREREQUISITES': + if ($block['attrs']['TYPE'] == 'aicc_script') { + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->prerequisites = addslashes($block['tagData']); + } + break; + case 'ADLCP:MAXTIMEALLOWED': + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->maxtimeallowed = addslashes($block['tagData']); + break; + case 'ADLCP:TIMELIMITACTION': + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->timelimitaction = addslashes($block['tagData']); + break; + case 'ADLCP:DATAFROMLMS': + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->datafromlms = addslashes($block['tagData']); + break; + case 'ADLCP:MASTERYSCORE': + $parent = array_pop($parents); + array_push($parents, $parent); + if (!isset($block['tagData'])) { + $block['tagData'] = ''; + } + $scoes->elements[$manifest][$parent->organization][$parent->identifier]->masteryscore = addslashes($block['tagData']); + break; + } + } + } + return $scoes; +} + +function scorm_parse_scorm($pkgdir,$scormid) { + global $CFG; + + $launch = 0; + $manifestfile = $pkgdir.'/imsmanifest.xml'; + + if (is_file($manifestfile)) { + + $xmlstring = file_get_contents($manifestfile); + $objXML = new xml2Array(); + $manifests = $objXML->parse($xmlstring); + // print_r($manifests); + $scoes = new stdClass(); + $scoes->version = ''; + $scoes = scorm_get_manifest($manifests,$scoes); + + if (count($scoes->elements) > 0) { + $olditems = get_records('scorm_scoes','scorm',$scormid); + foreach ($scoes->elements as $manifest => $organizations) { + foreach ($organizations as $organization => $items) { + foreach ($items as $identifier => $item) { + $newitem = new stdClass(); + $newitem->scorm = $scormid; + $newitem->manifest = $manifest; + $newitem->organization = $organization; + $standarddatas = array('parent', 'identifier', 'launch', 'scormtype', 'title'); + foreach ($standarddatas as $standarddata) { + $newitem->$standarddata = $item->$standarddata; + } + + if ($olditemid = scorm_array_search('identifier',$newitem->identifier,$olditems)) { + $newitem->id = $olditemid; + $id = update_record('scorm_scoes',$newitem); + unset($olditems[$olditemid]); + delete_records('scorm_scoes_data','scoid',$olditem->id); + } else { + $id = insert_record('scorm_scoes',$newitem); + } + + $data = new stdClass(); + $data->scormid = $scormid; + $data->scoid = $id; + $optionaldatas = scorm_optionals_data(); + foreach ($optionalsdatas as $optionaldata) { + if (isset($item->$optionaldata)) { + $data->name = $optionaldata; + $data->value = $item->$optionaldata; + $dataid = insert_record('scorm_scoes_data'); + } + } + + if (($launch == 0) && ((empty($scoes->defaultorg)) || ($scoes->defaultorg == $identifier))) { + $launch = $id; + } + } + } + } + if (!empty($olditems)) { + foreach($olditems as $olditem) { + delete_records('scorm_scoes','id',$olditem->id); + delete_records('scorm_scoes_data','scoid',$olditem->id); + delete_records('scorm_scoes_track','scoid',$olditem->id); + } + } + set_field('scorm','version',$scoes->version,'id',$scormid); + } + } + + return $launch; +} + +/* Usage + Grab some XML data, either from a file, URL, etc. however you want. Assume storage in $strYourXML; + + $objXML = new xml2Array(); + $arrOutput = $objXML->parse($strYourXML); + print_r($arrOutput); //print it out, or do whatever! + +*/ +class xml2Array { + + var $arrOutput = array(); + var $resParser; + var $strXmlData; + + /** + * Convert a utf-8 string to html entities + * + * @param string $str The UTF-8 string + * @return string + */ + function utf8_to_entities($str) { + global $CFG; + + $entities = ''; + $values = array(); + $lookingfor = 1; + + if (empty($CFG->unicodedb)) { // If Unicode DB support enable does not convert string + $textlib = textlib_get_instance(); + for ($i = 0; $i < $textlib->strlen($str,'utf-8'); $i++) { + $thisvalue = ord($str[$i]); + if ($thisvalue < 128) { + $entities .= $str[$i]; // Leave ASCII chars unchanged + } else { + if (count($values) == 0) { + $lookingfor = ($thisvalue < 224) ? 2 : 3; + } + $values[] = $thisvalue; + if (count($values) == $lookingfor) { + $number = ($lookingfor == 3) ? + (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64): + (($values[0] % 32) * 64) + ($values[1] % 64); + $entities .= '&#' . $number . ';'; + $values = array(); + $lookingfor = 1; + } + } + } + return $entities; + } else { + return $str; + } + } + + /** + * Parse an XML text string and create an array tree that rapresent the XML structure + * + * @param string $strInputXML The XML string + * @return array + */ + function parse($strInputXML) { + $this->resParser = xml_parser_create ('UTF-8'); + xml_set_object($this->resParser,$this); + xml_set_element_handler($this->resParser, "tagOpen", "tagClosed"); + + xml_set_character_data_handler($this->resParser, "tagData"); + + $this->strXmlData = xml_parse($this->resParser,$strInputXML ); + if(!$this->strXmlData) { + die(sprintf("XML error: %s at line %d", + xml_error_string(xml_get_error_code($this->resParser)), + xml_get_current_line_number($this->resParser))); + } + + xml_parser_free($this->resParser); + + return $this->arrOutput; + } + + function tagOpen($parser, $name, $attrs) { + $tag=array("name"=>$name,"attrs"=>$attrs); + array_push($this->arrOutput,$tag); + } + + function tagData($parser, $tagData) { + if(trim($tagData)) { + if(isset($this->arrOutput[count($this->arrOutput)-1]['tagData'])) { + $this->arrOutput[count($this->arrOutput)-1]['tagData'] .= $this->utf8_to_entities($tagData); + } else { + $this->arrOutput[count($this->arrOutput)-1]['tagData'] = $this->utf8_to_entities($tagData); + } + } + } + + function tagClosed($parser, $name) { + $this->arrOutput[count($this->arrOutput)-2]['children'][] = $this->arrOutput[count($this->arrOutput)-1]; + array_pop($this->arrOutput); + } + +} + +?> diff --git a/mod/scorm/datamodels/sequencinglib.php b/mod/scorm/datamodels/sequencinglib.php new file mode 100755 index 00000000000..bd0c321d96c --- /dev/null +++ b/mod/scorm/datamodels/sequencinglib.php @@ -0,0 +1,513 @@ +<?php + +// Cac ham danh cho viec thuc thi Sequencing +// --------Ket thuc cac ham danh cho viec thuc thi Sequencing ------------ + +// Cac ham danh cho viec thuc thi Rollup + +//----------------------------------------------------- +function scorm_rollup_updatestatus($scormid,$scoidchild, $userid) +{ + //$f = "D:\\test.txt"; + //@$ft = fopen($f,"a"); + //fwrite($ft,"\n >>>>> SCO goi Rollup la ".$scoidchild); + $scochild = get_record("scorm_scoes","id",$scoidchild); + $scoparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$scochild->parent); + //Danh sach cac con cua cha + $scochildren = get_records_select("scorm_scoes","scorm =".$scormid." and parent ='".$scoparent->identifier."'"); + //Lay gia tri last attempt + //fwrite($ft,"\n >>>>> Bat dau xu ly Rollup SCO cha ".$scoparent->id); + $attempt = scorm_get_last_attempt($scormid,$userid); + + if(!empty($scoparent)){ + $scoid = $scoparent->id; + $rolluprules = get_record("scorm_sequencing_rolluprules","scormid",$scormid,"scoid",$scoid); + if (!empty($rolluprules)){ + $idrolluprules = $rolluprules->id; + $rules = get_records_select('scorm_sequencing_rolluprule','scoid ='.$scoid.' and rolluprulesid ='. $idrolluprules); + + foreach ($rules as $rule){ + $ruleid = $rule->id; + $ruleConditions = get_record("scorm_sequencing_rollupruleconditions","scoid",$scoid,"rollupruleid",$ruleid); + $idruleConditions = $ruleConditions->id; + $conditions = get_records_select('scorm_sequencing_rolluprulecondition','scoid ='.$scoid.' and ruleconditionsid ='.$idruleConditions); + + //Truong hop 1: childactivitySet = all + // conditioncombination = any + if (($rule->childactivityset == 'all') && ($ruleConditions->conditioncombination=='any')){ + foreach($conditions as $condition){ + $conditionOK = false; + //Condition 1: condition = attempted operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='noOp')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + //fwrite($ft,"\n >>>>> Xu ly Rollup voi dieu kien attempt \n"); + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status != 'attempted'){ + //fwrite($ft,"\n >>>>> Co SCO con chua attempted \n"); + $conditionOK = false; + } + } + } + //Condition 2: condition = attempted operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='not')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + //fwrite($ft,"\n >>>>> Xu ly Rollup voi dieu kien not attempt \n"); + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status != 'notattempted'){ + $conditionOK = false; + } + } + } + //Condition 3: condition = satisfied operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='noOp')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->satisfied_status != 'satisfied'){ + $conditionOK = false; + } + } + } + //Condition 4: condition = satisfied operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='not')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->satisfied_status != 'notSatisfied'){ + $conditionOK = false; + } + } + } + //Condition 5: condition = completed operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='noOp')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status != 'completed'){ + $conditionOK = false; + } + } + } + //Condition 6: condition = completed operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='not')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status != 'notcompleted'){ + $conditionOK = false; + } + } + } + //Neu dieu kien van dung sau khi xem xet thi thuc hien action + if ($conditionOK == true){ + if ($ruleConditions->rollupruleaction == 'completed') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.completion_status','completed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi completed cho SCO ".$scoid); + } + if ($ruleConditions->rollupruleaction == 'satisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','passed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi satisfied\n"); + } + if ($ruleConditions->rollupruleaction == 'notSatisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','failed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi notSatisfied\n"); + } + //echo "<script language='JavaScript'>"; + //echo "alert('Thuc hien rollup. Trang thai ".$scoparent->identifier." la hoan thanh voi userid".$userid."');"; + //echo "<script>"; + + } + } + } + //Ket thuc truong hop 1 + //Truong hop 2: childactivitySet = any + // conditioncombination = any + if (($rule->childactivityset == 'any') && ($ruleConditions->conditioncombination=='any')){ + $conditionOK = false; + foreach($conditions as $condition){ + //$conditionOK = false; + //Condition 1: condition = attempted operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='noOp')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status = 'attempted'){ + $conditionOK = true; + } + } + } + //Condition 2: condition = attempted operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='not')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status = 'notattempted'){ + $conditionOK = true; + } + } + } + //Condition 3: condition = satisfied operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='noOp')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->satisfied_status = 'satisfied'){ + $conditionOK = true; + } + } + } + //Condition 4: condition = satisfied operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='not')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->satisfied_status = 'notSatisfied'){ + //fwrite($ft,"\n >>>>> Xu ly Rollup voi notSatisfied\n"); + $conditionOK = true; + } + } + } + //Condition 5: condition = completed operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='noOp')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status = 'completed'){ + $conditionOK = true; + } + } + } + //Condition 6: condition = completed operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='not')){ + $conditionOK = false; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + if ($usertrack->attempt_status = 'notcompleted'){ + $conditionOK = true; + } + } + } + //Neu dieu kien van dung sau khi xem xet thi thuc hien action + if ($conditionOK == true){ + if ($ruleConditions->rollupruleaction == 'completed') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.completion_status','completed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi completed\n"); + } + if ($ruleConditions->rollupruleaction == 'satisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','passed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi satisfied\n"); + } + if ($ruleConditions->rollupruleaction == 'notSatisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','failed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi notSatisfied\n"); + } + //echo "<script language='JavaScript'>"; + //echo "alert('Thuc hien rollup. Trang thai ".$scoparent->identifier." la hoan thanh voi userid".$userid."');"; + //echo "<script>"; + + } + } + } + //Ket thuc truong hop 2 + //Truong hop 3: childactivitySet = any + // conditioncombination = all + if (($rule->childactivityset == 'any') && ($ruleConditions->conditioncombination=='all')){ + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + $conditionOK = true; + foreach($conditions as $condition){ + //Condition 1: condition = attempted operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'attempted'){ + $conditionOK = false; + } + } + //Condition 2: condition = attempted operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notattempted'){ + $conditionOK = false; + } + } + //Condition 3: condition = satisfied operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'satisfied'){ + $conditionOK = false; + } + } + //Condition 4: condition = satisfied operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notSatisfied'){ + $conditionOK = false; + } + } + //Condition 5: condition = completed operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'completed'){ + $conditionOK = false; + } + } + //Condition 6: condition = completed operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notcompleted'){ + $conditionOK = false; + } + } + //Neu dieu kien van dung sau khi xem xet thi thuc hien action + } + } + if ($conditionOK == true){ + if ($ruleConditions->rollupruleaction == 'completed') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.completion_status','completed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi completed\n"); + } + if ($ruleConditions->rollupruleaction == 'satisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','passed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi satisfied\n"); + } + if ($ruleConditions->rollupruleaction == 'notSatisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','failed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi notSatisfied\n"); + } + //echo "<script language='JavaScript'>"; + //echo "alert('Thuc hien rollup. Trang thai ".$scoparent->identifier." la hoan thanh voi userid".$userid."');"; + //echo "<script>"; + + } + } + //Ket thuc truong hop 3 + //Truong hop 4: childactivitySet = all + // conditioncombination = all + if (($rule->childactivityset == 'all') && ($ruleConditions->conditioncombination=='all')){ + $conditionOK = true; + foreach ($scochildren as $sco){ + $usertrack = scorm_get_tracks($sco->id,$userid); + foreach($conditions as $condition){ + //Condition 1: condition = attempted operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'attempted'){ + $conditionOK = false; + } + } + //Condition 2: condition = attempted operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'attempted') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notattempted'){ + $conditionOK = false; + } + } + //Condition 3: condition = satisfied operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'satisfied'){ + $conditionOK = false; + } + } + //Condition 4: condition = satisfied operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'satisfied') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notSatisfied'){ + $conditionOK = false; + } + } + //Condition 5: condition = completed operator = 'noOp' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='noOp')){ + if ($usertrack->attempt_status != 'completed'){ + $conditionOK = false; + } + } + //Condition 6: condition = completed operator = 'not' + // Thuc hien rollupaction + if (($condition->condition == 'completed') && ($condition->operator=='not')){ + if ($usertrack->attempt_status != 'notcompleted'){ + $conditionOK = false; + } + } + //Neu dieu kien van dung sau khi xem xet thi thuc hien action + } + } + if ($conditionOK == true){ + if ($ruleConditions->rollupruleaction == 'completed') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.completion_status','completed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi completed\n"); + } + if ($ruleConditions->rollupruleaction == 'satisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','passed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi satisfied\n"); + } + if ($ruleConditions->rollupruleaction == 'notSatisfied') + { + scorm_insert_track($userid,$scormid,$scoid,$attempt,'cmi.success_status','failed'); + //fwrite($ft,"\n >>>>> Xu ly Rollup thanh cong voi notSatisfied\n"); + } + //echo "<script language='JavaScript'>"; + //echo "alert('Thuc hien rollup. Trang thai ".$scoparent->identifier." la hoan thanh voi userid".$userid."');"; + //echo "<script>"; + + } + } + //Ket thuc truong hop 4 + } + } + + //Thuc hien de qui cho Rollup voi cac muc cha + $scograndparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$scoparent->parent); + if (!empty($scograndparent)){ + //fwrite($ft,"\n >>>>> Quay lui Rollup SCO ".$scoparent->id); + scorm_rollup_updatestatus($scormid,$scoparent->id, $userid); + } + } +} + +// --------Ket thuc cac ham danh cho viec thuc thi Rollup ------- + +//---------Thuc hien sequencing rule ----------------- +function scorm_sequecingrule_implement($scormid,$scoidchild, $userid) +{ + $sequencingResult->rule = ''; //Rule co 3 truong hop exit, pre va post + $sequencingResult->action = ''; + + + $f = "D:\\test.txt"; + @$ft = fopen($f,"a"); + //fwrite($ft,"\n >>>>> Kiem tra Sequencing \n"); + + $scochild = get_record("scorm_scoes","id",$scoidchild); + $scoparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$scochild->parent); + //Danh sach cac con cua cha + $scochildren = get_records_select("scorm_scoes","scorm =".$scormid." and parent ='".$scoparent->identifier."'"); + //Lay gia tri last attempt + + $attempt = scorm_get_last_attempt($scormid,$userid); + + if(!empty($scoparent)){ + //fwrite($ft,"\n >>>>> Kiem tra Sequencing : Co Parent\n"); + + $scoid = $scoparent->id; + //Lay trang thai cua SCO cha + $usertrack = scorm_get_tracks($scoid,$userid); + //fwrite($ft,"\n >>>>> Kiem tra Sequencing : id Parent ".$scoid); + //fwrite($ft,"\n >>>>> Kiem tra Sequencing : usertrack ".$usertrack->status); + $sequencingrules = get_records_select("scorm_sequencing_ruleconditions","scormid=".$scormid." and scoid=".$scoid); + if (!empty($sequencingrules)){ + foreach($sequencingrules as $sequencingrule){ + //fwrite($ft,"\n >>>>> Kiem tra Sequencing : Co Sequencing o SCO".$sequencingrule->scoid); + + $idsequencingrule = $sequencingrule->id; + $ruleconditions = get_records_select('scorm_sequencing_rulecondition','scoid ='.$scoid.' and ruleconditionsid ='. $idsequencingrule); + + $conditionOK = true; + //Truong hop 1: conditioncombination = all + if ($sequencingrule->conditioncombination =='all'){ + //fwrite($ft,"\n >>>>> Kiem tra Sequencing :conditioncombination la all \n"); + $conditionOK = true; + //fwrite($ft,"\n >>>>> Usertrack->status la: ".$usertrack->status); + foreach ($ruleconditions as $rulecondition){ + //Neu co mot dieu kien khong thoa man thi se khong dung + if (($rulecondition->condition != $usertrack->status)&&($rulecondition->condition != $usertrack->success_status)&&($rulecondition->condition != $usertrack->satisfied_status)){ + $conditionOK = false; + } + } + } + //Truong hop 2: conditioncombination = any + if ($sequencingrule->conditioncombination =='any'){ + $conditionOK = false; + foreach ($ruleconditions as $rulecondition){ + //Neu co mot dieu kien thoa man thi se dung + if (($rulecondition->condition == $usertrack->status) || ($rulecondition->condition == $usertrack->success_status) || ($rulecondition->condition == $usertrack->satisfied_status) ){ + $conditionOK = true; + } + } + } + + //fwrite($ft,"\n >>>>> Gia tri conditionOK sau khi kiem tra dk la: ".$conditionOK); + //Neu dieu kien van dung thi thuc hien Action + if ($conditionOK == true){ + //fwrite($ft,"\n >>>>> Dieu kien Sequencing OK..Thuc hien Action \n"); + //Truong hop 1: ExitAction la Exit + if ($sequencingrule->exitconditionruleaction=='exit') + { + //fwrite($ft,"\n >>>>> Xu ly Sequencing thanh cong -- Thuc hien su kien exit \n"); + echo "<script language='JavaScript'>"; + echo "alert('Thuc hien sequen. Do Trang thai ".$scoparent->identifier." la hoan thanh. Tien hanh EXIT');"; + echo "</script>"; + $sequencingResult->rule = 'exit'; + $sequencingResult->action = 'exit'; + } + if ($sequencingrule->preconditionruleaction=='disabled') + { + //fwrite($ft,"\n >>>>> Xu ly Sequencing thanh cong -- Thuc hien su kien disable \n"); + echo "<script language='JavaScript'>"; + echo "alert('Thuc hien sequen. Do Trang thai ".$scoparent->identifier." la hoan thanh. Tien hanh Disable');"; + echo "</script>"; + $sequencingResult->rule = 'pre'; + $sequencingResult->action = 'disable'; + + } + + } + } + } + } + return $sequencingResult; +} +function get_sco_after_exit($scoid,$scormid){ + $scochild = get_record("scorm_scoes","id",$scoid); + $scoparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$scochild->parent); + $exitscoid = $scoid++; + $exitscochild = get_record("scorm_scoes","id",$exitscoid,"scorm",$scormid); + if (empty($exitscochild)){ + //Da ra ngoai vung scoid. Hay day chinh la sco cuoi cung + return 0; + } + else{ + $exitscoparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$exitscochild->parent); + //Neu chua ra khoi activity do thi tiep tuc + while ($exitscoparent->id == $scoparent->id){ + $exitscoid++; + $exitscochild = get_record("scorm_scoes","id",$exitscoid); + if (empty($exitscochild)){ + //Da ra ngoai vung scoid. Hay day chinh la sco cuoi cung + return 0; + } + else{ + $exitscoparent = get_record("scorm_scoes","scorm",$scormid,"identifier",$exitscochild->parent); + } + } + } + return $exitscoid; +} + +?> diff --git a/mod/scorm/datamodels/suspend.php b/mod/scorm/datamodels/suspend.php new file mode 100755 index 00000000000..260a98a5f31 --- /dev/null +++ b/mod/scorm/datamodels/suspend.php @@ -0,0 +1,29 @@ +<?php + require_once('../../config.php'); + require_once('locallib.php'); + + $id = required_param('id', PARAM_INT); // course ID + $scormid = required_param('scorm', PARAM_INT); // scorm ID + $scoid = required_param('sco', PARAM_INT); // suspend sco ID + $userid = required_param('userid', PARAM_INT); // user ID + + $attempt = scorm_get_last_attempt($scormid,$userid); + $statistic = get_record('scorm_statistic',"scormid",$scormid,"userid",$userid); + $statisticInput->accesstime = $statistic->accesstime; + $statisticInput->durationtime = $statistic->durationtime + time()- $statistic->accesstime; + $statisticInput->status = 'suspend'; + $statisticInput->attemptnumber = $attempt; + $statisticInput->scormid = $statistic->scormid; + $statisticInput->userid = $statistic->userid; + $statisticid = scorm_insert_statistic($statisticInput); + + $result = scorm_insert_trackmodel($userid, $scormid, $scoid,$attempt); + if ($result) { + echo "<script language='Javascript' type='text/javascript'>"; + echo "location.href='".$CFG->wwwroot." /course/view.php?id=".$id."';"; + echo "</script>"; + } else { + echo "Suspend failed"; + } +?> +