You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
782 lines
31 KiB
782 lines
31 KiB
<?php //$id: $
|
|
/*
|
|
----------------------------------------------------------------------
|
|
Dokeos - elearning and course management software
|
|
|
|
Copyright (c) 2004 Dokeos S.A.
|
|
Copyright (c) Denes Nagy (darkden@freemail.hu)
|
|
|
|
For a full list of contributors, see "credits.txt".
|
|
The full license can be read in "license.txt".
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
Contact: Dokeos, 181 rue Royale, B-1000 Brussels, Belgium, info@dokeos.com
|
|
----------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
==============================================================================
|
|
* Show the table of contents of a scorm-based tutorial
|
|
*
|
|
* This script is a double-pass script. When called, it initialises stuff
|
|
* (generally depending on $menu or $openfirst parameters), then calls
|
|
* the opensco.php script which opens the content in another frame and
|
|
* calls this script back without any action parameter.
|
|
* @author Denes Nagy <darkden@freemail.hu>
|
|
* @author Yannick Warnier <yannick.warnier@dokeos.com>
|
|
* @access public
|
|
*
|
|
* @package dokeos.scorm
|
|
*
|
|
==============================================================================
|
|
*/
|
|
|
|
//Initialisation includes
|
|
$langFile = "scorm";
|
|
require('../inc/global.inc.php');
|
|
$this_section=SECTION_COURSES;
|
|
|
|
include_once(api_get_path(LIBRARY_PATH).'database.lib.php');
|
|
include('XMLencode.php');
|
|
include('scormparsing.lib.php');
|
|
|
|
//error_log($_SERVER['REQUEST_URI']."---menu=".$_POST['menu'],0);
|
|
//error_log("---Script started. s_identifier is now:".$_SESSION['s_identifier']." and old s_identifier is now:".$_SESSION['old_sco_identifier'],0);
|
|
|
|
//Parameters reception
|
|
$edoceo = $_GET['edoceo'];
|
|
$openDir = $_REQUEST['openDir'];
|
|
$menu = $_REQUEST['menu'];
|
|
$openfirst = $_REQUEST['openfirst'];
|
|
//$request_file must be like this : http://localhost/root/Dokeos/Scorm_content1/imsmanifest.xml
|
|
//with SERVER_NAME (=127.0.0.1) it does not work
|
|
$request_file = $_REQUEST['file'];
|
|
// Check if the requested file is in the right location (scorm folder in course directory and no .. in the path)
|
|
$file_path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/scorm';
|
|
if(substr($request_file,0,strlen($file_path)) != $file_path || strpos($request_file,'..') > 0)
|
|
{
|
|
api_not_allowed();
|
|
}
|
|
//$s_identifier = $_SESSION['s_identifier'];
|
|
$_uid = $_SESSION['_uid'];
|
|
|
|
//Charset settings (very important for imported contents)
|
|
$charset = GetXMLEncode($_GET['file']);
|
|
header('Content-Type: text/html; charset='. $charset);
|
|
//The following charset describes the encoding of most language files
|
|
//For further enhancements and in order to ensure internationalisation, we
|
|
//should make every language file UTF-8, or adapt get_lang to be able to sort
|
|
//out the file encoding and use htmlentities if required.
|
|
//If using unicode characters (courses in non-european alphabets), change this
|
|
//to $charset_lang = 'UTF-8';
|
|
$charset_lang = 'ISO-8859-15';
|
|
|
|
//Latest language inits
|
|
$array_status=array(
|
|
'completed' => get_lang('ScormCompstatus'),
|
|
'passed' => get_lang('ScormPassed'),
|
|
'failed' => get_lang('ScormFailed'),
|
|
'incomplete' => get_lang('ScormIncomplete'),
|
|
'not attempted' => get_lang('ScormNotAttempted')
|
|
);
|
|
|
|
|
|
//Database table names init
|
|
$TBL_SCORM_MAIN = Database :: get_scorm_table(SCORM_MAIN_TABLE);
|
|
$TBL_SCORM_SCO_DATA = Database::get_scorm_sco_data_table();
|
|
|
|
//if the last sco visited hasn't been closed, mark it complete:
|
|
if(!empty($_SESSION['last_sco_closed']) && !empty($_SESSION['old_sco_identifier'])){
|
|
if($_SESSION['last_sco_closed'] != $_SESSION['old_sco_identifier']){
|
|
$sql = "SELECT status FROM $TBL_SCORM_SCO_DATA
|
|
WHERE contentId='".$_SESSION['contentId']."'
|
|
and scoIdentifier='".$_SESSION['old_sco_identifier']."'
|
|
and studentId='".$_SESSION['_uid']."'";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
if(mysql_num_rows($result)==1){
|
|
$my_row = mysql_fetch_row($result);
|
|
$my_status=$my_row[0];
|
|
if($my_status == 'not attempted'){
|
|
$sql = "UPDATE $TBL_SCORM_SCO_DATA
|
|
SET score='0', status='completed', time='00:00'
|
|
WHERE (studentId='".$__SESSION['_uid']."'
|
|
and scoIdentifier='".$_SESSION['old_sco_identifier']."'
|
|
and contentId='".$_SESSION['contentId']."')";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
$_SESSION['last_sco_closed'] = $_SESSION['old_sco_identifier'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Backup latest SCO-identifier opened (to use as reference to the last element seen in closesco.php)
|
|
//This operation needs only to be done when the user takes an action, not on automatic reloading
|
|
//of the script. This is checked with the $menu parameter (set on user actions)
|
|
|
|
$_SESSION['old_sco_identifier'] = $_SESSION['s_identifier'];
|
|
|
|
//Special condition to avoid buggy database refresh in case of restart (see closesco.php)
|
|
if($_SESSION['just_started']==true){
|
|
//error_log("Content was just started - prevent saving last element",0);
|
|
$_SESSION['dont_save_last']=true;
|
|
$_SESSION['just_started']=false;
|
|
}else{
|
|
//error_log("Content was not just started - allow saving last element",0);
|
|
$_SESSION['dont_save_last']=false;
|
|
}
|
|
|
|
|
|
//Ensure the items array (TOC) and other session variables are really erased on first visit or on restart
|
|
if ($openfirst == 'yes' or $menu == 'restart'){
|
|
//error_log("Openfirst or restart was set (unset session[s_identifier] and session[old_sco_identifier]",0);
|
|
//api_session_unregister('items');
|
|
$_SESSION['items'] = array();
|
|
$items = array();
|
|
$_SESSION['items_dictionary'] = array();
|
|
$items_dictionary = array();
|
|
$_SESSION['defaultorgtitle'] = '';
|
|
$defaultorgtitle = '';
|
|
$_SESSION['contentId'] = '';
|
|
if($menu == 'restart'){
|
|
$_SESSION['just_started']=true;
|
|
}
|
|
unset($_SESSION['s_identifier']);
|
|
unset($_SESSION['sco_identifier']);
|
|
unset($_SESSION['old_sco_identifier']);
|
|
}else{
|
|
//error_log("Openfirst or restart was NOT set",0);
|
|
$items = $_SESSION['items'];
|
|
$defaultorgtitle = $_SESSION['defaultorgtitle'];
|
|
if(!empty($menu)){
|
|
$_SESSION['just_started']=false;
|
|
}
|
|
}
|
|
|
|
//HTML start
|
|
|
|
$output = '<html>
|
|
<head>
|
|
<link rel="stylesheet" type="text/css" href="../css/scorm.css">
|
|
</head>
|
|
<body>';
|
|
|
|
//incoming variables :
|
|
//- $request_file
|
|
//- $tabledraw -> if true, then the program draws a help table for showing imsmanifest.xml tags (technical)
|
|
|
|
|
|
// More init (display tweaks)
|
|
$tabledraw=false; //if true, then technical information is shown
|
|
$wrap=true; //if false then the toc is not in a table, if true, then it is
|
|
$tablewidth=255; //this is the width of the content tables
|
|
|
|
$defaultorgref='';
|
|
$version='0.0';
|
|
$inorg=false; //whether we are inside an organisation or not
|
|
$intitle=$inmeta=$ingeneral=false;
|
|
$inversion=false;
|
|
$prereq=false;
|
|
$initem=0; //how deep we are in the hieracrchy of itmes
|
|
$itemindex=0;
|
|
$previouslevel=1;
|
|
$clusterinfo=0;
|
|
|
|
$exit_after_menu = false; //used to disable printing of javascript redirections in output.
|
|
|
|
/**
|
|
* This function displays a message in the message frame.
|
|
*
|
|
* For some reason (apparently JavaScript reasons), the message shown
|
|
* cannot be more than 66 characters. Together with the enveloppe, it
|
|
* gets to something around 250 characters, which apparently is too
|
|
* big for the "write" method
|
|
* @param string The message to show. Should be smaller than 66 characters.
|
|
* @return void Doesn't return anything, just writes to the message frame.
|
|
*/
|
|
function message($text) {
|
|
//ugly little patch for text encoding of accentuated characters - see other comments on 'accentuated' characters
|
|
// and improvements to this patch below in this file
|
|
#global $charset_lang;
|
|
#$text = htmlentities($text,ENT_QUOTES,$charset_lang);
|
|
$string = "<script type='text/javascript'>\n/* <![CDATA[ */\n".
|
|
"zwindow=open('','message');".
|
|
"s='<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"../css/scorm.css\"></head><body>".
|
|
"<div class=\"message\">$text</div></body></html>';".
|
|
"z=zwindow.document;".
|
|
"z.write(s);".
|
|
"z.close();".
|
|
"\n/* ]]> */\n</script>";
|
|
return $string;
|
|
}
|
|
|
|
|
|
|
|
/****************************************** PROCESSING **********************************/
|
|
//Real processing starts here
|
|
|
|
//1. Check whether we need to build the TOC ($items array) or if it already exists
|
|
if($openfirst=='yes' or $menu=='restart'){
|
|
//error_log("Openfirst or restart was set (generate $items)",0);
|
|
|
|
/*====================
|
|
PARSING THE XML FILE
|
|
====================*/
|
|
//1.1
|
|
if (!(list($xml_parser, $fp) = new_xml_parser($request_file))) {
|
|
die("<font color='red'>Error : could not open XML input - $request_file</font>");
|
|
}
|
|
|
|
//my variables : $xml_parser, new_xml_parser()
|
|
|
|
if ($tabledraw) {
|
|
$output .= "<table border='0'>"
|
|
."<tr>"
|
|
."<td>Row</td>"
|
|
."<td>Inorg</td>"
|
|
."<td>Initem</td>"
|
|
."<td>Tag name</td>"
|
|
."<td>Attributes</td>"
|
|
."</tr>";
|
|
}
|
|
|
|
while ($data = fread($fp, 4096)) { //reads the file in 4096 byte amounts
|
|
if (!xml_parse($xml_parser, $data, feof($fp))) {
|
|
die(sprintf("XML error: %s at line %d\n",
|
|
xml_error_string(xml_get_error_code($xml_parser)),
|
|
xml_get_current_line_number($xml_parser)));
|
|
}
|
|
}
|
|
if ($tabledraw) {
|
|
$output .= "</table>";
|
|
$output .= "parse complete<br /><hr />";
|
|
$output .= "<br />Total lines in xml file : ";
|
|
$output .= xml_get_current_line_number($xml_parser);
|
|
$output .= " (xml_get_current_line_number)";
|
|
$output .= "<br /><br />";
|
|
}
|
|
|
|
//build an index of items (item identifier => item index in $items). This should improve parsing speed.
|
|
foreach($items as $key => $content){
|
|
$items_dictionary[$content['identifier']] = $key;
|
|
}
|
|
$_SESSION['items_dictionary'] = $items_dictionary;
|
|
|
|
//As we asked to start from the beginning, set the current identifier to the first elem's
|
|
$i=1;
|
|
for(;$items[$i]['href']=='';$i++){
|
|
/*do nothing (get the index of the first item of href<>'')*/
|
|
}
|
|
$_SESSION['s_identifier'] = $items[$i]['identifier'];
|
|
$href=api_get_path('WEB_COURSE_PATH').$_course['path']."/scorm".$openDir."/".$items[$i]['href'];
|
|
//error_log("New s_identifier calculated: ".$_SESSION['s_identifier']." - Old one is: ".$_SESSION['old_sco_identifier'],0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//2. Compute the maximum depth level ot the TOC.
|
|
// For fixed width : width=$tablewidth (in two tables) and enable nbsp substitution in two places
|
|
// $items is a result of the imsmanifest.xml parsing
|
|
$maxlevel=1;
|
|
$i=1;
|
|
|
|
while ($items[$i]) {
|
|
if (($items[$i]['level'])>$maxlevel) { $maxlevel=$items[$i]['level']; }
|
|
$i++;
|
|
}
|
|
|
|
|
|
|
|
//3. Check if the content was ever opened or not,
|
|
// If not, add a line to scorm_main with the title and course code to record its existence
|
|
$sql = "SELECT dokeosCourse FROM $TBL_SCORM_MAIN WHERE "
|
|
." (contentTitle='".$openDir."' and dokeosCourse='".$_course['official_code']."')";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
$numrows = mysql_num_rows($result);
|
|
|
|
if ($numrows==0) { //this means that this Scorm content was never opened by anyone in this dokeos course
|
|
//$result = api_sql_query("SELECT MAX(contentId) FROM $TBL_SCORM_MAIN");
|
|
//$ar = mysql_fetch_array($result);
|
|
//$maxcontentId = $ar['MAX(contentId)']+1;
|
|
$sql = "INSERT INTO $TBL_SCORM_MAIN (contentTitle, dokeosCourse) VALUES "
|
|
." ('".$openDir."','".$_course['official_code']."')";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
}
|
|
|
|
//4. Get the contentId for this content (whether it has just been inserted or not) to check if the
|
|
// current user ever opened it.
|
|
$sql = "SELECT contentId FROM $TBL_SCORM_MAIN "
|
|
." WHERE (contentTitle='".$openDir."' and dokeosCourse='".$_course['official_code']."')";
|
|
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
$ar = mysql_fetch_array($result);
|
|
$mycontentId = $ar['contentId']; //it has to exist, because the previous step checked it existed and if not inserted it
|
|
$_SESSION['contentId'] = $mycontentId;
|
|
|
|
$everopened = true;
|
|
$numrows2 = 0;
|
|
$result2='';
|
|
$sql2 = "SELECT * FROM $TBL_SCORM_SCO_DATA WHERE (studentId='$_uid' and contentId='$mycontentId')";
|
|
$result2 = api_sql_query($sql2,__FILE__,__LINE__);
|
|
$numrows2 = mysql_num_rows($result2);
|
|
|
|
if ($numrows2 == 0) { //this means that this Scorm content was not opened by the current student
|
|
$everopened = false;
|
|
}
|
|
|
|
|
|
/*if($openfirst=='yes' or $menu='restart'){
|
|
$s_identifier = $items[1]['identifier'];
|
|
$href=api_get_path('WEB_COURSE_PATH').$_course['path']."/scorm".$openDir."/".$items[1]['href'];
|
|
$output .= "<script type='text/javascript'>",
|
|
"nextwindow=open('opensco.php?sco_href=$href&sco_identifier=$s_identifier&file=$request_file&edoceo=$edoceo&openDir=$openDir','message');",
|
|
"</script>";
|
|
}*/
|
|
|
|
|
|
|
|
/*=========================
|
|
IF RESTART WAS CLICKED...
|
|
=========================*/
|
|
//5 If the user clicked on "restart", the $items array has been cleaned
|
|
// and no element has any special mark whatsoever, except the first one which is highlighted
|
|
if ( $menu == 'restart' ) { //Restrart clicked
|
|
//error_log("Restart was set...",0);
|
|
if ( $version == '1.1' ) {
|
|
//5.1.a Ignore if version of SCORM is <1.2 (or that's basically what is intended)
|
|
//display warning message in message frame (side-menu bottom)
|
|
$output .= message(htmlentities(get_lang('ScormNoStatus'),ENT_QUOTE,$charset_lang));
|
|
} else {
|
|
//error_log("Update database for restart",0);
|
|
//5.1.b.1 Loads a blank page in the "sco" frame, probably to enable scorm API reinit
|
|
//$output .= "<script type='text/javascript'>
|
|
// xwindow=open('about:blank','sco');
|
|
// </script>";
|
|
|
|
//5.1.b.2 update the status for the current user and content in the SCORM data table
|
|
$sql = "UPDATE $TBL_SCORM_SCO_DATA SET score='0', status='not attempted', time='00:00' "
|
|
." WHERE (studentId='$_uid' and contentId='$mycontentId')";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
//5.1.b.3 clean session vars about this content
|
|
api_session_unregister('s_href');
|
|
$i=1;
|
|
for(;$items[$i]['href']=='';$i++){/*do nothing, just get the index of first elem with href*/}
|
|
$_SESSION['s_identifier'] = $items[$i]['identifier'];
|
|
//error_log("New s_identifier generated: ".$_SESSION['s_identifier'],0);
|
|
//api_session_unregister('s_identifier');
|
|
//5.1.b.4 display info message in message frame (side-menu bottom)
|
|
//$output .=message("<img src=\'../img/restart.jpg\' alt='restart'/>".get_lang('ScormRestarted'));//the img tag fails because of the htmlentities() call in message()
|
|
//$output .=message(htmlentities(get_lang('ScormRestarted'),ENT_QUOTES,$charset_lang));
|
|
}
|
|
//that's all for the special "restart" treatment, although it includes loading the scorm document - see rest of script
|
|
|
|
}elseif (($menu=='prev') or ($menu=='next')) {
|
|
//error_log("Menu was 'prev' or 'next'",0);
|
|
|
|
/*==============================
|
|
IF PREV OR NEXT WAS CLICKED...
|
|
==============================*/
|
|
//5.2.15 If 'previous' or 'next' were clicked, then we might need to reload this script in order
|
|
// to gain benefits of the database updates before we display the TOC
|
|
|
|
if ($version=='1.3') {
|
|
$output .=message('Sequencing for 1.3 not yet available');
|
|
}
|
|
else
|
|
{
|
|
// if the current identifier is empty, set to first item identifier
|
|
if (empty($_SESSION['s_identifier'])) {
|
|
$i=1;
|
|
for(;$items[$i]['href']=='';$i++){/*get index of first elem with href*/}
|
|
$_SESSION['s_identifier']=$items[$i]['identifier'];
|
|
//error_log("s_identifier was empty - new one: ".$_SESSION['s_identifier'],0);
|
|
|
|
}
|
|
|
|
$i=1;
|
|
if($openfirst == 'yes'){
|
|
//do nothing so that we start on first element
|
|
//$s_identifier = $items[1]['identifier'];
|
|
//error_log("Openfirst was set, found first valid elem: ".$items[$i]['identifier'],0);
|
|
for(;$items[$i]['href']=='';$i++){/*get index of first elem with href*/}
|
|
}else{
|
|
$i = $items_dictionary[$_SESSION['s_identifier']];
|
|
|
|
if ($menu=='next') {
|
|
//error_log("Menu was 'next'",0);
|
|
|
|
do { // we take the next sco which has a href
|
|
$i++;
|
|
} while (($items[$i]['href'] == '') and ($i <= count($items)));
|
|
if ($i>count($items)) {
|
|
$output .=message(htmlentities(get_lang('ScormNoNext'),ENT_QUOTES,$charset_lang));
|
|
#echo $output;
|
|
$exit_after_menu=true;
|
|
}
|
|
}
|
|
if ($menu=='prev') {
|
|
//error_log("Menu was 'prev'",0);
|
|
do { // we take the previous sco which has href
|
|
$i--;
|
|
} while (($items[$i]['href'] == '') and ($i >= 1));
|
|
if ($i<1) {
|
|
$output .=message(htmlentities(get_lang('ScormNoPrev'),ENT_QUOTES,$charset_lang));
|
|
#echo $output;
|
|
$exit_after_menu=true;
|
|
}
|
|
}
|
|
}
|
|
//sco opening begin
|
|
$href=api_get_path('WEB_COURSE_PATH').$_course['path']."/scorm".$openDir."/".$items[$i]['href'];
|
|
$_SESSION['s_identifier'] = $items[$i]['identifier'];
|
|
//error_log("New s_identifier: ".$_SESSION['s_identifier']." - Old one is: ".$_SESSION['old_sco_identifier'],0);
|
|
//$s_identifier = $identifier;
|
|
}
|
|
}elseif ($menu=='my_status') {
|
|
//error_log("Menu was 'my_status'",0);
|
|
|
|
/*===========================
|
|
IF MY STATUS WAS CLICKED...
|
|
===========================*/
|
|
//6. The user chose to display the status screen.
|
|
// @todo It is a very bad idea to mix status display and the rest of the TOC display.
|
|
// Ideally, this should be fixed by including here two different display scripts depending on what to display.
|
|
|
|
//allows to not highlight an element if currently displaying status
|
|
$_SESSION['displaying_status']=true;
|
|
|
|
if ($version=='1.1') {
|
|
$output .=message(htmlentities(get_lang('ScormNoStatus'),ENT_QUOTES,$charset_lang));
|
|
} else {
|
|
$w=$tablewidth-20;
|
|
$output .= "<br />";
|
|
|
|
//if display in fullscreen required
|
|
if (strcmp($_GET["fs"],"true")==0)
|
|
{ $output .= "<table align='center'>"; }
|
|
|
|
else
|
|
{ $output .= "<table class='margin_table'>"; }
|
|
|
|
$output .="<tr><td><div class='title'>".htmlentities(get_lang('ScormMystatus'),ENT_QUOTES,$charset_lang)."</div></td></tr>"
|
|
."<tr><td> </td></tr>"
|
|
."<tr><td>"
|
|
."<table border='0' class='data_table'><tr>\n"
|
|
."<td><div class='mystatusfirstrow'>".htmlentities(get_lang('ScormLessonTitle'),ENT_QUOTES,$charset_lang)."</div></td>\n"
|
|
."<td><div class='mystatusfirstrow'>".htmlentities(get_lang('ScormStatus'),ENT_QUOTES,$charset_lang)."</div></td>\n"
|
|
."<td><div class='mystatusfirstrow'>".htmlentities(get_lang('ScormScore'),ENT_QUOTES,$charset_lang)."</div></td>\n"
|
|
."<td><div class='mystatusfirstrow'>".htmlentities(get_lang('ScormTime'),ENT_QUOTES,$charset_lang)."</div></td></tr>\n";
|
|
|
|
//going through the items using the $items[] array instead of the database order ensures
|
|
// we get them in the same order as in the imsmanifest file, which is rather random when using
|
|
// the database table
|
|
foreach($items as $i => $myitem ) {
|
|
$sql="SELECT * FROM $TBL_SCORM_SCO_DATA
|
|
WHERE (studentId='$_uid'
|
|
AND contentId='".$mycontentId."'
|
|
AND scoIdentifier = '".$myitem['identifier']."')";// order by scoId";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
$ar = mysql_fetch_array($result);
|
|
|
|
$counter++;
|
|
if (($counter % 2)==0) { $oddclass="row_odd"; } else { $oddclass="row_even"; }
|
|
|
|
$lesson_status=$ar['status'];
|
|
$score=$ar['score'];
|
|
$time=$ar['time'];
|
|
$scoIdentifier=$ar['scoIdentifier'];
|
|
$title=$myitem['title'];
|
|
if (strlen($title)>65) {
|
|
$title=substr($title,0,64);
|
|
$title.="...";
|
|
}
|
|
//$title=str_replace(' ',' ',$title);
|
|
//Remove "NaN" if any (@todo: locate the source of these NaN)
|
|
$time = str_replace('NaN','00',$time);
|
|
if (($lesson_status=='completed') or ($lesson_status=='passed')) { $color='green'; } else { $color='black'; }
|
|
$output .= "<tr class='$oddclass'>\n"
|
|
."<td><div class='mystatus'>$title</div></td>\n"
|
|
."<td><font color='$color'><div class='mystatus'>".htmlentities($array_status[$lesson_status],ENT_QUOTES,$charset_lang)."</div></font></td>\n"
|
|
."<td><div class='mystatus' align='center'>".($score==0?'-':$score)."</div></td>\n"
|
|
."<td><div class='mystatus'>$time</div></td>\n"
|
|
."</tr>\n";
|
|
}
|
|
|
|
$output .= "</table></td></tr></table>";
|
|
}
|
|
$output .= "</body></html>";
|
|
//We just wanted the status, so skip the rest now (we don't need any footer anyway)
|
|
echo $output;
|
|
exit();
|
|
}
|
|
//echo "<pre>".print_r($items,true)."</pre>";
|
|
//if the action requested is any of restart, openfirst, next or prev, open the content in the content frame
|
|
//This is done by calling opensco.php in the 'message' frame, which in turn calls the document in the 'sco' frame.
|
|
if(($menu=='restart' or $menu=='next' or $menu=='prev' or $openfirst=='yes') and ($exit_after_menu==false)){
|
|
//error_log("Menu was 'prev' or 'next' or restart or openfirst was set and could include javascript for opensco and contents.php.",0);
|
|
//error_log('get_better_anchor_target returned '.$target_identifier,0);
|
|
$output .= "<script type='text/javascript'>\n/* <![CDATA[ */\n".
|
|
"nextwindow=open('opensco.php?sco_href=".base64_encode($href)."&sco_identifier=".$_SESSION['s_identifier']
|
|
."&file=$request_file&edoceo=$edoceo&openDir=$openDir','message');".
|
|
//this line enables the movement of the menu
|
|
//"thiswindow=open('contents.php?file=$request_file&openDir=$openDir&time=$time&edoceo=$edoceo#$identifier','contents');".
|
|
"thiswindow=open('contents.php?file=$request_file&openDir=$openDir&time=$time&edoceo=$edoceo','contents');".
|
|
"\n/* ]]> */\n</script>";
|
|
//sco opening end
|
|
}
|
|
|
|
|
|
/*=======================
|
|
TABLE OF CONTENTS TITLE
|
|
=======================*/
|
|
//6. If we are here, it's only because the user didn't choose the status display
|
|
// (otherwise we would have exited). SHOW the TOC here...
|
|
//6.1 Deal with the title
|
|
$t=$defaultorgtitle;
|
|
$t=str_replace(' ',' ',$t);
|
|
if ($wrap) {
|
|
$output .= "<div><b>$defaultorgtitle</b></div><br />";
|
|
} else {
|
|
$output .= "<table border='0' cellspacing='0' cellpadding='0' width='$tablewidth'>
|
|
<tr>
|
|
<td>
|
|
<div><b>$t</b></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<br />
|
|
<table border='0' cellspacing='0' cellpadding='0' width='$tablewidth'>";
|
|
}
|
|
|
|
//remember the title in the session (for later reuse)
|
|
$_SESSION['defaultorgtitle'] = $defaultorgtitle;
|
|
|
|
/*====================================
|
|
TABLE OF CONTENTS LISTING ROW BY ROW
|
|
====================================*/
|
|
//6.2 Displaying TOC
|
|
|
|
//$output .= "<pre>".print_r($items,true)."</pre>";
|
|
|
|
//6.2.1 Init vars to compute total items completed
|
|
$num_of_completed=0;
|
|
$i=1;
|
|
|
|
while ($items[$i]) {
|
|
//error_log("Now processing item $i: ".$items[$i]['identifier'],0);
|
|
|
|
//6.2.2 Set whether to highlight the item or not (don't highlight if displaying status)
|
|
$bold = false;
|
|
if( ( $items[$i]['identifier'] == $_SESSION['s_identifier']) && (!$_SESSION['displaying_status'])){
|
|
//error_log("Item is the current one",0);
|
|
$bold = true;
|
|
}
|
|
|
|
if (!$wrap) { $output .= "<tr>"; } //do not wrap this item with the previous one }
|
|
|
|
//6.2.3 if this user never opened this content
|
|
// make initial entries in the scorm data table for this student
|
|
// the order of insertions is the same as in the file (so it is the one we want to keep for status display)
|
|
// $index comes from outside this script and represents the scoId in the table
|
|
if ($everopened==false) {
|
|
$sql = "INSERT INTO $TBL_SCORM_SCO_DATA "
|
|
."(contentId, scoId, scoIdentifier, scoTitle, status, studentID, score, time)VALUES "
|
|
."('".$mycontentId."','".$index."','".$items[$i]['identifier']."','"
|
|
.addslashes($items[$i]['title'])."','not attempted','".$_uid."','0','00:00')";
|
|
$result = api_sql_query($sql,__FILE__,__LINE__);
|
|
}
|
|
|
|
//6.2.4 Display spaces/cells an amount of time that corresponds to the depth of the element in the hierarchy
|
|
if ($wrap) {
|
|
$output .= str_repeat(" ",$items[$i]['level']-1);
|
|
} else {
|
|
$output .= "<td>".str_repeat(" ",$items[$i]['level']-1)."</td>";
|
|
}
|
|
$col=$maxlevel-$items[$i]['level']+1; //prepare the number of columns to display
|
|
if (!$wrap) { $output .= '<td colspan="'.$col.'"><table border="0" cellspacing="0" cellpadding="0">'; }
|
|
|
|
//6.2.5 Get the document/content path
|
|
//$href="../../courses/$_course[path]/scorm".$openDir."/".urlencode($items[$i]['href']);
|
|
if(substr($items[$i]['href'],0,4)=='http'){
|
|
$href = $items[$i]['href'];
|
|
}else{
|
|
$href=api_get_path('WEB_COURSE_PATH').$_course['path']."/scorm".$openDir."/".$items[$i]['href'];
|
|
}
|
|
//6.2.6 Get useful values in practical variables
|
|
$identifier=$items[$i]['identifier'];
|
|
$prereq=$items[$i]['prereq'];
|
|
|
|
//6.2.7 Get the recorded status for this document/content
|
|
$sql3 = "SELECT status FROM $TBL_SCORM_SCO_DATA WHERE "
|
|
." (contentId='$mycontentId' and studentId='$_uid' and scoIdentifier='$identifier')";
|
|
$result3 = api_sql_query($sql3,__FILE__,__LINE__);
|
|
$ar3=mysql_fetch_array($result3);
|
|
$lesson_status=$ar3['status'];
|
|
|
|
//6.2.8 Count this lesson as completed if applicable
|
|
if (($lesson_status == 'completed') or ($lesson_status == 'passed')) { $num_of_completed++; }
|
|
|
|
//6.2.9 display this document/content name as a link to the document/content itself
|
|
if ($items[$i]['href']!='') {
|
|
if (!$wrap) { $output .= "<tr><td>"; } //display choice
|
|
|
|
//6.2.9.1 if the lesson was completed, display a little icon
|
|
if (($lesson_status=='completed') or ($lesson_status=='passed')) {
|
|
$output .= '<img src="../img/checkbox_on2.gif" border="0" width="13" height="11" alt="on"/>';
|
|
} else {
|
|
//insert the image but make it invisible, so that the space taken is identical (no movement when image appears)
|
|
$output .= '<img src="../img/checkbox_on2.gif" border="0" width="13" height="11" alt="on" style="visibility: hidden"/>';
|
|
//$output .= " ";
|
|
}
|
|
|
|
if (!$wrap) { $output .= "</td><td>"; } //display choice
|
|
|
|
//6.2.9.2 Add HTML anchor
|
|
$output .= "<a name='$identifier'>";
|
|
//6.2.9.3 Highlight if applicable
|
|
if ($bold) { $output .= "<b>";}
|
|
//6.2.9.4 Display the link
|
|
$output .= "<a title='". $items[$i]['title']. "' href='opensco.php";
|
|
if ($version=='1.3') { //SCORM version 1.3
|
|
if ($items[$i]['parameters']!='') {
|
|
// there should probably be an additional '?' here... maybe...
|
|
$output .= "{$items[$i]['parameters']}"."&"; //add parameters to the link
|
|
} else {
|
|
$output .= "?";
|
|
}
|
|
} else { //version is not 1.3
|
|
$output .= "?";
|
|
}
|
|
|
|
$output .= "sco_href=".base64_encode($href)."&sco_identifier=$identifier&file=$request_file&edoceo=$edoceo&openDir=$openDir' target='message' class='";
|
|
//as you might realise, this links points to the message frame, to opensco.php, which will then open the content
|
|
//in the contents frame
|
|
|
|
//change CSS class to "completed" if completed or passed
|
|
if (($lesson_status=='completed') or ($lesson_status=='passed')) { $output .= $array_status[$lesson_status]; }
|
|
$output .= "'>";
|
|
//error_log("Added link to opensco with sco_identifier=".$items[$i]['identifier'],0);
|
|
|
|
}else{
|
|
if (($lesson_status=='completed') or ($lesson_status=='passed')) {
|
|
$output .= '<img src="../img/checkbox_on2.gif" border="0" width="13" height="11" alt="on"/>';
|
|
} else {
|
|
//insert the image but make it invisible, so that the space taken is identical (no movement when image appears)
|
|
$output .= '<img src="../img/checkbox_on2.gif" border="0" width="13" height="11" alt="on" style="visibility: hidden"/>';
|
|
}
|
|
}
|
|
|
|
//6.2.10 Simulate htmlentities() just for spaces
|
|
$t=$items[$i]['title'];
|
|
|
|
//6.2.11 Display title (unfiltered if wrapped display - which is the default)
|
|
$t=str_replace(' ',' ',$t);
|
|
$twrap = $items[$i]['title'];
|
|
//cut the title string if too long to fit in typical frame width
|
|
$max_len = 37-(3*$items[$i]['level']);
|
|
if(strlen($twrap)>$max_len){
|
|
$twrap = str_replace(' ',' ',substr($twrap,0,$max_len-3).'...');
|
|
}
|
|
|
|
if (!$wrap) { $output .= $t; } else { $output .= $twrap; }
|
|
|
|
if ( $items[$i]['href'] != '' ) { $output .= "</a>"; } //see above for opening anchor tag "a name=..."
|
|
if ($bold) { $output .= "</b>";}
|
|
if (!$wrap) { $output .= "</td></tr></table></td></tr>\n"; } else { $output .= "<br />\n"; }
|
|
$i++;
|
|
}
|
|
|
|
//6.2.12 Display percentage complete
|
|
if ( count($items) != 0 ) {
|
|
$percent=round($num_of_completed/count($items)*100);
|
|
} else { //no item found. Display an error.
|
|
$output .= message(htmlentities(get_lang('ScormNoItems'),ENT_QUOTES,$charset_lang)); echo $output; exit();
|
|
}
|
|
|
|
$npercent=100-$percent;
|
|
if (($percent==0) and ($openfirst=='yes') and ($version != '1.3') and ($menu=='')) { $menu="next"; }
|
|
//ie. the first lesson appears if nothing is completed, not if in 1.3 and the user clicked onto Launch just now
|
|
|
|
if (!$wrap) { $output .= "</table>\n"; } // display-choice
|
|
|
|
/*=================
|
|
COMPLETION STATUS
|
|
=================*/
|
|
|
|
//6.2.13 Display the completion status of the whole learning path
|
|
//! temporary fix for the accentuated characters bug (due to different encoding
|
|
// between the imsmanifest file - which is the source to this frame's encoding - and the language file)
|
|
// The fix is temporary because it will most probably fail on any non-european special character
|
|
// To really fix this, there should be a function to check the language files' encoding and put it
|
|
// in the third argument to htmlentities() here below
|
|
$output .= '<br /><a name="statustable"></a><table border="0"><tr><td>'
|
|
.htmlentities(get_lang('ScormCompstatus'),ENT_QUOTES,$charset_lang)."<br />"
|
|
.'<table border="0" cellpadding="0" cellspacing="0"><tr><td>'
|
|
.'<img src="../img/bar_1.gif" width="1" height="12">'
|
|
.'<img src="../img/bar_1u.gif" width="'.$percent.'" height="12">'
|
|
.'<img src="../img/bar_1m.gif" width="1" height="12">'
|
|
.'<img src="../img/bar_1r.gif" width="'.$npercent.'" height="12">'
|
|
.'<img src="../img/bar_1.gif" width="1" height="12"></td></tr></table>'
|
|
."</td><td valign=\"bottom\"><font align=\"left\"><br />$percent %</td></tr></table>\n";
|
|
|
|
if ($tabledraw) {
|
|
$output .= "<br /><br /><hr /><br />";
|
|
$i=1;
|
|
while ($items[$i]) {
|
|
$output .= "Index : {$items[$i]['index']} "
|
|
."Identifierref : {$items[$i]['identifierref']} "
|
|
."Identifier : {$items[$i]['identifier']} "
|
|
."Level : {$items[$i]['level']} "
|
|
."Title : {$items[$i]['title']} "
|
|
."Href : {$items[$i]['href']} "
|
|
."Prereq : {$items[$i]['prereq']} "
|
|
."Parameters : {$items[$i]['parameters']} "
|
|
."Clusterinfo : {$items[$i]['clusterinfo']}<br />\n";
|
|
$i++;
|
|
}
|
|
$output .= "<br />\n";
|
|
$output .= "defaultorg : $defaultorgref<br />defaultorgtitle : $defaultorgtitle";
|
|
}
|
|
|
|
//6.2.14 If the parser was used, close it (and the XML file too)
|
|
if($openfirst=='yes' or $menu=='restart'){
|
|
xml_parser_free($xml_parser);
|
|
fclose($fp);
|
|
}
|
|
|
|
//$openfirst='no';
|
|
|
|
/*===========================
|
|
VERSION INFO
|
|
===========================*/
|
|
|
|
//$output .= "<p class=version>[ Scorm ".get_lang('ScormVersion')." : $version ]</p>";
|
|
|
|
//if($menu=='restart'){unset($_SESSION['s_identifier']);}
|
|
$_SESSION['contentId'] = $mycontentId;
|
|
$_SESSION['items'] = $items;
|
|
//if(empty($_SESSION['old_sco_identifier'])){
|
|
// $_SESSION['old_sco_identifier'] = $_SESSION['s_identifier'];
|
|
//}
|
|
$_SESSION['displaying_status']=false;
|
|
|
|
//if the user didn't click a menu button, then assume he clicked a SCORM button in the content,
|
|
// and that the element that needs to be updated by closesco.php is the latest element we have here,
|
|
// namely $_SESSION['s_identifier'], instead of $_SESSION['old_s_identifier']
|
|
if(empty($menu)){
|
|
$_SESSION['old_sco_identifier'] = $_SESSION['s_identifier'];
|
|
}
|
|
|
|
//error_log("End of script (before output). s_identifier is now:".$_SESSION['s_identifier']." and old s_identifier is now:".$_SESSION['old_sco_identifier'],0);
|
|
|
|
|
|
echo $output;
|
|
//error_log("End of script (after output). s_identifier is now:".$_SESSION['s_identifier']." and old s_identifier is now:".$_SESSION['old_sco_identifier'],0);
|
|
|
|
?>
|
|
</body></html>
|
|
|