function scorm_get_toc_object($user, $scorm, $currentorg = '', $scoid = '', $mode = 'normal', $attempt = '', $play = false, $organizationsco = null) { global $CFG, $DB, $PAGE, $OUTPUT; $modestr = ''; if ($mode != 'normal') { $modestr = '&mode=' . $mode; } $result = array(); $incomplete = false; if (!empty($organizationsco)) { $result[0] = $organizationsco; $result[0]->isvisible = true; $result[0]->statusicon = ''; $result[0]->url = ''; } if ($scoes = scorm_get_scoes($scorm->id, $currentorg)) { // Retrieve user tracking data for each learning object. $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { if ($usertrack = scorm_get_tracks($sco->id, $user->id, $attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } $usertracks[$sco->identifier] = $usertrack; } } } foreach ($scoes as $sco) { if (!isset($sco->isvisible)) { $sco->isvisible = true; } if (empty($sco->title)) { $sco->title = $sco->identifier; } if (scorm_version_check($scorm->version, SCORM_13)) { $sco->prereq = true; } else { $sco->prereq = empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites, $usertracks); } if ($sco->isvisible) { if (!empty($sco->launch)) { if (empty($scoid) && $mode != 'normal') { $scoid = $sco->id; } if (isset($usertracks[$sco->identifier])) { $usertrack = $usertracks[$sco->identifier]; $strstatus = get_string($usertrack->status, 'scorm'); if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->pix_url($usertrack->status, 'scorm') . '" alt="' . $strstatus . '" title="' . $strstatus . '" />'; } else { $statusicon = '<img src="' . $OUTPUT->pix_url('asset', 'scorm') . '" alt="' . get_string('assetlaunched', 'scorm') . '" title="' . get_string('assetlaunched', 'scorm') . '" />'; } if ($usertrack->status == 'notattempted' || $usertrack->status == 'incomplete' || $usertrack->status == 'browsed') { $incomplete = true; if ($play && empty($scoid)) { $scoid = $sco->id; } } $strsuspended = get_string('suspended', 'scorm'); $exitvar = 'cmi.core.exit'; if (scorm_version_check($scorm->version, SCORM_13)) { $exitvar = 'cmi.exit'; } if ($incomplete && isset($usertrack->{$exitvar}) && $usertrack->{$exitvar} == 'suspend') { $statusicon = '<img src="' . $OUTPUT->pix_url('suspend', 'scorm') . '" alt="' . $strstatus . ' - ' . $strsuspended . '" title="' . $strstatus . ' - ' . $strsuspended . '" />'; } } else { if ($play && empty($scoid)) { $scoid = $sco->id; } $incomplete = true; if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->pix_url('notattempted', 'scorm') . '" alt="' . get_string('notattempted', 'scorm') . '" title="' . get_string('notattempted', 'scorm') . '" />'; } else { $statusicon = '<img src="' . $OUTPUT->pix_url('asset', 'scorm') . '" alt="' . get_string('asset', 'scorm') . '" title="' . get_string('asset', 'scorm') . '" />'; } } } } if (empty($statusicon)) { $sco->statusicon = '<img src="' . $OUTPUT->pix_url('notattempted', 'scorm') . '" alt="' . get_string('notattempted', 'scorm') . '" title="' . get_string('notattempted', 'scorm') . '" />'; } else { $sco->statusicon = $statusicon; } $sco->url = 'a=' . $scorm->id . '&scoid=' . $sco->id . '¤torg=' . $currentorg . $modestr . '&attempt=' . $attempt; $sco->incomplete = $incomplete; if (!in_array($sco->id, array_keys($result))) { $result[$sco->id] = $sco; } } } // Get the parent scoes! $result = scorm_get_toc_get_parent_child($result, $currentorg); // Be safe, prevent warnings from showing up while returning array if (!isset($scoid)) { $scoid = ''; } return array('scoes' => $result, 'usertracks' => $usertracks, 'scoid' => $scoid); }
/** * Test launch_sco */ public function test_launch_sco() { global $DB; // Test invalid instance id. try { mod_scorm_external::launch_sco(0); $this->fail('Exception expected due to invalid mod_scorm instance id.'); } catch (moodle_exception $e) { $this->assertEquals('invalidrecord', $e->errorcode); } // Test not-enrolled user. $user = self::getDataGenerator()->create_user(); $this->setUser($user); try { mod_scorm_external::launch_sco($this->scorm->id); $this->fail('Exception expected due to not enrolled user.'); } catch (moodle_exception $e) { $this->assertEquals('requireloginerror', $e->errorcode); } // Test user with full capabilities. $this->setUser($this->student); // Trigger and capture the event. $sink = $this->redirectEvents(); $scoes = scorm_get_scoes($this->scorm->id); foreach ($scoes as $sco) { // Find launchable SCO. if ($sco->launch != '') { break; } } $result = mod_scorm_external::launch_sco($this->scorm->id, $sco->id); $result = external_api::clean_returnvalue(mod_scorm_external::launch_sco_returns(), $result); $events = $sink->get_events(); $this->assertCount(1, $events); $event = array_shift($events); // Checking that the event contains the expected values. $this->assertInstanceOf('\\mod_scorm\\event\\sco_launched', $event); $this->assertEquals($this->context, $event->get_context()); $moodleurl = new \moodle_url('/mod/scorm/player.php', array('id' => $this->cm->id, 'scoid' => $sco->id)); $this->assertEquals($moodleurl, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); // Invalid SCO. try { mod_scorm_external::launch_sco($this->scorm->id, -1); $this->fail('Exception expected due to invalid SCO id.'); } catch (moodle_exception $e) { $this->assertEquals('cannotfindsco', $e->errorcode); } }
function scorm_get_toc($user, $scorm, $liststyle, $currentorg = '', $scoid = '', $mode = 'normal', $attempt = '', $play = false) { global $CFG, $DB, $PAGE, $OUTPUT; $strexpand = get_string('expcoll', 'scorm'); $modestr = ''; if ($mode == 'browse') { $modestr = '&mode=' . $mode; } $result = new stdClass(); $result->toc = "<ul id='s0' class='{$liststyle}'>\n"; $tocmenus = array(); $result->prerequisites = true; $incomplete = false; // // Get the current organization infos // if (!empty($currentorg)) { if (($organizationtitle = $DB->get_field('scorm_scoes', 'title', array('scorm' => $scorm->id, 'identifier' => $currentorg))) != '') { $result->toc .= "\t<li>{$organizationtitle}</li>\n"; $tocmenus[] = $organizationtitle; } } // // 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 = scorm_get_scoes($scorm->id, $currentorg)) { // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { if ($usertrack = scorm_get_tracks($sco->id, $user->id, $attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } $usertracks[$sco->identifier] = $usertrack; } } } $level = 0; $sublist = 1; $previd = 0; $nextid = 0; $findnext = false; $parents[$level] = '/'; foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = $sco->title; if (!isset($sco->isvisible) || isset($sco->isvisible) && $sco->isvisible == 'true') { $isvisible = true; } 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='s{$sublist}' class='{$liststyle}'{$style}>\n"; $level++; } else { $result->toc .= $closelist; $level = $i; } $parents[$level] = $sco->parent; } } if (isset($scoes[$pos + 1])) { $nextsco = $scoes[$pos + 1]; } else { $nextsco = false; } $nextisvisible = false; if (!isset($nextsco->isvisible) || isset($nextsco->isvisible) && $nextsco->isvisible == 'true') { $nextisvisible = true; } if ($nextisvisible && $nextsco !== false && $sco->parent != $nextsco->parent && ($level == 0 || $level > 0 && $nextsco->parent == $sco->identifier)) { $sublist++; $icon = 'minus'; if (isset($_COOKIE['hide:SCORMitem' . $nextsco->id])) { $icon = 'plus'; } $result->toc .= "\t\t" . '<li><a href="javascript:expandCollide(\'img' . $sublist . '\',\'s' . $sublist . '\',' . $nextsco->id . ');">' . '<img id="img' . $sublist . '" src="' . $OUTPUT->mod_icon_url('pix/' . $icon, 'scorm') . '" alt="' . $strexpand . '" title="' . $strexpand . '"/></a>'; } else { if ($isvisible) { $result->toc .= "\t\t" . '<li><img src="' . $OUTPUT->mod_icon_url('pix/spacer', 'scorm') . '" alt="" />'; } } if (empty($sco->title)) { $sco->title = $sco->identifier; } if (!empty($sco->launch)) { if ($isvisible) { $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'); if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->mod_icon_url('pix/' . $usertrack->status, 'scorm') . '" alt="' . $strstatus . '" title="' . $strstatus . '" />'; } else { $statusicon = '<img src="' . $OUTPUT->mod_icon_url('pix/assetc', 'scorm') . '" alt="' . get_string('assetlaunched', 'scorm') . '" title="' . get_string('assetlaunched', 'scorm') . '" />'; } 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 . ')'; } $strsuspended = get_string('suspended', 'scorm'); if (isset($usertrack->{'cmi.core.exit'}) && $usertrack->{'cmi.core.exit'} == 'suspend') { if ($usertrack->status != 'completed') { $statusicon = '<img src="' . $OUTPUT->mod_icon_url('pix/suspend', 'scorm') . '" alt="' . $strstatus . ' - ' . $strsuspended . '" title="' . $strstatus . ' - ' . $strsuspended . '" />'; } } } else { if ($play && empty($scoid)) { $scoid = $sco->id; } if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->mod_icon_url('pix/notattempted', 'scorm') . '" alt="' . get_string('notattempted', 'scorm') . '" title="' . get_string('notattempted', 'scorm') . '" />'; $incomplete = true; } else { $statusicon = '<img src="' . $OUTPUT->mod_icon_url('pix/asset', 'scorm') . '" alt="' . get_string('asset', 'scorm') . '" title="' . get_string('asset', 'scorm') . '" />'; } } if ($sco->id == $scoid) { $startbold = '<b>'; $endbold = '</b>'; $findnext = true; $shownext = isset($sco->next) ? $sco->next : 0; $showprev = isset($sco->prev) ? $sco->prev : 0; } if ($nextid == 0 && scorm_count_launchable($scorm->id, $currentorg) > 1 && $nextsco !== false && !$findnext) { if (!empty($sco->launch)) { $previd = $sco->id; } } require_once 'sequencinglib.php'; if (scorm_seq_evaluate($sco->id, $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 .= $statusicon . ' ' . $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 .= ' ' . format_string($sco->title) . "</li>\n"; } } } else { $result->toc .= ' ' . format_string($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 = $DB->get_record('scorm_scoes', array('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) { $PAGE->requires->data_for_js('scormdata', array('plusicon' => $OUTPUT->mod_icon_url('pix/plus', 'scorm'), 'minusicon' => $OUTPUT->mod_icon_url('pix/minus', 'scorm'))); $PAGE->requires->js('mod/scorm/datamodels/scorm_datamodels.js'); } $url = $CFG->wwwroot . '/mod/scorm/player.php?a=' . $scorm->id . '¤torg=' . $currentorg . $modestr; $select = html_select::make_popup_form($url, 'scoid', $tocmenus, "tocmenu", $sco->id); $select->nothinglabel = false; $result->tocmenu = $OUTPUT->select($select); return $result; }
function scorm_get_toc($user,$scorm,$cmid,$toclink=TOCJSLINK,$currentorg='',$scoid='',$mode='normal',$attempt='',$play=false, $tocheader=false) { global $CFG, $DB, $PAGE, $OUTPUT; $modestr = ''; if ($mode == 'browse') { $modestr = '&mode='.$mode; } $result = new stdClass(); if ($tocheader) { $result->toc = '<div id="scorm_layout">'; $result->toc .= '<div id="scorm_toc">'; $result->toc .= '<div id="scorm_tree">'; } $result->toc .= '<ul>'; $tocmenus = array(); $result->prerequisites = true; $incomplete = false; // // Get the current organization infos // if (!empty($currentorg)) { if (($organizationtitle = $DB->get_field('scorm_scoes','title', array('scorm'=>$scorm->id,'identifier'=>$currentorg))) != '') { if ($play) { $result->toctitle = "$organizationtitle"; } else { $result->toc .= "\t<li>$organizationtitle</li>\n"; } $tocmenus[] = $organizationtitle; } } // // If not specified retrieve the last attempt number // if (empty($attempt)) { $attempt = scorm_get_attempt_count($user->id, $scorm); } $result->attemptleft = $scorm->maxattempt == 0 ? 1 : $scorm->maxattempt - $attempt; if ($scoes = scorm_get_scoes($scorm->id, $currentorg)){ // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } $usertracks[$sco->identifier] = $usertrack; } } } $level=0; $sublist=1; $previd = 0; $nextid = 0; $findnext = false; $parents[$level]='/'; $prevsco = ''; foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = $sco->title; if (!isset($sco->isvisible) || (isset($sco->isvisible) && ($sco->isvisible == 'true'))) { $isvisible = true; } if ($parents[$level] != $sco->parent) { if ($newlevel = array_search($sco->parent,$parents)) { for ($i=0; $i<($level-$newlevel); $i++) { $result->toc .= "\t\t</li></ul></li>\n"; } $level = $newlevel; } else { $i = $level; $closelist = ''; while (($i > 0) && ($parents[$level] != $sco->parent)) { if ($i === 1 && $level > 1) { $closelist .= "\t\t</ul></li>\n"; } else { $closelist .= "\t</li></ul></li>\n"; } $i--; } if (($i == 0) && ($sco->parent != $currentorg)) { $result->toc .= "\n\t<ul>\n"; $level++; } else { $result->toc .= $closelist; $level = $i; } $parents[$level] = $sco->parent; } } if ($isvisible) { $result->toc .= "<li>"; } if (isset($scoes[$pos+1])) { $nextsco = $scoes[$pos+1]; } else { $nextsco = false; } $nextisvisible = false; if (($nextsco !== false) && (!isset($nextsco->isvisible) || (isset($nextsco->isvisible) && ($nextsco->isvisible == 'true')))) { $nextisvisible = true; } if ($nextisvisible && ($nextsco !== false) && ($sco->parent != $nextsco->parent) && (($level==0) || (($level>0) && ($nextsco->parent == $sco->identifier)))) { $sublist++; } if (empty($sco->title)) { $sco->title = $sco->identifier; } if ($isvisible) { if (!empty($sco->launch)) { $score = ''; if (empty($scoid) && ($mode != 'normal')) { $scoid = $sco->id; } if (isset($usertracks[$sco->identifier])) { $usertrack = $usertracks[$sco->identifier]; $strstatus = get_string($usertrack->status,'scorm'); if ($sco->scormtype == 'sco') { $statusicon = '<img src="'.$OUTPUT->pix_url($usertrack->status, 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" />'; } else { $statusicon = '<img src="'.$OUTPUT->pix_url('assetc', 'scorm').'" alt="'.get_string('assetlaunched','scorm').'" title="'.get_string('assetlaunched','scorm').'" />'; } if (($usertrack->status == 'notattempted') || ($usertrack->status == 'incomplete') || ($usertrack->status == 'browsed')) { $incomplete = true; if ($play && empty($scoid)) { $scoid = $sco->id; } } if ($usertrack->score_raw != '' && has_capability('mod/scorm:viewscores', get_context_instance(CONTEXT_MODULE,$cmid))) { $score = '('.get_string('score','scorm').': '.$usertrack->score_raw.')'; } $strsuspended = get_string('suspended','scorm'); $exitvar = 'cmi.core.exit'; if (scorm_version_check($scorm->version, SCORM_13)) { $exitvar = 'cmi.exit'; } if ($incomplete && isset($usertrack->{$exitvar}) && ($usertrack->{$exitvar} == 'suspend')) { $statusicon = '<img src="'.$OUTPUT->pix_url('suspend', 'scorm').'" alt="'.$strstatus.' - '.$strsuspended.'" title="'.$strstatus.' - '.$strsuspended.'" />'; } } else { if ($play && empty($scoid)) { $scoid = $sco->id; } $incomplete = true; if ($sco->scormtype == 'sco') { $statusicon = '<img src="'.$OUTPUT->pix_url('notattempted', 'scorm').'" alt="'.get_string('notattempted','scorm').'" title="'.get_string('notattempted','scorm').'" />'; } else { $statusicon = '<img src="'.$OUTPUT->pix_url('asset', 'scorm').'" alt="'.get_string('asset','scorm').'" title="'.get_string('asset','scorm').'" />'; } } if ($sco->id == $scoid) { $findnext = true; } if (($nextid == 0) && (scorm_count_launchable($scorm->id,$currentorg) > 1) && ($nextsco!==false) && (!$findnext)) { if (!empty($sco->launch)) { $previd = $sco->id; } } if (scorm_version_check($scorm->version, SCORM_13)) { require_once($CFG->dirroot.'/mod/scorm/datamodels/sequencinglib.php'); $prereq = scorm_seq_evaluate($sco->id,$usertracks); } else { //TODO: split check for sco->prerequisites only for AICC as I think that's the only case it's set. $prereq = empty($sco->prerequisites) || scorm_eval_prerequisites($sco->prerequisites,$usertracks); } if ($prereq) { if ($sco->id == $scoid) { $result->prerequisites = true; } if (!empty($prevsco) && scorm_version_check($scorm->version, SCORM_13) && !empty($prevsco->hidecontinue)) { $result->toc .= '<span>'.$statusicon.' '.format_string($sco->title).'</span>'; } else if ($toclink == TOCFULLURL) { //display toc with urls for structure page $url = $CFG->wwwroot.'/mod/scorm/player.php?a='.$scorm->id.'&currentorg='.$currentorg.$modestr.'&scoid='.$sco->id; $result->toc .= $statusicon.' <a href="'.$url.'">'.format_string($sco->title).'</a>'.$score."\n"; } else { //display toc for inside scorm player if ($sco->launch) { $link = 'a='.$scorm->id.'&scoid='.$sco->id.'¤torg='.$currentorg.$modestr.'&attempt='.$attempt; $result->toc .= '<a title="'.$link.'">'.$statusicon.' '.format_string($sco->title).' '.$score.'</a>'; } else { $result->toc .= '<span>'.$statusicon.' '.format_string($sco->title).'</span>'; } } $tocmenus[$sco->id] = scorm_repeater('−',$level) . '>' . format_string($sco->title); } else { if ($sco->id == $scoid) { $result->prerequisites = false; } if ($play) { // should be disabled $result->toc .= '<span>'.$statusicon.' '.format_string($sco->title).'</span>'; } else { $result->toc .= $statusicon.' '.format_string($sco->title)."\n"; } } } else { $result->toc .= ' '.format_string($sco->title); } if (($nextsco === false) || $nextsco->parent == $sco->parent) { $result->toc .= "</li>\n"; } } if (($nextsco !== false) && ($nextid == 0) && ($findnext)) { if (!empty($nextsco->launch)) { $nextid = $nextsco->id; } } $prevsco = $sco; } for ($i=0;$i<$level;$i++) { $result->toc .= "\t\t</ul></li>\n"; } if ($play) { // it is possible that $scoid is still not set, in this case we don't want an empty object if ($scoid) { $sco = scorm_get_sco($scoid); } $sco->previd = $previd; $sco->nextid = $nextid; $result->sco = $sco; $result->incomplete = $incomplete; } else { $result->incomplete = $incomplete; } } $result->toc .= '</ul>'; // NEW IMS TOC if ($tocheader) { $result->toc .= '</div></div></div>'; $result->toc .= '<div id="scorm_navpanel"></div>'; } $url = new moodle_url('/mod/scorm/player.php?a='.$scorm->id.'¤torg='.$currentorg.$modestr); $result->tocmenu = $OUTPUT->single_select($url, 'scoid', $tocmenus, $sco->id, null, "tocmenu"); return $result; }
/** * Retrieves user tracking and SCO data and default SCORM values * * @param int $scormid the scorm id * @param int $attempt the attempt number * @return array warnings and the scoes data * @throws moodle_exception * @since Moodle 3.0 */ public static function get_scorm_user_data($scormid, $attempt) { global $CFG, $DB; $params = self::validate_parameters(self::get_scorm_user_data_parameters(), array('scormid' => $scormid, 'attempt' => $attempt)); $data = array(); $warnings = array(); $scorm = $DB->get_record('scorm', array('id' => $params['scormid']), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('scorm', $scorm->id); $context = context_module::instance($cm->id); self::validate_context($context); scorm_require_available($scorm, true, $context); $scorm->version = strtolower(clean_param($scorm->version, PARAM_SAFEDIR)); if (!file_exists($CFG->dirroot . '/mod/scorm/datamodels/' . $scorm->version . 'lib.php')) { $scorm->version = 'scorm_12'; } require_once $CFG->dirroot . '/mod/scorm/datamodels/' . $scorm->version . 'lib.php'; if ($scoes = scorm_get_scoes($scorm->id)) { $def = new stdClass(); $user = new stdClass(); foreach ($scoes as $sco) { $def->{$sco->id} = new stdClass(); $user->{$sco->id} = new stdClass(); // We force mode normal, this can be override by the client at any time. $def->{$sco->id} = get_scorm_default($user->{$sco->id}, $scorm, $sco->id, $params['attempt'], 'normal'); $userdata = array(); $defaultdata = array(); foreach ((array) $user->{$sco->id} as $key => $val) { $userdata[] = array('element' => $key, 'value' => $val); } foreach ($def->{$sco->id} as $key => $val) { $defaultdata[] = array('element' => $key, 'value' => $val); } $data[] = array('scoid' => $sco->id, 'userdata' => $userdata, 'defaultdata' => $defaultdata); } } $result = array(); $result['data'] = $data; $result['warnings'] = $warnings; return $result; }
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='s0' class='{$liststyle}'>\n"; $tocmenus = array(); $result->prerequisites = true; $incomplete = false; // // Get the current organization infos // if (!empty($currentorg)) { if (($organizationtitle = get_field('scorm_scoes', 'title', 'scorm', $scorm->id, 'identifier', $currentorg)) != '') { $result->toc .= "\t<li>{$organizationtitle}</li>\n"; $tocmenus[] = $organizationtitle; } } // // 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 = scorm_get_scoes($scorm->id, $currentorg)) { // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { if ($usertrack = scorm_get_tracks($sco->id, $user->id, $attempt)) { if ($usertrack->status == '') { $usertrack->status = 'notattempted'; } $usertracks[$sco->identifier] = $usertrack; } } } $level = 0; $sublist = 1; $previd = 0; $nextid = 0; $findnext = false; $parents[$level] = '/'; foreach ($scoes as $pos => $sco) { $isvisible = false; $sco->title = stripslashes($sco->title); if (!isset($sco->isvisible) || isset($sco->isvisible) && $sco->isvisible == 'true') { $isvisible = true; } 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='s{$sublist}' class='{$liststyle}'{$style}>\n"; $level++; } else { $result->toc .= $closelist; $level = $i; } $parents[$level] = $sco->parent; } } if ($isvisible) { $result->toc .= "\t\t<li>"; } if (isset($scoes[$pos + 1])) { $nextsco = $scoes[$pos + 1]; } else { $nextsco = false; } $nextisvisible = false; if (!isset($nextsco->isvisible) || isset($nextsco->isvisible) && $nextsco->isvisible == 'true') { $nextisvisible = true; } if ($nextisvisible && $nextsco !== false && $sco->parent != $nextsco->parent && ($level == 0 || $level > 0 && $nextsco->parent == $sco->identifier)) { $sublist++; $icon = 'minus'; if (isset($_COOKIE['hide:SCORMitem' . $nextsco->id])) { $icon = 'plus'; } $result->toc .= '<a href="javascript:expandCollide(\'img' . $sublist . '\',\'s' . $sublist . '\',' . $nextsco->id . ');"><img id="img' . $sublist . '" src="' . $scormpixdir . '/' . $icon . '.gif" alt="' . $strexpand . '" title="' . $strexpand . '"/></a>'; } else { if ($isvisible) { $result->toc .= '<img src="' . $scormpixdir . '/spacer.gif" alt="" />'; } } if (empty($sco->title)) { $sco->title = $sco->identifier; } if (!empty($sco->launch)) { if ($isvisible) { $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'); if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $scormpixdir . '/' . $usertrack->status . '.gif" alt="' . $strstatus . '" title="' . $strstatus . '" />'; } else { $statusicon = '<img src="' . $scormpixdir . '/assetc.gif" alt="' . get_string('assetlaunched', 'scorm') . '" title="' . get_string('assetlaunched', 'scorm') . '" />'; } 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 . ')'; } $strsuspended = get_string('suspended', 'scorm'); if (isset($usertrack->{'cmi.core.exit'}) && $usertrack->{'cmi.core.exit'} == 'suspend') { $statusicon = '<img src="' . $scormpixdir . '/suspend.gif" alt="' . $strstatus . ' - ' . $strsuspended . '" title="' . $strstatus . ' - ' . $strsuspended . '" />'; } } else { if ($play && empty($scoid)) { $scoid = $sco->id; } $incomplete = true; if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $scormpixdir . '/notattempted.gif" alt="' . get_string('notattempted', 'scorm') . '" title="' . get_string('notattempted', 'scorm') . '" />'; } else { $statusicon = '<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 = isset($sco->next) ? $sco->next : 0; $showprev = isset($sco->previous) ? $sco->previous : 0; } if ($nextid == 0 && scorm_count_launchable($scorm->id, $currentorg) > 1 && $nextsco !== false && !$findnext) { 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 .= $statusicon . ' ' . $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 .= $statusicon . ' ' . format_string($sco->title) . "</li>\n"; } } } else { $result->toc .= ' ' . format_string($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 = scorm_get_sco($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 type="text/javascript"> //<![CDATA[ function expandCollide(which,list,item) { var el = document.ids ? document.ids[list] : document.getElementById ? document.getElementById(list) : document.all[list]; which = which.substring(0,(which.length)); var el2 = document.ids ? document.ids[which] : document.getElementById ? document.getElementById(which) : document.all[which]; if (el.style.display != "none") { el2.src = "' . $scormpixdir . '/plus.gif"; el.style.display=\'none\'; new cookie("hide:SCORMitem" + item, 1, 356, "/").set(); } else { el2.src = "' . $scormpixdir . '/minus.gif"; el.style.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; }
/** * Test get scorm sco tracks */ public function test_mod_scorm_get_scorm_sco_tracks() { global $DB; $this->resetAfterTest(true); // Create users. $student = self::getDataGenerator()->create_user(); $otherstudent = self::getDataGenerator()->create_user(); $teacher = self::getDataGenerator()->create_user(); // Set to the student user. self::setUser($student); // Create courses to add the modules. $course = self::getDataGenerator()->create_course(); // First scorm. $record = new stdClass(); $record->course = $course->id; $scorm = self::getDataGenerator()->create_module('scorm', $record); // Users enrolments. $studentrole = $DB->get_record('role', array('shortname' => 'student')); $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual'); $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual'); // Create attempts. $scoes = scorm_get_scoes($scorm->id); $sco = array_shift($scoes); scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); scorm_insert_track($student->id, $scorm->id, $sco->id, 1, 'cmi.core.score.raw', '80'); scorm_insert_track($student->id, $scorm->id, $sco->id, 2, 'cmi.core.lesson_status', 'completed'); $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id, 1); $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); // 7 default elements + 2 custom ones. $this->assertCount(9, $result['data']['tracks']); $this->assertEquals(1, $result['data']['attempt']); $this->assertCount(0, $result['warnings']); // Find our tracking data. $found = 0; foreach ($result['data']['tracks'] as $userdata) { if ($userdata['element'] == 'cmi.core.lesson_status' and $userdata['value'] == 'completed') { $found++; } if ($userdata['element'] == 'cmi.core.score.raw' and $userdata['value'] == '80') { $found++; } } $this->assertEquals(2, $found); // Try invalid attempt. $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id, 10); $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); $this->assertCount(0, $result['data']['tracks']); $this->assertEquals(10, $result['data']['attempt']); $this->assertCount(1, $result['warnings']); $this->assertEquals('notattempted', $result['warnings'][0]['warningcode']); // Capabilities check. try { mod_scorm_external::get_scorm_sco_tracks($sco->id, $otherstudent->id); $this->fail('Exception expected due to invalid instance id.'); } catch (required_capability_exception $e) { $this->assertEquals('nopermissions', $e->errorcode); } self::setUser($teacher); // Ommit the attempt parameter, the function should calculate the last attempt. $result = mod_scorm_external::get_scorm_sco_tracks($sco->id, $student->id); $result = external_api::clean_returnvalue(mod_scorm_external::get_scorm_sco_tracks_returns(), $result); // 7 default elements + 1 custom one. $this->assertCount(8, $result['data']['tracks']); $this->assertEquals(2, $result['data']['attempt']); // Test invalid instance id. try { mod_scorm_external::get_scorm_sco_tracks(0, 1); $this->fail('Exception expected due to invalid instance id.'); } catch (moodle_exception $e) { $this->assertEquals('cannotfindsco', $e->errorcode); } // Invalid user. try { mod_scorm_external::get_scorm_sco_tracks($sco->id, 0); $this->fail('Exception expected due to invalid instance id.'); } catch (moodle_exception $e) { $this->assertEquals('invaliduser', $e->errorcode); } }
function scorm_get_toc($user, $scorm, $liststyle, $currentorg = '', $scoid = '', $mode = 'normal', $attempt = '', $play = false, $tocheader = false) { global $CFG, $DB, $PAGE, $OUTPUT; $strexpand = get_string('expcoll', 'scorm'); $modestr = ''; if ($mode == 'browse') { $modestr = '&mode=' . $mode; } $result = new stdClass(); //$result->toc = "<ul id='s0' class='$liststyle'>\n"; if ($tocheader) { $result->toc = '<div id="scorm_layout">'; $result->toc .= '<div id="scorm_toc">'; $result->toc .= '<div id="scorm_tree">'; } $result->toc .= '<ul>'; $tocmenus = array(); $result->prerequisites = true; $incomplete = false; // // Get the current organization infos // if (!empty($currentorg)) { if (($organizationtitle = $DB->get_field('scorm_scoes', 'title', array('scorm' => $scorm->id, 'identifier' => $currentorg))) != '') { if ($play) { $result->toctitle = "{$organizationtitle}"; } else { $result->toc .= "\t<li>{$organizationtitle}</li>\n"; } $tocmenus[] = $organizationtitle; } } // // 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 = scorm_get_scoes($scorm->id, $currentorg)) { // // Retrieve user tracking data for each learning object // $usertracks = array(); foreach ($scoes as $sco) { if (!empty($sco->launch)) { if (empty($scoid)) { $scoid = $sco->id; } 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 $pos => $sco) { $isvisible = false; $sco->title = $sco->title; if (!isset($sco->isvisible) || isset($sco->isvisible) && $sco->isvisible == 'true') { $isvisible = true; } if ($parents[$level] != $sco->parent) { if ($newlevel = array_search($sco->parent, $parents)) { for ($i = 0; $i < $level - $newlevel; $i++) { $result->toc .= "\t\t</li></ul></li>\n"; } $level = $newlevel; } else { $i = $level; $closelist = ''; while ($i > 0 && $parents[$level] != $sco->parent) { $closelist .= "\t\t</li></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='s$sublist' class='$liststyle'$style>\n"; $result->toc .= "\t\t<ul>\n"; $level++; } else { $result->toc .= $closelist; $level = $i; } $parents[$level] = $sco->parent; } } if ($isvisible) { $result->toc .= "\t\t<li>"; } if (isset($scoes[$pos + 1])) { $nextsco = $scoes[$pos + 1]; } else { $nextsco = false; } $nextisvisible = false; if (!isset($nextsco->isvisible) || isset($nextsco->isvisible) && $nextsco->isvisible == 'true') { $nextisvisible = true; } if ($nextisvisible && $nextsco !== false && $sco->parent != $nextsco->parent && ($level == 0 || $level > 0 && $nextsco->parent == $sco->identifier)) { $sublist++; $icon = 'minus'; if (isset($_COOKIE['hide:SCORMitem' . $nextsco->id])) { $icon = 'plus'; } //$result->toc .= '<a href="javascript:expandCollide(\'img'.$sublist.'\',\'s'.$sublist.'\','.$nextsco->id.');"><img id="img'.$sublist.'" src="'.$OUTPUT->pix_url($icon, 'scorm').'" alt="'.$strexpand.'" title="'.$strexpand.'"/></a>'; } else { if ($isvisible) { //$result->toc .= '<img src="'.$OUTPUT->pix_url('spacer', 'scorm').'" alt="" />'; } } if (empty($sco->title)) { $sco->title = $sco->identifier; } if (!empty($sco->launch)) { if ($isvisible) { $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'); if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->pix_url($usertrack->status, 'scorm') . '" alt="' . $strstatus . '" title="' . $strstatus . '" />'; } else { $statusicon = '<img src="' . $OUTPUT->pix_url('assetc', 'scorm') . '" alt="' . get_string('assetlaunched', 'scorm') . '" title="' . get_string('assetlaunched', 'scorm') . '" />'; } 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 . ')'; } $strsuspended = get_string('suspended', 'scorm'); if ($incomplete && isset($usertrack->{'cmi.core.exit'}) && $usertrack->{'cmi.core.exit'} == 'suspend') { $statusicon = '<img src="' . $OUTPUT->pix_url('suspend', 'scorm') . '" alt="' . $strstatus . ' - ' . $strsuspended . '" title="' . $strstatus . ' - ' . $strsuspended . '" />'; } } else { if ($play && empty($scoid)) { $scoid = $sco->id; } $incomplete = true; if ($sco->scormtype == 'sco') { $statusicon = '<img src="' . $OUTPUT->pix_url('notattempted', 'scorm') . '" alt="' . get_string('notattempted', 'scorm') . '" title="' . get_string('notattempted', 'scorm') . '" />'; } else { $statusicon = '<img src="' . $OUTPUT->pix_url('asset', 'scorm') . '" alt="' . get_string('asset', 'scorm') . '" title="' . get_string('asset', 'scorm') . '" />'; } } if ($sco->id == $scoid) { $startbold = '<b>'; $endbold = '</b>'; $findnext = true; $shownext = isset($sco->next) ? $sco->next : 0; $showprev = isset($sco->previous) ? $sco->previous : 0; } if ($nextid == 0 && scorm_count_launchable($scorm->id, $currentorg) > 1 && $nextsco !== false && !$findnext) { 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; $thisscoidstr = '&scoid=' . $sco->id; //$link = $CFG->wwwroot.'/mod/scorm/loadSCO.php?a='.$scorm->id.$thisscoidstr.$modestr; $link = 'a=' . $scorm->id . $thisscoidstr . '¤torg=' . $currentorg . $modestr . '&attempt=' . $attempt; //$result->toc .= $statusicon.' '.$startbold.'<a href="'.$url.'">'.format_string($sco->title).'</a>'.$score.$endbold."</li>\n"; //$result->toc .= '<a title="'.$link.'">'.$statusicon.' '.format_string($sco->title).' '.$score.'</a>'; if ($sco->launch) { $result->toc .= '<a title="' . $link . '">' . $statusicon . ' ' . format_string($sco->title) . ' ' . $score . '</a>'; } else { $result->toc .= '<span>' . $statusicon . ' ' . format_string($sco->title) . '</span>'; } $tocmenus[$sco->id] = scorm_repeater('−', $level) . '>' . format_string($sco->title); } else { if ($sco->id == $scoid) { $result->prerequisites = false; } if ($play) { // should be disabled $result->toc .= '<span>' . $statusicon . ' ' . format_string($sco->title) . '</span>'; } else { $result->toc .= $statusicon . ' ' . format_string($sco->title) . "\n"; } } if ($nextsco === false || $nextsco->parent == $sco->parent) { $result->toc .= '</li>'; } } } else { $result->toc .= ' ' . format_string($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 = scorm_get_sco($scoid); $sco->previd = $previd; $sco->nextid = $nextid; $result->sco = $sco; $result->incomplete = $incomplete; } else { $result->incomplete = $incomplete; } } $result->toc .= '</ul>'; // NEW IMS TOC if ($tocheader) { $result->toc .= '</div></div></div>'; $result->toc .= '<div id="scorm_navpanel"></div>'; } if ($scorm->hidetoc == 0) { $result->toc .= html_writer::script(js_writer::set_variable('scormdata', array('plusicon' => $OUTPUT->pix_url('plus', 'scorm'), 'minusicon' => $OUTPUT->pix_url('minus', 'scorm')))); $result->toc .= html_writer::script('', $CFG->wwwroot . '/lib/cookies.js'); $result->toc .= html_writer::script('', $CFG->wwwroot . '/mod/scorm/datamodels/scorm_datamodels.js'); } $url = new moodle_url('/mod/scorm/player.php?a=' . $scorm->id . '¤torg=' . $currentorg . $modestr); $result->tocmenu = $OUTPUT->single_select($url, 'scoid', $tocmenus, $sco->id, null, "tocmenu"); return $result; }