Hacked By AnonymousFox

Current Path : /home/missmand/public_html/js/lightbox/
Upload File :
Current File : /home/missmand/public_html/js/lightbox/newscorm.tar

aiccBlock.class.php000064400000004073152003363470010242 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the aiccResource class that deals with elemens from AICC Course Structure file
 * @package	chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 * @license	GNU/GPL
 */

/**
 * Class defining the Block elements in an AICC Course Structure file.
 */
require_once 'learnpathItem.class.php';
class aiccBlock extends learnpathItem {
    public $identifier = '';
    public $members = array();

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormResource
     * object from database records or from the array given as second param
     * @param	string	Type of construction needed ('db' or 'config', default = 'config')
     * @param	mixed	Depending on the type given, DB id for the lp_item or parameters array
     */
    function aiccBlock($type = 'config', $params) {
        if (isset($params)) {
            switch ($type) {
                case 'db':
                    //TODO: Implement this way of object creation.
                    return false;
                case 'config': // Do the same as the default.
                default:
                    foreach ($params as $a => $value) {
                        switch ($a) {
                               case 'system_id':
                                   $this->identifier = strtolower($value);
                                break;
                            case 'member':
                                if (strstr($value, ',') !== false) {
                                    $temp = split(',', $value);
                                    foreach ($temp as $val) {
                                        if (!empty($val)) {
                                            $this->members[] = $val;
                                        }
                                    }
                                }
                                break;
                        }
                    }
                    return true;
            }
        }
        return false;
    }
}
aicc.class.php000064400000127741152003363470007277 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Defines the AICC class, which is meant to contain the aicc items (nuclear elements)
 * @package chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 * @license	GNU/GPL
 */
/**
 * Code
 */
require_once 'aiccItem.class.php';
//require_once 'aiccMetadata.class.php';
//require_once 'aiccOrganization.class.php';
require_once 'aiccResource.class.php';
require_once 'aiccBlock.class.php';
/**
 * Class
 * @package chamilo.learnpath
 */
class aicc extends learnpath {

    public $config = array();
    public $config_basename = '';	// The configuration files might be multiple and might have
                                    // funny names. We need to keep the name of that file while we
                                    // install the content.
    public $config_files = array();
    public $config_exts = array(
        'crs' => 0, // Course description file (mandatory)
        'au'  => 0, // Assignable Unit file (mandatory)
        'des' => 0, // Descriptor file (mandatory)
        'cst' => 0, // Course structure file (mandatory)
        'ore' => 0, // Objectives relationshops file (optional)
        'pre' => 0, // Prerequisites file (optional)
        'cmp' => 0  // Completion Requirements file (optional)
    );
    public $aulist = array();
    public $au_order_list = array();
    public $au_order_list_new_id = array();
    public $deslist = array();
    public $cstlist = array();
    public $orelist = array();

    public $subdir = '';	// Path between the scorm/ directory and the config files e.g. maritime_nav/maritime_nav. This is the path that will be used in the lp_path when importing a package.
    public $zipname = '';	// Keeps the zipfile safe for the object's life so that we can use it if there is no title available.
    public $lastzipnameindex = 0;	// Keeps an index of the number of uses of the zipname so far.
    public $config_encoding = 'ISO-8859-1';
    public $debug = 0;

    /**
     * Class constructor. Based on the parent constructor.
     * @param	string	Course code
     * @param	integer	Learnpath ID in DB
     * @param	integer	User ID
     */
    public function __construct($course_code = null, $resource_id = null, $user_id = null) {
        if ($this->debug > 0) { error_log('In aicc::aicc()', 0); }
        if (!empty($course_code) && !empty($resource_id) && !empty($user_id)) {
            parent::__construct($course_code, $resource_id, $user_id);
        } else {
            //do nothing but still build the aicc object
        }
    }

    /**
     * Opens a resource
     * @param	integer	Database ID of the resource
     */
    public function open($id) {
        if ($this->debug > 0) { error_log('In aicc::open()', 0); }
        // Redefine parent method.
    }

    /**
     * Parses a set of AICC config files and puts everything into the $config array
     * @param	string	Path to the config files dir on the system. If not defined, uses the base path of the course's scorm dir
     * @return	array	Structured array representing the config files' contents
     */
    function parse_config_files($dir = '') {
        if ($this->debug > 0) {error_log('New LP - In aicc::parse_config_files('.$dir.')', 0); }
        if (empty($dir)) {
            // Get the path of the AICC config files dir.
            $dir = $this->subdir;
        }
        if (is_dir($dir) && is_readable($dir)) {
            // Now go through all the config files one by one and parse everything into AICC objects.

            // The basename for the config files is stored in $this->config_basename.

            // Parse the Course Description File (.crs) - ini-type.
            $crs_file = $dir.'/'.$this->config_files['crs'];
            $crs_params = $this->parse_ini_file_quotes_safe($crs_file);
            //echo '<pre>crs:'.print_r($crs_params, true).'</pre>';
            if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$crs_file.' has been parsed', 0); }

            // CRS distribute crs params into the aicc object.
            if (!empty($crs_params['course']['course_creator'])) {
                $this->course_creator = Database::escape_string($crs_params['course']['course_creator']);
            }
            if (!empty($crs_params['course']['course_id'])) {
                $this->course_id = Database::escape_string($crs_params['course']['course_id']);
            }
            if (!empty($crs_params['course']['course_system'])) {
                $this->course_system = $crs_params['course']['course_system'];
            }
            if (!empty($crs_params['course']['course_title'])) {
                $this->course_title = Database::escape_string($crs_params['course']['course_title']);
            }
            if (!empty($crs_params['course']['course_level'])) {
                $this->course_level = $crs_params['course']['course_level'];
            }
            if (!empty($crs_params['course']['max_fields_cst'])) {
                $this->course_max_fields_cst = $crs_params['course']['max_fields_cst'];
            }
            if (!empty($crs_params['course']['max_fields_ort'])) {
                $this->course_max_fields_ort = $crs_params['course']['max_fields_ort'];
            }
            if (!empty($crs_params['course']['total_aus'])) {
                $this->course_total_aus = $crs_params['course']['total_aus'];
            }
            if (!empty($crs_params['course']['total_blocks'])) {
                $this->course_total_blocks = $crs_params['course']['total_blocks'];
            }
            if (!empty($crs_params['course']['total_objectives'])) {
                $this->course_total_objectives = $crs_params['course']['total_objectives'];
            }
            if (!empty($crs_params['course']['total_complex_objectives'])) {
                $this->course_total_complex_objectives = $crs_params['course']['total_complex_objectives'];
            }
            if (!empty($crs_params['course']['version'])) {
                $this->course_version = $crs_params['course']['version'];
            }
            if (!empty($crs_params['course_description'])) {
                $this->course_description = Database::escape_string($crs_params['course_description']);
            }

            // Parse the Descriptor File (.des) - csv-type.
            $des_file = $dir.'/'.$this->config_files['des'];
            $des_params = $this->parse_csv_file($des_file);
            //echo '<pre>des:'.print_r($des_params, true).'</pre>';
            if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$des_file.' has been parsed', 0); }
            // Distribute des params into the aicc object.
            foreach ($des_params as $des){
                // One AU in AICC is equivalent to one SCO in SCORM (scormItem class).
                $oDes = new aiccResource('config', $des);
                $this->deslist[$oDes->identifier] = $oDes;
            }

            // Parse the Assignable Unit File (.au) - csv-type.
            $au_file = $dir.'/'.$this->config_files['au'];
            $au_params = $this->parse_csv_file($au_file);
            //echo '<pre>au:'.print_r($au_params, true).'</pre>';
            if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$au_file.' has been parsed', 0); }
            // Distribute au params into the aicc object.
            foreach ($au_params as $au) {
                $oAu = new aiccItem('config', $au);
                $this->aulist[$oAu->identifier] = $oAu;
                $this->au_order_list[] = $oAu->identifier;
            }

            // Parse the Course Structure File (.cst) - csv-type.
            $cst_file = $dir.'/'.$this->config_files['cst'];
            $cst_params = $this->parse_csv_file($cst_file, ',', '"', true);
            //echo '<pre>cst:'.print_r($cst_params, true).'</pre>';
            if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$cst_file.' has been parsed', 0); }
            // Distribute cst params into the aicc object.
            foreach ($cst_params as $cst) {
                $oCst = new aiccBlock('config', $cst);
                $this->cstlist[$oCst->identifier] = $oCst;
            }

            // Parse the Objectives Relationships File (.ore) - csv-type - if exists.
            // TODO: Implement these objectives. For now they're just parsed.
            if (!empty($this->config_files['ore'])) {
                $ore_file = $dir.'/'.$this->config_files['ore'];
                $ore_params = $this->parse_csv_file($ore_file, ',', '"', true);
                //echo '<pre>ore:'.print_r($ore_params,true).'</pre>';
                if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$ore_file.' has been parsed', 0); }
                // Distribute ore params into the aicc object.
                foreach ($ore_params as $ore) {
                    $oOre = new aiccObjective('config', $ore);
                    $this->orelist[$oOre->identifier] = $oOre;
                }
            }

            // Parse the Prerequisites File (.pre) - csv-type - if exists.
            if (!empty($this->config_files['pre'])) {
                $pre_file = $dir.'/'.$this->config_files['pre'];
                $pre_params = $this->parse_csv_file($pre_file);
                //echo '<pre>pre:'.print_r($pre_params, true).'</pre>';
                if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$pre_file.' has been parsed', 0); }
                // Distribute pre params into the aicc object.
                foreach ($pre_params as $pre) {
                    // Place a constraint on the corresponding block or AU.
                    if (in_array(api_strtolower($pre['structure_element']), array_keys($this->cstlist))) {
                        // If this references a block element:
                        $this->cstlist[api_strtolower($pre['structure_element'])]->prereq_string = api_strtolower($pre['prerequisite']);
                    }
                    if (in_array(api_strtolower($pre['structure_element']), array_keys($this->aulist))) {
                        // If this references a block element:
                        $this->aulist[api_strtolower($pre['structure_element'])]->prereq_string = api_strtolower($pre['prerequisite']);
                    }
                }
            }

            // Parse the Completion Requirements File (.cmp) - csv-type - if exists.
            // TODO: Implement this set of requirements (needs database changes).
            if (!empty($this->config_files['cmp'])) {
                $cmp_file = $dir.'/'.$this->config_files['cmp'];
                $cmp_params = $this->parse_csv_file($cmp_file);
                //echo '<pre>cmp:'.print_r($cmp_params, true).'</pre>';
                if ($this->debug > 1) { error_log('New LP - In aicc::parse_config_files() - '.$cmp_file.' has been parsed', 0); }
                // Distribute cmp params into the aicc object.
                foreach ($cmp_params as $cmp) {
                    //$oCmp = new aiccCompletionRequirements('config', $cmp);
                    //$this->cmplist[$oCmp->identifier] =& $oCmp;
                }
            }
        }
        return $this->config;
    }

    /**
     * Import the aicc object (as a result from the parse_config_files function) into the database structure
     * @param	string	Unique course code
     * @return	bool	Returns -1 on error
     */
    function import_aicc($course_code) {
        $course_id = api_get_course_int_id();
        
        if ($this->debug > 0) { error_log('New LP - In aicc::import_aicc('.$course_code.')', 0); }
        // Get table names.
        $new_lp = 'lp';
        $new_lp_item = 'lp_item';

        // The previous method wasn't safe to get the database name, so do it manually with the course_code.
        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE)." WHERE code='$course_code'";
        $res = Database::query($sql);
        if (Database::num_rows($res) < 1) { error_log('New LP - Database for '.$course_code.' not found '.__FILE__.' '.__LINE__, 0); return -1; }
        $row = Database::fetch_array($res);
                
        $new_lp = Database::get_course_table(TABLE_LP_MAIN);
        $new_lp_item = Database::get_course_table(TABLE_LP_ITEM);
        $get_max = "SELECT MAX(display_order) FROM $new_lp WHERE c_id = $course_id";
        $res_max = Database::query($get_max);
        if (Database::num_rows($res_max) < 1) {
            $dsp = 1;
        } else {
            $row = Database::fetch_array($res_max);
            $dsp = $row[0] + 1;
        }

        $this->config_encoding = "ISO-8859-1"; // TODO: We may apply detection for this value, see the function api_detect_encoding().

        $sql = "INSERT INTO $new_lp (c_id, lp_type, name, ref, description, path, force_commit, default_view_mod, default_encoding, js_lib, content_maker,display_order)" .
                "VALUES " .
                "($course_id, 3, '".$this->course_title."', '".$this->course_id."','".$this->course_description."'," .
                "'".$this->subdir."', 0, 'embedded', '".$this->config_encoding."'," .
                "'aicc_api.php','".$this->course_creator."',$dsp)";
        if ($this->debug > 2) { error_log('New LP - In import_aicc(), inserting path: '. $sql, 0); }
        $res = Database::query($sql);
        $lp_id = Database::insert_id();
        $this->lp_id = $lp_id;
        api_item_property_update(api_get_course_info($course_code), TOOL_LEARNPATH, $this->lp_id, 'LearnpathAdded', api_get_user_id());
        api_item_property_update(api_get_course_info($course_code), TOOL_LEARNPATH, $this->lp_id, 'visible', api_get_user_id());

        $previous = 0;
        foreach ($this->aulist as $identifier => $dummy) {
            $oAu =& $this->aulist[$identifier];
            //echo "Item ".$oAu->identifier;
            $field_add = '';
            $value_add = '';
            if (!empty($oAu->masteryscore)) {
                $field_add = 'mastery_score, ';
                $value_add = $oAu->masteryscore.',';
            }
            $title = $oAu->identifier;
            if (is_object($this->deslist[$identifier])) {
                $title = $this->deslist[$identifier]->title;
            }
            $path = $oAu->path;
            //$max_score = $oAu->max_score // TODO: Check if special constraint exists for this item.
            //$min_score = $oAu->min_score // TODO: Check if special constraint exists for this item.
            $parent = 0; // TODO: Deal with the parent.
            $previous = 0;
            $prereq = $oAu->prereq_string;
            //$previous = (!empty($this->au_order_list_new_id[x]) ? $this->au_order_list_new_id[x] : 0); // TODO: Deal with the previous.
            $sql_item = "INSERT INTO $new_lp_item (c_id, lp_id,item_type,ref,title, path,min_score,max_score, $field_add parent_item_id,previous_item_id,next_item_id, prerequisite,display_order) " .
                    "VALUES " .
                    "($course_id, $lp_id, 'au','".$oAu->identifier."','".$title."'," .
                    "'$path',0,100, $value_add" .
                    "$parent, $previous, 0, " .
                    "'$prereq', 0" .
                    ")";
            $res_item = Database::query($sql_item);
            if ($this->debug > 1) { error_log('New LP - In aicc::import_aicc() - inserting item : '.$sql_item.' : '.Database::error(), 0); }
            $item_id = Database::insert_id();
            // Now update previous item to change next_item_id.
            if ($previous != 0) {
                $upd = "UPDATE $new_lp_item SET next_item_id = $item_id WHERE c_id = $course_id AND id = $previous";
                $upd_res = Database::query($upd);
                // Update the previous item id.
            }
            $previous = $item_id;
        }
    }

     /**
     * Intermediate to import_package only to allow import from local zip files
     * @param	string	Path to the zip file, from the dokeos sys root
     * @param	string	Current path (optional)
     * @return string	Absolute path to the AICC description files or empty string on error
     */
    function import_local_package($file_path, $current_dir = '') {
        // TODO: Prepare info as given by the $_FILES[''] vector.
        $file_info = array();
        $file_info['tmp_name'] = $file_path;
        $file_info['name'] = basename($file_path);
        // Call the normal import_package function.
        return $this->import_package($file_info, $current_dir);
    }

    /**
     * Imports a zip file (presumably AICC) into the Chamilo structure
     * @param	string	Zip file info as given by $_FILES['userFile']
     * @return	string	Absolute path to the AICC config files directory or empty string on error
     */
    function import_package($zip_file_info, $current_dir = '') {
        if ($this->debug > 0) { error_log('In aicc::import_package('.print_r($zip_file_info, true).',"'.$current_dir.'") method', 0); }
        //ini_set('error_log', 'E_ALL');
        $maxFilledSpace = 1000000000;
        $zip_file_path = $zip_file_info['tmp_name'];
        $zip_file_name = $zip_file_info['name'];

        if ($this->debug > 0) { error_log('New LP - aicc::import_package() - Zip file path = '.$zip_file_path.', zip file name = '.$zip_file_name, 0); }
        $course_rel_dir  = api_get_course_path().'/scorm'; // Scorm dir web path starting from /courses
        $course_sys_dir = api_get_path(SYS_COURSE_PATH).$course_rel_dir; // The absolute system path of this course.
        $current_dir = replace_dangerous_char(trim($current_dir), 'strict'); // Current dir we are in, inside scorm/
        if ($this->debug > 0) { error_log('New LP - aicc::import_package() - Current_dir = '.$current_dir, 0); }

         //$uploaded_filename = $_FILES['userFile']['name'];
        // Get the name of the zip file without the extension.
        if ($this->debug > 0) { error_log('New LP - aicc::import_package() - Received zip file name: '.$zip_file_path, 0); }
        $file_info = pathinfo($zip_file_name);
        $filename = $file_info['basename'];
        $extension = $file_info['extension'];
        $file_base_name = str_replace('.'.$extension, '', $filename); // Filename without its extension.
        $this->zipname = $file_base_name; // Save for later in case we don't have a title.

        if ($this->debug > 0) { error_log('New LP - aicc::import_package() - Base file name is : '.$file_base_name, 0); }
        $new_dir = replace_dangerous_char(trim($file_base_name),'strict');
        $this->subdir = $new_dir;
        if($this->debug > 0) { error_log('New LP - aicc::import_package() - Subdir is first set to : '.$this->subdir, 0); }

        /*
        if (check_name_exist($course_sys_dir.$current_dir.'/'.$new_dir)) {
            $dialogBox = get_lang('FileExists');
            $stopping_error = true;
        }
        */
        $zipFile = new PclZip($zip_file_path);

        // Check the zip content (real size and file extension).

        $zipContentArray = $zipFile->listContent();

        $package_type = ''; // The type of the package. Should be 'aicc' after the next few lines.
        $package = ''; // The basename of the config files (if 'courses.crs' => 'courses').
        $at_root = false; // Check if the config files are at zip root.
        $config_dir = ''; // The directory in which the config files are. May remain empty.
        $files_found = array();
        $subdir_isset = false;
        // The following loop should be stopped as soon as we found the right config files (.crs, .au, .des and .cst).
        foreach ($zipContentArray as $thisContent) {
            if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
                // If a php file is found, do not authorize (security risk).
                if ($this->debug > 1) {error_log('New LP - aicc::import_package() - Found unauthorized file: '.$thisContent['filename'], 0); }
                return api_failure::set_failure('php_file_in_zip_file');
            } elseif (preg_match('?.*/aicc/$?', $thisContent['filename'])) {
                // If a directory named 'aicc' is found, package type = aicc, but continue,
                // because we need to find the right AICC files;
                if ($this->debug > 1) { error_log('New LP - aicc::import_package() - Found aicc directory: '.$thisContent['filename'], 0); }
                $package_type = 'aicc';
            } else {
                // else, look for one of the files we're searching for (something.crs case insensitive).
                $res = array();
                if (preg_match('?^(.*)\.(crs|au|des|cst|ore|pre|cmp)$?i', $thisContent['filename'], $res)) {
                    if ($this->debug > 1) { error_log('New LP - aicc::import_package() - Found AICC config file: '.$thisContent['filename'].'. Now splitting: '.$res[1].' and '.$res[2], 0); }
                    if ($thisContent['filename'] == basename($thisContent['filename'])) {
                        if ($this->debug > 2) { error_log('New LP - aicc::import_package() - '.$thisContent['filename'].' is at root level', 0); }
                        $at_root = true;
                        if (!is_array($files_found[$res[1]])) {
                            $files_found[$res[1]] = $this->config_exts; // Initialise list of expected extensions (defined in class definition).
                        }
                        $files_found[$res[1]][api_strtolower($res[2])] = $thisContent['filename'];
                        $subdir_isset = true;
                    } else {
                        if (!$subdir_isset) {
                            if (preg_match('?^.*/aicc$?i',dirname($thisContent['filename']))) {
                                //echo "Cutting subdir<br/>";
                                $this->subdir .= '/'.substr(dirname($thisContent['filename']), 0, -5);
                            } else {
                                //echo "Not cutting subdir<br/>";
                                $this->subdir .= '/'.dirname($thisContent['filename']);
                            }
                            $subdir_isset = true;
                        }
                        if ($this->debug > 2) { error_log('New LP - aicc::import_package() - '.$thisContent['filename'].' is not at root level - recording subdir '.$this->subdir, 0); }
                        $config_dir = dirname($thisContent['filename']); // Just the relative directory inside scorm/
                        if (!is_array($files_found[basename($res[1])])) {
                            $files_found[basename($res[1])] = $this->config_exts;
                        }
                        $files_found[basename($res[1])][api_strtolower($res[2])] = basename($thisContent['filename']);
                    }
                    $package_type = 'aicc';
                } else {
                    if ($this->debug > 3) { error_log('New LP - aicc::import_package() - File '.$thisContent['filename'].' didnt match any check', 0); }
                }
            }
            $realFileSize += $thisContent['size'];
        }
        if ($this->debug > 2) { error_log('New LP - aicc::import_package() - $files_found: '.print_r($files_found, true), 0); }
        if ($this->debug > 1) { error_log('New LP - aicc::import_package() - Package type is now '.$package_type, 0); }
        $mandatory = false;
        foreach ($files_found as $file_name => $file_exts) {
            $temp = (
                !empty($files_found[$file_name]['crs'])
                AND !empty($files_found[$file_name]['au'])
                AND !empty($files_found[$file_name]['des'])
                AND !empty($files_found[$file_name]['cst'])
            );
            if ($temp) {
                if ($this->debug > 1) { error_log('New LP - aicc::import_package() - Found all config files for '.$file_name, 0); }
                $mandatory = true;
                $package = $file_name;
                // Store base config file name for reuse in parse_config_files().
                $this->config_basename = $file_name;
                // Store filenames for reuse in parse_config_files().
                $this->config_files = $files_found[$file_name];
                // Get out, we only want one config files set.
                break;
            }
        }

        if ($package_type == '' || !$mandatory)
        // && defined('CHECK_FOR_AICC') && CHECK_FOR_AICC)
        {
            return api_failure::set_failure('not_aicc_content');
        }

        if (!enough_size($realFileSize, $course_sys_dir, $maxFilledSpace)) {
            return api_failure::set_failure('not_enough_space');
        }

        // It happens on Linux that $new_dir sometimes doesn't start with '/'
        if ($new_dir[0] != '/') {
            $new_dir = '/'.$new_dir;
        }
        // Cut trailing slash.
        if ($new_dir[strlen($new_dir) - 1] == '/') {
            $new_dir = substr($new_dir, 0, -1);
        }

        /* Uncompressing phase */

        /*
            We need to process each individual file in the zip archive to
            - add it to the database
            - parse & change relative html links
            - make sure the filenames are secure (filter funny characters or php extensions)
        */
        if (is_dir($course_sys_dir.$new_dir) OR @mkdir($course_sys_dir.$new_dir, api_get_permissions_for_new_directories())) {
            // PHP method - slower...
            if ($this->debug >= 1) { error_log('New LP - Changing dir to '.$course_sys_dir.$new_dir, 0); }
            $saved_dir = getcwd();
            chdir($course_sys_dir.$new_dir);
            $unzippingState = $zipFile->extract();
            for ($j = 0; $j < count($unzippingState); $j++) {
                $state = $unzippingState[$j];

                // TODO: Fix relative links in html files (?)
                $extension = strrchr($state["stored_filename"], '.');
                //if ($this->debug > 1) { error_log('New LP - found extension '.$extension.' in '.$state['stored_filename'], 0); }

            }

            if (!empty($new_dir)) {
                $new_dir = $new_dir.'/';
            }
            // Rename files, for example with \\ in it.
            if ($dir = @opendir($course_sys_dir.$new_dir)) {
                if ($this->debug == 1) { error_log('New LP - Opened dir '.$course_sys_dir.$new_dir, 0); }
                while ($file = readdir($dir)) {
                    if ($file != '.' && $file != '..') {
                        $filetype = 'file';

                        if (is_dir($course_sys_dir.$new_dir.$file)) $filetype = 'folder';

                        // TODO: RENAMING FILES CAN BE VERY DANGEROUS AICC-WISE, avoid that as much as possible!
                        //$safe_file = replace_dangerous_char($file, 'strict');
                        $find_str = array('\\', '.php', '.phtml');
                        $repl_str = array('/',  '.txt', '.txt');
                        $safe_file = str_replace($find_str, $repl_str, $file);

                        if ($safe_file != $file) {
                            //@rename($course_sys_dir.$new_dir, $course_sys_dir.'/'.$safe_file);
                            $mydir = dirname($course_sys_dir.$new_dir.$safe_file);
                            if (!is_dir($mydir)) {
                                $mysubdirs = split('/', $mydir);
                                $mybasedir = '/';
                                foreach ($mysubdirs as $mysubdir) {
                                    if (!empty($mysubdir)) {
                                        $mybasedir = $mybasedir.$mysubdir.'/';
                                        if (!is_dir($mybasedir)) {
                                            @mkdir($mybasedir, api_get_permissions_for_new_directories());
                                            if ($this->debug == 1) { error_log('New LP - Dir '.$mybasedir.' doesnt exist. Creating.', 0); }
                                        }
                                    }
                                }
                            }
                            @rename($course_sys_dir.$new_dir.$file, $course_sys_dir.$new_dir.$safe_file);
                            if ($this->debug == 1) { error_log('New LP - Renaming '.$course_sys_dir.$new_dir.$file.' to '.$course_sys_dir.$new_dir.$safe_file, 0); }
                        }
                        //set_default_settings($course_sys_dir, $safe_file, $filetype);
                    }
                }

                closedir($dir);
                chdir($saved_dir);
            }
        } else {
            return '';
        }
        return $course_sys_dir.$new_dir.$config_dir;
    }

    /**
     * Sets the proximity setting in the database
     * @param	string	Proximity setting
     */
    function set_proximity($proxy = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In aicc::set_proximity('.$proxy.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET content_local = '$proxy' WHERE c_id = ".$course_id." id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the theme setting in the database
     * @param	string	Theme setting
     */
    function set_theme($theme = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In aicc::set_theme('.$theme.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET theme = '$theme' WHERE c_id = ".$course_id." id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the image LP in the database
     * @param	string	Theme setting
     */
    function set_preview_image($preview_image = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {error_log('In aicc::set_preview_image('.$preview_image.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET preview_image = '$preview_image' WHERE c_id = ".$course_id." id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the Author LP in the database
     * @param	string	Theme setting
     */
    function set_author($author = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In aicc::set_author('.$author.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET author = '$author' WHERE c_id = ".$course_id." id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the content maker setting in the database
     * @param	string	Proximity setting
     */
    function set_maker($maker = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In aicc::set_maker method('.$maker.')', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET content_maker = '$maker' WHERE c_id = ".$course_id." id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Exports the current AICC object's files as a zip. Excerpts taken from learnpath_functions.inc.php::exportpath()
     * @param	integer	Learnpath ID (optional, taken from object context if not defined)
     */
    function export_zip($lp_id = null) {
        if ($this->debug > 0) { error_log('In aicc::export_zip method('.$lp_id.')', 0); }
        if (empty($lp_id)) {
            if (!is_object($this)) {
                return false;
            } else {
                $id = $this->get_id();
                if (empty($id)) {
                    return false;
                } else {
                    $lp_id = $this->get_id();
                }
            }
        }
        //error_log('New LP - in export_zip()', 0);
        // Zip everything that is in the corresponding scorm dir.
        // Write the zip file somewhere (might be too big to return).
        require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
        require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
        require_once api_get_path(LIBRARY_PATH).'document.lib.php';
        require_once api_get_path(LIBRARY_PATH).'pclzip/pclzip.lib.php';
        require_once 'learnpath_functions.inc.php';
        $course_id = api_get_course_int_id();
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
        $_course = Database::get_course_info(api_get_course_id());

        $sql = "SELECT * FROM $tbl_lp WHERE c_id = ".$course_id." id=".$lp_id;
        $result = Database::query($sql);
        $row = Database::fetch_array($result);
        $LPname = $row['path'];
        $list = split('/', $LPname);
        $LPnamesafe = $list[0];
        //$zipfoldername = '/tmp';
        //$zipfoldername = '../../courses/'.$_course['directory'].'/temp/'.$LPnamesafe;
        $zipfoldername = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/temp/'.$LPnamesafe;
        $scormfoldername = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/scorm/'.$LPnamesafe;
        $zipfilename = $zipfoldername.'/'.$LPnamesafe.'.zip';

        // Get a temporary dir for creating the zip file.

        //error_log('New LP - cleaning dir '.$zipfoldername, 0);
        deldir($zipfoldername); //make sure the temp dir is cleared
        mkdir($zipfoldername, api_get_permissions_for_new_directories());
        //error_log('New LP - made dir '.$zipfoldername, 0);

        // Create zipfile of given directory.
        $zip_folder = new PclZip($zipfilename);
        $zip_folder->create($scormfoldername.'/', PCLZIP_OPT_REMOVE_PATH, $scormfoldername.'/');

        //$zipfilename = '/var/www/dokeos-comp/courses/TEST2/scorm/example_document.html';
        //this file sending implies removing the default mime-type from php.ini
        //DocumentManager :: file_send_for_download($zipfilename, true, $LPnamesafe.".zip");
        DocumentManager :: file_send_for_download($zipfilename, true);

        // Delete the temporary zip file and directory in fileManage.lib.php
        my_delete($zipfilename);
        my_delete($zipfoldername);

        return true;
    }

    /**
     * Gets a resource's path if available, otherwise return empty string
     * @param	string	Resource ID as used in resource array
     * @return string	The resource's path as declared in config file course.crs
     */
    function get_res_path($id) {
        if ($this->debug > 0) { error_log('In aicc::get_res_path('.$id.') method', 0); }
        $path = '';
        if (isset($this->resources[$id])) {
            $oRes =& $this->resources[$id];
            $path = @$oRes->get_path();
        }
        return $path;
    }

    /**
     * Gets a resource's type if available, otherwise return empty string
     * @param	string	Resource ID as used in resource array
     * @return string	The resource's type as declared in the assignable unit (.au) file
     */
    function get_res_type($id) {
        if ($this->debug > 0) { error_log('In aicc::get_res_type('.$id.') method', 0); }
        $type = '';
        if (isset($this->resources[$id])) {
            $oRes =& $this->resources[$id];
            $temptype = $oRes->get_scorm_type();
            if (!empty($temptype)) {
                $type = $temptype;
            }
        }
        return $type;
    }

    /**
     * Gets the default organisation's title
     * @return	string	The organization's title
     */
    function get_title(){
        if ($this->debug > 0) { error_log('In aicc::get_title() method', 0); }
        $title = '';
        if (isset($this->config['organizations']['default'])) {
            $title = $this->organizations[$this->config['organizations']['default']]->get_name();
        } elseif (count($this->organizations) == 1) {
            // This will only get one title but so we don't need to know the index.
            foreach ($this->organizations as $id => $value) {
                $title = $this->organizations[$id]->get_name();
                break;
            }
        }
        return $title;
    }

    /**
     * TODO: Implement this function to restore items data from a set of AICC config files,
     * updating the existing table... This will prove very useful in case initial data
     * from config files were not imported well enough.
     */
    function reimport_aicc() {
        if ($this->debug > 0) { error_log('In aicc::reimport_aicc() method', 0); }
        //query current items list
        //get the identifiers
        //parse the config files
        //match both
        //update DB accordingly
        return true;
    }

    /**
     * Static function to parse AICC ini files.
     * Based on work by sinedeo at gmail dot com published on php.net (parse_ini_file()).
     * @param	string	File path
     * @return	array	Structured array
     */
    function parse_ini_file_quotes_safe($f) {
        $null = '';
        $r = $null;
        $sec = $null;
        $f = @file_get_contents($f);
        $f = api_convert_encoding($f, api_get_system_encoding(), $this->config_encoding);
        $f = preg_split('/\r?\n/', $f);
        for ($i = 0; $i < @count($f); $i++) {
            $newsec = 0;
            $w = @trim($f[$i]);
            if ($w) {
                if ((!$r) or ($sec)) {
                    if ((@substr($w, 0, 1) == '[') and (@substr($w, -1, 1)) == ']') {
                        $sec = @substr($w, 1, @strlen($w) - 2);
                        $newsec = 1;
                    }
                }
                if (!$newsec) {
                    $w = @explode('=', $w);
                    $k = @trim($w[0]);
                    unset($w[0]);
                    $v = @trim(@implode('=', $w));
                    if ((@substr($v, 0, 1) == "\"") and (@substr($v, -1, 1) == "\"")) {
                        $v = @substr($v, 1, @strlen($v) - 2);
                    }
                    if ($sec) {
                        if (api_strtolower($sec) == 'course_description') { // A special case.
                            $r[api_strtolower($sec)] = $k;
                        } else {
                            $r[api_strtolower($sec)][api_strtolower($k)] = $v;
                        }
                    } else {
                        $r[api_strtolower($k)] = $v;
                    }
                }
            }
        }
        return $r;
    }

    /**
     * Static function to parse AICC ini strings.
     * Based on work by sinedeo at gmail dot com published on php.net (parse_ini_file()).
     * @param		string	INI File string
     * @param		array	List of names of sections that should be considered as containing only hard string data (no variables), provided in lower case
     * @return	array	Structured array
     */
    function parse_ini_string_quotes_safe($s, $pure_strings = array()) {
        $null = '';
        $r = $null;
        $sec = $null;
        $s = api_convert_encoding($s, api_get_system_encoding(), $this->config_encoding);
        //$f = split("\r\n", $s);
        $f = preg_split('/\r?\n/', $s);
        for ($i = 0; $i < @count($f); $i++) {
            $newsec = 0;
            $w = @trim($f[$i]);
            if ($w) {
                if ((!$r) or ($sec)) {
                    if ((@substr($w, 0, 1) == '[') and (@substr($w, -1, 1)) == ']') {
                        $sec = @substr($w, 1, @strlen($w) - 2);
                        $pure_data = 0;
                        if (in_array(api_strtolower($sec), $pure_strings)) {
                            // This section can only be considered as pure string data (until the next section).
                            $pure_data = 1;
                            $r[api_strtolower($sec)] = '';
                        }
                        $newsec = 1;
                    }
                }
                if (!$newsec) {
                    $w = @explode('=', $w);
                    $k = @trim($w[0]);
                    unset($w[0]);
                    $v = @trim(@implode('=', $w));
                    if ((@substr($v, 0, 1) == "\"") and (@substr($v, -1, 1) == "\"")) {
                        $v = @substr($v, 1, @strlen($v) - 2);
                    }
                    if ($sec) {
                        if ($pure_data) {
                            $r[api_strtolower($sec)] .= $f[$i];
                        } else {
                            if (api_strtolower($sec) == 'course_description') { // A special case.
                                $r[api_strtolower($sec)] = $k;
                            } else {
                                $r[api_strtolower($sec)][api_strtolower($k)] = $v;
                            }
                        }
                    } else {
                        $r[api_strtolower($k)] = $v;
                    }
                }
            }
        }
        return $r;
    }

    /**
     * Static function that parses CSV files into simple arrays, based on a function
     * by spam at cyber-space dot nl published on php.net (fgetcsv()).
     * @param	string	Filepath
     * @param	string	CSV delimiter
     * @param	string	CSV enclosure
     * @param	boolean	Might one field name happen more than once on the same line? (then split by comma in the values)
     * @return array	Simple structured array
     */
    function parse_csv_file($f, $delim = ',', $enclosure = '"', $multiples = false) {
        $data = @file_get_contents($f);
        $data = api_convert_encoding($data, api_get_system_encoding(), $this->config_encoding);
        $enclosed = false;
        $fldcount = 0;
        $linecount = 0;
        $fldval = '';
        for ($i = 0; $i < strlen($data); $i++) {
            $chr = $data{$i};
            switch ($chr) {
                case $enclosure:
                    if ($enclosed && $data{$i+1} == $enclosure) {
                        $fldval .= $chr;
                        ++$i; // Skip the next character.
                    } else
                        $enclosed = !$enclosed;
                    break;
                case $delim:
                    if (!$enclosed) {
                        $ret_array[$linecount][$fldcount++] = $fldval;
                        $fldval = '';
                    } else
                        $fldval .= $chr;
                    break;
                case "\r":
                    if (!$enclosed&&$data{$i+1} == "\n")
                        continue;
                case "\n":
                    if (!$enclosed) {
                        $ret_array[$linecount++][$fldcount] = $fldval;
                        $fldcount = 0;
                        $fldval = '';
                    } else
                        $fldval .= $chr;
                    break;
                case "\\r":
                    if (!$enclosed&&$data{$i+1} == "\\n")
                        continue;
                case "\\n":
                    if (!$enclosed) {
                        $ret_array[$linecount++][$fldcount] = $fldval;
                        $fldcount = 0;
                        $fldval = '';
                    } else
                        $fldval .= $chr;
                    break;
                default:
                    $fldval .= $chr;
            }
        }
        if ($fldval) {
            $ret_array[$linecount][$fldcount] = $fldval;
        }
        // Transform the array to use the first line as titles.
        $titles = array();
        $ret_ret_array = array();
        foreach ($ret_array as $line_idx => $line) {
            if ($line_idx == 0) {
                $titles = $line;
            } else {
                $ret_ret_array[$line_idx] = array();
                foreach ($line as $idx => $val) {
                    if ($multiples && !empty($ret_ret_array[$line_idx][api_strtolower($titles[$idx])])) {
                        $ret_ret_array[$line_idx][api_strtolower($titles[$idx])] .= ','.$val;
                    } else {
                        $ret_ret_array[$line_idx][api_strtolower($titles[$idx])] = $val;
                    }
                }
            }
        }
        return $ret_ret_array;
    }
}
aiccItem.class.php000064400000014375152003363470010114 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the aiccItem class that deals with AICC Assignable Units (AUs)
 * @package	chamilo.learnpath
 * @author	Yannick Warnier	<ywarnier@beeznest.org>
 * @license	GNU/GPL
 */
/**
 * Code
 */
require_once 'learnpathItem.class.php';
/**
 * This class handles the elements from an AICC Descriptor file.
 * @package	chamilo.learnpath
 */
class aiccItem extends learnpathItem {
    public $identifier = ''; // AICC AU's system_id
    public $identifierref = '';
    public $parameters = ''; // AICC AU's web_launch
    public $title = ''; // no AICC equivalent
    public $sub_items = array(); // AICC elements (des)
    //public $prerequisites = ''; // defined in learnpathItem.class.php
    //public $max_score = ''; // defined in learnpathItem
    //public $path = ''; // defined in learnpathItem
    public $maxtimeallowed = '00:00:00'; // AICC AU's max_time_allowed
    public $timelimitaction = ''; // AICC AU's time_limit_action
    public $masteryscore = ''; // AICC AU's mastery_score
    public $core_vendor = ''; // AICC AU's core_vendor
    public $system_vendor = ''; // AICC AU's system_vendor
    public $au_type = ''; // AICC AU's type
    public $command_line = ''; // AICC AU's command_line
    public $debug = 0;

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormItem
     * object from database records or from the array given as second parameter
     * @param	string	Type of construction needed ('db' or 'config', default = 'config')
     * @param	mixed	Depending on the type given, DB id for the lp_item or parameters array
     */
    public function aiccItem($type = 'config', $params = array(), $course_id = null) {
        if (isset($params)) {
            switch ($type) {
                case 'db':
                    parent::__construct($params, api_get_user_id(), $course_id);
                    $this->aicc_contact = false;
                    //TODO: Implement this way of metadata object creation.
                    return false;
                case 'config': // Do the same as the default.
                default:
                     //if($first_item->type == XML_ELEMENT_NODE) this is already check prior to the call to this function
                     foreach ($params as $a => $value) {
                         switch ($a) {
                            case 'system_id':
                                $this->identifier = Database::escape_string(strtolower($value));
                                break;
                            case 'type':
                                $this->au_type = Database::escape_string($value);
                                break;
                            case 'command_line':
                                $this->command_line = Database::escape_string($value);
                                break;
                            case 'max_time_allowed':
                                $this->maxtimeallowed = Database::escape_string($value);
                                break;
                            case 'time_limit_action':
                                $this->timelimitaction = Database::escape_string($value);
                                break;
                            case 'max_score':
                                $this->max_score = Database::escape_string($value);
                                break;
                            case 'core_vendor':
                                $this->core_vendor = Database::escape_string($value);
                                break;
                            case 'system_vendor':
                                $this->system_vendor = Database::escape_string($value);
                                break;
                            case 'file_name':
                                $this->path = Database::escape_string($value);
                                break;
                            case 'mastery_score':
                                $this->masteryscore = Database::escape_string($value);
                                break;
                            case 'web_launch':
                                $this->parameters = Database::escape_string($value);
                                break;
                         }
                     }
                    return true;
            }
        }
        return false;
    }

    /**
     * Builds a flat list with the current item and calls itself recursively on all children
     * @param	array	Reference to the array to complete with the current item
     * @param	integer	Optional absolute order (pointer) of the item in this learning path
     * @param	integer	Optional relative order of the item at this level
     * @param	integer	Optional level. If not given, assumes it's level 0
     */
    function get_flat_list(&$list, &$abs_order, $rel_order = 1, $level = 0) {
        $list[] = array(
            'au_type' => $this->au_type,
            'command_line' => $this->command_line,
            'core_vendor' => $this->core_vendor,
            'identifier' => $this->identifier,
            'identifierref' => $this->identifierref,
            'masteryscore' => $this->masteryscore,
            'maxtimeallowed' => $this->maxtimeallowed,
            'level' => $level,
            'parameters' => $this->parameters,
            'prerequisites' => (!empty($this->prereq_string) ? $this->prereq_string : ''),
            'timelimitaction' => $this->timelimitaction,
        );
        $abs_order++;
        $i = 1;
        foreach ($this->sub_items as $id => $dummy) {
            $oSubitem =& $this->sub_items[$id];
            $oSubitem->get_flat_list($list, $abs_order, $i, $level + 1);
            $i++;
        }
    }

    /**
     * Save function. Uses the parent save function and adds a layer for AICC.
     * @param	boolean	Save from URL params (1) or from object attributes (0)
     */
    function save($from_outside = true, $prereqs_complete = false) {
        parent::save($from_outside, $prereqs_complete = false);
        // Under certain conditions, the scorm_contact should not be set, because no scorm signal was sent.
        $this->aicc_contact = true;
        if (!$this->aicc_contact) {
            //error_log('New LP - was expecting SCORM message but none received', 0);
        }
    }
}
aiccObjective.class.php000064400000004121152003363470011114 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the aiccResource class that deals with elemens from AICC Objectives file
 * @package	chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 * @license	GNU/GPL
 */

/**
 * Class defining the Block elements in an AICC Course Structure file.
 */
require_once 'learnpathItem.class.php';
class aiccObjective extends learnpathItem {
    public $identifier = '';
    public $members = array();

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormResource
     * object from database records or from the array given as second param
     * @param	string	Type of construction needed ('db' or 'config', default = 'config')
     * @param	mixed	Depending on the type given, DB id for the lp_item or parameters array
     */
    function aiccObjective($type = 'config', $params) {

        if (isset($params)) {
            switch ($type) {
                case 'db':
                    // TODO: Implement this way of object creation.
                    return false;
                case 'config': // Do the same as the default.
                default:
                     foreach ($params as $a => $value) {
                         switch ($a) {
                                case 'system_id':
                                    $this->identifier = strtolower($value);
                                 break;
                             case 'member':
                                 if (strstr($value, ',') !== false) {
                                     $temp = split(',', $value);
                                     foreach ($temp as $val) {
                                         if (!empty($val)) {
                                             $this->members[] = $val;
                                         }
                                     }
                                 }
                                 break;
                         }
                     }
                    return true;
            }
        }
        return false;
     }
}
aiccResource.class.php000064400000003705152003363470011000 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the aiccResource class that deals with elemens from AICC Descriptor file
 * @package	chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 * @license	GNU/GPL
 */

/**
 * Class defining the elements from an AICC Descriptor file.
 */
class aiccResource {
    public $identifier = '';
    public $title = '';
    public $description = '';
    public $developer_id = '';

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormResource
     * object from database records or from the array given as second param
     * @param	string	Type of construction needed ('db' or 'config', default = 'config')
     * @param	mixed	Depending on the type given, DB id for the lp_item or parameters array
     */
    public function aiccResource($type = 'config', $params) {

        if (isset($params)) {
            switch ($type) {
                case 'db':
                    // TODO: Implement this way of object creation.
                    return false;
                case 'config': // Do the same as the default.
                default:
                     foreach ($params as $a => $value) {
                         switch ($a) {
                                case 'system_id':
                                    $this->identifier = strtolower($value);
                                 break;
                             case 'title':
                                 $this->title = $value;
                             case 'description':
                                 $this->description = $value;
                                 break;
                             case 'developer_id':
                                 $this->developer_id = $value;
                                 break;
                         }
                     }
                    return true;
            }
        }
        return false;
     }
}
aicc_api.php000064400000066501152003363470007020 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 *	API event handler functions for AICC / CMIv4 in API communication mode
 *
 *	@author   Denes Nagy <darkden@freemail.hu>
 *  @author   Yannick Warnier <ywarnier@beeznest.org>
 *	@version  v 1.0
 *	@access   public
 *	@package  chamilo.learnpath
 * 	@license	GNU/GPL
 */

/**
 * This script is divided into three sections.
 * The first section (below) is the initialisation part.
 * The second section is the AICC object part
 * The third section defines the event handlers for Chamilo's internal messaging
 * and frames refresh
 *
 * This script implements the API messaging for AICC. The HACP messaging is
 * made by another set of scripts.
 */

/* INIT SECTION */

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Load common libraries using a compatibility script to bridge between 1.6 and 1.8.
require_once 'back_compat.inc.php';
// Load learning path libraries so we can use the objects to define the initial values of the API.
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';
require_once 'aicc.class.php';

// Is this needed? This is probabaly done in the header file.
$file							= $_SESSION['file'];
$oLP							= unserialize($_SESSION['lpobject']);
$oItem 							= $oLP->items[$oLP->current];
if (!is_object($oItem)) {
    error_log('New LP - scorm_api - Could not load oItem item', 0);
    exit;
}
$autocomplete_when_80pct = 0;

/* JavaScript Functions */

?>var scorm_logs=<?php echo (empty($oLP->scorm_debug) ? '0' : '3'); ?>; //debug log level for SCORM. 0 = none, 1=light, 2=a lot, 3=all - displays logs in log frame
var lms_logs=0; //debug log level for LMS actions. 0=none, 1=light, 2=a lot, 3=all
//logit_lms('scormfunctions.php included',0);

function APIobject() {
  this.LMSInitialize=LMSInitialize;
  this.LMSGetValue=LMSGetValue;
  this.LMSSetValue=LMSSetValue;
  this.LMSCommit=LMSCommit;
  this.LMSFinish=LMSFinish;
  this.LMSGetLastError=LMSGetLastError;
  this.LMSGetErrorString=LMSGetErrorString;
  this.LMSGetDiagnostic=LMSGetDiagnostic;
}

// It is not sure that the scos use the above declarations.

API = new APIobject(); //for scorm 1.2

var G_NoError = 0;
var G_GeneralException = 101;
var G_ServerBusy = 102;
var G_InvalidArgumentError = 201;
var G_ElementCannotHaveChildren = 202;
var G_ElementIsNotAnArray = 203;
var G_NotInitialized = 301;
var G_NotImplementedError = 401;
var G_InvalidSetValue = 402;
var G_ElementIsReadOnly = 403;
var G_ElementIsWriteOnly = 404;
var G_IncorrectDataType = 405;

var G_LastError = G_NoError ;

var commit = false ;

// Strictly SCORM variables.
var score=<?php echo $oItem->get_score();?>;
var max=<?php echo $oItem->get_max();?>;
var min=<?php echo $oItem->get_min();?>;
var lesson_status='<?php echo $oItem->get_status();?>';
var session_time='<?php echo $oItem->get_scorm_time('js');?>';
var suspend_data = '<?php echo $oItem->get_suspend_data();?>';
var lesson_location = '<?php echo $oItem->get_lesson_location();?>';
var total_time = '<?php echo $oItem->get_scorm_time('js');?>';

// Chamilo internal variables.
var saved_lesson_status = 'not attempted';
var lms_lp_id = <?php echo $oLP->get_id();?>;
var lms_item_id = <?php echo $oItem->get_id();?>;
//var lms_new_item_id = 0; //temporary value (only there between a load_item() and a LMSInitialize())
var lms_been_synchronized = 0;
var lms_initialized = 0;
var lms_total_lessons = <?php echo $oLP->get_total_items_count(); ?>;
var lms_complete_lessons = <?php echo $oLP->get_complete_items_count();?>;
var lms_progress_bar_mode = '<?php echo $oLP->progress_bar_mode;?>';
if(lms_progress_bar_mode == ''){lms_progress_bar_mode='%';}
var lms_view_id = '<?php echo $oLP->get_view();?>';
if(lms_view_id == ''){ lms_view_id = 1;}
var lms_user_id = '<?php echo $_user['user_id'];?>';
var lms_next_item = '<?php echo $oLP->get_next_item_id();?>';
var lms_previous_item = '<?php echo $oLP->get_previous_item_id();?>';
var lms_lp_type = '<?php echo $oLP->get_type();?>';
var lms_item_type = '<?php echo $oItem->get_type();?>';

// Backup for old values.
var old_score = 0;
var old_max = 0;
var old_min = 0;
var old_lesson_status = '';
var old_session_time = '';
var old_suspend_data = '';
var lms_old_item_id = 0;

function LMSInitialize() {  //this is the initialize function of all APIobjects
    logit_scorm('LMSInitialise()',0);
    lms_initialized=1;
    return('true');
}
function LMSGetValue(param) {
    //logit_scorm("LMSGetValue('"+param+"')",1);
    var result='';
    if(param=='cmi.core._children' || param=='cmi.core_children'){
        result='entry, exit, lesson_status, student_id, student_name, lesson_location, total_time, credit, lesson_mode, score, session_time';
    }else if(param == 'cmi.core.entry'){
        result='';
    }else if(param == 'cmi.core.exit'){
        result='';
    }else if(param == 'cmi.core.lesson_status'){
        if(lesson_status != '') {
            result=lesson_status;
        }
        else{
            result='not attempted';
        }
    }else if(param == 'cmi.core.student_id'){
        result='<?php echo $_user['user_id']; ?>';
    }else if(param == 'cmi.core.student_name'){
          <?php
            $who = addslashes(api_get_person_name($_user['firstName'], $_user['lastName']));
            echo "result='$who';";
          ?>
    }else if(param == 'cmi.core.lesson_location'){
        result=lesson_location;
    }else if(param == 'cmi.core.total_time'){
        result=total_time;
    }else if(param == 'cmi.core.score._children'){
        result='raw,min,max';
    }else if(param == 'cmi.core.score.raw'){
        result=score;
    }else if(param == 'cmi.core.score.max'){
        result=max;
    }else if(param == 'cmi.core.score.min'){
        result=min;
    }else if(param == 'cmi.core.score'){
        result=score;
    }else if(param == 'cmi.core.credit'){
        result='no-credit';
    }else if(param == 'cmi.core.lesson_mode'){
        result='normal';
    }else if(param == 'cmi.suspend_data'){
        result='<?php echo $oItem->get_suspend_data();?>';
    }else if(param == 'cmi.launch_data'){
        result='';
    }else if(param == 'cmi.objectives._count'){
        result='<?php echo $oItem->get_view_count();?>';
    }
    /*
    // Switch not working??? WTF???
    switch(param) {
        case 'cmi.core._children'		:
            result='entry, exit, lesson_status, student_id, student_name, lesson_location, total_time, credit, lesson_mode, score, session_time';
            break;
        case 'cmi.core_children'		:
            result='entry, exit, lesson_status, student_id, student_name, lesson_location, total_time, credit, lesson_mode, score, session_time';
            break;
        case 'cmi.core.entry'			:
            result='';
            break;
        case 'cmi.core.exit'			:
            result='';
            break;
        case 'cmi.core.lesson_status'	:
            if(lesson_status != '') {
                result=lesson_status;
            }
            else{
                result='not attempted';
            }
            break;
        case 'cmi.core.student_id'	   :
            result='<?php echo $_user['user_id']; ?>';
            break;
        case 'cmi.core.student_name'	:
          <?php
            $who = addslashes(api_get_person_name($_user['firstName'], $_user['lastName']));
            echo "result='$who';";
          ?>	break;
        case 'cmi.core.lesson_location'	:
            result='';
            break;
        case 'cmi.core.total_time'	:
            result=total_time;
            break;
        case 'cmi.core.score._children'	:
            result='raw,min,max';
            break;
        case 'cmi.core.score.raw'	:
            result=score;
            break;
        case 'cmi.core.score.max'	:
            result=max;
            break;
        case 'cmi.core.score.min'	:
            result=min;
            break;
        case 'cmi.core.score'		:
            result=score;
            break;
        case 'cmi.core.credit'		:
            result='no-credit';
            break;
        case 'cmi.core.lesson_mode'	:
            result='normal';
            break;
        case 'cmi.suspend_data'		:
            result='<?php echo $oItem->get_suspend_data();?>';
            break;
        case 'cmi.launch_data'		:
            result='';
            break;
        case 'cmi.objectives._count':
            result='<?php echo $oItem->get_view_count();?>';
            break;
        default 					:
            result='';
            break;
    }
    */
    logit_scorm("LMSGetValue('"+param+"') returned '"+result+"'",1);
    return result;
}
function LMSSetValue(param, val) {
    logit_scorm("LMSSetValue('"+param+"','"+val+"')",0);
    switch(param) {
    case 'cmi.core.score.raw'		: score= val ;			break;
    case 'cmi.core.score.max'		: max = val;			break;
    case 'cmi.core.score.min'		: min = val;			break;
    case 'cmi.core.lesson_location' : lesson_location = val;break;
    case 'cmi.core.lesson_status'	:
        saved_lesson_status = lesson_status;
        lesson_status = val;
        <?php if ($oLP->mode != 'fullscreen') { ?>
        //var update = update_toc(lesson_status,lms_item_id);
        <?php } ?>
        break;
    case 'cmi.completion_status'	: lesson_status = val;	break; //1.3
    case 'cmi.core.session_time'	: session_time = val;	break;
    case 'cmi.score.scaled'			: score = val ;			break; //1.3
    case 'cmi.success_status'		: success_status = val; break; //1.3
    case 'cmi.suspend_data'			: suspend_data = val;   break;
    }
    //var update = update_toc();
    //var update_progress = update_progress_bar();
    <?php
    if ($oLP->force_commit == 1) {
        echo "	var mycommit = LMSCommit('force');";
    }
    ?>
    return(true);
}
function savedata(origin) { //origin can be 'commit', 'finish' or 'terminate'
    <?php if ($autocomplete_when_80pct) { ?>
    if( ( lesson_status == 'incomplete') && (score >= (0.8*max) ) ){
      lesson_status = 'completed';
    }
    <?php }?>
    param = 'id='+lms_item_id+'&origin='+origin+'&score='+score+'&max='+max+'&min='+min+'&lesson_status='+lesson_status+'&time='+session_time+'&suspend_data='+suspend_data;

    url="http://<?php
    $self = api_get_self();
    $url = $_SERVER['HTTP_HOST'].$self;
    $url = substr($url, 0, -14); // 14 is the length of this file's name (/scorm_api.php).
    echo $url;
    ?>/lp_controller.php?cidReq=<?php echo api_get_course_id();?>&action=save&lp_id=<?php echo $oLP->get_id();?>&" + param + "";
    logit_lms('saving data (status='+lesson_status+')',1);
    xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location);
    //xajax_update_pgs();
    //xajax_update_toc();
}

function LMSCommit(val) {
    logit_scorm('LMSCommit()',0);
    commit = true ;
    savedata('commit');
    return('true');
}
function LMSFinish(val) {
    if ( !commit ) {
        logit_scorm('LMSFinish() (no LMSCommit())',1);
    }
    if ( commit ) {
        logit_scorm('LMSFinish() called',1);
        savedata('finish');
    }
    return('true');
}
function LMSGetLastError() {
    logit_scorm('LMSGetLastError()',1);
    return(G_LastError);
}
function LMSGetErrorString(errCode){
    logit_scorm('LMSGetErrorString()',1);
    return('No error !');
}
function LMSGetDiagnostic(errCode){
    logit_scorm('LMSGetDiagnostic()',1);
    return(API.LMSGetLastError());
}
<?php
/**
 * Chamilo-specific code that deals with event handling and inter-frames
 * messaging/refreshing.
 * Note that from now on, the Chamilo JS code in this library will act as
 * a controller, of the MVC pattern, and receive all requests for frame
 * updates, then redispatch to any frame concerned.
 */
?>
/**
 * Defining the AJAX-object class to be made available from other frames.
 */
function XAJAXobject() {
  this.xajax_switch_item_details=xajax_switch_item_details;
  this.switch_item=switch_item;
}

//it is not sure that the scos use the above declarations

oXAJAX = new XAJAXobject();
oxajax = new XAJAXobject();

/**
 * Cross-browser event handling by Scott Andrew
 * @param	element	Element that needs an event attached
 * @param   string	Event type (load, unload, click, keyDown, ...)
 * @param   string	Function name (the event handler)
 * @param   string	used in addEventListener
 */
function addEvent(elm, evType, fn, useCapture){
    if(elm.addEventListener){
        elm.addEventListener(evType, fn, useCapture);
        return true;
    }else if (elm.attachEvent){
        var r = elm.attachEvent('on' + evType, fn);
    }else{
        elm['on'+evType] = fn;
    }
}
/**
 * Add listeners to the page objects. This has to be defined for
 * the current context as it acts on objects that should exist
 * on the page
 */
function addListeners(){
    //exit if the browser doesn't support ID or tag retrieval
    logit_lms('Entering addListeners()',2);
    if(!document.getElementsByTagName){
        logit_lms("getElementsByTagName not available",2);
        return;
    }
    if(!document.getElementById){
        logit_lms("getElementById not available",2);
        return;
    }
    //assign event handlers to objects
    if(lms_lp_type==1 || lms_item_type=='asset'){
        logit_lms('Dokeos LP or asset',2);
        // If this path is a Chamilo learnpath, then start manual save
        // when something is loaded in there.
        var myelem = document.getElementById('content_id');
        if(!myelem){logit_lms("Impossible to find content_id element in document",2);}
        addEvent(myelem,'unload',chamilo_save_asset,false);
        logit_lms('Added event listener on content_id for unload',2);
    }
    logit_lms('Quitting addListeners()',2);
}
/**
 * Load an item into the content frame:
 * - making sure the previous item status have been saved
 * - first updating the current item ID (to save the right item)
 * - updating the frame src
 */
function load_item(item_id,url){
    if(document.getElementById('content_id'))
    {
        logit_lms('Loading item '+item_id,2);
        var cont_f = document.getElementById('content_id');
        if(cont_f.src){
            lms_old_item_id = lms_item_id;
            var lms_new_item_id = item_id;
            //load new content page into content frame
            if(lms_lp_type==1 || lms_item_type=='asset'){
                chamilo_save_asset();
            }
            cont_f.src = url;
            update_toc('unhighlight',lms_old_item_id);
            update_toc('highlight',item_id);
            /* legacy code
            lms_been_synchronized = 0;
            lms_initialized = 0;
            if(lms_lp_type==1 || lms_item_type=='asset'){
                lms_item_id = lms_new_item_id;
            }*/
            return true;
        }
        logit_lms('cont_f.src has no properties',0);
    }
    logit_lms('content_id has no properties',0);
    return false;
}
/**
 * Save a Chamilo learnpath item's time and mark as completed upon
 * leaving it
 */
function chamilo_save_asset(){
    //var linkparams = 'id='+lms_item_id+'&score='+score+'&max='+max+'&min='+min+'&lesson_status='+lesson_status+'&time='+session_time+'&suspend_data='+suspend_data;
    //var url = "<?php echo api_get_path(WEB_CODE_PATH).'newscorm/lp_controller.php'; ?>?action=save&" + linkparams + "";
    logit_lms('chamilo_save_asset: '+url,0);
    //frames["message_name"].src = url;
    xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location);
}
/**
 * Logs information about SCORM messages into the log frame
 * @param	string	Message to log
 * @param	integer Priority (0 for top priority, 3 for lowest)
 */
function logit_scorm(message,priority) {
    if (scorm_logs) {
        log_in_log("SCORM: " + message);
    }
    return false;

    /*if(frames["lp_log_name"] && scorm_logs>priority){
        frames["lp_log_name"].document.getElementById("log_content").innerHTML += "AICC: " + message + "<br/>";
    }*/
}

function log_in_log(message) {
    var ua = $.browser;
    if (ua.mozilla) {
        console.log(message);
    } else {
        if (window.console) {
            window.console.log(message);
        }
    }
}

/**
 * Logs information about LMS activity into the log frame
 * @param	string	Message to log
 * @param	integer Priority (0 for top priority, 3 for lowest)
 */
function logit_lms(message,priority) {
    /*
    if(frames["lp_log_name"] && lms_logs>priority){
        frames["lp_log_name"].document.getElementById("log_content").innerHTML += "LMS: " + message + "<br/>";
    }*/

    if (scorm_logs) {
        log_in_log("LMS: " + message);
    }
}

/**
 * update the Table Of Contents frame, by changing CSS styles, mostly
 * @param	string	Action to be taken
 * @param	integer	Item id to update
 */
function update_toc(update_action,update_id)
{
    <?php if ($oLP->mode != 'fullscreen') { ?>
        var myframe = frames["toc_name"];
        var myelem = myframe.document.getElementById("toc_"+update_id);
        var myelemimg = myframe.document.getElementById("toc_img_"+update_id);
        logit_lms('update_toc('+update_action+','+update_id+')',2);
        if(update_id != 0){
            switch(update_action){
                case 'unhighlight':
                    myelem.className = "scorm_item";
                    break;
                case 'highlight':
                    myelem.className = "scorm_item_highlight";
                    break;
                case 'not attempted':
                    if(myelemimg.src != '../img/notattempted.gif'){
                        myelemimg.src = "../img/notattempted.gif";
                        myelemimg.alt = "not attempted";
                    }
                    break;
                case 'incomplete':
                    if(myelemimg.src != '../img/incomplete.png'){
                        myelemimg.src = "../img/incomplete.png";
                        myelemimg.alt = "incomplete";
                    }
                    break;
                case 'completed':
                    if(myelemimg.src != '../img/completed.png'){
                        myelemimg.src = "../img/completed.png";
                        myelemimg.alt = "completed";
                    }
                    break;
                case 'failed':
                    if(myelemimg.src != '../img/failed.png'){
                        myelemimg.src = "../img/failed.png";
                        myelemimg.alt = "failed";
                    }
                    break;
                case 'passed':
                    if(myelemimg.src != '../img/completed.png' && myelemimg.alt != 'passed'){
                        myelemimg.src = "../img/completed.png";
                        myelemimg.alt = "passed";
                    }
                    break;
                case 'browsed':
                    if(myelemimg.src != '../img/completed.png' && myelemimg.alt != 'browsed'){
                        myelemimg.src = "../img/completed.png";
                        myelemimg.alt = "browsed";
                    }
                    break;
                default:
                    logit_lms('Update action unknown',2);
                    break;
            }
        }
        return true;
    <?php } ?>
    return true;
}
/**
 * Updates the progress bar with the new status. Prevents the need of a page refresh and flickering
 * @param	integer	Number of completed items
 * @param	integer	Number of items in total
 * @param	string  Display mode (absolute 'abs' or percentage '%').Defaults to %
 */
function update_progress_bar(nbr_complete, nbr_total, mode)
{
    logit_lms('update_progress_bar('+nbr_complete+','+nbr_total+','+mode+')',2);
    logit_lms('could update with data: '+lms_lp_id+','+lms_view_id+','+lms_user_id,2);
    var myframe = frames["nav_name"];
    if(myframe){
        if(mode == ''){mode='%';}
        if(nbr_total == 0){nbr_total=1;}
        var percentage = (nbr_complete/nbr_total)*100;
        percentage = Math.round(percentage);

        var progress_bar  = $("#progress_bar_value");
        progress_bar.css('width', percentage +"%");

    /*
        var pr_text  = myframe.document.getElementById('progress_text');
        var pr_full  = myframe.document.getElementById('progress_img_full');

        var pr_empty = myframe.document.getElementById('progress_img_empty');
        pr_full.width = percentage;
        pr_empty.width = 100-percentage;
        */

        var mytext = '';
        switch(mode){
            case 'abs':
                mytext = nbr_complete + '/' + nbr_total;
                break;
            case '%':
            default:
                mytext = percentage + '%';
                break;
        }
        pr_text.innerHTML = mytext;
    }
    return true;
}
function update_stats_page()
{
    var myframe = document.getElementById('content_id');
    var mysrc = myframe.location.href;
    if(mysrc == 'lp_controller.php?action=stats'){
        if(myframe && myframe.src){
            var mysrc = myframe.src;
            myframe.src = mysrc;
        }
        // = mysrc; //refresh page
    }
    return true;
}
/**
 * Updates the message frame with the given string
 */
function update_message_frame(msg_msg)
{
    if(msg_msg==null){msg_msg='';}
    var msg_f = frames["message_name"];
    if(!msg_f.document || !msg_f.document.getElementById('msg_div_id')){
        logit_lms('In update_message_frame() - message frame has no document property',0);
    }else{
        logit_lms('In update_message_frame() - updating frame',0);
        msg_f.document.getElementById('msg_div_id').innerHTML= msg_msg;
    }
}
/**
 * Function that handles the saving of an item and switching from an item to another.
 * Once called, this function should be able to do the whole process of (1) saving the
 * current item, (2) refresh all the values inside the SCORM API object, (3) open the
 * new item into the content_id frame, (4) refresh the table of contents, (5) refresh
 * the progress bar (completion), (6) refresh the message frame
 * @param	integer		Chamilo ID for the current item
 * @param	string		This parameter can be a string specifying the next
 *						item (like 'next', 'previous', 'first' or 'last') or the id to the next item
 */
function switch_item(current_item, next_item){
    /*
    if(!current_item){
        logit_lms('In switch - no current_item defined',0);
    }
    if(!next_item){
        logit_lms('In switch - no next_item defined',0);
    }
    */
    if(lms_item_id == next_item){
        return; //nothing to switch
    }
    //(1) save the current item
    logit_lms('Called switch_item with params '+lms_item_id+' and '+next_item+'',0);
    xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location);

    //(2) Refresh all the values inside this SCORM API object - use AJAX
    xajax_switch_item_details(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,next_item);

    //(3) open the new item in the content_id frame
    var cont_f = document.getElementById('content_id');
    if(!cont_f){logit_lms('In switch - content frame not found',0);return false;}
    switch(next_item){
        case 'next':
            next_item = lms_next_item;
            break;
        case 'previous':
            next_item = lms_previous_item;
            break;
        default:
            break;
    }
    cont_f.src = 'lp_controller.php?action=content&lp_id='+lms_lp_id+'&item_id='+next_item;

    //(4) refresh table of contents
    /*
    var toc_f = document.getElementById('toc_id');
    if(!toc_f){logit_lms('In switch - toc frame not found',0);return false;}
    var myrefresh = toc_f.src;
    toc_f.src = myrefresh;
    */

    //(5) refresh the progress bar
    /*
    var prg_f = document.getElementById('nav_id');
    if(!prg_f){logit_lms('In switch - navigation frame not found',0);return false;}
    var myrefresh = prg_f.src;
    prg_f.src = myrefresh;
    */

    //(6) refresh the message box (included in switch_item_details)
    return true;
}
/**
 * Save a specific item (with its interactions, if any) into the LMS through
 * an AJAX call. Originally, we used the xajax library. Now we use jQuery.
 * Because of the need to pass an array, we have to build the parameters
 * manually into GET[]
 */
function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location, interactions, lms_item_core_exit) {
        params='';
        params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
        params += '&iid='+lms_item_id+'&s='+score+'&max='+max+'&min='+min;
        params += '&status='+lesson_status+'&t='+session_time;
        params += '&suspend='+suspend_data+'&loc='+lesson_location;
        params += '&core_exit='+lms_item_core_exit;
        interact_string = '';
        for (i in interactions){
            interact_string += '&interact['+i+']=';
            interact_temp = '[';
            for (j in interactions[i]) {
                interact_temp += interactions[i][j]+',';
            }
            interact_temp = interact_temp.substr(0,(interact_temp.length-2)) + ']';
            interact_string += encodeURIComponent(interact_temp);
        }
        //interact_string = encodeURIComponent(interact_string.substr(0,(interact_string.length-1)));
        params += interact_string;
        /*params = {
            'lid': lms_lp_id,
            'uid': lms_user_id,
            'vid': lms_view_id,
            'iid': lms_item_id,
            's': score,
            'max': max,
            'min': min,
            'status': lesson_status,
            't': session_time,
            'suspend': suspend_data,
            'loc': lesson_location,
            'interact': interac_string,
            'core_exit': lms_item_core_exit
        }
        */
        $.ajax({
            type:"POST",
            data: params,
            url: "lp_ajax_save_item.php",
            dataType: "script",
            async: false
            }
        );
}
/**
 * Starts the timer with the server clock time.
 * Originally, we used the xajax library. Now we use jQuery
 */
function xajax_start_timer() {
    $.ajax({
        type: "GET",
        url: "lp_ajax_start_timer.php",
        dataType: "script",
        async: false
    });
}
/**
 * Save a specific item's objectives into the LMS through
 * an AJAX call. Originally, we used the xajax library. Now we use jQuery
 */
function xajax_save_objectives(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,item_objectives) {
        params='';
        params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
        params += '&iid='+lms_item_id;
        obj_string = '';
        for (i in item_objectives){
            obj_string += '&objectives['+i+']=';
            obj_temp = '[';
            for (j in item_objectives[i]) {
                obj_temp += item_objectives[i][j]+',';
            }
            obj_temp = obj_temp.substr(0,(obj_temp.length-2)) + ']';
            obj_string += encodeURIComponent(obj_temp);
        }
        params += obj_string;
        $.ajax({
            type: "POST",
            data: params,
            url: "lp_ajax_save_objectives.php",
            dataType: "script",
            async: false
        });
}
/**
 * Switch between two items through
 * an AJAX call. Originally, we used the xajax library. Now we use jQuery
 */
function xajax_switch_item_details(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,next_item) {
    params = {
        'lid': lms_lp_id,
        'uid': lms_user_id,
        'vid': lms_view_id,
        'iid': lms_item_id,
        'next': next_item
    }
    $.ajax({
        type: "POST",
        data: params,
        url: "lp_ajax_switch_item.php",
        dataType: "script",
        async: false
    });
}
addEvent(window,'load',addListeners,false);
aicc_hacp.php000064400000024404152003363470007156 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 *	API event handler functions for AICC / CMIv4 in HACP communication mode
 *
 *	@author   Denes Nagy <darkden@freemail.hu>
 *  @author   Yannick Warnier <ywarnier@beeznest.org>
 *	@version  v 1.0
 *	@access   public
 *	@package  chamilo.learnpath
 * 	@license	GNU/GPL
 */

/**
 * This script is divided into three sections.
 * The first section (below) is the initialisation part.
 * The second section is the AICC object part
 * The third section defines the event handlers for Chamilo's internal messaging
 * and frames refresh
 *
 * This script implements the HACP messaging for AICC. The API messaging is
 * made by another set of scripts.
 *
 * Rules for HACP processing of one AU
 * Rule #1 The first HACP message issued must be a GetParam
 * Rule #2 The last HACP message issued must be an ExitAU
 * Rule #3 At least one PutParam message must be issued prior to an ExitAU message
 * Rule #4 No HACP messages can be issued after a successfully issued ExitAU message
 *
 * Only suspend_data and core.lesson_location should be sent updated to a late GetParam
 * request. All other params should be as when the AU was launched.
 */

/* INIT SECTION */

$debug = 0;

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Use session ID as provided by the request.
if (!empty($_REQUEST['aicc_sid'])) {
    session_id($_REQUEST['aicc_sid']);
    if ($debug > 1) { error_log('New LP - '.__FILE__.','.__LINE__.' - reusing session ID '.$_REQUEST['aicc_sid'], 0); }
}
//Load common libraries using a compatibility script to bridge between 1.6 and 1.8.
require_once 'back_compat.inc.php';
if ($debug > 2) { error_log('New LP - '.__FILE__.','.__LINE__.' - Current session ID: '.session_id(), 0); }
//Load learning path libraries so we can use the objects to define the initial values of the API.
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';
require_once 'aicc.class.php';

// Is this needed? This is probabaly done in the header file.
//$_user							= $_SESSION['_user'];
$file							= $_SESSION['file'];
$oLP							= unserialize($_SESSION['lpobject']);
$oItem 							=& $oLP->items[$oLP->current];
if (!is_object($oItem)) {
    error_log('New LP - aicc_hacp - Could not load oItem item', 0);
    exit;
}
$autocomplete_when_80pct = 0;

$result = array(
    'core' => array(),
    'core_lesson' => array(),
    'core_vendor' => array(),
    'evaluation' => array(),
    'student_data' => array(),
);
$convert_enc = array('%25','%0D','%0A','%09','%20','%2D','%2F','%3B','%3F','%7B','%7D','%7C','%5C','%5E','%7E','%5B','%5D','%60','%23','%3E','%3C','%22');
$convert_dec = array('%',"\r","\n","\t",' ','-','/',';','?','{','}','|','\\','^','~','[',']','`','#','>','<','"');
$crlf = "\r\n";
//$tab = "\t";
$tab = "";
$s_ec = 'error='; //string for error code
$s_et = 'error_text='; //string for error text
$s_ad = 'aicc_data='; //string for aicc_data

$errors = array(0 => 'Successful', 1 => 'Invalid Command', 2 => 'Invalid AU password', 3 => 'Invalid Session ID');

$error_code = 0;
$error_text = '';
$aicc_data = '';
$result = '';
// Get REQUEST
if (!empty($_REQUEST['command'])) {
    //error_log('In '.__FILE__.', '.__LINE__.' - request is '.$_REQUEST['command'], 0);
    switch (strtolower($_REQUEST['command'])) {
        case 'getparam':
            // Request for all available data to be printed out in the answer.
            if (!empty($_REQUEST['version'])) {
                $hacp_version = Database::escape_string($_REQUEST['version']);
            }
            if (!empty($_REQUEST['session_id'])) {
                $hacp_session_id = Database::escape_string($_REQUEST['session_id']);
            }
            $error_code = 0;
            $error_text = $errors[$error_code];
            //$result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad;
            $result .= '[Core]'.$crlf;
            $result .= $tab.'Student_ID='.$_user['user_id'].$crlf;
            $result .= $tab.'Student_Name='.api_get_person_name($_user['firstName'], $_user['lastName']).$crlf;
            $result .= $tab.'Lesson_Location='.$oItem->get_lesson_location().$crlf;
            $result .= $tab.'Credit='.$oItem->get_credit().$crlf;
            $result .= $tab.'Lesson_Status='.$oItem->get_status().$crlf;
            $result .= $tab.'Score='.$oItem->get_score().$crlf;
            $result .= $tab.'Time='.$oItem->get_scorm_time('js').$crlf;
            $result .= $tab.'Lesson_Mode='.$oItem->get_lesson_mode().$crlf;
            $result .= '[Core_Lesson]'.$crlf;
            $result .= $oItem->get_suspend_data().$crlf;
            $result .= '[Core_Vendor]'.$crlf;
            $result .= $oItem->get_launch_data.$crlf;
            $result .= '[Comments]'.$crlf;
            $result .= $crlf;
            $result .= '[Evaluation]'.$crlf;
            $result .= $tab.'Course_ID={'.api_get_course_id().'}'.$crlf;
            //$result .= '[Objectives_Status]'.$crlf;
            $result .= '[Student_Data]'.$crlf;
            $result .= $tab.'Mastery_Score='.$oItem->masteryscore.$crlf;
            //$result .= '[Student_Demographics]'.$crlf;
            //$result .= '[Student_Preferences]'.$crlf;

            //error_log('Returning message: '.$result,0);
            //$result = str_replace($convert_dec, $convert_enc, $result);
            //error_log('Returning message (encoded): '.$result,0);
            break;
        case 'putparam':
            $hacp_version = '';
            $hacp_session_id = '';
            $hacp_aicc_data = '';
            foreach ($_REQUEST as $name => $value) {
                //escape the value as described in the AICC documentation p170
                switch (strtolower($name)) {
                    case 'version':
                        $hacp_version = $value;
                        break;
                    case 'session_id':
                        $hacp_session_id = $value;
                        break;
                    case 'aicc_data':
                        //error_log('In '.__FILE__.', '.__LINE__.' - aicc data before translation is '.$value, 0);
                        $value = str_replace('+', ' ', $value);
                        $value = str_replace($convert_enc, $convert_dec, $value);
                        $hacp_aicc_data = $value;
                        break;
                }
            }
            //error_log('In '.__FILE__.', '.__LINE__.' - aicc data is '.$hacp_aicc_data, 0);
            // Treat the incoming request:
            $aicc = new aicc();
            $msg_array = $aicc->parse_ini_string_quotes_safe($hacp_aicc_data, array('core_lesson', 'core_vendor'));
            //error_log('Message is now in this form: '.print_r($msg_array, true), 0);
            foreach ($msg_array as $key => $dummy) {
                switch (strtolower($key)) {
                    case 'core':
                        foreach ($msg_array[$key] as $subkey => $value){
                            switch (strtolower($subkey)) {
                                case 'lesson_location':
                                    //error_log('Setting lesson_location to '.$value, 0);
                                    $oItem->set_lesson_location($value);
                                    break;
                                case 'lesson_status':
                                    //error_log('Setting lesson_status to '.$value, 0);
                                    $oItem->set_status($value);
                                    break;
                                case 'score':
                                    //error_log('Setting lesson_score to '.$value, 0);
                                    $oItem->set_score($value);
                                    break;
                                case 'time':
                                    //error_log('Setting lesson_time to '.$value, 0);
                                    $oItem->set_time($value);
                                    break;
                            }
                        }
                        break;
                    case 'core_lesson':
                        //error_log('Setting suspend_data to '.print_r($msg_array[$key], true), 0);
                        $oItem->current_data = $msg_array[$key];
                        break;
                    case 'comments':
                        break;
                    case 'objectives_status':
                        break;
                    case 'student_data':
                        break;
                    case 'student_preferences':
                        break;
                }
            }

            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            $oItem->save(false);
            break;
        case 'putcomments':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        case 'putobjectives':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        case 'putpath':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        case 'putinteractions':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        case 'putperformance':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        case 'exitau':
            $error_code = 0;
            $error_text = $errors[$error_code];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf.$s_ad.$crlf;
            break;
        default:
            $error_code = 1;
            $error_text = $errors[1];
            $result = $s_ec.$error_code.$crlf.$s_et.$error_text.$crlf;
    }
}
$_SESSION['lpobject'] = serialize($oLP);
// Content type must be text/plain.
header('Content-type: text/plain');
echo $result;
audiorecorder.inc.php000064400000007215152003363470010664 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Created on 27.09.2006
 * Include the lazlo file necessary to use the audiorecorder
 * @author Sebastian Wagner <seba.wagner@gmail.com>
 * @author Eric Marguin <e.marguin@elixir-interactive.com>
 * @author Arnaud Ligot <arnaud@cblue.be>
 * @package chamilo.learnpath
 */
global $_configuration;
$web_path = api_get_path(WEB_CODE_PATH);
$getid3_path = api_get_path(LIBRARY_PATH);

require_once $getid3_path.'getid3/getid3.php';

function getFLVDuration($flv_path) {
    $getid3 = new getID3;
    $getid3->encoding = 'UTF-8';
    try {
        $getid3->Analyze($flv_path);
        return $getid3->info['playtime_seconds'];
    } catch (Exception $e) {
        return 0;
    }
}

$course_id = api_get_course_int_id();

if ($audio_recorder_studentview == 'false') {
    $width = 295;
    $height= 90;
    $player = 'recorder2.swf';
    $server = (api_get_setting('service_visio', 'visio_use_rtmpt') == 'true' ? 'rtmpt://' : 'rtmp://').api_get_setting('service_visio', 'visio_host').':'.(api_get_setting('service_visio', 'visio_use_rtmpt') == 'true' ? '80' : api_get_setting('service_visio', 'visio_port')).'/recorder';
    $post_uri = urlencode($web_path.'conference/audiopost.php?course_code='.api_get_course_id().'&user_id='.api_get_user_id().'&checker='.md5(api_get_course_id().api_get_user_id().gmdate('Ymd').$_configuration['security_key']));
    $filename = 'lpi'.$audio_recorder_item_id.'-'.gmdate('YmdHis').api_get_user_id().'.flv'; // Using -z- as fields splitter.
    $path_to_lzx = $web_path.'conference/'.$player.'?server='.urlencode($server).'&postURI='.$post_uri.'&filename='.$filename;
} else {
    $width = 295;
    $height = 24;
    $player = 'player2.swf';
    $cp = api_get_course_path();
    $docs = Database::get_course_table(TABLE_DOCUMENT);
    $select = "SELECT * FROM $docs " .
            " WHERE c_id = $course_id AND path like BINARY '/audio/lpi".Database::escape_string($audio_recorder_item_id)."-%' AND filetype='file' " .
            " ORDER BY path DESC";
    $res = Database::query($select);
    if (Database::num_rows($res) > 0) {
        $row = Database::fetch_array($res);
        //$filepath = api_get_path(WEB_COURSE_PATH).$cp.'/document'.$row['path'];
        $duration = getFLVDuration(api_get_path(SYS_COURSE_PATH).$cp.'/document'.$row['path']);
        $filepath = api_get_path(WEB_CODE_PATH).'document/download.php?'.api_get_cidreq().'&doc_url='.$row['path'];
        $path_to_lzx = $web_path.'conference/'.$player.'?uri='.urlencode($filepath).'&autostart=true&duration='.$duration;
    }
}

if (!empty($path_to_lzx)) {
    $recorder_content = sprintf("<object type=\"application/x-shockwave-flash\" data=\"%s\" ".
            "width='$width' height='$height'>".
             "<param name=\"movie\" value=\"%s\">".
         "<param name=\"quality\" value=\"high\">".
         "<param name=\"scale\" value=\"noscale\">".
         "<param name=\"salign\" value=\"LT\">".
         "<param name=\"menu\" value=\"false\"></object>", $path_to_lzx, $path_to_lzx);
    if ($audio_recorder_studentview == 'false') {
        echo '<script type="text/javascript">
        function show_audiorecorder() {
            document.getElementById("audiorecorder_frame").innerHTML = "'.addslashes($recorder_content).'";
            document.getElementById("show_audiorecorder_div").style.display="none";
        }
        </script>';
        // Commented the audio for the version stable.
        //echo '<div id="show_audiorecorder_div"><a style="cursor:pointer" onclick="show_audiorecorder()">'.get_lang('ShowAudioRecorder').'</a></div>';
        //echo '<div id="audiorecorder_frame"></div>';
    } else {
        echo $recorder_content;
    }
}back_compat.inc.php000064400000001061152003363470010271 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script used to allow compatibility between the New SCORM tool and both
 * version 1.6.3 and 1.8 of Dokeos by loading libraries in a different way.
 * The switch is now deprecated and this file will be renamed later on to
 * something like lp_includes.inc.php
 * @package chamilo.learnpath
 */
/**
 * Code
 */
require_once '../inc/global.inc.php';
require_once api_get_path(LIBRARY_PATH).'fileDisplay.lib.php';
require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php'; // replace_dangerous_char()blank.php000064400000002573152003363470006356 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Script that displays a blank page (with later a message saying why)
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
$language_file = array('learnpath', 'document','exercice');

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;
require_once '../inc/global.inc.php';
Display::display_reduced_header();

if (isset($_GET['error'])) {
    switch ($_GET['error']){
        case 'document_deleted':
            echo '<br /><br />';
            Display::display_error_message(get_lang('DocumentHasBeenDeleted'));
            break;
        case 'prerequisites':
            echo '<br /><br />';
            Display::display_warning_message(get_lang('LearnpathPrereqNotCompleted'));
            break;
        case 'document_not_found':
            echo '<br /><br />';
            Display::display_warning_message(get_lang('FileNotFound'));
            break;
        case 'reached_one_attempt':
            echo '<br /><br />';
            Display::display_warning_message(get_lang('ReachedOneAttempt'));
            break;
        default:
            break;
    }
} elseif (isset($_GET['msg']) && $_GET['msg'] == 'exerciseFinished') {
    echo '<br /><br />';
    Display::display_normal_message(get_lang('ExerciseFinished'));
}
?>
</body>
</html>
content_makers.inc.php000064400000001736152003363470011053 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * The only usage of this script is to have a portable way of keeping the list of content makers
 * @package chamilo.learnpath
 * @author Yannick Warnier <yannick.warnier@beeznest.com>
 */

$content_origins = array(
    '--'.get_lang('GenericScorm').'--',
    '--'.get_lang('Other').'--',
    'Accent',
    'Accenture',
    'ADLNet',
      'Articulate',
      'ATutor',
      'Blackboard',
      'Calfat',
      'Captivate',
    'Chamilo',
    'Chamilo 2',
    'Claroline',
    'Commest',
    'Coursebuilder',
    'Docent',
    'Dokeos',
    'Dreamweaver',
    'Easyquiz',
    'e-doceo',
    'ENI Editions',
    'Explio',
    'Flash',
    'HTML',
    'HotPotatoes',
    'Hyperoffice',
    'Ingenatic',
    'Instruxion',
    'iProgress',
    'Lectora',
    'Microsoft',
    'Onlineformapro',
    'Opikanoba',
    'Plantyn',
    'Saba',
    'Skillsoft',
    'Speechi',
    'Thomson-NETg',
    'U&I Learning',
    'Udutu',
    'WebCT'
);
display_audiorecorder.php000064400000002247152003363470011641 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Script opened in an iframe and containing the learning path's table of contents
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 * @license	GNU/GPL
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'scorm.class.php';
require_once 'aicc.class.php';

if (isset($_SESSION['lpobject'])) {
    $oLP = unserialize($_SESSION['lpobject']);
    if (is_object($oLP)) {
        $_SESSION['oLP'] = $oLP;
    } else {
        die('Could not instanciate lp object.');
    }
}

$lp_theme_css = $_SESSION['oLP']->get_theme();
$scorm_css_header = true;
Display::display_reduced_header();

echo '<body dir="'.api_get_text_direction().'">';

	echo '<div id="audiorecorder">	';
    $audio_recorder_studentview = 'true';
    $audio_recorder_item_id = $_SESSION['oLP']->current;
    if (api_get_setting('service_visio', 'active') == 'true') {
    	require_once 'audiorecorder.inc.php';
	}
    echo '</div>';
    // end of audiorecorder include
echo '</body></html>';download.php000064400000003453152003363470007074 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 *	This file is responsible for  passing requested documents to the browser.
 *
 *	@package chamilo.document
 */
/**
 * Code
 * Many functions updated and moved to lib/document.lib.php
 */
session_cache_limiter('none');

require_once '../inc/global.inc.php';
$this_section = SECTION_COURSES;

// Protection
api_protect_course_script();

if (!isset($_course)) {
    api_not_allowed(true);
}

$doc_url = $_GET['doc_url'];
// Change the '&' that got rewritten to '///' by mod_rewrite back to '&'
$doc_url = str_replace('///', '&', $doc_url);
// Still a space present? it must be a '+' (that got replaced by mod_rewrite)
$doc_url = str_replace(' ', '+', $doc_url);

$doc_url = str_replace(array('../', '\\..', '\\0', '..\\'), array('', '', '', ''), $doc_url); //echo $doc_url;

if (strpos($doc_url,'../') OR strpos($doc_url,'/..')) {
   $doc_url = '';
}
$sys_course_path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/scorm';
$user_id = api_get_user_id();

if ($_SESSION['oLP']) {
    $lp_id      = $_SESSION['oLP']->get_id();
    $lp_item_id = $_SESSION['oLP']->current;    
    $lp_item_info = new learnpathItem($lp_item_id);
    if (!empty($lp_item_info)) {
    //if (basename($lp_item_info->path) == basename($doc_url)) {
        $visible = learnpath::is_lp_visible_for_student($lp_id, $user_id);
        
        if ($visible) {
            event_download($doc_url);  
            if (Security::check_abs_path($sys_course_path.$doc_url, $sys_course_path.'/')) {
                $full_file_name = $sys_course_path.$doc_url;
                DocumentManager::file_send_for_download($full_file_name);
                exit;
            }
        }
    //}
    }        
}

Display::display_error_message(get_lang('ProtectedDocument'));//api_not_allowed backbutton won't work.
exit;embed.php000064400000001336152003363470006337 0ustar00<?php
    require_once '../inc/global.inc.php';
    $type = $_REQUEST['type'];
    $src  = Security::remove_XSS($_REQUEST['src']);
    if ($type == 'youtube')  {
        $src = 'http://www.youtube.com/embed/'.$src;
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
      <div id="content" style="width: 700px ;margin-left:auto; margin-right:auto;">
        <br />
        <iframe class="youtube-player" type="text/html" width="640" height="385" src="<?php echo $src; ?>" frameborder="0">
        </iframe>
      </div>
  </body>
</html>

<?php 
    } else {
        api_not_allowed();
    }index.php000064400000000575152003363470006376 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Redirection script
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

require 'back_compat.inc.php';
header('location: lp_controller.php?'.api_get_cidReq().'&action=list');
README.txt000064400000002625152003363470006252 0ustar00To use the new SCORM tool, first run the scorm_update_db.php from where it is in the (new)scorm directory.
This will update all courses databases by adding 4 tables (lp, lp_item, lp_view and lp_item_view) and add an lp_type table in the main database.
If you had installed a development version of the newscorm tool, it is a good idea to also run scorm_update_db_alter.php to make sure the latest changes are in.

Then go to the database and modify the 'tool' table in each course to add the 'newscorm' tool (copy the tool row from 'learnpath' and change the name). Make sure to select newscorm/lp_list.php as a starting point for this tool (although index.php should work as well).
You can do this by using the scorm_migrate_hometools.php script now.

If you are using one of these scripts, it is vital you check by yourself that all the courses have been migrated accurately. For example, you will need to check that all links to learning paths in the course homepage are pointing towards the newscorm/ directory, not scorm/ or learnpath/.

Go to one of your courses' homepage and try the new SCORM tool as you would normally do with the old SCORM tool.
Please note this DOES NOT manage the Dokeos learnpaths yet.

For more detailed development information, please read the corresponding Wiki page: http://www.dokeos.com/wiki/index.php/SCORM_tool_redesign

For any problem contact me directly at <yannick.warnier@beeznest.com>
learnpath.class.php000064400001533161152003363470010354 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This class defines the parent attributes and methods for Chamilo learnpaths and SCORM
 * learnpaths. It is used by the scorm class.
 *
 * @package chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 * @author	Julio Montoya   <gugli100@gmail.com> Several improvements and fixes
 */
/**
 * Defines the learnpath parent class
 * @package chamilo.learnpath
 */

class learnpath {

    public $attempt = 0; // The number for the current ID view.
    public $cc; // Course (code) this learnpath is located in. @todo change name for something more comprensible ...
    public $current; // Id of the current item the user is viewing.
    public $current_score; // The score of the current item.
    public $current_time_start; // The time the user loaded this resource (this does not mean he can see it yet).
    public $current_time_stop; // The time the user closed this resource.
    public $default_status = 'not attempted';
    public $encoding = 'UTF-8';
    public $error = '';
    public $extra_information = ''; // This string can be used by proprietary SCORM contents to store data about the current learnpath.
    public $force_commit = false; // For SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue().
    public $index; // The index of the active learnpath_item in $ordered_items array.
    public $items = array();
    public $last; // item_id of last item viewed in the learning path.
    public $last_item_seen = 0; // In case we have already come in this learnpath, reuse the last item seen if authorized.
    public $license; // Which license this course has been given - not used yet on 20060522.
    public $lp_id; // DB ID for this learnpath.
    public $lp_view_id; // DB ID for lp_view
    public $log_file; // File where to log learnpath API msg.
    public $maker; // Which maker has conceived the content (ENI, Articulate, ...).
    public $message = '';
    public $mode = 'embedded'; // Holds the video display mode (fullscreen or embedded).
    public $name; // Learnpath name (they generally have one).
    public $ordered_items = array(); // List of the learnpath items in the order they are to be read.
    public $path = ''; // Path inside the scorm directory (if scorm).
    public $theme; // The current theme of the learning path.
    public $preview_image; // The current image of the learning path.

    // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1).
    public $prevent_reinit = 1;

    // Describes the mode of progress bar display.
    public $seriousgame_mode = 0;

    public $progress_bar_mode = '%';

    // Percentage progress as saved in the db.
    public $progress_db = '0';
    public $proximity; // Wether the content is distant or local or unknown.
    public $refs_list = array (); //list of items by ref => db_id. Used only for prerequisites match.
    // !!!This array (refs_list) is built differently depending on the nature of the LP.
    // If SCORM, uses ref, if Chamilo, uses id to keep a unique value.
    public $type; //type of learnpath. Could be 'dokeos', 'scorm', 'scorm2004', 'aicc', ...
    // TODO: Check if this type variable is useful here (instead of just in the controller script).
    public $user_id; //ID of the user that is viewing/using the course
    public $update_queue = array();
    public $scorm_debug = 0;
    public $arrMenu = array(); // Array for the menu items.
    public $debug = 0; // Logging level.
    public $lp_session_id = 0;
    public $lp_view_session_id = 0; // The specific view might be bound to a session.
    public $prerequisite = 0;
    public $use_max_score = 0; // 1 or 0 // BAT : Dont like Chamilo style, which sets use_max_score to 1. Default was 1 /BAT
    public $created_on      = '';
    public $modified_on     = '';
    public $publicated_on   = '';
    public $expired_on      = '';
    public $ref = null;
    public $course_int_id;
    public $course_info = array();

    public function get_course_int_id() {
        return isset($this->course_int_id) ? $this->course_int_id : api_get_course_int_id();
    }

    public function set_course_int_id($course_id) {
        return $this->course_int_id = intval($course_id);
    }

    /**
     * Class constructor. Needs a database handler, a course code and a learnpath id from the database.
     * Also builds the list of items into $this->items.
     * @param	string		Course code
     * @param	integer		Learnpath ID
     * @param	integer		User ID
     * @return	boolean		True on success, false on error
     */
    public function __construct($course, $lp_id, $user_id) {
        $this->encoding = api_get_system_encoding(); // Chamilo 1.8.8: We intend always to use the system encoding.
        // Check course code.
        $course_id = api_get_course_int_id();

        if ($this->debug > 0) { error_log('New LP - In learnpath::__construct('.$course.','.$lp_id.','.$user_id.')', 0); }
        if (empty($course)) {
            $this->error = 'Course code is empty';
            return false;
        } else {
            $course_info = api_get_course_info($course);
            if (!empty($course_info)) {
                $this->cc 			= $course_info['code'];
                $this->course_info  = $course_info;
                $course_id 	        = $course_info['real_id'];
            } else {
                $this->error = 'Course code does not exist in database ('.$sql.')';
                return false;
            }
        }

        $this->set_course_int_id($course_id);

        // Check learnpath ID.
        if (empty($lp_id)) {
            $this->error = 'Learnpath ID is empty';
            return false;
        } else {
            // TODO: Make it flexible to use any course_code (still using env course code here).
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
            $lp_id = intval($lp_id);
            $sql = "SELECT * FROM $lp_table WHERE id = '$lp_id' AND c_id = $course_id";
            if ($this->debug > 2) { error_log('New LP - learnpath::__construct() '.__LINE__.' - Querying lp: '.$sql, 0); }
            $res = Database::query($sql);
            if (Database::num_rows($res) > 0) {
                $this->lp_id            = $lp_id;
                $row                    = Database::fetch_array($res);
                $this->type             = $row['lp_type'];
                $this->name             = stripslashes($row['name']);
                //$this->encoding       = $row['default_encoding']; // Chamilo 1.8.8: We intend not to use 'default_encoding' field anymore.
                $this->proximity        = $row['content_local'];
                $this->theme            = $row['theme'];
                $this->maker            = $row['content_maker'];
                $this->prevent_reinit   = $row['prevent_reinit'];
    	        $this->seriousgame_mode = $row['seriousgame_mode'];
                $this->license          = $row['content_license'];
                $this->scorm_debug      = $row['debug'];
                $this->js_lib           = $row['js_lib'];
                $this->path             = $row['path'];
                $this->preview_image    = $row['preview_image'];
                $this->author           = $row['author'];
                $this->hide_toc_frame   = $row['hide_toc_frame'];
                $this->lp_session_id    = $row['session_id'];
                $this->use_max_score    = $row['use_max_score'];
                $this->created_on       = $row['created_on'];
                $this->modified_on      = $row['modified_on'];
                $this->ref              = $row['ref'];

                if ($row['publicated_on'] != '0000-00-00 00:00:00') {
                    $this->publicated_on   = $row['publicated_on'];
                }

                if ($row['expired_on'] != '0000-00-00 00:00:00') {
                    $this->expired_on     = $row['expired_on'];
                }
                if ($this->type == 2) {
                    if ($row['force_commit'] == 1) {
                        $this->force_commit = true;
                    }
                }
                $this->mode = $row['default_view_mod'];
            } else {
                $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
                return false;
            }
        }

        // Check user ID.
        if (empty($user_id)) {
            $this->error = 'User ID is empty';
            return false;
        } else {
            $user_info  = api_get_user_info($user_id);
            if (!empty($user_info)) {
                $this->user_id = $user_info['user_id'];
            } else {
                $this->error = 'User ID does not exist in database ('.$sql.')';
                return false;
            }
        }
        // End of variables checking.

        $session_id = api_get_session_id();
        //  Get the session condition for learning paths of the base + session.
        $session = api_get_session_condition($session_id);
        // Now get the latest attempt from this user on this LP, if available, otherwise create a new one.
        $lp_table = Database::get_course_table(TABLE_LP_VIEW);

        // Selecting by view_count descending allows to get the highest view_count first.
        $sql = "SELECT * FROM $lp_table WHERE c_id = $course_id AND lp_id = '$lp_id' AND user_id = '$user_id' $session ORDER BY view_count DESC";
        $res = Database::query($sql);
        if ($this->debug > 2) { error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - querying lp_view: ' . $sql, 0); }

        if (Database :: num_rows($res) > 0) {
            if ($this->debug > 2) {
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - Found previous view', 0);
            }
            $row = Database :: fetch_array($res);
            $this->attempt              = $row['view_count'];
            $this->lp_view_id           = $row['id'];
            $this->last_item_seen       = $row['last_item'];
            $this->progress_db          = $row['progress'];
            $this->lp_view_session_id   = $row['session_id'];
        } else {
            if ($this->debug > 2) {
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - NOT Found previous view', 0);
            }
            $this->attempt = 1;
            $sql_ins = "INSERT INTO $lp_table (c_id, lp_id, user_id, view_count, session_id) VALUES ($course_id, $lp_id, $user_id, 1, $session_id)";
            $res_ins = Database::query($sql_ins);
            $this->lp_view_id = Database :: insert_id();
            if ($this->debug > 2) {
                error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - inserting new lp_view: ' . $sql_ins, 0);
            }
        }

        // Initialise items.
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
        $sql = "SELECT * FROM $lp_item_table WHERE c_id = $course_id AND lp_id = '".$this->lp_id."' ORDER BY parent_item_id, display_order";
        $res = Database::query($sql);

        if ($this->debug > 2) {
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - query lp items: ' . $sql, 0);
            error_log('-- Start while--', 0);
        }

        $lp_item_id_list = array();

        while ($row = Database::fetch_array($res)) {
            $oItem = '';
            $lp_item_id_list[] = $row['id'];
            switch ($this->type) {
                case 3: //aicc
                    $oItem = new aiccItem('db', $row['id'], $course_id);
                    if (is_object($oItem)) {
                        $my_item_id = $oItem->get_id();
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
                        $oItem->set_prevent_reinit($this->prevent_reinit);
                        // Don't use reference here as the next loop will make the pointed object change.
                        $this->items[$my_item_id] = $oItem;
                        $this->refs_list[$oItem->ref] = $my_item_id;
                        if ($this->debug > 2) {
                            error_log('New LP - learnpath::__construct() - aicc object with id ' . $my_item_id . ' set in items[]', 0);
                        }
                    }
                    break;
                case 2:
                    require_once 'scorm.class.php';
                    require_once 'scormItem.class.php';
                    $oItem = new scormItem('db', $row['id'], $course_id);
                    if (is_object($oItem)) {
                        $my_item_id = $oItem->get_id();
                        $oItem->set_lp_view($this->lp_view_id, $course_id);
                        $oItem->set_prevent_reinit($this->prevent_reinit);
                        // Don't use reference here as the next loop will make the pointed object change.

                        $this->items[$my_item_id] = $oItem;
                        $this->refs_list[$oItem->ref] = $my_item_id;
                        if ($this->debug > 2) {
                            error_log('New LP - object with id ' . $my_item_id . ' set in items[]', 0);
                        }
                    }
                    break;
                case 1:
                default:
                    if ($this->debug > 2) {
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - calling learnpathItem', 0);
                    }
                    $oItem = new learnpathItem($row['id'], $user_id, $course_id, $row);

                    if ($this->debug > 2) {
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - end calling learnpathItem', 0);
                    }
                    if (is_object($oItem)) {
                        $my_item_id = $oItem->get_id();
                        //$oItem->set_lp_view($this->lp_view_id); // Moved down to when we are sure the item_view exists.
                        $oItem->set_prevent_reinit($this->prevent_reinit);
                        // Don't use reference here as the next loop will make the pointed object change.
                        $this->items[$my_item_id] = $oItem;
                        $this->refs_list[$my_item_id] = $my_item_id;
                        if ($this->debug > 2) {
                            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - object with id ' . $my_item_id . ' set in items[]', 0);
                        }
                    }
                    break;
            }

            // Items is a list of pointers to all items, classified by DB ID, not SCO id.
            if ($row['parent_item_id'] == 0 || empty ($this->items[$row['parent_item_id']])) {
                if (is_object($this->items[$row['id']])) {
                  $this->items[$row['id']]->set_level(0);
                }
            } else {
                $level = $this->items[$row['parent_item_id']]->get_level() + 1;
                $this->items[$row['id']]->set_level($level);
                if (is_object($this->items[$row['parent_item_id']])) {
                    // Items is a list of pointers from item DB ids to item objects.
                    $this->items[$row['parent_item_id']]->add_child($row['id']);
                } else {
                    if ($this->debug > 2) {
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - The parent item (' . $row['parent_item_id'] . ') of item ' . $row['id'] . ' could not be found', 0);
                    }
                }
            }

            // Setting the view in the item object.
            if (is_object($this->items[$row['id']])) {
                $this->items[$row['id']]->set_lp_view($this->lp_view_id, $course_id);
                if ($this->items[$row['id']]->get_type() == TOOL_HOTPOTATOES) {
                    $this->items[$row['id']]->current_start_time = 0;
                    $this->items[$row['id']]->current_stop_time	= 0;
                }
            }
        }

        if ($this->debug > 2) {
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' ----- end while ----', 0);
        }

        if (!empty($lp_item_id_list)) {
            $lp_item_id_list_to_string = implode("','", $lp_item_id_list);

            // Get last viewing vars.
            $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
            // This query should only return one or zero result.
            $sql = "SELECT lp_item_id, status FROM $lp_item_view_table
                    WHERE c_id = $course_id AND lp_view_id = ".$this->lp_view_id." AND lp_item_id IN ('".$lp_item_id_list_to_string."')
                    ORDER BY view_count DESC ";

            if ($this->debug > 2) {
                error_log('New LP - learnpath::__construct() - Selecting item_views: ' . $sql, 0);
            }

            $status_list = array();
            $res = Database::query($sql);
            while ($row = Database :: fetch_array($res) ) {
                $status_list[$row['lp_item_id']] = $row['status'];
            }

            foreach ($lp_item_id_list as $item_id) {
                if (isset($status_list[$item_id])) {
                    $status = $status_list[$item_id];
                    if (is_object($this->items[$item_id])) {
                        $this->items[$item_id]->set_status($status);
                        if (empty($status)) {
                            $this->items[$item_id]->set_status($this->default_status);
                        }
                    }
                } else {
                    if (is_object($this->items[$item_id])) {
                        $this->items[$item_id]->set_status($this->default_status);
                    }
                    // Add that row to the lp_item_view table so that we have something to show in the stats page.
                    $sql_ins = "INSERT INTO $lp_item_view_table (c_id, lp_item_id, lp_view_id, view_count, status)
                                VALUES ($course_id, ".$item_id . "," . $this->lp_view_id . ", 1, 'not attempted')";
                    if ($this->debug > 2) {
                        error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - Inserting blank item_view : ' . $sql_ins, 0);
                    }
                    $res_ins = Database::query($sql_ins);
                }
            }
        }

        $this->ordered_items = self::get_flat_ordered_items_list($this->get_id(), 0, $course_id);
        $this->max_ordered_items = 0;
        foreach ($this->ordered_items as $index => $dummy) {
            if ($index > $this->max_ordered_items && !empty($dummy)) {
                $this->max_ordered_items = $index;
            }
        }
        // TODO: Define the current item better.
        $this->first();
        if ($this->debug > 2) {
            error_log('New LP - learnpath::__construct() ' . __LINE__ . ' - End of learnpath constructor for learnpath ' . $this->get_id(), 0);
        }
    }

    /**
     * Function rewritten based on old_add_item() from Yannick Warnier. Due the fact that users can decide where the item should come, I had to overlook this function and
     * I found it better to rewrite it. Old function is still available. Added also the possibility to add a description.
     *
     * @param int $parent
     * @param int $previous
     * @param string $type
     * @param int  resource ID (ref)
     * @param string $title
     * @param string $description
     * @return int
     */
    public function add_item($parent, $previous, $type = 'dokeos_chapter', $id, $title, $description, $prerequisites = 0, $max_time_allowed = 0) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::add_item(' . $parent . ',' . $previous . ',' . $type . ',' . $id . ',' . $title . ')', 0);
        }
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        $parent = intval($parent);
        $previous = intval($previous);
        $type = Database::escape_string($type);
        $id = intval($id);
        $max_time_allowed = Database::escape_string(htmlentities($max_time_allowed));
        if (empty ($max_time_allowed)) {
            $max_time_allowed = 0;
        }
        $title       = Database::escape_string($title);
        $description = Database::escape_string($description);
        $sql_count = "	SELECT COUNT(id) AS num
                        FROM $tbl_lp_item
                        WHERE c_id = $course_id AND lp_id = " . $this->get_id() . " AND parent_item_id = " . $parent;

        $res_count = Database::query($sql_count);
        $row = Database :: fetch_array($res_count);
        $num = $row['num'];

        if ($num > 0) {
            if ($previous == 0) {
                $sql = "SELECT id, next_item_id, display_order
                           FROM " . $tbl_lp_item . "
                           WHERE   c_id = $course_id AND
                                   lp_id = " . $this->get_id() . " AND
                                   parent_item_id = " . $parent . " AND
                                   previous_item_id = 0 OR previous_item_id=" . $parent;
                $result = Database::query($sql);
                $row = Database :: fetch_array($result);

                $tmp_previous = 0;
                $next = $row['id'];
                $display_order = 0;
            } else {
                $previous = (int) $previous;
                $sql = "SELECT id, previous_item_id, next_item_id, display_order
						FROM $tbl_lp_item
                        WHERE c_id = $course_id AND lp_id = " . $this->get_id() . " AND id = " . $previous;

                $result = Database::query($sql);
                $row 	= Database :: fetch_array($result);

                $tmp_previous = $row['id'];
                $next = $row['next_item_id'];

                $display_order = $row['display_order'];
            }
        } else {
            $tmp_previous = 0;
            $next = 0;
            $display_order = 0;
        }

        $new_item_id = -1;
        $id = Database::escape_string($id);

        if ($type == 'quiz') {
            $sql = 'SELECT SUM(ponderation)
					FROM ' . Database :: get_course_table(TABLE_QUIZ_QUESTION) . ' as quiz_question
                    INNER JOIN  ' . Database :: get_course_table(TABLE_QUIZ_TEST_QUESTION) . ' as quiz_rel_question
                    ON quiz_question.id = quiz_rel_question.question_id
                    WHERE   quiz_rel_question.exercice_id = '.$id." AND
	            			quiz_question.c_id = $course_id AND
	            			quiz_rel_question.c_id = $course_id ";
            $rsQuiz = Database::query($sql);
            $max_score = Database :: result($rsQuiz, 0, 0);

            //Disabling the exercise if we add it inside a LP
            $exercise = new Exercise();
            $exercise->read($id);
            $exercise->disable();
            $exercise->save();
        } else {
            $max_score = 100;
        }

        if ($prerequisites != 0) {
            $sql_ins = "INSERT INTO " . $tbl_lp_item . " (
            					c_id,
                                lp_id, ".
                                "item_type, ".
                                "ref, ".
                                "title, ".
                                "description, ".
                                "path, ".
                                "max_score, ".
                                "parent_item_id, ".
                                "previous_item_id, ".
                                "next_item_id, ".
                                "display_order, ".
                                "prerequisite, ".
                                "max_time_allowed ".
                            ") VALUES (
                            	$course_id ,
                                ".$this->get_id() . ", ".
                                "'" . $type . "', ".
                                "'', ".
                                "'" . $title . "', ".
                                "'" . $description . "', ".
                                "'" . $id . "', ".
                                "'" . $max_score . "', ".
                                $parent . ", ".
                                $previous . ", ".
                                $next . ", ".
                                ($display_order +1) . ", ".
                                $prerequisites . ", ".
                                $max_time_allowed .
                            ")";
        } else {
            // Insert new item.
            $sql_ins = "
                            INSERT INTO " . $tbl_lp_item . " ( ".
            					"c_id, ".
                                "lp_id, ".
                                "item_type, ".
                                "ref, ".
                                "title, ".
                                "description, ".
                                "path, ".
                                "max_score, ".
                                "parent_item_id, ".
                                "previous_item_id, ".
                                "next_item_id, ".
                                "display_order, ".
                                "max_time_allowed ".
                            ") VALUES (".
            					$course_id. ",".
                                $this->get_id() . ",".
                                "'" . $type . "',".
                                "'',".
                                "'" . $title . "',".
                                "'" . $description . "',".
                                "'" . $id . "',".
                                "'" . $max_score . "',".
                                $parent . ",".
                                $previous . ",".
                                $next . ",".
                                ($display_order +1) . ",".
                                $max_time_allowed .
                            ")";
        }

        if ($this->debug > 2) {
            error_log('New LP - Inserting dokeos_chapter: ' . $sql_ins, 0);
        }

        $res_ins = Database::query($sql_ins);

        if ($res_ins > 0) {
            $new_item_id = Database :: insert_id($res_ins);

            // Update the item that should come after the new item.
            $sql_update_next = "
                            UPDATE " . $tbl_lp_item . "
                            SET previous_item_id = " . $new_item_id . "
                            WHERE c_id = $course_id AND id = " . $next;

            Database::query($sql_update_next);

            // Update the item that should be before the new item.
            $sql_update_previous = "
                            UPDATE " . $tbl_lp_item . "
                            SET next_item_id = " . $new_item_id . "
                            WHERE c_id = $course_id AND id = " . $tmp_previous;

            Database::query($sql_update_previous);

            // Update all the items after the new item.
            $sql_update_order = "
                            UPDATE " . $tbl_lp_item . "
                            SET display_order = display_order + 1
                            WHERE
                                c_id = $course_id AND
                                lp_id = " . $this->get_id() . " AND
                                id <> " . $new_item_id . " AND
                                parent_item_id = " . $parent . " AND
                                display_order > " . $display_order;

            Database::query($sql_update_order);

            // Update the item that should come after the new item.
            $sql_update_ref = "UPDATE " . $tbl_lp_item . "
                               SET ref = " . $new_item_id . "
                               WHERE c_id = $course_id AND id = " . $new_item_id;
            Database::query($sql_update_ref);

        }

        // Upload audio.
        if (!empty ($_FILES['mp3']['name'])) {
            // Create the audio folder if it does not exist yet.
            global $_course;
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
            if (!is_dir($filepath . 'audio')) {
                mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
                $audio_id = add_document($_course, '/audio', 'folder', 0, 'audio');
                api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id(), null, null, null, null, api_get_session_id());
				api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'invisible', api_get_user_id(), null, null, null, null, api_get_session_id());
            }

            // Upload the file in the documents tool.
            include_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
            $file_path = handle_uploaded_document($_course, $_FILES['mp3'], api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document', '/audio', api_get_user_id(), '', '', '', '', false);

            // Getting the filename only.
            $file_components = explode('/', $file_path);
            $file = $file_components[count($file_components) - 1];

            // Store the mp3 file in the lp_item table.
            $sql_insert_audio = "UPDATE $tbl_lp_item SET audio = '" . Database::escape_string($file) . "' WHERE id = '" . Database::escape_string($new_item_id) . "'";
            Database::query($sql_insert_audio);
        }
        return $new_item_id;
    }

    /**
     * Static admin function allowing addition of a learnpath to a course.
     * @param	string	Course code
     * @param	string	Learnpath name
     * @param	string	Learnpath description string, if provided
     * @param	string	Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
     * @param	string	Type of files origin (default = 'zip', others = 'dir','web_dir',...)
     * @param	string	Zip file containing the learnpath or directory containing the learnpath
     * @return	integer	The new learnpath ID on success, 0 on failure
     */
    public static function add_lp($course, $name, $description = '', $learnpath = 'guess', $origin = 'zip', $zipname = '', $publicated_on = '', $expired_on = '') {
        global $charset;
        $course_id = api_get_course_int_id();
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
        // Check course code exists.
        // Check lp_name doesn't exist, otherwise append something.
        $i = 0;
        $name = Database::escape_string($name);

        // Session id.
        $session_id = api_get_session_id();

        $check_name = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND name = '$name'";
        //if ($this->debug > 2) { error_log('New LP - Checking the name for new LP: '.$check_name, 0); }
        $res_name = Database::query($check_name);

        if ($publicated_on == '0000-00-00 00:00:00' || empty($publicated_on)) {
            //by default the publication date is the same that the creation date
            //The behaviour above was changed due BT#2800
        	global $_custom;
        	if (isset($_custom['lps_hidden_when_no_start_date']) && $_custom['lps_hidden_when_no_start_date']) {
            	$publicated_on = '';
        	} else {
        		$publicated_on = api_get_utc_datetime();
        	}
        } else {
            $publicated_on   = Database::escape_string(api_get_utc_datetime($publicated_on));
        }

        if ($expired_on == '0000-00-00 00:00:00' || empty($expired_on)) {
            $expired_on = '';
        } else {
            $expired_on   = Database::escape_string(api_get_utc_datetime($expired_on));
        }

        while (Database :: num_rows($res_name)) {
            // There is already one such name, update the current one a bit.
            $i++;
            $name = $name . ' - ' . $i;
            $check_name = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND name = '$name'";
            //if ($this->debug > 2) { error_log('New LP - Checking the name for new LP: '.$check_name, 0); }
            $res_name = Database::query($check_name);
        }
        // New name does not exist yet; keep it.
        // Escape description.
        $description = Database::escape_string(api_htmlentities($description, ENT_QUOTES, $charset)); // Kevin: added htmlentities().
        $type = 1;
        switch ($learnpath) {
            case 'guess':
                break;
            case 'dokeos':
            case 'chamilo':
                $type = 1;
                break;
            case 'aicc':
                break;
        }

        switch ($origin) {
            case 'zip':
                // Check zipname string. If empty, we are currently creating a new Chamilo learnpath.
                break;
            case 'manual':
            default:
                $get_max = "SELECT MAX(display_order) FROM $tbl_lp WHERE c_id = $course_id";
                $res_max = Database::query($get_max);
                if (Database :: num_rows($res_max) < 1) {
                    $dsp = 1;
                } else {
                    $row = Database :: fetch_array($res_max);
                    $dsp = $row[0] + 1;
                }

                $sql_insert = "INSERT INTO $tbl_lp (c_id, lp_type,name,description,path,default_view_mod, default_encoding,display_order,content_maker,content_local,js_lib,session_id, created_on, publicated_on, expired_on) " .
                              "VALUES ($course_id, $type,'$name','$description','','embedded','UTF-8','$dsp','Chamilo','local','','".$session_id."', '".api_get_utc_datetime()."' , '".$publicated_on."' , '".$expired_on."')";

                Database::query($sql_insert);
                $id = Database :: insert_id();
                if ($id > 0) {
                    $course_info = api_get_course_info();
                    // Insert into item_property.
                    api_item_property_update($course_info, TOOL_LEARNPATH, $id, 'LearnpathAdded', api_get_user_id());
                    api_set_default_visibility($id, TOOL_LEARNPATH);
                    return $id;
                }
                break;
        }
    }

    /**
     * Appends a message to the message attribute
     * @param	string	Message to append.
     */
    public function append_message($string) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::append_message()', 0);
        }
        $this->message .= $string;
    }

    /**
     * Autocompletes the parents of an item in case it's been completed or passed
     * @param	integer	Optional ID of the item from which to look for parents
     */
    public function autocomplete_parents($item) {
        $debug = $this->debug;
        if ($debug) {
            error_log('Learnpath::autocomplete_parents()', 0);
        }
        if (empty($item)) {
            $item = $this->current;
        }
        $parent_id = $this->items[$item]->get_parent();

        if (isset($this->items[$item]) && is_object($this->items[$item]) and !empty($parent_id)) {
            // if $item points to an object and there is a parent.
            if ($debug) {
                error_log('Autocompleting parent of item ' . $item . ' (item ' . $parent_id . ')', 0);
            }
            $current_item = $this->items[$item];
            $parent = $this->items[$parent_id]; // Get the parent.
            // New experiment including failed and browsed in completed status.
            $current_status = $current_item->get_status();
            if ($current_item->is_done() || $current_status == 'browsed' || $current_status == 'failed') {
                // If the current item is completed or passes or succeeded.
                $completed = true;
                if ($debug) {
                    error_log('Status of current item is alright', 0);
                }

                foreach ($parent->get_children() as $child) {
                    // Check all his brothers (parent's children) for completion status.
                    if ($child != $item) {
                        if ($debug) {
                            error_log('Looking at brother with ID ' . $child . ', status is ' . $this->items[$child]->get_status(), 0);
                        }
                        //if($this->items[$child]->status_is(array('completed','passed','succeeded')))
                        // Trying completing parents of failed and browsed items as well.
                        if ($this->items[$child]->status_is(array(
                                'completed',
                                'passed',
                                'succeeded',
                                'browsed',
                                'failed'
                            ))) {
                            // Keep completion status to true.
                        } else {
                            if ($this->debug > 2) {
                                error_log('Found one incomplete child of ' . $parent_id . ': ' . $child . ' is ' . $this->items[$child]->get_status(), 0);
                            }
                            $completed = false;
                        }
                    }
                }

                if ($completed) { // If all the children were completed:
                    $parent->set_status('completed');
                    $parent->save(false, $this->prerequisites_match($parent->get_id()));
                    $this->update_queue[$parent->get_id()] = $parent->get_status();
                    if ($debug) {
                        error_log('Added parent to update queue ' . print_r($this->update_queue, true), 0);
                    }
                    $this->autocomplete_parents($parent->get_id()); // Recursive call.
                }
            } else {
                //error_log('New LP - status of current item is not enough to get bothered with it', 0);
            }
        } else {
            if ($debug) {
                error_log("#$item is an item doesn't have parents");
            }
        }
    }

    /**
     * Autosaves the current results into the database for the whole learnpath
     */
    public function autosave() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::autosave()', 0);
        }
        // TODO: Add save operations for the learnpath itself.
    }

    /**
     * Clears the message attribute
     */
    public function clear_message() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::clear_message()', 0);
        }
        $this->message = '';
    }

    /**
     * Closes the current resource
     *
     * Stops the timer
     * Saves into the database if required
     * Clears the current resource data from this object
     * @return	boolean	True on success, false on failure
     */
    public function close() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::close()', 0);
        }
        if (empty ($this->lp_id)) {
            $this->error = 'Trying to close this learnpath but no ID is set';
            return false;
        }
        $this->current_time_stop = time();
        if ($this->save) {
            $learnpath_view_table = Database :: get_course_table(TABLE_LP_VIEW);
            /*
            $sql = "UPDATE $learnpath_view_table " .
                    "SET " .
                    "stop_time = ".$this->current_time_stop.", " .
                    "score = ".$this->current_score.", ".
                    "WHERE learnpath_id = '".$this->lp_id."'";
            //$res = Database::query($sql);
            $res = Database::query($res);
            if (Database::affected_rows($res) < 1) {
                $this->error = 'Could not update learnpath_view table while closing learnpath';
                return false;
            }
            */
        }
        $this->ordered_items = array ();
        $this->index = 0;
        unset ($this->lp_id);
        //unset other stuff
        return true;
    }

    /**
     * Static admin function allowing removal of a learnpath
     * @param	string	Course code
     * @param	integer	Learnpath ID
     * @param	string	Whether to delete data or keep it (default: 'keep', others: 'remove')
     * @return	boolean	True on success, false on failure (might change that to return number of elements deleted)
     */
    public function delete($course = null, $id = null, $delete = 'keep') {
        $course_id = api_get_course_int_id();

        // TODO: Implement a way of getting this to work when the current object is not set.
        // In clear: implement this in the item class as well (abstract class) and use the given ID in queries.
        //if (empty($course)) { $course = api_get_course_id(); }
        //if (empty($id)) { $id = $this->get_id(); }
        // If an ID is specifically given and the current LP is not the same, prevent delete.
        if (!empty ($id) && ($id != $this->lp_id)) {
            return false;
        }

        $lp             = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_item        = Database :: get_course_table(TABLE_LP_ITEM); // Proposed by Christophe (clefevre), see below.
        $lp_view        = Database :: get_course_table(TABLE_LP_VIEW);
        $lp_item_view   = Database :: get_course_table(TABLE_LP_ITEM_VIEW);


        //if ($this->debug > 0) { error_log('New LP - In learnpath::delete()', 0); }
        // Delete lp item id.
        foreach ($this->items as $id => $dummy) {
            //$this->items[$id]->delete();
            $sql_del_view = "DELETE FROM $lp_item_view WHERE c_id = $course_id AND lp_item_id = '" . $id . "'";
            $res_del_item_view = Database::query($sql_del_view);
        }

        // Proposed by Christophe (nickname: clefevre), see http://www.dokeos.com/forum/viewtopic.php?t=29673
        $sql_del_item = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        $res_del_item = Database::query($sql_del_item);

        $sql_del_view = "DELETE FROM $lp_view WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        //if ($this->debug > 2) { error_log('New LP - Deleting views bound to lp '.$this->lp_id.': '.$sql_del_view, 0); }
        $res_del_view = Database::query($sql_del_view);
        self::toggle_publish($this->lp_id, 'i');
        //if ($this->debug > 2) { error_log('New LP - Deleting lp '.$this->lp_id.' of type '.$this->type, 0); }
        if ($this->type == 2 || $this->type == 3) {
            // This is a scorm learning path, delete the files as well.
            $sql = "SELECT path FROM $lp WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
            $res = Database::query($sql);
            if (Database :: num_rows($res) > 0) {
                $row = Database :: fetch_array($res);
                $path = $row['path'];
                $sql = "SELECT id FROM $lp WHERE c_id = ".$course_id." AND path = '$path' AND id != " . $this->lp_id;
                $res = Database::query($sql);
                if (Database :: num_rows($res) > 0) { // Another learning path uses this directory, so don't delete it.
                    if ($this->debug > 2) {
                        error_log('New LP - In learnpath::delete(), found other LP using path ' . $path . ', keeping directory', 0);
                    }
                } else {
                    // No other LP uses that directory, delete it.
                    $course_rel_dir = api_get_course_path() . '/scorm/'; // scorm dir web path starting from /courses
                    $course_scorm_dir = api_get_path(SYS_COURSE_PATH) . $course_rel_dir; // The absolute system path for this course.
                    if ($delete == 'remove' && is_dir($course_scorm_dir . $path) and !empty ($course_scorm_dir)) {
                        if ($this->debug > 2) {
                            error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: ' . $course_scorm_dir . $path, 0);
                        }
                        // Proposed by Christophe (clefevre).
                        if (strcmp(substr($path, -2), "/.") == 0) {
                            $path = substr($path, 0, -1); // Remove "." at the end.
                        }
                        //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
                        rmdirr($course_scorm_dir . $path);
                    }
                }
            }
        }
        $sql_del_lp = "DELETE FROM $lp WHERE c_id = ".$course_id." AND id = " . $this->lp_id;
        //if ($this->debug > 2) { error_log('New LP - Deleting lp '.$this->lp_id.': '.$sql_del_lp, 0); }
        $res_del_lp = Database::query($sql_del_lp);
        $this->update_display_order(); // Updates the display order of all lps.
        api_item_property_update(api_get_course_info(), TOOL_LEARNPATH, $this->lp_id, 'delete', api_get_user_id());

        require_once '../gradebook/lib/be.inc.php';

        // Delete link of gradebook tool
        //$tbl_grade_link = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
        /*$sql = 'SELECT gl.id FROM ' . $tbl_grade_link . ' gl WHERE gl.type="4" AND gl.ref_id="' . $id . '";';
        $result = Database::query($sql);
        $row = Database :: fetch_array($result, 'ASSOC');*/

        // Fixing gradebook link deleted see #5229.
        /*
        if (!empty($row['id'])) {
               $link = LinkFactory :: load($row['id']);
            if ($link[0] != null) {
                   $link[0]->delete();
            }
        }*/
        require_once api_get_path(SYS_CODE_PATH).'gradebook/lib/gradebook_functions.inc.php';
        $link_info = is_resource_in_course_gradebook(api_get_course_id(), 4 , $id, api_get_session_id());
        if ($link_info !== false) {
            remove_resource_from_course_gradebook($link_info['id']);
        }

        if (api_get_setting('search_enabled') == 'true') {
            require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
            $r = delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
        }
    }

    /**
     * Removes all the children of one item - dangerous!
     * @param	integer	Element ID of which children have to be removed
     * @return	integer	Total number of children removed
     */
    public function delete_children_items($id) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::delete_children_items(' . $id . ')', 0);
        }
        $num = 0;
        if (empty ($id) || $id != strval(intval($id))) {
            return false;
        }
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $id";
        $res = Database::query($sql);
        while ($row = Database :: fetch_array($res)) {
            $num += $this->delete_children_items($row['id']);
            $sql_del = "DELETE FROM $lp_item WHERE c_id = ".$course_id." AND id = " . $row['id'];
            $res_del = Database::query($sql_del);
            $num++;
        }
        return $num;
    }

    /**
     * Removes an item from the current learnpath
     * @param	integer	Elem ID (0 if first)
     * @param	integer	Whether to remove the resource/data from the system or leave it (default: 'keep', others 'remove')
     * @return	integer	Number of elements moved
     * @todo implement resource removal
     */
    public function delete_item($id, $remove = 'keep') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::delete_item()', 0);
        }
        // TODO: Implement the resource removal.
        if (empty ($id) || $id != strval(intval($id))) {
            return false;
        }
        // First select item to get previous, next, and display order.
        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_sel = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND id = $id";
        $res_sel = Database::query($sql_sel);
        if (Database :: num_rows($res_sel) < 1) {
            return false;
        }
        $row = Database :: fetch_array($res_sel);
        $previous = $row['previous_item_id'];
        $next = $row['next_item_id'];
        $display = $row['display_order'];
        $parent = $row['parent_item_id'];
        $lp = $row['lp_id'];
        // Delete children items.
        $num = $this->delete_children_items($id);
        if ($this->debug > 2) {
            error_log('New LP - learnpath::delete_item() - deleted ' . $num . ' children of element ' . $id, 0);
        }
        // Now delete the item.
        $sql_del = "DELETE FROM $lp_item WHERE c_id = $course_id AND id = $id";
        if ($this->debug > 2) {
            error_log('New LP - Deleting item: ' . $sql_del, 0);
        }
        $res_del = Database::query($sql_del);
        // Now update surrounding items.
        $sql_upd = "UPDATE $lp_item SET next_item_id = $next WHERE c_id = ".$course_id." AND id = $previous";
        $res_upd = Database::query($sql_upd);
        $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous WHERE c_id = ".$course_id." AND id = $next";
        $res_upd = Database::query($sql_upd);
        // Now update all following items with new display order.
        $sql_all = "UPDATE $lp_item SET display_order = display_order-1 WHERE c_id = ".$course_id." AND lp_id = $lp AND parent_item_id = $parent AND display_order > $display";
        $res_all = Database::query($sql_all);

        //Removing prerequisites since the item will not longer exist
        $sql_all = "UPDATE $lp_item SET prerequisite = '' WHERE c_id = ".$course_id." AND prerequisite = $id";
        $res_all = Database::query($sql_all);

        // Remove from search engine if enabled.
        if (api_get_setting('search_enabled') == 'true') {
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
            $res = Database::query($sql);
            if (Database :: num_rows($res) > 0) {
                $row2 = Database :: fetch_array($res);
                require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
                $di = new ChamiloIndexer();
                $di->remove_document((int) $row2['search_did']);
            }
            $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
            Database::query($sql);
        }
    }

    /**
     * Updates an item's content in place
     * @param	integer	Element ID
     * @param	integer	Parent item ID
     * @param	integer Previous item ID
     * @param   string	Item title
     * @param   string  Item description
     * @param   string  Prerequisites (optional)
     * @param   string  Indexing terms (optional)
     * @param   array   The array resulting of the $_FILES[mp3] element
     * @return	boolean	True on success, false on error
     */
    public function edit_item($id, $parent, $previous, $title, $description, $prerequisites = 0, $audio = null, $max_time_allowed = 0) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::edit_item()', 0);
        }
        if (empty ($max_time_allowed)) {
            $max_time_allowed = 0;
        }
        if (empty ($id) || ($id != strval(intval($id))) || empty ($title)) {
            return false;
        }

        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_select = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND id = " . $id;
        $res_select = Database::query($sql_select);
        $row_select = Database :: fetch_array($res_select);
        $audio_update_sql = '';
        if (is_array($audio) && !empty ($audio['tmp_name']) && $audio['error'] === 0) {
            // Create the audio folder if it does not exist yet.
            global $_course;
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
            if (!is_dir($filepath . 'audio')) {
                mkdir($filepath . 'audio', api_get_permissions_for_new_directories());
                $audio_id = add_document($_course, '/audio', 'folder', 0, 'audio');
                api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id(), null, null, null, null, api_get_session_id());
				api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'invisible', api_get_user_id(), null, null, null, null, api_get_session_id());
            }

            // Upload file in documents.
            $pi = pathinfo($audio['name']);
            if ($pi['extension'] == 'mp3') {
                $c_det = api_get_course_info($this->cc);
                $bp = api_get_path(SYS_COURSE_PATH) . $c_det['path'] . '/document';
                $path = handle_uploaded_document($c_det, $audio, $bp, '/audio', api_get_user_id(), 0, null, 0, 'rename', false, 0);
                $path = substr($path, 7);
                // Update reference in lp_item - audio path is the path from inside de document/audio/ dir.
                $audio_update_sql = ", audio = '" . Database::escape_string($path) . "' ";
            }
        }

        $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
        $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;

        // TODO: htmlspecialchars to be checked for encoding related problems.
        if ($same_parent && $same_previous) {
            // Only update title and description.
            $sql_update = " UPDATE " . $tbl_lp_item . "
                            SET title = '" . Database::escape_string($title) . "',
                                prerequisite = '" . $prerequisites . "',
                                description = '" . Database::escape_string($description) . "'
                                " . $audio_update_sql . ",
                                max_time_allowed = '" . Database::escape_string($max_time_allowed) . "'
                            WHERE c_id = ".$course_id." AND id = " . $id;
            $res_update = Database::query($sql_update);
        } else {
            $old_parent = $row_select['parent_item_id'];
            $old_previous = $row_select['previous_item_id'];
            $old_next = $row_select['next_item_id'];
            $old_order = $row_select['display_order'];
            $old_prerequisite = $row_select['prerequisite'];
            $old_max_time_allowed = $row_select['max_time_allowed'];

            /* BEGIN -- virtually remove the current item id */
            /* for the next and previous item it is like the current item doesn't exist anymore */

            if ($old_previous != 0) {
                $sql_update_next = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET next_item_id = " . $old_next . "
                                    WHERE c_id = ".$course_id." AND id = " . $old_previous;
                $res_update_next = Database::query($sql_update_next);
                //echo '<p>' . $sql_update_next . '</p>';
            }

            if ($old_next != 0) {
                $sql_update_previous = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET previous_item_id = " . $old_previous . "
                                    WHERE c_id = ".$course_id." AND id = " . $old_next;
                $res_update_previous = Database::query($sql_update_previous);

                //echo '<p>' . $sql_update_previous . '</p>';
            }

            // display_order - 1 for every item with a display_order bigger then the display_order of the current item.
            $sql_update_order = "
                            UPDATE " . $tbl_lp_item . "
                            SET display_order = display_order - 1
                            WHERE
                                c_id = ".$course_id." AND
                                display_order > " . $old_order . " AND lp_id = " . $this->lp_id . " AND
                                parent_item_id = " . $old_parent;
            $res_update_order = Database::query($sql_update_order);

            //echo '<p>' . $sql_update_order . '</p>';

            /* END -- virtually remove the current item id */

            /* BEGIN -- update the current item id to his new location */

            if ($previous == 0) {
                // Select the data of the item that should come after the current item.
                $sql_select_old = "SELECT id, display_order
                                    FROM " . $tbl_lp_item . "
                                    WHERE
                                        c_id = ".$course_id." AND
                                        lp_id = " . $this->lp_id . " AND
                                        parent_item_id = " . $parent . " AND
                                        previous_item_id = " . $previous;
                $res_select_old = Database::query($sql_select_old);
                $row_select_old = Database :: fetch_array($res_select_old);

                //echo '<p>' . $sql_select_old . '</p>';

                // If the new parent didn't have children before.
                if (Database :: num_rows($res_select_old) == 0) {
                    $new_next = 0;
                    $new_order = 1;
                } else {
                    $new_next = $row_select_old['id'];
                    $new_order = $row_select_old['display_order'];
                }

                //echo 'New next_item_id of current item: ' . $new_next . '<br />';
                //echo 'New previous_item_id of current item: ' . $previous . '<br />';
                //echo 'New display_order of current item: ' . $new_order . '<br />';

            } else {
                // Select the data of the item that should come before the current item.
                $sql_select_old = " SELECT next_item_id, display_order
                                    FROM " . $tbl_lp_item . "
                                    WHERE c_id = ".$course_id." AND id = " . $previous;
                $res_select_old = Database::query($sql_select_old);
                $row_select_old = Database :: fetch_array($res_select_old);

                //echo '<p>' . $sql_select_old . '</p>';

                //echo 'New next_item_id of current item: ' . $row_select_old['next_item_id'] . '<br />';
                //echo 'New previous_item_id of current item: ' . $previous . '<br />';
                //echo 'New display_order of current item: ' . ($row_select_old['display_order'] + 1) . '<br />';

                $new_next = $row_select_old['next_item_id'];
                $new_order = $row_select_old['display_order'] + 1;
            }

            // TODO: htmlspecialchars to be checked for encoding related problems.
            // Update the current item with the new data.
            $sql_update = "UPDATE " . $tbl_lp_item . "
                            SET
                                title = '" . Database::escape_string($title) . "',
                                description = '" . Database::escape_string($description) . "',
                                parent_item_id = " . $parent . ",
                                previous_item_id = " . $previous . ",
                                next_item_id = " . $new_next . ",
                                display_order = " . $new_order . "
                                " . $audio_update_sql . "
                            WHERE c_id = ".$course_id." AND id = " . $id;
            $res_update_next = Database::query($sql_update);
            //echo '<p>' . $sql_update . '</p>';

            if ($previous != 0) {
                // Update the previous item's next_item_id.
                $sql_update_previous = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET next_item_id = " . $id . "
                                    WHERE c_id = ".$course_id." AND id = " . $previous;
                $res_update_next = Database::query($sql_update_previous);
                //echo '<p>' . $sql_update_previous . '</p>';
            }

            if ($new_next != 0) {
                // Update the next item's previous_item_id.
                $sql_update_next = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET previous_item_id = " . $id . "
                                    WHERE c_id = ".$course_id." AND id = " . $new_next;
                $res_update_next = Database::query($sql_update_next);
                //echo '<p>' . $sql_update_next . '</p>';
            }

            if ($old_prerequisite != $prerequisites) {
                $sql_update_next = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET prerequisite = " . $prerequisites . "
                                    WHERE c_id = ".$course_id." AND id = " . $id;
                $res_update_next = Database::query($sql_update_next);
            }

            if ($old_max_time_allowed != $max_time_allowed) {
                $sql_update_max_time_allowed = "
                                    UPDATE " . $tbl_lp_item . "
                                    SET max_time_allowed = " . $max_time_allowed . "
                                    WHERE c_id = ".$course_id." AND id = " . $id;
                $res_update_max_time_allowed = Database::query($sql_update_max_time_allowed);
            }

            // Update all the items with the same or a bigger display_order than the current item.
            $sql_update_order = "
                               UPDATE " . $tbl_lp_item . "
                               SET display_order = display_order + 1
                               WHERE
                                   c_id = ".$course_id." AND
                                   lp_id = " . $this->get_id() . " AND
                                   id <> " . $id . " AND
                                   parent_item_id = " . $parent . " AND
                                   display_order >= " . $new_order;

            $res_update_next = Database::query($sql_update_order);
        }
    }

    /**
     * Updates an item's prereq in place
     * @param	integer	Element ID
     * @param	string	Prerequisite Element ID
     * @param	string	Prerequisite item type
     * @param	string	Prerequisite min score
     * @param	string	Prerequisite max score
     * @return	boolean	True on success, false on error
     */
    public function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::edit_item_prereq(' . $id . ',' . $prerequisite_id . ',' . $mastery_score . ',' . $max_score . ')', 0);
        }

        if (empty ($id) or ($id != strval(intval($id))) or empty ($prerequisite_id)) {
            return false;
        }

        $prerequisite_id = Database::escape_string($prerequisite_id);

        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        if (!is_numeric($mastery_score) || $mastery_score < 0) {
            $mastery_score = 0;
        }

        if (!is_numeric($max_score) || $max_score < 0) {
            $max_score = 100;
        }

        if ($mastery_score > $max_score) {
            $max_score = $mastery_score;
        }

        if (!is_numeric($prerequisite_id)) {
            $prerequisite_id = 'NULL';
        }

        $sql_upd = " UPDATE " . $tbl_lp_item . "
                     SET prerequisite = " . $prerequisite_id . " WHERE c_id = ".$course_id." AND id = " . $id;
        $res_upd = Database::query($sql_upd);

        if ($prerequisite_id != 'NULL' && $prerequisite_id != '') {
            $sql_upd = " UPDATE " . $tbl_lp_item . " SET
                         mastery_score = " . $mastery_score .
                         //", max_score = " . $max_score . " " . // Max score cannot be changed in the form anyway - see display_item_prerequisites_form().
                        " WHERE c_id = ".$course_id." AND ref = '" . $prerequisite_id . "'"; // Will this be enough to ensure unicity?
            $res_upd = Database::query($sql_upd);
        }
        // TODO: Update the item object (can be ignored for now because refreshed).
        return true;
    }

    /**
     * Escapes a string with the available database escape function
     * @param	string	String to escape
     * @return	string	String escaped
     * @deprecated use  Database::escape_string
     */
    public function escape_string($string) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::escape_string('.$string.')', 0); }
        return Database::escape_string($string);
    }

    /**
     * Static admin function exporting a learnpath into a zip file
     * @param	string	Export type (scorm, zip, cd)
     * @param	string	Course code
     * @param	integer Learnpath ID
     * @param	string	Zip file name
     * @return	string	Zip file path (or false on error)
     */
    public function export_lp($type, $course, $id, $zipname) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::export_lp()', 0); }
        if (empty($type) || empty($course) || empty($id) || empty($zipname)) {
            return false;
        }
        $url = '';
        switch ($type) {
            case 'scorm':
                break;
            case 'zip':
                break;
            case 'cdrom':
                break;
        }
        return $url;
    }

    /**
     * Gets all the chapters belonging to the same parent as the item/chapter given
     * Can also be called as abstract method
     * @param	integer	Item ID
     * @return	array	A list of all the "brother items" (or an empty array on failure)
     */
    public function get_brother_chapters($id) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_brother_chapters()', 0);
        }

        if (empty ($id) OR $id != strval(intval($id))) {
            return array ();
        }

        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_parent = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND id = $id AND item_type='dokeos_chapter'";
        $res_parent = Database::query($sql_parent);
        if (Database :: num_rows($res_parent) > 0) {
            $row_parent = Database :: fetch_array($res_parent);
            $parent = $row_parent['parent_item_id'];
            $sql_bros = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $parent AND id = $id AND item_type='dokeos_chapter' ORDER BY display_order";
            $res_bros = Database::query($sql_bros);
            $list = array ();
            while ($row_bro = Database :: fetch_array($res_bros)) {
                $list[] = $row_bro;
            }
            return $list;
        }
        return array ();
    }

    /**
     * Gets all the items belonging to the same parent as the item given
     * Can also be called as abstract method
     * @param	integer	Item ID
     * @return	array	A list of all the "brother items" (or an empty array on failure)
     */
    public function get_brother_items($id) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_brother_items(' . $id . ')', 0);
        }

        if (empty ($id) OR $id != strval(intval($id))) {
            return array ();
        }

        $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_parent = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id = $id";
        $res_parent = Database::query($sql_parent);
        if (Database :: num_rows($res_parent) > 0) {
            $row_parent = Database :: fetch_array($res_parent);
            $parent = $row_parent['parent_item_id'];
            $sql_bros = "SELECT * FROM $lp_item WHERE c_id = ".$course_id." AND parent_item_id = $parent ORDER BY display_order";
            $res_bros = Database::query($sql_bros);
            $list = array ();
            while ($row_bro = Database :: fetch_array($res_bros)) {
                $list[] = $row_bro;
            }
            return $list;
        }
        return array ();
    }

    /**
     * Get the specific prefix index terms of this learning path
     * @return  array Array of terms
     */
    public function get_common_index_terms_by_prefix($prefix) {
        require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
        $terms = get_specific_field_values_list_by_prefix($prefix, $this->cc, TOOL_LEARNPATH, $this->lp_id);
        $prefix_terms = array();
        if (!empty($terms)) {
            foreach ($terms as $term) {
                $prefix_terms[] = $term['value'];
            }
        }
        return $prefix_terms;
    }

    /**
     * Gets the number of items currently completed
     * @return integer The number of items currently completed
     */
    public function get_complete_items_count() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_complete_items_count()', 0);
        }
        $i = 0;
        foreach ($this->items as $id => $dummy) {
            // Trying failed and browsed considered "progressed" as well.
            if ($this->items[$id]->status_is(array(
                    'completed',
                    'passed',
                    'succeeded',
                    'browsed',
                    'failed'
                )) && $this->items[$id]->get_type() != 'dokeos_chapter' && $this->items[$id]->get_type() != 'dir') {
                $i++;
            }
        }
        return $i;
    }

    /**
     * Gets the current item ID
     * @return	integer	The current learnpath item id
     */
    public function get_current_item_id() {
        $current = 0;
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_current_item_id()', 0);
        }
        if (!empty ($this->current)) {
            $current = $this->current;
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_current_item_id() - Returning ' . $current, 0);
        }
        return $current;
    }

    /**
     * Force to get the first learnpath item id
     * @return	integer	The current learnpath item id
     */
    public function get_first_item_id() {
        $current = 0;
        if (is_array($this->ordered_items)) {
            $current = $this->ordered_items[0];
        }
        return $current;
    }

    /**
     * Gets the total number of items available for viewing in this SCORM
     * @return	integer	The total number of items
     */
    public function get_total_items_count() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_total_items_count()', 0);
        }
        return count($this->items);
    }

    /**
     * Gets the total number of items available for viewing in this SCORM but without chapters
     * @return	integer	The total no-chapters number of items
     */
    public function get_total_items_count_without_chapters() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_total_items_count_without_chapters()', 0);
        }
        $total = 0;
        foreach ($this->items as $temp2) {
            if (!in_array($temp2->get_type(), array(
                    'dokeos_chapter',
                    'chapter',
                    'dir'
                )))
            $total++;
        }
        return $total;
    }

    /**
     * Gets the first element URL.
     * @return	string	URL to load into the viewer
     */
    public function first() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::first()', 0);
            error_log('$this->last_item_seen '.$this->last_item_seen);
            //error_log('$this->items '.print_r($this->items, 1));
            //error_log('$this->ordered_items '.print_r($this->ordered_items, 1));
        }

        // Test if the last_item_seen exists and is not a dir.
        if (count($this->ordered_items) == 0) {
            $this->index = 0;
        }

        if ($this->debug > 0) {
            if (isset($this->items[$this->last_item_seen])) {
                $status = $this->items[$this->last_item_seen]->get_status();
            }
            error_log('status '.$status);
        }

         if (!empty($this->last_item_seen) &&
             !empty($this->items[$this->last_item_seen]) &&
             $this->items[$this->last_item_seen]->get_type() != 'dir' &&
             $this->items[$this->last_item_seen]->get_type() != 'dokeos_chapter'
             //with this change (below) the LP will NOT go to the next item, it will take lp item we left
             //&& !$this->items[$this->last_item_seen]->is_done()
             ) {

            if ($this->debug > 2) {
                error_log('New LP - In learnpath::first() - Last item seen is ' . $this->last_item_seen.' of type '.$this->items[$this->last_item_seen]->get_type(), 0);
            }
            $index = -1;
            foreach ($this->ordered_items as $myindex => $item_id) {
                if ($item_id == $this->last_item_seen) {
                    $index = $myindex;
                    break;
                }
            }
            if ($index == -1) {
                // Index hasn't changed, so item not found - panic (this shouldn't happen).
                if ($this->debug > 2) {
                    error_log('New LP - Last item (' . $this->last_item_seen . ') was found in items but not in ordered_items, panic!', 0);
                }
                return false;
            } else {
                $this->last     = $this->last_item_seen;
                $this->current  = $this->last_item_seen;
                $this->index    = $index;
            }
        } else {
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::first() - No last item seen', 0);
            }
            $index = 0;
            // Loop through all ordered items and stop at the first item that is
            // not a directory *and* that has not been completed yet.
            while ( !empty($this->ordered_items[$index]) AND
                    is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') AND
                    (
                            $this->items[$this->ordered_items[$index]]->get_type() == 'dir' OR
                            $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter' OR
                            $this->items[$this->ordered_items[$index]]->is_done() === true
                    ) AND $index < $this->max_ordered_items) {
                $index++;
            }
            $this->last     = $this->current;
            // current is
            $this->current  = isset($this->ordered_items[$index]) ? $this->ordered_items[$index] : null;
            $this->index    = $index;
            if ($this->debug > 2) {
                error_log('$index ' . $index);
            }
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::first() - No last item seen. New last = ' . $this->last . '(' . $this->ordered_items[$index] . ')', 0);
            }
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::first() - First item is ' . $this->get_current_item_id());
        }
    }

    /**
     * Gets the information about an item in a format usable as JavaScript to update
     * the JS API by just printing this content into the <head> section of the message frame
     * @param	integer		Item ID
     * @return	string
     */
    public function get_js_info($item_id = '') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_js_info(' . $item_id . ')', 0);
        }

        $info = '';
        $item_id = Database::escape_string($item_id);

        if (!empty($item_id) && is_object($this->items[$item_id])) {
            //if item is defined, return values from DB
            $oItem = $this->items[$item_id];
            $info .= '<script language="javascript">';
            $info .= "top.set_score(" . $oItem->get_score() . ");\n";
            $info .= "top.set_max(" . $oItem->get_max() . ");\n";
            $info .= "top.set_min(" . $oItem->get_min() . ");\n";
            $info .= "top.set_lesson_status('" . $oItem->get_status() . "');";
            $info .= "top.set_session_time('" . $oItem->get_scorm_time('js') . "');";
            $info .= "top.set_suspend_data('" . $oItem->get_suspend_data() . "');";
            $info .= "top.set_saved_lesson_status('" . $oItem->get_status() . "');";
            $info .= "top.set_flag_synchronized();";
            $info .= '</script>';
            if ($this->debug > 2) {
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
            }
            return $info;

        } else {

            // If item_id is empty, just update to default SCORM data.
            $info .= '<script language="javascript">';
            $info .= "top.set_score(" . learnpathItem :: get_score() . ");\n";
            $info .= "top.set_max(" . learnpathItem :: get_max() . ");\n";
            $info .= "top.set_min(" . learnpathItem :: get_min() . ");\n";
            $info .= "top.set_lesson_status('" . learnpathItem :: get_status() . "');";
            $info .= "top.set_session_time('" . learnpathItem :: get_scorm_time('js') . "');";
            $info .= "top.set_suspend_data('" . learnpathItem :: get_suspend_data() . "');";
            $info .= "top.set_saved_lesson_status('" . learnpathItem :: get_status() . "');";
            $info .= "top.set_flag_synchronized();";
            $info .= '</script>';
            if ($this->debug > 2) {
                error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
            }
            return $info;
        }
    }

    /**
     * Gets the js library from the database
     * @return	string	The name of the javascript library to be used
     */
    public function get_js_lib() {
        $lib = '';
        if (!empty ($this->js_lib)) {
            $lib = $this->js_lib;
        }
        return $lib;
    }

    /**
     * Gets the learnpath database ID
     * @return	integer	Learnpath ID in the lp table
     */
    public function get_id() {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_id()', 0); }
        if (!empty ($this->lp_id)) {
            return $this->lp_id;
        } else {
            return 0;
        }
    }

    /**
     * Gets the last element URL.
     * @return string URL to load into the viewer
     */
    public function get_last() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_last()', 0);
        }
        $this->index = count($this->ordered_items) - 1;
        return $this->ordered_items[$this->index];
    }

    /**
     * Gets the navigation bar for the learnpath display screen
     * @return	string	The HTML string to use as a navigation bar
     */
    public function get_navigation_bar() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_navigation_bar()', 0);
        }
        $lp_id = $this->lp_id;
        $mycurrentitemid = $this->get_current_item_id();

        // BAT : removed : 
        // <a href="lp_controller.php?action=stats&'.api_get_cidreq(true).'&lp_id='.$lp_id.'" onClick="window.parent.API.save_asset();return true;" target="content_name_blank" title="stats" id="stats_link"><img border="0" src="../img/lp_stats.gif" title="' . get_lang('Reporting') . '"></a>

        $navbar = array( 
          'previous' => '<div onClick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" ' . 
                        'style="width: 40px; height: 40px;
                                background-size: 32px;
                                background-repeat: no-repeat;
                                background-position: 2px 2px;
                                z-index: 1234;
                                cursor: pointer;
                                background-image: url(\'../css/mm_skip_backward.png\');
                                display: inline-block;' .
                        'class="mm_img_previous mm_img" alt="' . get_lang('ScormPrevious') . '" title="' . get_lang('ScormPrevious') . '"></div>',
          'next'     => '<div onClick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" ' . 
                        'style="width: 40px; height: 40px;
                                background-size: 32px;
                                background-repeat: no-repeat;
                                background-position: 2px 2px;
                                z-index: 1234;
                                cursor: pointer;
                                background-image: url(\'../css/mm_skip_forward.png\');
                                display: inline-block;' .
                        '    class="mm_img_next     mm_img" alt="' . get_lang('ScormNext') . '" title="' . get_lang('ScormNext') . '"></div>'
        );
        
        /* BAT
        $navbar = array(
            'previous' => '<a href="" onClick="switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous"><img border="0" src="../img/lp_leftarrow.gif" title="' . get_lang('ScormPrevious') . '"></a>',
            'next' => '<a href="" onClick="switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next"  ><img border="0" src="../img/lp_rightarrow.gif" title="' . get_lang('ScormNext') . '"></a>'
        ); */
        
        if ($this->mode == 'fullscreen') {
            $navbar['fullscreen'] = '<a href="lp_controller.php?action=mode&mode=embedded" target="_top" title="embedded mode"><img border="0" src="../img/view_choose.gif" title="'.get_lang('ScormExitFullScreen').'"></a>';
        }
        
        // /BAT
        
        return $navbar;
    }

    /**
     * Gets the next resource in queue (url).
     * @return	string	URL to load into the viewer
     */
    public function get_next_index() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_next_index()', 0);
        }
        // TODO
        $index = $this->index;
        $index++;
        if ($this->debug > 2) {
            error_log('New LP - Now looking at ordered_items[' . ($index) . '] - type is ' . $this->items[$this->ordered_items[$index]]->type, 0);
        }
        while (!empty ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') AND $index < $this->max_ordered_items) {
            $index++;
            if ($index == $this->max_ordered_items){
                if ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') {
                    return $this->index;
                } else {
                    return $index;
                }
            }
        }
        if (empty ($this->ordered_items[$index])) {
            return $this->index;
        }
        if ($this->debug > 2) {
            error_log('New LP - index is now ' . $index, 0);
        }
        return $index;
    }

    /**
     * Gets item_id for the next element
     * @return	integer	Next item (DB) ID
     */
    public function get_next_item_id() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_next_item_id()', 0);
        }
        $new_index = $this->get_next_index();
        if (!empty ($new_index)) {
            if (isset ($this->ordered_items[$new_index])) {
                if ($this->debug > 2) {
                    error_log('New LP - In learnpath::get_next_index() - Returning ' . $this->ordered_items[$new_index], 0);
                }
                return $this->ordered_items[$new_index];
            }
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
        }
        return 0;
    }

    /**
     * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
     *
     * Generally, the package provided is in the form of a zip file, so the function
     * has been written to test a zip file. If not a zip, the function will return the
     * default return value: ''
     * @param	string	the path to the file
     * @param	string 	the original name of the file
     * @return	string	'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
     */
    public static function get_package_type($file_path, $file_name) {

        // Get name of the zip file without the extension.
        $file_info = pathinfo($file_name);
        $filename = $file_info['basename']; // Name including extension.
        $extension = $file_info['extension']; // Extension only.

        if (!empty($_POST['ppt2lp']) && !in_array(strtolower($extension), array(
                'dll',
                'exe'
            ))) {
            return 'oogie';
        }
        if (!empty($_POST['woogie']) && !in_array(strtolower($extension), array(
                'dll',
                'exe'
            ))) {
            return 'woogie';
        }

        $file_base_name = str_replace('.' . $extension, '', $filename); // Filename without its extension.

        $zipFile = new PclZip($file_path);
        // Check the zip content (real size and file extension).
        $zipContentArray = $zipFile->listContent();
        $package_type = '';
        $at_root = false;
        $manifest = '';

        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
        if (is_array($zipContentArray) && count($zipContentArray) > 0) {
            foreach ($zipContentArray as $thisContent) {
                if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
                    // New behaviour: Don't do anything. These files will be removed in scorm::import_package.
                }
                elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
                    $manifest = $thisContent['filename']; // Just the relative directory inside scorm/
                    $package_type = 'scorm';
                    break; // Exit the foreach loop.
                }
                elseif (preg_match('/aicc\//i', $thisContent['filename'])) {
                    // If found an aicc directory... (!= false means it cannot be false (error) or 0 (no match)).
                    $package_type = 'aicc';
                    //break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
                } else {
                    $package_type = '';
                }
            }
        }
        return $package_type;
    }

    /**
     * Gets the previous resource in queue (url). Also initialises time values for this viewing
     * @return string URL to load into the viewer
     */
    public function get_previous_index() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_previous_index()', 0);
        }
        $index = $this->index;
        if (isset ($this->ordered_items[$index -1])) {
            $index--;
            while (isset ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter')) {
                $index--;
                if ($index < 0) {
                    return $this->index;
                }
            }
        } else {
            if ($this->debug > 2) {
                error_log('New LP - get_previous_index() - there was no previous index available, reusing ' . $index, 0);
            }
            // There is no previous item.
        }
        return $index;
    }

    /**
     * Gets item_id for the next element
     * @return	integer	Previous item (DB) ID
     */
    public function get_previous_item_id() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_previous_item_id()', 0);
        }
        $new_index = $this->get_previous_index();
        return $this->ordered_items[$new_index];
    }

    /**
     * Gets the progress value from the progress_db attribute
     * @return	integer	Current progress value
     */
    public function get_progress() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_progress()', 0);
        }
        if (!empty ($this->progress_db)) {
            return $this->progress_db;
        }
        return 0;
    }

    /**
     * Gets the progress value from the progress field in the database (allows use as abstract method)
     * @param	integer	Learnpath ID
     * @param	integer	User ID
     * @param	string	Mode of display ('%','abs' or 'both')
     * @param	string	Course database name (optional, defaults to '')
     * @param	boolean	Whether to return null if no record was found (true), or 0 (false) (optional, defaults to false)
     * @return	integer	Current progress value as found in the database
     */
    public static function get_db_progress($lp_id, $user_id, $mode = '%', $course_code = '', $sincere = false,$session_id = 0) {

        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_db_progress()', 0); }
        $session_id = intval($session_id);
        $course_info = api_get_course_info($course_code);
        $session_condition = api_get_session_condition($session_id);
        $course_id = $course_info['real_id'];
        $table = Database :: get_course_table(TABLE_LP_VIEW);
        $sql = "SELECT * FROM $table WHERE c_id = ".$course_id." AND lp_id = $lp_id AND user_id = $user_id $session_condition";
        $res = Database::query($sql);
        $view_id = 0;
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $progress = $row['progress'];
            $view_id = $row['id'];
        } else {
            if ($sincere) {
                return null;
            }
        }

        if (empty ($progress)) {
            $progress = '0';
        }

        if ($mode == '%') {
            return $progress . '%';
        } else {
            // Get the number of items completed and the number of items total.
            $tbl = Database :: get_course_table(TABLE_LP_ITEM);
            $sql = "SELECT count(*) FROM $tbl
            		WHERE c_id = $course_id AND c_id = ".$course_id." AND lp_id = " . $lp_id . " AND item_type NOT IN('dokeos_chapter','chapter','dir')";
            $res = Database::query($sql);
            $row = Database :: fetch_array($res);
            $total = $row[0];
            $tbl_item_view = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
            $tbl_item = Database :: get_course_table(TABLE_LP_ITEM);

            //$sql = "SELECT count(distinct(lp_item_id)) FROM $tbl WHERE lp_view_id = ".$view_id." AND status IN ('passed','completed','succeeded')";
            // Trying as also counting browsed and failed items.
            $sql = "SELECT count(distinct(lp_item_id))
                    FROM $tbl_item_view as item_view
                    INNER JOIN $tbl_item as item
                    ON item.id = item_view.lp_item_id
                    AND item_type NOT IN('dokeos_chapter','chapter','dir')
                    WHERE
                    	item_view.c_id 	= $course_id AND
                    	item.c_id 		= $course_id  AND
                    	lp_view_id 		= " . $view_id . " AND
            			status IN ('passed','completed','succeeded','browsed','failed')"; //echo '<br />';
            $res = Database::query($sql);
            $row = Database :: fetch_array($res);
            $completed = $row[0];
            if ($mode == 'abs') {
                return $completed . '/' . $total;
            } elseif ($mode == 'both') {
                if ($progress < ($completed / ($total ? $total : 1))) {
                    $progress = number_format(($completed / ($total ? $total : 1)) * 100, 0);
                }
                return $progress . '% (' . $completed . '/' . $total . ')';
            }
        }
        return $progress;
    }

    /**
     * Returns the HTML necessary to print a mediaplayer block inside a page
     * @return string	The mediaplayer HTML
     */
    public function get_mediaplayer($autostart='true') {
        $course_id = api_get_course_int_id();
        global $_course;
        $tbl_lp_item 		= Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_lp_item_view 	= Database :: get_course_table(TABLE_LP_ITEM_VIEW);

        // Getting all the information about the item.
        $sql = "SELECT * FROM " . $tbl_lp_item . " as lp INNER  JOIN " . $tbl_lp_item_view . " as lp_view on lp.id = lp_view.lp_item_id " .
                "WHERE  lp.id = '" . $_SESSION['oLP']->current . "' AND
                        lp.c_id = $course_id AND
                        lp_view.c_id = $course_id";
        $result = Database::query($sql);
        $row 	= Database::fetch_assoc($result);
        $output = '';

        if (!empty ($row['audio'])) {

            $list = $_SESSION['oLP']->get_toc();
            $type_quiz = false;

            foreach($list as $toc) {
                if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type']=='quiz') ) {
                    $type_quiz = true;
                }
            }

            if ($type_quiz) {
                if ($_SESSION['oLP']->prevent_reinit == 1) {
                    $row['status'] === 'completed' ? $autostart_audio = 'false' : $autostart_audio = 'true';
                } else {
                    $autostart_audio = $autostart;
                }
            } else {
                $autostart_audio = 'true';
            }

            // The mp3 player.
            $output  = '<div id="container">';
            $output .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
            $output .= '<script type="text/javascript">
                            var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
                            s1.addParam("allowscriptaccess","always");
                                s1.addParam("flashvars","file=' . api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=' . $autostart_audio.'");
                            s1.write("container");
						</script>
                        </div>';
        }
        return $output;
    }

    /**
     * This function checks if the learnpath is visible for student after the progress of its prerequisite is completed, and considering time availability
     * @param	int		Learnpath id
     * @param	int		Student id
     * @param   string  Course code (optional)
     * @return	bool	True if
     */
    public static function is_lp_visible_for_student($lp_id, $student_id, $course = null) {
        $lp_id = (int)$lp_id;
        $course = api_get_course_info($course);
        $tbl_learnpath = Database :: get_course_table(TABLE_LP_MAIN);
        // Get current prerequisite
        $sql = "SELECT id, prerequisite, publicated_on, expired_on FROM $tbl_learnpath WHERE c_id = ".$course['real_id']." AND id = $lp_id";
        $rs  = Database::query($sql);
        $now = time();
        if (Database::num_rows($rs)>0) {
            $row = Database::fetch_array($rs, 'ASSOC');
            $prerequisite = $row['prerequisite'];
            $is_visible = true;
            $progress = 0;

            if (!empty($prerequisite)) {
                $progress = self::get_db_progress($prerequisite,$student_id,'%', '', false, api_get_session_id());
                $progress = intval($progress);
                if ($progress < 100) {
                    $is_visible = false;
                }
            }

            // Also check the time availability of the LP

            if ($is_visible) {
	            //Adding visibility reestrinctions
	            if (!empty($row['publicated_on']) && $row['publicated_on'] != '0000-00-00 00:00:00') {
	            	if ($now < api_strtotime($row['publicated_on'], 'UTC')) {
	            		//api_not_allowed();
	            		$is_visible = false;
	            	}
	            }

	            //Blocking empty start times see BT#2800
	            global $_custom;
	            if (isset($_custom['lps_hidden_when_no_start_date']) && $_custom['lps_hidden_when_no_start_date']) {
		            if (empty($row['publicated_on']) || $row['publicated_on'] == '0000-00-00 00:00:00') {
		            	//api_not_allowed();
		            	$is_visible = false;
		            }
	            }

	            if (!empty($row['expired_on']) && $row['expired_on'] != '0000-00-00 00:00:00') {
	            	if ($now > api_strtotime($row['expired_on'], 'UTC')) {
	            		//api_not_allowed();
	            		$is_visible = false;
	            	}
	            }
            }
            return $is_visible;
        }
        return false;
    }
    /**
     * Gets a progress bar for the learnpath by counting the number of items in it and the number of items
     * completed so far.
     * @param	string	Mode in which we want the values
     * @param	integer	Progress value to display (optional but mandatory if used in abstract context)
     * @param	string	Text to display near the progress value (optional but mandatory in abstract context)
     * @param	boolean true if it comes from a Diplay LP view
     * @return	string	HTML string containing the progress bar
     */
    public function get_progress_bar($mode = '', $percentage = -1, $text_add = '', $from_lp = false) {
        //if ($this->debug > 0) {error_log('New LP - In learnpath::get_progress_bar('.$mode.','.$percentage.','.$text_add.','.$from_lp.')', 0); }
        global $lp_theme_css;

        // Setting up the CSS path of the current style if exists.
        if (!empty ($lp_theme_css)) {
            $css_path = api_get_path(WEB_CODE_PATH) . 'css/' . $lp_theme_css . '/images/';
        } else {
            $css_path = '../img/';
        }

        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_progress_bar()', 0); }
        if (isset($this) && is_object($this) && ($percentage == '-1' OR $text_add == '')) {
            list($percentage, $text_add) = $this->get_progress_bar_text($mode);
        }
        $text = $percentage . $text_add;
        //@todo use Display::display_progress();
        $output = '<div class="progress progress-striped" style="maring: 0; padding: 0">
                        <div id="progress_bar_value" class="bar" style="width: '.$text.'; maring: 0; padding: 0"></div>
                    </div>';
                    // BAT removed <div class="progresstext" id="progress_text">' . $text . '</div>'; /BAT

        return $output;
    }

    /**
     * Gets the progress bar info to display inside the progress bar. Also used by scorm_api.php
     * @param	string	Mode of display (can be '%' or 'abs').abs means we display a number of completed elements per total elements
     * @param	integer	Additional steps to fake as completed
     * @return	list	Percentage or number and symbol (% or /xx)
     */
    public function get_progress_bar_text($mode = '', $add = 0) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_progress_bar_text()', 0);
        }
        if (empty ($mode)) {
            $mode = $this->progress_bar_mode;
        }
        $total_items = $this->get_total_items_count_without_chapters();
        if ($this->debug > 2) {
            error_log('New LP - Total items available in this learnpath: ' . $total_items, 0);
        }
        $i = $this->get_complete_items_count();
        if ($this->debug > 2) {
            error_log('New LP - Items completed so far: ' . $i, 0);
        }
        if ($add != 0) {
            $i += $add;
            if ($this->debug > 2) {
                error_log('New LP - Items completed so far (+modifier): ' . $i, 0);
            }
        }
        $text = '';
        if ($i > $total_items) {
            $i = $total_items;
        }
        if ($mode == '%') {
            if ($total_items > 0) {
                $percentage = ((float) $i / (float) $total_items) * 100;
            } else {
                $percentage = 0;
            }
            $percentage = number_format($percentage, 0);
            $text = '%';
        }
        elseif ($mode == 'abs') {
            $percentage = $i;
            $text = '/' . $total_items;
        }
        return array (
            $percentage,
            $text
        );
    }

    /**
     * Gets the progress bar mode
     * @return	string	The progress bar mode attribute
     */
    public function get_progress_bar_mode() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
        }
        if (!empty ($this->progress_bar_mode)) {
            return $this->progress_bar_mode;
        } else {
            return '%';
        }
    }

    /**
     * Gets the learnpath proximity (remote or local)
     * @return	string	Learnpath proximity
     */
    public function get_proximity() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_proximity()', 0);
        }
        if (!empty ($this->proximity)) {
            return $this->proximity;
        } else {
            return '';
        }
    }

    /**
     * Gets the learnpath theme (remote or local)
     * @return	string	Learnpath theme
     */
    public function get_theme() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_theme()', 0);
        }
        if (!empty ($this->theme)) {
            return $this->theme;
        } else {
            return '';
        }
    }

    /**
     * Gets the learnpath session id
     * @return	string	Learnpath theme
     */
    public function get_lp_session_id() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_lp_session_id()', 0);
        }
        if (!empty ($this->lp_session_id)) {
            return $this->lp_session_id;
        } else {
            return 0;
        }
    }

    /**
     * Gets the learnpath image
     * @return	string	Web URL of the LP image
     */
    public function get_preview_image() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_preview_image()', 0);
        }
        if (!empty($this->preview_image)) {
            return $this->preview_image;
        } else {
            return '';
        }
    }

    public function get_preview_image_path($size = null, $path_type = 'web') {
        $preview_image = $this->get_preview_image();
        if (isset($preview_image) && !empty($preview_image)) {
            $image_sys_path = api_get_path(SYS_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
            $image_path = api_get_path(WEB_COURSE_PATH).$this->course_info['path'].'/upload/learning_path/images/';
            if (isset($size)) {
                $info = pathinfo($preview_image);
                $image_custom_size = $info['filename'].'.'.$size.'.'.$info['extension'];
                if (file_exists($image_sys_path.$image_custom_size)) {
                    if ($path_type == 'web') {
                        return $image_path.$image_custom_size;
                    } else {
                        return $image_sys_path.$image_custom_size;
                    }
                }
            } else {
                if ($path_type == 'web') {
                    return $image_path.$preview_image;
                } else {
                    return $image_sys_path.$preview_image;
                }
            }
        }
        return false;
    }

    /**
     * Gets the learnpath author
     * @return	string	LP's author
     */
    public function get_author() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_author()', 0);
        }
        if (!empty ($this->author)) {
            return $this->author;
        } else {
            return '';
        }
    }
	/**
	 * Gets the learnpath author
	 * @return	string	LP's author
	 */
	public function get_hide_toc_frame() {
		if ($this->debug > 0) {
			error_log('New LP - In learnpath::get_author()', 0);
		}
		if (!empty ($this->hide_toc_frame)) {
			return $this->hide_toc_frame;
		} else {
			return '';
		}
	}

    /**
     * Generate a new prerequisites string for a given item. If this item was a sco and
     * its prerequisites were strings (instead of IDs), then transform those strings into
     * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
     * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
     * same rule as the scorm_export() method
     * @param	integer		Item ID
     * @return	string		Prerequisites string ready for the export as SCORM
     */
    public function get_scorm_prereq_string($item_id) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_scorm_prereq_string()', 0);
        }
        if (!is_object($this->items[$item_id])) {
            return false;
        }
        $oItem = $this->items[$item_id];
        $prereq = $oItem->get_prereq_string();

        if (empty($prereq)) {
            return '';
        }
        //var_dump($this->refs_list, $prereq);
        if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) {
            // If the prerequisite is a simple integer ID and this ID exists as an item ID,
            // then simply return it (with the ITEM_ prefix).
            //return 'ITEM_' . $prereq;
            return $this->items[$prereq]->ref;
        } else {
            if (isset($this->refs_list[$prereq])) {
                // It's a simple string item from which the ID can be found in the refs list,
                // so we can transform it directly to an ID for export.
                return $this->items[$this->refs_list[$prereq]]->ref;
            } else if (isset($this->refs_list['ITEM_'.$prereq])) {
                return $this->items[$this->refs_list['ITEM_'.$prereq]]->ref;
            } else {
                // The last case, if it's a complex form, then find all the IDs (SCORM strings)
                // and replace them, one by one, by the internal IDs (chamilo db)
                // TODO: Modify the '*' replacement to replace the multiplier in front of it
                // by a space as well.
                $find = array (
                    '&',
                    '|',
                    '~',
                    '=',
                    '<>',
                    '{',
                    '}',
                    '*',
                    '(',
                    ')'
                );
                $replace = array (
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' ',
                    ' '
                );
                $prereq_mod = str_replace($find, $replace, $prereq);
                $ids = split(' ', $prereq_mod);
                foreach ($ids as $id) {
                    $id = trim($id);
                    if (isset ($this->refs_list[$id])) {
                        $prereq = preg_replace('/[^a-zA-Z_0-9](' . $id . ')[^a-zA-Z_0-9]/', 'ITEM_' . $this->refs_list[$id], $prereq);
                    }
                }
                error_log('New LP - In learnpath::get_scorm_prereq_string(): returning modified string: ' . $prereq, 0);
                return $prereq;
            }
        }
    }

    /**
     * Returns the XML DOM document's node
     * @param	resource	Reference to a list of objects to search for the given ITEM_*
     * @param	string		The identifier to look for
     * @return	mixed		The reference to the element found with that identifier. False if not found
     */
    public function get_scorm_xml_node(& $children, $id) {
        for ($i = 0; $i < $children->length; $i++) {
            $item_temp = $children->item($i);
            if ($item_temp->nodeName == 'item') {
                if ($item_temp->getAttribute('identifier') == $id) {
                    return $item_temp;
                }
            }
            $subchildren = $item_temp->childNodes;
            if ($subchildren->length > 0) {
                $val = $this->get_scorm_xml_node($subchildren, $id);
                if (is_object($val)) {
                    return $val;
                }
            }
        }
        return false;
    }

    /**
     * Returns a usable array of stats related to the current learnpath and user
     * @return array	Well-formatted array containing status for the current learnpath
     */
    public function get_stats() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_stats()', 0);
        }
        // TODO
    }

    /**
     * Static method. Can be re-implemented by children. Gives an array of statistics for
     * the given course (for all learnpaths and all users)
     * @param	string	Course code
     * @return array	Well-formatted array containing status for the course's learnpaths
     */
    public function get_stats_course($course) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_course()', 0); }
        // TODO
    }

    /**
     * Static method. Can be re-implemented by children. Gives an array of statistics for
     * the given course and learnpath (for all users)
     * @param	string	Course code
     * @param	integer	Learnpath ID
     * @return array	Well-formatted array containing status for the specified learnpath
     */
    public function get_stats_lp($course, $lp) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp()', 0); }
        // TODO
    }

    /**
     * Static method. Can be re-implemented by children. Gives an array of statistics for
     * the given course, learnpath and user.
     * @param	string	Course code
     * @param	integer	Learnpath ID
     * @param	integer	User ID
     * @return array	Well-formatted array containing status for the specified learnpath and user
     */
    public function get_stats_lp_user($course, $lp, $user) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_lp_user()', 0); }
        // TODO
    }

    /**
     * Static method. Can be re-implemented by children. Gives an array of statistics for
     * the given course and learnpath (for all users)
     * @param	string	Course code
     * @param	integer	User ID
     * @return array	Well-formatted array containing status for the user's learnpaths
     */
    public function get_stats_user($course, $user) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::get_stats_user()', 0); }
        // TODO
    }

    /**
     * Gets the status list for all LP's items
     * @return	array	Array of [index] => [item ID => current status]
     */
    public function get_items_status_list() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_items_status_list()', 0);
        }
        $list = array ();
        foreach ($this->ordered_items as $item_id) {
            $list[] = array (
                $item_id => $this->items[$item_id]->get_status()
            );
        }
        return $list;
    }

    /**
     * Return the number of interactions for the given learnpath Item View ID.
     * This method can be used as static.
     * @param	integer	Item View ID
     * @param   integer course id
     * @return	integer	Number of interactions
     */
    public static function get_interactions_count_from_db($lp_iv_id, $course_id) {
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
        $lp_iv_id = intval($lp_iv_id);
        $course_id = intval($course_id);

        $sql = "SELECT count(*) FROM $table WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
        $res = Database::query($sql);
        $res = 0;
        if (Database::num_rows($res)) {
            $row = Database::fetch_array($res);
            $num = $row[0];
        }
        return $num;
    }

    /**
     * Return the interactions as an array for the given lp_iv_id.
     * This method can be used as static.
     * @param	integer	Learnpath Item View ID
     * @return	array
     * @todo 	Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
     */
    public static function get_iv_interactions_array($lp_iv_id = 0) {
        $course_id = api_get_course_int_id();
        $list = array ();
        $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
        $sql = "SELECT * FROM $table WHERE c_id = ".$course_id." AND lp_iv_id = $lp_iv_id ORDER BY order_id ASC";
        $res = Database::query($sql);
        $num = Database :: num_rows($res);
        if ($num > 0) {
            $list[] = array (
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
                'id' => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES),
                'type' => api_htmlentities(get_lang('Type'), ENT_QUOTES),
                'time' => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES),
                'correct_responses' => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES),
                'student_response' => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES),
                'result' => api_htmlentities(get_lang('Result'), ENT_QUOTES),
                'latency' => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES)
            );
            while ($row = Database :: fetch_array($res)) {
                $list[] = array (
                    'order_id' => ($row['order_id'] + 1),
                    'id' => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
                    'type' => $row['interaction_type'],
                    'time' => $row['completion_time'],
                    //'correct_responses' => $row['correct_responses'],
                    'correct_responses' => '', // Hide correct responses from students.
                    'student_response' => $row['student_response'],
                    'result' => $row['result'],
                    'latency' => $row['latency']
                );
            }
        }
        return $list;
    }

    /**
     * Return the number of objectives for the given learnpath Item View ID.
     * This method can be used as static.
     * @param	integer	Item View ID
     * @return	integer	Number of objectives
     */
    public static function get_objectives_count_from_db($lp_iv_id, $course_id) {
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
        $course_id = intval($course_id);
        $lp_iv_id = intval($lp_iv_id);
        $sql = "SELECT count(*) FROM $table WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id";
        //@todo seems that this always returns 0
        $res = Database::query($sql);
        $res = 0;
        if (Database::num_rows($res)) {
            $row = Database :: fetch_array($res);
            $num = $row[0];
        }
        return $num;
    }

    /**
     * Return the objectives as an array for the given lp_iv_id.
     * This method can be used as static.
     * @param	integer	Learnpath Item View ID
     * @return	array
     * @todo 	Translate labels
     */
    public function get_iv_objectives_array($lp_iv_id = 0) {
        $course_id = api_get_course_int_id();
        $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
        $sql = "SELECT * FROM $table WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id ORDER BY order_id ASC";
        $res = Database::query($sql);
        $num = Database :: num_rows($res);
        $list = array();
        if ($num > 0) {
            $list[] = array (
                'order_id' => api_htmlentities(get_lang('Order'), ENT_QUOTES),
                'objective_id' => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES),
                'score_raw' => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES),
                'score_max' => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES),
                'score_min' => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES),
                'status' => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES)
            );
            while ($row = Database :: fetch_array($res)) {
                $list[] = array (
                    'order_id' => ($row['order_id'] + 1),
                    'objective_id' => urldecode($row['objective_id']), // urldecode() because they often have %2F or stuff like that.
                    'score_raw' => $row['score_raw'],
                    'score_max' => $row['score_max'],
                    'score_min' => $row['score_min'],
                    'status' => $row['status']
                );
            }
        }
        return $list;
    }

    /**
     * Generate and return the table of contents for this learnpath. The (flat) table returned can be
     * used by get_html_toc() to be ready to display
     * @return	array	TOC as a table with 4 elements per row: title, link, status and level
     */
    public function get_toc() {
        if ($this->debug > 0) {
            error_log('learnpath::get_toc()', 0);
        }
        $toc = array();
        //echo "<pre>".print_r($this->items,true)."</pre>";
        foreach ($this->ordered_items as $item_id) {
            if ($this->debug > 2) {
                error_log('learnpath::get_toc(): getting info for item ' . $item_id, 0);
            }
            // TODO: Change this link generation and use new function instead.
            $toc[] = array (
                'id'            => $item_id,
                'title'         => $this->items[$item_id]->get_title(),
                'status'        => $this->items[$item_id]->get_status(),
                'level'         => $this->items[$item_id]->get_level(),
                'type'          => $this->items[$item_id]->get_type(),
                'description'   => $this->items[$item_id]->get_description(),
                'path'          => $this->items[$item_id]->get_path(),
            );
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_toc() - TOC array: ' . print_r($toc, true), 0);
        }
        return $toc;
    }

    /**
     * Generate and return the table of contents for this learnpath. The JS
     * table returned is used inside of scorm_api.php
     * @return  string  A JS array vairiable construction
     */
    public function get_items_details_as_js($varname = 'olms.lms_item_types') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_items_details_as_js()', 0);
        }
        $toc = $varname.' = new Array();';
        //echo "<pre>".print_r($this->items,true)."</pre>";
        foreach ($this->ordered_items as $item_id) {
            $toc.= $varname."['i$item_id'] = '".$this->items[$item_id]->get_type()."';";
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_items_details_as_js() - TOC array: ' . print_r($toc, true), 0);
        }
        return $toc;
    }

    /**
     * Gets the learning path type
     * @param	boolean		Return the name? If false, return the ID. Default is false.
     * @return	mixed		Type ID or name, depending on the parameter
     */
    public function get_type($get_name = false) {
        $res = false;
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_type()', 0);
        }
        if (!empty ($this->type)) {
            if ($get_name) {
                // Get it from the lp_type table in main db.
            } else {
                $res = $this->type;
            }
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_type() - Returning ' . ($res ? $res : 'false'), 0);
        }
        return $res;
    }

    /**
     * Gets the learning path type as static method
     * @param	boolean		Return the name? If false, return the ID. Default is false.
     * @return	mixed		Type ID or name, depending on the parameter
     */
    public static function get_type_static($lp_id = 0) {
        $course_id = api_get_course_int_id();
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT lp_type FROM $tbl_lp WHERE c_id = $course_id AND id = '" . $lp_id . "'";
        $res = Database::query($sql);
        if ($res === false) {
            return null;
        }
        if (Database :: num_rows($res) <= 0) {
            return null;
        }
        $row = Database :: fetch_array($res);
        return $row['lp_type'];
    }

    /**
     * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
     * This method can be used as abstract and is recursive
     * @param	integer	Learnpath ID
     * @param	integer	Parent ID of the items to look for
     * @return	mixed	Ordered list of item IDs or false on error
     */
    public static function get_flat_ordered_items_list($lp, $parent = 0, $course_id = null) {
        if (empty($course_id)) {
            $course_id = api_get_course_int_id();
        } else {
            $course_id = intval($course_id);
        }
        $list = array();
        if (empty ($lp)) {
            return false;
        }
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql = "SELECT id FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $lp AND parent_item_id = $parent ORDER BY display_order";
        $res = Database::query($sql);
        while ($row = Database :: fetch_array($res)) {
            $sublist = learnpath :: get_flat_ordered_items_list($lp, $row['id'], $course_id);
            $list[] = $row['id'];
            foreach ($sublist as $item) {
                $list[] = $item;
            }
        }
        return $list;
    }

    /**
     * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
     * @return	string	HTML TOC ready to display
     */
    public function get_html_toc($toc_list = null) {
        global $_configuration;
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);

        if ($this->debug > 0) {
            error_log('In learnpath::get_html_toc()', 0);
        }
        if (empty($toc_list)) {
            $toc_list = $this->get_toc();
        }
        $html = '<div id="scorm_title" class="scorm_title">'.Security::remove_XSS($this->get_name()) . '</div>';

        $hide_teacher_icons_lp = isset($_configuration['hide_teacher_icons_lp']) ? $_configuration['hide_teacher_icons_lp'] : true;

        if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
            $gradebook = Security :: remove_XSS($_GET['gradebook']);
            if ($this->get_lp_session_id() == api_get_session_id()) {
                $html .= '<div id="actions_lp" class="actions_lp">';
                    $html .= '<div class="btn-group">';
                    $html .= "<a class='btn' href='lp_controller.php?" . api_get_cidreq()."&amp;gradebook=$gradebook&amp;action=build&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . get_lang('Overview') . "</a>";
                    $html .= "<a class='btn' href='lp_controller.php?" . api_get_cidreq()."&amp;action=add_item&amp;type=step&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . get_lang('Edit') . "</a>";
                    $html .= '<a class="btn" href="lp_controller.php?'.api_get_cidreq()."&amp;gradebook=$gradebook&amp;action=edit&amp;lp_id=" . $this->lp_id.'">'.get_lang('Settings').'</a>';
                    $html .= '</div>';
                $html .= '</div>';
            }
        }
        $html .= '<div id="inner_lp_toc" class="inner_lp_toc">';
        require_once 'resourcelinker.inc.php';

        // Temporary variables.
        $mycurrentitemid = $this->get_current_item_id();
        $color_counter = 0;
        $i = 0;

        foreach ($toc_list as $item) {
            // TODO: Complete this
            $icon_name = array (
                'not attempted' => '../img/notattempted.gif',
                'incomplete'    => '../img/incomplete.png',
                'failed'        => '../img/delete.png',
                'completed'     => '../img/completed.png',
                'passed'        => '../img/passed.png',
                'succeeded'     => '../img/succeeded.png',
                'browsed'       => '../img/completed.png',
            );

            $style = 'scorm_item';
            $scorm_color_background = 'scorm_item';
            $style_item = 'scorm_item';
            $current = false;

            if ($item['id'] == $this->current) {
                $style = 'scorm_item_highlight';
                $scorm_color_background = 'scorm_item_highlight';
            } else {
                if ($color_counter % 2 == 0) {
                    $scorm_color_background = 'scorm_item_1';
                } else {
                    $scorm_color_background = 'scorm_item_2';
                }
                if ($item['type'] == 'dokeos_module' || $item['type'] == 'dokeos_chapter') {
                	$scorm_color_background =' scorm_item_section ';
                }
            }

            if ($scorm_color_background != '') {
                $html .= '<div id="toc_' . $item['id'] . '" class="' . $scorm_color_background . '">';
            }

            // The anchor will let us center the TOC on the currently viewed item &^D
            if ($item['type'] != 'dokeos_module' && $item['type'] != 'dokeos_chapter') {
                $html .= '<div class="' . $style_item . '" style="padding-left: ' . ($item['level'] * 1.5) . 'em; padding-right:' . ($item['level'] / 2) . 'em"             title="' . $item['description'] . '" >';
                $html .= '<a name="atoc_' . $item['id'] . '" />';
            } else {
                $html .= '<div class="' . $style_item . '" style="padding-left: ' . ($item['level'] * 2) . 'em; padding-right:' . ($item['level'] * 1.5) . 'em"             title="' . $item['description'] . '" >';
            }
            $title = $item['title'];
            if (empty ($title)) {
                $title = rl_get_resource_name(api_get_course_id(), $this->get_id(), $item['id']);
            }

            $title = Security::remove_XSS($title);
            if ($item['type'] != 'dokeos_chapter' && $item['type'] != 'dir' && $item['type'] != 'dokeos_module') {
                //$html .= "<a href='lp_controller.php?".api_get_cidreq()."&action=content&lp_id=".$this->get_id()."&item_id=".$item['id']."' target='lp_content_frame_name'>".$title."</a>" ;
                $url = $this->get_link('http', $item['id'], $toc_list);
                //$html .= '<a href="'.$url.'" target="content_name" onClick="top.load_item('.$item['id'].',\''.$url.'\');">'.$title.'</a>' ;
                //$html .= '<a href="" onClick="top.load_item('.$item['id'].',\''.$url.'\');return false;">'.$title.'</a>' ;

                //<img align="absbottom" width="13" height="13" src="../img/lp_document.png">&nbsp;background:#aaa;
                $html .= '<a href="" onClick="switch_item(' .$mycurrentitemid . ',' .$item['id'] . ');' .'return false;" >' . stripslashes($title) . '</a>';
            } elseif ($item['type'] == 'dokeos_module' || $item['type'] == 'dokeos_chapter') {
                $html .= "<img align='absbottom' width='13' height='13' src='../img/lp_dokeos_module.png'>&nbsp;" . stripslashes($title);
            } elseif ($item['type'] == 'dir') {
                $html .= stripslashes($title);
            }

            /*$tbl_track_e_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
            $user_id = api_get_user_id();

            $sql = "SELECT path  FROM $tbl_track_e_exercises, $tbl_lp_item
                    WHERE   c_id = $course_id AND
                            path =   '" . $item['path'] . "' AND
                            exe_user_id =  '$user_id' AND
                            exe_cours_id = '$course_code' AND
                            path = exe_exo_id AND
                            status <> 'incomplete'";
            $result = Database::query($sql);
            $count = Database :: num_rows($result);*/
            if ($item['type'] == 'quiz') {
                if ($item['status'] == 'completed') {
                    $html .= "&nbsp;<img id='toc_img_" . $item['id'] . "' src='" . $icon_name[$item['status']] . "' alt='" . substr($item['status'], 0, 1) . "' width='14' />";
                } else {
                    $html .= "&nbsp;<img id='toc_img_" . $item['id'] . "' src='" . $icon_name['not attempted'] . "' alt='" . substr('not attempted', 0, 1) . "' width='14' />";
                }
            } else {
                if ($item['type'] != 'dokeos_chapter' && $item['type'] != 'dokeos_module' && $item['type'] != 'dir') {
                    $html .= "&nbsp;<img id='toc_img_" . $item['id'] . "' src='" . $icon_name[$item['status']] . "' alt='" . substr($item['status'], 0, 1) . "' width='14' />";
                }
            }

            $html .= "</div>";

            if ($scorm_color_background != '') {
                $html .= '</div>';
            }

            $color_counter++;
        }
        $html .= "</div>";
        return $html;
    }

    /**
     * Gets the learnpath maker name - generally the editor's name
     * @return	string	Learnpath maker name
     */
    public function get_maker() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_maker()', 0);
        }
        if (!empty ($this->maker)) {
            return $this->maker;
        } else {
            return '';
        }
    }

    /**
     * Gets the user-friendly message stored in $this->message
     * @return	string	Message
     */
    public function get_message() {

        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_message()', 0);
        }
        return $this->message;
    }

    /**
     * Gets the learnpath name/title
     * @return	string	Learnpath name/title
     */
    public function get_name() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_name()', 0);
        }
        if (!empty ($this->name)) {
            return $this->name;
        } else {
            return 'N/A';
        }
    }

    /**
     * Gets a link to the resource from the present location, depending on item ID.
     * @param	string	Type of link expected
     * @param	integer	Learnpath item ID
     * @return	string	Link to the lp_item resource
     */
    public function get_link($type = 'http', $item_id = null, $provided_toc = false) {
        $course_id = $this->get_course_int_id();

        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_link(' . $type . ',' . $item_id . ')', 0);
        }
        if (empty($item_id)) {
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: ' . $this->get_current_item_id(), 0);
            }
            $item_id = $this->get_current_item_id();
        }

        if (empty($item_id)) {
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
            }
            //still empty, this means there was no item_id given and we are not in an object context or
            //the object property is empty, return empty link
            $item_id = $this->first();
            return '';
        }

        $file = '';
        $lp_table 			= Database::get_course_table(TABLE_LP_MAIN);
        $lp_item_table 		= Database::get_course_table(TABLE_LP_ITEM);
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
        $item_id 			= Database::escape_string($item_id);

        $sql = "SELECT l.lp_type as ltype, l.path as lpath, li.item_type as litype, li.path as lipath, li.parameters as liparams
        		FROM $lp_table l
                INNER JOIN $lp_item_table li
                    ON (li.lp_id = l.id AND l.c_id = $course_id AND li.c_id = $course_id )
        		WHERE li.id = $item_id ";
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_link() - selecting item ' . $sql, 0);
        }
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $lp_type = $row['ltype'];
            $lp_path = $row['lpath'];
            $lp_item_type = $row['litype'];
            $lp_item_path = $row['lipath'];
            $lp_item_params = $row['liparams'];
            if (empty ($lp_item_params) && strpos($lp_item_path, '?') !== false) {
                list ($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
            }
            $sys_course_path = api_get_path(SYS_COURSE_PATH) . api_get_course_path();
            if ($type == 'http') {
                $course_path = api_get_path(WEB_COURSE_PATH) . api_get_course_path(); //web path
            } else {
                $course_path = $sys_course_path; //system path
            }

            // Fixed issue BT#1272 - If the item type is a Chamilo Item (quiz, link, etc), then change the lp type to thread it as a normal Chamilo LP not a SCO.
            if (in_array($lp_item_type, array('quiz', 'document', 'link', 'forum', 'thread', 'student_publication'))) {
                $lp_type = 1;
            }

            if ($this->debug > 2) {
                error_log('New LP - In learnpath::get_link() - $lp_type ' . $lp_type, 0);
                error_log('New LP - In learnpath::get_link() - $lp_item_type ' . $lp_item_type, 0);
            }

            // Now go through the specific cases to get the end of the path
            // @todo Use constants instead of int values.

            switch ($lp_type) {
                case 1 :
                    if ($lp_item_type == 'dokeos_chapter') {
                        $file = 'lp_content.php?type=dir';
                    } else {
                        require_once 'resourcelinker.inc.php';
                        $file = rl_get_resource_link_for_learnpath($course_id, $this->get_id(), $item_id);

                        if ($this->debug > 0) {
                            error_log('rl_get_resource_link_for_learnpath - file: ' . $file, 0);
                        }

                        if ($lp_item_type == 'link') {
                            require_once api_get_path(LIBRARY_PATH).'link.lib.php';
                            if (is_youtube_link($file)) {
                                $src  = get_youtube_video_id($file);
                                $file = 'embed.php?type=youtube&src='.$src;
                            }
                        } else {
                            // check how much attempts of a exercise exits in lp
                            $lp_item_id = $this->get_current_item_id();
                            $lp_view_id = $this->get_view_id();

                            $prevent_reinit = null;
                            if (isset($this->items[$this->current])) {
                                $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
                            }

                            if (empty($provided_toc)) {
                                if ($this->debug > 0) {
                                    error_log('In learnpath::get_link() Loading get_toc ', 0);
                                }
                                $list = $this->get_toc();
                            } else {
                                if ($this->debug > 0) {
                                    error_log('In learnpath::get_link() Loading get_toc from "cache" ', 0);
                                }
                                $list = $provided_toc;
                            }

                            $type_quiz = false;

                            foreach ($list as $toc) {
                                if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
                                    $type_quiz = true;
                                }
                            }

                            if ($type_quiz) {
                                $lp_item_id = Database::escape_string($lp_item_id);
                                $lp_view_id = Database::escape_string($lp_view_id);
                                $sql = "SELECT count(*) FROM $lp_item_view_table
                                        WHERE c_id = $course_id AND lp_item_id='" . (int) $lp_item_id . "' AND lp_view_id ='" . (int) $lp_view_id . "' AND status='completed'";
                                $result = Database::query($sql);
                                $row_count = Database :: fetch_row($result);
                                $count_item_view = (int) $row_count[0];
                                $not_multiple_attempt = 0;
                                if ($prevent_reinit === 1 && $count_item_view > 0) {
                                    $not_multiple_attempt = 1;
                                }
                                $file .= '&not_multiple_attempt=' . $not_multiple_attempt;
                            }

                            $tmp_array = explode('/', $file);
                            $document_name = $tmp_array[count($tmp_array) - 1];
                            if (strpos($document_name, '_DELETED_')) {
                                $file = 'blank.php?error=document_deleted';
                            }
                        }
                    }
                    break;
                case 2 :
                    if ($this->debug > 2) {
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
                    }

                    if ($lp_item_type != 'dir') {
                        // Quite complex here:
                        // We want to make sure 'http://' (and similar) links can
                        // be loaded as is (withouth the Chamilo path in front) but
                        // some contents use this form: resource.htm?resource=http://blablabla
                        // which means we have to find a protocol at the path's start, otherwise
                        // it should not be considered as an external URL.

                        //if ($this->prerequisites_match($item_id)) {
                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
                            if ($this->debug > 2) {
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
                            }
                            // Distant url, return as is.
                            $file = $lp_item_path;
                        } else {
                            if ($this->debug > 2) {
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
                            }
                            // Prevent getting untranslatable urls.
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
                            // Prepare the path.
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
                            // TODO: Fix this for urls with protocol header.
                            $file = str_replace('//', '/', $file);
                            $file = str_replace(':/', '://', $file);
                            if (substr($lp_path, -1) == '/') {
                                $lp_path = substr($lp_path, 0, -1);
                            }

                            if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $lp_item_path))) {
                                // if file not found.
                                $decoded = html_entity_decode($lp_item_path);
                                list ($decoded) = explode('?', $decoded);
                                if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $decoded))) {
                                    require_once 'resourcelinker.inc.php';
                                    $file = rl_get_resource_link_for_learnpath($course_id, $this->get_id(), $item_id);
                                    if (empty($file)) {
                                        $file = 'blank.php?error=document_not_found';
                                    } else {
                                        $tmp_array = explode('/', $file);
                                        $document_name = $tmp_array[count($tmp_array) - 1];
                                        if (strpos($document_name, '_DELETED_')) {
                                            $file = 'blank.php?error=document_deleted';
                                        } else {
                                            $file = 'blank.php?error=document_not_found';
                                        }
                                    }
                                } else {
                                    $file = $course_path . '/scorm/' . $lp_path . '/' . $decoded;
                                }
                            }
                        }
                        //}else{
                        //prerequisites did not match
                        //$file = 'blank.php';
                        //}
                        // We want to use parameters if they were defined in the imsmanifest
                        if (strpos($file, 'blank.php') === false) {
                            $file .= (strstr($file, '?') === false ? '?' : '') . $lp_item_params;
                        }
                    } else {
                        $file = 'lp_content.php?type=dir';
                    }
                    break;
                case 3 :
                    if ($this->debug > 2) {
                        error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
                    }
                    // Formatting AICC HACP append URL.
                    $aicc_append = '?aicc_sid=' . urlencode(session_id()) . '&aicc_url=' . urlencode(api_get_path(WEB_CODE_PATH) . 'newscorm/aicc_hacp.php') . '&';
                    if ($lp_item_type != 'dir') {
                        // Quite complex here:
                        // We want to make sure 'http://' (and similar) links can
                        // be loaded as is (withouth the Chamilo path in front) but
                        // some contents use this form: resource.htm?resource=http://blablabla
                        // which means we have to find a protocol at the path's start, otherwise
                        // it should not be considered as an external URL.

                        if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
                            if ($this->debug > 2) {
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
                            }
                            // Distant url, return as is.
                            $file = $lp_item_path;
                            // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
                            /*
                            if (stristr($file,'<servername>') !== false) {
                                $file = str_replace('<servername>', $course_path.'/scorm/'.$lp_path.'/', $lp_item_path);
                            }
                            */
                            if (stripos($file, '<servername>') !== false) {
                                //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
                                $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
                                $file = str_replace('<servername>', $web_course_path . '/scorm/' . $lp_path, $lp_item_path);
                            }
                            //
                            $file .= $aicc_append;
                        } else {
                            if ($this->debug > 2) {
                                error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
                            }
                            // Prevent getting untranslatable urls.
                            $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
                            $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
                            // Prepare the path - lp_path might be unusable because it includes the "aicc" subdir name.
                            $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
                            // TODO: Fix this for urls with protocol header.
                            $file = str_replace('//', '/', $file);
                            $file = str_replace(':/', '://', $file);
                            $file .= $aicc_append;
                        }
                    } else {
                        $file = 'lp_content.php?type=dir';
                    }
                    break;
                case 4 :
                    break;
                default :
                    break;
            }
        }
        if ($this->debug > 2) {
            error_log('New LP - In learnpath::get_link() - returning "' . $file . '" from get_link', 0);
        }
        return $file;
    }

    /**
     * Gets the latest usable view or generate a new one
     * @param	integer	Optional attempt number. If none given, takes the highest from the lp_view table
     * @return	integer	DB lp_view id
     */
    public function get_view($attempt_num = 0) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_view()', 0);
        }
        $search = '';
        // Use $attempt_num to enable multi-views management (disabled so far).
        if ($attempt_num != 0 AND intval(strval($attempt_num)) == $attempt_num) {
            $search = 'AND view_count = ' . $attempt_num;
        }
        // When missing $attempt_num, search for a unique lp_view record for this lp and user.
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);

        $course_id = api_get_course_int_id();

        $sql = "SELECT id, view_count FROM $lp_view_table
        		WHERE c_id = ".$course_id." AND lp_id = " . $this->get_id() ." AND user_id = " . $this->get_user_id() . " " .$search .
        		" ORDER BY view_count DESC";
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $this->lp_view_id = $row['id'];
        } else {
            // There is no database record, create one.
            $sql = "INSERT INTO $lp_view_table (c_id, lp_id,user_id,view_count) VALUES
            		($course_id, " . $this->get_id() . "," . $this->get_user_id() . ",1)";
            $res = Database::query($sql);
            $id = Database :: insert_id();
            $this->lp_view_id = $id;
        }
        return $this->lp_view_id;
    }

    /**
     * Gets the current view id
     * @return	integer	View ID (from lp_view)
     */
    public function get_view_id() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_view_id()', 0);
        }
        if (!empty ($this->lp_view_id)) {
            return $this->lp_view_id;
        } else {
            return 0;
        }
    }

    /**
     * Gets the update queue
     * @return	array	Array containing IDs of items to be updated by JavaScript
     */
    public function get_update_queue() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_update_queue()', 0);
        }
        return $this->update_queue;
    }

    /**
     * Gets the user ID
     * @return	integer	User ID
     */
    public function get_user_id() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::get_user_id()', 0);
        }
        if (!empty ($this->user_id)) {
            return $this->user_id;
        } else {
            return false;
        }
    }

    /**
     * Checks if any of the items has an audio element attached
     * @return  bool    True or false
     */
    public function has_audio() {
        if ($this->debug > 1) {
            error_log('New LP - In learnpath::has_audio()', 0);
        }
        $has = false;
        foreach ($this->items as $i => $item) {
            if (!empty ($this->items[$i]->audio)) {
                $has = true;
                break;
            }
        }
        return $has;
    }

    /**
     * Logs a message into a file
     * @param	string 	Message to log
     * @return	boolean	True on success, false on error or if msg empty
     */
    public function log($msg) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::log()', 0);
        }
        // TODO
        $this->error .= $msg;
        return true;
    }

    /**
     * Moves an item up and down at its level
     * @param	integer	Item to move up and down
     * @param	string	Direction 'up' or 'down'
     * @return	integer	New display order, or false on error
     */
    public function move_item($id, $direction) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::move_item(' . $id . ',' . $direction . ')', 0);
        }
        if (empty ($id) or empty ($direction)) {
            return false;
        }
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_sel = "SELECT *
                    FROM " . $tbl_lp_item . "
                    WHERE c_id = ".$course_id." AND id = " . $id;
        $res_sel = Database::query($sql_sel);
        // Check if elem exists.
        if (Database :: num_rows($res_sel) < 1) {
            return false;
        }
        // Gather data.
        $row = Database :: fetch_array($res_sel);
        $previous = $row['previous_item_id'];
        $next = $row['next_item_id'];
        $display = $row['display_order'];
        $parent = $row['parent_item_id'];
        $lp = $row['lp_id'];
        // Update the item (switch with previous/next one).
        switch ($direction) {
            case 'up' :
                if ($this->debug > 2) {
                    error_log('Movement up detected', 0);
                }
                if ($display <= 1) { /*do nothing*/
                } else {
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item
                                 WHERE c_id = ".$course_id." AND id = $previous";

                    if ($this->debug > 2) {
                        error_log('Selecting previous: ' . $sql_sel2, 0);
                    }
                    $res_sel2 = Database::query($sql_sel2);
                    if (Database :: num_rows($res_sel2) < 1) {
                        $previous_previous = 0;
                    }
                    // Gather data.
                    $row2 = Database :: fetch_array($res_sel2);
                    $previous_previous = $row2['previous_item_id'];
                    // Update previous_previous item (switch "next" with current).
                    if ($previous_previous != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $id WHERE c_id = ".$course_id." AND id = $previous_previous";
                        if ($this->debug > 2) {
                            error_log($sql_upd2, 0);
                        }
                        $res_upd2 = Database::query($sql_upd2);
                    }
                    // Update previous item (switch with current).
                    if ($previous != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next, previous_item_id = $id, display_order = display_order +1
                                    WHERE c_id = ".$course_id." AND id = $previous";
                        if ($this->debug > 2) {
                            error_log($sql_upd2, 0);
                        }
                        $res_upd2 = Database::query($sql_upd2);
                    }

                    // Update current item (switch with previous).
                    if ($id != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $previous, previous_item_id = $previous_previous, display_order = display_order-1
                                    WHERE c_id = ".$course_id." AND id = $id";
                        if ($this->debug > 2) {
                            error_log($sql_upd2, 0);
                        }
                        $res_upd2 = Database::query($sql_upd2);
                    }
                    // Update next item (new previous item).
                    if ($next != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous
                                     WHERE c_id = ".$course_id." AND id = $next";
                        if ($this->debug > 2) {
                            error_log($sql_upd2, 0);
                        }
                        $res_upd2 = Database::query($sql_upd2);
                    }
                    $display = $display -1;
                }
                break;

            case 'down' :
                if ($this->debug > 2) {
                    error_log('Movement down detected', 0);
                }
                if ($next == 0) { /* Do nothing. */
                } else {
                    $sql_sel2 = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND id = $next";
                    if ($this->debug > 2) {
                        error_log('Selecting next: ' . $sql_sel2, 0);
                    }
                    $res_sel2 = Database::query($sql_sel2);
                    if (Database :: num_rows($res_sel2) < 1) {
                        $next_next = 0;
                    }
                    // Gather data.
                    $row2 = Database :: fetch_array($res_sel2);
                    $next_next = $row2['next_item_id'];
                    // Update previous item (switch with current).
                    if ($previous != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next
                                     WHERE c_id = ".$course_id." AND id = $previous";
                        $res_upd2 = Database::query($sql_upd2);
                    }
                    // Update current item (switch with previous).
                    if ($id != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $next, next_item_id = $next_next, display_order = display_order+1
                                     WHERE c_id = ".$course_id." AND id = $id";
                        $res_upd2 = Database::query($sql_upd2);
                    }

                    // Update next item (new previous item).
                    if ($next != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous, next_item_id = $id, display_order = display_order-1
                                     WHERE c_id = ".$course_id." AND id = $next";
                        $res_upd2 = Database::query($sql_upd2);
                    }

                    // Update next_next item (switch "previous" with current).
                    if ($next_next != 0) {
                        $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $id
                                     WHERE c_id = ".$course_id." AND id = $next_next";
                        $res_upd2 = Database::query($sql_upd2);
                    }
                    $display = $display +1;
                }
                break;
            default :
                return false;
        }
        return $display;
    }

    /**
     * Move a learnpath up (display_order)
     * @param	integer	Learnpath ID
     */
    public function move_up($lp_id) {
        $course_id = api_get_course_int_id();
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
        $res = Database::query($sql);
        if ($res === false)
            return false;
        $lps = array ();
        $lp_order = array ();
        $num = Database :: num_rows($res);
        // First check the order is correct, globally (might be wrong because
        // of versions < 1.8.4)
        if ($num > 0) {
            $i = 1;
            while ($row = Database :: fetch_array($res)) {
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
                    $need_fix = true;
                    $sql_u = "UPDATE $lp_table SET display_order = $i WHERE c_id = ".$course_id." AND id = " . $row['id'];
                    $res_u = Database::query($sql_u);
                }
                $row['display_order'] = $i;
                $lps[$row['id']] = $row;
                $lp_order[$i] = $row['id'];
                $i++;
            }
        }
        if ($num > 1) { // If there's only one element, no need to sort.
            $order = $lps[$lp_id]['display_order'];
            if ($order > 1) { // If it's the first element, no need to move up.
                $sql_u1 = "UPDATE $lp_table SET display_order = $order WHERE c_id = ".$course_id." AND id = " . $lp_order[$order - 1];
                $res_u1 = Database::query($sql_u1);
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order - 1) . " WHERE c_id = ".$course_id." AND id = " . $lp_id;
                $res_u2 = Database::query($sql_u2);
            }
        }
    }

    /**
     * Move a learnpath down (display_order)
     * @param	integer	Learnpath ID
     */
    public function move_down($lp_id) {
        $course_id = api_get_course_int_id();
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
        $res = Database::query($sql);
        if ($res === false)
            return false;
        $lps = array ();
        $lp_order = array ();
        $num = Database :: num_rows($res);
        $max = 0;
        // First check the order is correct, globally (might be wrong because
        // of versions < 1.8.4).
        if ($num > 0) {
            $i = 1;
            while ($row = Database :: fetch_array($res)) {
                $max = $i;
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
                    $need_fix = true;
                    $sql_u = "UPDATE $lp_table SET display_order = $i
                              WHERE c_id = ".$course_id." AND id = " . $row['id'];
                    $res_u = Database::query($sql_u);
                }
                $row['display_order'] = $i;
                $lps[$row['id']] = $row;
                $lp_order[$i] = $row['id'];
                $i++;
            }
        }
        if ($num > 1) { // If there's only one element, no need to sort.
            $order = $lps[$lp_id]['display_order'];
            if ($order < $max) { // If it's the first element, no need to move up.
                $sql_u1 = "UPDATE $lp_table SET display_order = $order
                           WHERE c_id = ".$course_id." AND id = " . $lp_order[$order + 1];
                $res_u1 = Database::query($sql_u1);
                $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order + 1) . "
                           WHERE c_id = ".$course_id." AND id = " . $lp_id;
                $res_u2 = Database::query($sql_u2);
            }
        }
    }

    /**
     * Updates learnpath attributes to point to the next element
     * The last part is similar to set_current_item but processing the other way around
     */
    public function next() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::next()', 0);
        }
        $this->last = $this->get_current_item_id();
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
        $this->autocomplete_parents($this->last);
        $new_index = $this->get_next_index();
        if ($this->debug > 2) {
            error_log('New LP - New index: ' . $new_index, 0);
        }
        $this->index = $new_index;
        if ($this->debug > 2) {
            error_log('New LP - Now having orderedlist[' . $new_index . '] = ' . $this->ordered_items[$new_index], 0);
        }
        $this->current = $this->ordered_items[$new_index];
        if ($this->debug > 2) {
            error_log('New LP - new item id is ' . $this->current . '-' . $this->get_current_item_id(), 0);
        }
    }

    /**
     * Open a resource = initialise all local variables relative to this resource. Depending on the child
     * class, this might be redefined to allow several behaviours depending on the document type.
     * @param integer Resource ID
     * @return boolean True on success, false otherwise
     */
    public function open($id) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::open()', 0);
        }
        // TODO:
        // set the current resource attribute to this resource
        // switch on element type (redefine in child class?)
        // set status for this item to "opened"
        // start timer
        // initialise score
        $this->index = 0; //or = the last item seen (see $this->last)
    }

    /**
     * Check that all prerequisites are fulfilled. Returns true and an empty string on succes, returns false
     * and the prerequisite string on error.
     * This function is based on the rules for aicc_script language as described in the SCORM 1.2 CAM documentation page 108.
     * @param	integer	Optional item ID. If none given, uses the current open item.
     * @return	boolean	True if prerequisites are matched, false otherwise - Empty string if true returned, prerequisites string otherwise.
     */
    public function prerequisites_match($item = null) {
        if ($this->debug > 0) {
            error_log('In learnpath::prerequisites_match()', 0);
        }
        if (empty($item)) {
            $item = $this->current;
        }
        if (is_object($this->items[$item])) {

            if ($this->type == 2) {
                //Getting prereq from scorm
                $prereq_string = $this->get_scorm_prereq_string($item);
            } else {
                $prereq_string = $this->items[$item]->get_prereq_string();
            }

            if (empty($prereq_string)) {
                return true;
            }
            // Clean spaces.
            $prereq_string = str_replace(' ', '', $prereq_string);
            if ($this->debug > 0) {
                error_log('Found prereq_string: ' . $prereq_string, 0);
            }
            // Now send to the parse_prereq() function that will check this component's prerequisites.
            $result = $this->items[$item]->parse_prereq($prereq_string, $this->items, $this->refs_list, $this->get_user_id());

            if ($result === false) {
                $this->set_error_msg($this->items[$item]->prereq_alert);
            }
        } else {
            $result = true;
            if ($this->debug > 1) {
                error_log('$this->items[' . $item . '] was not an object', 0);
            }
        }

        if ($this->debug > 1) {
            error_log('End of prerequisites_match(). Error message is now ' . $this->error, 0);
        }
        return $result;
    }

    /**
     * Updates learnpath attributes to point to the previous element
     * The last part is similar to set_current_item but processing the other way around
     */
    public function previous() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::previous()', 0);
        }
        $this->last = $this->get_current_item_id();
        $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
        $this->autocomplete_parents($this->last);
        $new_index = $this->get_previous_index();
        $this->index = $new_index;
        $this->current = $this->ordered_items[$new_index];
    }

    /**
     * Publishes a learnpath. This basically means show or hide the learnpath
     * to normal users.
     * Can be used as abstract
     * @param	integer	Learnpath ID
     * @param	string	New visibility
     */
    public function toggle_visibility($lp_id, $set_visibility = 1) {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::toggle_visibility()', 0); }
        $action = 'visible';
        if ($set_visibility != 1) {
            $action = 'invisible';
        }
        return api_item_property_update(api_get_course_info(), TOOL_LEARNPATH, $lp_id, $action, api_get_user_id());
    }

    /**
     * Publishes a learnpath. This basically means show or hide the learnpath
     * on the course homepage
     * Can be used as abstract
     * @param	integer	Learnpath id
     * @param	string	New visibility (v/s - visible/invisible)
     */
    public static function toggle_publish($lp_id, $set_visibility = 'v') {
        //if ($this->debug > 0) { error_log('New LP - In learnpath::toggle_publish()', 0); }
        $course_id = api_get_course_int_id();
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = Database::escape_string($lp_id);
        $sql = "SELECT * FROM $tbl_lp where c_id = ".$course_id." AND id=$lp_id";
        $result = Database::query($sql);
        if (Database::num_rows($result)) {
            $row = Database :: fetch_array($result);
            $name = domesticate($row['name']);
            if ($set_visibility == 'i') {
                $s = $name . " " . get_lang('LearnpathNotPublished');
                $dialogBox = $s;
                $v = 0;
            }
            if ($set_visibility == 'v') {
                $s = $name . " " . get_lang('LearnpathPublished');
                $dialogBox = $s;
                $v = 1;
            }
        } else {
            return false;
        }

        $session_id = api_get_session_id();
        $session_condition = api_get_session_condition($session_id);

        $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
        $link = 'newscorm/lp_controller.php?action=view&lp_id=' . $lp_id.'&id_session='.$session_id;
        $sql = "SELECT * FROM $tbl_tool WHERE c_id = ".$course_id." AND link='$link' and image='scormbuilder.gif' and link LIKE '$link%' $session_condition";
        $result = Database::query($sql);
        $num = Database :: num_rows($result);
        //if ($this->debug > 2) { error_log('New LP - '.$sql.' - '.$num, 0); }
        if (($set_visibility == 'i') && ($num > 0)) {
            $sql = "DELETE FROM $tbl_tool WHERE c_id = ".$course_id." AND (link='$link' and image='scormbuilder.gif' $session_condition)";
        } elseif (($set_visibility == 'v') && ($num == 0)) {
            $sql = "INSERT INTO $tbl_tool (c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
            	    ($course_id, '$name','$link','scormbuilder.gif','$v','0','pastillegris.gif',0, $session_id)";
        } elseif (($set_visibility == 'v') && ($num > 0)) {
            $sql = "UPDATE $tbl_tool SET c_id = $course_id, name = '$name', link = '$link', image = 'scormbuilder.gif', visibility = '$v', admin = '0', address = 'pastillegris.gif', added_tool = 0, session_id = $session_id
            	    WHERE c_id = ".$course_id." AND (link='$link' and image='scormbuilder.gif' $session_condition)";
        } else {
            // Parameter and database incompatible, do nothing, exit.
            return false;
        }
        $result = Database::query($sql);
        //if ($this->debug > 2) { error_log('New LP - Leaving learnpath::toggle_visibility: '.$sql, 0); }
    }

    /**
     * Restart the whole learnpath. Return the URL of the first element.
     * Make sure the results are saved with anoter method. This method should probably be
     * redefined in children classes.
     * To use a similar method  statically, use the create_new_attempt() method
     * @return string URL to load in the viewer
     */
    public function restart() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::restart()', 0);
        }
        // TODO
        // Call autosave method to save the current progress.
        //$this->index = 0;
        $session_id = api_get_session_id();
        $course_id = api_get_course_int_id();
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
        $sql = "INSERT INTO $lp_view_table (c_id, lp_id, user_id, view_count, session_id) " .
        	   "VALUES ($course_id, " . $this->lp_id . "," . $this->get_user_id() . "," . ($this->attempt + 1) . ", $session_id)";
        if ($this->debug > 2) {
            error_log('New LP - Inserting new lp_view for restart: ' . $sql, 0);
        }
        $res = Database::query($sql);
        if ($view_id = Database :: insert_id($res)) {
            $this->lp_view_id = $view_id;
            $this->attempt = $this->attempt + 1;
        } else {
            $this->error = 'Could not insert into item_view table...';
            return false;
        }
        $this->autocomplete_parents($this->current);
        foreach ($this->items as $index => $dummy) {
            $this->items[$index]->restart();
            $this->items[$index]->set_lp_view($this->lp_view_id);
        }
        $this->first();
        return true;
    }

    /**
     * Saves the current item
     * @return	boolean
     */
    public function save_current() {
        if ($this->debug > 0) {
            error_log('learnpath::save_current()', 0);
        }
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
        if ($this->debug > 2) {
            error_log('New LP - save_current() saving item ' . $this->current, 0);
        }
        if ($this->debug > 2) {
            error_log('' . print_r($this->items, true), 0);
        }
        if (is_object($this->items[$this->current])) {
            //$res = $this->items[$this->current]->save(false);
            $res = $this->items[$this->current]->save(false, $this->prerequisites_match($this->current));
            $this->autocomplete_parents($this->current);
            $status = $this->items[$this->current]->get_status();
            $this->append_message('new_item_status: ' . $status);
            $this->update_queue[$this->current] = $status;
            return $res;
        }
        return false;
    }

    /**
     * Saves the given item
     * @param	integer	Item ID. Optional (will take from $_REQUEST if null)
     * @param	boolean	Save from url params (true) or from current attributes (false). Optional. Defaults to true
     * @return	boolean
     */
    public function save_item($item_id = null, $from_outside = true) {
        $debug = $this->debug;
        if ($debug) {
            error_log('In learnpath::save_item(' . $item_id . ',' . intval($from_outside). ')', 0);
        }
        // TODO: Do a better check on the index pointing to the right item (it is supposed to be working
        // on $ordered_items[] but not sure it's always safe to use with $items[]).
        if (empty($item_id)) {
            $item_id = intval($_REQUEST['id']);
        }
        if (empty($item_id)) {
            $item_id = $this->get_current_item_id();
        }
        if (isset($this->items[$item_id]) && is_object($this->items[$item_id])) {
            if ($debug) { error_log('Object exists'); }
            $res = $this->items[$item_id]->save($from_outside, $this->prerequisites_match($item_id));
            if ($debug) {
                error_log('update_queue before:');
                error_log(print_r($this->update_queue,1));
            }
            $this->autocomplete_parents($item_id);

            $status = $this->items[$item_id]->get_status();
            $this->update_queue[$item_id] = $status;

            if ($debug) {
                error_log('get_status(): ' . $status);
                error_log('update_queue after:');
                error_log(print_r($this->update_queue,1));
            }
            return $res;
        }
        return false;
    }

    /**
     * Saves the last item seen's ID only in case
     */
    public function save_last() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::save_last()', 0);
        }
        $session_condition = api_get_session_condition(api_get_session_id(), true, false);
        $table = Database :: get_course_table(TABLE_LP_VIEW);
        if (isset($this->current)) {
            if ($this->debug > 2) {
                error_log('New LP - Saving current item (' . $this->current . ') for later review', 0);
            }
            $sql = "UPDATE $table SET last_item = " . Database::escape_string($this->get_current_item_id()). "
                    WHERE   c_id = $course_id AND
                            lp_id = " . $this->get_id() . " AND
                            user_id = " . $this->get_user_id()." ".$session_condition;

            if ($this->debug > 2) {
                error_log('New LP - Saving last item seen : ' . $sql, 0);
            }
            $res = Database::query($sql);
        }

        // Save progress.
        list($progress, $text) = $this->get_progress_bar_text('%');
        if ($progress >= 0 && $progress <= 100) {
            $progress = (int) $progress;
            $sql = "UPDATE $table SET progress = $progress
                    WHERE   c_id = ".$course_id." AND
                            lp_id = " . $this->get_id() . " AND
                            user_id = " . $this->get_user_id()." ".$session_condition;
            $res = Database::query($sql); // Ignore errors as some tables might not have the progress field just yet.
            $this->progress_db = $progress;
        }
    }

    /**
     * Sets the current item ID (checks if valid and authorized first)
     * @param	integer	New item ID. If not given or not authorized, defaults to current
     */
    public function set_current_item($item_id = null) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_current_item(' . $item_id . ')', 0);
        }
        if (empty ($item_id)) {
            if ($this->debug > 2) {
                error_log('New LP - No new current item given, ignore...', 0);
            }
            // Do nothing.
        } else {
            if ($this->debug > 2) {
                error_log('New LP - New current item given is ' . $item_id . '...', 0);
            }
            if (is_numeric($item_id)) {
                $item_id = Database::escape_string($item_id);
                // TODO: Check in database here.
                $this->last = $this->current;
                $this->current = $item_id;
                // TODO: Update $this->index as well.
                foreach ($this->ordered_items as $index => $item) {
                    if ($item == $this->current) {
                        $this->index = $index;
                        break;
                    }
                }
                if ($this->debug > 2) {
                    error_log('New LP - set_current_item(' . $item_id . ') done. Index is now : ' . $this->index, 0);
                }
            } else {
                error_log('New LP - set_current_item(' . $item_id . ') failed. Not a numeric value: ', 0);
            }
        }
    }

    /**
     * Sets the encoding
     * @param	string	New encoding
     * TODO (as of Chamilo 1.8.8): Check in the future whether this method is needed.
     */
    public function set_encoding($enc = 'UTF-8') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_encoding()', 0);
        }

        $course_id = api_get_course_int_id();

        /* // Deprecated code (Chamilo 1.8.8).
        $enc = strtoupper($enc);
        $encodings = array (
            'UTF-8',
            'ISO-8859-1',
            'ISO-8859-15',
            'cp1251',
            'cp1252',
            'KOI8-R',
            'BIG5',
            'GB2312',
            'Shift_JIS',
            'EUC-JP',
            ''
        );
        if (in_array($enc, $encodings)) { // TODO: Incorrect comparison, fix it.
            $lp = $this->get_id();
            if ($lp != 0) {
                $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' WHERE id = " . $lp;
                $res = Database::query($sql);
                return $res;
            }
        }
        return false;
        */

        $enc = api_refine_encoding_id($enc);
        if (empty($enc)) {
            $enc = api_get_system_encoding();
        }
        if (api_is_encoding_supported($enc)) {
            $lp = $this->get_id();
            if ($lp != 0) {
                $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
                $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' WHERE c_id = ".$course_id." AND id = " . $lp;
                $res = Database::query($sql);
                return $res;
            }
        }
        return false;
    }

    /**
     * Sets the JS lib setting in the database directly.
     * This is the JavaScript library file this lp needs to load on startup
     * @param	string	Proximity setting
     * @return  boolean True on update success. False otherwise.
     */
    public function set_jslib($lib = '') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_jslib()', 0);
        }
        $lp = $this->get_id();
        $course_id = api_get_course_int_id();

        if ($lp != 0) {
            $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET js_lib = '$lib' WHERE c_id = ".$course_id." AND id = " . $lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the name of the LP maker (publisher) (and save)
     * @param	string	Optional string giving the new content_maker of this learnpath
     * @return  boolean True
     */
    public function set_maker($name = '') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_maker()', 0);
        }
        if (empty ($name))
            return false;
        $this->maker = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $course_id = api_get_course_int_id();
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET content_maker = '" . $this->maker . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new content_maker : ' . $this->maker, 0);
        }
        Database::query($sql);
        return true;
    }

    /**
     * Sets the name of the current learnpath (and save)
     * @param	string	Optional string giving the new name of this learnpath
     * @return  boolean True/False
     */
    public function set_name($name = null) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_name()', 0);
        }
        if (empty($name)) {
            return false;
        }
        $this->name = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $course_id = api_get_course_int_id();
        $sql = "UPDATE $lp_table SET name = '" . $this->name . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new name : ' . $this->name, 0);
        }
        Database::query($sql);
        // If the lp is visible on the homepage, change his name there.
        if (Database::affected_rows()) {
            $session_id = api_get_session_id();
            $session_condition = api_get_session_condition($session_id);
            $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
            $link = 'newscorm/lp_controller.php?action=view&lp_id=' . $lp_id.'&id_session='.$session_id;
            $sql = "UPDATE $tbl_tool SET name = '$this->name'
            	    WHERE c_id = ".$course_id." AND (link='$link' and image='scormbuilder.gif' $session_condition)";
            Database::query($sql);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Set index specified prefix terms for all items in this path
     * @param   string  Comma-separated list of terms
     * @param   char Xapian term prefix
     * @return  boolean False on error, true otherwise
     */
    public function set_terms_by_prefix($terms_string, $prefix) {
        $course_id = api_get_course_int_id();
        if (api_get_setting('search_enabled') !== 'true')
            return false;

        if (!extension_loaded('xapian')) {
            return false;
        }

        $terms_string = trim($terms_string);
        $terms = explode(',', $terms_string);
        array_walk($terms, 'trim_value');

        $stored_terms = $this->get_common_index_terms_by_prefix($prefix);

        // Don't do anything if no change, verify only at DB, not the search engine.
        if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0))
            return false;

        require_once 'xapian.php'; // TODO: Try catch every xapian use or make wrappers on API.
        require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
        require_once api_get_path(LIBRARY_PATH).'search/xapian/XapianQuery.php';
        require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';

        $items_table = Database :: get_course_table(TABLE_LP_ITEM);
        // TODO: Make query secure agains XSS : use member attr instead of post var.
        $lp_id = intval($_POST['lp_id']);
        $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND lp_id = $lp_id";
        $result = Database::query($sql);
        $di = new ChamiloIndexer();

        while ($lp_item = Database :: fetch_array($result)) {
            // Get search_did.
            $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
            $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
            $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);

            //echo $sql; echo '<br>';
            $res = Database::query($sql);
            if (Database::num_rows($res) > 0) {

                $se_ref = Database :: fetch_array($res);

                // Compare terms.
                $doc = $di->get_document($se_ref['search_did']);

                $xapian_terms = xapian_get_doc_terms($doc, $prefix);

                $xterms = array ();
                foreach ($xapian_terms as $xapian_term) {
                    $xterms[] = substr($xapian_term['name'], 1);
                }

                $dterms = $terms;

                $missing_terms = array_diff($dterms, $xterms);
                $deprecated_terms = array_diff($xterms, $dterms);

                // Save it to search engine.
                foreach ($missing_terms as $term) {
                    $doc->add_term($prefix . $term, 1);
                }
                foreach ($deprecated_terms as $term) {
                    $doc->remove_term($prefix . $term);
                }
                $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
                $di->getDb()->flush();
            } else {
                //@todo What we should do here?
            }
        }
        return true;
    }

    /**
     * Sets the theme of the LP (local/remote) (and save)
     * @param	string	Optional string giving the new theme of this learnpath
     * @return   bool    Returns true if theme name is not empty
     */
    public function set_theme($name = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_theme()', 0);
        }
        $this->theme = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET theme = '" . $this->theme . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new theme : ' . $this->theme, 0);
        }
        //$res = Database::query($sql);
        $res = Database::query($sql);
        return true;
    }

    /**
     * Sets the image of an LP (and save)
     * @param	 string	Optional string giving the new image of this learnpath
     * @return bool   Returns true if theme name is not empty
     */
    public function set_preview_image($name = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_preview_image()', 0);
        }

        $this->preview_image = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET preview_image = '" . $this->preview_image . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new preview image : ' . $this->preview_image, 0);
        }
        $res = Database::query($sql);
        return true;
    }

    /**
     * Sets the author of a LP (and save)
     * @param	string	Optional string giving the new author of this learnpath
     * @return   bool    Returns true if author's name is not empty
     */
    public function set_author($name = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_author()', 0);
        }
        $this->author = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET author = '" . $this->author . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new preview author : ' . $this->author, 0);
        }
        Database::query($sql);
        return true;
	}
	/**
	* Sets the hide_toc_frame parameter of a LP (and save)
	* @param	int	1 if frame is hiddent 0 thenelse
	* @return   bool    Returns true if author's name is not empty
	*/
	public function set_hide_toc_frame($hide) {
	    $course_id = api_get_course_int_id();
		if ($this->debug > 0) {
			error_log('New LP - In learnpath::set_hide_toc_frame()', 0);
		}
        if (intval($hide) == $hide){
            $this->hide_toc_frame = $hide;
            $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
            $lp_id = $this->get_id();
            $sql = "UPDATE $lp_table SET hide_toc_frame = '" . $this->hide_toc_frame . "'
            WHERE c_id = ".$course_id." AND id = '$lp_id'";
            if ($this->debug > 2) {
                error_log('New LP - lp updated with new preview hide_toc_frame : ' . $this->author, 0);
            }
            Database::query($sql);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Sets the prerequisite of a LP (and save)
     * @param	int		integer giving the new prerequisite of this learnpath
     * @return 	bool 	returns true if prerequisite is not empty
     */
    public function set_prerequisite($prerequisite) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_prerequisite()', 0);
        }
        $this->prerequisite = intval($prerequisite);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET prerequisite = '".$this->prerequisite."'
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new preview requisite : ' . $this->requisite, 0);
        }
        Database::query($sql);
        return true;
    }

    /**
     * Sets the location/proximity of the LP (local/remote) (and save)
     * @param	string	Optional string giving the new location of this learnpath
     * @return  boolean True on success / False on error
     */
    public function set_proximity($name = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_proximity()', 0);
        }
        if (empty ($name))
            return false;

        $this->proximity = Database::escape_string($name);
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET content_local = '" . $this->proximity . "'
                WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new proximity : ' . $this->proximity, 0);
        }
        $res = Database::query($sql);
        return true;
    }

    /**
     * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
     * @param	integer	DB ID of the item
     */
    public function set_previous_item($id) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_previous_item()', 0);
        }
        $this->last = $id;
    }


     /**
     * Sets use_max_score
     * @param   string  Optional string giving the new location of this learnpath
     * @return  boolean True on success / False on error
     */
    public function set_use_max_score($use_max_score = 0) { // BAT : set to 0 by default. Was 1 before /BAT
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_use_max_score()', 0);
        }
        $use_max_score = intval($use_max_score);
        $this->use_max_score = $use_max_score;
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET use_max_score = '" . $this->use_max_score . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";

        if ($this->debug > 2) {
            error_log('New LP - lp updated with new use_max_score : ' . $this->use_max_score, 0);
        }
        $res = Database::query($sql);
        return true;
    }

     /**
     * Sets and saves the expired_on date
     * @param   string  Optional string giving the new author of this learnpath
     * @return   bool    Returns true if author's name is not empty
     */
    public function set_expired_on($expired_on) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_expired_on()', 0);
        }

        if (!empty($expired_on)) {
            $this->expired_on = Database::escape_string(api_get_utc_datetime($expired_on));
        } else {
            $this->expired_on = '';
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET expired_on = '" . $this->expired_on . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new expired_on : ' . $this->expired_on, 0);
        }
        $res = Database::query($sql);
        return true;
    }

    /**
     * Sets and saves the publicated_on date
     * @param   string  Optional string giving the new author of this learnpath
     * @return   bool    Returns true if author's name is not empty
     */
    public function set_publicated_on($publicated_on) {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_expired_on()', 0);
        }
        if (!empty($publicated_on)) {
            $this->publicated_on = Database::escape_string(api_get_utc_datetime($publicated_on));
        } else {
            $this->publicated_on = '';
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET publicated_on = '" . $this->publicated_on . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new publicated_on : ' . $this->publicated_on, 0);
        }
        $res = Database::query($sql);
        return true;
    }



    /**
     * Sets and saves the expired_on date
     * @param   string  Optional string giving the new author of this learnpath
     * @return   bool    Returns true if author's name is not empty
     */
    public function set_modified_on() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_expired_on()', 0);
        }
        $this->modified_on = api_get_utc_datetime();
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $lp_id = $this->get_id();
        $sql = "UPDATE $lp_table SET modified_on = '" . $this->modified_on . "' WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) {
            error_log('New LP - lp updated with new expired_on : ' . $this->modified_on, 0);
        }
        Database::query($sql);
        return true;
    }

    /**
     * Sets the object's error message
     * @param	string	Error message. If empty, reinits the error string
     * @return 	void
     */
    public function set_error_msg($error = '') {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::set_error_msg()', 0);
        }
        if (empty ($error)) {
            $this->error = '';
        } else {
            $this->error .= $error;
        }
    }

    /**
     * Launches the current item if not 'sco' (starts timer and make sure there is a record ready in the DB)
     * @param  boolean     Whether to allow a new attempt or not
     * @return boolean     True
     */
    public function start_current_item($allow_new_attempt = false) {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::start_current_item()', 0);
        }
        if ($this->current != 0 AND is_object($this->items[$this->current])) {
            $type = $this->get_type();
            $item_type = $this->items[$this->current]->get_type();
            if (($type == 2 && $item_type != 'sco') OR ($type == 3 && $item_type != 'au') OR ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)) {
                $this->items[$this->current]->open($allow_new_attempt);

                $this->autocomplete_parents($this->current);
                $prereq_check = $this->prerequisites_match($this->current);
                $this->items[$this->current]->save(false, $prereq_check);
                //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
            } else {
                // If sco, then it is supposed to have been updated by some other call.
            }
            if ($item_type == 'sco') {
                $this->items[$this->current]->restart();
            }
        }
        if ($this->debug > 0) {
            error_log('New LP - End of learnpath::start_current_item()', 0);
        }
        return true;
    }

    /**
     * Stops the processing and counters for the old item (as held in $this->last)
     * @return boolean  True/False
     */
    public function stop_previous_item() {
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::stop_previous_item()', 0);
        }

        if ($this->last != 0 && $this->last != $this->current && is_object($this->items[$this->last])) {
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' is object', 0);
            }
            switch ($this->get_type()) {
                case '3' :
                    if ($this->items[$this->last]->get_type() != 'au') {
                        if ($this->debug > 2) {
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 3 is <> au', 0);
                        }
                        $this->items[$this->last]->close();
                        //$this->autocomplete_parents($this->last);
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
                    } else {
                        if ($this->debug > 2) {
                            error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
                        }
                    }
                case '2' :
                    if ($this->items[$this->last]->get_type() != 'sco') {
                        if ($this->debug > 2) {
                            error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 2 is <> sco', 0);
                        }
                        $this->items[$this->last]->close();
                        //$this->autocomplete_parents($this->last);
                        //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
                    } else {
                        if ($this->debug > 2) {
                            error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
                        }
                    }
                    break;
                case '1' :
                default :
                    if ($this->debug > 2) {
                        error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 1 is asset', 0);
                    }
                    $this->items[$this->last]->close();
                    break;
            }
        } else {
            if ($this->debug > 2) {
                error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
            }
            return false;
        }
        return true;
    }

    /**
     * Updates the default view mode from fullscreen to embedded and inversely
     * @return	string The current default view mode ('fullscreen' or 'embedded')
     */
    public function update_default_view_mode() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::update_default_view_mode()', 0);
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $view_mode = $row['default_view_mod'];
            if ($view_mode == 'fullscreen') {
                $view_mode = 'embedded';
            } elseif ($view_mode == 'embedded') {
                $view_mode = 'embedframe';
            } elseif ($view_mode == 'embedframe') {
                $view_mode = 'fullscreen';
            }
            $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode' WHERE c_id = ".$course_id." AND id = " . $this->get_id();
            $res = Database::query($sql);
            $this->mode = $view_mode;
            return $view_mode;
        } else {
            if ($this->debug > 2) {
                error_log('New LP - Problem in update_default_view() - could not find LP ' . $this->get_id() . ' in DB', 0);
            }
        }
        return -1;
    }

    /**
     * Updates the default behaviour about auto-commiting SCORM updates
     * @return	boolean	True if auto-commit has been set to 'on', false otherwise
     */
    public function update_default_scorm_commit() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $force = $row['force_commit'];
            if ($force == 1) {
                $force = 0;
                $force_return = false;
            } elseif ($force == 0) {
                $force = 1;
                $force_return = true;
            }
            $sql = "UPDATE $lp_table SET force_commit = $force WHERE c_id = ".$course_id." AND id = " . $this->get_id();
            $res = Database::query($sql);
            $this->force_commit = $force_return;
            return $force_return;
        } else {
            if ($this->debug > 2) {
                error_log('New LP - Problem in update_default_scorm_commit() - could not find LP ' . $this->get_id() . ' in DB', 0);
            }
        }
        return -1;
    }

    /**
     * Updates the order of learning paths (goes through all of them by order and fills the gaps)
     * @return	bool	True on success, false on failure
     */
    public function update_display_order() {
        $course_id = api_get_course_int_id();
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);

        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." ORDER BY display_order";
        $res = Database::query($sql);
        if ($res === false)
            return false;
        $lps = array ();
        $lp_order = array ();
        $num = Database :: num_rows($res);
        // First check the order is correct, globally (might be wrong because
        // of versions < 1.8.4).
        if ($num > 0) {
            $i = 1;
            while ($row = Database :: fetch_array($res)) {
                if ($row['display_order'] != $i) { // If we find a gap in the order, we need to fix it.
                    $need_fix = true;
                    $sql_u = "UPDATE $lp_table SET display_order = $i WHERE c_id = ".$course_id." AND id = " . $row['id'];
                    $res_u = Database::query($sql_u);
                }
                $i++;
            }
        }
        return true;
    }

    /**
     * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
     * @return	boolean	True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
     */
    public function update_reinit() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::update_reinit()', 0);
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $force = $row['prevent_reinit'];
            if ($force == 1) {
                $force = 0;
            } elseif ($force == 0) {
                $force = 1;
            }
            $sql = "UPDATE $lp_table SET prevent_reinit = $force WHERE c_id = ".$course_id." AND id = " . $this->get_id();
            $res = Database::query($sql);
            $this->prevent_reinit = $force;
            return $force;
        } else {
            if ($this->debug > 2) {
                error_log('New LP - Problem in update_reinit() - could not find LP ' . $this->get_id() . ' in DB', 0);
            }
        }
        return -1;
    }

    /**
     * Determine the attempt_mode thanks to prevent_reinit and seriousgame_mode db flag
     *
     * @return string 'single', 'multi' or 'seriousgame'
     * @author ndiechburg <noel@cblue.be>
     **/
    public function get_attempt_mode() {
        if (!isset($this->seriousgame_mode)) { //Set default value for seriousgame_mode
            $this->seriousgame_mode=0;
        }
        if (!isset($this->prevent_reinit)) { // Set default value for prevent_reinit
            $this->prevent_reinit =1;
        }
        if ($this->seriousgame_mode == 1 && $this->prevent_reinit == 1) {
            return 'seriousgame';
        }
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 1) {
            return 'single';
        }
        if ($this->seriousgame_mode == 0 && $this->prevent_reinit == 0) {
            return 'multiple';
        }
        return 'single';
    }

    /**
     * Register the attempt mode into db thanks to flags prevent_reinit and seriousgame_mode flags
     *
     * @param string 'seriousgame', 'single' or 'multiple'
     * @return boolean
     * @author ndiechburg <noel@cblue.be>
     **/
    public function set_attempt_mode($mode) {
        $course_id = api_get_course_int_id();
        switch ($mode) {
            case 'seriousgame' :
              $sg_mode = 1;
              $prevent_reinit = 1;
              break;
            case 'single' :
              $sg_mode = 0;
              $prevent_reinit = 1;
              break;
            case 'multiple' :
              $sg_mode = 0;
              $prevent_reinit = 0;
              break;
            default :
              $sg_mode = 0;
              $prevent_reinit = 0;
              break;
        }
        $this->prevent_reinit = $prevent_reinit;
        $this->seriousgame_mode = $sg_mode;
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "UPDATE $lp_table SET prevent_reinit = $prevent_reinit , seriousgame_mode = $sg_mode WHERE c_id = ".$course_id." AND id = " . $this->get_id();
        $res = Database::query($sql);
        if ($res) {
            return true;
        } else {
          return false;
        }
    }

  /**
   * switch between multiple attempt, single attempt or serious_game mode (only for scorm)
   *
   * @return boolean
   * @author ndiechburg <noel@cblue.be>
   **/
    public function switch_attempt_mode() {
        if ($this->debug > 0) {
          error_log('New LP - In learnpath::switch_attempt_mode()', 0);
        }
        $mode = $this->get_attempt_mode();
        switch ($mode) {
            case 'single' :
              $next_mode = 'multiple';
              break;
            case 'multiple' :
              $next_mode = 'seriousgame';
              break;
            case 'seriousgame' :
              $next_mode = 'single';
              break;
            default :
              $next_mode = 'single';
              break;
        }
        $this->set_attempt_mode($next_mode);
    }

  /**
   * Swithc the lp in ktm mode. This is a special scorm mode with unique attempt but possibility to do again a completed item.
   *
   * @return boolean true if seriousgame_mode has been set to 1, false otherwise
   * @author ndiechburg <noel@cblue.be>
   **/
    public function set_seriousgame_mode() {
        $course_id = api_get_course_int_id();
		if ($this->debug > 0) {
			error_log('New LP - In learnpath::set_seriousgame_mode()', 0);
		}
		$lp_table = Database :: get_course_table(TABLE_LP_MAIN);
		$sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
		$res = Database::query($sql);
		if (Database :: num_rows($res) > 0) {
			$row = Database :: fetch_array($res);
			$force = $row['seriousgame_mode'];
			if ($force == 1) {
				$force = 0;
			} elseif ($force == 0) {
				$force = 1;
			}
			$sql = "UPDATE $lp_table SET seriousgame_mode = $force WHERE c_id = ".$course_id." AND id = " . $this->get_id();
			$res = Database::query($sql);
			$this->seriousgame_mode = $force;
			return $force;
		} else {
			if ($this->debug > 2) {
				error_log('New LP - Problem in set_seriousgame_mode() - could not find LP ' . $this->get_id() . ' in DB', 0);
			}
		}
		return -1;
    }

    /**
     * Updates the "scorm_debug" value that shows or hide the debug window
     * @return	boolean	True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
     */
    public function update_scorm_debug() {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::update_scorm_debug()', 0);
        }
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = " . $this->get_id();
        $res = Database::query($sql);
        if (Database :: num_rows($res) > 0) {
            $row = Database :: fetch_array($res);
            $force = $row['debug'];
            if ($force == 1) {
                $force = 0;
            } elseif ($force == 0) {
                $force = 1;
            }
            $sql = "UPDATE $lp_table SET debug = $force WHERE c_id = ".$course_id." AND id = " . $this->get_id();
            $res = Database::query($sql);
            $this->scorm_debug = $force;
            return $force;
        } else {
            if ($this->debug > 2) {
                error_log('New LP - Problem in update_scorm_debug() - could not find LP ' . $this->get_id() . ' in DB', 0);
            }
        }
        return -1;
    }

    /**
     * Function that makes a call to the function sort_tree_array and create_tree_array
     * @author Kevin Van Den Haute
     * @param  array
     */
    public function tree_array($array) {
        if ($this->debug > 1) {
            error_log('New LP - In learnpath::tree_array()', 0);
        }
        $array = $this->sort_tree_array($array);
        $this->create_tree_array($array);
    }

    /**
     * Creates an array with the elements of the learning path tree in it
     *
     * @author Kevin Van Den Haute
     * @param array $array
     * @param int $parent
     * @param int $depth
     * @param array $tmp
     */
    public function create_tree_array($array, $parent = 0, $depth = -1, $tmp = array ()) {
        if ($this->debug > 1) {
            error_log('New LP - In learnpath::create_tree_array())', 0);
        }
        if (is_array($array)) {
            for ($i = 0; $i < count($array); $i++) {
                if ($array[$i]['parent_item_id'] == $parent) {
                    if (!in_array($array[$i]['parent_item_id'], $tmp)) {
                        $tmp[] = $array[$i]['parent_item_id'];
                        $depth++;
                    }
                    $preq = (empty($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
                    $audio = isset($array[$i]['audio']) ? $array[$i]['audio'] : null;
                    $this->arrMenu[] = array (
                        'id' => $array[$i]['id'],
                        'item_type' => $array[$i]['item_type'],
                        'title' => $array[$i]['title'],
                        'path' => $array[$i]['path'],
                        'description' => $array[$i]['description'],
                        'parent_item_id' => $array[$i]['parent_item_id'],
                        'previous_item_id' => $array[$i]['previous_item_id'],
                        'next_item_id' => $array[$i]['next_item_id'],
                        'min_score' => $array[$i]['min_score'],
                        'max_score' => $array[$i]['max_score'],
                        'mastery_score' => $array[$i]['mastery_score'],
                        'display_order' => $array[$i]['display_order'],
                        'prerequisite' => $preq,
                        'depth' => $depth,
                        'audio' => $audio
                    );

                    $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
                }
            }
        }
    }

    /**
     * Sorts a multi dimensional array by parent id and display order
     * @author Kevin Van Den Haute
     *
     * @param array $array (array with al the learning path items in it)
     *
     * @return array
     */
    public function sort_tree_array($array) {
        foreach ($array as $key => $row) {
            $parent[$key] = $row['parent_item_id'];
            $position[$key] = $row['display_order'];
        }

        if (count($array) > 0)
            array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);

        return $array;
    }

    /**
     * Function that creates a table structure with a learning path his modules, chapters and documents.
     * Also the actions for the modules, chapters and documents are in this table.
     * @author Kevin Van Den Haute
     * @param int $lp_id
     * @return string
     */
    public function overview() {
        $is_allowed_to_edit = api_is_allowed_to_edit(null,true);

        if ($this->debug > 0) {
            error_log('New LP - In learnpath::overview()', 0);
        }
        global $_course;
        $_SESSION['gradebook'] = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
        $return = '';

        $update_audio = isset($_GET['updateaudio']) ? $_GET['updateaudio'] : null;

        // we need to start a form when we want to update all the mp3 files
        if ($update_audio == 'true') {
            $return .= '<form action="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;updateaudio=' . Security :: remove_XSS($_GET['updateaudio']) .'&amp;action=' . Security :: remove_XSS($_GET['action']) . '&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" method="post" enctype="multipart/form-data" name="updatemp3" id="updatemp3">';
        }
        $return .= '<div id="message"></div>';

        $return_audio = '<table class="data_table">';
        $return_audio .= '<tr>';
        $return_audio .= '<th width="60%">' . get_lang('Title') . '</th>';
        $return_audio .= '<th>' . get_lang('Audio') . '</th>';
   		$return_audio .= '</tr>';

        if ($update_audio != 'true') {
        	$return .= '<div class="span12">';
            $return .= self::return_new_tree($update_audio);
        	$return .='</div>';
        	$return .= Display::div(Display::url(get_lang('Save'), '#', array('id'=>'listSubmit', 'class'=>'btn')), array('style'=>'float:left; margin-top:15px;width:100%'));
        } else {
            $return_audio .= self::return_new_tree($update_audio);
        	$return .= $return_audio.'</table>';
        }

        // We need to close the form when we are updating the mp3 files.
        if ($update_audio == 'true') {
            $return .= '<div style="margin:40px 0; float:right;"><button class="save" type="submit" name="save_audio" id="save_audio">' . get_lang('SaveAudioAndOrganization') . '</button></div>'; // TODO: What kind of language variable is this?
        }

        // We need to close the form when we are updating the mp3 files.
        if ($update_audio == 'true' && count($arrLP) != 0) {
            $return .= '</form>';
        }
        return $return;
    }

    public function return_new_tree($update_audio = 'false', $drop_element_here = false) {
        $ajax_url = api_get_path(WEB_AJAX_PATH).'lp.ajax.php';
        echo '<script>
        var newOrderData= "";
        function processChildren(parentId) {
            //Loop through the children of the UL element defined by the parentId
            var ulParentID= "UL_" + parentId;
            $("#" + ulParentID).children().each(function () {

                /*Only process elements with an id attribute (in order to skip the blank,
                    unmovable <li> elements.*/

                if ($(this).attr("id")) {
                    /*Build a string of data with the childs ID and parent ID,
                        using the "|" as a delimiter between the two IDs and the "^"
                        as a record delimiter (these delimiters were chosen in case the data
                        involved includes more common delimiters like commas within the content)
                    */
                    newOrderData= newOrderData + $(this).attr("id") + "|" + parentId + "^";

                    //Determine if this child is a containter
                    if ($(this).is(".container")) {
                        //Process the child elements of the container
                        processChildren($(this).attr("id"));
                    }
                }
            });  //end of children loop
        } //end of processChildren function

        $(function() {

            $(".item_data").live("mouseover", function(event) {
                $(".button_actions", this).show();
            });

            $(".item_data").live("mouseout", function() {
                $(".button_actions",this).hide();
            });

            $(".button_actions").hide();

            $( ".lp_resource" ).sortable({
                items: ".lp_resource_element ",
                handle: ".moved", //only the class "moved"
                cursor: "move",
                connectWith: "#lp_item_list",
                placeholder: "ui-state-highlight", //defines the yellow highlight

                start: function(event, ui) {
                    $(ui.item).css("width", "160px");
                    $(ui.item).find(".item_data").attr("style", "");

                },
                stop: function(event, ui) {
                    $(ui.item).css("width", "100%");
                },
            });

            $("#lp_item_list").sortable({
                items: "li",
                handle: ".moved", //only the class "moved"
                cursor: "move",
                placeholder: "ui-state-highlight", //defines the yellow highlight

                update: function(event, ui) {

                    //Walk through the direct descendants of the lp_item_list <ul>
                    $("#lp_item_list").children().each(function () {

                        /*Only process elements with an id attribute (in order to skip the blank,
                        unmovable <li> elements.*/

                        if ($(this).attr("id")) {
                                /*Build a string of data with the child s ID and parent ID,
                                using the "|" as a delimiter between the two IDs and the "^"
                                as a record delimiter (these delimiters were chosen in case the data
                                involved includes more common delimiters like commas within the content)
                                */
                                newOrderData= newOrderData + $(this).attr("id") + "|" + "0" + "^";

                                //Determine if this child is a containter
                                if ($(this).is(".li_container")) {
                                    //Process the child elements of the container
                                    processChildren($(this).attr("id"));
                                }
                            }
                    }); //end of lp_item_list children loop

                    var order = "new_order="+ newOrderData + "&a=update_lp_item_order";
                    $.post("'.$ajax_url.'", order, function(reponse){
                        $("#message").html(reponse);
                    });
                },
                receive: function(event, ui) {

                    var id = $(ui.item).attr("data_id");
                    var type = $(ui.item).attr("data_type");
                    var title = $(ui.item).attr("title");

                    if (ui.item.parent()[0]) {
                        var parent_id = $(ui.item.parent()[0]).attr("id");
                        var previous_id = $(ui.item.prev()).attr("id");

                        if (parent_id) {
                            parent_id = parent_id.split("_")[1];
                            var params = {
                                    "a": "add_lp_item",
                                    "id": id,
                                    "parent_id": parent_id,
                                    "previous_id": previous_id,
                                    "type": type,
                                    "title" : title
                                };
                            $.ajax({
                                type: "GET",
                                url: "'.$ajax_url.'",
                                data: params,
                                async: false,
                                success: function(data) {
                                    if (data == -1) {
                                    } else {

                                        $(".normal-message").hide();
                                        $(ui.item).attr("id", data);
                                        $(ui.item).addClass("lp_resource_element_new");
                                        $(ui.item).find(".item_data").attr("style", "");
                                        $(ui.item).addClass("record li_container");
                                        $(ui.item).removeClass("lp_resource_element");
                                        $(ui.item).removeClass("doc_resource");
                                    }
                                }
                            });
                        }
                    }//
                }//end receive
            });
        });
        </script>';

        $is_allowed_to_edit = api_is_allowed_to_edit(null,true);

        $course_id = api_get_course_int_id();
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        $sql = "SELECT * FROM $tbl_lp_item
                WHERE c_id = $course_id AND lp_id = ".$this->lp_id;

        $result = Database::query($sql);
        $arrLP = array();
        while ($row = Database :: fetch_array($result)) {
            $row['title'] = Security :: remove_XSS($row['title']);
            $row['description'] = Security :: remove_XSS($row['description']);
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite'],
                'display_order' => $row['display_order'],
                'audio' => $row['audio']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);
        $default_data = null;
        $default_content = null;

        $elements = array();
        $return_audio = null;

        for ($i = 0; $i < count($arrLP); $i++) {
            $title = $arrLP[$i]['title'];

            $title_cut = cut($arrLP[$i]['title'], 25);

            //Link for the documents
            if ($arrLP[$i]['item_type'] == 'document') {
                $url = api_get_self() . '?'.api_get_cidreq().'&amp;action=view_item&amp;mode=preview_document&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id;
                $title_cut = Display::url($title_cut, $url, array('class' => 'ajax'));
            }

            if (($i % 2) == 0) {
                $oddclass = 'row_odd';
            } else {
                $oddclass = 'row_even';
            }
            $return_audio .= '<tr id ="lp_item_'.$arrLP[$i]['id'] .'" class="' . $oddclass . '">';

            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);

            $icon = '';
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
            	$icon = '<img src="../img/lp_' . $icon_name . '.png" />';
            } else {
            	if (file_exists('../img/lp_' . $icon_name . '.gif')) {
            		$icon = '<img src="../img/lp_' . $icon_name . '.gif"  />';
            	} else {
            		$icon = '<img src="../img/folder_document.gif" />';
            	}
            }

            // The audio column.
            $return_audio  .= '<td align="center">';

            $audio = '';

            if (!$update_audio OR $update_audio <> 'true') {
                if (!empty($arrLP[$i]['audio'])) {
                    /*$audio .= '<span id="container'.$i.'"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</span>';
                    $audio .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
                    $audio .= '<script type="text/javascript">
                                    var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
                                    s1.addParam("allowscriptaccess","always");
                                    s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $arrLP[$i]['audio'] . '");
                                    s1.write("container' . $i . '");
                                </script>';*/
                } else {
                    $audio .= '';
                }
            } else {
                if ($arrLP[$i]['item_type'] != 'dokeos_chapter' && $arrLP[$i]['item_type'] != 'dokeos_module' && $arrLP[$i]['item_type'] != 'dir') {
                    $audio .= '<input type="file" name="mp3file' . $arrLP[$i]['id'] . '" id="mp3file" />';
                    if (!empty ($arrLP[$i]['audio'])) {
                        $audio .= '<br />'.Security::remove_XSS($arrLP[$i]['audio']).'<br /><input type="checkbox" name="removemp3' . $arrLP[$i]['id'] . '" id="checkbox' . $arrLP[$i]['id'] . '" />' . get_lang('RemoveAudio');
                    }
                }
            }
            $return_audio .= Display::span($icon.' '.$title).Display::tag('td', $audio, array('style'=>''));
            $return_audio .= '</td>';
			$move_icon = '';
            $move_item_icon = '';
			$edit_icon = '';
			$delete_icon = '';
            $audio_icon = '';
            $prerequisities_icon = '';

            if ($is_allowed_to_edit) {
                if (!$update_audio OR $update_audio <> 'true') {
                    $move_icon .= '<a class="moved" href="#">';
					$move_icon .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
                    $move_icon .= '</a>';
                }

                // No edit for this item types
                if (!in_array($arrLP[$i]['item_type'], array('sco', 'asset'))) {
                    if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module'))) {
                        $edit_icon .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit_item&amp;view=build&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '&amp;path_item=' . $arrLP[$i]['path'] . '">';
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
                        $edit_icon .= '</a>';
                    } else {
                        $edit_icon .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '&amp;path_item=' . $arrLP[$i]['path'] . '">';
                        $edit_icon .= Display::return_icon('edit.png', get_lang('LearnpathEditModule'), array(), ICON_SIZE_TINY);
                        $edit_icon .= '</a>';
                    }
                }

                $delete_icon .= ' <a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=delete_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '" onClick="return confirmation(\'' . addslashes($title) . '\');">';
                $delete_icon .= Display::return_icon('delete.png', get_lang('LearnpathDeleteModule'), array(), ICON_SIZE_TINY);
                $delete_icon .= '</a>';

                $url = api_get_self() . '?cidReq='.Security::remove_XSS($_GET['cidReq']).'&view=build&id='.$arrLP[$i]['id'] .'&lp_id='.$this->lp_id;

                if (!in_array($arrLP[$i]['item_type'], array('dokeos_chapter', 'dokeos_module', 'dir'))) {
                    $prerequisities_icon = Display::url(Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_TINY), $url.'&action=edit_item_prereq');
                    $move_item_icon = Display::url(Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_TINY), $url.'&action=move_item');
                    $audio_icon = Display::url(Display::return_icon('audio.png', get_lang('UplUpload'), array(), ICON_SIZE_TINY), $url.'&action=add_audio');
                }
            }
            if ($update_audio != 'true') {
            	$row = $move_icon.' '.$icon.Display::span($title_cut).Display::span($audio.$edit_icon.$prerequisities_icon.$move_item_icon.$audio_icon.$delete_icon, array('class'=>'button_actions'));
            } else {
            	$row = Display::span($title.$icon).Display::span($audio, array('class'=>'button_actions'));
            }
            $parent_id = $arrLP[$i]['parent_item_id'];

            $default_data[$arrLP[$i]['id']] = $row;
            $default_content[$arrLP[$i]['id']] = $arrLP[$i];

            if (empty($parent_id)) {
            	$elements[$arrLP[$i]['id']]['data'] = $row;
            	$elements[$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
            } else {
            	$parent_arrays = array();
            	if ($arrLP[$i]['depth'] > 1) {
            		//Getting list of parents
            		for($j = 0; $j < $arrLP[$i]['depth']; $j++) {
            			foreach($arrLP as $item) {
            				if ($item['id'] == $parent_id) {
            					if ($item['parent_item_id'] == 0) {
            						$parent_id = $item['id'];
            						break;
            					} else {
            						$parent_id = $item['parent_item_id'];
            						if (empty($parent_arrays)) {
            							$parent_arrays[] = intval($item['id']);
            						}
            						$parent_arrays[] = $parent_id;
            						break;
            					}
            				}
            			}
            		}
            	}

            	if (!empty($parent_arrays)) {
	            	$parent_arrays = array_reverse($parent_arrays);
	            	$val = '$elements';
	            	$x = 0;
	            	foreach($parent_arrays as $item) {
	            		if ($x != count($parent_arrays) -1) {
	            			$val .= '["'.$item.'"]["children"]';
	            		} else {
	            			$val .= '["'.$item.'"]["children"]';
	            		}
	            		$x++;
	            	}
	            	$val .= "";
	            	$code_str = $val."[".$arrLP[$i]['id']."][\"load_data\"] = '".$arrLP[$i]['id']."' ; ";
	            	eval($code_str);
            	} else {
	            	$elements[$parent_id]['children'][$arrLP[$i]['id']]['data'] = $row;
	            	$elements[$parent_id]['children'][$arrLP[$i]['id']]['type'] = $arrLP[$i]['item_type'];
            	}
            }
        }

        $return = '<div class="lp_tree well">';

        $return .= '<ul id="lp_item_list">';
        $return .='<h4>'.$this->name.'</h4><br>';

        $tree = self::print_recursive($elements, $default_data, $default_content);

        if (!empty($tree)) {
            $return .= $tree;
        } else {
            if ($drop_element_here) {
                $return .= Display::return_message(get_lang("DragAndDropAnElementHere"));
            }
        }
        $return .= '</ul>';
        if ($update_audio == 'true') {
            $return = $return_audio;
        } else {
            $return .= '</div>';
        }
        return $return;
    }

    function print_recursive($elements, $default_data, $default_content) {
        $return = '';
        foreach ($elements as $key => $item) {
            if (isset($item['load_data']) || empty($item['data'])) {
                $item['data'] = $default_data[$item['load_data']];
                $item['type'] = $default_content[$item['load_data']]['item_type'];
            }
            $sub_list = '';
            if (isset($item['type']) && $item['type'] == 'dokeos_chapter') {
                $sub_list = Display::tag('li', '', array('class'=>'sub_item empty')); // empty value
            }
            if (empty($item['children'])) {
                $sub_list = Display::tag('ul', $sub_list, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
                $active = null;
                if (isset($_REQUEST['id']) && $key == $_REQUEST['id']) {
                    $active = 'active';
                }
                $return  .= Display::tag('li', Display::div($item['data'], array('class'=>"item_data $active")).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
            } else {
                //sections
                if (isset($item['children'])) {
                    $data = self::print_recursive($item['children'], $default_data, $default_content);
                }
                $sub_list = Display::tag('ul', $sub_list.$data, array('id'=>'UL_'.$key, 'class'=>'record li_container'));
                $return .= Display::tag('li', Display::div($item['data'], array('class'=>'item_data')).$sub_list, array('id'=>$key, 'class'=>'record li_container'));
            }
        }
        return $return;
    }

    /**
     * This function builds the action menu
     * @return void
     */
    public function build_action_menu() {
        $is_allowed_to_edit = api_is_allowed_to_edit(null,true);

        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;
        $return = '<div class="actions">';

        //$return .= '<a href="lp_controller.php?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&action=build&lp_id=' . $this->lp_id . '">' . Display :: return_icon('home.png', get_lang('Build'),'',ICON_SIZE_MEDIUM).'</a>';

        //$return .=  '<a href="' . api_get_self().'?'.api_get_cidreq().'&amp;gradebook=' . $gradebook . '&amp;action=admin_view&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang('BasicOverview') . '">' . Display :: return_icon('move_learnpath.png', get_lang('BasicOverview'),'',ICON_SIZE_MEDIUM).'</a>';
        $return .=  '<a href="lp_controller.php?'.api_get_cidreq().'&amp;gradebook=' . $gradebook . '&action=view&lp_id=' . $_SESSION['oLP']->lp_id . '&isStudentView=true">' . Display :: return_icon('preview_view.png', get_lang('Display'),'',ICON_SIZE_MEDIUM).'</a> ';

        //$return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=step&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang('NewStep') . '">' . Display :: return_icon('new_learnigpath_object.png', get_lang('NewStep'),'',ICON_SIZE_MEDIUM).'</a>';
//		echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=chapter&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang('NewChapter') . '">' . Display :: return_icon('add_learnpath_section.png', get_lang('NewChapter'),'',ICON_SIZE_MEDIUM).'</a>';

        $return .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;action=admin_view&amp;lp_id=' . $_SESSION['oLP']->lp_id . '&amp;updateaudio=true">' . Display :: return_icon('upload_audio.png', get_lang('UpdateAllAudioFragments'),'',ICON_SIZE_MEDIUM).'</a>';

        $return .= '<a href="lp_controller.php?'.api_get_cidreq().'&amp;action=edit&amp;lp_id=' . $_SESSION['oLP']->lp_id . '">' . Display :: return_icon('settings.png', get_lang('CourseSettings'),'',ICON_SIZE_MEDIUM).'</a>';

        $buttons = array(
            array(
                'title' => get_lang('SetPrerequisiteForEachItem'),
                'href' => 'lp_controller.php?'.api_get_cidreq().'&amp;action=set_previous_step_as_prerequisite&amp;lp_id=' . $_SESSION['oLP']->lp_id,
            ),
            array(
                'title' => get_lang('ClearAllPrerequisites'),
                'href' => 'lp_controller.php?'.api_get_cidreq().'&amp;action=clear_prerequisites&amp;lp_id=' . $_SESSION['oLP']->lp_id,
            ),
        );
        $return .= Display::group_button(get_lang('PrerequisitesOptions'), $buttons);
        $return .= '</div>';
        echo $return;
    }

    /**
     * This functions builds the LP tree based on data from the database.
     * @return string
     * @deprecated use the return_new_tree() function
     * @uses dtree.js :: necessary javascript for building this tree
     */
    public function build_tree() {
        $course_id = api_get_course_int_id();

        $return = "<script type=\"text/javascript\">\n";
        $return .= "\tm = new dTree('m');\n\n";
        $return .= "\tm.config.folderLinks		= true;\n";
        $return .= "\tm.config.useCookies		= true;\n";
        $return .= "\tm.config.useIcons			= true;\n";
        $return .= "\tm.config.useLines			= true;\n";
        $return .= "\tm.config.useSelection		= true;\n";
        $return .= "\tm.config.useStatustext	= false;\n\n";


        $menu = 0;
        $parent = '';
        $return .= "\tm.add(" . $menu . ", -1, '" . addslashes(Security::remove_XSS(($this->name))) . "');\n";
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        $sql = " SELECT id, title, description, item_type, path, parent_item_id, previous_item_id, next_item_id, max_score, min_score, mastery_score, display_order
                 FROM $tbl_lp_item
                 WHERE c_id = ".$course_id." AND lp_id = " . Database::escape_string($this->lp_id);
        $result = Database::query($sql);
        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $row['title'] = Security :: remove_XSS($row['title']);
            $row['description'] = Security :: remove_XSS($row['description']);

            $arrLP[] = array (
                'id' 				=> $row['id'],
                'item_type' 		=> $row['item_type'],
                'title' 			=> $row['title'],
                'path' 				=> $row['path'],
                'description' 		=> $row['description'],
                'parent_item_id' 	=> $row['parent_item_id'],
                'previous_item_id' 	=> $row['previous_item_id'],
                'next_item_id' 		=> $row['next_item_id'],
                'max_score' 		=> $row['max_score'],
                'min_score' 		=> $row['min_score'],
                'mastery_score' 	=> $row['mastery_score'],
                'display_order' 	=> $row['display_order']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);
        $title = '';
        for ($i = 0; $i < count($arrLP); $i++) {
            $title = addslashes($arrLP[$i]['title']);
            $menu_page = api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=view_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $_SESSION['oLP']->lp_id;
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
                $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/lp_" . $icon_name . ".png', '../img/lp_" . $icon_name . ".png');\n";
            } else
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
                    $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/lp_" . $icon_name . ".gif', '../img/lp_" . $icon_name . ".gif');\n";
                } else {
                    $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/folder_document.gif', '../img/folder_document.gif');\n";
                }
            if ($menu < $arrLP[$i]['id'])
                $menu = $arrLP[$i]['id'];
        }

        $return .= "\n\tdocument.write(m);\n";
        $return .= "\t if(!m.selectedNode) m.s(1);";
        $return .= "</script>\n";

        return $return;
    }

    /**
     * Creates the default learning path folder
     */
    public static function generate_learning_path_folder($course) {
        //Creating learning_path folder
    	$dir = '/learning_path';
    	$filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document';
    	$folder = false;
    	if (!is_dir($filepath.'/'.$dir)) {
    		$folder = create_unexisting_directory($course, api_get_user_id(), api_get_session_id(), 0, 0, $filepath, $dir , get_lang('LearningPaths'), 0);
    	} else {
    		$folder = true;
    	}
        return $folder;
    }

    public function generate_lp_folder($course, $lp_name = null) {
    	$filepath = '';
    	$dir = '/learning_path/';

        if (empty($lp_name)) {
            $lp_name = $this->name;
        }

        $folder = self::generate_learning_path_folder($course);
    	//Creating LP folder
    	if ($folder) {
    		//Limits title size
    		$title = api_substr(replace_dangerous_char($lp_name), 0 , 80);
    		$dir   = $dir.$title;
    		$filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document';
    		if (!is_dir($filepath.'/'.$dir)) {
    			$folder = create_unexisting_directory($course, api_get_user_id(), 0, 0, 0, $filepath, $dir , $lp_name);
    		} else {
    			$folder = true;
    		}
    		$dir = $dir.'/';
    		if ($folder) {
    			$filepath = api_get_path(SYS_COURSE_PATH) . $course['path'] . '/document'.$dir;
    		}
    	}
    	$array =  array('dir' => $dir, 'filepath' => $filepath, 'folder' => $folder);
    	return $array;
    }

    /**
     * Create a new document //still needs some finetuning
     * @param array $_course
     * @return string
     */
    public function create_document($_course) {
        $course_id = api_get_course_int_id();
        global $charset;
        $dir = isset ($_GET['dir']) ? $_GET['dir'] : $_POST['dir']; // Please, do not modify this dirname formatting.
        if (strstr($dir, '..'))
            $dir = '/';
        if ($dir[0] == '.')
            $dir = substr($dir, 1);
        if ($dir[0] != '/')
            $dir = '/' . $dir;
        if ($dir[strlen($dir) - 1] != '/')
            $dir .= '/';

        $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $dir;

        if (empty($_POST['dir']) && empty($_GET['dir'])) {
            //Generates folder
        	$result     = $this->generate_lp_folder($_course);
        	$dir 		= $result['dir'];
        	$filepath 	= $result['filepath'];
        }

        if (!is_dir($filepath)) {
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
            $dir = '/';
        }

        // stripslashes() before calling replace_dangerous_char() because $_POST['title']
        // is already escaped twice when it gets here.
        $title = replace_dangerous_char(stripslashes($_POST['title']));
        $title = disable_dangerous_file($title);

        $filename = $title;
        $content = $_POST['content_lp'];

        $tmp_filename = $filename;

        $i = 0;
        while (file_exists($filepath . $tmp_filename . '.html'))
            $tmp_filename = $filename . '_' . ++ $i;

        $filename = $tmp_filename . '.html';
        $content = stripslashes($content);

        $content = str_replace(api_get_path(WEB_COURSE_PATH), api_get_path(REL_PATH).'courses/', $content);

        // Change the path of mp3 to absolute.

        // The first regexp deals with ../../../ urls.

        $content = preg_replace("|(flashvars=\"file=)(\.+/)+|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
        // The second regexp deals with audio/ urls.
        $content = preg_replace("|(flashvars=\"file=)([^/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);
        // For flv player: To prevent edition problem with firefox, we have to use a strange tip (don't blame me please).
        $content = str_replace('</body>', '<style type="text/css">body{}</style></body>', $content);

        if (!file_exists($filepath . $filename)) {
            if ($fp = @ fopen($filepath . $filename, 'w')) {
                fputs($fp, $content);
                fclose($fp);

                $file_size = filesize($filepath . $filename);
                $save_file_path = $dir.$filename;

                $document_id = add_document($_course, $save_file_path, 'file', $file_size, $tmp_filename);

                if ($document_id) {
                    api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', api_get_user_id(), null, null, null, null, api_get_session_id());

                    $new_comment = (isset($_POST['comment'])) ? trim($_POST['comment']) : '';
                    $new_title = (isset($_POST['title'])) ? trim($_POST['title']) : '';

                    if ($new_comment || $new_title) {
                        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
                        $ct = '';
                        if ($new_comment)
                            $ct .= ", comment='" . Database::escape_string($new_comment). "'";
                        if ($new_title)
                            $ct .= ", title='" . Database::escape_string(htmlspecialchars($new_title, ENT_QUOTES, $charset))."' ";

                        $sql_update = "UPDATE " . $tbl_doc ." SET " . substr($ct, 1)." WHERE c_id = ".$course_id." AND id = " . $document_id;
                        Database::query($sql_update);
                    }
                }
                return $document_id;
            }
        }
    }

    /**
     * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
     * @param 	array $_course array
     * @return 	void
     */
    public function edit_document($_course) {
        $course_id = api_get_course_int_id();
        global $_configuration;
        $dir = isset ($_GET['dir']) ? $_GET['dir'] : $_POST['dir']; // Please, do not modify this dirname formatting.

        if (strstr($dir, '..'))
            $dir = '/';

        if ($dir[0] == '.')
            $dir = substr($dir, 1);

        if ($dir[0] != '/')
            $dir = '/' . $dir;

        if ($dir[strlen($dir) - 1] != '/')
            $dir .= '/';

        $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $dir;

        if (!is_dir($filepath)) {
            $filepath = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document/';
            $dir = '/';
        }

        $table_doc = Database :: get_course_table(TABLE_DOCUMENT);
        if (isset($_POST['path']) && !empty($_POST['path'])) {
        	$document_id = intval($_POST['path']);
            $sql = "SELECT path FROM " . $table_doc . " WHERE c_id = $course_id AND id = " . $document_id;
            $res = Database::query($sql);
            $row = Database :: fetch_array($res);
            $content = stripslashes($_POST['content_lp']);
            $file = $filepath . $row['path'];

            if ($fp = @ fopen($file, 'w')) {
                $content = text_filter($content);
                $content = str_replace(api_get_path(WEB_COURSE_PATH), $_configuration['url_append'] . '/courses/', $content);

                // Change the path of mp3 to absolute.
                // The first regexp deals with ../../../ urls.
                $content = preg_replace("|(flashvars=\"file=)(\.+/)+|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
                // The second regexp deals with audio/ urls.
                $content = preg_replace("|(flashvars=\"file=)([^/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);

                fputs($fp, $content);
                fclose($fp);

                $sql_update = "UPDATE " . $table_doc ." SET title='".Database::escape_string($_POST['title'])."' WHERE c_id = ".$course_id." AND id = " . $document_id;
                Database::query($sql_update);
            }
        }
    }

    /**
     * Displays the selected item, with a panel for manipulating the item
     * @param int $item_id
     * @param string $msg
     * @return string
     */
    public function display_item($item_id, $msg = null, $show_actions = true) {
        $course_id = api_get_course_int_id();
        $return = '';
        if (is_numeric($item_id)) {
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
            $tbl_doc     = Database :: get_course_table(TABLE_DOCUMENT);
            $sql = "SELECT lp.* FROM " . $tbl_lp_item . " as lp
                    WHERE c_id = ".$course_id." AND lp.id = " . Database::escape_string($item_id);
            $result = Database::query($sql);
            while ($row = Database :: fetch_array($result,'ASSOC')) {
                $_SESSION['parent_item_id'] = ($row['item_type'] == 'dokeos_chapter' || $row['item_type'] == 'dokeos_module' || $row['item_type'] == 'dir') ? $item_id : 0;

                // Prevents wrong parent selection for document, see Bug#1251.
                if ($row['item_type'] != 'dokeos_chapter' || $row['item_type'] != 'dokeos_module') {
                    $_SESSION['parent_item_id'] = $row['parent_item_id'];
                }

                if ($show_actions) {
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                }
                $return .= '<div style="padding:10px;">';

                if ($msg != '')
                    $return .= $msg;

                $return .= '<h3>'.$row['title'].'</h3>';
                switch ($row['item_type']) {
                    case TOOL_QUIZ:
                        if (!empty($row['path'])) {
                            require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.class.php';
                            $exercise = new Exercise();
                            $exercise->read($row['path']);
                            $return .= $exercise->description.'<br />';
                        }
                        break;
                    case TOOL_DOCUMENT:
                        $tbl_doc      = Database :: get_course_table(TABLE_DOCUMENT);
                        $sql_doc      = "SELECT path FROM " . $tbl_doc . " WHERE c_id = ".$course_id." AND id = " . Database::escape_string($row['path']);
                        $result       = Database::query($sql_doc);
                        $path_file    = Database::result($result, 0, 0);
                        $path_parts   = pathinfo($path_file);
                        // TODO: Correct the following naive comparisons, also, htm extension is missing.
                        if (in_array($path_parts['extension'], array (
                                'html',
                                'txt',
                                'png',
                                'jpg',
                                'JPG',
                                'jpeg',
                                'JPEG',
                                'gif',
                                'swf'
                            ))) {
                            $return .= $this->display_document($row['path'], true, true);
                        }
                        break;
                }
                $return .= '</div>';
            }
        }
        return $return;
    }

    /**
     * Shows the needed forms for editing a specific item
     * @param int $item_id
     * @return string
     */
    public function display_edit_item($item_id) {
        global $_course; // It will disappear.
        $course_id = api_get_course_int_id();
        $return = '';
        if (is_numeric($item_id)) {
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
            $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND id = " . Database::escape_string($item_id);
            $res = Database::query($sql);
            $row = Database::fetch_array($res);

            switch ($row['item_type']) {
                case 'dokeos_chapter' :
                case 'dir' :
                case 'asset' :
                case 'sco' :
                    if (isset ($_GET['view']) && $_GET['view'] == 'build') {
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', 'edit', $item_id, $row);
                    } else {
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentChapter') . ' :', $row);
                    }
                    break;
                case TOOL_DOCUMENT :
                    $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
                    $sql_step = " SELECT lp.*, doc.path as dir
                                    FROM " . $tbl_lp_item . " as lp
                                    LEFT JOIN " . $tbl_doc . " as doc ON doc.id = lp.path
                                    WHERE 	lp.c_id = $course_id AND
                    						doc.c_id = $course_id AND
                    						lp.id = " . Database::escape_string($item_id);
                    $res_step = Database::query($sql_step);
                    $row_step = Database :: fetch_array($res_step);
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_document_form('edit', $item_id, $row_step);
                    break;
                case TOOL_LINK :
                    $link_id = (string) $row['path'];
                    if (ctype_digit($link_id)) {
                        $tbl_link = Database :: get_course_table(TABLE_LINK);
                        $sql_select = 'SELECT url FROM ' . $tbl_link . ' WHERE c_id = '.$course_id.' AND id = ' . Database::escape_string($link_id);
                        $res_link = Database::query($sql_select);
                        $row_link = Database :: fetch_array($res_link);
                        if (is_array($row_link)) {
                            $row['url'] = $row_link['url'];
                        }
                    }
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_link_form('edit', $item_id, $row);
                    break;
                case 'dokeos_module' :
                    if (isset ($_GET['view']) && $_GET['view'] == 'build') {
                        $return .= $this->display_manipulate($item_id, $row['item_type']);
                        $return .= $this->display_item_form($row['item_type'], get_lang('EditCurrentModule') . ' :', 'edit', $item_id, $row);
                    } else {
                        $return .= $this->display_item_small_form($row['item_type'], get_lang('EditCurrentModule') . ' :', $row);
                    }
                    break;
                case TOOL_QUIZ :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_quiz_form('edit', $item_id, $row);
                    break;
                case TOOL_HOTPOTATOES :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
                    break;
                case TOOL_STUDENTPUBLICATION :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_student_publication_form('edit', $item_id, $row);
                    break;
                case TOOL_FORUM :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_forum_form('edit', $item_id, $row);
                    break;
                case TOOL_THREAD :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_thread_form('edit', $item_id, $row);
                    break;
            }
        }

        return $return;
    }

    /**
     * Function that displays a list with al the resources that could be added to the learning path
     * @return string
     */
    public function display_resources() {
        global $_course; // TODO: Don't use globals.
        $course_code = api_get_course_id();

        //Get all the docs
        $documents = $this->get_documents();

        //Get all the exercises
        $exercises = $this->get_exercises();

        // Get all the links
        $links = $this->get_links();

        //Get al the student publications
        $works = $this->get_student_publications();

        //Get al the forums
        $forums = $this->get_forums(null, $course_code);

        $headers = array(   Display::return_icon('folder_document.png', get_lang('Documents'), array(), 64),
                            Display::return_icon('quiz.png',  get_lang('Quiz'), array(), 64),
                            Display::return_icon('links.png', get_lang('Links'), array(), 64),
                            Display::return_icon('works.png', get_lang('Works'), array(), 64),
                            Display::return_icon('forum.png', get_lang('Forums'), array(), 64),
                            Display::return_icon('add_learnpath_section.png', get_lang('NewChapter'), array(), 64)
                        );
        echo Display::display_normal_message(get_lang('ClickOnTheLearnerViewToSeeYourLearningPath'));
        $chapter = $_SESSION['oLP']->display_item_form('chapter', get_lang('EnterDataNewChapter'), 'add_item');
        echo Display::tabs($headers, array($documents, $exercises, $links, $works, $forums, $chapter), 'resource_tab');
        return true;
    }

    /**
     * Returns the extension of a document
     * @param string filename
     * @return string Extension (part after the last dot)
     */
    public function get_extension($filename) {
        $explode = explode('.', $filename);
        return $explode[count($explode) - 1];
    }

    /**
     * Displays a document by id
     *
     * @param unknown_type $id
     * @return unknown
     */
    public function display_document($id, $show_title = false, $iframe = true, $edit_link = false) {
        global $_course; // It is temporary.
        $course_id = api_get_course_int_id();
        $return = '';
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
        $sql_doc = "SELECT * FROM " . $tbl_doc . "
                    WHERE c_id = ".$course_id." AND id = " . $id;
        $res_doc = Database::query($sql_doc);
        $row_doc = Database :: fetch_array($res_doc);

        // TODO: Add a path filter.
        if ($iframe) {
            $return .= '<iframe id="learnpath_preview_frame" frameborder="0" height="400" width="100%" scrolling="auto" src="' . api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/document' . str_replace('%2F', '/', urlencode($row_doc['path'])) . '?' . api_get_cidreq() . '"></iframe>';
        } else {
            $return .= file_get_contents(api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $row_doc['path']);
        }

        return $return;
    }

    /**
     * Return HTML form to add/edit a quiz
     * @param	string	Action (add/edit)
     * @param	integer	Item ID if already exists
     * @param	mixed	Extra information (quiz ID if integer)
     * @return	string	HTML form
     */
    public function display_quiz_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = $extra_info['title'];
            $item_description = $extra_info['description'];
        } elseif (is_numeric($extra_info)) {
            $sql_quiz = "SELECT title, description FROM " . $tbl_quiz . " WHERE c_id = ".$course_id." AND id = " . $extra_info;

            $result = Database::query($sql_quiz);
            $row = Database :: fetch_array($result);
            $item_title = $row['title'];
            $item_description = $row['description'];
        } else {
            $item_title = '';
            $item_description = '';
        }
        $item_title			= Security::remove_XSS($item_title);
        $item_description 	= Security::remove_XSS($item_description);

        $legend = '<legend>';
        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;

        $result = Database::query($sql);
        $arrLP = array ();
        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite'],
                'max_time_allowed' => $row['max_time_allowed']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        if ($action == 'add')
            $legend .= get_lang('CreateTheExercise') . '&nbsp;:';
        elseif ($action == 'move') $legend .= get_lang('MoveTheCurrentExercise') . '&nbsp;:';
        else
            $legend .= get_lang('EditCurrentExecice') . '&nbsp;:';
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
        }
        $legend .= '</legend>';
        $return .= '<div class="sectioncomment">';

        $return .= '<form method="POST">';
        $return .= $legend;
        $return .= '<table class="lp_form">';

        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>';
            $return .= '<td class="input"><input id="idTitle" name="title" size="44" type="text" value="' . $item_title . '" /></td>';
            $return .= '</tr>';
        }

        $return .= '<tr>';

        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>';
        $return .= '<td class="input">';

        $return .= '<select id="idParent" style="width:100%;" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';

        $return .= '<option class="top" value="0">' . $this->name . '</option>';

        $arrHide = array (
            $id
        );
        //$parent_item_id = $_SESSION['parent_item_id'];
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                } else {
                    $arrHide[] = $arrLP[$i]['id'];
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
            }
        }
        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';

        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . '</label></td>';
        $return .= '<td class="input">';

        $return .= '<select class="learnpath_item_form" style="width:100%;" id="previous" name="previous" size="1">';

        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add') $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        if ($action != 'move') {
            $id_prerequisite = 0;
            if (is_array($arrLP)) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }
            $arrHide = array ();
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = 0;
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                }
            }
            /*// Commented the prerequisites, only visible in edit (exercise).
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idPrerequisites">'.get_lang('LearnpathPrerequisites').'</label></td>';
            $return .= '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang('NoPrerequisites').'</option>';

                foreach($arrHide as $key => $value){
                    if($key==$s_selected_position && $action == 'add'){
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
                    }
                    elseif($key==$id_prerequisite && $action == 'edit'){
                        $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
                    }
                    else{
                        $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
                    }
                }

            $return .= "</select></td>";
            */
            $return .= '</tr>';
            /*$return .= '<tr>';
            $return .= '<td class="label"><label for="maxTimeAllowed">' . get_lang('MaxTimeAllowed') . '</label></td>';
            $return .= '<td class="input"><input name="maxTimeAllowed" style="width:98%;" id="maxTimeAllowed" value="' . $extra_info['max_time_allowed'] . '" /></td>';

            // Remove temporarily the test description.
            //$return .= '<td class="label"><label for="idDescription">'.get_lang('Description').' :</label></td>';
            //$return .= '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>';

            $return .= '</tr>'; */
        }

        $return .= '<tr>';
        if ($action == 'add') {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('AddExercise') . '</button></td>';
        } else {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('EditCurrentExecice') . '</button></td>';
        }

        $return .= '</tr>';
        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        }
        elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }

        $return .= '<input name="type" type="hidden" value="' . TOOL_QUIZ . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';

        $return .= '</form>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Addition of Hotpotatoes tests
     * @param	string	Action
     * @param	integer	Internal ID of the item
     * @param	mixed	Extra information - can be an array with title and description indexes
     * @return  string	HTML structure to display the hotpotatoes addition formular
     */
    public function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        global $charset;
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
            $item_description = stripslashes($extra_info['description']);
        } elseif (is_numeric($extra_info)) {
            $TBL_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);

            $sql_hot = "SELECT * FROM " . $TBL_DOCUMENT . "
                        WHERE   c_id = ".$course_id." AND
                                path LIKE '" . $uploadPath . "/%/%htm%' AND
                                id = " . (int) $extra_info . "
                        ORDER BY id ASC";

            $res_hot = Database::query($sql_hot);

            $row = Database::fetch_array($res_hot);

            $item_title = $row['title'];
            $item_description = $row['description'];

            if (!empty ($row['comment'])) {
                $item_title = $row['comment'];
            }
        } else {
            $item_title = '';
            $item_description = '';
        }

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        $result = Database::query($sql);
        $arrLP = array ();
        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite'],
                'max_time_allowed' => $row['max_time_allowed']
            );
        }

        $legend = '<legend>';
        if ($action == 'add')
            $legend .= get_lang('CreateTheExercise');
        elseif ($action == 'move') $legend .= get_lang('MoveTheCurrentExercise');
        else
            $legend .= get_lang('EditCurrentExecice');
        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
            $legend .= Display :: return_warning_message(get_lang('Warning') . ' ! ' . get_lang('WarningEditingDocument'));
        }
        $legend .= '</legend>';

        $return .= '<form method="POST">';
        $return .= $legend;
        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . ' :</label></td>';
        $return .= '<td class="input">';
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
        $arrHide = array (
            $id
        );

        if (count($arrLP) > 0) {
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($action != 'add') {
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                    } else {
                        $arrHide[] = $arrLP[$i]['id'];
                    }
                } else {
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                        $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                }
            }

            reset($arrLP);
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . ' :</label></td>';
        $return .= '<td class="input">';
        $return .= '<select id="previous" name="previous" size="1">';
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add') $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';

        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . ' :</label></td>';
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>';
            $return .= '</tr>';
            $id_prerequisite = 0;
            if (is_array($arrLP) && count($arrLP) > 0) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }

                $arrHide = array ();
                for ($i = 0; $i < count($arrLP); $i++) {
                    if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                        if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                            $s_selected_position = $arrLP[$i]['id'];
                        elseif ($action == 'add') $s_selected_position = 0;
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];

                    }
                }
            }
        }

        $return .= '<tr>';
        $return .= '<td>&nbsp; </td><td><button class="save" name="submit_button" action="edit" type="submit">' . get_lang('SaveHotpotatoes') . '</button></td>';
        $return .= '</tr>';
        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        } elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }
        $return .= '<input name="type" type="hidden" value="' . TOOL_HOTPOTATOES . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
        $return .= '</form>';
        return $return;
    }

    /**
     * Return the form to display the forum edit/add option
     * @param	string	Action (add/edit)
     * @param	integer	ID of the lp_item if already exists
     * @param	mixed	Forum ID or title
     * @return	string	HTML form
     */
    public function display_forum_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        global $charset;
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_forum = Database :: get_course_table(TABLE_FORUM);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
        }
        elseif (is_numeric($extra_info)) {
            $sql_forum = "SELECT forum_title as title, forum_comment as comment
                            FROM " . $tbl_forum . "
                            WHERE c_id = ".$course_id." AND forum_id = " . $extra_info;

            $result = Database::query($sql_forum);
            $row = Database :: fetch_array($result);

            $item_title = $row['title'];
            $item_description = $row['comment'];
        } else {
            $item_title = '';
            $item_description = '';
        }

        $legend = '<legend>';

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . "
                WHERE   c_id = ".$course_id." AND
                        lp_id = " . $this->lp_id;

        $result = Database::query($sql);

        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        if ($action == 'add')
            $legend .= get_lang('CreateTheForum') . '&nbsp;:';
        elseif ($action == 'move') $legend .= get_lang('MoveTheCurrentForum') . '&nbsp;:';
        else
            $legend .= get_lang('EditCurrentForum') . '&nbsp;:';

        $legend .= '</legend>';

        $return .= '<div class="sectioncomment">';
        $return .= '<form method="POST">';
        $return .= $legend;
        $return .= '<table class="lp_form">';

        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>';
            $return .= '<td class="input"><input id="idTitle" size="44" name="title" type="text" value="' . $item_title . '" class="learnpath_item_form" /></td>';
            $return .= '</tr>';
        }

        $return .= '<tr>';
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>';
        $return .= '<td class="input">';
        $return .= '<select id="idParent" style="width:100%;" name="parent" onChange="javascript: load_cbo(this.value);" class="learnpath_item_form" size="1">';
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
        $arrHide = array (
            $id
        );

        //$parent_item_id = $_SESSION['parent_item_id'];
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                } else {
                    $arrHide[] = $arrLP[$i]['id'];
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
            }
        }
        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $return .= "\t\t\t\t" . '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . '</label></td>';
        $return .= '<td class="input">';
        $return .= "\t\t\t\t" . '<select id="previous" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add') $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }

        $return .= "\t\t\t\t" . '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '</tr>';
            $id_prerequisite = 0;
            if (is_array($arrLP)) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }

            $arrHide = array ();
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = 0;
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                }
            }
            $return .= '</tr>';
        }
        $return .= '<tr>';

        if ($action == 'add') {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit"> ' . get_lang('AddForumToCourse') . ' </button></td>';
        } else {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit"> ' . get_lang('EditCurrentForum') . ' </button></td>';
        }
        $return .= '</tr>';
        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        }
        elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }
        $return .= '<input name="type" type="hidden" value="' . TOOL_FORUM . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
        $return .= '</form>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Return HTML form to add/edit forum threads
     * @param	string	Action (add/edit)
     * @param	integer	Item ID if already exists in learning path
     * @param	mixed	Extra information (thread ID if integer)
     * @return 	string	HTML form
     */
    public function display_thread_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        if (empty($course_id)) {
            return null;
        }
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_forum = Database :: get_course_table(TABLE_FORUM_THREAD);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
        } elseif (is_numeric($extra_info)) {
            $sql_forum = "SELECT thread_title as title FROM $tbl_forum
                            WHERE c_id = $course_id AND thread_id = " . $extra_info;

            $result = Database::query($sql_forum);
            $row = Database :: fetch_array($result);

            $item_title = $row['title'];
            $item_description = '';
        } else {
            $item_title = '';
            $item_description = '';
        }

        $return = null;

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . "
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;

        $result = Database::query($sql);

        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite']
            );
        }

        $this->tree_array($arrLP);

        $arrLP = $this->arrMenu;

        unset ($this->arrMenu);

        $return .= '<form method="POST">';
        if ($action == 'add')
            $return .= '<legend>' . get_lang('CreateTheForum') . '</legend>';
        elseif ($action == 'move') $return .= '<p class="lp_title">' . get_lang('MoveTheCurrentForum') . '&nbsp;:</p>';
        else
            $return .= '<legend>' . get_lang('EditCurrentForum') . '</legend>';


        $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>';
        $return .= '<td class="input">';
        $return .= '<select id="idParent" name="parent" onChange="javascript: load_cbo(this.value);" size="1">';
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
        $arrHide = array (
            $id
        );

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                } else {
                    $arrHide[] = $arrLP[$i]['id'];
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
            }
        }
        if ($arrLP != null) {
            reset($arrLP);
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . '</label></td>';
        $return .= '<td class="input">';
        $return .= "\t\t\t\t" . '<select id="previous" name="previous" size="1">';
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add') $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }
        $return .= "\t\t\t\t" . '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>';
            $return .= '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>';
            $return .= '</tr>';
            $return .= '<tr>';
            $return .= '</tr>';

            $id_prerequisite = 0;
            if ($arrLP != null) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }

            $arrHide = array();
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = 0;
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];

                }
            }

            $return .= '<tr>';
            $return .= '<td class="label"><label for="idPrerequisites">' . get_lang('LearnpathPrerequisites') . '</label></td>';
            $return .= '<td class="input"><select name="prerequisites" id="prerequisites"><option value="0">' . get_lang('NoPrerequisites') . '</option>';

            foreach ($arrHide as $key => $value) {
                if ($key == $s_selected_position && $action == 'add') {
                    $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
                }
                elseif ($key == $id_prerequisite && $action == 'edit') {
                    $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
                } else {
                    $return .= '<option value="' . $key . '">' . $value['value'] . '</option>';
                }
            }
            $return .= "</select></td>";
            $return .= '</tr>';

        }
        $return .= '<tr>';
        $return .= '<td></td><td>
                    <button class="save" name="submit_button" type="submit" value="'.get_lang('Ok').'" />'.get_lang('Ok').'</button></td>';
        $return .= '</tr>';
        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        }
        elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }

        $return .= '<input name="type" type="hidden" value="' . TOOL_THREAD . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
        $return .= '</form>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Return the HTML form to display an item (generally a section/module item)
     * @param	string	Item type (module/dokeos_module)
     * @param	string	Title (optional, only when creating)
     * @param	string	Action ('add'/'edit')
     * @param	integer	lp_item ID
     * @param	mixed	Extra info
     * @return	string 	HTML form
     */
    public function display_item_form($item_type, $title = '', $action = 'add_item', $id = 0, $extra_info = 'new') {
        $course_id = api_get_course_int_id();
        global $_course;
        global $charset;

        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

        if ($id != 0 && is_array($extra_info)) {
            $item_title 		= $extra_info['title'];
            $item_description 	= $extra_info['description'];
            $item_path = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
            $item_path_fck = '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
        } else {
            $item_title = '';
            $item_description = '';
            $item_path_fck = '';
        }



        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $id  = intval($id);
        $sql = "SELECT * FROM " . $tbl_lp_item . "
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id . " AND id != $id";

        if ($item_type == 'module')
            $sql .= " AND parent_item_id = 0";

        $result = Database::query($sql);
        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id'                => $row['id'],
                'item_type'         => $row['item_type'],
                'title'             => $row['title'],
                'path'              => $row['path'],
                'description'       => $row['description'],
                'parent_item_id'    => $row['parent_item_id'],
                'previous_item_id'  => $row['previous_item_id'],
                'next_item_id'      => $row['next_item_id'],
                'max_score'         => $row['max_score'],
                'min_score'         => $row['min_score'],
                'mastery_score'     => $row['mastery_score'],
                'prerequisite'      => $row['prerequisite'],
                'display_order'     => $row['display_order']
            );
        }

        $this->tree_array($arrLP);

        $arrLP = isset($this->arrMenu) ? $this->arrMenu : null;

        unset ($this->arrMenu);

        $gradebook = isset($_GET['gradebook']) ? Security :: remove_XSS($_GET['gradebook']) : null;

        $url = api_get_self() . '?' .api_get_cidreq().'&gradeboook='.$gradebook.'&action='.$action.'&type='.$item_type.'&lp_id='.$this->lp_id;

        $form = new FormValidator('form', 'POST',  $url);

        $defaults['title'] = api_html_entity_decode($item_title, ENT_QUOTES, $charset);
        $defaults['description'] = $item_description;

        $form->addElement('header', $title);

        //$arrHide = array($id);
        $arrHide[0]['value'] = Security :: remove_XSS($this->name);
        $arrHide[0]['padding'] = 3;
        $charset = api_get_system_encoding();

        if ($item_type != 'module' && $item_type != 'dokeos_module') {
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($action != 'add') {
                    if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                        $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
                        if ($parent == $arrLP[$i]['id']) {
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
                        }
                    }
                } else {
                    if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
                        $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                        $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
                        if ($parent == $arrLP[$i]['id']) {
                            $s_selected_parent = $arrHide[$arrLP[$i]['id']];
                        }
                    }
                }
            }

            if ($action != 'move') {
                $form->addElement('text', 'title', get_lang('Title'), 'id="idTitle" class="learnpath_chapter_form" size="40%"');
                $form->applyFilter('title', 'html_filter');
                $form->addRule('title', get_lang('ThisFieldIsRequired'), 'required');
                //$form->addElement('textarea', 'description', get_lang('Description').' :', 'id="idDescription"');
            } else {
                $form->addElement('hidden', 'title');
            }

            $parent_select = $form->addElement('select', 'parent', get_lang('Parent'), '', 'class="learnpath_chapter_form" style="width:37%;" id="idParent" onchange="javascript: load_cbo(this.value);"');

            foreach ($arrHide as $key => $value) {
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
            }
            if (!empty($s_selected_parent)) {
            	$parent_select->setSelected($s_selected_parent);
            }
        }
        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $arrHide = array();

        // POSITION
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                //this is the same!
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id']) {
                    $s_selected_position = $arrLP[$i]['id'];
                } elseif ($action == 'add') {
                    $s_selected_position = $arrLP[$i]['id'];
                }

                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
            }
        }

        $position = $form->addElement('select', 'previous', get_lang('Position'), '', 'id="previous" class="learnpath_chapter_form" style="width:37%;"');

        $padding = isset($value['padding']) ? $value['padding'] : 0;

        $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:' . $padding . 'px;"');

        foreach ($arrHide as $key => $value) {
            $position->addOption($value['value'] . '"', $key, 'style="padding-left:' . $padding . 'px;"');
        }

        if (!empty ($s_selected_position)) {
            $position->setSelected($s_selected_position);
        }

        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $form->addElement('style_submit_button', 'submit_button', get_lang('SaveSection'), 'class="save"');

        if ($item_type == 'module' || $item_type == 'dokeos_module') {
            $form->addElement('hidden', 'parent', '0');
        }
        //fix in order to use the tab
        if ($item_type == 'chapter') {
            $form->addElement('hidden', 'type', 'chapter');
        }

        $extension = null;
        if (!empty($item_path)) {
        	$extension = pathinfo($item_path, PATHINFO_EXTENSION);
        }

        //assets can't be modified

        //$item_type == 'asset' ||
        if (( $item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {

            if ($item_type == 'sco') {
                $form->addElement('html', '<script type="text/javascript">alert("' . get_lang('WarningWhenEditingScorm') . '")</script>');
            }
            $renderer = $form->defaultRenderer();
            $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');

            $relative_prefix = '';
            $editor_config = array( 'ToolbarSet' 			=> 'LearningPathDocuments',
                                    'Width' 				=> '100%',
                                    'Height' 				=> '500',
                                    'FullPage' 				=> true,
                                    'CreateDocumentDir' 	=> $relative_prefix,
           							'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/scorm/',
            						'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().$item_path_fck
                                    );
            $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
            $content_path = (api_get_path(SYS_COURSE_PATH).api_get_course_path().$item_path_fck);
            //$defaults['content_lp'] = file_get_contents($item_path);
            $defaults['content_lp'] = file_get_contents($content_path);
        }

        $form->addElement('hidden', 'type', 'dokeos_' . $item_type);
        $form->addElement('hidden', 'post_time', time());
        $form->setDefaults($defaults);
        return $form->return_form();
    }

    /**
     * Returns the form to update or create a document
     * @param	string	Action (add/edit)
     * @param	integer	ID of the lp_item (if already exists)
     * @param	mixed	Integer if document ID, string if info ('new')
     * @return	string	HTML form
     */
    public function display_document_form($action = 'add', $id = 0, $extra_info = 'new') {
        $course_id = api_get_course_int_id();
        global $charset;
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_doc 	 = Database :: get_course_table(TABLE_DOCUMENT);


        $no_display_edit_textarea = false;

        //If action==edit document
        //We don't display the document form if it's not an editable document (html or txt file)
        if ($action == "edit") {
            if (is_array($extra_info)) {
                $path_parts = pathinfo($extra_info['dir']);
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
                    $no_display_edit_textarea = true;
                }
            }
        }
        $no_display_add = false;

        // If action==add an existing document
        // We don't display the document form if it's not an editable document (html or txt file).
        if ($action == "add") {
            if (is_numeric($extra_info)) {
                $sql_doc = "SELECT path FROM " . $tbl_doc . " WHERE c_id = ".$course_id." AND id = " . Database::escape_string($extra_info);
                $result = Database::query($sql_doc);
                $path_file = Database :: result($result, 0, 0);
                $path_parts = pathinfo($path_file);
                if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
                    $no_display_add = true;
                }
            }
        }
        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
            $item_description = stripslashes($extra_info['description']);
            $item_terms = stripslashes($extra_info['terms']);
            if (empty ($item_title)) {
                $path_parts = pathinfo($extra_info['path']);
                $item_title = stripslashes($path_parts['filename']);
            }
        } elseif (is_numeric($extra_info)) {
            $sql_doc = "SELECT path, title FROM " . $tbl_doc . "
                        WHERE c_id = ".$course_id." AND id = " . Database::escape_string($extra_info);

            $result = Database::query($sql_doc);
            $row 	= Database::fetch_array($result);

            $explode = explode('.', $row['title']);

            if (count($explode) > 1) {
                for ($i = 0; $i < count($explode) - 1; $i++)
                    $item_title .= $explode[$i];
            } else {
                $item_title = $row['title'];
            }

            $item_title = str_replace('_', ' ', $item_title);
            if (empty ($item_title)) {
                $path_parts = pathinfo($row['path']);
                $item_title = stripslashes($path_parts['filename']);
            }
        } else {
            $item_title = '';
            $item_description = '';
        }
        $return = '<legend>';

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . "
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        $result = Database::query($sql);
        $arrLP = array ();
        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' 				=> $row['id'],
                'item_type' 		=> $row['item_type'],
                'title' 			=> $row['title'],
                'path' 				=> $row['path'],
                'description' 		=> $row['description'],
                'parent_item_id' 	=> $row['parent_item_id'],
                'previous_item_id'	=> $row['previous_item_id'],
                'next_item_id' 		=> $row['next_item_id'],
                'display_order' 	=> $row['display_order'],
                'max_score' 		=> $row['max_score'],
                'min_score' 		=> $row['min_score'],
                'mastery_score' 	=> $row['mastery_score'],
                'prerequisite' 		=> $row['prerequisite']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        if ($action == 'add') {
            $return .= get_lang('CreateTheDocument');
        } elseif ($action == 'move') {
            $return .= get_lang('MoveTheCurrentDocument');
        } else {
            $return .= get_lang('EditTheCurrentDocument');
        }

        $return .= '</legend>';

        if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
            $return .= Display :: return_warning_message('<strong>' . get_lang('Warning') . ' !</strong><br />' . get_lang('WarningEditingDocument'), false);
        }
        $form = new FormValidator('form', 'POST', api_get_self() . '?' .$_SERVER['QUERY_STRING'], '', array('enctype'=> "multipart/form-data"));
        $defaults['title'] = Security :: remove_XSS($item_title);
        if (empty($item_title)) {
            $defaults['title'] = Security::remove_XSS($item_title);
        }
        $defaults['description'] = $item_description;
        $form->addElement('html', $return);
        if ($action != 'move') {
            $form->addElement('text', 'title', get_lang('Title'), array('id' => 'idTitle', 'class' => 'span4'));
            $form->applyFilter('title', 'html_filter');
        }

        //$arrHide = array($id);

        $arrHide[0]['value'] = $this->name;
        $arrHide[0]['padding'] = 3;

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                    $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
                    if ($parent == $arrLP[$i]['id']) {
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
                    }
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
                    $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
                    if ($parent == $arrLP[$i]['id']) {
                        $s_selected_parent = $arrHide[$arrLP[$i]['id']];
                    }
                }
            }
        }

        $parent_select = $form->addElement('select', 'parent', get_lang('Parent'), '', 'class="learnpath_item_form" id="idParent" style="width:40%;" onchange="javascript: load_cbo(this.value);"');
        $my_count=0;
        foreach ($arrHide as $key => $value) {
            if ($my_count!=0) {
                // The LP name is also the first section and is not in the same charset like the other sections.
                $value['value'] = Security :: remove_XSS($value['value']);
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
            } else {
                $value['value'] = Security :: remove_XSS($value['value']);
                $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
            }
            $my_count++;
        }

        if (!empty($id)) {
            $parent_select->setSelected($parent);
        } else {
            $parent_item_id = $_SESSION['parent_item_id'];
            $parent_select->setSelected($parent_item_id);
        }

        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $arrHide = array ();

        //POSITION
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $s_selected_position = $arrLP[$i]['id'];
                elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
                $arrHide[$arrLP[$i]['id']]['value'] = get_lang('After') . ' "' . $arrLP[$i]['title'] . '"';
            }
        }

        $position = $form->addElement('select', 'previous', get_lang('Position'), '', 'id="previous" class="learnpath_item_form" style="width:40%;"');
        $position->addOption(get_lang('FirstPosition'), 0);

        foreach ($arrHide as $key => $value) {
        	$padding = isset($value['padding']) ? $value['padding']: 0;
            $position->addOption($value['value'], $key, 'style="padding-left:' . $padding . 'px;"');
        }
        $position->setSelected($s_selected_position);

        if (is_array($arrLP)) {
            reset($arrLP);
        }

        if ($action != 'move') {
            $id_prerequisite = 0;
            if (is_array($arrLP)) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }

            $arrHide = array ();

            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if (isset($extra_info['previous_item_id']) && $extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];

                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];

                }
            }

            if (!$no_display_add) {
                if (($extra_info == 'new' || $extra_info['item_type'] == TOOL_DOCUMENT || $_GET['edit'] == 'true')) {
                    if (isset ($_POST['content']))
                        $content = stripslashes($_POST['content']);
                    elseif (is_array($extra_info)) {
                        //If it's an html document or a text file
                        if (!$no_display_edit_textarea) {
                            $content = $this->display_document($extra_info['path'], false, false);
                        }
                    } elseif (is_numeric($extra_info))
                    	$content = $this->display_document($extra_info, false, false);
                    else
                        $content = '';

                    if (!$no_display_edit_textarea) {
                        // We need to calculate here some specific settings for the online editor.
                        // The calculated settings work for documents in the Documents tool
                        // (on the root or in subfolders).
                        // For documents in native scorm packages it is unclear whether the
                        // online editor should be activated or not.

                    	// A new document, it is in the root of the repository.
                    	$relative_path 	 = '';
                    	$relative_prefix = '';

                    	if (is_array($extra_info) && $extra_info != 'new') {
                            // The document already exists. Whe have to determine its relative path towards the repository root.
                            $relative_path = explode('/', $extra_info['dir']);
                            $cnt = count($relative_path) - 2;
                            if ($cnt < 0) {
                                $cnt = 0;
                            }
                            $relative_prefix = str_repeat('../', $cnt);
                            $relative_path 	 = array_slice($relative_path, 1, $cnt);
                            $relative_path 	 = implode('/', $relative_path);
                            if (strlen($relative_path) > 0) {
                                $relative_path = $relative_path . '/';
                            }
                        } else {
                        	global $_course;
							$result = $this->generate_lp_folder($_course);
							$relative_path = api_substr($result['dir'], 1, strlen($result['dir']));
							$relative_prefix = '../../';
                        }

                        $editor_config = array( 'ToolbarSet' 			=> 'LearningPathDocuments',
                        						'Width' 				=> '100%',
                        						'Height' 				=> '500',
                        						'FullPage' 				=> true,
                            					'CreateDocumentDir' 	=> $relative_prefix,
                            					'CreateDocumentWebDir' 	=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/',
                            					'BaseHref' 				=> api_get_path(WEB_COURSE_PATH) . api_get_course_path().'/document/'.$relative_path
                        );

                        if ($_GET['action'] == 'add_item') {
                            $class = 'add';
                            $text = get_lang('LPCreateDocument');
                        } else
                            if ($_GET['action'] == 'edit_item') {
                                $class = 'save';
                                $text = get_lang('SaveDocument');
                            }

                        $form->addElement('style_submit_button', 'submit_button', $text, 'class="' . $class . '"');
                        $renderer = $form->defaultRenderer();
                        $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');

                        $form->addElement('html', '<div>');
                        $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
                        $form->addElement('html', '</div>');
                        $defaults['content_lp'] = $content;
                    }
                } elseif (is_numeric($extra_info)) {
                    $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'class="save"');
                    $return = $this->display_document($extra_info, true, true, true);
                    $form->addElement('html', $return);
                }
            }
        }

        if ($action == 'move') {
            $form->addElement('hidden', 'title', $item_title);
            $form->addElement('hidden', 'description', $item_description);
        }
        if (is_numeric($extra_info)) {
            $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'value="submit_button", class="save"');
            $form->addElement('hidden', 'path', $extra_info);
        } elseif (is_array($extra_info)) {
            $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'class="save"');
            $form->addElement('hidden', 'path', $extra_info['path']);
        }

        $form->addElement('hidden', 'type', TOOL_DOCUMENT);
        $form->addElement('hidden', 'post_time', time());

        $form->setDefaults($defaults);

        return $form->return_form();
    }

    /**
     * Return HTML form to add/edit a link item
     * @param string	Action (add/edit)
     * @param integer	Item ID if exists
     * @param mixed		Extra info
     * @return	string	HTML form
     */
    public function display_link_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        global $charset;
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_link = Database :: get_course_table(TABLE_LINK);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
            $item_description = stripslashes($extra_info['description']);
            $item_url = stripslashes($extra_info['url']);
        } elseif (is_numeric($extra_info)) {
            $sql_link = "SELECT title, description, url FROM " . $tbl_link . " WHERE c_id = ".$course_id." AND id = " . $extra_info;
            $result = Database::query($sql_link);
            $row = Database :: fetch_array($result);
            $item_title       = $row['title'];
            $item_description = $row['description'];
            $item_url = $row['url'];
        } else {
            $item_title = '';
            $item_description = '';
            $item_url = '';
        }

        $legend = '<legend>';

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        $result = Database::query($sql);
        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        if ($action == 'add')
            $legend .= get_lang('CreateTheLink') . '&nbsp;:';
        elseif ($action == 'move') $legend .= get_lang('MoveCurrentLink') . '&nbsp;:';
        else
            $legend .= get_lang('EditCurrentLink') . '&nbsp;:';

        $legend .= '</legend>';

        $return .= '<div class="sectioncomment">';
        $return .= '<form method="POST">';
        $return .= $legend;
        $return .= '<table>';

        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>';
            $return .= '<td class="input"><input id="idTitle" name="title" size="44" type="text" value="' . $item_title . '" class="learnpath_item_form"/></td>';
            $return .= '</tr>';
        }

        $return .= '<tr>';
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>';
        $return .= '<td class="input">';
        $return .= '<select id="idParent" style="width:100%;" name="parent" onChange="javascript: load_cbo(this.value);" class="learnpath_item_form" size="1">';
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
        $arrHide = array (
            $id
        );

        $parent_item_id = $_SESSION['parent_item_id'];

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                } else {
                    $arrHide[] = $arrLP[$i]['id'];
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                    $return .= '<option ' . (($parent_item_id == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
            }
        }

        if (is_array($arrLP)) {
            reset($arrLP);
        }

        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . '</label></td>';
        $return .= '<td class="input">';

        $return .= '<select id="previous" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent_item_id && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add')
                    $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }
        $return .= '</select>';
        $return .= '</td>';
        $return .= '</tr>';

        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idURL">' . get_lang('Url') . '</label></td>';
            $return .= '<td class="input"><input' . (is_numeric($extra_info) ? ' disabled="disabled"' : '') . ' id="idURL" name="url" style="width:99%;" type="text" value="' . $item_url . '" class="learnpath_item_form" /></td>';
            $return .= '</tr>';
            $id_prerequisite = 0;
            if (is_array($arrLP)) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }

            $arrHide = array();
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = 0;
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];

                }
            }
            $return .= '</tr>';
        }

        $return .= '<tr>';
        if ($action == 'add') {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('AddLinkToCourse') . '</button></td>';
        } else {
            $return .= '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('EditCurrentLink') . '</button></td>';
        }
        $return .= '</tr>';
        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        } elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }
        $return .= '<input name="type" type="hidden" value="' . TOOL_LINK . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
        $return .= '</form>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Return HTML form to add/edit a student publication (work)
     * @param	string	Action (add/edit)
     * @param	integer	Item ID if already exists
     * @param	mixed	Extra info (work ID if integer)
     * @return	string	HTML form
     */
    public function display_student_publication_form($action = 'add', $id = 0, $extra_info = '') {
        $course_id = api_get_course_int_id();
        global $charset;

        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $tbl_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);

        if ($id != 0 && is_array($extra_info)) {
            $item_title = stripslashes($extra_info['title']);
            $item_description = stripslashes($extra_info['description']);
        }
        elseif (is_numeric($extra_info)) {
            $sql_publication = "SELECT title, description FROM " . $tbl_publication . "
                            WHERE c_id = ".$course_id." AND id = " . $extra_info;

            $result = Database::query($sql_publication);
            $row = Database :: fetch_array($result);

            $item_title = $row['title'];
        } else {
            $item_title = get_lang('Student_publication');
        }

        $legend = '<legend>';

        if ($id != 0 && is_array($extra_info))
            $parent = $extra_info['parent_item_id'];
        else
            $parent = 0;

        $sql = "SELECT * FROM " . $tbl_lp_item . "
                WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;

        $result = Database::query($sql);

        $arrLP = array ();

        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' => $row['id'],
                'item_type' => $row['item_type'],
                'title' => $row['title'],
                'path' => $row['path'],
                'description' => $row['description'],
                'parent_item_id' => $row['parent_item_id'],
                'previous_item_id' => $row['previous_item_id'],
                'next_item_id' => $row['next_item_id'],
                'display_order' => $row['display_order'],
                'max_score' => $row['max_score'],
                'min_score' => $row['min_score'],
                'mastery_score' => $row['mastery_score'],
                'prerequisite' => $row['prerequisite']
            );
        }

        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        if ($action == 'add')
            $legend .= get_lang('Student_publication') . '&nbsp;:' . "\n";
        elseif ($action == 'move') $legend .= get_lang('MoveCurrentStudentPublication') . '&nbsp;:' . "\n";
        else
            $legend .= get_lang('EditCurrentStudentPublication') . '&nbsp;:' . "\n";
        $legend .= '</legend>';

        $return .= '<div class="sectioncomment">';
        $return .= '<form method="POST">';
        $return .= $legend;
        $return .= '<table class="lp_form">';
        if ($action != 'move') {
            $return .= '<tr>';
            $return .= '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>';
            $return .= '<td class="input"><input id="idTitle" name="title" size="44" type="text" value="' . $item_title . '" class="learnpath_item_form" /></td>';
            $return .= '</tr>';
        }
        $return .= '<tr>';
        $return .= '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>';
        $return .= '<td class="input">';
        $return .= "\t\t\t\t" . '<select id="idParent" name="parent" style="width:100%;" onChange="javascript: load_cbo(this.value);" class="learnpath_item_form" size="1">';
        //$parent_item_id = $_SESSION['parent_item_id'];
        $return .= '<option class="top" value="0">' . $this->name . '</option>';
        $arrHide = array (
            $id
        );

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($action != 'add') {
                if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
                } else {
                    $arrHide[] = $arrLP[$i]['id'];
                }
            } else {
                if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
                    $return .= '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</option>';
            }
        }

        if (is_array($arrLP)) {
            reset($arrLP);
        }
        $return .= "\t\t\t\t" . '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        $return .= '<tr>';
        $return .= '<td class="label"><label for="previous">' . get_lang('Position') . '</label></td>';
        $return .= '<td class="input">';
        $return .= "\t\t\t\t" . '<select id="previous" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
        $return .= '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
                if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                    $selected = 'selected="selected" ';
                elseif ($action == 'add') $selected = 'selected="selected" ';
                else
                    $selected = '';

                $return .= '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang('After') . ' "' . $arrLP[$i]['title'] . '"</option>';
            }
        }
        $return .= "\t\t\t\t" . '</select>';
        $return .= '</td>';
        $return .= '</tr>';
        if ($action != 'move') {
            $id_prerequisite = 0;
            if (is_array($arrLP)) {
                foreach ($arrLP as $key => $value) {
                    if ($value['id'] == $id) {
                        $id_prerequisite = $value['prerequisite'];
                        break;
                    }
                }
            }
            $arrHide = array ();
            for ($i = 0; $i < count($arrLP); $i++) {
                if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
                    if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
                        $s_selected_position = $arrLP[$i]['id'];
                    elseif ($action == 'add') $s_selected_position = 0;
                    $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];

                }
            }

            // Commented the prerequisites, only visible in edit (work).
            /*
                    $return .= '<tr>';
                    $return .= '<td class="label"><label for="idPrerequisites">'.get_lang('LearnpathPrerequisites').'</label></td>';
                    $return .= '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang('NoPrerequisites').'</option>';

                    foreach($arrHide as $key => $value) {
                        if ($key == $s_selected_position && $action == 'add') {
                            $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
                        }
                        elseif ($key == $id_prerequisite && $action == 'edit') {
                            $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
                        }
                        else {
                            $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
                        }
                    }

                    $return .= "</select></td>";
            */
            $return .= '</tr>';
        }

        $return .= '<tr>';
        if ($action == 'add') {
            $return .= '<td>&nbsp</td><td><button class="save" name="submit_button" type="submit">' . get_lang('AddAssignmentToCourse') . '</button></td>';
        } else {
            $return .= '<td>&nbsp</td><td><button class="save" name="submit_button" type="submit">' . get_lang('EditCurrentStudentPublication') . '</button></td>';
        }
        $return .= '</tr>';

        $return .= '</table>';

        if ($action == 'move') {
            $return .= '<input name="title" type="hidden" value="' . $item_title . '" />';
            $return .= '<input name="description" type="hidden" value="' . $item_description . '" />';
        }

        if (is_numeric($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info . '" />';
        } elseif (is_array($extra_info)) {
            $return .= '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />';
        }
        $return .= '<input name="type" type="hidden" value="' . TOOL_STUDENTPUBLICATION . '" />';
        $return .= '<input name="post_time" type="hidden" value="' . time() . '" />';
        $return .= '</form>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Displays the menu for manipulating a step
     * @return string html
     */
    public function display_manipulate($item_id, $item_type = TOOL_DOCUMENT) {
        $course_id = api_get_course_int_id();
        $course_code = api_get_course_id();

        global $charset, $_course;
        $return = '<div class="actions">';

        switch ($item_type) {
            case 'dokeos_chapter' :
            case 'chapter' :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateChapter');
                break;

            case 'dokeos_module' :
            case 'module' :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateModule');
                break;

            case TOOL_DOCUMENT :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateDocument');
                break;

            case TOOL_LINK :
            case 'link' :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateLink');
                break;

            case TOOL_QUIZ :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateQuiz');
                break;

            case TOOL_STUDENTPUBLICATION :
                // Commented the message cause should not show it.
                //$lang = get_lang('TitleManipulateStudentPublication');
                break;
        }

        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $item_id = intval($item_id);
        $sql    = "SELECT * FROM " . $tbl_lp_item . " as lp WHERE lp.c_id = ".$course_id." AND lp.id = " . $item_id;
        $result = Database::query($sql);

        $row = Database::fetch_assoc($result);

        $audio_player = null;
        // We display an audio player if needed.
        if (!empty($row['audio'])) {
            $audio_player .= '<div class="lp_mediaplayer" id="container"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</div>';
            $audio_player .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
            $audio_player .= '<script>
                                var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
                                s1.addParam("allowscriptaccess","always");
                                s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=true");
                                s1.write("container");
                            </script>';
        }
        $url = api_get_self() . '?cidReq='.Security::remove_XSS($_GET['cidReq']).'&view=build&id='.$item_id .'&lp_id='.$this->lp_id;

        $return .= Display::url(Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL), $url.'&action=edit_item&path_item=' . $row['path']);
        $return .= Display::url(Display::return_icon('move.png', get_lang('Move'), array(), ICON_SIZE_SMALL), $url.'&action=move_item');

        // Commented for now as prerequisites cannot be added to chapters.
        if ($item_type != 'dokeos_chapter' && $item_type != 'chapter') {
            $return .= Display::url(Display::return_icon('accept.png', get_lang('LearnpathPrerequisites'), array(), ICON_SIZE_SMALL), $url.'&action=edit_item_prereq');
        }
        $return .= Display::url(Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL), $url.'&action=delete_item');

         if ($item_type == TOOL_HOTPOTATOES ) {
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
        }

        if ($item_type == TOOL_DOCUMENT ) {
            $document_data = DocumentManager::get_document_data_by_id($row['path'], $course_code);
            $return .= get_lang('File').': '.$document_data['absolute_path_from_document'];
        }

        $return .= '</div>';

        if (!empty($audio_player)) {
            $return .= '<br />'.$audio_player;
        }

        return $return;
    }

    /**
     * Creates the javascript needed for filling up the checkboxes without page reload
     *
     * @return string
     */
    public function get_js_dropdown_array() {
        $course_id = api_get_course_int_id();

        $return = 'var child_name = new Array();' . "\n";
        $return .= 'var child_value = new Array();' . "\n\n";
        $return .= 'child_name[0] = new Array();' . "\n";
        $return .= 'child_value[0] = new Array();' . "\n\n";
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $sql_zero = "SELECT * FROM " . $tbl_lp_item . "
                    WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id . " AND parent_item_id = 0
                    ORDER BY display_order ASC";
        $res_zero = Database::query($sql_zero);

        global $charset;
        $i = 0;

        while ($row_zero = Database :: fetch_array($res_zero)) {
        	$js_var = json_encode(get_lang('After').' '.$row_zero['title']);
            $return .= 'child_name[0][' . $i . '] = '.$js_var.' ;' . "\n";
            $return .= 'child_value[0][' . $i++ . '] = "' . $row_zero['id'] . '";' . "\n";
        }
        $return .= "\n";
        $sql = "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = ".$course_id." AND lp_id = " . $this->lp_id;
        $res = Database::query($sql);
        while ($row = Database :: fetch_array($res)) {
            $sql_parent = "
                            SELECT * FROM " . $tbl_lp_item . "
                            WHERE c_id = ".$course_id." AND parent_item_id = " . $row['id'] . "
                            ORDER BY display_order ASC";
            $res_parent = Database::query($sql_parent);
            $i = 0;
            $return .= 'child_name[' . $row['id'] . '] = new Array();' . "\n";
            $return .= 'child_value[' . $row['id'] . '] = new Array();' . "\n\n";

            while ($row_parent = Database :: fetch_array($res_parent)) {
            	$js_var = json_encode(get_lang('After').' '.$row_parent['title']);
                $return .= 'child_name[' . $row['id'] . '][' . $i . '] =   '.$js_var.' ;' . "\n";
                $return .= 'child_value[' . $row['id'] . '][' . $i++ . '] = "' . $row_parent['id'] . '";' . "\n";
            }
            $return .= "\n";
        }
        return $return;
    }

    /**
     * Display the form to allow moving an item
     * @param	integer		Item ID
     * @return	string		HTML form
     */
    public function display_move_item($item_id) {
        $course_id = api_get_course_int_id();

        global $_course; //will disappear
        global $charset;
        $return = '';

        if (is_numeric($item_id)) {
            $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);

            $sql = "SELECT * FROM " . $tbl_lp_item . "
                    WHERE c_id = ".$course_id." AND id = " . $item_id;

            $res = Database::query($sql);
            $row = Database :: fetch_array($res);

            switch ($row['item_type']) {
                case 'dokeos_chapter' :
                case 'dir' :
                case 'asset' :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_item_form($row['item_type'], get_lang('MoveCurrentChapter'), 'move', $item_id, $row);
                    break;
                case 'dokeos_module' :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_item_form($row['item_type'], 'Move th current module:', 'move', $item_id, $row);
                    break;
                case TOOL_DOCUMENT :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_document_form('move', $item_id, $row);
                    break;
                case TOOL_LINK :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_link_form('move', $item_id, $row);
                    break;
                case TOOL_HOTPOTATOES :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_link_form('move', $item_id, $row);
                    break;
                case TOOL_QUIZ :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_quiz_form('move', $item_id, $row);
                    break;
                case TOOL_STUDENTPUBLICATION :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_student_publication_form('move', $item_id, $row);
                    break;
                case TOOL_FORUM :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_forum_form('move', $item_id, $row);
                    break;
                case TOOL_THREAD :
                    $return .= $this->display_manipulate($item_id, $row['item_type']);
                    $return .= $this->display_forum_form('move', $item_id, $row);
                    break;
            }
        }

        return $return;
    }

    /**
     * Displays a basic form on the overview page for changing the item title and the item description.
     * @param string $item_type
     * @param string $title
     * @param array $data
     * @return string
     */
    public function display_item_small_form($item_type, $title = '', $data = array()) {
        $url = api_get_self() . '?' .api_get_cidreq().'&action=edit_item&lp_id='.$this->lp_id;
        $form = new FormValidator('small_form', 'post', $url);
        $form->addElement('header', $title);
        $form->addElement('text', 'title', get_lang('Title'));
        $form->addElement('button', 'submit_button', get_lang('Save'));
        $form->addElement('hidden', 'id', $data['id']);
        $form->addElement('hidden', 'parent', $data['parent_item_id']);
        $form->addElement('hidden', 'previous', $data['previous_item_id']);
        $form->setDefaults(array('title' => $data['title']));
        return $form->toHtml();
    }

    /**
     * Return HTML form to allow prerequisites selection
     * @param	integer Item ID
     * @return	string	HTML form
     */
    public function display_item_prerequisites_form($item_id) {
        $course_id = api_get_course_int_id();
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $item_id = intval($item_id);
        /* Current prerequisite */
        $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND id = " . $item_id;
        $result = Database::query($sql);
        $row    = Database::fetch_array($result);

        $preq_id = $row['prerequisite'];
        //$preq_mastery = $row['mastery_score'];
        //$preq_max = $row['max_score'];

        $return = $this->display_manipulate($item_id, TOOL_DOCUMENT);

        $return = '<legend>';
        $return .= get_lang('AddEditPrerequisites');
        $return .= '</legend>';

        $return .= '<form method="POST">';

        $return .= '<table class="data_table">';
        $return .= '<tr>';
        $return .= '<th height="24">' . get_lang('LearnpathPrerequisites') . '</th>';
        $return .= '<th width="70" height="24">' . get_lang('Minimum') . '</th>';
        $return .= '<th width="70" height="24">' . get_lang('Maximum') . '</th>';
        $return .= '</tr>';

        // Adding the none option to the prerequisites see http://www.chamilo.org/es/node/146
        $return .= '<tr >';
        $return .= '<td colspan="3" class="radio">';
        $return .= '<input checked="checked" id="idNone" name="prerequisites"  style="margin-left:0px; margin-right:10px;" type="radio" />';
        $return .= '<label for="idNone">' . get_lang('None') . '</label>';
        $return .= '</tr>';

        $sql 	= "SELECT * FROM " . $tbl_lp_item . " WHERE c_id = $course_id AND lp_id = " . $this->lp_id;
        $result = Database::query($sql);
        $arrLP = array ();
        while ($row = Database :: fetch_array($result)) {
            $arrLP[] = array (
                'id' 				=> $row['id'],
                'item_type' 		=> $row['item_type'],
                'title' 			=> $row['title'],
                'ref' 				=> $row['ref'],
                'description' 		=> $row['description'],
                'parent_item_id' 	=> $row['parent_item_id'],
                'previous_item_id'	=> $row['previous_item_id'],
                'next_item_id' 		=> $row['next_item_id'],
                'max_score' 		=> $row['max_score'],
                'min_score' 		=> $row['min_score'],
                'mastery_score' 	=> $row['mastery_score'],
                'prerequisite' 		=> $row['prerequisite'],
                'next_item_id' 		=> $row['next_item_id'],
                'display_order' 	=> $row['display_order']
            );
            if ($row['ref'] == $preq_id) {
                $preq_mastery = $row['mastery_score'];
                $preq_max = $row['max_score'];
            }
        }
        $this->tree_array($arrLP);
        $arrLP = $this->arrMenu;
        unset ($this->arrMenu);

        for ($i = 0; $i < count($arrLP); $i++) {
            if ($arrLP[$i]['id'] == $item_id)
                break;
            $return .= '<tr>';
            $return .= '<td class="radio"' . (($arrLP[$i]['item_type'] != TOOL_QUIZ && $arrLP[$i]['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . '>';

            $return .= '<label for="id' . $arrLP[$i]['id'] . '">';

            $return .= '<input' . (($arrLP[$i]['id'] == $preq_id) ? ' checked="checked" ' : '') . (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter') ? ' disabled="disabled" ' : ' ') . 'id="id' . $arrLP[$i]['id'] . '" name="prerequisites" style="margin-left:' . $arrLP[$i]['depth'] * 10 . 'px; margin-right:10px;" type="radio" value="' . $arrLP[$i]['id'] . '" />';
            $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
            if (file_exists('../img/lp_' . $icon_name . '.png')) {
                $return .= '<img alt="" src="../img/lp_' . $icon_name . '.png" style="margin-right:5px;" title="" />';
            } else
                if (file_exists('../img/lp_' . $icon_name . '.gif')) {
                    $return .= '<img alt="" src="../img/lp_' . $icon_name . '.gif" style="margin-right:5px;" title="" />';
                } else {
                    $return .= Display::return_icon('folder_document.gif','',array('style'=>'margin-right:5px;'));
                }
            $return .=  $arrLP[$i]['title'] . '</label>';
            $return .= '</td>';

            //$return .= '<td class="radio"' . (($arrLP[$i]['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . ' />';

            if ($arrLP[$i]['item_type'] == TOOL_QUIZ) {
                $return .= '<td class="exercise" style="border:1px solid #ccc;">';
                $return .= '<center><input size="4" maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" /></center>';
                $return .= '</td>';
                $return .= '<td class="exercise" style="border:1px solid #ccc;">';
                $return .= '<center><input size="4" maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" /></center>';
                $return .= '</td>';
            }
            if ($arrLP[$i]['item_type'] == TOOL_HOTPOTATOES) {
                $return .= '<td class="exercise" style="border:1px solid #ccc;">';
                $return .= '<center><input size="4" maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" /></center>';
                $return .= '</td>';
                $return .= '<td class="exercise" style="border:1px solid #ccc;">';
                $return .= '<center><input size="4" maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" /></center>';
                $return .= '</td>';
            }
            $return .= '</tr>';
        }
        $return .= '<tr>';
        $return .= '</tr>';
        $return .= '</table>';
        $return .= '<div style="padding-top:3px;">';
        $return .= '<button class="save" name="submit_button" type="submit">' . get_lang('ModifyPrerequisites') . '</button>';

        $return .= '</form>';
        return $return;
    }

    /**
     * Return HTML list to allow prerequisites selection for lp
     * @param	integer Item ID
     * @return	string	HTML form
     */
    public function display_lp_prerequisites_list() {
        $course_id = api_get_course_int_id();
        $lp_id = $this->lp_id;
        $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);

        // get current prerequisite
        $sql = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $lp_id ";
        $result = Database::query($sql);
        $row = Database :: fetch_array($result);
        $preq_id = $row['prerequisite'];
        $session_id = api_get_session_id();
        $session_condition = api_get_session_condition($session_id);
        $sql 	= "SELECT * FROM $tbl_lp WHERE c_id = $course_id $session_condition ORDER BY display_order ";
        $rs = Database::query($sql);
        $return = '';
        $return .= '<select name="prerequisites" >';
        $return .= '<option value="0">'.get_lang('None').'</option>';
        if (Database::num_rows($rs) > 0) {
            while ($row = Database::fetch_array($rs)) {
                if ($row['id'] == $lp_id) {
                    continue;
                }
                $return .= '<option value="'.$row['id'].'" '.(($row['id']==$preq_id)?' selected ' : '').'>'.$row['name'].'</option>';
            }
        }
        $return .= '</select>';
        return $return;
    }

    /**
     * Creates a list with all the documents in it
     * @return string
     */
    public function get_documents() {
    	$course_info = api_get_course_info();
    	$document_tree = DocumentManager::get_document_preview($course_info, $this->lp_id, null, 0, true);
    	return $document_tree;
    }

    /**
     * Creates a list with all the exercises (quiz) in it
     * @return string
     */
    public function get_exercises() {
        $course_id = api_get_course_int_id();

        // New for hotpotatoes.
        $uploadPath = DIR_HOTPOTATOES; //defined in main_api
        $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
        $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);

        $session_id = api_get_session_id();
        $condition_session = api_get_session_condition($session_id);

        $sql_quiz = "SELECT * FROM $tbl_quiz WHERE c_id = $course_id AND active<>'-1' $condition_session ORDER BY title ASC";
        $sql_hot  = "SELECT * FROM $tbl_doc  WHERE c_id = $course_id AND path LIKE '" . $uploadPath . "/%/%htm%'  $condition_session ORDER BY id ASC";

        $res_quiz = Database::query($sql_quiz);
        $res_hot  = Database::query($sql_hot);

        $return = '<ul class="lp_resource">';

        $return .= '<li class="lp_resource_element">';
        $return .= '<img alt="" src="../img/new_test_small.gif" style="margin-right:5px;" title="" />';
        $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'exercice/exercise_admin.php?lp_id=' . $this->lp_id . '">' . get_lang('NewExercise') . '</a>';
        $return .= '</li>';

        // Display hotpotatoes
        while ($row_hot = Database :: fetch_array($res_hot)) {
            $return .= '<li class="lp_resource_element" data_id="'.$row_hot['id'].'" data_type="hotpotatoes" title="'.$row_hot['title'].'" >';

            $return .= '<a class="moved" href="#">';
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
            $return .= '</a> ';

            $return .= '<img src="../img/hotpotatoes_s.png" style="margin-right:5px;" title="" width="16px" />';
            $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq().'&amp;action=add_item&amp;type=' . TOOL_HOTPOTATOES . '&amp;file=' . $row_hot['id'] . '&amp;lp_id=' . $this->lp_id . '">'.
                        ((!empty ($row_hot['comment'])) ? $row_hot['comment'] : Security :: remove_XSS($row_hot['title'])) . '</a>';
            $return .= '</li>';
        }

        while ($row_quiz = Database :: fetch_array($res_quiz)) {
            $return .= '<li class="lp_resource_element" data_id="'.$row_quiz['id'].'" data_type="quiz" title="'.$row_quiz['title'].'" >';

            $return .= '<a class="moved" href="#">';
            $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
            $return .= '</a> ';

            $return .= '<img alt="" src="../img/quizz_small.gif" style="margin-right:5px;" title="" />';
            $return .= '<a href="' . api_get_self() . '?'.api_get_cidreq().'&amp;action=add_item&amp;type=' . TOOL_QUIZ . '&amp;file=' . $row_quiz['id'] . '&amp;lp_id=' . $this->lp_id . '">' .
                        Security :: remove_XSS(cut($row_quiz['title'], 80)).
                        '</a>';
            $return .= '</li>';
        }

        $return .= '</ul>';
        return $return;
    }

    /**
     * Creates a list with all the links in it
     * @return string
     */
    public function get_links() {
        $course_id = api_get_course_int_id();
        $tbl_link = Database :: get_course_table(TABLE_LINK);

        $session_id = api_get_session_id();
        $condition_session = api_get_session_condition($session_id);

        $sql_link = "SELECT id, title FROM $tbl_link WHERE c_id = ".$course_id." $condition_session ORDER BY title ASC";
        $res_link = Database::query($sql_link);

        $return = '<ul class="lp_resource">';
        $return .= '<li class="lp_resource_element">';
        $return .= '<img alt="" src="../img/linksnew.gif" style="margin-right:5px;width:16px" title="" />';
        $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'link/link.php?' . api_get_cidreq() . '&action=addlink&amp;lp_id=' . $this->lp_id . '" title="' . get_lang('LinkAdd') . '">' . get_lang('LinkAdd') . '</a>';
        $return .= '</li>';
        $course_info = api_get_course_info();

        while ($row_link = Database :: fetch_array($res_link)) {
            $item_visibility = api_get_item_visibility($course_info, TOOL_LINK, $row_link['id'], $session_id);
            if ($item_visibility != 2)  {
                $return .= '<li class="lp_resource_element" data_id="'.$row_link['id'].'" data_type="'.TOOL_LINK.'" title="'.$row_link['title'].'" >';

                $return .= '<a class="moved" href="#">';
                $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
                $return .= '</a> ';

                $return .= '<img alt="" src="../img/lp_link.gif" style="margin-right:5px;" title="" />';

                $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_LINK . '&amp;file=' . $row_link['id'] . '&amp;lp_id=' . $this->lp_id . '">'.
                            $row_link['title'].
                            '</a>';
                $return .= '</li>';
            }
        }
        $return .= '</ul>';
        return $return;
    }

    /**
     * Creates a list with all the student publications in it
     * @return unknown
     */
    public function get_student_publications() {
        $return = '<div class="lp_resource" >';
        $return .= '<div class="lp_resource_element">';
        $return .= '<img align="left" alt="" src="../img/works_small.gif" style="margin-right:5px;" title="" />';
        $return .= '<a href="' . api_get_self() . '?' . api_get_cidreq() . '&amp;action=add_item&amp;type=' . TOOL_STUDENTPUBLICATION . '&amp;lp_id=' . $this->lp_id . '">' . get_lang('AddAssignmentPage') . '</a>';
        $return .= '</div>';
        $return .= '</div>';
        return $return;
    }

    /**
     * Creates a list with all the forums in it
     * @return string
     */
    public function get_forums() {
        require_once '../forum/forumfunction.inc.php';
        require_once '../forum/forumconfig.inc.php';

        $a_forums = get_forums();

        $return = '<ul class="lp_resource">';

        //First add link
        $return .= '<li class="lp_resource_element">';
        $return .= '<img alt="" src="../img/forum_new_small.gif" style="margin-right:5px;" title="" />';
        $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'forum/index.php?' . api_get_cidreq() . '&action=add&amp;content=forum&amp;origin=learnpath&amp;lp_id=' . $this->lp_id . '" title="' . get_lang('CreateANewForum') . '">' . get_lang('CreateANewForum') . '</a>';
        $return .= '</li>';

        $return .= '<script>
                    function toggle_forum(forum_id){
                        if(document.getElementById("forum_"+forum_id+"_content").style.display == "none"){
                            document.getElementById("forum_"+forum_id+"_content").style.display = "block";
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . api_get_path(WEB_IMG_PATH) . 'remove.gif";
                        } else {
                            document.getElementById("forum_"+forum_id+"_content").style.display = "none";
                            document.getElementById("forum_"+forum_id+"_opener").src = "' . api_get_path(WEB_IMG_PATH) . 'add.gif";
                        }
                    }
                </script>';

        foreach ($a_forums as $forum) {
            $return .= '<li class="lp_resource_element" data_id="'.$forum['forum_id'].'" data_type="'.TOOL_FORUM.'" title="'.$forum['forum_title'].'" >';

            if (!empty($forum['forum_id'])) {

                $return .= '<a class="moved" href="#">';
				$return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
                $return .= ' </a>';

                $return .= '<img alt="" src="../img/lp_forum.gif" style="margin-right:5px;" title="" />';

                $return .= '<a style="cursor:hand" onclick="javascript: toggle_forum(' . $forum['forum_id'] . ')" style="vertical-align:middle">
                                <img src="' . api_get_path(WEB_IMG_PATH) . 'add.gif" id="forum_' . $forum['forum_id'] . '_opener" align="absbottom" />
                            </a>
                            <a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_FORUM . '&amp;forum_id=' . $forum['forum_id'] . '&amp;lp_id=' . $this->lp_id . '" style="vertical-align:middle">' . Security :: remove_XSS($forum['forum_title']) . '</a>';
            }
            $return .= '</li>';

            $return .= '<div style="display:none" id="forum_' . $forum['forum_id'] . '_content">';
            $a_threads = get_threads($forum['forum_id']);
            if (is_array($a_threads)) {
                foreach ($a_threads as $thread) {
                    $return .= '<li class="lp_resource_element" data_id="'.$thread['thread_id'].'" data_type="'.TOOL_THREAD.'" title="'.$thread['thread_title'].'" >';

                    $return .= '&nbsp;<a class="moved" href="#">';
                    $return .= Display::return_icon('move_everywhere.png', get_lang('Move'), array(), ICON_SIZE_TINY);
                    $return .= ' </a>';

                    $return .= Display::return_icon('forumthread.png', get_lang('Thread'), array(), ICON_SIZE_TINY);

                    $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_THREAD . '&amp;thread_id=' . $thread['thread_id'] . '&amp;lp_id=' . $this->lp_id . '">' . Security :: remove_XSS($thread['thread_title']) . '</a>';
                    $return .= '</li>';
                }
            }
            $return .= '</div>';

        }
        $return .= '</ul>';
        return $return;
    }

    /**
     * // TODO: The output encoding should be equal to the system encoding.
     *
     * Exports the learning path as a SCORM package. This is the main function that
     * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
     * whole thing and returns the zip.
     *
     * This method needs to be called in PHP5, as it will fail with non-adequate
     * XML package (like the ones for PHP4), and it is *not* a static method, so
     * you need to call it on a learnpath object.
     * @TODO The method might be redefined later on in the scorm class itself to avoid
     * creating a SCORM structure if there is one already. However, if the initial SCORM
     * path has been modified, it should use the generic method here below.
     * @TODO link this function with the export_lp() function in the same class
     * @param	string	Optional name of zip file. If none, title of learnpath is
     * 					domesticated and trailed with ".zip"
     * @return	string	Returns the zip package string, or null if error
     */
    public  function scorm_export() {
        global $_course;

        $course_id = api_get_course_int_id();

        // Remove memory and time limits as much as possible as this might be a long process...
        if (function_exists('ini_set')) {
            $mem = ini_get('memory_limit');
            if (substr($mem, -1, 1) == 'M') {
                $mem_num = substr($mem, 0, -1);
                if ($mem_num < 128) {
                    ini_set('memory_limit', '128M');
                }
            } else {
                ini_set('memory_limit', '128M');
            }
            ini_set('max_execution_time', 600);
        }

        // Create the zip handler (this will remain available throughout the method).
        $archive_path = api_get_path(SYS_ARCHIVE_PATH);
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
        $temp_dir_short = uniqid();
        $temp_zip_dir = $archive_path.'/'.$temp_dir_short;
        $temp_zip_file = $temp_zip_dir.'/'.md5(time()).'.zip';
        $zip_folder = new PclZip($temp_zip_file);
        $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
        $root_path = $main_path = api_get_path(SYS_PATH);
        $files_cleanup = array();

        // Place to temporarily stash the zipfiles.
        // create the temp dir if it doesn't exist
        // or do a cleanup befor creating the zipfile.
        if (!is_dir($temp_zip_dir)) {
            mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
        } else {
            // Cleanup: Check the temp dir for old files and delete them.
            $handle = opendir($temp_zip_dir);
            while (false !== ($file = readdir($handle))) {
                if ($file != '.' && $file != '..') {
                    unlink("$temp_zip_dir/$file");
                }
            }
            closedir($handle);
        }
        $zip_files = $zip_files_abs = $zip_files_dist = array();
        if (is_dir($current_course_path.'/scorm/'.$this->path) && is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml')) {
            // Remove the possible . at the end of the path.
            $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
            $dest_path_to_scorm_folder = str_replace('//','/',$temp_zip_dir.'/scorm/'.$dest_path_to_lp);
            mkdir($dest_path_to_scorm_folder, api_get_permissions_for_new_directories(), true);
            $zip_files_dist = copyr($current_course_path.'/scorm/'.$this->path, $dest_path_to_scorm_folder, array('imsmanifest'), $zip_files);
        }
        // Build a dummy imsmanifest structure. Do not add to the zip yet (we still need it).
        // This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
        // Aggregation Model official document, secion "2.3 Content Packaging".
        $xmldoc = new DOMDocument('1.0'); // We are going to build a UTF-8 encoded manifest. Later we will recode it to the desired (and supported) encoding.
        $root = $xmldoc->createElement('manifest');
        $root->setAttribute('identifier', 'SingleCourseManifest');
        $root->setAttribute('version', '1.1');
        $root->setAttribute('xmlns', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2');
        $root->setAttribute('xmlns:adlcp', 'http://www.adlnet.org/xsd/adlcp_rootv1p2');
        $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
        $root->setAttribute('xsi:schemaLocation', 'http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd');
        // Build mandatory sub-root container elements.
        $metadata = $xmldoc->createElement('metadata');
        $md_schema = $xmldoc->createElement('schema', 'ADL SCORM');
        $metadata->appendChild($md_schema);
        $md_schemaversion = $xmldoc->createElement('schemaversion', '1.2');
        $metadata->appendChild($md_schemaversion);
        $root->appendChild($metadata);

        $organizations = $xmldoc->createElement('organizations');

        $resources = $xmldoc->createElement('resources');

        // Build the only organization we will use in building our learnpaths.
        $organizations->setAttribute('default', 'chamilo_scorm_export');
        $organization = $xmldoc->createElement('organization');
        $organization->setAttribute('identifier', 'chamilo_scorm_export');
        // To set the title of the SCORM entity (=organization), we take the name given
        // in Chamilo and convert it to HTML entities using the Chamilo charset (not the
        // learning path charset) as it is the encoding that defines how it is stored
        // in the database. Then we convert it to HTML entities again as the "&" character
        // alone is not authorized in XML (must be &amp;).
        // The title is then decoded twice when extracting (see scorm::parse_manifest).
        $org_title = $xmldoc->createElement('title', api_utf8_encode($this->get_name()));
        $organization->appendChild($org_title);

        $folder_name = 'document';

        // Removes the learning_path/scorm_folder path when exporting see #4841
        $path_to_remove = null;
        $result = $this->generate_lp_folder($_course);

        if (isset($result['dir']) && strpos($result['dir'], 'learning_path')) {
            $path_to_remove = 'document'.$result['dir'];
            $path_to_replace = $folder_name.'/';
        }

        //Fixes chamilo scorm exports
        if ($this->ref == 'chamilo_scorm_export') {
            $path_to_remove = 'scorm/'.$this->path.'/document/';
        }

        // For each element, add it to the imsmanifest structure, then add it to the zip.
        // Always call the learnpathItem->scorm_export() method to change it to the SCORM format.
        $link_updates = array();

        foreach ($this->items as $index => $item) {
            if (!in_array($item->type, array(TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION))) {
                // Get included documents from this item.
                if ($item->type == 'sco')
                    $inc_docs = $item->get_resources_from_source(null, api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.'scorm/'.$this->path.'/'.$item->get_path());
                else
                    $inc_docs = $item->get_resources_from_source();
                // Give a child element <item> to the <organization> element.
                $my_item_id = $item->get_id();
                $my_item = $xmldoc->createElement('item');
                $my_item->setAttribute('identifier', 'ITEM_'.$my_item_id);
                $my_item->setAttribute('identifierref', 'RESOURCE_'.$my_item_id);
                $my_item->setAttribute('isvisible', 'true');
                // Give a child element <title> to the <item> element.
                $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
                $my_item->appendChild($my_title);
                // Give a child element <adlcp:prerequisites> to the <item> element.
                $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $this->get_scorm_prereq_string($my_item_id));
                $my_prereqs->setAttribute('type', 'aicc_script');
                $my_item->appendChild($my_prereqs);
                // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
                //$xmldoc->createElement('adlcp:maxtimeallowed','');
                // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
                //$xmldoc->createElement('adlcp:timelimitaction','');
                // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
                //$xmldoc->createElement('adlcp:datafromlms','');
                // Give a child element <adlcp:masteryscore> to the <item> element.
                $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
                $my_item->appendChild($my_masteryscore);

                // Attach this item to the organization element or hits parent if there is one.
                if (!empty($item->parent) && $item->parent != 0) {
                    $children = $organization->childNodes;
                    $possible_parent = &$this->get_scorm_xml_node($children, 'ITEM_'.$item->parent);
                    if (is_object($possible_parent)) {
                        $possible_parent->appendChild($my_item);
                    } else {
                        if ($this->debug > 0) { error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found'); }
                    }
                } else {
                    if ($this->debug > 0) { error_log('No parent'); }
                    $organization->appendChild($my_item);
                }

                // Get the path of the file(s) from the course directory root.
                $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');

                if (!empty($path_to_remove)) {
                    //From docs
                    $my_xml_file_path = str_replace($path_to_remove, $path_to_replace, $my_file_path);

                    //From quiz
                    if ($this->ref == 'chamilo_scorm_export') {
                        $path_to_remove = 'scorm/'.$this->path.'/';
                        $my_xml_file_path = str_replace($path_to_remove, '', $my_file_path);
                    }
                } else {
                     $my_xml_file_path = $my_file_path;
                }

                $my_sub_dir = dirname($my_file_path);
                $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
                //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_QUOTES, 'UTF-8');
                $my_xml_sub_dir = $my_sub_dir;
                // Give a <resource> child to the <resources> element
                $my_resource = $xmldoc->createElement('resource');
                $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
                $my_resource->setAttribute('type', 'webcontent');
                $my_resource->setAttribute('href', $my_xml_file_path);
                // adlcp:scormtype can be either 'sco' or 'asset'.
                if ($item->type == 'sco') {
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
                } else {
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
                }
                // xml:base is the base directory to find the files declared in this resource.
                $my_resource->setAttribute('xml:base', '');
                // Give a <file> child to the <resource> element.
                $my_file = $xmldoc->createElement('file');
                $my_file->setAttribute('href', $my_xml_file_path);
                $my_resource->appendChild($my_file);

                // Dependency to other files - not yet supported.
                $i = 1;
                foreach ($inc_docs as $doc_info) {
                    if (count($doc_info) < 1 || empty($doc_info[0])) { continue; }
                    $my_dep = $xmldoc->createElement('resource');
                    $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
                    $my_dep->setAttribute('identifier', $res_id);
                    $my_dep->setAttribute('type', 'webcontent');
                    $my_dep->setAttribute('adlcp:scormtype', 'asset');
                    $my_dep_file = $xmldoc->createElement('file');
                    // Check type of URL.
                    //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2], 0);
                    if ($doc_info[1] == 'remote') {
                        // Remote file. Save url as is.
                        $my_dep_file->setAttribute('href', $doc_info[0]);
                        $my_dep->setAttribute('xml:base', '');
                    } elseif ($doc_info[1] == 'local') {
                        switch ($doc_info[2]) {
                            case 'url': // Local URL - save path as url for now, don't zip file.
                                $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
                                $current_dir = dirname($abs_path);
                                $current_dir = str_replace('\\', '/', $current_dir);
                                $file_path = realpath($abs_path);
                                $file_path = str_replace('\\', '/', $file_path);
                                $my_dep_file->setAttribute('href', $file_path);
                                $my_dep->setAttribute('xml:base', '');
                                if (strstr($file_path, $main_path) !== false) {
                                    // The calculated real path is really inside Chamilo's root path.
                                    // Reduce file path to what's under the DocumentRoot.
                                    $file_path = substr($file_path, strlen($root_path) - 1);
                                    //echo $file_path;echo '<br /><br />';
                                    //error_log(__LINE__.'Reduced url path: '.$file_path, 0);
                                    $zip_files_abs[] = $file_path;
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                    $my_dep_file->setAttribute('href', $file_path);
                                    $my_dep->setAttribute('xml:base', '');
                                } elseif (empty($file_path)) {
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
                                    if (strpos($document_root, -1) == '/') {
                                        $document_root = substr(0, -1, $document_root);
                                    }*/
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
                                    $file_path = str_replace('//', '/', $file_path);
                                    if (file_exists($file_path)) {
                                        $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                        $my_dep_file->setAttribute('href', $file_path);
                                        $my_dep->setAttribute('xml:base', '');
                                    }
                                }
                                break;
                            case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
                                $my_dep_file->setAttribute('href', $doc_info[0]);
                                $my_dep->setAttribute('xml:base', '');

                                //$current_dir = str_replace('\\', '/', dirname($current_course_path.'/'.$item->get_file_path())).'/';
                                // The next lines fix a bug when using the "subdir" mode of Chamilo, whereas
                                // an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
                                $abs_img_path_without_subdir = $doc_info[0];
                                $relp = api_get_path(REL_PATH); // The url-append config param.
                                $pos = strpos($abs_img_path_without_subdir, $relp);
                                if ($pos === 0) {
                                    $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir, strlen($relp));
                                }
                                //$file_path = realpath(api_get_path(SYS_PATH).$doc_info[0]);
                                $file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
                                $file_path = str_replace('\\', '/', $file_path);
                                $file_path = str_replace('//', '/', $file_path);
                                //error_log(__LINE__.'Abs path: '.$file_path, 0);
                                // Prepare the current directory path (until just under 'document') with a trailing slash.
                                $cur_path = substr($current_course_path, -1) == '/' ? $current_course_path : $current_course_path.'/';
                                // Check if the current document is in that path.
                                if (strstr($file_path, $cur_path) !== false) {
                                    // The document is in that path, now get the relative path
                                    // to the containing document.
                                    $orig_file_path = dirname($cur_path.$my_file_path).'/';
                                    $orig_file_path = str_replace('\\', '/', $orig_file_path);
                                    $relative_path = '';
                                    if (strstr($file_path, $cur_path) !== false) {
                                        $relative_path = substr($file_path, strlen($orig_file_path));
                                        $file_path = substr($file_path, strlen($cur_path));
                                    } else {
                                        // This case is still a problem as it's difficult to calculate a relative path easily
                                        // might still generate wrong links.
                                        //$file_path = substr($file_path,strlen($cur_path));
                                        // Calculate the directory path to the current file (without trailing slash).
                                        $my_relative_path = dirname($file_path);
                                        $my_relative_path = str_replace('\\', '/', $my_relative_path);
                                        $my_relative_file = basename($file_path);
                                        // Calculate the directory path to the containing file (without trailing slash).
                                        $my_orig_file_path = substr($orig_file_path, 0, -1);
                                        $dotdot = '';
                                        $subdir = '';
                                        while (strstr($my_relative_path, $my_orig_file_path) === false && (strlen($my_orig_file_path) > 1) && (strlen($my_relative_path) > 1)) {
                                            $my_relative_path2 = dirname($my_relative_path);
                                            $my_relative_path2 = str_replace('\\', '/', $my_relative_path2);
                                            $my_orig_file_path = dirname($my_orig_file_path);
                                            $my_orig_file_path = str_replace('\\', '/', $my_orig_file_path);
                                            $subdir = substr($my_relative_path, strlen($my_relative_path2) + 1).'/'.$subdir;
                                            $dotdot += '../';
                                            $my_relative_path = $my_relative_path2;
                                        }
                                        $relative_path = $dotdot.$subdir.$my_relative_file;
                                    }
                                    // Put the current document in the zip (this array is the array
                                    // that will manage documents already in the course folder - relative).
                                    $zip_files[] = $file_path;
                                    // Update the links to the current document in the containing document (make them relative).
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $relative_path);
                                    $my_dep_file->setAttribute('href', $file_path);
                                    $my_dep->setAttribute('xml:base', '');
                                } elseif (strstr($file_path, $main_path) !== false) {
                                    // The calculated real path is really inside Chamilo's root path.
                                    // Reduce file path to what's under the DocumentRoot.
                                    $file_path = substr($file_path, strlen($root_path));
                                    //echo $file_path;echo '<br /><br />';
                                    //error_log('Reduced path: '.$file_path, 0);
                                    $zip_files_abs[] = $file_path;
                                    $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                    $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                    $my_dep->setAttribute('xml:base', '');
                                } elseif (empty($file_path)) {
                                    /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
                                    if(strpos($document_root,-1) == '/') {
                                        $document_root = substr(0, -1, $document_root);
                                    }*/
                                    $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
                                    $file_path = str_replace('//', '/', $file_path);
                                    if (file_exists($file_path)) {
                                        $file_path = substr($file_path,strlen($current_dir)); // We get the relative path.
                                        $zip_files[] = $my_sub_dir.'/'.$file_path;
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                        $my_dep_file->setAttribute('href','document/'.$file_path);
                                        $my_dep->setAttribute('xml:base', '');
                                    }
                                }
                                break;
                             case 'rel': // Path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
                                 if (substr($doc_info[0], 0, 2) == '..') {
                                     // Relative path going up.
                                     $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
                                     $current_dir = str_replace('\\', '/', $current_dir);
                                     $file_path = realpath($current_dir.$doc_info[0]);
                                     $file_path = str_replace('\\', '/', $file_path);

                                     //error_log($file_path.' <-> '.$main_path,0);
                                     if (strstr($file_path, $main_path) !== false) {
                                         // The calculated real path is really inside Chamilo's root path.
                                         // Reduce file path to what's under the DocumentRoot.
                                         $file_path = substr($file_path, strlen($root_path));
                                         //error_log('Reduced path: '.$file_path, 0);
                                         $zip_files_abs[] = $file_path;
                                         $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                         $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                         $my_dep->setAttribute('xml:base', '');
                                     }
                                 } else {
                                     $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
                                     $my_dep_file->setAttribute('href', $doc_info[0]);
                                     $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
                                 }
                                 break;
                             default:
                                 $my_dep_file->setAttribute('href', $doc_info[0]);
                                 $my_dep->setAttribute('xml:base', '');
                                 break;
                        }
                    }
                    $my_dep->appendChild($my_dep_file);
                    $resources->appendChild($my_dep);
                    $dependency = $xmldoc->createElement('dependency');
                    $dependency->setAttribute('identifierref', $res_id);
                    $my_resource->appendChild($dependency);
                    $i++;
                }
                //$my_dependency = $xmldoc->createElement('dependency');
                //$my_dependency->setAttribute('identifierref', '');
                $resources->appendChild($my_resource);
                $zip_files[] = $my_file_path;

                //error_log('File '.$my_file_path. ' added to $zip_files', 0);
            } else {
                // If the item is a quiz or a link or whatever non-exportable, we include a step indicating it.
                switch ($item->type) {
                    case TOOL_LINK:
                    $my_item = $xmldoc->createElement('item');
                    $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
                    $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
                    $my_item->setAttribute('isvisible', 'true');
                    // Give a child element <title> to the <item> element.
                    $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
                    $my_item->appendChild($my_title);
                    // Give a child element <adlcp:prerequisites> to the <item> element.
                    $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
                    $my_prereqs->setAttribute('type', 'aicc_script');
                    $my_item->appendChild($my_prereqs);
                    // Give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported.
                    //$xmldoc->createElement('adlcp:maxtimeallowed', '');
                    // Give a child element <adlcp:timelimitaction> to the <item> element - not yet supported.
                    //$xmldoc->createElement('adlcp:timelimitaction', '');
                    // Give a child element <adlcp:datafromlms> to the <item> element - not yet supported.
                    //$xmldoc->createElement('adlcp:datafromlms', '');
                    // Give a child element <adlcp:masteryscore> to the <item> element.
                    $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
                    $my_item->appendChild($my_masteryscore);

                    // Attach this item to the organization element or its parent if there is one.
                    if (!empty($item->parent) && $item->parent != 0) {
                        $children = $organization->childNodes;
                        for ($i = 0; $i < $children->length; $i++) {
                            $item_temp = $children->item($i);
                            if ($item_temp -> nodeName == 'item') {
                                if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
                                    $item_temp -> appendChild($my_item);
                                }
                            }
                        }
                    } else {
                        $organization->appendChild($my_item);
                    }

                    $my_file_path = 'link_'.$item->get_id().'.html';
                        $sql = 'SELECT url, title FROM '.Database :: get_course_table(TABLE_LINK).' WHERE c_id = '.$course_id.' AND id='.$item->path;
                    $rs = Database::query($sql);
                    if ($link = Database :: fetch_array($rs)) {
                        $url = $link['url'];
                        $title = stripslashes($link['title']);
                        $links_to_create[$my_file_path] = array('title' => $title, 'url' => $url);
                        //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_QUOTES, 'UTF-8');
                        $my_xml_file_path = $my_file_path;
                        $my_sub_dir = dirname($my_file_path);
                        $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
                        //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_QUOTES, 'UTF-8');
                        $my_xml_sub_dir = $my_sub_dir;
                        // Give a <resource> child to the <resources> element.
                        $my_resource = $xmldoc->createElement('resource');
                        $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
                        $my_resource->setAttribute('type', 'webcontent');
                        $my_resource->setAttribute('href', $my_xml_file_path);
                        // adlcp:scormtype can be either 'sco' or 'asset'.
                        $my_resource->setAttribute('adlcp:scormtype', 'asset');
                        // xml:base is the base directory to find the files declared in this resource.
                        $my_resource->setAttribute('xml:base', '');
                        // give a <file> child to the <resource> element.
                        $my_file = $xmldoc->createElement('file');
                        $my_file->setAttribute('href', $my_xml_file_path);
                        $my_resource->appendChild($my_file);
                        $resources->appendChild($my_resource);
                    }
                        break;
                    case TOOL_QUIZ:
                    require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.class.php';
                    $exe_id = $item->path; // Should be using ref when everything will be cleaned up in this regard.
                    $exe = new Exercise();
                    $exe->read($exe_id);
                    $my_item = $xmldoc->createElement('item');
                    $my_item->setAttribute('identifier', 'ITEM_'.$item->get_id());
                    $my_item->setAttribute('identifierref', 'RESOURCE_'.$item->get_id());
                    $my_item->setAttribute('isvisible', 'true');
                    // Give a child element <title> to the <item> element.
                    $my_title = $xmldoc->createElement('title', htmlspecialchars(api_utf8_encode($item->get_title()), ENT_QUOTES, 'UTF-8'));
                    $my_item->appendChild($my_title);
                    $my_max_score = $xmldoc->createElement('max_score', $item->get_max());
                    //$my_item->appendChild($my_max_score);
                    // Give a child element <adlcp:prerequisites> to the <item> element.
                    $my_prereqs = $xmldoc->createElement('adlcp:prerequisites', $item->get_prereq_string());
                    $my_prereqs->setAttribute('type','aicc_script');
                    $my_item->appendChild($my_prereqs);
                    // Give a child element <adlcp:masteryscore> to the <item> element.
                    $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore', $item->get_mastery_score());
                    $my_item->appendChild($my_masteryscore);

                    // Attach this item to the organization element or hits parent if there is one.
                    if (!empty($item->parent) && $item->parent != 0) {
                        $children = $organization->childNodes;
                        for ($i = 0; $i < $children->length; $i++) {
                            $item_temp = $children->item($i);
                            if ($item_temp -> nodeName == 'item') {
                                if ($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent) {
                                    $item_temp -> appendChild($my_item);
                                }
                            }
                        }
                    } else {
                        $organization->appendChild($my_item);
                    }

                    // Include export scripts.
                    require_once api_get_path(SYS_CODE_PATH).'exercice/export/scorm/scorm_export.php';

                    // Get the path of the file(s) from the course directory root
                    //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
                    $my_file_path = 'quiz_'.$item->get_id().'.html';
                    // Write the contents of the exported exercise into a (big) html file
                    // to later pack it into the exported SCORM. The file will be removed afterwards.
                        $contents = export_exercise($exe_id, true);
                    $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
                    $res = file_put_contents($tmp_file_path, $contents);
                    if ($res === false) { error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__, 0); }
                    $files_cleanup[] = $tmp_file_path;
                    //error_log($tmp_path); die();
                    //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_QUOTES, 'UTF-8');
                    $my_xml_file_path = $my_file_path;
                    $my_sub_dir = dirname($my_file_path);
                    $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
                    //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_QUOTES, 'UTF-8');
                    $my_xml_sub_dir = $my_sub_dir;
                    // Give a <resource> child to the <resources> element.
                    $my_resource = $xmldoc->createElement('resource');
                    $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
                    $my_resource->setAttribute('type', 'webcontent');
                    $my_resource->setAttribute('href', $my_xml_file_path);
                    // adlcp:scormtype can be either 'sco' or 'asset'.
                    $my_resource->setAttribute('adlcp:scormtype', 'sco');
                    // xml:base is the base directory to find the files declared in this resource.
                    $my_resource->setAttribute('xml:base', '');
                    // Give a <file> child to the <resource> element.
                    $my_file = $xmldoc->createElement('file');
                    $my_file->setAttribute('href', $my_xml_file_path);
                    $my_resource->appendChild($my_file);

                    // Get included docs.
                    $inc_docs = $item->get_resources_from_source(null,$tmp_file_path);
                    // Dependency to other files - not yet supported.
                    $i = 1;
                    foreach ($inc_docs as $doc_info) {
                        if (count($doc_info) < 1 || empty($doc_info[0])) { continue; }
                        $my_dep = $xmldoc->createElement('resource');
                        $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
                        $my_dep->setAttribute('identifier', $res_id);
                        $my_dep->setAttribute('type', 'webcontent');
                        $my_dep->setAttribute('adlcp:scormtype', 'asset');
                        $my_dep_file = $xmldoc->createElement('file');
                        // Check type of URL.
                        //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2], 0);
                        if ($doc_info[1] == 'remote') {
                            // Remote file. Save url as is.
                            $my_dep_file->setAttribute('href', $doc_info[0]);
                            $my_dep->setAttribute('xml:base', '');
                        } elseif ($doc_info[1] == 'local') {
                            switch ($doc_info[2]) {
                                case 'url': // Local URL - save path as url for now, don't zip file.
                                    // Save file but as local file (retrieve from URL).
                                    $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH), '', $doc_info[0]);
                                    $current_dir = dirname($abs_path);
                                    $current_dir = str_replace('\\', '/', $current_dir);
                                    $file_path = realpath($abs_path);
                                    $file_path = str_replace('\\', '/', $file_path);
                                    $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                    $my_dep->setAttribute('xml:base', '');
                                    if (strstr($file_path, $main_path) !== false) {
                                        // The calculated real path is really inside the chamilo root path.
                                        // Reduce file path to what's under the DocumentRoot.
                                        $file_path = substr($file_path, strlen($root_path));
                                        //echo $file_path;echo '<br /><br />';
                                        //error_log('Reduced path: '.$file_path, 0);
                                        $zip_files_abs[] = $file_path;
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                        $my_dep->setAttribute('xml:base', '');
                                        } elseif (empty($file_path)) {
                                        /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
                                        if (strpos($document_root,-1) == '/') {
                                            $document_root = substr(0, -1, $document_root);
                                        }*/
                                        $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
                                        $file_path = str_replace('//', '/', $file_path);
                                        if (file_exists($file_path)) {
                                            $file_path = substr($file_path, strlen($current_dir)); // We get the relative path.
                                            $zip_files[] = $my_sub_dir.'/'.$file_path;
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => 'document/'.$file_path);
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                             $my_dep->setAttribute('xml:base', '');
                                        }
                                    }
                                    break;
                                case 'abs': // Absolute path from DocumentRoot. Save file and leave path as is in the zip.
                                    $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
                                    $current_dir = str_replace('\\', '/', $current_dir);
                                    $file_path = realpath($doc_info[0]);
                                    $file_path = str_replace('\\', '/', $file_path);
                                    $my_dep_file->setAttribute('href', $file_path);
                                    $my_dep->setAttribute('xml:base', '');

                                    if (strstr($file_path,$main_path) !== false) {
                                        // The calculated real path is really inside the chamilo root path.
                                        // Reduce file path to what's under the DocumentRoot.
                                        $file_path = substr($file_path, strlen($root_path));
                                        //echo $file_path;echo '<br /><br />';
                                        //error_log('Reduced path: '.$file_path, 0);
                                        $zip_files_abs[] = $file_path;
                                        $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                        $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                        $my_dep->setAttribute('xml:base', '');
                                        } elseif (empty($file_path)) {
                                        /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH), api_get_path(REL_PATH)));
                                        if (strpos($document_root,-1) == '/') {
                                            $document_root = substr(0, -1, $document_root);
                                        }*/
                                        $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
                                        $file_path = str_replace('//', '/', $file_path);
                                        if (file_exists($file_path)) {
                                            $file_path = substr($file_path,strlen($current_dir)); // We get the relative path.
                                            $zip_files[] = $my_sub_dir.'/'.$file_path;
                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path);
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                             $my_dep->setAttribute('xml:base', '');
                                        }
                                    }
                                    break;
                                case 'rel': // Path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
                                    if (substr($doc_info[0], 0, 2) == '..') {
                                        // Relative path going up.
                                        $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
                                        $current_dir = str_replace('\\', '/', $current_dir);
                                        $file_path = realpath($current_dir.$doc_info[0]);
                                        $file_path = str_replace('\\', '/', $file_path);
                                        //error_log($file_path.' <-> '.$main_path, 0);
                                        if (strstr($file_path, $main_path) !== false) {
                                            // The calculated real path is really inside Chamilo's root path.
                                            // Reduce file path to what's under the DocumentRoot.

                                            $file_path = substr($file_path, strlen($root_path));
                                            $file_path_dest = $file_path;

                                           // File path is courses/CHAMILO/document/....
                                           $info_file_path = explode('/', $file_path);
                                           if ($info_file_path[0] == 'courses') { // Add character "/" in file path.
                                               $file_path_dest = 'document/'.$file_path;
                                           }

                                            //error_log('Reduced path: '.$file_path, 0);
                                            $zip_files_abs[] = $file_path;

                                            $link_updates[$my_file_path][] = array('orig' => $doc_info[0], 'dest' => $file_path_dest);
                                            $my_dep_file->setAttribute('href', 'document/'.$file_path);
                                             $my_dep->setAttribute('xml:base', '');
                                        }
                                    } else {
                                        $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
                                        $my_dep_file->setAttribute('href', $doc_info[0]);
                                        $my_dep->setAttribute('xml:base', $my_xml_sub_dir);
                                    }
                                    break;
                                default:
                                    $my_dep_file->setAttribute('href', $doc_info[0]); // ../../courses/
                                    $my_dep->setAttribute('xml:base', '');
                                    break;
                            }
                        }
                        $my_dep->appendChild($my_dep_file);
                        $resources->appendChild($my_dep);
                        $dependency = $xmldoc->createElement('dependency');
                        $dependency->setAttribute('identifierref', $res_id);
                        $my_resource->appendChild($dependency);
                        $i++;
                    }
                    $resources->appendChild($my_resource);
                    $zip_files[] = $my_file_path;
                        break;
                    default:
                    // Get the path of the file(s) from the course directory root
                    $my_file_path = 'non_exportable.html';
                    //$my_xml_file_path = api_htmlentities(api_utf8_encode($my_file_path), ENT_COMPAT, 'UTF-8');
                    $my_xml_file_path = $my_file_path;
                    $my_sub_dir = dirname($my_file_path);
                    $my_sub_dir = str_replace('\\', '/', $my_sub_dir);
                    //$my_xml_sub_dir = api_htmlentities(api_utf8_encode($my_sub_dir), ENT_COMPAT, 'UTF-8');
                    $my_xml_sub_dir = $my_sub_dir;
                    // Give a <resource> child to the <resources> element.
                    $my_resource = $xmldoc->createElement('resource');
                    $my_resource->setAttribute('identifier', 'RESOURCE_'.$item->get_id());
                    $my_resource->setAttribute('type', 'webcontent');

                        $my_resource->setAttribute('href', $folder_name.'/'.$my_xml_file_path);
                    // adlcp:scormtype can be either 'sco' or 'asset'.
                    $my_resource->setAttribute('adlcp:scormtype', 'asset');
                    // xml:base is the base directory to find the files declared in this resource.
                    $my_resource->setAttribute('xml:base', '');
                    // Give a <file> child to the <resource> element.
                    $my_file = $xmldoc->createElement('file');
                    $my_file->setAttribute('href', 'document/'.$my_xml_file_path);
                    $my_resource->appendChild($my_file);
                    $resources->appendChild($my_resource);
                        break;
                }
            }
        }


        $organizations->appendChild($organization);
        $root->appendChild($organizations);
        $root->appendChild($resources);
        $xmldoc->appendChild($root);


        // TODO: Add a readme file here, with a short description and a link to the Reload player
        // then add the file to the zip, then destroy the file (this is done automatically).
        // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138

        //error_log(print_r($zip_files,true), 0);
        foreach ($zip_files as $file_path) {
            if (empty($file_path)) { continue; }
            //error_log(__LINE__.'getting document from '.$sys_course_path.$_course['path'].'/'.$file_path.' removing '.$sys_course_path.$_course['path'].'/',0);
            $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
            $this->create_path($dest_file);
            //error_log('copy '.api_get_path(SYS_COURSE_PATH).$_course['path'].'/'.$file_path.' to '.api_get_path(SYS_ARCHIVE_PATH).$temp_dir_short.'/'.$file_path,0);
            //echo $main_path.$file_path.'<br />';
            @copy($sys_course_path.$_course['path'].'/'.$file_path, $dest_file);
            // Check if the file needs a link update.
            if (in_array($file_path, array_keys($link_updates))) {
                $string = file_get_contents($dest_file);
                unlink($dest_file);
                foreach ($link_updates[$file_path] as $old_new) {
                    //error_log('Replacing '.$old_new['orig'].' by '.$old_new['dest'].' in '.$file_path, 0);
                    // This is an ugly hack that allows .flv files to be found by the flv player that
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
                    } elseif (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 6) == 'video/') {
                        $old_new['dest'] = str_replace('video/', '../../../../video/', $old_new['dest']);
                    }
                    //Fix to avoid problems with default_course_document
                    if (strpos("main/default_course_document", $old_new['dest'] === false)) {
                        $new_dest = str_replace('document/', $mult.'document/', $old_new['dest']);
                    } else {
                        //$new_dest = str_replace('main/default_course_document', $mult.'document/main/default_course_document', $old_new['dest']);
                        $new_dest = $old_new['dest'];
                    }

                    //$string = str_replace($old_new['orig'], $old_new['dest'], $string);
                    $string = str_replace($old_new['orig'], $new_dest, $string);

                    //Add files inside the HTMLs
                    $new_path = str_replace('/courses/', '', $old_new['orig']);
                    //var_dump($sys_course_path.$new_path); var_dump($archive_path.$temp_dir_short.'/'.$old_new['dest']); echo '---';
                    if (file_exists($sys_course_path.$new_path)) {
                        copy($sys_course_path.$new_path, $archive_path.$temp_dir_short.'/'.$old_new['dest']);
                    }
                }
                file_put_contents($dest_file, $string);
            }
        }
        foreach ($zip_files_abs as $file_path) {
            if (empty($file_path)) { continue; }
            //error_log(__LINE__.'checking existence of '.$main_path.$file_path.'', 0);
            if (!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)) { continue; }
            //error_log(__LINE__.'getting document from '.$main_path.$file_path.' removing '.api_get_path(SYS_COURSE_PATH).$_course['path'].'/', 0);
            $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
            $this->create_path($dest_file);
            //error_log('Created path '.api_get_path(SYS_ARCHIVE_PATH).$temp_dir_short.'/document/'.$file_path, 0);
            //error_log('copy '.api_get_path(SYS_COURSE_PATH).$_course['path'].'/'.$file_path.' to '.api_get_path(SYS_ARCHIVE_PATH).$temp_dir_short.'/'.$file_path, 0);
            //echo $main_path.$file_path.' - '.$dest_file.'<br />';

            copy($main_path.$file_path, $dest_file);
            // Check if the file needs a link update.
            if (in_array($file_path, array_keys($link_updates))) {
                $string = file_get_contents($dest_file);
                unlink($dest_file);
                foreach ($link_updates[$file_path] as $old_new) {
                    //error_log('Replacing '.$old_new['orig'].' by '.$old_new['dest'].' in '.$file_path, 0);
                    // This is an ugly hack that allows .flv files to be found by the flv player that
                    // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
                    // to find the flv to play in document/main/, so we replace main/ in the flv path by
                    // ../../.. to return from inc/lib/flv_player to the document/main path.
                    if (substr($old_new['dest'], -3) == 'flv' && substr($old_new['dest'], 0, 5) == 'main/') {
                        $old_new['dest'] = str_replace('main/', '../../../', $old_new['dest']);
                    }
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
                }
                file_put_contents($dest_file, $string);
            }
        }


        if (is_array($links_to_create)) {
            foreach ($links_to_create as $file => $link) {
               $file_content = '<!DOCTYPE html>
    <head>
                   <meta charset="'.api_get_language_isocode().'" />
        <title>'.$link['title'].'</title>
    </head>
    <body dir="'.api_get_text_direction().'">
                        <div style="text-align:center">
                        <a href="'.$link['url'].'">'.$link['title'].'</a></div>
    </body>
</html>';
                file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
            }
        }

        // Add non exportable message explanation.
        $lang_not_exportable = get_lang('ThisItemIsNotExportable');
        $file_content = '<!DOCTYPE html>
    <head>
            <meta charset="'.api_get_language_isocode().'" />
        <title>'.$lang_not_exportable.'</title>
        <meta http-equiv="Content-Type" content="text/html; charset='.api_get_system_encoding().'" />
    </head>
        <body dir="'.api_get_text_direction().'">';
        $file_content .=
<<<EOD
        <style>
            .error-message {
                font-family: arial, verdana, helvetica, sans-serif;
                border-width: 1px;
                border-style: solid;
                left: 50%;
                margin: 10px auto;
                min-height: 30px;
                padding: 5px;
                right: 50%;
                width: 500px;
                background-color: #FFD1D1;
                border-color: #FF0000;
                color: #000;
            }
        </style>
    <body>
        <div class="error-message">
            $lang_not_exportable
        </div>
    </body>
</html>
EOD;
        if (!is_dir($archive_path.$temp_dir_short.'/document')) {
            @mkdir($archive_path.$temp_dir_short.'/document', api_get_permissions_for_new_directories());
        }
        file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);

        // Add the extra files that go along with a SCORM package.
        $main_code_path = api_get_path(SYS_CODE_PATH).'newscorm/packaging/';
        $extra_files = scandir($main_code_path);
        foreach ($extra_files as $extra_file) {
            if (strpos($extra_file, '.') === 0)
                continue;
            else {
                $dest_file = $archive_path . $temp_dir_short . '/' . $extra_file;
                $this->create_path($dest_file);
                copy($main_code_path.$extra_file, $dest_file);
            }
        }

        // Finalize the imsmanifest structure, add to the zip, then return the zip.

        $manifest = @$xmldoc->saveXML();
        $manifest = api_utf8_decode_xml($manifest); // The manifest gets the system encoding now.
        file_put_contents($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml', $manifest);
        $zip_folder->add($archive_path.'/'.$temp_dir_short, PCLZIP_OPT_REMOVE_PATH, $archive_path.'/'.$temp_dir_short.'/');

        // Clean possible temporary files.
        foreach ($files_cleanup as $file) {
            $res = unlink($file);
            if ($res === false) { error_log('Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__, 0); }
        }
        // Send file to client.
        //$name = 'scorm_export_'.$this->lp_id.'.zip';
        require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';

        $name = replace_dangerous_char($this->get_name()).'.zip';

        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
    }

    public function scorm_export_to_pdf($lp_id) {
        $lp_id = intval($lp_id);
        $files_to_export = array();
        $course_data = api_get_course_info($this->cc);
        if (!empty($course_data)) {
            $scorm_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/scorm/'.$this->path;

            $list = self::get_flat_ordered_items_list($lp_id);
            if (!empty($list)) {
                foreach ($list as $item_id) {
                    $item = $this->items[$item_id];
                    switch ($item->type) {
                        case 'document':
                            //Getting documents from a LP with chamilo documents
                            $file_data = DocumentManager::get_document_data_by_id($item->path, $this->cc);
                            $file_path = api_get_path(SYS_COURSE_PATH).$course_data['path'].'/document'.$file_data['path'];
                            if (file_exists($file_path)) {
                                $files_to_export[] = array('title'=>$item->get_title(),'path'=>$file_path);
                            }
                            break;
                        case 'asset': //commes from a scorm package generated by chamilo
                        case 'sco':
                            $file_path = $scorm_path.'/'.$item->path;
                            if (file_exists($file_path)) {
                                $files_to_export[] = array('title'=>$item->get_title(), 'path' => $file_path);
                            }
                            break;
                        case 'dokeos_chapter':
                        case 'dir':
                        case 'chapter':
                            $files_to_export[] = array('title'=> $item->get_title(), 'path'=>null);
                            break;
                    }
                }
            }
            $pdf = new PDF();
            $result = $pdf->html_to_pdf($files_to_export, $this->name, $this->cc, true);
            return $result;
        }
        return false;
    }

    /**
     * Temp function to be moved in main_api or the best place around for this. Creates a file path
     * if it doesn't exist
     */
    public function create_path($path) {
        $path_bits = split('/', dirname($path));

        // IS_WINDOWS_OS has been defined in main_api.lib.php
        $path_built = IS_WINDOWS_OS ? '' : '/';

        foreach ($path_bits as $bit) {
            if (!empty ($bit)) {
                $new_path = $path_built . $bit;
                if (is_dir($new_path)) {
                    $path_built = $new_path . '/';
                } else {
                    mkdir($new_path, api_get_permissions_for_new_directories());
                    $path_built = $new_path . '/';
                }
            }
        }
    }

    /**
     * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
     * @return	boolean	The results of the unlink function, or false if there was no image to start with
     */
    public function delete_lp_image() {
        $img = $this->get_preview_image();
        if ($img != '') {
            $del_file = $this->get_preview_image_path(null, 'sys');
            if (isset($del_file) && file_exists($del_file)) {
                $del_file_2 = $this->get_preview_image_path(64, 'sys');
                if (file_exists($del_file_2)) {
                    unlink($del_file_2);
                }
                $this->set_preview_image('');
                return @unlink($del_file);
            }
        }
        return false;
    }

    /**
     * Uploads an author image to the upload/learning_path/images directory
     * @param	array	The image array, coming from the $_FILES superglobal
     * @return	boolean	True on success, false on error
     */
    public function upload_image($image_array) {
        $image_moved = false;
        if (!empty ($image_array['name'])) {
            $upload_ok = process_uploaded_file($image_array);
            $has_attachment = true;
        } else {
            $image_moved = true;
        }

        if ($upload_ok) {
            if ($has_attachment) {
                $courseDir = api_get_course_path() . '/upload/learning_path/images';
                $sys_course_path = api_get_path(SYS_COURSE_PATH);
                $updir = $sys_course_path . $courseDir;
                // Try to add an extension to the file if it hasn't one.
                $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);

                if (!filter_extension($new_file_name)) {
                    //Display :: display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
                    $image_moved = false;
                } else {
                    $file_extension = explode('.', $image_array['name']);
                    $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
                    $filename = uniqid('');
                    $new_file_name = $filename.'.'.$file_extension;
                    $new_path = $updir.'/'.$new_file_name;

                    // Resize the image.
                    $temp = new Image($image_array['tmp_name']);
                    $picture_infos = $temp->get_image_info();
                    if ($picture_infos['width'] > 104) {
                        $thumbwidth = 104;
                    } else {
                        $thumbwidth = $picture_infos['width'];
                    }
                    if ($picture_infos['height'] > 96) {
                        $new_height = 96;
                    } else {
                        $new_height = $picture_infos['height'];
                    }

                    $temp->resize($thumbwidth, $new_height, 0);
                    $result = $temp->send_image($new_path);

                    // Storing the image filename.
                    if ($result) {
                        $image_moved = true;
                        $this->set_preview_image($new_file_name);

                        //Resize to 32
                        $temp->resize(64, 64, 0);
                        $temp->send_image($updir.'/'.$filename.'.64.'.$file_extension);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public function set_autolunch($lp_id, $status) {
        $course_id = api_get_course_int_id();
        $lp_id   = intval($lp_id);
        $status  = intval($status);
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);

        //Setting everything to autolunch = 0
        $attributes['autolunch'] = 0;
        $where = array('session_id = ? AND c_id = ? '=> array(api_get_session_id(), $course_id));
        Database::update($lp_table, $attributes, $where);
        if ($status == 1) {
            //Setting my lp_id to autolunch = 1
            $attributes['autolunch'] = 1;
            $where = array('id = ? AND session_id = ? AND c_id = ?'=> array($lp_id, api_get_session_id(), $course_id));
            Database::update($lp_table, $attributes, $where );
        }
    }


    /**
    * Gets previous_item_id for the next element of the lp_item table
    * @author Isaac flores paz
    * @return	integer	Previous item ID
    */
    function select_previous_item_id() {
        $course_id = api_get_course_int_id();
    	if ($this->debug > 0) {
    		error_log('New LP - In learnpath::select_previous_item_id()', 0);
    	}
    	$table_lp_item = Database::get_course_table(TABLE_LP_ITEM);

    	// Get the max order of the items
    	$sql_max_order = "SELECT max(display_order) AS display_order FROM $table_lp_item  WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "'";
    	$rs_max_order = Database::query($sql_max_order);
    	$row_max_order = Database::fetch_object($rs_max_order);
    	$max_order = $row_max_order->display_order;
    	// Get the previous item ID
    	$sql_max = "SELECT id as previous FROM $table_lp_item WHERE c_id = $course_id AND lp_id = '" . $this->lp_id . "' AND display_order = '".$max_order."' ";
    	$rs_max = Database::query($sql_max, __FILE__, __LINE__);
    	$row_max = Database::fetch_object($rs_max);

    	// Return the previous item ID
    	return $row_max->previous;
    }

    function copy() {
        $main_path = api_get_path(SYS_CODE_PATH);
        require_once $main_path.'coursecopy/classes/CourseBuilder.class.php';
        require_once $main_path.'coursecopy/classes/CourseArchiver.class.php';
        require_once $main_path.'coursecopy/classes/CourseRestorer.class.php';
        require_once $main_path.'coursecopy/classes/CourseSelectForm.class.php';

        //Course builder
        $cb = new CourseBuilder();

        //Setting tools that will be copied
        $cb->set_tools_to_build(array('learnpaths'));

        //Setting elements that will be copied
        $cb->set_tools_specific_id_list(array('learnpaths' => array($this->lp_id)));

        $course = $cb->build();

        //Course restorer
        $course_restorer = new CourseRestorer($course);
        $course_restorer->set_add_text_in_items(true);
        $course_restorer->set_tool_copy_settings(array('learnpaths' => array('reset_dates' => true)));
        $course_restorer->restore(api_get_course_id(), api_get_session_id(), false, false);
    }

    function verify_document_size($s) {
        $post_max = ini_get('post_max_size');
        $upl_max = ini_get('upload_max_filesize');
        $documents_total_space = DocumentManager::documents_total_space();
        $course_max_space = DocumentManager::get_course_quota();
        $total_size = filesize($s) + $documents_total_space;
        if (filesize($s)>$post_max || filesize($s)>$upl_max  || $total_size>$course_max_space ){
            return true;
        } else{
            return false;
        }
    }

    function clear_prerequisites() {
        $course_id = $this->get_course_int_id();
        if ($this->debug > 0) {
            error_log('New LP - In learnpath::clear_prerequisites()', 0);
        }
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $lp_id = $this->get_id();
        //Cleaning prerequisites
        $sql = "UPDATE $tbl_lp_item SET prerequisite = ''
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id'";
        Database::query($sql);

        //Cleaning mastery score for exercises
        $sql = "UPDATE $tbl_lp_item SET mastery_score = ''
                WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND item_type = 'quiz'";
        Database::query($sql);
    }

    function set_previous_step_as_prerequisite_for_all_items() {
        $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
        $course_id = $this->get_course_int_id();
        $lp_id = $this->get_id();

        if (!empty($this->items)) {
            $old_id = null;
            $old_max = 0;
            $old_type = null;
            foreach ($this->items as $item) {
                if (!empty($old_id)) {
                    $current_item_id = $item->get_id();
                    if ($old_type == 'quiz') {
                        $sql = "UPDATE $tbl_lp_item SET mastery_score = '$old_max' WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$old_id'";
                        Database::query($sql);
                    }
                    $sql = "UPDATE $tbl_lp_item SET prerequisite = '$old_id' WHERE c_id = ".$course_id." AND lp_id = '$lp_id' AND id = '$current_item_id'";
                    Database::query($sql);
                }
                $old_id = $item->get_id();
                $old_max = $item->get_max();
                $old_type = $item->get_type();
            }
        }
    }
}

if (!function_exists('trim_value')) {
    function trim_value(& $value) {
        $value = trim($value);
    }
}
learnpathItem.class.php000064400000333064152003363470011172 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This file contains the lp_item class, that inherits from the learnpath class
 * @package	chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * lp_item defines items belonging to a learnpath. Each item has a name, a score, a use time and additional
 * information that enables tracking a user's progress in a learning path
 */
class learnpathItem {
	public $attempt_id; // Also called "objectives" SCORM-wise.
	public $audio; // The path to an audio file (stored in document/audio/).
	public $children = array(); // Contains the ids of children items.
	public $condition; // If this item has a special condition embedded.
	public $current_score;
	public $current_start_time;
	public $current_stop_time;
	public $current_data = '';
	public $db_id;
	public $db_item_view_id = '';
	public $description = '';
	public $file;
	// At the moment, interactions are just an array of arrays with a structure of 8 text fields
	// id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
	public $interactions = array();
	public $interactions_count = 0;
	public $objectives = array();
	public $objectives_count = 0;
	public $launch_data = '';
	public $lesson_location = '';
	public $level = 0;
    public $core_exit = '';
	//var $location; // Only set this for SCORM?
	public $lp_id;
	public $max_score;
	public $mastery_score;
	public $min_score;
	public $max_time_allowed = '';
	public $name;
	public $next;
	public $parent;
	public $path; // In some cases the exo_id = exercise_id in courseDb exercices table.
	public $possible_status = array('not attempted','incomplete','completed','passed','failed','browsed');
	public $prereq_string = '';
	public $prereq_alert = '';
	public $prereqs = array();
	public $previous;
	public $prevent_reinit = 1; // 0 =  multiple attempts   1 = one attempt
	public $seriousgame_mode;
	public $ref;
	public $save_on_close = true;
	public $search_did = null;
	public $status;
	public $title;
	public $type; // This attribute can contain chapter|link|student_publication|module|quiz|document|forum|thread
	public $view_id;
    //var used if absolute session time mode is used
    private $last_scorm_session_time = 0;

	const debug = 0; // Logging parameter.

	/**
	 * Class constructor. Prepares the learnpath_item for later launch
	 *
	 * Don't forget to use set_lp_view() if applicable after creating the item.
	 * Setting an lp_view will finalise the item_view data collection
	 * @param	integer	Learnpath item ID
	 * @param	integer	User ID
     * @param	integer	course int id
	 * @return	boolean	True on success, false on failure
	 */
	public function __construct($id, $user_id = null, $course_id = null, $item_content = null) {
		// Get items table.
		if (!isset($user_id)) { $user_id = api_get_user_id(); }
		if (self::debug > 0) { error_log("learnpathItem constructor: id: $id user_id: $user_id course_id: $course_id item_content: $item_content", 0); }
		$id = intval($id);

        if (empty($item_content)) {
            $items_table = Database::get_course_table(TABLE_LP_ITEM);
            if (empty($course_id)) {
                $course_id = api_get_course_int_id();
            } else {
                $course_id = intval($course_id);
            }
            $sql = "SELECT * FROM $items_table WHERE c_id = $course_id AND id = $id";
            //error_log('New LP - Creating item object from DB: '.$sql, 0);
            $res = Database::query($sql);
            if (Database::num_rows($res) < 1) {
                $this->error = 'Could not find given learnpath item in learnpath_item table';
                return false;
            }
            $row = Database::fetch_array($res);
        } else {
            $row = $item_content;
        }

		$this->lp_id	 = $row['lp_id'];
		$this->max_score = $row['max_score'];
		$this->min_score = $row['min_score'];
		$this->name 	 = $row['title'];
		$this->type 	 = $row['item_type'];
		$this->ref		 = $row['ref'];
		$this->title	 = $row['title'];
		$this->description = $row['description'];
		$this->path		 = $row['path'];
		$this->mastery_score = $row['mastery_score'];
		$this->parent	 = $row['parent_item_id'];
		$this->next		 = $row['next_item_id'];
		$this->previous	 = $row['previous_item_id'];
		$this->display_order = $row['display_order'];
		$this->prereq_string = $row['prerequisite'];
		$this->max_time_allowed = $row['max_time_allowed'];
		if (isset($row['launch_data'])){
			$this->launch_data = $row['launch_data'];
		}
		$this->save_on_close = true;
		$this->db_id = $id;
        //$this->seriousgame_mode = $this->get_seriousgame_mode();
        $this->seriousgame_mode = 0;

		// Get search_did.
		if (api_get_setting('search_enabled') == 'true') {
			$tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
			$sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
			// TODO: Verify if it's possible to assume the actual course instead of getting it from db.
			$sql = sprintf($sql, $tbl_se_ref, api_get_course_id(), TOOL_LEARNPATH, $this->lp_id, $id);
            if (self::debug > 0) { error_log($sql); };
			$res = Database::query($sql);
			if (Database::num_rows($res) > 0) {
				$se_ref = Database::fetch_array($res);
				$this->search_did = (int)$se_ref['search_did'];
			}
		}
		$this->audio = $row['audio'];
		if (self::debug > 0) error_log('New LP - End of learnpathItem constructor for item '.$id, 0);
		return true;
	}

	/**
	 * Adds a child to the current item
	 */
	public function add_child($item) {
		if (self::debug > 0) { error_log('learnpathItem::add_child()', 0); }
		if (!empty($item)) {
			// Do not check in DB as we expect the call to come from the learnpath class which should
			// be aware of any fake.
			$this->children[] = $item;
		}
	}

	/**
	 * Adds an interaction to the current item
	 * @param	int		Index (order ID) of the interaction inside this item
	 * @param	array	Array of parameters: id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
	 * @result	void
	 */
	public function add_interaction($index, $params) {
		$this->interactions[$index] = $params;
		// Take the current maximum index to generate the interactions_count.
		if(($index+1)>$this->interactions_count){
			$this->interactions_count = $index + 1;
		}
		/*
		if (is_array($this->interactions[$index]) && count($this->interactions[$index]) > 0) {
			$this->interactions[$index] = $params;
			return false;
		} else {
			if (count($params)==8) { // We rely on the calling script to provide parameters in the right order.
				$this->interactions[$index] = $params;
				return true;
			} else {
				return false;
			}
		}
		*/
	}

	/**
	 * Adds an objective to the current item
	 * @param	array	Array of parameters: id(0), status(1), score_raw(2), score_max(3), score_min(4)
	 * @result	void
	 */
	public function add_objective($index, $params) {
		if(empty($params[0])){return null;}
		$this->objectives[$index] = $params;
		// Take the current maximum index to generate the objectives_count.
		if ((count($this->objectives) + 1) > $this->objectives_count) {
			$this->objectives_count = (count($this->objectives) + 1);
		}
	}

	/**
	 * Closes/stops the item viewing. Finalises runtime values. If required, save to DB.
	 * @return	boolean	True on success, false otherwise
	 */
	public function close() {
		if (self::debug > 0) { error_log('learnpathItem::close()', 0); }
	   	$this->current_stop_time = time();
	   	$type = $this->get_type();
		if ($type != 'sco') {
			if ($type == TOOL_QUIZ or $type == TOOL_HOTPOTATOES) {
				$this->get_status(true,true); // Update status (second option forces the update).
			} else {
				$this->status = $this->possible_status[2];
			}
		}
		if ($this->save_on_close) {
			$this->save();
		}
		return true;
	}

	/**
	 * Deletes all traces of this item in the database
	 * @return	boolean	true. Doesn't check for errors yet.
	 */
	public function delete() {
		if (self::debug > 0) { error_log('learnpath_item::delete() for item '.$this->db_id, 0); }
		$lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		$lp_item = Database::get_course_table(TABLE_LP_ITEM);

        $course_id = api_get_course_int_id();

		$sql_del_view = "DELETE FROM $lp_item_view WHERE c_id = $course_id AND lp_item_id = ".$this->db_id;
		if (self::debug > 0) { error_log('Deleting from lp_item_view: '.$sql_del_view, 0); }
		Database::query($sql_del_view);

		$sql_sel = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id = ".$this->db_id;
		$res_sel = Database::query($sql_sel);
		if (Database::num_rows($res_sel) < 1) {
            return false;
        }

		$sql_del_item = "DELETE FROM $lp_item WHERE c_id = $course_id AND id = ".$this->db_id;
		Database::query($sql_del_item);
        if (self::debug > 0) { error_log('Deleting from lp_item: '.$sql_del_view); }

		if (api_get_setting('search_enabled') == 'true') {
			if (!is_null($this->search_did)) {
				require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
				$di = new ChamiloIndexer();
				$di->remove_document($this->search_did);
			}
		}
		return true;
	}

	/**
	 * Drops a child from the children array
	 * @param	string index of child item to drop
	 * @return 	void
	 */
	public function drop_child($item) {
		if (self::debug > 0) { error_log('learnpathItem::drop_child()', 0); }
		if (!empty($item)) {
			foreach ($this->children as $index => $child) {
				if ($child == $item) {
					$this->children[$index] = null;
				}
			}
		}
	}

	/**
	 * Gets the current attempt_id for this user on this item
	 * @return	integer	The attempt_id for this item view by this user, or 1 if none defined
	 */
	public function get_attempt_id() {
		if (self::debug > 0) { error_log('learnpathItem::get_attempt_id() on item '.$this->db_id, 0); }
		$res = 1;
		if (!empty($this->attempt_id)) {
			$res = $this->attempt_id;
		}
		if (self::debug > 0) { error_log('New LP - End of learnpathItem::get_attempt_id() on item '.$this->db_id.' - Returning '.$res, 0); }
		return $res;
	}

	/**
	 * Gets a list of the item's children
	 * @return	array	Array of children items IDs
	 */
	public function get_children() {
		if (self::debug > 0) { error_log('learnpathItem::get_children()', 0); }
		$list = array();
		foreach ($this->children as $child) {
			if (!empty($child)) {
				//error_log('New LP - Found '.$child, 0);
				$list[] = $child;
			}
		}
		return $list;
	}

	/**
	 * Gets the core_exit value from the database
	 */
	public function get_core_exit() {
		return $this->core_exit;
	}

	/**
	 * Gets the credit information (rather scorm-stuff) based on current status and reinit
	 * autorization. Credit tells the sco(content) if Chamilo will record the data it is sent (credit) or not (no-credit)
	 * @return	string	'credit' or 'no-credit'. Defaults to 'credit' because if we don't know enough about this item, it's probably because it was never used before.
	 */
	public function get_credit() {
		if (self::debug > 1) {error_log('learnpathItem::get_credit()', 0); }
		$credit = 'credit';
		// Now check the value of prevent_reinit (if it's 0, return credit as the default was).
		if ($this->get_prevent_reinit() != 0) { // If prevent_reinit == 1 (or more).
			// If status is not attempted or incomplete, credit anyway. Otherwise:
			// Check the status in the database rather than in the object, as checking in the object
			// would always return "no-credit" when we want to set it to completed.
			$status = $this->get_status(true);
			if (self::debug > 2) { error_log('learnpathItem::get_credit() - get_prevent_reinit!=0 and status is '.$status, 0); }
            //0=not attempted - 1 = incomplete
			if ($status != $this->possible_status[0] && $status != $this->possible_status[1]) {
				$credit = 'no-credit';
			}
		}
        if (self::debug > 1) {error_log("learnpathItem::get_credit() returns: $credit"); }
		return $credit;
	}

	/**
	 * Gets the current start time property
	 * @return	integer	Current start time, or current time if none
	 */
	public function get_current_start_time() {
		if (self::debug > 0) { error_log('learnpathItem::get_current_start_time()', 0); }
		if (empty($this->current_start_time)) {
			return time();
		} else {
			return $this->current_start_time;
		}
	}

	/**
	 * Gets the item's description
	 * @return	string	Description
	 */
	public function get_description() {
		if (self::debug > 0) { error_log('learnpathItem::get_description()', 0); }
		if (empty($this->description)) { return ''; }
		return $this->description;
	}

	/**
	 * Gets the file path from the course's root directory, no matter what tool it is from.
	 * @return	string	The file path, or an empty string if there is no file attached, or '-1' if the file must be replaced by an error page
	 */
	public function get_file_path($path_to_scorm_dir = '') {
	    $course_id = api_get_course_int_id();
		if (self::debug > 0) { error_log('learnpathItem::get_file_path()', 0); }
		$path = $this->get_path();
   		$type = $this->get_type();
		if (empty($path)) {
			if ($type == 'dokeos_chapter' || $type == 'chapter' || $type == 'dir') {
				return '';
			} else {
				return '-1';
			}
		} elseif ($path == strval(intval($path))) {
			// The path is numeric, so it is a reference to a Chamilo object.
			switch ($type) {
				case 'dokeos_chapter':
				case 'dir':
				case 'chapter':
					return '';
				case TOOL_DOCUMENT:
					$table_doc = Database::get_course_table(TABLE_DOCUMENT);
					$sql = 'SELECT path FROM '.$table_doc.' WHERE c_id = '.$course_id.' AND id = '.$path;
					$res = Database::query($sql);
					$row = Database::fetch_array($res);
					$real_path = 'document'.$row['path'];
					return $real_path;
				case TOOL_STUDENTPUBLICATION:
				case TOOL_QUIZ:
				case TOOL_FORUM:
				case TOOL_THREAD:
				case TOOL_LINK:
				default:
					return '-1';
			}
		} else {
			if (!empty($path_to_scorm_dir)) {
				$path = $path_to_scorm_dir.$path;
			}
			return $path;
		}
	}

	/**
	 * Gets the DB ID
	 * @return	integer	Database ID for the current item
	 */
	public function get_id() {
		if (self::debug > 1) {error_log('learnpathItem::get_id()', 0); }
		if (!empty($this->db_id)) {
			return $this->db_id;
		}
		// TODO: Check this return value is valid for children classes (SCORM?).
		return 0;
	}

	/**
	 * Loads the interactions into the item object, from the database.
	 * If object interactions exist, they will be overwritten by this function,
	 * using the database elements only.
	 * @return void Directly sets the interactions attribute in memory
	 */
	public function load_interactions() {
		$this->interactions = array();
        $course_id = api_get_course_int_id();
		$tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		$sql = "SELECT id FROM $tbl " .
					"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
					"AND   lp_view_id = ".$this->view_id." " .
					"AND   view_count = ".$this->attempt_id;
		$res = Database::query($sql);
		if (Database::num_rows($res) > 0) {
			$row = Database::fetch_array($res);
			$lp_iv_id = $row[0];
			$iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
			$iva_sql = "SELECT * FROM $iva_table " .
					   "WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id ";
			$res_sql = Database::query($iva_sql);
			while ($row = Database::fetch_array($res_sql)) {
				$this->interactions[$row['interaction_id']] = array($row['interaction_id'], $row['interaction_type'], $row['weighting'], $row['completion_time'], $row['correct_responses'], $row['student_responses'], $row['result'], $row['latency']);
			}
		}
	}

	/**
	 * Gets the current count of interactions recorded in the database
	 * @param   bool    Whether to count from database or not (defaults to no)
	 * @return	int	The current number of interactions recorder
	 */
	public function get_interactions_count($checkdb = false) {
		if (self::debug > 1) { error_log('learnpathItem::get_interactions_count()', 0); }
		$return = 0;
        $course_id = api_get_course_int_id();

		if ($checkdb) {
			$tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
			$sql = "SELECT id FROM $tbl " .
					"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
					"AND   lp_view_id = ".$this->view_id." " .
					"AND   view_count = ".$this->get_attempt_id();
			$res = Database::query($sql);
			if (Database::num_rows($res) > 0) {
				$row = Database::fetch_array($res);
				$lp_iv_id = $row[0];
				$iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
				$iva_sql = "SELECT count(id) as mycount FROM $iva_table " .
							"WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id ";
				$res_sql = Database::query($iva_sql);
				if (Database::num_rows($res_sql) > 0) {
					$row = Database::fetch_array($res_sql);
					$return = $row['mycount'];
				}
			}
		} else {
			if(!empty($this->interactions_count)){
				$return = $this->interactions_count;
			}
		}
		return $return;
	}

	/**
	 * Gets the JavaScript array content to fill the interactions array.
	 * @params  bool    Whether to check directly into the database (default no)
	 * @return  string  An empty string if no interaction, a JS array definition otherwise
	 */
	public function get_interactions_js_array($checkdb = false) {
		$return = '';
		if ($checkdb) {
			$this->load_interactions(true);
		}
		foreach ($this->interactions as $id => $in) {
			$return .= "['$id','".$in[1]."','".$in[2]."','".$in[3]."','".$in[4]."','".$in[5]."','".$in[6]."','".$in[7]."'],";
		}
		if (!empty($return)) {
			$return = substr($return, 0, -1);
		}
		return $return;
	}

	/**
	 * Gets the current count of objectives recorded in the database
	 * @return	int	The current number of objectives recorder
	 */
	public function get_objectives_count() {
		if (self::debug > 1) { error_log('learnpathItem::get_objectives_count()', 0);}
		$res = 0;
		if (!empty($this->objectives_count)) {
			$res = $this->objectives_count;
		}
		return $res;
	}

	/**
	 * Gets the launch_data field found in imsmanifests (this is SCORM- or AICC-related, really)
	 * @return	string	Launch data as found in imsmanifest and stored in Chamilo (read only). Defaults to ''.
	 */
	public function get_launch_data() {
		if (self::debug > 0) { error_log('learnpathItem::get_launch_data()', 0); }
		if (!empty($this->launch_data)) {
			return $this->launch_data;
		}
		return '';
	}

	/**
	 * Gets the lesson location
	 * @return string	lesson location as recorded by the SCORM and AICC elements. Defaults to ''
	 */
	public function get_lesson_location() {
		if (self::debug > 0) { error_log('learnpathItem::get_lesson_location()', 0); }
		if (!empty($this->lesson_location)) { return $this->lesson_location; } else { return ''; }
	}

	/**
	 * Gets the lesson_mode (scorm feature, but might be used by aicc as well as dokeos paths)
	 *
	 * The "browse" mode is not supported yet (because there is no such way of seeing a sco in Chamilo)
	 * @return	string	'browse','normal' or 'review'. Defaults to 'normal'
	 */
	public function get_lesson_mode() {
		$mode = 'normal';
		if ($this->get_prevent_reinit() != 0) { // If prevent_reinit == 0
			$my_status = $this->get_status();
			if ($my_status != $this->possible_status[0] && $my_status != $this->possible_status[1]) {
				$mode = 'review';
			}
		}
		return $mode;
	}

	/**
	 * Gets the depth level
	 * @return int	Level. Defaults to 0
	 */
	public function get_level() {
		if (self::debug > 0) { error_log('learnpathItem::get_level()', 0); }
		if (empty($this->level)) { return 0; }
		return $this->level;
	}

	/**
	 * Gets the mastery score
	 */
	public function get_mastery_score() {
		if (self::debug > 0) { error_log('learnpathItem::get_mastery_score()', 0); }
		if (isset($this->mastery_score)) { return $this->mastery_score; } else { return -1; }
	}

	/**
	 * Gets the maximum (score)
	 * @return	int	Maximum score. Defaults to 100 if nothing else is defined
	 */
	public function get_max(){
		if (self::debug > 0) { error_log('learnpathItem::get_max()', 0); }
		if ($this->type == 'sco') {
			if (!empty($this->view_max_score) && $this->view_max_score > 0) {
				return $this->view_max_score;
			} elseif ($this->view_max_score === '') {
				return $this->view_max_score;
			} else {
				if (!empty($this->max_score)) { return $this->max_score; } else { return 100; }
			}
		} else {
			if (!empty($this->max_score)) { return $this->max_score; } else { return 100; }
		}
	}

	/**
	 * Gets the maximum time allowed for this user in this attempt on this item
	 * @return	string	Time string in SCORM format (HH:MM:SS or HH:MM:SS.SS or HHHH:MM:SS.SS)
	 */
	public function get_max_time_allowed() {
		if (self::debug > 0) {error_log('learnpathItem::get_max_time_allowed()', 0); }
		if (!empty($this->max_time_allowed)) { return $this->max_time_allowed; } else { return ''; }
	}

	/**
	 * Gets the minimum (score)
	 * @return int	Minimum score. Defaults to 0
	 */
	public function get_min() {
		if (self::debug > 0) { error_log('learnpathItem::get_min()', 0); }
		if (!empty($this->min_score)) { return $this->min_score; } else { return 0; }
	}

	/**
	 * Gets the parent ID
	 * @return	int	Parent ID. Defaults to null
	 */
	public function get_parent(){
		if (self::debug > 0) { error_log('learnpathItem::get_parent()', 0); }
		if (!empty($this->parent)) {
			return $this->parent;
		}
		// TODO: Check this return value is valid for children classes (SCORM?).
		return null;
	}

	/**
	 * Gets the path attribute.
	 * @return	string	Path. Defaults to ''
	 */
	public function get_path(){
		if (self::debug > 0) { error_log('learnpathItem::get_path()', 0); }
		if (empty($this->path)) { return ''; }
		return $this->path;
	}

	/**
	 * Gets the prerequisites string
	 * @return	string	Empty string or prerequisites string if defined. Defaults to
	 */
	public function get_prereq_string() {
		if (self::debug > 0) { error_log('learnpathItem::get_prereq_string()', 0); }
		if (!empty($this->prereq_string)) {
			return $this->prereq_string;
		} else {
			return '';
		}
	}

	/**
	 * Gets the prevent_reinit attribute value (and sets it if not set already)
	 * @return	int	1 or 0 (defaults to 1)
	 */
	public function get_prevent_reinit() {
	    $course_id = api_get_course_int_id();
		if (self::debug > 2) { error_log('learnpathItem::get_prevent_reinit()', 0); }
		if (!isset($this->prevent_reinit)) {
			if (!empty($this->lp_id)) {
				$table = Database::get_course_table(TABLE_LP_MAIN);
			   	$sql = "SELECT prevent_reinit FROM $table WHERE c_id = $course_id AND id = ".$this->lp_id;
				$res = Database::query($sql);
				if (Database::num_rows($res) < 1) {
					$this->error = "Could not find parent learnpath in learnpath table";
					if (self::debug > 2) { error_log('New LP - End of learnpathItem::get_prevent_reinit() - Returning false', 0); }
					return false;
				} else {
					$row = Database::fetch_array($res);
					$this->prevent_reinit = $row['prevent_reinit'];
				}
			} else {
				$this->prevent_reinit = 1; // Prevent reinit is always 1 by default - see learnpath.class.php.
			}
		}
		if (self::debug > 2) { error_log('New LP - End of learnpathItem::get_prevent_reinit() - Returned '.$this->prevent_reinit, 0); }
		return $this->prevent_reinit;
	}

	/**
     * Returns 1 if seriousgame_mode is activated, 0 otherwise
     *
     * @return int (0 or 1)
     * @author ndiechburg <noel@cblue.be>
     **/
    public function get_seriousgame_mode() {
        if(self::debug>2){error_log('learnpathItem::get_seriousgame_mode()',0);}
        $course_id = api_get_course_int_id();
        if (!isset($this->seriousgame_mode)) {
            if (!empty($this->lp_id)) {
            $table = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "SELECT seriousgame_mode FROM $table WHERE c_id = $course_id AND id = ".$this->lp_id;
            $res = @Database::query($sql);
            if (Database::num_rows($res)<1) {
                $this->error = "Could not find parent learnpath in learnpath table";
                if(self::debug>2){error_log('New LP - End of learnpathItem::get_seriousgame_mode() - Returning false',0);}
                return false;
            } else {
                $row = Database::fetch_array($res);
                $this->seriousgame_mode = isset($row['seriousgame_mode'])? $row['seriousgame_mode'] : 0;
            }
            } else {
                $this->seriousgame_mode = 0; //SeriousGame mode is always off by default
            }
        }
        if(self::debug>2){error_log('New LP - End of learnpathItem::get_seriousgame_mode() - Returned '.$this->seriousgame_mode,0);}
        return $this->seriousgame_mode;
    }

    /**
	 * Gets the item's reference column
	 * @return string	The item's reference field (generally used for SCORM identifiers)
	 */
	public function get_ref() {
		return $this->ref;
	}

	/**
	 * Gets the list of included resources as a list of absolute or relative paths of
	 * resources included in the current item. This allows for a better SCORM export.
	 * The list will generally include pictures, flash objects, java applets, or any other
	 * stuff included in the source of the current item. The current item is expected
	 * to be an HTML file. If it is not, then the function will return and empty list.
	 * @param	string	type (one of the Chamilo tools) - optional (otherwise takes the current item's type)
	 * @param	string	path (absolute file path) - optional (otherwise takes the current item's path)
	 * @param	int		level of recursivity we're in
	 * @return	array	List of file paths. An additional field containing 'local' or 'remote' helps determine if the file should be copied into the zip or just linked
	 */
	public function get_resources_from_source($type = null, $abs_path = null, $recursivity = 1) {
		$max = 5;
		if ($recursivity > $max) {
			return array();
		}
		if (!isset($type)) {
			$type = $this->get_type();
		}
		if (!isset($abs_path)) {
			$path = $this->get_file_path();
   			$abs_path = api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.$path;
   			//echo "Abs path coming from item : ".$abs_path."<br />\n";
		}
		/*
		else {
			echo "Abs path coming from param: ".$abs_path."<br />\n";
		}
		*/
		//error_log(str_repeat(' ',$recursivity).'Analyse file '.$abs_path, 0);
		$files_list = array();
		$type = $this->get_type();

		switch ($type) {
			case TOOL_DOCUMENT :
			case TOOL_QUIZ:
			case 'sco':
				// Get the document and, if HTML, open it.

				if (is_file($abs_path)) {
					// for now, read the whole file in one go (that's gonna be a problem when the file is too big).
					$info = pathinfo($abs_path);

					$ext = $info['extension'];

					switch (strtolower($ext)) {
						case 'html':
						case 'htm':
						case 'shtml':
						case 'css':
					 		$wanted_attributes = array('src', 'url', '@import', 'href', 'value');
							// Parse it for included resources.
							$file_content = file_get_contents($abs_path);
							// Get an array of attributes from the HTML source.
							$attributes = DocumentManager::parse_HTML_attributes($file_content, $wanted_attributes);

							// Look at 'src' attributes in this file
							foreach ($wanted_attributes as $attr) {
								if (isset($attributes[$attr])) {
									// Find which kind of path these are (local or remote).
									$sources = $attributes[$attr];

									foreach ($sources as $source) {
										// Skip what is obviously not a resource.
										if (strpos($source, "+this.")) continue; // javascript code - will still work unaltered.
										if (strpos($source, '.') === false) continue; // No dot, should not be an external file anyway.
										if (strpos($source, 'mailto:')) continue; // mailto link.
										if (strpos($source, ';') && !strpos($source, '&amp;')) continue; // Avoid code - that should help.

										if ($attr == 'value') {
											if (strpos($source , 'mp3file')) {
												$files_list[] = array(substr($source, 0, strpos($source , '.swf') + 4), 'local', 'abs');
												$mp3file = substr($source , strpos($source , 'mp3file=') + 8);
												if (substr($mp3file, 0, 1) == '/')
													$files_list[] = array($mp3file, 'local', 'abs');
												else
													$files_list[] = array($mp3file, 'local', 'rel');
											} elseif (strpos($source, 'flv=') === 0) {

												$source = substr($source, 4);
												if (strpos($source, '&') > 0) {
													$source = substr($source, 0, strpos($source, '&'));
												}
												if (strpos($source, '://') > 0) {
													if (strpos($source, api_get_path(WEB_PATH)) !== false) {
														// We found the current portal url.
														$files_list[] = array($source, 'local', 'url');
													} else {
														// We didn't find any trace of current portal.
														$files_list[] = array($source, 'remote', 'url');
													}
												} else {
													$files_list[] = array($source, 'local', 'abs');
												}
												continue; // Skipping anything else to avoid two entries (while the others can have sub-files in their url, flv's can't).
											}
										}

										if (strpos($source, '://') > 0) {

											// Cut at '?' in a URL with params.
											if (strpos($source, '?') > 0) {
												$second_part = substr($source, strpos($source, '?'));
												if (strpos($second_part, '://') > 0) {
													// If the second part of the url contains a url too, treat the second one before cutting.

													$pos1 = strpos($second_part, '=');
													$pos2 = strpos($second_part, '&');
													$second_part = substr($second_part, $pos1 + 1, $pos2 - ($pos1 + 1));
													if (strpos($second_part, api_get_path(WEB_PATH)) !== false) {
														// We found the current portal url.
														$files_list[] = array($second_part, 'local', 'url');
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $second_part, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													} else {
														// We didn't find any trace of current portal.
														$files_list[] = array($second_part, 'remote', 'url');
													}
												} elseif(strpos($second_part, '=') > 0) {
													if (substr($second_part, 0, 1) === '/') {
														// Link starts with a /, making it absolute (relative to DocumentRoot).
														$files_list[] = array($second_part, 'local', 'abs');
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $second_part, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													} elseif(strstr($second_part, '..') === 0) {
														// Link is relative but going back in the hierarchy.
														$files_list[] = array($second_part, 'local', 'rel');
														$dir = dirname($abs_path);
														$new_abs_path = realpath($dir.'/'.$second_part);
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													} else {
														// No starting '/', making it relative to current document's path.
														if (substr($second_part, 0, 2) == './') {
															$second_part = substr($second_part, 2);
														}
														$files_list[] = array($second_part, 'local', 'rel');
														$dir = dirname($abs_path);
														$new_abs_path = realpath($dir.'/'.$second_part);
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													}
												}
												// Leave that second part behind now.
												$source = substr($source, 0, strpos($source, '?'));
												if (strpos($source,'://') > 0) {
													if (strpos($source, api_get_path(WEB_PATH)) !== false) {
														// We found the current portal url.
														$files_list[] = array($source, 'local', 'url');
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $source, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													} else {
														// We didn't find any trace of current portal.
														$files_list[] = array($source, 'remote', 'url');
													}
												} else {
													// No protocol found, make link local.
													if (substr($source, 0, 1) === '/') {
														// Link starts with a /, making it absolute (relative to DocumentRoot).
														$files_list[] = array($source, 'local', 'abs');
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $source, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													}
													elseif (strstr($source, '..') === 0) {
														// Link is relative but going back in the hierarchy.
														$files_list[] = array($source, 'local', 'rel');
														$dir = dirname($abs_path);
														$new_abs_path = realpath($dir.'/'.$source);
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													} else {
														// No starting '/', making it relative to current document's path.
														if (substr($source, 0, 2) == './') {
															$source = substr($source, 2);
														}
														$files_list[] = array($source, 'local', 'rel');
														$dir = dirname($abs_path);
														$new_abs_path = realpath($dir.'/'.$source);
														$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
														if (count($in_files_list) > 0) {
															$files_list = array_merge($files_list, $in_files_list);
														}
													}
												}
											}

											// Found some protocol there.
											if (strpos($source, api_get_path(WEB_PATH)) !== false) {
												// We found the current portal url.
												$files_list[] = array($source, 'local', 'url');
												$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $source, $recursivity + 1);
												if (count($in_files_list) > 0) {
													$files_list = array_merge($files_list, $in_files_list);
												}
											} else {
												// We didn't find any trace of current portal.
												$files_list[] = array($source, 'remote', 'url');
											}
										} else {
											// No protocol found, make link local.
											if (substr($source, 0, 1) === '/') {
												// Link starts with a /, making it absolute (relative to DocumentRoot).
												$files_list[] = array($source, 'local', 'abs');
												$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $source, $recursivity + 1);
												if (count($in_files_list) > 0) {
													$files_list = array_merge($files_list, $in_files_list);
												}
											} elseif (strstr($source, '..') === 0) {
												// Link is relative but going back in the hierarchy.
												$files_list[] = array($source, 'local', 'rel');
												$dir = dirname($abs_path);
												$new_abs_path = realpath($dir.'/'.$source);
												$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
												if (count($in_files_list) > 0) {
													$files_list = array_merge($files_list, $in_files_list);
												}
											} else {

												// No starting '/', making it relative to current document's path.
                                                if (strpos($source, 'width=') || strpos($source, 'autostart=')) {
                                                    continue;
                                                }

												if (substr($source, 0, 2) == './') {
													$source = substr($source, 2);
												}
												$files_list[] = array($source, 'local', 'rel');
												$dir = dirname($abs_path);
												$new_abs_path = realpath($dir.'/'.$source);
												$in_files_list[] = learnpathItem::get_resources_from_source(TOOL_DOCUMENT, $new_abs_path, $recursivity + 1);
												if (count($in_files_list) > 0) {
													$files_list = array_merge($files_list, $in_files_list);
												}
											}
										}
									}
								}
							}
							break;
						default:
							break;
					}

				} else {
					// The file could not be found.
					return false;
				}
				break;
			default: // Ignore.
				break;
		}
		//error_log(str_repeat(' ', $recursivity), 'found files '.print_r($files_list, true), 0);
		//return $files_list;
		$checked_files_list = array();
		$checked_array_list = array();
		foreach ($files_list as $idx => $file) {
			if (!empty($file[0])) {
				if (!in_array($file[0], $checked_files_list)) {
					$checked_files_list[] = $files_list[$idx][0];
					$checked_array_list[] = $files_list[$idx];
				}
			}
		}
		return $checked_array_list;
	}

	/**
	 * Gets the score
	 * @return	float	The current score or 0 if no score set yet
	 */
	public function get_score() {
		if (self::debug > 0) { error_log('learnpathItem::get_score()', 0); }
		$res = 0;
		if (!empty($this->current_score)) {
			$res = $this->current_score;
		}
		if (self::debug > 1) { error_log('New LP - Out of learnpathItem::get_score() - returning '.$res, 0); }
		return $res;
	}

	/**
	 * Gets the item status
	 * @param	boolean	Do or don't check into the database for the latest value. Optional. Default is true
	 * @param	boolean	Do or don't update the local attribute value with what's been found in DB
	 * @return	string	Current status or 'Not attempted' if no status set yet
	 */
	public function get_status($check_db = true, $update_local = false) {
	    $course_id = api_get_course_int_id();
		if (self::debug > 0) { error_log('learnpathItem::get_status() on item '.$this->db_id, 0); }
		if ($check_db) {
			if (self::debug > 2) { error_log('learnpathItem::get_status(): checking db', 0); }
            if (!empty($this->db_item_view_id)) {
                $table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
                $sql = "SELECT status FROM $table WHERE c_id = $course_id AND id = '".$this->db_item_view_id."' AND view_count = '".$this->get_attempt_id()."'";
                if (self::debug > 2) { error_log('learnpathItem::get_status() - Checking DB: '.$sql, 0); }

                $res = Database::query($sql);
                if (Database::num_rows($res) == 1) {
                    $row = Database::fetch_array($res);
                    if ($update_local) {
                        $this->set_status($row['status']);
                    }
                    if (self::debug > 2) { error_log('learnpathItem::get_status() - Returning db value '.$row['status'], 0); }
                    return $row['status'];
                }
            }
		} else {
			if (self::debug > 2) { error_log('learnpathItem::get_status() - in get_status: using attrib', 0); }
			if (!empty($this->status)) {
				if (self::debug > 2) { error_log('learnpathItem::get_status() - Returning attrib: '.$this->status, 0); }
				return $this->status;
			}
		}
   		if (self::debug > 2) { error_log('learnpathItem::get_status() - Returning default '.$this->possible_status[0], 0); }
		return $this->possible_status[0];
	}

	/**
	 * Gets the suspend data
	 */
	public function get_suspend_data() {
		if (self::debug > 0) { error_log('learnpathItem::get_suspend_data()', 0); }
		// TODO: Improve cleaning of breaklines ... it works but is it really a beautiful way to do it ?
		if (!empty($this->current_data)) { return str_replace(array("\r", "\n"), array('\r', '\n'), $this->current_data); } else { return ''; }
	}

	/**
	 * Gets the total time spent on this item view so far
	 * @param	string	Origin of the request. If coming from PHP, send formatted as xxhxx'xx", otherwise use scorm format 00:00:00
	 * @param	integer	Given time is a default time to return formatted
	 */
	public function get_scorm_time($origin = 'php', $given_time = null, $query_db = false) {
		$h = get_lang('h');
        $course_id = api_get_course_int_id();
		if (!isset($given_time)) {
			if (self::debug > 2) { error_log('learnpathItem::get_scorm_time(): given time empty, current_start_time = '.$this->current_start_time, 0); }
			if (is_object($this)) {
				if ($query_db === true) {
					$table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
					$sql = "SELECT start_time, total_time FROM $table
					        WHERE  c_id = $course_id AND
					               id = '".$this->db_item_view_id."' AND
					               view_count = '".$this->get_attempt_id()."'";
					$res = Database::query($sql);
					$row = Database::fetch_array($res);
					$start = $row['start_time'];
					$stop = $start + $row['total_time'];
				} else {
					$start = $this->current_start_time;
					$stop = $this->current_stop_time;
				}
				if (!empty($start)) {
					if (!empty($stop)) {
						$time = $stop - $start;
					} else {
						$time = time() - $start;
					}
				}
			} else {
				if ($origin == 'js') {
					return '00:00:00';
				} else {
					return '00'.$h.'00\'00"';
				}
			}
		} else {
			$time = $given_time;
		}
		if (self::debug > 2) { error_log('learnpathItem::get_scorm_time(): intermediate = '.$time, 0); }
		$hours = $time/3600;
		$mins  = ($time%3600)/60;
		$secs  = ($time%60);
  		if ($origin == 'js') {
			$scorm_time = trim(sprintf("%4d:%02d:%02d", $hours, $mins, $secs));
  		} else {
			$scorm_time = trim(sprintf("%4d$h%02d'%02d\"", $hours, $mins, $secs));
  		}
		if (self::debug > 2) { error_log('learnpathItem::get_scorm_time('.$scorm_time.')', 0); }
  		return $scorm_time;
	}

	public function get_terms() {
		$lp_item = Database::get_course_table(TABLE_LP_ITEM);
        $course_id = api_get_course_int_id();
		$sql = "SELECT * FROM $lp_item WHERE c_id = $course_id AND id='".Database::escape_string($this->db_id)."'";
		$res = Database::query($sql);
		$row = Database::fetch_array($res);
		return $row['terms'];
	}

	/**
	 * Returns the item's title
	 * @return	string	Title
	 */
	public function get_title() {
		if (self::debug > 0) { error_log('learnpathItem::get_title()', 0); }
		if (empty($this->title)) { return ''; }
		return $this->title;
	}

	/**
	 * Returns the total time used to see that item
	 * @return	integer	Total time
	 */
	public function get_total_time() {
		if (self::debug > 0) { error_log('learnpathItem::get_total_time() for item '.$this->db_id.' - Initially, current_start_time = '.$this->current_start_time.' and current_stop_time = '.$this->current_stop_time, 0); }
		if ($this->current_start_time == 0) { // Shouldn't be necessary thanks to the open() method.
			if (self::debug > 2) { error_log('learnpathItem::get_total_time() - Current start time was empty', 0); }
			$this->current_start_time = time();
		}
		//$this->current_stop_time=time();
		if (time() < $this->current_stop_time or $this->current_stop_time == 0) {
			if (self::debug > 2) { error_log('learnpathItem::get_total_time() - Current stop time was greater than the current time or was empty', 0); }
			// If this case occurs, then we risk to write huge time data in db.
			// In theory, stop time should be *always* updated here, but it might be used in some unknown goal.
			$this->current_stop_time = time();
		}
		$time = $this->current_stop_time - $this->current_start_time;
		if ($time < 0) {
			if (self::debug > 2) { error_log('learnpathItem::get_total_time() - Time smaller than 0. Returning 0', 0); }
			return 0;
		} else {
			if (self::debug > 2) { error_log('learnpathItem::get_total_time() - Current start time = '.$this->current_start_time.', current stop time = '.$this->current_stop_time.' Returning '.$time."-----------\n", 0); }
			return $time;
		}
	}

	/**
	 * Gets the item type
	 * @return	string	The item type (can be doc, dir, sco, asset)
	 */
	public function get_type() {
		$res = 'asset';
		if (!empty($this->type)) {
			$res = $this->type;
		}
		if (self::debug > 2) { error_log('learnpathItem::get_type() - Returning '.$res.' for item '.$this->db_id, 0); }
		return $res;
	}

	/**
	 * Gets the view count for this item
	 * @return  int     Number of attempts or 0
     */
	public function get_view_count() {
		if (self::debug > 0) { error_log('learnpathItem::get_view_count()', 0); }
		if (!empty($this->attempt_id)) {
			return $this->attempt_id;
		} else {
			return 0;
		}
	}

	/**
	 * Tells if an item is done ('completed','passed','succeeded') or not
	 * @return	bool	True if the item is done ('completed','passed','succeeded'), false otherwise
	 */
	function is_done(){
   		if ($this->status_is(array('completed', 'passed', 'succeeded', 'failed'))) {
   			if (self::debug > 2) { error_log('learnpath::is_done() - Item '.$this->get_id().' is complete', 0); }
   			return true;
   		}else{
   			if (self::debug > 2) { error_log('learnpath::is_done() - Item '.$this->get_id().' is not complete', 0); }
   			return false;
   		}
	}

	/**
	 * Tells if a restart is allowed (take it from $this->prevent_reinit and $this->status)
	 * @return	integer	-1 if retaking the sco another time for credit is not allowed,
	 * 					 0 if it is not allowed but the item has to be finished
	 * 					 1 if it is allowed. Defaults to 1
	 */
	public function is_restart_allowed() {
		if (self::debug > 2) { error_log('learnpathItem::is_restart_allowed()', 0); }
		$restart = 1;
		$mystatus = $this->get_status(true);
		if ($this->get_prevent_reinit() > 0){ // If prevent_reinit == 1 (or more)
			// If status is not attempted or incomplete, authorize retaking (of the same) anyway. Otherwise:
			if ($mystatus != $this->possible_status[0] && $mystatus != $this->possible_status[1]) {
				$restart = -1;
			}else{ //status incompleted or not attempted
				$restart = 0;
			}
		} else {
			if ($mystatus == $this->possible_status[0] || $mystatus == $this->possible_status[1]) {
				$restart = -1;
			}
		}
		if (self::debug > 2) { error_log('New LP - End of learnpathItem::is_restart_allowed() - Returning '.$restart, 0); }
		return $restart;
	}

	/**
	 * Opens/launches the item. Initialises runtime values.
	 * @return	boolean	True on success, false on failure.
	 */
	public function open($allow_new_attempt = false) {
		if (self::debug > 0) { error_log('learnpathItem::open()', 0); }
		if ($this->prevent_reinit == 0) {
			$this->current_score = 0;
			$this->current_start_time = time();
			// In this case, as we are opening the item, what is important to us
			// is the database status, in order to know if this item has already
			// been used in the past (rather than just loaded and modified by
			// some javascript but not written in the database).
			// If the database status is different from 'not attempted', we can
			// consider this item has already been used, and as such we can
			// open a new attempt. Otherwise, we'll just reuse the current
			// attempt, which is generally created the first time the item is
			// loaded (for example as part of the table of contents).
			$stat = $this->get_status(true);
			if ($allow_new_attempt && isset($stat) && ($stat != $this->possible_status[0])) {
				$this->attempt_id = $this->attempt_id + 1; // Open a new attempt.
			}
			$this->status = $this->possible_status[1];
		} else {
			/*if ($this->current_start_time == 0) {
				// Small exception for start time, to avoid amazing values.
				$this->current_start_time = time();
			}*/
			// If we don't init start time here, the time is sometimes calculated from the last start time.
			$this->current_start_time = time();

			//error_log('New LP - reinit blocked by setting', 0);
		}
	}

	/**
	 * Outputs the item contents
	 * @return	string	HTML file (displayable in an <iframe>) or empty string if no path defined
	 */
	function output() {
		if (self::debug > 0) { error_log('learnpathItem::output()', 0); }
		if (!empty($this->path) and is_file($this->path)) {
			$output = '';
			$output .= file_get_contents($this->path);
			return $output;
		}
		return '';
	}

	/**
	 * Parses the prerequisites string with the AICC logic language
	 * @param	string	The prerequisites string as it figures in imsmanifest.xml
	 * @param	Array	Array of items in the current learnpath object. Although we're in the learnpathItem object, it's necessary to have a list of all items to be able to check the current item's prerequisites
	 * @param	Array	List of references (the "ref" column in the lp_item table) that are strings used in the expression of prerequisites.
	 * @param	integer	The user ID. In some cases like Chamilo quizzes, it's necessary to have the user ID to query other tables (like the results of quizzes)
	 * @return	boolean	True if the list of prerequisites given is entirely satisfied, false otherwise
	 */
	public function parse_prereq($prereqs_string, $items, $refs_list, $user_id) {
		if (self::debug > 0) { error_log('learnpathItem::parse_prereq() for learnpath '.$this->lp_id.' with string '.$prereqs_string, 0); }

        $course_id = api_get_course_int_id();
		// Deal with &, |, ~, =, <>, {}, ,, X*, () in reverse order.
		$this->prereq_alert = '';
		// First parse all parenthesis by using a sequential loop (looking for less-inclusives first).

		if ($prereqs_string == '_true_') { return true; }
		if ($prereqs_string == '_false_') {
			if (empty($this->prereq_alert)) {
				$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
			}
			return false;
		}
		while (strpos($prereqs_string, '(') !== false) {
			// Remove any () set and replace with its value.
			$matches = array();
			$res = preg_match_all('/(\(([^\(\)]*)\))/', $prereqs_string, $matches);
			if ($res) {
				foreach ($matches[2] as $id => $match) {
					$str_res = $this->parse_prereq($match, $items, $refs_list, $user_id);
					if ($str_res) {
						$prereqs_string = str_replace($matches[1][$id], '_true_', $prereqs_string);
					} else {
						$prereqs_string = str_replace($matches[1][$id], '_false_', $prereqs_string);
					}
				}
			}
		}

		// Parenthesis removed, now look for ORs as it is the lesser-priority binary operator (= always uses one text operand).
		if (strpos($prereqs_string, '|') === false) {
			if (self::debug > 1) { error_log('New LP - Didnt find any OR, looking for AND', 0); }
			if (strpos($prereqs_string, '&') !== false) {
				$list = split('&', $prereqs_string);
				if (count($list) > 1) {
					$andstatus = true;
					foreach ($list as $condition) {
						$andstatus = $andstatus && $this->parse_prereq($condition, $items, $refs_list, $user_id);
						if (!$andstatus) {
							if (self::debug > 1) { error_log('New LP - One condition in AND was false, short-circuit', 0); }
							break;
						}
					}
					if (empty($this->prereq_alert) && !$andstatus) {
						$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
					}
					return $andstatus;
				} else {
					if (isset($items[$refs_list[$list[0]]])) {
						$status = $items[$refs_list[$list[0]]]->get_status(true);
						$returnstatus = (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3]));
						if (empty($this->prereq_alert) && !$returnstatus) {
							$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
						}
						return $returnstatus;
					}
					$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
					return false;
				}
			} else {
				// No ORs found, now look for ANDs.

				if (self::debug > 1) { error_log('New LP - Didnt find any AND, looking for =', 0); }
				if (strpos($prereqs_string, '=') !== false) {
					if (self::debug > 1) { error_log('New LP - Found =, looking into it', 0); }
					// We assume '=' signs only appear when there's nothing else around.
					$params = split('=', $prereqs_string);
					if (count($params) == 2) {
						// Right number of operands.
						if (isset($items[$refs_list[$params[0]]])) {
							$status = $items[$refs_list[$params[0]]]->get_status(true);
							$returnstatus = ($status == $params[1]);
							if (empty($this->prereq_alert) && !$returnstatus) {
								$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
							}
							return $returnstatus;
						}
						$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
						return false;
					}
				} else {
					// No ANDs found, look for <>

					if (self::debug > 1) { error_log('New LP - Didnt find any =, looking for <>', 0); }
					if (strpos($prereqs_string, '<>') !== false) {
						if (self::debug > 1) { error_log('New LP - Found <>, looking into it', 0); }
						// We assume '<>' signs only appear when there's nothing else around.
						$params = split('<>', $prereqs_string);
						if (count($params) == 2) {
							// Right number of operands.
							if (isset($items[$refs_list[$params[0]]])) {
								$status = $items[$refs_list[$params[0]]]->get_status(true);
								$returnstatus = ($status != $params[1]);
								if (empty($this->prereq_alert) && !$returnstatus) {
									$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
								}
								return $returnstatus;
							}
							$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
							return false;
						}
					} else {
						// No <> found, look for ~ (unary)
						if (self::debug > 1) { error_log('New LP - Didnt find any =, looking for ~', 0); }
						// Only remains: ~ and X*{}
						if (strpos($prereqs_string, '~') !== false) {
							// Found NOT.
							if (self::debug > 1) { error_log('New LP - Found ~, looking into it', 0); }
							$list = array();
							$myres = preg_match('/~([^(\d+\*)\{]*)/', $prereqs_string, $list);
							if ($myres) {
								$returnstatus = !$this->parse_prereq($list[1], $items, $refs_list, $user_id);
								if (empty($this->prereq_alert) && !$returnstatus) {
									$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
								}
								return $returnstatus;
							} else {
								// Strange...
								if (self::debug > 1) { error_log('New LP - Found ~ but strange string: '.$prereqs_string, 0); }
							}
						} else {
							// Finally, look for sets/groups

							if (self::debug > 1) { error_log('New LP - Didnt find any ~, looking for groups', 0); }
							// Only groups here.
							$groups = array();
							$groups_there = preg_match_all('/((\d+\*)?\{([^\}]+)\}+)/', $prereqs_string, $groups);

							if ($groups_there) {
								foreach ($groups[1] as $gr) { // Only take the results that correspond to the big brackets-enclosed condition.
									if (self::debug > 1) { error_log('New LP - Dealing with group '.$gr, 0); }
									$multi = array();
									$mycond = false;
									if (preg_match('/(\d+)\*\{([^\}]+)\}/', $gr, $multi)) {
										if (self::debug > 1) { error_log('New LP - Found multiplier '.$multi[0], 0); }
										$count = $multi[1];
										$list = split(',', $multi[2]);
										$mytrue = 0;
										foreach ($list as $cond) {
											if (isset($items[$refs_list[$cond]])) {
												$status = $items[$refs_list[$cond]]->get_status(true);
												if (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3])) {
													$mytrue ++;
													if (self::debug > 1) { error_log('New LP - Found true item, counting.. ('.($mytrue).')', 0); }
												}
											} else {
												if (self::debug > 1) { error_log('New LP - item '.$cond.' does not exist in items list', 0); }
											}
										}
										if ($mytrue >= $count) {
											if (self::debug > 1) { error_log('New LP - Got enough true results, return true', 0); }
											$mycond = true;
										} else {
											if (self::debug > 1) { error_log('New LP - Not enough true results', 0); }
										}
									} else {
										if (self::debug > 1) { error_log('New LP - No multiplier', 0); }
										$list = split(',', $gr);
										$mycond = true;
										foreach ($list as $cond) {
											if (isset($items[$refs_list[$cond]])) {
												$status = $items[$refs_list[$cond]]->get_status(true);
												if (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3])){
													$mycond = true;
													if (self::debug > 1) { error_log('New LP - Found true item', 0); }
												} else {
													if (self::debug > 1) { error_log('New LP - Found false item, the set is not true, return false', 0); }
													$mycond = false;
													break;
												}
											} else {
												if (self::debug > 1) { error_log('New LP - item '.$cond.' does not exist in items list', 0); }
												if (self::debug > 1) { error_log('New LP - Found false item, the set is not true, return false', 0); }
												$mycond = false;
												break;
											}
										}
									}
									if (!$mycond && empty($this->prereq_alert)) {
										$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
									}
									return $mycond;
								}
							} else {

								// Nothing found there either. Now return the value of the corresponding resource completion status.
								if (self::debug > 1) { error_log('New LP - Didnt find any group, returning value for '.$prereqs_string, 0); }

								if (isset($items[$refs_list[$prereqs_string]])) {
									if ($items[$refs_list[$prereqs_string]]->type == 'quiz') {

										// 1. Checking the status in current items.
										$status = $items[$refs_list[$prereqs_string]]->get_status(true);
										//error_log('hello '.$status);
										$returnstatus = (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3]));

										if (!$returnstatus) {
											if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' not complete', 0); }
										} else {
											if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' complete', 0); }
										}

										// For one attempt LPs.
										if ($this->prevent_reinit == 1) {

											// 2. If is completed we check the results in the DB of the quiz.
											if ($returnstatus) {
												//AND origin_lp_item_id = '.$user_id.'
												$sql = 'SELECT exe_result, exe_weighting
														FROM '.Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES).'
														WHERE 	exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.'
																AND exe_user_id = '.$user_id.'
																AND orig_lp_id = '.$this->lp_id.' AND orig_lp_item_id = '.$prereqs_string.'
																AND status <> "incomplete"
														ORDER BY exe_date DESC
														LIMIT 0, 1';
												//error_log('results :'.$items[$refs_list[$prereqs_string]]->path. ':'.$user_id);

												$rs_quiz = Database::query($sql);
												if ($quiz = Database :: fetch_array($rs_quiz)) {
													if ($quiz['exe_result'] >= $items[$refs_list[$prereqs_string]]->get_mastery_score()) {
														$returnstatus = true;
													} else {
														$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
														$returnstatus = false;
													}
												} else {
													$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
													$returnstatus = false;
												}
											}

										} else {
											// 3. for multiple attempts we check that there are minimun 1 item completed.

											// Checking in the database.
											$sql = 'SELECT exe_result, exe_weighting
													FROM '.Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES).'
													WHERE	exe_exo_id = '.$items[$refs_list[$prereqs_string]]->path.'
															AND exe_user_id = '.$user_id.' AND orig_lp_id = '.$this->lp_id.' AND orig_lp_item_id = '.$prereqs_string.' ';
											//error_log('results 2:'.$items[$refs_list[$prereqs_string]]->path. ':'.$user_id);

											$rs_quiz = Database::query($sql);
											if (Database::num_rows($rs_quiz) > 0) {
												while ($quiz = Database :: fetch_array($rs_quiz)) {
													if ($quiz['exe_result'] >= $items[$refs_list[$prereqs_string]]->get_mastery_score()) {
														$returnstatus = true;
														break;
													} else {
														$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
														$returnstatus = false;
													}
												}
											} else {
												$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
												$returnstatus = false;
											}
										}
										return $returnstatus;
									} else {
										$status = $items[$refs_list[$prereqs_string]]->get_status(false);
										$returnstatus = (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3]));

										if (!$returnstatus) {
											if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' not complete', 0); }
										} else {
											if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' complete', 0); }
										}

										//$returnstatus = true;
										if ($returnstatus  && $this->prevent_reinit == 1) {
											// I would prefer check in the database.
											$lp_item_view = Database::get_course_table(TABLE_LP_ITEM_VIEW);
											$lp_view = Database::get_course_table(TABLE_LP_VIEW);

											$sql = 'SELECT id FROM '.$lp_view.'
													WHERE c_id = '.$course_id.' AND user_id = '.$user_id.'  AND lp_id = '.$this->lp_id.' LIMIT 0, 1';
											$rs_lp = Database::query($sql);
											$lp_id = Database :: fetch_row($rs_lp);
											$my_lp_id = $lp_id[0];

											$sql = 'SELECT status FROM '.$lp_item_view.'
													WHERE c_id = '.$course_id.' AND lp_view_id = '.$my_lp_id.' AND lp_item_id = '.$refs_list[$prereqs_string].' LIMIT 0, 1';
											$rs_lp = Database::query($sql);
											$status_array = Database :: fetch_row($rs_lp);
											$status	= $status_array[0];

											$returnstatus = (($status == $this->possible_status[2]) OR ($status == $this->possible_status[3]));
											if (!$returnstatus && empty($this->prereq_alert)){
												$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
											}
											if (!$returnstatus) {
												if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' not complete', 0); }
											} else {
												if (self::debug > 1) { error_log('New LP - Prerequisite '.$prereqs_string.' complete', 0); }
											}
										}
										return $returnstatus;
									}
								} else {
									if (self::debug > 1) { error_log('New LP - Could not find '.$prereqs_string.' in '.print_r($refs_list, true), 0); }
								}
							}
						}
					}
				}
			}
		} else {
			$list = split("\|",$prereqs_string);
			if(count($list)>1){
				if (self::debug > 1) { error_log('New LP - Found OR, looking into it', 0); }
				$orstatus = false;
				foreach ($list as $condition) {
					if (self::debug > 1) { error_log('New LP - Found OR, adding it ('.$condition.')', 0); }
					$orstatus = $orstatus || $this->parse_prereq($condition, $items, $refs_list, $user_id);
					if ($orstatus) {
						// Shortcircuit OR.
						if (self::debug > 1) { error_log('New LP - One condition in OR was true, short-circuit', 0); }
						break;
					}
				}
				if (!$orstatus && empty($this->prereq_alert)) {
					$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
				}
				return $orstatus;
			} else {
				if (self::debug>1) { error_log('New LP - OR was found but only one elem present !?', 0); }
				if (isset($items[$refs_list[$list[0]]])) {
					$status = $items[$refs_list[$list[0]]]->get_status(true);
					$returnstatus = (($status == 'completed') OR ($status == 'passed'));
					if (!$returnstatus && empty($this->prereq_alert)) {
						$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
					}
					return $returnstatus;
				}
			}
		}
		if(empty($this->prereq_alert)){
			$this->prereq_alert = get_lang('LearnpathPrereqNotCompleted');
		}
		if (self::debug > 1) { error_log('New LP - End of parse_prereq. Error code is now '.$this->prereq_alert, 0); }
		return false;
	}

	/**
	 * Reinits all local values as the learnpath is restarted
	 * @return	boolean	True on success, false otherwise
	 */
	public function restart() {
        if (self::debug > 0) { error_log('learnpathItem::restart()', 0); }
        if ($this->type == 'sco') { //If this is a sco, chamilo can't update the time without explicit scorm call
            $this->current_start_time = 0;
            $this->current_stop_time = 0; //Those 0 value have this effect
            $this->last_scorm_session_time = 0;
        }
		$this->save();

        //For serious game  : We reuse same attempt_id
        if ($this->get_seriousgame_mode() == 1 && $this->type == 'sco') {
    			$this->current_start_time = 0;
    			$this->current_stop_time = 0;
          return true;
        }
		$allowed = $this->is_restart_allowed();
		if ($allowed === -1) {
			// Nothing allowed, do nothing.
		} elseif ($allowed === 1) {
			// Restart as new attempt is allowed, record a new attempt.
			$this->attempt_id = $this->attempt_id + 1; // Simply reuse the previous attempt_id.
			$this->current_score = 0;
			$this->current_start_time = 0;
			$this->current_stop_time = 0;
			$this->current_data = '';
			$this->status = $this->possible_status[0];
			$this->interactions_count = 0;
			$this->interactions = array();
			$this->objectives_count = 0;
			$this->objectives = array();
			$this->lesson_location = '';
			if ($this->type != TOOL_QUIZ) {
				$this->write_to_db();
			}
		} else {
			// Restart current element is allowed (because it's not finished yet),
			// reinit current.
			//$this->current_score = 0;
			$this->current_start_time = 0;
			$this->current_stop_time = 0;
			//$this->current_data = '';
			//$this->status = $this->possible_status[0];
			$this->interactions_count = $this->get_interactions_count(true);
      if ($this->type == 'sco')
        $this->scorm_init_time();
		}
		return true;
	}

	/**
	 * Saves data in the database
	 * @param	boolean	Save from URL params (1) or from object attributes (0)
	 * @param	boolean	The results of a check on prerequisites for this item. True if prerequisites are completed, false otherwise. Defaults to false. Only used if not sco or au
	 * @return	boolean	True on success, false on failure
	 */
	public function save($from_outside = true, $prereqs_complete = false) {
		if (self::debug > 0) { error_log('learnpathItem::save()', 0); }
	 	// First check if parameters passed via GET can be saved here
	 	// in case it's a SCORM, we should get:
		if ($this->type == 'sco' || $this->type== 'au') {
			$status = $this->get_status(true);
			if ($this->prevent_reinit == 1 AND
				$status != $this->possible_status[0] AND $status != $this->possible_status[1]) {
				if (self::debug > 1) { error_log('learnpathItem::save() - save reinit blocked by setting', 0); }
				// Do nothing because the status has already been set. Don't allow it to change.
				// TODO: Check there isn't a special circumstance where this should be saved.
			} else {
				if (self::debug > 1) { error_log('learnpathItem::save() - SCORM save request received', 0); }
				//get all new settings from the URL
				if ($from_outside) {
					if (self::debug > 1) { error_log('learnpathItem::save() - Getting item data from outside', 0); }
					foreach ($_GET as $param => $value) {
						$value = Database::escape_string($value);
						switch ($param) {
							case 'score':
								$this->set_score($value);
								if (self::debug > 2) { error_log('learnpathItem::save() - setting score to '.$value, 0); }
								break;
							case 'max':
								$this->set_max_score($value);
								if (self::debug > 2) { error_log('learnpathItem::save() - setting view_max_score to '.$value, 0); }
								break;
			 				case 'min':
			 					$this->min_score = $value;
			 					if (self::debug > 2) { error_log('learnpathItem::save() - setting min_score to '.$value, 0); }
			 					break;
			 				case 'lesson_status':
			 					if (!empty($value)) {
				 					$this->set_status($value);
                                    if (self::debug > 2) { error_log('learnpathItem::save() - setting status to '.$value, 0); }
			 					}
			 					break;
			 				case 'time':
			 					$this->set_time($value);
			 					if (self::debug > 2) { error_log('learnpathItem::save() - setting time to '.$value, 0); }
			 					break;
			 				case 'suspend_data':
			 					$this->current_data = $value;
			 					if (self::debug > 2) { error_log('learnpathItem::save() - setting suspend_data to '.$value, 0); }
			 					break;
			 				case 'lesson_location':
			 					$this->set_lesson_location($value);
			 					if (self::debug > 2) { error_log('learnpathItem::save() - setting lesson_location to '.$value, 0); }
			 					break;
			 				case 'core_exit':
			 					$this->set_core_exit($value);
			 					if (self::debug > 2) { error_log('learnpathItem::save() - setting core_exit to '.$value, 0); }
			 					break;
			 				case 'interactions':
			 					//$interactions = unserialize($value);
			 					//foreach($interactions as $interaction){
			 					//	;
			 					//}
			 					break;
			 				case 'objectives':
			 					break;
			 				//case 'maxtimeallowed':
			 					//$this->set_max_time_allowed($value);
			 					//break;
			 				/*
			 				case 'objectives._count':
			 					$this->attempt_id = $value;
			 					break;
			 				*/
			 				default:
			 					// Ignore.
			 					break;
						}
			 		}
				} else {
					if (self::debug > 1) { error_log('learnpathItem::save() - Using inside item status', 0); }
					// Do nothing, just let the local attributes be used.
				}
			}
		} else { // If not SCO, such messages should not be expected.
			$type = strtolower($this->type);
			switch ($type) {
				case 'asset':
		 			if ($prereqs_complete) {
			 			$this->set_status($this->possible_status[2]);
		 			}
		 			break;
		 		case TOOL_HOTPOTATOES: break;
		 		case TOOL_QUIZ: return false;break;
				default:
		 			// For now, everything that is not sco and not asset is set to
		 			// completed when saved.
		 			if ($prereqs_complete) {
		 				$this->set_status($this->possible_status[2]);
		 			}
				break;
	 		}
		}
		//$time = $this->time
		if (self::debug > 1) { error_log('New LP - End of learnpathItem::save() - Calling write_to_db()', 0); }
		return $this->write_to_db();
	}

	/**
	 * Sets the number of attempt_id to a given value
	 * @param	integer	The given value to set attempt_id to
	 * @return	boolean	TRUE on success, FALSE otherwise
	 */
	public function set_attempt_id($num) {
		if (self::debug > 0) { error_log('learnpathItem::set_attempt_id()', 0); }
	 	if ($num == strval(intval($num)) && $num >= 0) {
	 		$this->attempt_id = $num;
	 		return true;
	 	}
	 	return false;
	}

	/**
	 * Sets the core_exit value to the one given
	 * @return  bool    True (always)
	 */
	public function set_core_exit($value) {
		switch($value){
			case '':
				$this->core_exit = '';
				break;
			case 'suspend':
				$this->core_exit = 'suspend';
				break;
			default:
				$this->core_exit = 'none';
				break;
		}
		return true;
	}

	/**
	 * Sets the item's description
	 * @param	string	Description
	 * @return  void
	 */
	public function set_description($string = '') {
		if (self::debug > 0) { error_log('learnpathItem::set_description()', 0); }
		if (!empty($string)) { $this->description = $string; }
	}

	/**
	 * Sets the lesson_location value
	 * @param	string	lesson_location as provided by the SCO
	 * @return	boolean	True on success, false otherwise
	 */
	public function set_lesson_location($location) {
   		if (self::debug > 0) { error_log('learnpathItem::set_lesson_location()', 0); }
		if (isset($location)) {
   			$this->lesson_location = Database::escape_string($location);
  			return true;
  		}
	 	return false;
	}

	/**
	 * Sets the item's depth level in the LP tree (0 is at root)
	 * @param	integer	Level
	 * @return  void
	 */
	public function set_level($int = 0) {
		if (self::debug > 0) { error_log('learnpathItem::set_level('.$int.')', 0); }
		if (!empty($int) AND $int == strval(intval($int))) { $this->level = $int; }
	}

	/**
	 * Sets the lp_view id this item view is registered to
	 * @param	integer	lp_view DB ID
	 * @return  void
	 * @todo //todo insert into lp_item_view if lp_view not exists
	 */
	public function set_lp_view($lp_view_id, $course_id = null) {
	    if (empty($course_id)) {
	        $course_id = api_get_course_int_id();
	    } else {
	        $course_id = intval($course_id);
	    }
	    if (self::debug > 0) { error_log('learnpathItem::set_lp_view('.$lp_view_id.')', 0); }
		if (!empty($lp_view_id) and $lp_view_id = intval(strval($lp_view_id))) {
	 		$this->view_id = $lp_view_id;

		 	$item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		 	// Get the lp_item_view with the highest view_count.
		 	$sql = "SELECT * FROM $item_view_table
                    WHERE   c_id = $course_id AND
                            lp_item_id = ".$this->get_id()." AND
                            lp_view_id = ".$lp_view_id."
                    ORDER BY view_count DESC";

		 	if (self::debug > 2) { error_log('learnpathItem::set_lp_view() - Querying lp_item_view: '.$sql, 0); }
		 	$res = Database::query($sql);
		 	if (Database::num_rows($res) > 0) {
		 		$row = Database::fetch_array($res);
		 		$this->db_item_view_id      = $row['id'];
		 		$this->attempt_id           = $row['view_count'];
				$this->current_score        = $row['score'];
				$this->current_data         = $row['suspend_data'];
				$this->view_max_score       = $row['max_score'];
				$this->status               = $row['status'];
                $this->current_start_time	= $row['start_time'];
                $this->current_stop_time 	= $this->current_start_time + $row['total_time'];
				$this->lesson_location      = $row['lesson_location'];
				$this->core_exit            = $row['core_exit'];
			 	if (self::debug > 2) { error_log('learnpathItem::set_lp_view() - Updated item object with database values', 0); }

			 	// Now get the number of interactions for this little guy.
			 	$item_view_interaction_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
			 	$sql = "SELECT * FROM $item_view_interaction_table WHERE c_id = $course_id AND lp_iv_id = '".$this->db_item_view_id."'";
                //error_log('sql10->'.$sql);
				$res = Database::query($sql);
				if ($res !== false) {
					$this->interactions_count = Database::num_rows($res);
				} else {
					$this->interactions_count = 0;
				}
			 	// Now get the number of objectives for this little guy.
			 	$item_view_objective_table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
			 	$sql = "SELECT * FROM $item_view_objective_table WHERE c_id = $course_id AND lp_iv_id = '".$this->db_item_view_id."'";
                //error_log('sql11->'.$sql);
				$res = Database::query($sql);
				if ($res !== false) {
					$this->objectives_count = Database::num_rows($res);
				} else {
					$this->objectives_count = 0;
				}
		 	}
	 	}
		// End
		if (self::debug > 2) { error_log('New LP - End of learnpathItem::set_lp_view()', 0); }
	}

	/**
	 * Sets the path
	 * @param	string	Path
	 * @return  void
	 */
	public function set_path($string = '') {
		if (self::debug > 0) { error_log('learnpathItem::set_path()', 0); }
		if (!empty($string)) { $this->path = $string; }
	}

	/**
	 * Sets the prevent_reinit attribute. This is based on the LP value and is set at creation time for
	 * each learnpathItem. It is a (bad?) way of avoiding a reference to the LP when saving an item.
	 * @param	integer	1 for "prevent", 0 for "don't prevent" saving freshened values (new "not attempted" status etc)
	 * @return  void
	 */
	public function set_prevent_reinit($prevent) {
		if (self::debug > 0) { error_log('learnpathItem::set_prevent_reinit()', 0); }
		if ($prevent) {
			$this->prevent_reinit = 1;
		} else {
			$this->prevent_reinit = 0;
		}
	}

	/**
	 * Sets the score value. If the mastery_score is set and the score reaches
	 * it, then set the status to 'passed'.
	 * @param	float	Score
	 * @return	boolean	True on success, false otherwise
	 */
	public function set_score($score) {
        //$possible_status = array('not attempted','incomplete','completed','passed','failed','browsed');
        $debug = self::debug;
   		if ($debug > 0) { error_log('learnpathItem::set_score('.$score.')', 0); }
   		if (($this->max_score <= 0 || $score <= $this->max_score) && ($score >= $this->min_score)) {
   			$this->current_score = $score;
   			$master = $this->get_mastery_score();
   			$current_status = $this->get_status(false);

            //Fixes bug when SCORM doesn't send a mastery score even if they sent a score!
            if ($master == -1) {
                $master = $this->max_score;
            }

            if ($debug > 0) {
                error_log('get_mastery_score: '.$master);
                error_log('current_status: '.$current_status);
                error_log('current score : '.$this->current_score);
            }

   			// If mastery_score is set AND the current score reaches the mastery score AND the current status is different from 'completed', then set it to 'passed'.
            /*
   			if ($master != -1 && $this->current_score >= $master && $current_status != $this->possible_status[2]) {
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[3]);
   				$this->set_status($this->possible_status[3]); //passed
   			} elseif ($master != -1 && $this->current_score < $master) {
                if ($debug > 0) error_log('Status changed to: '.$this->possible_status[4]);
   				$this->set_status($this->possible_status[4]); //failed
   			}*/
  			return true;
  		}
	 	return false;
	}

	/**
	 * Sets the maximum score for this item
	 * @param	int		Maximum score - must be a decimal or an empty string
	 * @return	boolean	True on success, false on error
	 */
	public function set_max_score($score) {
   		if (self::debug > 0) { error_log('learnpathItem::set_max_score('.$score.')', 0); }
	 	if (is_int($score) or $score == '') {
	 		$this->view_max_score = Database::escape_string($score);
	 		if (self::debug > 1) { error_log('learnpathItem::set_max_score() - Updated object score of item '.$this->db_id.' to '.$this->view_max_score, 0); }
	 		return true;
	 	}
	 	return false;
	}

	/**
	 * Sets the status for this item
	 * @param	string	Status - must be one of the values defined in $this->possible_status
	 * @return	boolean	True on success, false on error
	 */
	public function set_status($status) {
   		if (self::debug > 0) { error_log('learnpathItem::set_status('.$status.')', 0); }
	 	$found = false;
	 	foreach ($this->possible_status  as $possible) {
	 		if (preg_match('/^'.$possible.'$/i', $status)) {
	 			$found = true;
	 		}
	 	}
	 	//if (in_array($status, $this->possible_status)) {
	 	if ($found) {
	 		$this->status = Database::escape_string($status);
	 		if (self::debug > 1) { error_log('learnpathItem::set_status() - Updated object status of item '.$this->db_id.' to '.$this->status, 0); }
	 		return true;
	 	}
	 	//error_log('New LP - '.$status.' was not in the possible status', 0);
	 	$this->status = $this->possible_status[0];
	 	return false;
	}

	/**
	 * Set the terms for this learnpath item
	 * @param   string  Terms, as a comma-split list
	 * @return  boolean Always return true
	 */
	public function set_terms($terms) {
		global $charset;
        $course_id = api_get_course_int_id();
		$lp_item = Database::get_course_table(TABLE_LP_ITEM);
		require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
		$a_terms = split(',', $terms);
		$i_terms = split(',', $this->get_terms());
		foreach ($i_terms as $term) {
			if (!in_array($term, $a_terms)) { array_push($a_terms, $term); }
		}
		$new_terms = $a_terms;
		$new_terms_string = implode(',', $new_terms);
		$terms_update_sql = '';
		// TODO: Validate csv string.
		$terms_update_sql = "UPDATE $lp_item SET terms = '". Database::escape_string(api_htmlentities($new_terms_string, ENT_QUOTES, $charset)) . "'
		                      WHERE c_id = $course_id AND id=".$this->get_id();
		$res = Database::query($terms_update_sql);
		// Save it to search engine.
		if (api_get_setting('search_enabled') == 'true') {
			$di = new ChamiloIndexer();
			$di->update_terms($this->get_search_did(), $new_terms);
		}
		return true;
	}

	/**
	 * Get the document ID from inside the text index database
	 * @return  int	 Search index database document ID
	 */
	public function get_search_did() {
		return $this->search_did;
	}

	/**
	 * Sets the item viewing time in a usable form, given that SCORM packages often give it as 00:00:00.0000
	 * @param	string	Time as given by SCORM
	 * @return  void
	 */
	public function set_time($scorm_time, $format = 'scorm') {
   		if (self::debug > 0) { error_log('learnpathItem::set_time('.$scorm_time.')', 0); }
	 	if ($scorm_time == 0 and ($this->type != 'sco') and $this->current_start_time != 0) {
	 		$my_time = time() - $this->current_start_time;
	 		if ($my_time > 0) {
	 			$this->update_time($my_time);
	 			if (self::debug > 0) { error_log('learnpathItem::set_time('.$scorm_time.') - found asset - set time to '.$my_time, 0); }
	 		}
	 	} else {
            switch ($format) {
                case 'scorm':
                    $res = array();
                    if (preg_match('/^(\d{1,4}):(\d{2}):(\d{2})(\.\d{1,4})?/', $scorm_time, $res)) {
                        $time = time();
                        $hour = $res[1];
                        $min = $res[2];
                        $sec = $res[3];
                        // Getting total number of seconds spent.
                        $total_sec = $hour*3600 + $min*60 + $sec;
                        $this->scorm_update_time($total_sec);
                    }
                    break;
                case 'int':
                    $this->scorm_update_time($scorm_time);
                    break;
	 		}
	 	}
	}

	/**
	 * Sets the item's title
	 * @param	string	Title
	 * @return  void
	 */
	public function set_title($string = '') {
   		if (self::debug > 0) { error_log('learnpathItem::set_title()', 0); }
		if (!empty($string)) { $this->title = $string; }
	}

	/**
	 * Sets the item's type
	 * @param	string	Type
	 * @return  void
	 */
	public function set_type($string = '') {
   		if (self::debug > 0) { error_log('learnpathItem::set_type()', 0); }
		if (!empty($string)) { $this->type = $string; }
	}

	/**
	 * Checks if the current status is part of the list of status given
	 * @param	strings_array	An array of status to check for. If the current status is one of the strings, return true
	 * @return	boolean			True if the status was one of the given strings, false otherwise
	 */
	public function status_is($list = array()) {
   		if (self::debug > 1) { error_log('learnpathItem::status_is('.print_r($list,true).') on item '.$this->db_id, 0); }
		$mystatus = $this->get_status(true);
		if (empty($mystatus)) {
			return false;
		}
		$found = false;
		foreach ($list as $status) {
			if (preg_match('/^'.$status.'$/i', $mystatus)) {
				if (self::debug > 2) { error_log('New LP - learnpathItem::status_is() - Found status '.$status.' corresponding to current status', 0); }
				$found = true;
				return $found;
			}
		}
		if (self::debug > 2) { error_log('New LP - learnpathItem::status_is() - Status '.$mystatus.' did not match request', 0); }
		return $found;
	}

	/**
	 * Updates the time info according to the given session_time
	 * @param	integer	Time in seconds
	 * @return  void
	 * TODO: Make this method better by allowing better/multiple time slices.
	 */
	public function update_time($total_sec = 0) {
   		if (self::debug > 0) { error_log('learnpathItem::update_time('.$total_sec.')', 0); }
		if ($total_sec >= 0) {
	 		// Getting start time from finish time. The only problem in the calculation is it might be
	 		// modified by the scripts processing time.
	 		$now = time();
	 		$start = $now-$total_sec;
			$this->current_start_time = $start;
			$this->current_stop_time  = $now;
	 		/*if (empty($this->current_start_time)) {
	 			$this->current_start_time = $start;
	 			$this->current_stop_time  = $now;
	 		} else {
	 			//if ($this->current_stop_time != $this->current_start_time) {
		 			// If the stop time has already been set before to something else
		 			// than the start time, add the given time to what's already been
		 			// recorder.
		 			// This is the SCORM way of doing things, because the time comes from
		 			// core.session_time, not core.total_time
	 				// UPDATE: adding time to previous time is only done on SCORM's finish()
	 				// call, not normally, so for now ignore this section.
		 			//$this->current_stop_time = $this->current_stop_time + $stop;
		 			//error_log('New LP - Adding '.$stop.' seconds - now '.$this->current_stop_time, 0);
		 		//} else {
		 			// If no previous stop time set, use the one just calculated now from
		 			// start time.
		 			//$this->current_start_time = $start;
		 			//$this->current_stop_time  = $now;
		 			//error_log('New LP - Setting '.$stop.' seconds - now '.$this->current_stop_time, 0);
		 		//}
	 		}*/
		}
	}

	/**
     * Special scorm update time function. This function will update time directly into db for scorm objects
     **/
    public function scorm_update_time($total_sec = 0) {
        if (self::debug > 0) error_log('Funcion called: scorm_update_time');
        if (self::debug > 0) error_log("total_sec: $total_sec");

        //Step 1 : get actual total time stored in db
        $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
        $course_id = api_get_course_int_id();

        $get_view_sql = 'SELECT total_time, status FROM '.$item_view_table.'
                         WHERE c_id = '.$course_id.' AND lp_item_id="'.$this->db_id.'" AND lp_view_id="'.$this->view_id.'" AND view_count="'.$this->attempt_id.'" ;';
        $result=Database::query($get_view_sql);
        $row=Database::fetch_array($result);

        if (!isset($row['total_time'])) {
            $total_time = 0;
        } else {
            $total_time = $row['total_time'];
        }
        if (self::debug > 0) error_log("total_time: $total_time");

        //Step 2.1 : if normal mode total_time = total_time + total_sec
        if (api_get_setting('scorm_cumulative_session_time') != 'false'){
            $total_time += $total_sec;
            //$this->last_scorm_session_time = $total_sec;
        } else {
            //Step 2.2 : if not cumulative mode total_time = total_time - last_update + total_sec
            $total_time = $total_time - $this->last_scorm_session_time + $total_sec;
            $this->last_scorm_session_time = $total_sec;
        }
        //Step 3 update db only if status != completed, passed, browsed or seriousgamemode not activated
        $case_completed = array('completed','passed','browsed','failed'); //TODO COMPLETE
        if ($this->seriousgame_mode!=1 || !in_array($row['status'], $case_completed)) {
            $update_view_sql ="UPDATE $item_view_table SET total_time = '$total_time'
                               WHERE c_id = $course_id AND lp_item_id = {$this->db_id} AND lp_view_id = {$this->view_id} AND view_count = {$this->attempt_id}";
            if (self::debug > 0) error_log($update_view_sql);
            $result=Database::query($update_view_sql);
        }
    }

    /**
    * Set the total_time to 0 into db
    **/
    public function scorm_init_time(){
      $item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
      $course_id = api_get_course_int_id();
      $update_view_sql='UPDATE '.$item_view_table.' SET total_time = 0, start_time='.time().'
                        WHERE c_id = '.$course_id.' AND lp_item_id="'.$this->db_id.'" AND lp_view_id="'.$this->view_id.'" AND view_count="'.$this->attempt_id.'" ;';
      Database::query($update_view_sql);
    }

    /**
	 * Write objectives to DB. This method is separate from write_to_db() because otherwise
	 * objectives are lost as a side effect to AJAX and session concurrent access
	 * @return	boolean		True or false on error
	 */
	public function write_objectives_to_db() {
   		if (self::debug > 0) { error_log('learnpathItem::write_objectives_to_db()', 0); }
        $course_id = api_get_course_int_id();
	 	if (is_array($this->objectives) && count($this->objectives) > 0) {
	 		// Save objectives.
	 		$tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
	 		$sql = "SELECT id FROM $tbl " .
	 				"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
	 				"AND   lp_view_id = ".$this->view_id." " .
	 				"AND   view_count = ".$this->attempt_id;
	 		$res = Database::query($sql);
	 		if (Database::num_rows($res) > 0) {
	 			$row = Database::fetch_array($res);
	 			$lp_iv_id = $row[0];
	 			if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Got item_view_id '.$lp_iv_id.', now checking objectives ', 0); }
		 		foreach($this->objectives as $index => $objective){
		 			$iva_table = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
		 			$iva_sql = "SELECT id FROM $iva_table " .
		 					"WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id " .
		 					//"AND order_id = $index";
							//also check for the objective ID as it must be unique for this SCO view
		 					"AND objective_id = '".Database::escape_string($objective[0])."'";
		 			$iva_res = Database::query($iva_sql);
					// id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
		 			if(Database::num_rows($iva_res)>0){
		 				// Update (or don't).
		 				$iva_row = Database::fetch_array($iva_res);
		 				$iva_id = $iva_row[0];
		 				$ivau_sql = "UPDATE $iva_table " .
		 					"SET objective_id = '".Database::escape_string($objective[0])."'," .
		 					"status = '".Database::escape_string($objective[1])."'," .
		 					"score_raw = '".Database::escape_string($objective[2])."'," .
		 					"score_min = '".Database::escape_string($objective[4])."'," .
		 					"score_max = '".Database::escape_string($objective[3])."' " .
		 					"WHERE c_id = $course_id AND id = $iva_id";
		 				$ivau_res = Database::query($ivau_sql);
		 				//error_log($ivau_sql, 0);
		 			}else{
		 				// Insert new one.
		 				$ivai_sql = "INSERT INTO $iva_table " .
		 						"(c_id, lp_iv_id, order_id, objective_id, status, score_raw, score_min, score_max )" .
		 						"VALUES" .
		 						"($course_id, ".$lp_iv_id.", ".$index.",'".Database::escape_string($objective[0])."','".Database::escape_string($objective[1])."'," .
		 						"'".Database::escape_string($objective[2])."','".Database::escape_string($objective[4])."','".Database::escape_string($objective[3])."')";
		 				$ivai_res = Database::query($ivai_sql);
		 				//error_log($ivai_sql);
		 			}
		 		}
	 		}
	 	} else {
	 		//error_log('no objective to save: '.print_r($this->objectives, 1));
	 	}
	}

	/**
	 * Writes the current data to the database
	 * @return	boolean	Query result
	 */
	 public function write_to_db() {
         if (self::debug > 0) { error_log('learnpathItem::write_to_db()', 0); }

		// Check the session visibility.
		if (!api_is_allowed_to_session_edit()) {
            if (self::debug > 0) { error_log('return false api_is_allowed_to_session_edit'); }
			return false;
		}
		$course_id  = api_get_course_int_id();
   		$mode       = $this->get_lesson_mode();
   		$credit     = $this->get_credit();

   		$item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		$sql_verified = 'SELECT status FROM '.$item_view_table.'
		                 WHERE c_id = '.$course_id.' AND lp_item_id="'.$this->db_id.'" AND lp_view_id="'.$this->view_id.'" AND view_count="'.$this->get_attempt_id().'" ;';
		$rs_verified = Database::query($sql_verified);
		$row_verified = Database::fetch_array($rs_verified);

   		$my_case_completed = array('completed', 'passed', 'browsed', 'failed'); // Added by Isaac Flores.

        $save = true;

        if (isset($row_verified) && isset($row_verified['status'])) {
            if (in_array($row_verified['status'], $my_case_completed)) {
                $save = false;
            }
        }

   		if ((($save === false && $this->type == 'sco') ||
                ($this->type == 'sco' && ($credit == 'no-credit' OR $mode == 'review' OR $mode == 'browse'))
            ) && ($this->seriousgame_mode != 1 && $this->type == 'sco')
           ){
   			if (self::debug>1) {
                error_log("This info shouldn't be saved as the credit or lesson mode info prevent it");
                error_log('learnpathItem::write_to_db() - credit('.$credit.') or lesson_mode('.$mode.') prevent recording!',0);
            }
   		} else {
	  		// Check the row exists.
	  		$inserted = false;

	  		// This a special case for multiple attempts and Chamilo exercises.
	  		if ($this->type == 'quiz' && $this->get_prevent_reinit() == 0 && $this->get_status() == 'completed') {
	  			// We force the item to be restarted.
	  			$this->restart();

	  			$sql = "INSERT INTO $item_view_table " .
			 			"(c_id, total_time, " .
			 			"start_time, " .
			 			"score, " .
			 			"status, " .
			 			"max_score, ".
			 			"lp_item_id, " .
			 			"lp_view_id, " .
			 			"view_count, " .
			 			"suspend_data, " .
			 			//"max_time_allowed," .
			 			"lesson_location)" .
			 			"VALUES" .
			 			"($course_id, ".$this->get_total_time()."," .
			 			"".$this->current_start_time."," .
			 			"".$this->get_score()."," .
			 			"'".$this->get_status(false)."'," .
			 			"'".$this->get_max()."'," .
			 			"".$this->db_id."," .
			 			"".$this->view_id."," .
			 			"".$this->get_attempt_id()."," .
			 			"'".Database::escape_string($this->current_data)."'," .
			 			//"'".$this->get_max_time_allowed()."'," .
			 			"'".$this->lesson_location."')";
	  			if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Inserting into item_view forced: '.$sql, 0); }
			 	$res = Database::query($sql);
			 	$this->db_item_view_id = Database::insert_id();
			 	$inserted = true;
	  		}

		 	$item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		 	$check = "SELECT * FROM $item_view_table " .
		 			"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id. " " .
		 			"AND   lp_view_id = ".$this->view_id. " ".
		 			"AND   view_count = ".$this->get_attempt_id();
		 	if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Querying item_view: '.$check, 0); }
		 	$check_res = Database::query($check);
		 	// Depending on what we want (really), we'll update or insert a new row
		 	// now save into DB.
		 	$res = 0;
		 	if (!$inserted && Database::num_rows($check_res) < 1) {
			 	$sql = "INSERT INTO $item_view_table " .
			 			"(c_id, total_time, " .
			 			"start_time, " .
			 			"score, " .
			 			"status, " .
			 			"max_score, ".
			 			"lp_item_id, " .
			 			"lp_view_id, " .
			 			"view_count, " .
			 			"suspend_data, " .
			 			//"max_time_allowed," .
			 			"lesson_location)" .
			 			"VALUES" .
			 			"($course_id, ".$this->get_total_time()."," .
			 			"".$this->current_start_time."," .
			 			"".$this->get_score()."," .
			 			"'".$this->get_status(false)."'," .
			 			"'".$this->get_max()."'," .
			 			"".$this->db_id."," .
			 			"".$this->view_id."," .
			 			"".$this->get_attempt_id()."," .
			 			"'".Database::escape_string($this->current_data)."'," .
			 			//"'".$this->get_max_time_allowed()."'," .
			 			"'".$this->lesson_location."')";
			 	if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Inserting into item_view: '.$sql, 0); }
			 	$res = Database::query($sql);
			 	$this->db_item_view_id = Database::insert_id();
		 	} else {
		 		$sql = '';
		 		if ($this->type == 'hotpotatoes') {
				 	$sql = "UPDATE $item_view_table " .
				 			"SET total_time = ".$this->get_total_time().", " .
				 			" start_time = ".$this->get_current_start_time().", " .
				 			" score = ".$this->get_score().", " .
				 			" status = '".$this->get_status(false)."'," .
				 			" max_score = '".$this->get_max()."'," .
				 			" suspend_data = '".Database::escape_string($this->current_data)."'," .
				 			" lesson_location = '".$this->lesson_location."' " .
				 			"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
				 			"AND lp_view_id = ".$this->view_id." " .
				 			"AND view_count = ".$this->get_attempt_id();
		 		} else {
		 			// For all other content types...
		 			if ($this->type == 'quiz') {
		 				$my_status = ' ';
		 				$total_time = ' ';
		 				if (!empty($_REQUEST['exeId'])) {
							$TBL_TRACK_EXERCICES = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);

							$safe_exe_id = Database::escape_string($_REQUEST['exeId']);
			 				$sql = 'SELECT start_date,exe_date FROM ' . $TBL_TRACK_EXERCICES . ' WHERE exe_id = '.(int)$safe_exe_id;
							$res = Database::query($sql);
							$row_dates = Database::fetch_array($res);

							$time_start_date = convert_sql_date($row_dates['start_date']);
							$time_exe_date 	 = convert_sql_date($row_dates['exe_date']);
							$mytime = ((int)$time_exe_date-(int)$time_start_date);
							$total_time =" total_time = ".$mytime.", ";
		 				}
		 			} else {
		 				$my_type_lp = learnpath::get_type_static($this->lp_id);
		 				// This is a array containing values finished
		 				$case_completed = array('completed', 'passed', 'browsed', 'failed');

	     				//is not multiple attempts
                        if ($this->seriousgame_mode==1 && $this->type=='sco') {
                          $total_time =" total_time = total_time +".$this->get_total_time().", ";
                          $my_status = " status = '".$this->get_status(false)."' ,";
                        } elseif ($this->get_prevent_reinit()==1) {
                              // Process of status verified into data base.
                              $sql_verified = 'SELECT status FROM '.$item_view_table.' WHERE c_id = '.$course_id.' AND lp_item_id="'.$this->db_id.'" AND lp_view_id="'.$this->view_id.'" AND view_count="'.$this->get_attempt_id().'" ;';
                              $rs_verified = Database::query($sql_verified);
                              $row_verified = Database::fetch_array($rs_verified);

                              // Get type lp: 1=lp dokeos and  2=scorm.
                              // If not is completed or passed or browsed and learning path is scorm.
                              if (!in_array($this->get_status(false), $case_completed) && $my_type_lp == 2 ) { //&& $this->type!='dir'
                                  $total_time =" total_time = total_time +".$this->get_total_time().", ";
                                  $my_status = " status = '".$this->get_status(false)."' ,";
                              } else {
                                  // Verified into data base.
                                  if (!in_array($row_verified['status'], $case_completed) && $my_type_lp == 2 ) { //&& $this->type!='dir'
                                      $total_time =" total_time = total_time +".$this->get_total_time().", ";
                                      $my_status = " status = '".$this->get_status(false)."' ,";
                                  } elseif (in_array($row_verified['status'], $case_completed) && $my_type_lp == 2 && $this->type != 'sco' ) { //&& $this->type!='dir'
                                      $total_time =" total_time = total_time +".$this->get_total_time().", ";
                                      $my_status = " status = '".$this->get_status(false)."' ,";
                                  } else {
                                  //&& !in_array($row_verified['status'], $case_completed)
                                  //is lp dokeos
                                      if ($my_type_lp == 1 && $this->type != 'chapter') {
                                          $total_time = " total_time = total_time + ".$this->get_total_time().", ";
                                          $my_status = " status = '".$this->get_status(false)."' ,";
                                      }
                                  }
                              }
                        } else {
                            // Multiple attempts are allowed.
                            if (in_array($this->get_status(false), $case_completed) &&  $my_type_lp == 2) {
                                // Reset zero new attempt ?
                                $my_status = " status = '".$this->get_status(false)."' ,";
                            }  elseif (!in_array($this->get_status(false), $case_completed) && $my_type_lp == 2) {
                                $total_time =" total_time = ".$this->get_total_time().", ";
                                $my_status = " status = '".$this->get_status(false)."' ,";
                            } else {
                                // It is dokeos LP.
                                $total_time =" total_time = total_time +".$this->get_total_time().", ";
                                $my_status = " status = '".$this->get_status(false)."' ,";
                            }

                            // Code added by Isaac Flores.
                            // This code line fixes the problem of wrong status.
                            if ($my_type_lp == 2) {
                                // Verify current status in multiples attempts.
                                $sql_status = 'SELECT status FROM '.$item_view_table.' WHERE c_id = '.$course_id.' AND lp_item_id="'.$this->db_id.'" AND lp_view_id="'.$this->view_id.'" AND view_count="'.$this->get_attempt_id().'" ';
                                $rs_status = Database::query($sql_status);
                                $current_status = Database::result($rs_status, 0, 'status');
                                if (in_array($current_status, $case_completed)) {
                                    $my_status = '';
                                    $total_time = '';
                                } else {
                                    $total_time = " total_time = total_time +".$this->get_total_time().", ";
                                }
                            }
                        }
                    }

                    if ($this->type == 'sco') { //IF scorm scorm_update_time has already updated total_tim in db
                        $sql = "UPDATE $item_view_table " .
                                " SET ".//start_time = ".$this->get_current_start_time().", " . //scorm_init_time does it
                                " score = ".$this->get_score().", " .
                                $my_status.
                                " max_score = '".$this->get_max()."'," .
                                " suspend_data = '".Database::escape_string($this->current_data)."'," .
                                //" max_time_allowed = '".$this->get_max_time_allowed()."'," .
                                " lesson_location = '".$this->lesson_location."' " .
                                "WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
                                "AND lp_view_id = ".$this->view_id." " .
                                "AND view_count = ".$this->get_attempt_id();
                    } else {
                        $sql = "UPDATE $item_view_table " .
                                "SET " .$total_time.
                                " start_time = ".$this->get_current_start_time().", " .
                                " score = ".$this->get_score().", " .
                                $my_status.
                                " max_score = '".$this->get_max()."'," .
                                " suspend_data = '".Database::escape_string($this->current_data)."'," .
                                //" max_time_allowed = '".$this->get_max_time_allowed()."'," .
                                " lesson_location = '".$this->lesson_location."' " .
                                "WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
                                "AND lp_view_id = ".$this->view_id." " .
                                "AND view_count = ".$this->get_attempt_id();
                    }
                    $this->current_start_time = time();
                }
                if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Updating item_view: '.$sql, 0); }
                $res = Database::query($sql);
            }

		 	if (is_array($this->interactions) && count($this->interactions) > 0) {
		 		// Save interactions.
		 		$tbl = Database::get_course_table(TABLE_LP_ITEM_VIEW);
		 		$sql = "SELECT id FROM $tbl " .
		 				"WHERE c_id = $course_id AND lp_item_id = ".$this->db_id." " .
		 				"AND   lp_view_id = ".$this->view_id." " .
		 				"AND   view_count = ".$this->get_attempt_id();
		 		$res = Database::query($sql);
		 		if (Database::num_rows($res) > 0) {
		 			$row = Database::fetch_array($res);
		 			$lp_iv_id = $row[0];
		 			if (self::debug > 2) { error_log('learnpathItem::write_to_db() - Got item_view_id '.$lp_iv_id.', now checking interactions ', 0); }
			 		foreach ($this->interactions as $index => $interaction) {
			 			$correct_resp = '';
			 			if (is_array($interaction[4]) && !empty($interaction[4][0])) {
			 				foreach ($interaction[4] as $resp) {
			 					$correct_resp .= $resp.',';
			 				}
			 				$correct_resp = substr($correct_resp, 0, strlen($correct_resp) - 1);
			 			}
			 			$iva_table = Database::get_course_table(TABLE_LP_IV_INTERACTION);
			 			$iva_sql = "SELECT id FROM $iva_table " .
			 					"WHERE c_id = $course_id AND lp_iv_id = $lp_iv_id " .
//			 					"AND order_id = $index";
								//also check for the interaction ID as it must be unique for this SCO view
			 					"AND (order_id = $index " .
			 					"OR interaction_id = '".Database::escape_string($interaction[0])."')";
			 			$iva_res = Database::query($iva_sql);
						// id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
			 			if (Database::num_rows($iva_res) > 0) {
			 				// Update (or don't).
			 				$iva_row = Database::fetch_array($iva_res);
			 				$iva_id = $iva_row[0];
			 				$ivau_sql = "UPDATE $iva_table " .
			 					"SET interaction_id = '".Database::escape_string($interaction[0])."'," .
			 					"interaction_type = '".Database::escape_string($interaction[1])."'," .
			 					"weighting = '".Database::escape_string($interaction[3])."'," .
			 					"completion_time = '".Database::escape_string($interaction[2])."'," .
			 					"correct_responses = '".Database::escape_string($correct_resp)."'," .
			 					"student_response = '".Database::escape_string($interaction[5])."'," .
			 					"result = '".Database::escape_string($interaction[6])."'," .
			 					"latency = '".Database::escape_string($interaction[7])."'" .
			 					"WHERE c_id = $course_id AND id = $iva_id";
			 				Database::query($ivau_sql);
			 			} else {
			 				// Insert new one.
			 				$ivai_sql = "INSERT INTO $iva_table (c_id, order_id, lp_iv_id, interaction_id, interaction_type, " .
			 						"weighting, completion_time, correct_responses, " .
			 						"student_response, result, latency)" .
			 						"VALUES" .
			 						"($course_id, ".$index.",".$lp_iv_id.",'".Database::escape_string($interaction[0])."','".Database::escape_string($interaction[1])."'," .
			 						"'".Database::escape_string($interaction[3])."','".Database::escape_string($interaction[2])."','".Database::escape_string($correct_resp)."'," .
			 						"'".Database::escape_string($interaction[5])."','".Database::escape_string($interaction[6])."','".Database::escape_string($interaction[7])."'" .
			 						")";
			 				Database::query($ivai_sql);
			 			}
			 		}
		 		}
		 	}
   		}
		if (self::debug > 2) { error_log('End of learnpathItem::write_to_db()', 0); }
	 	return true;
	 }

     function add_audio() {
        $course_info = api_get_course_info();
        $filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document/';

        if (!is_dir($filepath.'audio')) {
            mkdir($filepath.'audio', api_get_permissions_for_new_directories());
            $audio_id = add_document($course_info, '/audio', 'folder', 0, 'audio');
            api_item_property_update($course_info, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id(), null, null, null, null, api_get_session_id());
			api_item_property_update($course_info, TOOL_DOCUMENT, $audio_id, 'invisible', api_get_user_id(), null, null, null, null, api_get_session_id());
        }

        $key = 'file';

        if (!isset($_FILES[$key]['name']) || !isset($_FILES[$key]['tmp_name'])) {
            return false;
        }
        $result = DocumentManager::upload_document($_FILES, '/audio', null, null, 0, 'rename', false, false);
        $file_path = null;

        if ($result) {
            $file_path = basename($result['path']);

            // Store the mp3 file in the lp_item table.
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
            $sql_insert_audio = "UPDATE $tbl_lp_item SET audio = '".Database::escape_string($file_path)."'
                                WHERE c_id = {$course_info['real_id']} AND id = '".Database::escape_string($this->db_id)."'";
            Database::query($sql_insert_audio);
        }
        return $file_path;
    }

     function add_audio_from_documents($doc_id) {
        $course_info = api_get_course_info();
        $document_data = DocumentManager::get_document_data_by_id($doc_id, $course_info['code']);

        if (!empty($document_data)) {
            $file_path = basename($document_data['path']);
            // Store the mp3 file in the lp_item table.
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
            $sql_insert_audio = "UPDATE $tbl_lp_item SET audio = '".Database::escape_string($file_path)."'
                                 WHERE c_id = {$course_info['real_id']} AND id = '".Database::escape_string($this->db_id)."'";
            Database::query($sql_insert_audio);
        }
        return $file_path;
    }

    function remove_audio() {
        $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
        $course_id = api_get_course_int_id();
        if (empty($this->db_id)) {
            return false;
        }
        $sql 	= "UPDATE $tbl_lp_item SET audio = '' WHERE c_id = $course_id AND id IN (".$this->db_id.")";
        Database::query($sql);
    }

    static function humanize_status($status, $decorate = true) {
        $mylanglist = array(
            'completed' => 'ScormCompstatus',
            'incomplete' => 'ScormIncomplete',
            'failed' => 'ScormFailed',
            'passed' => 'ScormPassed',
            'browsed' => 'ScormBrowsed',
            'not attempted' => 'ScormNotAttempted'
        );

        $my_lesson_status = get_lang($mylanglist[$status]);

        switch ($status) {
            case 'completed':
            case 'browsed':
                $class_status = 'info';
                break;
            case 'incomplete':
                $class_status = 'warning';
                break;
            case 'passed':
                $class_status = 'success';
                break;
            case 'failed':
                $class_status = 'important';
                break;
            default:
                $class_status = 'default';
                break;
        }
        if ($decorate) {
            return Display::label($my_lesson_status, $class_status);
        } else {
            return $my_lesson_status;
        }
    }
}
learnpathList.class.php000064400000017402152003363470011202 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * File containing the declaration of the learnpathList class.
 * @package	chamilo.learnpath
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * This class is only a learning path list container with several practical methods for sorting the list and
 * provide links to specific paths
 * @uses	Database.lib.php to use the database
 * @uses	learnpath.class.php to generate learnpath objects to get in the list
 */
class learnpathList {
    public $list = array(); // Holds a flat list of learnpaths data from the database.
    public $ref_list = array(); // Holds a list of references to the learnpaths objects (only filled by get_refs()).
    public $alpha_list = array(); // Holds a flat list of learnpaths sorted by alphabetical name order.
    public $course_code;
    public $user_id;
    public $refs_active = false;

    /**
     * This method is the constructor for the learnpathList. It gets a list of available learning paths from
     * the database and creates the learnpath objects. This list depends on the user that is connected
     * (only displays) items if he has enough permissions to view them.
     * @param	integer		User ID
     * @param	string		Optional course code (otherwise we use api_get_course_id())
     * @param	int			Optional session id (otherwise we use api_get_session_id())
     * @return	void
     */
    function __construct($user_id, $course_code = '', $session_id = null, $order_by = null, $check_publication_dates = false) {
        $course_info = api_get_course_info($course_code);
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
        $tbl_tool = Database::get_course_table(TABLE_TOOL_LIST);
                
        $this->course_code = $course_code;        
        $this->user_id = $user_id;
        
        $course_id = $course_info['real_id'];
        
        if (empty($course_id)) {
            return false;
        }

        // Condition for the session.
        if (isset($session_id)) {
            $session_id = intval($session_id);
        } else {
            $session_id = api_get_session_id();
        }
        $condition_session = api_get_session_condition($session_id, true, true);
        $order = "ORDER BY display_order ASC, name ASC";
        if (isset($order_by)) {
           $order = Database::parse_conditions(array('order'=>$order_by));           
        }
        $now = api_get_utc_datetime();
        $time_conditions = '';
        
        if ($check_publication_dates) {
            $time_conditions = " AND ( (publicated_on <> '0000-00-00 00:00:00' AND publicated_on < '$now'  AND expired_on <> '0000-00-00 00:00:00'  AND expired_on > '$now' )  OR 
                        (publicated_on <> '0000-00-00 00:00:00'  AND publicated_on < '$now'  AND expired_on = '0000-00-00 00:00:00') OR
                        (publicated_on = '0000-00-00 00:00:00'   AND expired_on <> '0000-00-00 00:00:00' AND expired_on > '$now') OR                        
                        (publicated_on = '0000-00-00 00:00:00'   AND expired_on = '0000-00-00 00:00:00' )) 
            ";
        }
        
        $sql = "SELECT * FROM $lp_table WHERE c_id = $course_id $time_conditions $condition_session $order";
        
        $res = Database::query($sql);
        $names = array();
        while ($row = Database::fetch_array($res,'ASSOC')) {
            // Check if published.
            $pub = '';            
            // Use domesticate here instead of Database::escape_string because
            // it prevents ' to be slashed and the input (done by learnpath.class.php::toggle_visibility())
            // is done using domesticate()
            $myname = domesticate($row['name']);
            $mylink = 'newscorm/lp_controller.php?action=view&lp_id='.$row['id'].'&id_session='.$session_id;
            $sql2="SELECT * FROM $tbl_tool WHERE c_id = $course_id AND (name='$myname' and image='scormbuilder.gif' and link LIKE '$mylink%')";
            //error_log('New LP - learnpathList::__construct - getting visibility - '.$sql2, 0);
            $res2 = Database::query($sql2);
            if (Database::num_rows($res2) > 0) {
                $row2 = Database::fetch_array($res2);
                $pub = $row2['visibility'];
            } else {
                $pub = 'i';
            }
            // Check if visible.
            $vis = api_get_item_visibility(api_get_course_info($course_code), 'learnpath', $row['id'], $session_id);
            
            if (!empty($row['created_on']) && $row['created_on'] != '0000-00-00 00:00:00') {
            	$row['created_on'] = $row['created_on'];
            } else {
            	$row['created_on'] = '';
            }
            if (!empty($row['modified_on']) && $row['modified_on'] != '0000-00-00 00:00:00') {
                $row['modified_on'] = $row['modified_on'];
            } else {
                $row['modified_on'] = '';
            }
            
            if (!empty($row['publicated_on']) && $row['publicated_on'] != '0000-00-00 00:00:00') {
                $row['publicated_on'] = $row['publicated_on'];
            } else {
                $row['publicated_on'] = '';
            }
            
            if (!empty($row['expired_on']) && $row['expired_on'] != '0000-00-00 00:00:00') {
                $row['expired_on'] = $row['expired_on'];
            } else {
                $row['expired_on'] = '';
            }
            $this->list[$row['id']] = array(
                'lp_type'           => $row['lp_type'],
                'lp_session'        => $row['session_id'],
                'lp_name'           => stripslashes($row['name']),
                'lp_desc'           => stripslashes($row['description']),
                'lp_path'           => $row['path'],
                'lp_view_mode'      => $row['default_view_mod'],
                'lp_force_commit'   => $row['force_commit'],
                'lp_maker'	        => stripslashes($row['content_maker']),
                'lp_proximity'      => $row['content_local'],
                //'lp_encoding'     => $row['default_encoding'],
                'lp_encoding'       => api_get_system_encoding(),  // Chamilo 1.8.8: We intend always to use the system encoding.
                'lp_visibility'     => $vis,
                'lp_published'	    => $pub,
                'lp_prevent_reinit' => $row['prevent_reinit'],
          			'seriousgame_mode' => $row['seriousgame_mode'],
                'lp_scorm_debug'    => $row['debug'],
                'lp_display_order'  => $row['display_order'],
                'lp_preview_image'  => stripslashes($row['preview_image']),
                'autolaunch'        => $row['autolunch'],
                'session_id'        => $row['session_id'],
                'created_on'        => $row['created_on'],
                'modified_on'       => $row['modified_on'],
                'publicated_on'     => $row['publicated_on'],
                'expired_on'        => $row['expired_on']
                );
            $names[$row['name']] = $row['id'];
           }
           $this->alpha_list = asort($names);
    }

    /**
     * Gets references to learnpaths for all learnpaths IDs kept in the local list.
     * This applies a transformation internally on list and ref_list and returns a copy of the refs list
     * @return	array	List of references to learnpath objects
     */
    function get_refs() {
        foreach ($this->list as $id => $dummy) {
            $this->ref_list[$id] = new learnpath($this->course_code, $id, $this->user_id);
        }
        $this->refs_active = true;
        return $this->ref_list;
    }

    /**
     * Gets a table of the different learnpaths we have at the moment
     * @return	array	Learnpath info as [lp_id] => ([lp_type]=> ..., [lp_name]=>...,[lp_desc]=>...,[lp_path]=>...)
     */
    function get_flat_list() {
        return $this->list;
    }
}
learnpath_functions.inc.php000064400000313172152003363470012105 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This is a function library for the learning path.
 *
 * Due to the face that the learning path has been built upon the resoucelinker,
 * naming conventions have changed at least 2 times. You can see here in order the :
 * 1. name used in the first version of the resourcelinker
 * 2. name used in the first version of the LP
 * 3. name used in the second (current) version of the LP
 *
 *       1.       2.        3.
 *   Category = Chapter = Module
 *   Item (?) = Item    = Step
 *
 * @author  Denes Nagy <darkden@evk.bke.hu>, main author
 * @author  Roan Embrechts, some code cleaning
 * @author  Yannick Warnier <yannick.warnier@beeznest.com>, multi-level learnpath behaviour + new SCORM tool
 * @access  public
 * @package chamilo.learnpath
 * @todo rename functions to coding conventions: not deleteitem but delete_item, etc
 * @todo rewrite functions to comply with phpDocumentor
 * @todo remove code duplication
 */


use \ChamiloSession as Session;

/**
 * This function deletes an item
 * @param integer 	$id: the item we want to delete
 * @return boolean	True if item was deleted, false if not found or error
 */
function deleteitem($id) {
    
    $course_id = api_get_course_int_id();
    
    $tbl_learnpath_item     = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter  = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    // Get the display order for this item before it is deleted.
    $sql = "SELECT display_order, parent_item_id FROM $tbl_lp_item WHERE c_id = $course_id AND id=$id";
    $result = Database::query($sql);
    if (Database::num_rows($result) == 0) {
        return false;
    }
    $row = Database::fetch_row($result);
    $display_order = $row[0];
    $parent_item_id = $row[1];
    // Delete the item.
    $sql = "DELETE FROM $tbl_learnpath_item WHERE c_id = $course_id AND id='$id'";
    $result = Database::query($sql);
    if ($result === false) {
        return false;
    }
    // Update the other items and chapters.
    $sql = "UPDATE $tbl_learnpath_item SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_item_id";
    $result = Database::query($sql);
    $sql = "UPDATE $tbl_learnpath_chapter SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_item_id";
    $result = Database::query($sql);

    return true;
}

/**
 * This function deletes a module(chapter) and all its steps(items).
 *
 * @param integer id of the chapter we want to delete
 * @return boolean	True on success and false if not found or error
 */
function deletemodule($parent_item_id) {
    global $learnpath_id;
    $course_id = api_get_course_int_id();
    $tbl_learnpath_item 	= Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter 	= Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);

    // Added for multi-level behaviour - slightly recursive.
    $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id=$learnpath_id";
    $result = Database::query($sql);
    while ($row = Database::fetch_array($result)) {
        if ($row['parent_item_id'] == $parent_item_id) {
            // Delete every subchapter.
            if (deletemodule($row['id']) === false) {
                return false;
            }
        }
    }

    // Get this chapter's display order.
    $sql = "SELECT display_order, parent_item_id FROM $tbl_learnpath_chapter 
    		WHERE c_id = $course_id AND id=$parent_item_id and lp_id=$learnpath_id";
    $result = Database::query($sql);
    if (Database::num_rows($result) == 0) {
        return false;
    }
    $row = Database::fetch_row($result);

    $display_order = $row[0];
    $parent_id = $row[1];

    // Delete the chapter itself.
    $sql = "DELETE FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (id=$parent_item_id and lp_id=$learnpath_id)";
    $result = Database::query($sql);
    // Delete items from that chapter.
    $sql2 = "DELETE FROM $tbl_learnpath_item WHERE c_id = $course_id AND parent_item_id=$parent_item_id";
    $result = Database::query($sql2);

    // Update all other chapters accordingly.
    $sql = "UPDATE $tbl_learnpath_item SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_id";
    $result = Database::query($sql);
    $sql = "UPDATE $tbl_learnpath_chapter SET display_order = display_order-1 WHERE c_id = $course_id AND display_order > $display_order AND parent_item_id = $parent_id";
    $result = Database::query($sql);

    return true;
}

/**
 * This function deletes an entire path.
 *
 * @param integer 	$id: the path we want to delete
 * @return	void
 */
function deletepath($path_id) {
    $tbl_learnpath_main = Database :: get_course_table(TABLE_LEARNPATH_MAIN);
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    
    $course_id = api_get_course_int_id();

    $sql = "DELETE FROM $tbl_learnpath_main WHERE c_id = $course_id AND lp_id='$path_id'";
    $result = Database::query($sql);

    //@TODO check how this function is used before uncommenting the following
    //also delete all elements inside that path
    $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id = $path_id";
    $result = Database::query($sql);
    while ($row = Database::fetch_array($result)) {
        deletemodule($row['id']);
    }
}

/**
 * This function moves an item.
 *
 * @param string    $direction: move the given chapter up or down
 * @param integer   Item ID
 * @param integer   $moduleid: the id of the chapter the element resides in
 * @return	boolean	Returns false on error
 * @note    With this new version, the moveitem deals with items AND directories (not the base-level modules). This is a lot more complicated but is a temporary step towards new database structure as 'everything is an item'
 */
function moveitem($direction, $id, $moduleid, $type = 'item') {
    global $learnpath_id;
    $course_id = api_get_course_int_id();
    
    $tbl_learnpath_item     = Database::get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter  = Database::get_course_table(TABLE_LEARNPATH_CHAPTER);

    $tree = get_learnpath_tree($learnpath_id);
    $orig_order = 0;
    $orig_type = '';
    $orig_id = $id;
    foreach ($tree[$moduleid] as $row) {
        // If this is the element we want (be it a chapter or an item), get its data.
        if (($row['id'] == $id) && ($row['type'] == $type)) {
            $orig_order = $row['display_order'];
            $orig_type = $row['type'];
            break;
        }
    }

    $dest_order = 0;
    $dest_type = '';
    $dest_id = 0;
    if ($direction == 'up') {
        if (!empty ($tree[$moduleid][$orig_order - 1])) {
            $dest_order = $orig_order - 1;
            $dest_type = $tree[$moduleid][$orig_order - 1]['type'];
            $dest_id = $tree[$moduleid][$orig_order - 1]['id'];
        } else {
            return false;
        }
    } else {
        // Move down.
        if (!empty ($tree[$moduleid][$orig_order + 1])) {
            $dest_order = $orig_order + 1;
            $dest_type = $tree[$moduleid][$orig_order + 1]['type'];
            $dest_id = $tree[$moduleid][$orig_order + 1]['id'];
        } else {
            return false;
        }
    }

    $sql1 = '';
    $sql2 = '';
    if ($orig_type == 'chapter') {
        $sql1 = "UPDATE $tbl_learnpath_chapter SET display_order = ".$dest_order." WHERE c_id = $course_id AND (id=$orig_id and parent_item_id=$moduleid)";
    } elseif ($orig_type == 'item') {
        $sql1 = "UPDATE $tbl_learnpath_item SET display_order = ".$dest_order." WHERE c_id = $course_id AND (id=$orig_id and parent_item_id=$moduleid)";
    } else {
        return false;
    }

    if ($dest_type == 'chapter') {
        $sql2 = "UPDATE $tbl_learnpath_chapter SET display_order = ".$orig_order." WHERE c_id = $course_id AND (id='$dest_id' and parent_item_id=$moduleid)";
    } elseif ($dest_type == 'item') {
        $sql2 = "UPDATE $tbl_learnpath_item SET display_order = ".$orig_order." WHERE c_id = $course_id AND (id='$dest_id' and parent_item_id=$moduleid)";
    } else {
        return false;
    }
    Database::query($sql1);
    Database::query($sql2);
}

/**
 * This function moves a module (also called chapter or category).
 *
 * @param   string $direction: move the given chapter up or down
 * @param   integer $id: the id of the chapter we want to move
 * @return	void
 */
function movemodule($direction, $id) {
    global $learnpath_id;
    $course_id = api_get_course_int_id();
    
    $tbl_learnpath_chapter  = Database::get_course_table(TABLE_LEARNPATH_CHAPTER);
    if ($direction == 'up') {
        $sortDirection = 'DESC';
    } else {
        $sortDirection = 'ASC';
    }

    // Select all chapters of first level (parent_item_id = 0).
    $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (lp_id=$learnpath_id AND parent_item_id = 0) ORDER BY display_order $sortDirection";
    $result = Database::query($sql);
    $previousrow = '';

    // See similar comment in moveitem() function.
    // @TODO: this only works for chapters in multi-level mode. Why not gather
    // this function and moveitem to keep only one multi-uses function?
    while ($row = Database::fetch_array($result)) {
        // Step 2: Performing the move (only happens when passed trhough step 1 at least once).
        if (!empty ($this_cat_order)) {
            $next_cat_order = $row['display_order'];
            $next_cat_id = $row['id'];

            $sql1 = "UPDATE $tbl_learnpath_chapter SET display_order = '$next_cat_order' WHERE c_id = $course_id AND (id='$this_cat_id' and lp_id=$learnpath_id)";
            $sql2 = "UPDATE $tbl_learnpath_chapter SET display_order = '$this_cat_order' WHERE c_id = $course_id AND (id='$next_cat_id' and lp_id=$learnpath_id)";
            Database::query($sql1);
            Database::query($sql2);
            unset ($this_cat_order);
            unset ($next_cat_order);
            unset ($next_cat_id);
            break;
        }

        // Step 1: Looking for the order of the row we want to move.
        if ($row['id'] == $id) {
            $this_cat_order = $row['display_order'];
            $this_cat_id = $id;
        }
    }
}

/**
 * Inserts a new element in a learnpath table (item or chapter)
 * @param		string	Element type ('chapter' or 'item')
 * @param		string	Chapter name
 * @param		string	Chapter description (optional)
 * @param		integer	Parent chapter ID (default: 0)
 * @param		integer Learnpath ID
 * @param		mixed		If type 'item', then array(prereq_id=>value, prereq_..)
 * @return	integer	The new chapter ID, or false on failure
 * @TODO	Finish this function before it is used. Currently only chapters can be added using it.
 * @note This function is currently never used!
 */
function insert_item($type = 'item', $name, $chapter_description = '', $parent_id = 0, $learnpath_id = 0, $params = null) {
    $tbl_learnpath_chapter	= Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    $tbl_learnpath_item 	= Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    
    $course_id = api_get_course_int_id();

    // Getting the last order number from the chapters table, in this learnpath, for the parent chapter given.
    $sql = "SELECT * FROM $tbl_learnpath_chapter
            WHERE c_id = $course_id AND lp_id=$learnpath_id  AND parent_item_id = $parent_id
            ORDER BY display_order DESC";
    $result = Database::query($sql);
    $row = Database::fetch_array($result);
    $last_chapter_order = $row['display_order'];

    // Getting the last order number of the items.
    $sql = "SELECT * FROM $tbl_learnpath_item
            WHERE c_id = $course_id AND parent_item_id = $parent_id
            ORDER BY display_order DESC";
    $result = Database::query($sql);
    $row = Database::fetch_array($result);
    $last_item_order = $row['display_order'];
    $new_order = max($last_chapter_order, $last_item_order) + 1;
	
    if ($type === 'chapter') {
        $sql = "INSERT INTO $tbl_learnpath_chapter (c_id, lp_id, chapter_name, chapter_description, display_order)
				VALUES ( $course_id, 
						'".domesticate($learnpath_id)."',
                        '".domesticate(htmlspecialchars($name))."',
                        '".domesticate(htmlspecialchars($chapter_description))."',
                        $new_order )";
        $result = Database::query($sql);
        if ($result === false) {
            return false;
        }
        $id = Database :: insert_id();
    } elseif ($type === 'item') {
        $sql = "INSERT INTO $tbl_learnpath_item (c_id, parent_item_id, item_type, display_order) VALUES 
        		($course_id, '".domesticate($parent_id)."','".domesticate(htmlspecialchars($type))."', $new_order )";
        $result = Database::query($sql);
        if ($result === false) {
            return false;
        }
        $id = Database :: insert_id();
    }
    return $id;
}

/**
 * This function returns an array with all the learnpath categories/chapters
 * @return array List of learnpath chapter titles
 */
function array_learnpath_categories() {
    $course_id = api_get_course_int_id();    
    global $learnpath_id;
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);

    $sql = "SELECT * FROM  $tbl_learnpath_chapter  WHERE c_id = $course_id AND (lp_id=$learnpath_id) ORDER BY display_order ASC";
    $result = Database::query($sql);

    while ($row = Database::fetch_array($result)) {
        $array_learnpath_categories[] = array($row['id'], $row['chapter_name']);
    }
    //$array_learnpath_categories = array($array_learnpath_categories_name, $array_learnpath_categories_id);
    return $array_learnpath_categories;
}

/**
* Displays the learnpath chapters(=modules,categories) and their contents.
* @param    integer     Chapter ID to display now (enables recursive behaviour)
* @param    array       The array as returned by get_learnpath_tree, with all the elements of a learnpath compiled and structured into the array, by chapter id
* @param    integer     Level (the depth of the call - helps in display)
* @todo eliminate all global $lang declarations, use get_lang, improve structure.
* @author   Denes Nagy
* @author   Roan Embrechts
* @author   Yannick Warnier <yannick.warnier@beeznest.com> - complete redesign for multi-level learnpath chapters
*/
function display_learnpath_chapters($parent_item_id = 0, $tree = array (), $level = 0) {
    //error_log('New LP - In learnpath_functions::display_learnpath_chapters', 0);
    global $color2;
    global $xml_output;
    global $learnpath_id;
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

    // @todo: coding standards: Language variables are CaMMelCaSe, all other variables should use the underscoring method.
    $lg_move_down = get_lang('LearnpathMoveDown');
    $lg_move_up = get_lang('LearnpathMoveUp');
    $lg_edit_learnpath_module = get_lang('LearnpathEditModule');
    $lg_delete_learnpath_module = get_lang('LearnpathDeleteModule');
    $lg_nochapters = get_lang('LearnpathNoChapters');
    $lg_prerequisites = get_lang('LearnpathPrerequisites');
    $lg_prerequisites_limit = get_lang('LearnpathPrerequisitesLimit');
    $lg_add_learnpath_item = get_lang('LearnpathAddItem');
    $lg_title_and_desc = get_lang('LearnpathTitleAndDesc');
    $lg_change_order = get_lang('LearnpathChangeOrder');
    $lg_add_prereqi = get_lang('LearnpathAddPrereqi');
    $lg_add_title_and_desc = get_lang('LearnpathAddTitleAndDesc');
    $lg_delete = get_lang('Delete');

    if ($parent_item_id === 0) {

        // This is the first time we use the function, define the tree and display learnpath name.
        $tree = get_learnpath_tree($learnpath_id);

        $num_modules = count($tree);
        //$num_modules = Database::num_rows($result);
        if ($num_modules == 0) {
            // do not diplay useless information
            //echo "<tr><td>&nbsp;$lg_nochapters</td></tr>";
        } else {
            echo "  <tr align='center' valign='top'><td><b>&nbsp;$lg_title_and_desc </b></td>\n"."    <td><b>&nbsp;$lg_add_learnpath_item </b></td>\n";
            if (is_prereq($learnpath_id)) {
                echo "    <td bgcolor='#ddddee'><b>&nbsp;$lg_prerequisites_limit </b></td>\n";
            } else {
                echo "    <td><b>&nbsp;$lg_prerequisites </b></td>\n";
            }

            echo "    <td colspan='2'><b>&nbsp;$lg_change_order </b></td><td><b>&nbsp;$lg_add_prereqi </b></td>\n"."    <td><b>&nbsp;$lg_add_title_and_desc </b></td><td><b>&nbsp;$lg_delete </b></td>\n"."  </tr>\n";
        }
    }

    $i = 1;
    $counter = 0;
    $num_modules = count($tree[$parent_item_id]);

    //while ($row = Database::fetch_array($result))
    if (isset ($tree[$parent_item_id])) {
        foreach ($tree[$parent_item_id] as $row) {
            if ($row['item_type'] === 'dokeos_chapter') {
                $xml_output .= "<chapter>";
                $xml_output .= "<chaptertitle>".$row['title']."</chaptertitle>";
                $xml_output .= "<chapterdescription>".$row['description']."</chapterdescription>";

                $counter ++;
                if (($counter % 2) == 0) {
                    $oddclass = 'row_odd';
                } else {
                    $oddclass = 'row_even';
                }

                //echo '<tr class="'.$oddclass.'">'."\n".'  <td>'.str_repeat("&nbsp;&gt;", $level)."<img src='../img/documents.gif' alt='folder'/><a href='".api_get_self()."?lp_id=$learnpath_id&item_id={$row['id']}&action=add&type=learnpathitem&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9'><b>&nbsp;".$row['title']."</b></a>"."<br /><i><div align='justify'>&nbsp;".str_repeat("&nbsp;&nbsp;&nbsp;", $level)."</i></td>\n".'  <td  align="center"><a href="'.api_get_self()."?lp_id=$learnpath_id&item_id={$row['id']}&action=add&type=learnpathitem&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9"><img src='../img/0.gif' width='13' height='13' border='0' title='$lg_add_learnpath_item'></a></td>\n"."  <td";
                echo '<tr class="'.$oddclass.'">'."\n".'  <td>'.str_repeat("&nbsp;&gt;", $level)."<img src='../img/documents.gif' alt='folder'/><a href='".api_get_self()."?lp_id=$learnpath_id&parent_item_id=".$row['id']."&action=add_sub_item'><b>&nbsp;".$row['title']."</b></a>"."<br /><i><div align='justify'>&nbsp;".str_repeat("&nbsp;&nbsp;&nbsp;", $level)."</i></td>\n".'  <td  align="center"><a href="'.api_get_self()."?lp_id=$learnpath_id&parent_item_id=".$row['id']."&action=add_sub_item\"><img src='../img/0.gif' width='13' height='13' border='0' title='$lg_add_learnpath_item'></a></td>\n"."  <td";
                if (is_prereq($learnpath_id)) {
                    echo " bgcolor='#ddddee'";
                }
                echo ">".$row['prerequisite']."</td>\n";

                // Showing the edit, delete and move icons.
                if (api_is_allowed_to_edit()) {
                    $myaction = 'move_item';
                    if ($i < $num_modules) {
                        // If we are still under the number of chapters in this section, show "move down".
                        //echo "  <td align=center>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=".$myaction."&amp;direction=down&amp;moduleid=".$parent_item_id."&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a></td>\n";
                        echo "  <td align=center>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=".$myaction."&direction=down&moduleid=".$parent_item_id."&id=".$row['id']."'>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a></td>\n";
                    } else {
                        echo '  <td align="center">&nbsp;</td>'."\n";
                    }

                    if ($i > 1) {
                        //echo '  <td align="center">'."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=".$myaction."&amp;direction=up&amp;moduleid=".$parent_item_id."&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>"."</td>\n";
                        echo '  <td align="center">'."<a href='".api_get_self()."?lp_id=$learnpath_id&action=".$myaction."&direction=up&moduleid=".$parent_item_id."&id=".$row['id']."'>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>"."</td>\n";
                    } else {
                        echo '  <td align="center">&nbsp;</td>'."\n";
                    }

                    echo "  <td align='center'>&nbsp;</td>\n";
                    //echo "  <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=editmodule&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_module\">"."</a>"."</td>\n";
                    echo "  <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=edititem&id=".$row['id']."'>"."<img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_module\">"."</a>"."</td>\n";

                    //echo "  <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=deletemodule&amp;id=".$row['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9 onclick=\"javascript: return confirmation('".$row['chapter_name']."');\">"."<img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_module\">"."</a>"."</td>\n";
                    echo "  <td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=delete_item&id=".$row['id']."' onclick=\"javascript: return confirmation('".$row['title']."');\">"."<img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_module\">"."</a>"."</td>\n";
                }

                echo "</tr>\n";
                $i ++;

                $xml_output .= "<items>";

                //display_learnpath_items($row['id']);
                display_learnpath_chapters($row['id'], $tree, $level + 1);

                $xml_output .= "</items>";
                $xml_output .= "</chapter>";

            } else //if //($row['item_type'] === 'item')
            {
                $row_items = $row;
                echo "<tr>\n  <td colspan='2' valign='top'>";
                //require 'resourcelinker.inc.php';
                display_addedresource_link_in_learnpath($row_items['item_type'], $row_items['ref'], '', $row_items['id'], 'builder', 'icon', $level);

                if ($row_items['description']) {
                    echo "<div align='justify'>&nbsp;&nbsp;&nbsp;{$row_items['description']}";
                }

                echo "</td>";

                if (is_prereq($learnpath_id)) {
                    echo '<td bgcolor="#EEEEFF">';
                } else {
                    echo "<td>";
                }

                if (api_is_allowed_to_edit()) {

                    if ($row_items['prerequisite'] != '') {
                        $prereq = $row_items['prerequisite'];

                        //if ($row_items['prereq_type'] == 'i') {
                            // item
                            $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE id='$prereq'"; // Check if prereq has been deleted.
                            $result_items2 = Database::query($sql_items2);
                            $number_items2 = Database::num_rows($result_items2);
                            if ($number_items2 == 0) {
                                echo get_lang('PrerequisiteDeletedError');
                            }
                            $row_items2 = Database::fetch_array($result_items2);
                            display_addedresource_link_in_learnpath($row_items2['item_type'], $row_items2['ref'], '', $row_items2['id'], 'builder', '', 0);
                            if ((($row_items2['item_type'] == TOOL_QUIZ) or ($row_items2['item_type'] == 'HotPotatoes')) and ($row_items['prerequisite'])) {
                                //echo "&nbsp;({$row_items2['title']})";
                            }
                        //}
                        /*
                        if ($row_items['prereq_type'] == 'c') {
                            // chapter
                            $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE id='$prereq' AND item_type='dokeos_chapter'"; // Check if prereq has been deleted.
                            $result_items2 = Database::query($sql_items2);
                            $number_items2 = Database::num_rows($result_items2);
                            if ($number_items2 == 0) {
                                echo "<font color='red'>$lg_prereq_deleted_error</font>";
                            }
                            $row_items2 = Database::fetch_array($result_items2);
                            echo " {$row_items2['title']}";
                        }*/
                    }
                    echo "</font></td>";
                    $xml_output .= "<element_type>".$row_items['item_type']."</element_type>";
                    $xml_output .= "<element_id>".$row_items['item_id']."</element_id>";

                    // Move
                    if ($i < $num_modules) {
                        echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=moveitem&amp;type=item&amp;direction=down&amp;moduleid=".$parent_item_id."&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\">"."</a>"."</td>";
                    } else {
                        echo "<td width='30' align='center'>&nbsp;</td>";
                    }

                    if ($i > 1) {
                        echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=moveitem&amp;type=item&amp;direction=up&amp;moduleid=".$parent_item_id."&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\">"."</a>";
                    } else {
                        echo "<td width='30' align='center'>&nbsp;</td>";
                    }
                    echo "</td>"."<td align='center'>";

                    // Edit prereq
                    echo "<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=edititemprereq&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/scormpre.gif\" border=\"0\" title=\"$lg_add_prereq\">"."</a>"."</td>";

                    // Edit
                    echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&amp;action=edititem&amp;id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_item\">"."</a>"."</td>";

                    // Delete
                    echo "<td align='center'>"."<a href='".api_get_self()."?lp_id=$learnpath_id&action=deleteitem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9>"."<img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_item\" onclick=\"javascript: return confirmation('".$row_items['item_type']."');\">"."</a>";
                }
                $i ++;
                echo "</td></tr>";
            }
        }
    }
}


/**
 * Displays the learning path items/steps.
 * @param		integer		Category ID
 * @return	void
 * @todo eliminate all global $lang declarations, use get_lang, improve structure.
 */
function display_learnpath_items($categoryid) {
    global $xml_output;
    global $lg_prerequisites, $lg_move_down, $lg_move_up, $lg_edit_learnpath_item, $lg_delete_learnpath_item, $learnpath_id, $lg_add_prereq, $lg_prereq_deleted_error, $lg_pre_short, $langThisItem;
    $course_id = api_get_course_int_id();
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

    $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$categoryid' ORDER BY display_order ASC";
    $result_items = Database::query($sql_items);
    $number_items = Database::num_rows($result_items);
    $i = 1;

    while ($row_items = Database::fetch_array($result_items)) {
        echo "<tr><td colspan='2' valign='top'>";
        display_addedresource_link_in_learnpath($row_items['item_type'], $row_items['ref'], '', $row_items['id'], 'builder', 'icon');
        if ($row_items['description']) {
            echo "<div align='justify'><font color='#999999'>&nbsp;&nbsp;&nbsp;{$row_items['description']}</font>";
        }
        echo "</td>";
        if (is_prereq($learnpath_id)) {
            echo '<td bgcolor="#EEEEFF">';
        } else {
            echo "<td>";
        }

        if (api_is_allowed_to_edit()) {
            //error_log('Is allowed to edit item'.$row_items['id'], 0);
            // TODO: Fix by adding true prerequisites parsing (and cycle through).
            // Over simplification here, we transform prereq_id field into prerequisite field.
            if ($row_items['prerequisite'] != '') {
                $prereq = $row_items['prerequisite'];

                //if ($row_items['prereq_type'] == 'i') {
                    // item
                    $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND id='$prereq'"; // Check if prereq has been deleted.
                    $result_items2 = Database::query($sql_items2);
                    $number_items2 = Database::num_rows($result_items2);
                    if ($number_items2 == 0) {
                        echo "<font color=red>$lg_prereq_deleted_error</font>";
                    }
                    $row_items2 = Database::fetch_array($result_items2);
                    display_addedresource_link_in_learnpath($row_items2['item_type'], $row_items2['ref'], '', $row_items2['id'], 'builder', '');
                    if ((($row_items2['item_type'] == 'Exercise') or ($row_items2['item_type'] == 'HotPotatoes')) and ($row_items['prerequisites'])) {
                        echo "&nbsp;({$row_items2['title']})";
                    }
                //}
                /*if ($row_items['prereq_type'] == 'c') {
                    // chapter
                    $sql_items2 = "SELECT * FROM $tbl_learnpath_chapter WHERE id='$prereq'"; //check if prereq has been deleted
                    $result_items2 = Database::query($sql_items2);
                    $number_items2 = Database::num_rows($result_items2);
                    if ($number_items2 == 0) {
                        echo "<font color=red>$lg_prereq_deleted_error</font>";
                    }
                    $row_items2 = Database::fetch_array($result_items2);
                    echo " {$row_items2['chapter_name']}";
                }*/
            }
            echo "</font></td>";
            $xml_output .= "<element_type>".$row_items['item_type']."</element_type>";
            $xml_output .= "<element_id>".$row_items['id']."</element_id>";

            // Move
            if ($i < $number_items) {
                echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=moveitem&direction=down&moduleid=".$categoryid."&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/down.gif\" border=\"0\" title=\"$lg_move_down\"></a></td>";
            } else {
                echo "<td width='30' align='center'>&nbsp;</td>";
            }

            if ($i > 1) {
                echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=moveitem&direction=up&moduleid=".$categoryid."&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/up.gif\" border=\"0\" title=\"$lg_move_up\"></a>";
            } else {
                echo "<td width='30' align='center'>&nbsp;</td>";
            }
            echo "</td><td align='center'>";

            // Edit prereq
            echo "<a href='".api_get_self()."?lp_id=$learnpath_id&action=edititemprereq&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/scormpre.gif\" border=\"0\" title=\"$lg_add_prereq\"></a></td>";

            // Edit
            echo "<td align='center'><a href='".api_get_self()."?lp_id=$learnpath_id&action=edititem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/edit.gif\" border=\"0\" title=\"$lg_edit_learnpath_item\"></a></td>";

            // Delete
            echo "<td align='center'>";
            echo "<a href='".api_get_self()."?lp_id=$learnpath_id&action=deleteitem&id=".$row_items['id']."'&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9><img src=\"../img/delete.gif\" border=\"0\" title=\"$lg_delete_learnpath_item\" onclick=\"javascript: return confirmation('".$langThisItem."');\"></a>";
        }
        $i ++;
        echo "</td></tr>";
    }
}

/**
 * This function returns the items belonging to the chapter that contains the given item (brother items)
 * @param	integer	Item id
 * @return	array		Table containing the items
 */
function learnpath_items($itemid) {
    global $xml_output;
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $course_id = api_get_course_int_id();
    

    $sql_items = "SELECT parent_item_id FROM $tbl_lp_item WHERE c_id = $course_id AND id='$itemid'";
    $moduleid_sql = Database::query($sql_items);
    $moduleid_array = Database::fetch_array($moduleid_sql); // First row of the results.
    $moduleid = $moduleid_array['parent_item_id'];

    $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$moduleid' ORDER BY display_order ASC";
    $result_items = Database::query($sql_items);
    $ar = Database::fetch_array($result_items);
    while ($ar != '') {
        $result[] = $ar;
        $ar = Database::fetch_array($result_items);
    }
    return $result;
}

/**
 * This function returns the chapters belonging to the path that contais the given chapter (brother chapters)
 * @param	integer	Learnpath id
 * @return array		Table containing the chapters
 */
function learnpath_chapters($learnpath_id) {
    global $xml_output, $learnpath_id;    
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
    $course_id = api_get_course_int_id();
    

    $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id='$learnpath_id' AND item_type='dokeos_chapter' ORDER BY display_order ASC";
    //$sql_items = "SELECT * FROM $tbl_learnpath_chapter WHERE lp_id='$learnpath_id' ORDER BY display_order ASC";
    $result_items = Database::query($sql_items);
    $ar = Database::fetch_array($result_items);
    while ($ar != '') {
        $result[] = $ar;
        $ar = Database::fetch_array($result_items);
    }
    return $result;
}

/**
 * This function tells if a learnpath contains items which are prerequisite to other items
 * @param	integer	Learnpath id
 * @return	boolean	True if this learnpath contains an item which is a prerequisite to something
 */
function is_prereq($learnpath_id) {
    global $xml_output;
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
    $course_id = api_get_course_int_id();

    $prereq = false;

    $sql_items = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id='$learnpath_id' AND parent_item_id=0 ORDER BY display_order ASC";
    $result_items = Database::query($sql_items);
    while ($ar = Database::fetch_array($result_items)) {
        $c = $ar['id'];
        $sql_items2 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND parent_item_id='$c' ORDER BY display_order ASC";
        $result_items2 = Database::query($sql_items2);
        while ($ar2 = Database::fetch_array($result_items2)) {
            if ($ar2['prerequisite'] != '') {
                $prereq = true;
            }
        }
    }
    return ($prereq);
}

/**
 * This function returns the prerequisite sentence
 * @param	integer	Item ID
 * @return	string 	Prerequisite warning text
 */
function prereqcheck($id_in_path) {
    // 1. Initialise and import working vars.
    global $learnpath_id, $_user;
    global $langPrereqToEnter, $langPrereqTestLimit1, $langPrereqTestLimit2, $langPrereqTestLimitNow, $langPrereqFirstNeedTo, $langPrereqModuleMinimum1, $langPrereqModuleMinimum2;
    $tbl_learnpath_user = Database :: get_course_table(TABLE_LEARNPATH_USER);
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    
    $course_id = api_get_course_int_id();

    // 2. Initialise return value.
    $prereq = false;

    // 3. Get item data from the database.
    $sql_items = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND id='$id_in_path'";
    $result_items = Database::query($sql_items);
    $row = Database::fetch_array($result_items);
    // 4. Check prerequisite's type.
    if ($row['prereq_type'] == 'i') {
        // 4.a If prerequisite is of type 'i' (item):
        // 4.a.1 Get data ready for use.
        $id_in_path3 = $row['prereq_id'];
        $prereq_limit = $row['prereq_completion_limit'];

        // 4.a.2 Get data from the user-item relation.
        if ($_user['user_id'] == '') {
            $user_id = '0';
        } else {
            $user_id = $_user['user_id'];
        }
        $sql_items3 = "SELECT * FROM $tbl_learnpath_user WHERE c_id = $course_id AND (learnpath_item_id='$id_in_path3' and user_id=$user_id)";
        $result_items3 = Database::query($sql_items3);
        $row3 = Database::fetch_array($result_items3);

        // 4.a.3 Get the link that needs to be shown for the current item (not the prereq)
        $stepname = display_addedresource_link_in_learnpath($row['item_type'], $row['ref'], '', $id_in_path, 'builder', 'nolink');
        // This is the step we want to open.
        $stepname = trim($stepname); // Removing occasional line breaks and white spaces

        // 4.a.4 Get the prerequisite item.
        $sql6 = "SELECT * FROM $tbl_learnpath_item WHERE (id='$id_in_path3')";
        $result6 = Database::query($sql6);
        $row6 = Database::fetch_array($result6);
        // 4.a.5 Get a link to the prerequisite item.
        $prereqname = display_addedresource_link_in_learnpath($row6['item_type'], $row6['ref'], '', $id_in_path3, 'builder', 'nolink'); //this is the prereq of the step we want to open

        // 4.a.5 Initialise limit value.
        $limitok = true;
        // 4.a.6 Get prerequisite limit.
        if ($prereq_limit) {
            // 4.a.6.a If the completion limit exists.
            if ($row3['score'] < $prereq_limit) {
                // 4.a.6.a.a If the completion limit hasn't been reached, then display the corresponding message.
                $prereq = $langPrereqToEnter.$stepname.$langPrereqTestLimit1."$prereq_limit".$langPrereqTestLimit2.$prereqname.". (".$langPrereqTestLimitNow.$row3['score'].")";
            } else {
                // 4.a.6.a.b The completion limit has been reached. Prepare to return false (no prereq hanging).
                $prereq = false;
            }
        } else {
            // 4.a.6.b If the completion limit doesn't exist.
            if ($row3['status'] == 'completed' or $row3['status'] == 'passed') {
                // 4.a.6.b.a If the prerequisite status is 'completed'.
                $prereq = false;
            } else {
                // 4.a.6.b.b The prerequisite status is not 'completed', return corresponding message.
                $prereq = $langPrereqToEnter.$stepname.$langPrereqFirstNeedTo.$prereqname.'.';
            }
        }
    } elseif ($row['prereq_type'] == 'c') {
        // 4.b If prerequisite is of type 'c' (chapter).
        // 4.b.1 Get data ready to use.
        $id_in_path2 = $row['prereq_id'];
        // 4.b.2 Get all items in the prerequisite chapter.
        $sql_items3 = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND parent_item_id='$id_in_path2'";
        $result_items3 = Database::query($sql_items3);
        $allcompleted = true;
        while ($row3 = Database::fetch_array($result_items3)) {
            // 4.b.3 Cycle through items in the prerequisite chapter.
            // 4.b.3.1 Get data ready to use.
            $id_in_path4 = $row3['id'];
            if ($_user['user_id'] == '') {
                $user_id = '0';
            } else {
                $user_id = $_user['user_id'];
            }
            // 4.b.3.2 Get user-item relation.
            $sql_items4 = "SELECT * FROM $tbl_learnpath_user WHERE c_id = $course_id AND (learnpath_item_id='$id_in_path4' and user_id=$user_id)";
            $result_items4 = Database::query($sql_items4);
            $row4 = Database::fetch_array($result_items4);
            // 4.b.3.3 If any of these elements is not 'completed', the overall completion status is false.
            if ($row4['status'] != 'completed' and $row4['status'] != 'passed') {
                $allcompleted = false;
            }
        }
        if ($allcompleted) {
            // 4.b.4.a All items were completed, prepare to return that there is no prerequisite blocking the way.
            $prereq = false;
        } else {
            // 4.b.4.b Something was not completed. Return corresponding message.
            $sql5 = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (lp_id='$learnpath_id' and id='$id_in_path2')";
            $result5 = Database::query($sql5);
            $row5 = Database::fetch_array($result5);
            $prereqmodulename = trim($row5['chapter_name']);
            $prereq = $langPrereqModuleMinimum1.$prereqmodulename.$langPrereqModuleMinimum2;
        }
    } else {
        // 5. If prerequisite type undefined, no prereq.
        $prereq = false;
    }
    // 6. Return the message (or false if no prerequisite waiting).
    return ($prereq);
}

/**
 * Constructs the tree that will be used to build the learnpath structure
 * @params  integer     Learnpath_id
 * @return  array       Tree of the learnpath structure
 * @author  Yannick Warnier <yannick.warnier@beeznest.com>
 * @comment This is a temporary function, which exists while the chapters and items
 *          are still in separate tables in the database. This function gathers the data in a unique tree.
 */
function get_learnpath_tree($learnpath_id) {
    //error_log('New LP - In learnpath_functions::get_learnpath_tree', 0);
    // Init elems
    //global $tbl_learnpath_item, $tbl_learnpath_chapter;
    /*
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    */
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

    $tree = array();
    $chapters = array();    
    $all_items_by_chapter = array();
    $course_id = api_get_course_int_id();
    
    $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = ".$learnpath_id." AND item_type='dokeos_chapter' ORDER BY display_order";
    //error_log('New LP - learnpath_functions - get_learnpath_tree: '.$sql,0);
    $res = Database::query($sql);
    // Format the $chapters_by_parent array so we have a suitable structure to work with.
    while ($row = Database::fetch_array($res)) {
        $chapters[] = $row;
        // Shouldn't be necessary (check no null value).
        if (empty ($row['parent_item_id'])) {
            $row['parent_item_id'] = 0;
        }
        //$chapters_by_parent[$row['parent_item_id']][$row['previous_item_id']] = $row;
        $all_items_by_chapter[$row['parent_item_id']][$row['display_order']] = $row;
        $all_items_by_chapter[$row['parent_item_id']][$row['display_order']]['type'] = 'dokeos_chapter';
    }

    // Now for every item in each chapter, get a suitable structure too.
    foreach ($chapters as $row) {
        // Select items from this chapter.
        $sql = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND parent_item_id = ".$row['id']." ORDER BY display_order";
        //error_log('New LP - learnpath_functions - get_learnpath_tree: '.$sql, 0);
        $res = Database::query($sql);
        //error_log('New LP - learnpath_functions - get_learnpath_tree: Found '.Database::num_rows($res).' results', 0);
        while ($myrow = Database::fetch_array($res, 'ASSOC')) {
            //$items[] = $myrow;
            //$items_by_chapter[$myrow['parent_item_id']][$myrow['display_order']] = $myrow;
            $all_items_by_chapter[$row['id']][$myrow['display_order']] = $myrow;
            $all_items_by_chapter[$row['id']][$myrow['display_order']]['type'] = 'item';
        }
    }
    //array_multisort($all_items_by_chapter[0], SORT_ASC, SORT_NUMERIC);
    foreach ($all_items_by_chapter as $key => $subrow) {
        ksort($all_items_by_chapter[$key]);
    }

    //all items should now be well-ordered
    //error_log('New LP - In get_learnpath_tree, returning '.print_r($all_items_by_chapter,true), 0);
    return $all_items_by_chapter;
}

/**
 * Gives a list of sequencial elements IDs for next/previous actions
 * @param   array   The elements tree as returned by get_learnpath_tree()
 * @param   integer The chapter id to start from
 * @param   boolean Whether to include chapters or not
 * @return  array   List of elements in the first to last order
 * @author  Yannick Warnier <yannick.warnier@beeznest.com>
 */
function get_ordered_items_list($tree, $chapter = 0, $include_chapters = false) {
    $list = array ();
    foreach ($tree[$chapter] as $order => $elem) {
        if ($elem['type'] == 'chapter') {
            if ($include_chapters === true) {
                $list[] = array ('id' => $elem['id'], 'type' => $elem['type']);
            }
            $res = get_ordered_items_list($tree, $elem['id'], $include_chapters);
            foreach ($res as $elem) {
                $list[] = $elem;
            }
        } elseif ($elem['type'] == 'item') {
            $list[] = array ('id' => $elem['id'], 'type' => $elem['type'], 'item_type' => $elem['item_type'], 'parent_item_id' => $elem['parent_item_id'], 'item_id' => $elem['item_id']);
        }
    }
    return $list;
}

/**
 * Displays the structure of a chapter recursively. Takes the result of get_learnpath_tree as argument
 * @param	array		Chapter structure
 * @param	integer	Chapter ID (start point in the tree)
 * @param	integer	Learnpath ID
 * @param	integer	User ID
 * @param	boolean	Indicates if the style is wrapped (true) or extended (false)
 * @param	integer	Level reached so far in the tree depth (enables recursive behaviour)
 * @return	array		Number of items, Number of items completed
 * @author	Many changes by Yannick Warnier <yannick.warnier@beeznest.com>
 */
function display_toc_chapter_contents($tree, $parent_item_id = 0, $learnpath_id, $uid, $wrap, $level = 0) {
    //global $tbl_learnpath_user;
    $tbl_learnpath_user = Database :: get_course_table(TABLE_LEARNPATH_USER);
    $num = 0;
    $num_completed = 0;
    foreach ($tree[$parent_item_id] as $order => $elem) {

        $bold = false;
        if (!empty ($_SESSION['cur_open']) && ($elem['id'] == $_SESSION['cur_open'])) {
            $bold = true;
        }
        if ($elem['type'] === 'chapter') {
            if ($wrap) {
                echo str_repeat("&nbsp;&nbsp;", $level).shorten(strip_tags($elem['chapter_name']), (35 - 3 * $level))."<br />\n";
            } else {
                echo "<tr><td colspan='3'>".str_repeat("&nbsp;&nbsp;", $level).shorten($elem['chapter_name'], (35 - 3 * $level))."</td></tr>\n";
            }

            if ($wrap) {
                if ($elem['chapter_description'] != '') {
                    echo "<div class='description'>".str_repeat("&nbsp;&nbsp;", $level)."&nbsp;".shorten($elem['chapter_description'], (35 - 3 * $level))."</div>\n";
                }
            } else {
                if ($elem['chapter_description'] != '') {
                    echo "<tr><td colspan='3'><div class='description'>".str_repeat("&nbsp;&nbsp;", $level)."&nbsp;".shorten($elem['chapter_description'], (35 - 3 * $level))."</div></td></tr>\n";
                }
            }
            list ($a, $b) = display_toc_chapter_contents($tree, $elem['id'], $learnpath_id, $uid, $wrap, $level + 1);
            $num += $a;
            $num_completed += $b;

        } elseif ($elem['type'] === 'item') {
            // If this element is an item (understand: not a directory/module).
            $sql0 = "SELECT * FROM $tbl_learnpath_user WHERE (user_id='".$uid."' and learnpath_item_id='".$elem['id']."' and lp_id='".$learnpath_id."')";
            $result0 = Database::query($sql0);
            $row0 = Database::fetch_array($result0);

            $completed = '';
            if (($row0['status'] == 'completed') or ($row0['status'] == 'passed')) {
                $completed = 'completed';
                $num_completed ++;
            }

            if ($wrap) {
                echo str_repeat("&nbsp;&nbsp;", $level)."<a name='{$elem['id']}' />\n";
            } else {
                echo "<tr><td>".str_repeat("&nbsp;&nbsp;", $level-1)."<a name='{$elem['id']}' />\n";
            }

            if ($wrap) {
                $icon = 'wrap';
            }

            if ($bold) {
                echo "<b>";
            }
            display_addedresource_link_in_learnpath($elem['item_type'], $elem['ref'], $completed, $elem['id'], 'player', $icon);
            if ($bold) {
                echo "</b>";
            }
            if ($wrap) {
                echo "<br />\n";
            } else {
                echo "</td></tr>\n";
            }

            $num ++;
        }
    }
    return array ($num, $num_completed);
}

/**
 * Returns a string to display in the tracking frame within the contents.php page (for example)
 * @param   integer     Learnpath id
 * @param   integer     Current user id
 * @param   integer     Starting chapter id
 * @param   array       Tree of elements as returned by get_learnpath_tree()
 * @param   integer     Level of recursivity we have reached
 * @param   integer     Counter of elements already displayed
 * @author  Yannick Warnier <yannick.warnier@beeznest.com>
 * @deprecated this function seems to be unused
 * @note : forced display because of display_addedresource_link_in_learnpath behaviour (outputing a string would be better)
 */
function get_tracking_table($learnpath_id, $user_id, $parent_item_id = 0, $tree = false, $level = 0, $counter = 0) {
    $tbl_learnpath_chapter = Database :: get_course_learnpath_chapter_table();
    $tbl_learnpath_item = Database :: get_course_learnpath_item_table();
    $tbl_learnpath_user = Database :: get_course_learnpath_user_table();
    //$mytable = '';
    $include_chapters = true;

    if (!is_array($tree)) {
        // Get a tree of the current learnpath elements.
        $tree = get_learnpath_tree($learnpath_id);
    }
    foreach ($tree[$parent_item_id] as $order => $elem) {
        if (($counter % 2) == 0) {
            $oddclass = 'row_odd';
        } else {
            $oddclass = 'row_even';
        }

        if ($elem['type'] == 'chapter') {
            if ($include_chapters === true) {
                //$mytable .= "<tr class='$oddclass'><td colspan = '3'>".str_repeat('&nbsp;',$level*2+2).$elem['chapter_name']."</td></tr>\n";
                echo "<tr class='$oddclass'><td colspan = '3'>".str_repeat('&nbsp;', $level * 2 + 2).$elem['chapter_name']."</td></tr>\n";
            }
            $counter ++;
            //$mytable .= get_tracking_table($learnpath_id, $user_id, $elem['id'], $tree, $level + 1, $counter );
            get_tracking_table($learnpath_id, $user_id, $elem['id'], $tree, $level + 1, $counter);

        } elseif ($elem['type'] == 'item') {

            $sql = "SELECT * FROM $tbl_learnpath_user "."WHERE user_id = $user_id "."AND lp_id = $learnpath_id "."AND learnpath_item_id = ".$elem['id'];
            $res = Database::query($sql);
            $myrow = Database::fetch_array($res);

            if (($myrow['status'] == 'completed') || ($myrow['status'] == 'passed')) {
                $color = 'blue';
                $statusmessage = get_lang('Complete');
            } else {
                $color = 'black';
                $statusmessage = get_lang('Incomplete');
            }

            $link = get_addedresource_link_in_learnpath($elem['item_type'], $elem['id'], $elem['item_id']);
            //$link = display_addedresource_link_in_learnpath($elem['item_type'], $elem['id'], $row['status'], $elem['item_id'], 'player', 'none');

            //$mytable .= "<tr class='$oddclass'>"
            echo "<tr class='$oddclass'>"."<td class='mystatus'>".str_repeat("&nbsp;", $level * 2 + 2);
            //."<a href='$link?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target='toc'>hop</a>"
            display_addedresource_link_in_learnpath($elem['item_type'], $elem['ref'], $myrow['status'], $elem['id'], 'player', 'wrap');
            //we should also add the total score here
            echo "<td>"."<font color='$color'><div class='mystatus'>".$statusmessage."</div></font>"."</td>"."<td>"."<div class='mystatus' align='center'>". ($myrow['score'] == 0 ? '-' : $myrow['score'])."</div>"."</td>"."</tr>\n";
            $counter ++;
        }
    }
    //return $mytable;
    return true;
}

/**
 * This function returns false if there is at least one item in the path
 * @param	Learnpath ID
 * @return	boolean	True if nothing was found, false otherwise
 */
function is_empty($id) {
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
    $course_id = api_get_course_int_id();

    $sql = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND lp_id=$id ORDER BY display_order ASC";
    $result = Database::query($sql);
    $num_modules = Database::num_rows($result);
    $empty = true;

    if ($num_modules != 0) {
        while ($row = Database::fetch_array($result)) {

            $num_items = 0;
            $parent_item_id = $row['id'];
            $sql2 = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND (parent_item_id=$parent_item_id) ORDER BY display_order ASC";
            $result2 = Database::query($sql2);
            $num_items = Database::num_rows($result2);
            if ($num_items > 0) {
                $empty = false;
            }
        }
    }

    return ($empty);
}

/**
 * This function writes $content to $filename
 * @param	string	Destination filename
 * @param	string	Learnpath name
 * @param	integer	Learnpath ID
 * @param	string	Content to write
 * @return	void
 */
function exporttofile($filename, $LPname, $LPid, $content) {

    global $circle1_files; // This keeps all the files which are exported [0]:filename [1]:LP name.
    // The $circle1_files variable is going to be used to a deep extent in the imsmanifest.xml.
    global $expdir;

    if (!$handle = fopen($expdir.'/'.$filename, 'w')) {
        echo "Cannot open file ($filename)";
    }
    if (fwrite($handle, $content) === false) {
        echo "Cannot write to file ($filename)";
        exit;
    }
    fclose($handle);

    $circle1_files[0][] = $filename;
    $circle1_files[1][] = $LPname;
    $circle1_files[2][] = $LPid;
}

/**
 * This function exports the given Chamilo test
 * @param	integer	Test ID
 * @return string 	The test itself as an HTML string
 */
function export_exercise($item_id) {

    global $expdir, $_course, $_configuration, $_SESSION, $_SERVER, $language_interface, $langExerciseNotFound, $langQuestion, $langOk, $origin, $questionNum;

    $exerciseId = $item_id;
    
    require_once '../exercice/testcategory.class.php';
    require_once '../exercice/exercise.class.php';    
    require_once '../exercice/question.class.php';
    require_once '../exercice/answer.class.php';
    require_once '../exercice/exercise.lib.php';
    
    $TBL_EXERCISES = Database :: get_course_table(TABLE_QUIZ_TEST);

    /* Clears the exercise session */
    if (isset ($_SESSION['objExercise'])) {
        Session::erase('objExercise');
    }
    if (isset ($_SESSION['objQuestion'])) {
        Session::erase('objQuestion');
    }
    if (isset ($_SESSION['objAnswer'])) {
        Session::erase('objAnswer');
    }
    if (isset ($_SESSION['questionList'])) {
        Session::erase('questionList');
    }
    if (isset ($_SESSION['exerciseResult'])) {
        Session::erase('exerciseResult');
    }

    // If the object is not in the session:
    if (!isset ($_SESSION['objExercise'])) {
        // Construction of Exercise.
        $objExercise = new Exercise();

        $sql = "SELECT title,description,sound,type,random,active FROM $TBL_EXERCISES WHERE id='$exerciseId'";
        // If the specified exercise doesn't exist or is disabled:
        if (!$objExercise->read($exerciseId) || (!$objExercise->selectStatus() && !api_is_allowed_to_edit() && ($origin != 'learnpath'))) {
            die($langExerciseNotFound);
        }

        // Saves the object into the session.
        Session::write('objExercise',$objExercise);
    }

    $exerciseTitle = $objExercise->selectTitle();
    $exerciseDescription = $objExercise->selectDescription();
    $exerciseSound = $objExercise->selectSound();
    $randomQuestions = $objExercise->isRandom();
    $exerciseType = $objExercise->selectType();

    if (!isset ($_SESSION['questionList'])) {
        // Selects the list of question ID.
        $questionList = $randomQuestions ? $objExercise->selectRandomList() : $objExercise->selectQuestionList();

        // Saves the question list into the session.
        Session::write('questionList',$questionList);
    }

    $nbrQuestions = sizeof($questionList);

    // If questionNum comes from POST and not from GET:
    if (!$questionNum || $_POST['questionNum']) {
        // Only used for sequential exercises (see $exerciseType).
        if (!$questionNum) {
            $questionNum = 1;
        } else {
            $questionNum ++;
        }
    }

    $exerciseTitle = text_filter($exerciseTitle);

    $test .= "<h3>".$exerciseTitle."</h3>";

    if (!empty ($exerciseSound)) {
        $test .= "<a href=\"../document/download.php?doc_url=%2Faudio%2F".$exerciseSound."\"&SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9 target=\"_blank\"><img src=\"../img/sound.gif\" border=\"0\" align=\"absmiddle\" alt=".get_lang("Sound")."\" /></a>";
    }

    $exerciseDescription = text_filter($exerciseDescription);

    // Writing the .js file with to check the correct answers begin.
    $scriptfilename = "Exercice".$item_id.".js";
    $s = "<script type=\"text/javascript\" src='../js/".$scriptfilename."'></script>";
    $test .= $s;

    $content = "function evaluate() {
        alert('Test evaluated.');
        }
        ";

    if (!$handle = fopen($expdir.'/js/'.$scriptfilename, 'w')) {
        echo "Cannot open file ($scriptfilename)";
    }
    if (fwrite($handle, $content) === false) {
        echo "Cannot write to file ($filename)";
        exit;
    }
    fclose($handle);

    // Writing the .js file with to check the correct answers end.
    $s = "
        <p>$exerciseDescription</p>
        <table width='100%' border='0' cellpadding='1' cellspacing='0'>
         <form method='post' action=''><input type=\"hidden\" name=\"SQMSESSID\" value=\"36812c2dea7d8d6e708d5e6a2f09b0b9\" />
         <input type='hidden' name='formSent' value='1' />
         <input type='hidden' name='exerciseType' value='".$exerciseType."' />
         <input type='hidden' name='questionNum' value='".$questionNum."' />
         <input type='hidden' name='nbrQuestions' value='".$nbrQuestions."' />
         <tr>
          <td>
          <table width='100%' cellpadding='4' cellspacing='2' border='0'>";

    $exerciseType = 1; // So to list all questions in one page.
    $test .= $s;

    $i = 0;

    foreach ($questionList as $questionId) {
        $i ++;

        // For sequential exercises.
        if ($exerciseType == 2) {
            // If it is not the right question, goes to the next loop iteration.
            if ($questionNum != $i) {
                continue;
            } else {
                // if the user has already answered this question:
                if (isset ($exerciseResult[$questionId])) {
                    // Construction of the Question object.
                    $objQuestionTmp = new Question();

                    // Reads question informations.
                    $objQuestionTmp->read($questionId);

                    $questionName = $objQuestionTmp->selectTitle();

                    // Destruction of the Question object.
                    unset ($objQuestionTmp);

                    $test .= '<tr><td>'.get_lang('AlreadyAnswered').' &quot;'.$questionName.'&quot;</td></tr>';

                    break;
                }
            }
        }

        echo $s = "<tr bgcolor='#e6e6e6'><td valign='top' colspan='2'>".get_lang('Question')." ";
        // Call the showQuestion() function from exercise.lib.php. This basically displays the question in a table.
        $test .= showQuestion($questionId, false, 'export', $i);

    } // end foreach()

    $s = "</table></td></tr><tr><td><br/><input type='button' value='".$langOk."' onclick=\"javascript: evaluate(); alert('Evaluated.');\">";
    $s .= "</td></tr></form></table>";
    $s .= "<script type='text/javascript'> loadPage(); </script>";
    $b = 2;
    $test .= $s;
    return ($test);
}

/**
 * This function exports the given item
 * @param	integer	Id from learnpath_items table
 * @param	integer	Item id
 * @param	string	Itm type
 * @param	boolean	Shall the SCORM communications features be added? (true). Default: false.
 * @return	void (outputs a zip file)
 * @todo	Try using the SCORM communications addition (adding a button and several javascript calls to the SCORM API) elsewhere than just in the export feature, so it doesn't look like an incoherent feature
 */

function exportitem($id, $item_id, $item_type, $add_scorm_communications = false) {
    $course_id = api_get_course_int_id();

    global $circle1_files, $expdir, $_course, $_SESSION, $GLOBALS;
    global $timeNoSecFormat, $dateFormatLong, $language_interface, $langPubl, $langDone, $langThisCourseDescriptionIsEmpty, $lg_course_description, $lg_introduction_text, $_cid, $langHotPotatoesFinished, $lg_author, $lg_date, $lg_groups, $lg_users, $lg_ass, $lg_dropbox, $test, $langQuestion;

    $libp = api_get_path(SYS_CODE_PAH);
    include_once $libp.'exercice/exercise.class.php';
    include_once $libp.'question.class.php';
    include_once $libp.'answer.class.php';
    include_once $libp.'exercise.lib.php';

    $langLasting = '';//avoid code parser warning
    include_once $libp.'lang/english/announcements.inc.php'; //this line is here only for $langPubl in announcements
    include_once $libp.'lang/'.$language_interface.'/announcements.inc.php'; //this line is here only for $langPubl in announcements
    include_once $libp.'lang/english/agenda.inc.php'; //this line is here only for $langLasting
    include_once $libp.'lang/'.$language_interface.'/agenda.inc.php'; //this line is here only for $langLasting
    include_once $libp.'lang/english/course_description.inc.php'; //this line is here only for $langThisCourseDescriptionIsEmpty
    include_once $libp.'lang/'.$language_interface.'/course_description.inc.php'; //				 -||-
    include_once $libp.'lang/english/resourcelinker.inc.php';
    include_once $libp.'lang/'.$language_interface.'/resourcelinker.inc.php';
    include_once $libp.'lang/english/learnpath.inc.php';
    include_once $libp.'lang/'.$language_interface.'/learnpath.inc.php';
    include_once $libp.'lang/english/exercice.inc.php';
    include_once $libp.'lang/'.$language_interface.'/exercice.inc.php';

    include_once '../resourcelinker/resourcelinker.inc.php';

    $LPname = display_addedresource_link_in_learnpath($item_type, $item_id, '', $id, 'builder', 'nolink');

    $expcontent = "<!--
        This is an exported file from Chamilo Learning Path belonging to a Scorm compliant content package.
        Do not modify or replace individually.

        Export module author : Denes Nagy <darkden@evk.bke.hu>

        -->

        ";
    // Files needed for communicating with the scos.
    $scocomfiles = "<script type='text/javascript' src='../js/APIWrapper.js'></script>"."<script type='text/javascript' src='../js/SCOFunctions.js'></script>";
    $expcontent .= '<html><head><link rel="stylesheet" href="../css/default.css" type="text/css" media="screen,projection" />'.$scocomfiles.'</head><body>';

    $donebutton .= "<script type='text/javascript'>
            /* <![CDATA[ */
            loadPage();
            var   studentName = '!';
            var   lmsStudentName = doLMSGetValue(  'cmi.core.student_name' );
            if ( lmsStudentName  != '' )
            {
               studentName = ' ' + lmsStudentName +   '!';
            }
            /* ]]> */
            </script>
            <br /><br />
            <form><input type=\"hidden\" name=\"SQMSESSID\" value=\"36812c2dea7d8d6e708d5e6a2f09b0b9\" />
                <table cols='3'	width='100%' align='center'>
                    <tr>
                    <td	align='middle'><input type = 'button' value	= '  ".$langDone."  ' onclick = \"javascript: doQuit('completed');\" id='button2' name='button2'></td>
                    </tr>
                </table>
            </form>";

    /**
     * Switch between the different element types, namely:
     * - Agenda
     * - Ad_Valvas
     * - Course_description
     * - Document
     * - Introduction_text
     * - HotPotatoes
     * - Exercise
     * - Post
     * - Forum          ]
     * - Thread         ]
     * - Dropbox        ]
     * - Assignments    ] These elements are all replaced by a simple message in the exported document.
     * - Groups         ]
     * - Users          ]
     * - Link _self
     * - Link _blank
     */
    switch ($item_type) {

        // AGENDA BEGIN
        case 'Agenda':
            // 1. Get agenda event data from the database table.
            $TABLEAGENDA = Database :: get_course_table(TABLE_AGENDA);
            $sql = "SELECT * FROM ".$TABLEAGENDA." where c_id = $course_id AND (id=$item_id)";
            $result = Database::query($sql);

            // 2. Prepare table output.
            $expcontent .= "<table class=\"data_table\" >";
            $barreMois = '';

            // 3. For each event corresponding to this agenda, do the following:
            while ($myrow = Database::fetch_array($result)) {
                $start_date_local = api_get_local_time($myrow['start_date'], null, date_default_timezone_get());
                //3.1 Make the blue month bar appear only once.
                if ($barreMois != api_format_date($start_date_local, "%m")) {
                    // 3.1.1 Update the check value for the month bar.
                    $barreMois = api_format_date($start_date_local, "%m");
                    // 3.1.2 Display the month bar.
                    $expcontent .= "<tr><td id=\"title\" colspan=\"2\" class=\"month\" valign=\"top\">".api_format_date($start_date_local, "%B %Y")."</td></tr>";
                }

                // 3.2 Display the agenda items (of this month): the date, hour and title.
                $db_date = (int) api_format_date($start_date_local, "%d");
                if ($_GET['day'] != $db_date) {
                    // 3.2.1.a If the day given in the URL (might not be set) is different from this element's day, use style 'data'.
                    $expcontent .= "<tr><td class=\"data\" colspan='2'>";
                } else {
                    // 3.2.1.b Else (same day) use style 'datanow'.
                    $expcontent .= "<tr><td class=\"datanow\" colspan='2'>";
                }
                // 3.2.2 Mark an anchor for this date.
                $expcontent .= "<a name=\"".$db_date."\"></a>"; // anchoring
                // 3.2.3 Write the date and time of this event to the export string.
                $expcontent .= api_format_date($start_date_local);
                // 3.2.4 If a duration is set, write it, otherwise ignore.
                if ($myrow['duration'] == '') {
                    $expcontent .= "<br />";
                } else {
                    $expcontent .= " / ".$langLasting." ".$myrow['duration']."<br />"; //langLasting comes from lang/x/agenda.inc.php
                }
                // 3.2.5 Write the title.
                $expcontent .= $myrow['title'];
                $expcontent .= "</td></tr>";
                // 3.2.6 Prepare the content of the agenda item.
                $content = $myrow['content'];
                // 3.2.7 Make clickable???
                $content = make_clickable($content);
                $content = text_filter($content);
                // 3.2.8 Write the prepared content to the export string.
                $expcontent .= "<tr><td class=\"text\" colspan='2'>";
                $expcontent .= $content;
                $expcontent .= "</td></tr>";

                // Displaying the agenda item of this month: the added resources.
                // This part is not included into LP export.
                /*if (check_added_resources("Agenda", $myrow['id'])) {
                    $content.= "<tr><td colspan='2'>";
                    $content.= "<i>".get_lang('AddedResources')."</i><br />";
                    display_added_resources("Agenda", $myrow['id']);
                    $content.= "</td></tr>";
                }*/

            }
            // 4. Finish the export string.
            $expcontent .= "<tr></table>";

            break;

        // ANNOUNCEMENT BEGIN
        case 'Ad_Valvas':
            // 1. Get the announcement data from the database
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $sql = "SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id='$item_id'";
            $result = Database::query($sql);

            // 2. Initialise export string
            $expcontent .= "<table class=\"data_table\">";

            // 3. For each announcement matching the query
            while ($myrow = Database::fetch_array($result)) {
                // 3.1 Get the __ field data.
                $content = $myrow[1];
                //$content = nl2br($content);
                // 3.2 Prepare the data for export.
                $content = make_clickable($content);
                $content = text_filter($content);

                // 3.3 Get a UNIX(?<-mktime) Timestamp of the end_date for this announcement.
                $last_post_datetime = $myrow['end_date']; // post time format  datetime of database layer (MySQL is assumed)
                list ($last_post_date, $last_post_time) = split(' ', $last_post_datetime);
                list ($year, $month, $day) = explode('-', $last_post_date);
                list ($hour, $min) = explode(':', $last_post_time);
                $announceDate = mktime($hour, $min, 0, $month, $day, $year);

                // 3.4 Compare the end date to the last login date of the user (mark it in red if he has not already read it).
                if ($announceDate > $_SESSION['user_last_login_datetime']) {
                    $colorBecauseNew = " color=\"red\" ";
                } else {
                    $colorBecauseNew = '  ';
                }

                // 3.5 Write this content to the export string (formatted HTML array).
                $expcontent .= "<tr>\n"."<td class=\"cell_header\">\n"."<font ".$colorBecauseNew.">".$langPubl." : ".api_convert_and_format_date($last_post_datetime, null, date_default_timezone_get())."</font>\n"."</td>\n"."</tr>\n"."<tr>\n"."<td>\n".$content."</td>\n"."</tr>\n";

            } // while loop

            // 4 Finish the export string.
            $expcontent .= "</table>";

            break;

        // Course_description BEGIN
        case 'Course_description':
            // 1. Get course description data from database.
            $tbl_course_description = Database :: get_course_table(TABLE_COURSE_DESCRIPTION);
            $result = Database::query("SELECT id, title, content FROM ".$tbl_course_description." WHERE c_id = $course_id  ORDER BY id");

            // 2. Check this element.
            if (Database::num_rows($result)) {
                // 2.a This course has one (or more) description in the database.
                $expcontent .= "<hr noshade=\"noshade\" size=\"1\" />";
                // 2.a.1 For each description available for this course.
                while ($row = Database::fetch_array($result)) {
                    // 2.a.1.1 Write title to export string.
                    $expcontent .= "<h4>".$row['title']."</h4>";
                    // 2.a.1.2 Prepare content.
                    $content = make_clickable(nl2br($row['content']));
                    $content = text_filter($content);
                    // 2.a.1.3 Write content to the export string.
                    $expcontent .= $content;
                }
            } else {
                // 2.b This course has no description available.
                $expcontent .= "<br /><h4>$langThisCourseDescriptionIsEmpty</h4>";
            }

            break;

        // DOCUMENT BEGIN
        case 'Document':
            // 1. Get the document data from the database.
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
            $sql_query = "SELECT * FROM $tbl_document WHERE c_id = $course_id AND id=$item_id";
            $sql_result = Database::query($sql_query);
            $myrow = Database::fetch_array($sql_result);
            // 2. Get the origin path of the document to treat it internally.
            $orig = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$myrow['path'];
            // 3. Make some kind of strange transformation to get the destination filepath ???
            $pathname = explode('/', $myrow['path']);
            $last = count($pathname) - 1;
            $filename = 'data/'.$filename.$pathname[$last];
            $copyneeded = true;

            // Html files do not need to be copied as the ok button is inserted into them,
            // so don't copy directly.
            $extension = explode('.', $pathname[$last]);
            // This old condition was WRONG for names like design.html.old. Instead, we now get the extension
            // by using preg_match to match case-insensitive (probably faster than 4 conditions).
            //if (($extension[1]=='htm') or ($extension[1]=='html') or ($extension[1]=='HTM') or ($extension[1]=='HTML')) {
            // 4. Check the file extension.
            if (preg_match('/.*(\.htm(l)?)$/i', $pathname[$last])) {
                // 4.a If this file ends with ".htm(l)", we consider it's an HTML file.
                // src tag check begin
                // We now check if there is any src attribute in htm(l) files, if yes, we have to also export
                // the target file (swf, mp3, video,...) of that src tag.
                // In case of absolute links (http://) this is not neccessary, but makes no error.
                // However still missing : relative links case with subdirs -> the necessary dirs are not created in the exported package.

                // 4.a.1 Get the file contents into $file.
                $file = file_get_contents($orig);

                // 4.a.2 Get all the src links in this file.
                //preg_match_all("|((?i)src=\".*\" )|U",$file,$match);
                $match = GetSRCTags($orig);

                // 4.a.3 For each src tag found, do the following:
                for ($i = 0; $i < count($match); $i ++) {
                    // 4.a.3.1 Get the tag (split from the key).
                    list ($key, $srctag) = each($match);
                    $src = $srctag;

                    // 4.a.3.2 Check the link kind (web or absolute/relative).
                    if (stristr($src, 'http') === false) {
                        // 4.a.3.2.a Do something only if relative (otherwise the user will be able to see it too anyway).
                        // 4.a.3.2.a.1 Get a proper URL and remove all './'
                        $src = urldecode($src); //mp3
                        //$src=str_replace('./','',$src);
                        $src = preg_replace('/^\.\//', '', $src);
                        // 4.a.3.2.a.2 Remove the player link from the URL (only use the mp3 file).
                        $src = str_replace('mp3player.swf?son=', '', $src); //mp3
                        // 4.a.3.2.a.3 Remove funny link parts.
                        $src = str_replace('?0', '', $src); //mp3
                        // The previous lines are used when creating docs with Chamilo Document tool's htmlarea
                        // Rows marked by 'mp3' are needed because the mp3 plugin inserts the swf-mp3 links in a very strange way
                        // and we can decode them with those 3 lines, hoping this will not cause errors in case of other htmls,
                        // created by any other software.
                        // 4.a.3.2.a.4 Prepare source and destination paths.
                        $source = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.dirname($myrow['path']).'/'.$src;
                        $dest = $expdir.'/data/'.$src;
                        //CopyNCreate($source,$dest);
                        rcopy($source, $dest);
                    } //else...?
                }

                // src tag check end

                // sco communication insertion begin
                // 4.a.4 If we want to add SCORM actions and a "Done" button, do the following:
                if ($add_scorm_communications === true) {
                    if ($bodyclose = strpos($file, '</body>')) {
                        $file = substr_replace($file, $scocomfiles.$donebutton, $bodyclose, 7);
                    } elseif ($htmlclose = strpos($file, '</html>')) {
                        $file = substr_replace($file, $scocomfiles.$donebutton, $htmlclose, 7);
                        $file .= '</html>';
                    } else {
                        $file .= $scocomfiles.$donebutton;
                    }
                } //sco communication insertion end

                // 4.a.5 Replace the file's name by adding the element's ID before htm.
                // This will not work with uppercase HTML though. Maybe use the preg_replace syntax proposed...
                $filename = str_replace('.htm', $id.'.htm', $filename);
                //$filename=preg_replace('/.*(\.htm(l)?)$/i',$id.$1,$filename);
                // 4.a.6 Export these contents to a file and set the circle1_files array for later reuse.
                exporttofile($filename, $LPname, $id, $file);

                // The file has been copied, so ask not to copy it again.
                $copyneeded = false;

            } //if (htm(l) files) end

            // 5. If we still need to copy the file (e.g. it was not an HTML file), then copy and set circle1_files for later reuse.
            if ($copyneeded) {
                copy($orig, $expdir.'/'.$filename);
                $circle1_files[0][] = $filename;
                $circle1_files[1][] = $LPname;
                $circle1_files[2][] = $id;
            }

            //echo $orig;
            return;

        // Introduction_text BEGIN
        case 'Introduction_text':
            // 1 Get the introduction text data from the database.
            $TBL_INTRO = Database :: get_course_tool_intro_table();
            // Modified by Ivan Tcholakov, 15-SEP-2008.
            //$result = Database::query("SELECT * FROM ".$TBL_INTRO." WHERE id=1");
            $result = Database::query("SELECT * FROM ".$TBL_INTRO." WHERE c_id = $course_id AND id='course_homepage'");
            //
            $myrow = Database::fetch_array($result);
            $intro = $myrow['intro_text'];
            // 2 Write introduction text to the export string.
            $expcontent .= '<br />'.$intro;
            break;

        // HotPotatoes BEGIN
        case 'HotPotatoes':
            // 1. Get HotPotatoes data from the document table.
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM $tbl_document WHERE c_id = $course_id AND id=$item_id");
            $myrow = Database::fetch_array($result);
            // 2. Get the document path.
            $testfile = api_get_path(SYS_COURSE_PATH).$_course['path']."/document".urldecode($myrow['path']);
            // 3. Get the document contents into a string.
            $content = file_get_contents($testfile);
            // 4. Get the document filename (just the file, no path) - would probably be better to use PHP native function.
            $pathname = explode('/', $myrow['path']);
            $last = count($pathname) - 1;
            $filename = 'data/'.$filename.$pathname[$last];

            // 4beta - get all linked files and copy them (procedure copied from documents type).
            // Get all the src links in this file.
            $match = GetSRCTags($testfile);
            // For each src tag found, do the following:
            foreach ($match as $src) {
                //Check the link kind (web or absolute/relative)
                if (stristr($src, 'http') === false) {
                    // Do something only if relative (otherwise the user will be able to see it too anyway).
                    // Get a proper URL and remove all './'
                    $src = urldecode($src); //mp3
                    $src = str_replace('./', '', $src);
                    // Remove the player link from the URL (only use the mp3 file).
                    $src = str_replace('mp3player.swf?son=', '', $src); //mp3
                    // Remove funny link parts.
                    $src = str_replace('?0', '', $src); //mp3
                    // The previous lines are used when creating docs with Chamilo Document tool's htmlarea.
                    // Rows marked by 'mp3' are needed because the mp3 plugin inserts the swf-mp3 links in a very strange way
                    // and we can decode them with those 3 lines, hoping this will not cause errors in case of other htmls,
                    // created by any other software.
                    // Prepare source and destination paths.
                    $source = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.dirname($myrow['path']).'/'.$src;
                    $dest = $expdir.'/data/'.$src;
                    //CopyNCreate($source,$dest);
                    rcopy($source, $dest);
                } //else...?
            }

            // 5. Prepare the special "close window" for this test.
            $closewindow = "<html><head><link rel='stylesheet' type='text/css' href='../css/default.css'></head><body>"."<br /><div class='message'>$langHotPotatoesFinished</div></body></html>";

            // Finish is the function of HP to save scores, we insert our scorm function calls to its beginning
            // 'Score' is the variable that tracks the score in HP tests.
            // 6.
            $mit = "function Finish(){";

            $js_content = "var SaveScoreVariable = 0; // This variable is included by Chamilo LP export\n"."function mySaveScore() // This function is included by Chamilo LP export\n"."{\n"."   if (SaveScoreVariable==0)\n"."		{\n"."	   SaveScoreVariable = 1;\n".
                //the following function are implemented in SCOFunctions.js
    "      exitPageStatus = true;\n"."      computeTime();\n"."      doLMSSetValue( 'cmi.core.score.raw', Score );\n"."      doLMSSetValue( 'cmi.core.lesson_status', 'completed' );\n"."      doLMSCommit();\n"."      doLMSFinish();\n".
                //				"      document.write('".$closewindow."');\n".
        //if you insert the previous row, the test does not appear correctly !!!!
    "		}\n"."}\n"."function Finish(){\n"." mySaveScore();";

            $start = "<script type='text/javascript'> loadPage(); </script>";
            // 7. Replace the current MIT function call by our set of functions. In clear, transform HP to SCORM.
            $content = str_replace($mit, $js_content, $content);
            // 8. Finally, add the API loading calls (although that might have been done first).
            $content = str_replace("</script>", "</script>".$scocomfiles.$start, $content);

            // 9. Change the filename to add the database ID and export to a new file,
            // setting the circle1_files array for later reuse.
            $filename = str_replace('.htm', $id.'.htm', $filename);
            exporttofile($filename, $LPname, $id, $content);

            return;

        // Chamilo test BEGIN
        case 'Exercise':
            //1 Use the export_exercise() function to do the job of constructing the question's HTML table
            $expcontent .= export_exercise($item_id);
            break;

        // POST BEGIN
        case 'Post':
            // 1. Get the forum post data from the database.
            $tbl_posts =Database::get_course_table(TABLE_FORUM_POST);
            $tbl_posts_text =Database::get_course_table(TOOL_FORUM_POST_TEXT_TABLE);
            $result = Database::query("SELECT * FROM $tbl_posts where c_id = $course_id AND post_id=$item_id");
            $myrow = Database::fetch_array($result);
            // Grabbing the title of the post.
            $sql_titel = "SELECT * FROM $tbl_posts_text WHERE c_id = $course_id AND post_id=".$myrow['post_id'];
            $result_titel = Database::query($sql_titel);
            $myrow_titel = Database::fetch_array($result_titel);

            $posternom = $myrow['nom'];
            $posterprenom = $myrow['prenom'];
            $posttime = $myrow['post_time'];
            $posttext = $myrow_titel['post_text'];
            $posttitle = $myrow_titel['post_title'];
            $posttext = str_replace('"', "'", $posttext);

            //2 Export contents as an HTML table
            $expcontent .= "<table border='0' cellpadding='3' cellspacing='1' width='100%'>
                            <tr>
                                <td colspan='2' bgcolor='#e6e6e6'><b>$posttitle</b><br />$posttext</td>
                            </tr>
                            <tr>
                                <td colspan='2'></td>
                            </tr>
                            <tr>
                                <td bgcolor='#cccccc' align='left'>$lg_author : $posterprenom $posternom</td>
                                <td align='right' bgcolor='#cccccc'>$lg_date : $posttime</td>
                            </tr>
                            <tr><td colspan='2' height='10'></td></tr>
                        </table>";
            break;

        // NOT IMPLEMENTED ITEMS BEGIN
        case 'Forum':
        case 'Thread':
        case 'Dropbox':
        case 'Assignments':
        case 'Groups':
        case 'Users':
            // 1. Instead of building something, put an info message.
            $langItemMissing1 = "There was a ";
            $langItemMissing2 = "page (step) here in the original Chamilo Learning Path.";
            $expcontent .= "<div class='message'>$langItemMissing1 $item_type $langItemMissing2</div>";
            break;

        // Link BEGIN
        case 'Link _self':
        case 'Link _blank':
            // 1. Get the link data from the database.
            $TABLETOOLLINK = Database :: get_course_link_table();
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$item_id");
            $myrow = Database::fetch_array($result);
            $thelink = $myrow['url'];
            // 2. Check the link type (open in blank page or in current page).
            if ($item_type == 'Link _blank')
            {
                $target = '_blank';
            }
            // 3. Write the link to the export string.
            $expcontent .= "<a href='$thelink?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target='".$target."'>$LPname</a>";
            // 4. Change the element type for later changes (this is lost, however, so useless here).
            $item_type = 'Link'; // To put this to the filename.
            //$LPname="<a href='$thelink?SQMSESSID=36812c2dea7d8d6e708d5e6a2f09b0b9' target=".$target.">$LPname</a>";
            // I am still not sure about Link export : to export them as files or they can appear in the TOC at once ?
            // To enable the second possibility, unrem the row $LPname=...
            break;
    }

    // Now we add the Done button and the initialize function : loadpage()
    // not in the case of Documents, HotP
    if ($item_type != 'Exercise' and ($add_scorm_communications === true)) {
        $expcontent .= $donebutton;
    }
    // End the export string with valid HTML tags.
    $expcontent .= "</body></html>";

    // Prepare new file name.
    $filename = $item_type.$id.".htm";
    // Write the export content to the new file.
    exporttofile('data/'.$filename, $LPname, $id, $expcontent);
}

/**
 * This function exports the given item's description into a separate file
 * @param	integer	Item id
 * @param	string	Item type
 * @param	string	Description
 * @return void
 */
function exportdescription($id, $item_type, $description) {
    global $expdir;
    $filename = $item_type.$id.'.desc';
    $expcontent = $description;
    exporttofile($expdir.$filename, 'description_of_'.$item_type.$id, 'description_of_item_'.$id, $expcontent);
}

/**
 * This function deletes an entire directory
 * @param	string	The directory path
 * @return boolean	True on success, false on failure
 */
function deldir($dir) {
    $dh = opendir($dir);
    while ($file = readdir($dh)) {
        if ($file != '.' && $file != '..') {
            $fullpath = $dir.'/'.$file;
            if (!is_dir($fullpath)) {
                unlink($fullpath);
            } else {
                deldir($fullpath);
            }
        }
    }

    closedir($dh);

    if (rmdir($dir)) {
        return true;
    }
    return false;
}

/**
 * Export SCORM content into a zip file
 *
 * Basically, all this function does is put the scorm directory back into a zip file (like the one
 * that was most probably used to import the course at first)
 * @deprecated this function is only called in the newscorm/scorm_admin.php which is deprecated
 * 
 * @param	string	Name of the SCORM path (or the directory under which it resides)
 * @param	array		Not used right now. Should replace the use of global $_course
 * @return	void
 * @author	imandak80
 */
function exportSCORM($scormname, $course) {
    global $_course;

    // Initialize.
    $tmpname = api_get_path(SYS_COURSE_PATH).$_course['path'].'/scorm';
    $zipfoldername = $tmpname.$scormname;
    $zipfilename = $zipfoldername.'.zip';

    // Create zipfile of given directory.
    include_once api_get_path(LIBRARY_PATH).'pclzip/pclzip.lib.php';
    $zip_folder = new PclZip($zipfilename);
    $list = 1;
    //$list = $zip_folder->create($zipfoldername.'/',PCLZIP_OPT_REMOVE_PATH,$tmpname.$scormname."/"); // whitout folder
    $list = $zip_folder->create($zipfoldername.'/', PCLZIP_OPT_REMOVE_PATH, $tmpname);
    if ($list == 0) {
        //echo "Error  : ".$zip_folder->errorInfo(true);
    }

    // Send to client.
    DocumentManager :: file_send_for_download($zipfilename, false, basename($scormname.'.zip'));

    // Clear.
    include_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
    my_delete($zipfilename);
}

/**
 * This function returns an xml tag
 * $data behaves as the content in case of full tags
 * $data is an array of attributes in case of returning an opening tag
 * @param	string
 * @param	string
 * @param	array
 * @param	string
 * @return string
 */
function xmltagwrite($tagname, $which, $data, $linebreak = 'yes') {
    switch ($which) {
        case 'open':
            $tag = '<'.$tagname;
            $i = 0;
            while ($data[0][$i]) {
                $tag .= ' '.$data[0][$i]."=\"".$data[1][$i]."\"";
                $i ++;
            }
            if ($tagname == 'file') {
                $closing = '/';
            }
            $tag .= $closing.'>';
            if ($linebreak != 'no_linebreak') {
                $tag .= "\n";
            }
            break;
        case 'close':
            $tag = '</'.$tagname.'>';
            if ($linebreak != 'no_linebreak') {
                $tag .= "\n";
            }
            break;
        case 'full':
            $tag = '<'.$tagname;
            $tag .= '>'.$data.'</'.$tagname.'>';
            if ($linebreak != 'no_linebreak') {
                $tag .= "\n";
            }
            break;
    }
    return $tag;
}

/**
 * This function writes the imsmanifest.xml and exports the chapter names
 * @param	array		Array containing filenames
 * @param	integer	Learnpath_id
 * @return	void
 */
function createimsmanifest($circle1_files, $learnpath_id) {
    global $_course, $LPname, $expdir, $LPnamesafe;
    //$tbl_learnpath_main, $tbl_learnpath_chapter, $tbl_learnpath_item,
    $tbl_learnpath_main = Database :: get_course_table(TABLE_LEARNPATH_MAIN);
    $tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
    $tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);

    include_once '../metadata/md_funcs.php'; // RH: export metadata

    // Header
    // Charset should be dependent on content.
    $header = '<?xml version="1.0" encoding="'.api_get_system_encoding().'"?>'."\n<manifest identifier='".$LPnamesafe."' version='1.1'\n xmlns='http://www.imsproject.org/xsd/imscp_rootv1p1p2'\n xmlns:adlcp='http://www.adlnet.org/xsd/adlcp_rootv1p2'\n xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n xsi:schemaLocation='http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd\n http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd\n http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd'>\n";

    $org .= xmltagwrite('metadata', 'open');
    $org .= '  '.xmltagwrite('schema', 'full', 'ADL SCORM');
    $org .= '  '.xmltagwrite('schemaversion', 'full', '1.2');
    $org .= xmltagwrite('metadata', 'close');

    $defaultorgname = 'default_org';

    $attributes[0][0] = 'default';
    $attributes[1][0] = $defaultorgname;
    $org .= xmltagwrite('organizations', 'open', $attributes);

    $attributes[0][0] = 'identifier';
    $attributes[1][0] = $defaultorgname;
    $org .= '  '.xmltagwrite('organization', 'open', $attributes);

    $org .= '    '.xmltagwrite('title', 'full', $LPname);

    // Items list.
    $i = 0;
    $course_id = api_get_course_int_id();
    
    $previous_item_id = '00';
    while ($circle1_files[0][$i]) {
        // Check whether we are in the border of two chapters.
        //if (!$desc=strpos($circle1_files[2][$i],'scription')) {  // This is needed if the descriptions are exported to file.

        $sql = "SELECT * FROM $tbl_learnpath_item WHERE c_id = $course_id AND (id=".$circle1_files[2][$i].")";
        $result = Database::query($sql);
        $row = Database::fetch_array($result);
        $parent_item_id = $row['parent_item_id'];

        if ($parent_item_id != $previous_item_id) {
            // We create the item tag for the chapter (without indifierref).
            $sql2 = "SELECT * FROM $tbl_learnpath_chapter WHERE c_id = $course_id AND (id=".$parent_item_id.")";
            $result2 = Database::query($sql2);
            $row2 = Database::fetch_array($result2);
            $chapter_name = $row2['chapter_name'];

            $attributes = '';
            $attributes[0][] = 'identifier';
            $attributes[1][] = 'chapter_'.$row2['id'];
            $attributes[0][] = 'isvisible';
            $attributes[1][] = '1';
            if ($previous_item_id != '00') {
                $org .= '    '.xmltagwrite('item', 'close');
            }

            $org .= '    '.xmltagwrite('item', 'open', $attributes);
            $org .= '      '.xmltagwrite('title', 'full', $chapter_name);

            if ($row2['chapter_description'] != '') {
                // Chapter description.
                $attributes = '';
                $attributes[0][] = 'identifier';
                $attributes[1][] = 'chapter_'.$row2['id'].'_desc';
                $attributes[0][] = 'isvisible';
                $attributes[1][] = '1';
                $org .= '    '.xmltagwrite('item', 'open', $attributes);
                $org .= '      '.xmltagwrite('title', 'full', ' '.$row2['chapter_description']);
                $org .= '    '.xmltagwrite('item', 'close');
            }
        }
        $previous_item_id = $parent_item_id;
        //}

        $attributes = ''; // Item output.
        $attributes[0][] = 'identifier';
        $attributes[1][] = 'item_'.$circle1_files[2][$i];
        $attributes[0][] = 'identifierref';
        $attributes[1][] = 'item_ref_'.$circle1_files[2][$i];
        $attributes[0][] = 'isvisible';
        $attributes[1][] = '1';
        $org .= '    '.xmltagwrite('item', 'open', $attributes);
        $org .= '      '.xmltagwrite('title', 'full', $circle1_files[1][$i]);

        if ($row['prereq_id'] != '') {
            // Item prerequisites.
            $attributes = '';
            $attributes[0][] = 'type';
            $attributes[1][] = 'aicc_script';
            $org .= '      '.xmltagwrite('adlcp:prerequisites', 'open', $attributes, 'no_linebreak');
            if ($row['prereq_type'] == 'i') {
                $org .= 'item_'.$row['prereq_id'];
            }
            if ($row['prereq_type'] == 'c') {
                $org .= 'chapter_'.$row['prereq_id'];
            }
            $org .= xmltagwrite('adlcp:prerequisites', 'close', $attributes);
        }

        if ($row['description'] != '') {
            // Item description.
            $attributes = '';
            $attributes[0][] = 'identifier';
            $attributes[1][] = 'item_'.$circle1_files[2][$i].'_desc';
            $attributes[0][] = 'isvisible';
            $attributes[1][] = '1';
            $org .= '    '.xmltagwrite('item', 'open', $attributes);
            $org .= '      '.xmltagwrite('title', 'full', ' '.$row['description']);
            $org .= '    '.xmltagwrite('item', 'close');
        }

        $mds = new mdstore(true); // RH: export metadata; if no table, create it
        if (($mdt = $mds->mds_get($row['item_type'].'.'.$row['item_id'])))
            if (($mdo = api_strpos($mdt, '<metadata>')) && ($mdc = api_strpos($mdt, '</metadata>')))
                $org .= '    '.api_substr($mdt, $mdo, $mdc - $mdo + 11)."\n";

        $org .= '    '.xmltagwrite('item', 'close');
        $i ++;
    }

    if ($circle1_files) {
        $org .= '    '.xmltagwrite('item', 'close');
    } // Not needed in case of a blank path.
    $org .= '  '.xmltagwrite('organization', 'close');
    $org .= xmltagwrite('organizations', 'close');
    $org .= xmltagwrite('resources', 'open');

    // Resources list.
    $i = 0;
    while ($circle1_files[0][$i]) {
        $attributes = '';
        $attributes[0][] = 'identifier';
        $attributes[1][] = 'item_ref_'.$circle1_files[2][$i];
        $attributes[0][] = 'type';
        $attributes[1][] = 'webcontent';
        $attributes[0][] = 'adlcp:scormtype';
        $attributes[1][] = 'sco';
        $attributes[0][] = 'href';
        $attributes[1][] = $circle1_files[0][$i];
        $org .= '  '.xmltagwrite('resource', 'open', $attributes);

        $org .= '    '.xmltagwrite('metadata', 'open');
        $org .= '      '.xmltagwrite('schema', 'full', 'ADL SCORM');
        $org .= '      '.xmltagwrite('schemaversion', 'full', '1.2');
        $org .= '    '.xmltagwrite('metadata', 'close');

        $attributes = '';
        $attributes[0][] = 'href';
        $attributes[1][] = $circle1_files[0][$i];
        $org .= '    '.xmltagwrite('file', 'open', $attributes);

        $org .= '  '.xmltagwrite('resource', 'close');
        $i ++;
    }

    $org .= xmltagwrite('resources', 'close');
    $org .= xmltagwrite('manifest', 'close');
    $manifest = $header.$org;

    exporttofile('imsmanifest.xml', 'Manifest file', '0', $manifest);
}

/**
 * Gets the tags of the file given as parameter
 *
 * if $filename is not found, GetSRCTags(filename) will return FALSE
 * @param string		File path
 * @return mixed		Array of strings on success, false on failure
 * @author unknown
 * @author Included by imandak80
 */
function GetSRCTags($fileName) {
    if (!($fp = fopen($fileName, 'r'))) {
        // Iif file can't be opened, return false.
        return false;
    }
    // Read file contents.
    $contents = fread($fp, filesize($fileName));
    fclose($fp);

    $matches = array();
    $srcList = array();
    // Get all src tags contents in this file. Use multi-line search.
    preg_match_all('/src(\s)*=(\s)*[\'"]([^\'"]*)[\'"]/mi', $contents, $matches); // Get the img src as contained between " or '

    foreach ($matches[3] as $match) {
        if (!in_array($match, $srcList)) {
            $srcList[] = $match;
        }
    }
    if (count($srcList) == 0) {
        return false;
    }
    return $srcList;
}

/**
 * Copy file and create directories in the path if needed.
 *
 * @param	string	$source Source path
 * @param	string	$dest Destination path
 * @return boolean 	true on success, false on failure
 */
function CopyNCreate($source, $dest) {
    if (strcmp($source, $dest) == 0)
        return false;

    $dir = '';
    $tdest = explode('/', $dest);
    for ($i = 0; $i < sizeof($tdest) - 1; $i ++) {
        $dir = $dir.$tdest[$i].'/';
        if (!is_dir($dir))
            if (!mkdir($dir, api_get_permissions_for_new_directories()))
                return false;
    }

    if (!copy($source, $dest))
        return false;

    return true;
}

function rcopy($source, $dest) {
    //error_log($source." -> ".$dest, 0);
    if (!file_exists($source)) {
        //error_log($source." does not exist", 0);
        return false;
    }

    if (is_dir($source)) {
        //error_log($source." is a dir", 0);
        // This is a directory.
        // Remove trailing '/'
        if (strrpos($source, '/') == sizeof($source) - 1) {
            $source = substr($source, 0, size_of($source) - 1);
        }
        if (strrpos($dest, '/') == sizeof($dest) - 1) {
            $dest = substr($dest, 0, size_of($dest) - 1);
        }

        if (!is_dir($dest)) {
            $res = @mkdir($dest, api_get_permissions_for_new_directories());
            if ($res !== false) {
                return true;
            } else {
                // Remove latest part of path and try creating that.
                if (rcopy(substr($source, 0, strrpos($source, '/')), substr($dest, 0, strrpos($dest, '/')))) {
                    return @mkdir($dest, api_get_permissions_for_new_directories());
                } else {
                    return false;
                }
            }
        }
        return true;
    } else {
        // This is presumably a file.
        //error_log($source." is a file", 0);
        if (!@ copy($source, $dest)) {
            //error_log("Could not simple-copy $source", 0);
            $res = rcopy(dirname($source), dirname($dest));
            if ($res === true) {
                //error_log("Welcome dir created", 0);
                return @ copy($source, $dest);
            } else {
                return false;
                //error_log("Error creating path", 0);
            }
        } else {
            //error_log("Could well simple-copy $source", 0);
            return true;
        }
    }
}
lp_add.php000064400000014122152003363470006503 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @author Julio Montoya <gugli100@gmail.com> Adding formvalidator support
 *
 * @package chamilo.learnpath
 */
/**
 * Code
 */
$this_section = SECTION_COURSES;
api_protect_course_script();

/* Libraries */

require 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
require 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
// Name of the language file that needs to be included.
$language_file = 'learnpath';

/* Header and action code */

$currentstyle = api_get_setting('stylesheets');
$htmlHeadXtra[] = '<script>
function setFocus(){
    $("#learnpath_title").focus();
}

$(document).ready(function () {
    setFocus();
});

function advanced_parameters() {
    if(document.getElementById(\'options\').style.display == \'none\') {
        document.getElementById(\'options\').style.display = \'block\';
        document.getElementById(\'img_plus_and_minus\').innerHTML=\'&nbsp;<img style="vertical-align:middle;" src="../img/div_hide.gif" alt="" />&nbsp;'.get_lang('AdvancedParameters').'\';
    } else {
        document.getElementById(\'options\').style.display = \'none\';
        document.getElementById(\'img_plus_and_minus\').innerHTML=\'&nbsp;<img style="vertical-align:middle;" src="../img/div_show.gif" alt="" />&nbsp;'.get_lang('AdvancedParameters').'\';
    }
}

function activate_start_date() {
	if(document.getElementById(\'start_date_div\').style.display == \'none\') {
		document.getElementById(\'start_date_div\').style.display = \'block\';
	} else {
		document.getElementById(\'start_date_div\').style.display = \'none\';
	}
}

function activate_end_date() {
    if(document.getElementById(\'end_date_div\').style.display == \'none\') {
        document.getElementById(\'end_date_div\').style.display = \'block\';
    } else {
        document.getElementById(\'end_date_div\').style.display = \'none\';
    }
}
</script>';

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$isStudentView  = isset($_REQUEST['isStudentView']) ? $_REQUEST['isStudentView'] : null;
$learnpath_id   = isset($_REQUEST['lp_id']) ? $_REQUEST['lp_id'] : null;

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}

if ((!$is_allowed_to_edit) || ($isStudentView)) {
    //error_log('New LP - User not authorized in lp_add.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
}
// From here on, we are admin because of the previous condition, so don't check anymore.

/*
    Course admin section
    - all the functions not available for students - always available in this case (page only shown to admin)
*/
if (isset($_SESSION['gradebook'])){
    $gradebook =	$_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook=='view') {
    $interbreadcrumb[]= array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));

Display::display_header(get_lang('LearnpathAddLearnpath'), 'Path');

echo '<div class="actions">';
echo '<a href="lp_controller.php?cidReq='.$_course['sysCode'].'">'.Display::return_icon('back.png', get_lang('ReturnToLearningPaths'),'',ICON_SIZE_MEDIUM).'</a>';
echo '</div>';

Display::display_normal_message(get_lang('AddLpIntro'), false);

if ($_POST AND empty($_REQUEST['lp_name'])) {
    Display::display_error_message(get_lang('FormHasErrorsPleaseComplete'), false);
}

$form = new FormValidator('lp_add', 'post', 'lp_controller.php');

// Form title
$form->addElement('header', null, get_lang('AddLpToStart'));

// Title
$form->addElement('text', 'lp_name', api_ucfirst(get_lang('LPName')), array('class' => 'span6'));
$form->applyFilter('lp_name', 'html_filter');
$form->addRule('lp_name', get_lang('ThisFieldIsRequired'), 'required');

$form->addElement('hidden', 'post_time', time());
$form->addElement('hidden', 'action', 'add_lp');

$advanced = '<a href="javascript://" onclick=" return advanced_parameters()"><span id="img_plus_and_minus"><div style="vertical-align:top;" ><img style="vertical-align:middle;" src="../img/div_show.gif" alt="" />&nbsp;'.get_lang('AdvancedParameters').'</div></span></a>';
$form -> addElement('advanced_settings',$advanced);
$form -> addElement('html','<div id="options" style="display:none">');

//Start date
$form->addElement('checkbox', 'activate_start_date_check', null, get_lang('EnableStartTime'), array('onclick' => 'activate_start_date()'));
$form->addElement('html','<div id="start_date_div" style="display:block;">');
$form->addElement('datepicker', 'publicated_on', get_lang('PublicationDate'), array('form_name'=>'lp_add'), 5);
$form->addElement('html','</div>');

//End date
$form->addElement('checkbox', 'activate_end_date_check', null, get_lang('EnableEndTime'), array('onclick' => 'activate_end_date()'));
$form->addElement('html','<div id="end_date_div" style="display:none;">');
$form->addElement('datepicker', 'expired_on', get_lang('ExpirationDate'), array('form_name'=>'lp_add'), 5);
$form->addElement('html','</div>');

$form->addElement('html','</div>');

$defaults['activate_start_date_check']  = 1;

$defaults['publicated_on']  = date('Y-m-d 08:00:00');
$defaults['expired_on']     = date('Y-m-d 08:00:00',time()+84600);

$form->setDefaults($defaults);
$form->addElement('style_submit_button', 'Submit',get_lang('CreateLearningPath'),'class="save"');


$form->display();
// Footer
Display::display_footer();lp_add_audio.php000064400000011103152003363470007660 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This is a learning path creation and player tool in Chamilo - previously
 * @author Julio Montoya  - Improving the list of templates
 * @package chamilo.learnpath
 */
/**
 * INIT SECTION
 */

$this_section = SECTION_COURSES;

api_protect_course_script();

include 'learnpath_functions.inc.php';
include 'resourcelinker.inc.php';

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$isStudentView  = (int) $_REQUEST['isStudentView'];
$learnpath_id   = (int) $_REQUEST['lp_id'];
$submit			= $_POST['submit_button'];


$type = isset($_GET['type']) ? $_GET['type'] : null;
$action = isset($_GET['action']) ? $_GET['action'] : null;

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_add_item.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
    exit;
}
/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", 'name' => $_SESSION['oLP']->get_name());

switch ($type) {
    case 'chapter':
        $interbreadcrumb[]= array ('url' => 'lp_controller.php?action=add_item&type=step&lp_id='.$_SESSION['oLP']->get_id(), 'name' => get_lang('NewStep'));
        $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewChapter'));
        break;
    case 'document':
        $interbreadcrumb[]= array ('url' => 'lp_controller.php?action=add_item&type=step&lp_id='.$_SESSION['oLP']->get_id(), 'name' => get_lang('NewStep'));
        break;
    default:
        $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewStep'));
        break;
}

if ($action == 'add_item' && $type == 'document' ) {
    $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewDocumentCreated'));
}

// Theme calls.
$show_learn_path = true;
$lp_item_id = isset($_GET['id']) ? intval($_GET['id']) : null;
if (empty($lp_item_id)) {
    api_not_allowed();
}

Display::display_header(null, 'Path');

$suredel = trim(get_lang('AreYouSureToDelete'));

/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid" style="overflow:hidden">';
echo '<div id="lp_sidebar" class="span4">';
echo $_SESSION['oLP']->return_new_tree(null, true); 
// Show the template list.
echo '</div>';

echo '<div id="doc_form" class="span8">';

$form = new FormValidator('add_audio', 'post', api_get_self().'?action=add_audio&id='.$lp_item_id, null, array('enctype' => 'multipart/form-data'));
$form->addElement('header', get_lang('UplUpload'));
$form->addElement('file', 'file', get_lang('File'));
$form->addElement('hidden', 'id', $lp_item_id);

$lp_item = new learnpathItem($lp_item_id);

if (isset($lp_item->audio) && !empty($lp_item->audio))  {
    $form->addElement('checkbox', 'delete_file', null, get_lang('RemoveAudio'));
    
    $player = '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
    $player .= '<div id="preview"></div><script type="text/javascript">
                    var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
                    s1.addParam("allowscriptaccess","always");
                    s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $lp_item->audio . '");
                    s1.write("preview");
                </script>';    
    $form->addElement('label', get_lang('Preview'), $player);
}
$form->addElement('button', 'submit', get_lang('Edit'));

$course_info = api_get_course_info();
$document_tree = DocumentManager::get_document_preview($course_info, null, null, 0, false, '/audio', 'lp_controller.php?action=add_audio&id='.$lp_item_id);

$form->display();

echo '<legend>'.get_lang('SelectAnAudioFileFromDocuments').'</legend>';
echo $document_tree;
echo '</div>';

echo '</div>';

/* FOOTER */
Display::display_footer();lp_add_item.php000064400000027555152003363470007537 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This is a learning path creation and player tool in Chamilo - previously
 * learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update
 * @author Julio Montoya  - Improving the list of templates
 * @package chamilo.learnpath
 */
/**
 * INIT SECTION
 */

$this_section = SECTION_COURSES;

api_protect_course_script();

include 'learnpath_functions.inc.php';
include 'resourcelinker.inc.php';

$language_file = 'learnpath';

$htmlHeadXtra[] = '
<script>
var temp = false;
var load_default_template = '. ((isset($_POST['submit']) || empty($_SERVER['QUERY_STRING'])) ? 'false' : 'true' ) .';

function FCKeditor_OnComplete( editorInstance ) {
    editorInstance.Events.AttachEvent( \'OnSelectionChange\', check_for_title) ;
    //document.getElementById(\'frmModel\').innerHTML = "<iframe id=\'frame_template\' name=\'my_frame_template\' height=890px width=220px; frameborder=0 src=\''.api_get_path(WEB_LIBRARY_PATH).'fckeditor/editor/fckdialogframe.html \'>";
    loaded = true;
}

function check_for_title() {
    if (temp) {

        // This functions shows that you can interact directly with the editor area
        // DOM. In this way you have the freedom to do anything you want with it.

        // Get the editor instance that we want to interact with.
        var oEditor = FCKeditorAPI.GetInstance(\'content_lp\') ;

        // Get the Editor Area DOM (Document object).
        var oDOM = oEditor.EditorDocument ;

        var iLength ;
        var contentText ;
        var contentTextArray;
        var bestandsnaamNieuw = "";
        var bestandsnaamOud = "";

        // The are two diffent ways to get the text (without HTML markups).
        // It is browser specific.

        if( document.all )		// If Internet Explorer.
        {
            contentText = oDOM.body.innerText ;
        }
        else					// If Gecko.
        {
            var r = oDOM.createRange() ;
            r.selectNodeContents( oDOM.body ) ;
            contentText = r.toString() ;
        }

        var index=contentText.indexOf("/*<![CDATA");
        contentText=contentText.substr(0,index);

        // Compose title if there is none
        contentTextArray = contentText.split(\' \') ;
        var x=0;
        for(x=0; (x<5 && x<contentTextArray.length); x++) {
            if(x < 4) {
                bestandsnaamNieuw += contentTextArray[x] + \' \';
            } else {
                bestandsnaamNieuw += contentTextArray[x];
            }
        }
    }
    temp=true;
}

function InnerDialogLoaded() {
    if (document.all) {
        // if is iexplorer
        var B=new window.frames.content_lp___Frame.FCKToolbarButton(\'Templates\',window.content_lp___Frame.FCKLang.Templates);
    } else {
        var B=new window.frames[0].FCKToolbarButton(\'Templates\',window.frames[0].FCKLang.Templates);
    }
    return 	B.ClickFrame();
};'."\n".

$_SESSION['oLP']->get_js_dropdown_array() .

'function load_cbo(id){' ."\n" .
  'if (!id) {return false;}'.
  'var cbo = document.getElementById(\'previous\');' .
  'for(var i = cbo.length - 1; i > 0; i--) {' .
    'cbo.options[i] = null;' .
  '}' ."\n" .
  'var k=0;' .
  'for(var i = 1; i <= child_name[id].length; i++){' ."\n" .
  '  cbo.options[i] = new Option(child_name[id][i-1], child_value[id][i-1]);' ."\n" .
  '  k=i;' ."\n" .
  '}' ."\n" .
  //'if( typeof cbo != "undefined" ) {'."\n" .
  'cbo.options[k].selected = true;'."\n" .
   //'}'."\n" .
'}

$(function() {
    if ($(\'#previous\')) {
        if(\'parent is\'+$(\'#idParent\').val()) {
            load_cbo($(\'#idParent\').val());
        }
    }
    //Loads LP item tabs

    $("#resource_tab").tabs();
    $(\'.lp_resource_element\').click(function() {
        window.location.href = $(\'a\', this).attr(\'href\');
    });
});
</script>';

/* Constants and variables */

//Already set in lp_controller.php
//$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$isStudentView  = isset($_REQUEST['isStudentView']) ? $_REQUEST['isStudentView'] : null;
$learnpath_id   = isset($_REQUEST['lp_id']) ? intval($_REQUEST['lp_id']) : null;
$submit			= isset($_POST['submit_button']) ? $_POST['submit_button'] : null;

$type = isset($_GET['type']) ? $_GET['type'] : null;
$action = isset($_GET['action']) ? $_GET['action'] : null;

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_add_item.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
    exit;
}
/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
        'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
        'name' => get_lang('ToolGradebook')
    );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", 'name' => $_SESSION['oLP']->get_name());

switch ($type) {
    case 'chapter':
        $interbreadcrumb[]= array ('url' => 'lp_controller.php?action=add_item&type=step&lp_id='.$_SESSION['oLP']->get_id(), 'name' => get_lang('NewStep'));
        $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewChapter'));
        break;
    case 'document':
        $interbreadcrumb[]= array ('url' => 'lp_controller.php?action=add_item&type=step&lp_id='.$_SESSION['oLP']->get_id(), 'name' => get_lang('NewStep'));
        break;
    default:
        $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewStep'));
        break;
}

if ($action == 'add_item' && $type == 'document' ) {
    $interbreadcrumb[]= array ('url' => '#', 'name' => get_lang('NewDocumentCreated'));
}

// Theme calls.
$show_learn_path = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

Display::display_header(null, 'Path');

$suredel = trim(get_lang('AreYouSureToDelete'));
//@todo move this somewhere else css/fix.css
?>
<style>
    #feedback { font-size: 1.4em; }
    #resExercise .ui-selecting { background: #FECA40; }
    #resExercise .ui-selected { background: #F39814; color: white; }
    #resExercise { list-style-type: none; margin: 0; padding: 0; width: 60%; }
    #resExercise li { margin: 3px; padding: 0.4em; font-size: 1.4em; height: 18px; }

    /* Fixes LP toolbar */
    #resource_tab li a {
        padding: 5px 4px;
    }
</style>

<script>
function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}
function confirmation(name) {
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
        return true;
    } else {
        return false;
    }
}

$(document).ready(function() {
    $("#hide_bar_template").toggle(
        function() {
            $("#lp_sidebar").hide();
            $(this).css({'background-image' : 'url("../img/hide2.png")'})
            $("#doc_form").removeClass("span8");
            $("#doc_form").addClass("span11");
        },
        function() {
            $("#lp_sidebar").show();
            $("#doc_form").removeClass("span11");
            $("#doc_form").addClass("span8");
            $(this).css('background-image', 'url("../img/hide0.png")');
        }
    );
});
</script>
<?php

/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid" style="overflow:hidden">';
echo '<div id="lp_sidebar" class="span4">';

echo $_SESSION['oLP']->return_new_tree(null, true);

$message = isset($_REQUEST['message']) ? $_REQUEST['message'] : null;

// Show the template list.
if ($type == 'document' && !isset($_GET['file'])) {
    // Show the template list.
    echo '<div id="frmModel" style="display:block; height:890px;width:100px; position:relative;"></div>';
}

echo '</div>';

//hide bar div
if ($action == 'add_item' && $type == 'document' && !isset($_GET['file'])) {
    echo '<div id="hide_bar_template"></div>';
}

echo '<div id="doc_form" class="span8">';

//@todo use session flash messages
if (in_array($message, array('ItemUpdated'))) {
    echo Display::return_message(get_lang($message));
}

if (isset($new_item_id) && is_numeric($new_item_id)) {
    switch ($type) {
        case 'chapter':
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $_POST['type']);
            Display::display_confirmation_message(get_lang('NewChapterCreated'));
            break;
        case TOOL_LINK:
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewLinksCreated'));
            break;
        case TOOL_STUDENTPUBLICATION:
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewStudentPublicationCreated'));
            break;
        case 'module':
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewModuleCreated'));
            break;
        case TOOL_QUIZ:
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewExerciseCreated'));
            break;
        case TOOL_DOCUMENT:
            Display::display_confirmation_message(get_lang('NewDocumentCreated'));
            echo $_SESSION['oLP']->display_item($new_item_id);
            break;
        case TOOL_FORUM:
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewForumCreated'));
            break;
        case 'thread':
            echo $_SESSION['oLP']->display_manipulate($new_item_id, $type);
            Display::display_confirmation_message(get_lang('NewThreadCreated'));
            break;
    }
} else {
    switch ($type) {
        case 'chapter':
            echo $_SESSION['oLP']->display_item_form($type, get_lang('EnterDataNewChapter'));
            break;
        case 'module':
            echo $_SESSION['oLP']->display_item_form($type, get_lang('EnterDataNewModule'));
            break;
        case 'document':
            if (isset($_GET['file']) && is_numeric($_GET['file'])) {
                echo $_SESSION['oLP']->display_document_form('add', 0, $_GET['file']);
            } else {
                echo $_SESSION['oLP']->display_document_form('add', 0);
            }
            break;
        case 'hotpotatoes':
            echo $_SESSION['oLP']->display_hotpotatoes_form('add', 0, $_GET['file']);
            break;
        case 'quiz':
            echo Display::display_warning_message(get_lang('ExerciseCantBeEditedAfterAddingToTheLP'));
            echo $_SESSION['oLP']->display_quiz_form('add', 0, $_GET['file']);
            break;
        case 'forum':
            echo $_SESSION['oLP']->display_forum_form('add', 0, $_GET['forum_id']);
            break;
        case 'thread':
            echo $_SESSION['oLP']->display_thread_form('add', 0, $_GET['thread_id']);
            break;
        case 'link':
            echo $_SESSION['oLP']->display_link_form('add', 0, $_GET['file']);
            break;
        case 'student_publication':
            echo $_SESSION['oLP']->display_student_publication_form('add', 0, $_GET['file']);
            break;
        case 'step':
            $_SESSION['oLP']->display_resources();
            break;
    }
}
echo '</div>';
echo '</div>';

/* FOOTER */
Display::display_footer();lp_admin_view.php000064400000026354152003363470010107 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @package chamilo.learnpath
*/

/**
 * INIT SECTION 
 */

$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

include 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
include 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
$language_file = "learnpath";

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp      = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

$isStudentView  = (int) $_REQUEST['isStudentView'];
$learnpath_id   = (int) $_REQUEST['lp_id'];
$submit			= $_POST['submit_button'];

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' and $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_admin_view.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
}
// From here on, we are admin because of the previous condition, so don't check anymore.

$course_id = api_get_course_int_id(); 

$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";
$result = Database::query($sql_query);
$therow = Database::fetch_array($result);

/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", "name" => stripslashes("{$therow['name']}"));
$interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

if (isset($_REQUEST['updateaudio'])) {
    $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('UpdateAllAudioFragments'));
} else {
    $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('BasicOverview'));
}

// Theme calls.
$show_learn_path = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

/* DISPLAY SECTION */


// POST action handling (uploading mp3, deleting mp3)
if (isset($_POST['save_audio'])) {
    
    //Updating the lp.modified_on
    $_SESSION['oLP']->set_modified_on();                
                
    // Deleting the audio fragments.
    foreach ($_POST as $key => $value) {
        if (substr($key, 0, 9) == 'removemp3') {
            $lp_items_to_remove_audio[] = str_ireplace('removemp3', '', $key);
            // Removing the audio from the learning path item.
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
            $in = implode(',', $lp_items_to_remove_audio);
        }
    }
    if (count($lp_items_to_remove_audio)>0) {
        $sql 	= "UPDATE $tbl_lp_item SET audio = '' WHERE c_id = $course_id AND id IN (".$in.")";
        $result = Database::query($sql);
    }           

    // Uploading the audio files.
    foreach ($_FILES as $key => $value) {
        if (substr($key, 0, 7) == 'mp3file' AND !empty($_FILES[$key]['tmp_name'])) {
            // The id of the learning path item.
            $lp_item_id = str_ireplace('mp3file', '', $key);

            // Create the audio folder if it does not exist yet.
            global $_course;
            $filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/';
            if (!is_dir($filepath.'audio')) {
                mkdir($filepath.'audio', api_get_permissions_for_new_directories());
                $audio_id = add_document($_course, '/audio', 'folder', 0, 'audio');
                api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id(), null, null, null, null, api_get_session_id());
            }

            // Check if file already exits into document/audio/
            $file_name = $_FILES[$key]['name'];
            $file_name = stripslashes($file_name);
            // Add extension to files without one (if possible).
            $file_name = add_ext_on_mime($file_name, $_FILES[$key]['type']);

            $clean_name = replace_dangerous_char($file_name);
            // No "dangerous" files.
            $clean_name = disable_dangerous_file($clean_name);

            $check_file_path = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/audio/'.$clean_name;

            // If the file exists we generate a new name.
            if (file_exists($check_file_path)) {
                $filename_components = explode('.', $clean_name);
                // Gettting the extension of the file.
                $file_extension = $filename_components[count($filename_components) - 1];
                // Adding something random to prevent overwriting.
                $filename_components[count($filename_components) - 1] = time();
                // Reconstructing the new filename.
                $clean_name = implode($filename_components) .'.'.$file_extension;
                // Using the new name in the $_FILES superglobal.
                $_FILES[$key]['name'] = $clean_name;
            }

            // Upload the file in the documents tool.
            include_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
            $file_path = handle_uploaded_document($_course, $_FILES[$key], api_get_path(SYS_COURSE_PATH).$_course['path'].'/document','/audio', api_get_user_id(), '', '', '', '', false);

            // Getting the filename only.
            $file_components = explode('/', $file_path);
            $file = $file_components[count($file_components) - 1];

            // Store the mp3 file in the lp_item table.
            $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
            $sql_insert_audio = "UPDATE $tbl_lp_item SET audio = '".Database::escape_string($file)."' 
                                 WHERE c_id = $course_id AND id = '".Database::escape_string($lp_item_id)."'";
            Database::query($sql_insert_audio);
        }
    }
    //Display::display_confirmation_message(get_lang('ItemUpdated'));
    $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
    header('Location: '.$url);
    exit;
}

Display::display_header(null, 'Path');

$suredel = trim(get_lang('AreYouSureToDelete'));

?>
<script>

var newOrderData= "";
//source code found in http://www.swartzfager.org/blog/dspNestedList.cfm

$(function() {
    <?php
    if (!isset($_REQUEST['updateaudio'])) { 
    ?>
	$("#lp_item_list").sortable({ 
		items: "li",
		handle: ".moved", //only the class "moved" 
		cursor: "move",  
		placeholder: "ui-state-highlight", //defines the yellow highlight			   
	});	

	$("#listSubmit").click(function () {
		//Disable the submit button to prevent a double-click
		$(this).attr("disabled","disabled");
		//Initialize the variable that will contain the data to submit to the form
		newOrderData= "";
		//All direct descendants of the lp_item_list will have a parentId of 0
		var parentId= 0;
		
		//Walk through the direct descendants of the lp_item_list <ul>
		$("#lp_item_list").children().each(function () {
			
			/*Only process elements with an id attribute (in order to skip the blank,
			unmovable <li> elements.*/
			
			if ($(this).attr("id")) {
					/*Build a string of data with the child's ID and parent ID, 
					 using the "|" as a delimiter between the two IDs and the "^" 
					 as a record delimiter (these delimiters were chosen in case the data
					 involved includes more common delimiters like commas within the content)
					*/
					newOrderData= newOrderData + $(this).attr("id") + "|" + "0" + "^";
					
					//Determine if this child is a containter
					if ($(this).is(".li_container")) {
						  //Process the child elements of the container
                        processChildren($(this).attr("id"));
					}
				}
			
		}); //end of lp_item_list children loop
		
		//Write the newOrderData string out to the listResults form element
		//$("#listResults").val(newOrderData);
		var order = "new_order="+ newOrderData + "&a=update_lp_item_order";
		$.post("<?php echo api_get_path(WEB_AJAX_PATH)?>lp.ajax.php", order, function(reponse){
            $("#message").html(reponse);
        }); 

		 setTimeout(function() {
		        $("#message").html('');
		    }, 3000);
						
		return false;
		
	}); //end of lp_item_list event assignment
	
	<?php } ?>
	function processChildren(parentId) {
		//Loop through the children of the UL element defined by the parentId
		var ulParentID= "UL_" + parentId;
		$("#" + ulParentID).children().each(function () {
			
			/*Only process elements with an id attribute (in order to skip the blank,
				unmovable <li> elements.*/
				
			if ($(this).attr("id")) {
                /*Build a string of data with the child's ID and parent ID, 
                    using the "|" as a delimiter between the two IDs and the "^" 
                    as a record delimiter (these delimiters were chosen in case the data
                    involved includes more common delimiters like commas within the content)
                */
                newOrderData= newOrderData + $(this).attr("id") + "|" + parentId + "^";

                //Determine if this child is a containter
                if ($(this).is(".container")) {
                    //Process the child elements of the container
                    processChildren($(this).attr("id"));
                }
            }				
		});  //end of children loop		
	} //end of processChildren function	
});

/* <![CDATA[ */
function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}

function confirmation(name) {
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
        return true;
    } else {
        return false;
    }
}
</script>
<?php

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid">';
echo '<div class="span3">';
echo $_SESSION['oLP']->return_new_tree(null, true); 
echo '</div>';

echo '<div class="span9">';
switch ($_GET['action']) {
    case 'edit_item':
        if (isset($is_success) && $is_success === true) {
            Display::display_confirmation_message(get_lang('LearnpathItemEdited'));
        } else {
            echo $_SESSION['oLP']->display_edit_item($_GET['id']);
        }
        break;
    case 'delete_item':
        if (isset($is_success) && $is_success === true) {
            Display::display_confirmation_message(get_lang('LearnpathItemDeleted'));
        }
        break;
}

echo '</div>';
echo '</div>';

/* FOOTER */
Display::display_footer();
lp_ajax_initialize.php000064400000016372152003363470011130 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This script, in particular, enables the process of SCO's initialization. It
 * resets the JavaScript values for each SCO to the current LMS status.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php
use \ChamiloSession as Session;

$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';
require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'scorm.class.php';
require_once 'aicc.class.php';
require_once 'learnpathItem.class.php';
require_once 'scormItem.class.php';
require_once 'aiccItem.class.php';

/**
 * Get one item's details
 * @param   integer LP ID
 * @param   integer user ID
 * @param   integer View ID
 * @param   integer Current item ID
 * @param   integer New item ID
 */
function initialize_item($lp_id, $user_id, $view_id, $next_item) {
    $return = '';
    if ($debug > 0) { error_log('In initialize_item('.$lp_id.','.$user_id.','.$view_id.','.$next_item.')', 0); }
    /*$item_id may be one of:
     * -'next'
     * -'previous'
     * -'first'
     * -'last'
     * - a real item ID
     */

    $mylp = '';

    $lpobject = Session::read('lpobject');
    if (isset($lpobject)) {
        $oLP = unserialize($lpobject);
        if ($debug) error_log("lpobject was set");
        if (!is_object($oLP)) {
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code, $lp_id, $user_id);
            if ($debug) error_log("Creating learnpath");
        } else {
            $mylp = $oLP;
            if ($debug) error_log("Loading learnpath from unserialize");
        }
    } else {
        if ($debug) {
            error_log("lpobject was not set");
        }
    }
    $mylp->set_current_item($next_item);
    if ($debug > 1) { error_log('In initialize_item() - new item is '.$next_item, 0); }
    $mylp->start_current_item(true);

    if (is_object($mylp->items[$next_item])) {
        if ($debug > 1) { error_log('In initialize_item - recovering existing item object '.$next_item, 0); }
        $mylpi = $mylp->items[$next_item];
    } else {
        if ($debug > 1) { error_log('In initialize_item - generating new item object '.$next_item, 0); }
        $mylpi = new learnpathItem($next_item, $user_id);
    }

    if ($mylpi) {
        $mylpi->set_lp_view($view_id);
    }

    /*
     * now get what's needed by the SCORM API:
     * -score
     * -max
     * -min
     * -lesson_status
     * -session_time
     * -suspend_data
     */
    $myscore = $mylpi->get_score();
    $mymax = $mylpi->get_max();
    if ($mymax === '') { $mymax = "''"; }
    $mymin = $mylpi->get_min();

    $mylesson_status = $mylpi->get_status();
    $mytotal_time = $mylpi->get_scorm_time('js', null, true);
    $mymastery_score = $mylpi->get_mastery_score();
    $mymax_time_allowed = $mylpi->get_max_time_allowed();
    $mylaunch_data = $mylpi->get_launch_data();
    $mysession_time = $mylpi->get_total_time();
    $mysuspend_data = $mylpi->get_suspend_data();
    $mylesson_location = $mylpi->get_lesson_location();
    $myic = $mylpi->get_interactions_count();
    $myistring = '';
    for ($i = 0; $i < $myic; $i++) {
        $myistring .= ",[".$i.",'','','','','','','']";
    }
    if (!empty($myistring)) {
        $myistring = substr($myistring, 1);
    }
	// Obtention des donnees d'objectifs

	$mycoursedb = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
    $course_id = api_get_course_int_id();
	$mylp_iv_id = $mylpi->db_item_view_id;

    $phpobjectives = array();

    if (!empty($mylp_iv_id)) {
        $sql = "SELECT objective_id, status, score_raw, score_max, score_min
            FROM $mycoursedb
            WHERE lp_iv_id = $mylp_iv_id AND c_id = $course_id
            ORDER BY id ASC;";
        $res = Database::query($sql);
        while ($row = Database::fetch_row($res)) {
            $phpobjectives[] = $row;
        }
    }
	$myobjectives = json_encode($phpobjectives);

    $return .=
            "olms.score=".$myscore.";" .
            "olms.max=".$mymax.";" .
            "olms.min=".$mymin.";" .
            "olms.lesson_status='".$mylesson_status."';" .
            "olms.lesson_location='".$mylesson_location."';" .
            "olms.session_time='".$mysession_time."';" .
            "olms.suspend_data='".$mysuspend_data."';" .
            "olms.total_time = '".$mytotal_time."';" .
            "olms.mastery_score = '".$mymastery_score."';" .
            "olms.max_time_allowed = '".$mymax_time_allowed."';" .
            "olms.launch_data = '".$mylaunch_data."';" .
            "olms.interactions = new Array(".$myistring.");" .
            //"olms.item_objectives = new Array();" .
            "olms.item_objectives = ".$myobjectives.";" .
            "olms.G_lastError = 0;" .
            "olms.G_LastErrorMessage = 'No error';" ;
    /*
     * and re-initialise the rest (proper to the LMS)
     * -lms_lp_id
     * -lms_item_id
     * -lms_old_item_id
     * -lms_new_item_id
     * -lms_initialized
     * -lms_progress_bar_mode
     * -lms_view_id
     * -lms_user_id
     */
    $mytotal = $mylp->get_total_items_count_without_chapters();
    $mycomplete = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = ($myprogress_mode == '' ? '%' : $myprogress_mode);
    $mynext = $mylp->get_next_item_id();
    $myprevious = $mylp->get_previous_item_id();
    $myitemtype = $mylpi->get_type();
    $mylesson_mode = $mylpi->get_lesson_mode();
    $mycredit = $mylpi->get_credit();
    $mylaunch_data = $mylpi->get_launch_data();
    $myinteractions_count = $mylpi->get_interactions_count();
    $myobjectives_count = $mylpi->get_objectives_count();
    $mycore_exit = $mylpi->get_core_exit();

    $return .=
            "olms.lms_lp_id=".$lp_id.";" .
            "olms.lms_item_id=".$next_item.";" .
            "olms.lms_old_item_id=0;" .
            "olms.lms_initialized=0;" .
            "olms.lms_view_id=".$view_id.";" .
            "olms.lms_user_id=".$user_id.";" .
            "olms.next_item=".$next_item.";" . // This one is very important to replace possible literal strings.
            "olms.lms_next_item=".$mynext.";" .
            "olms.lms_previous_item=".$myprevious.";" .
            "olms.lms_item_type = '".$myitemtype."';" .
            "olms.lms_item_credit = '".$mycredit."';" .
            "olms.lms_item_lesson_mode = '".$mylesson_mode."';" .
            "olms.lms_item_launch_data = '".$mylaunch_data."';" .
            "olms.lms_item_interactions_count = '".$myinteractions_count."';" .
            "olms.lms_item_objectives_count = '".$myinteractions_count."';" .
            "olms.lms_item_core_exit = '".$mycore_exit."';" .
            "olms.asset_timer = 0;";

    $mylp->set_error_msg('');
    $mylp->prerequisites_match(); // Check the prerequisites are all complete.
    if ($debug > 1) { error_log('Prereq_match() returned '.htmlentities($mylp->error), 0); }
    if ($debug > 1) { error_log("return = $return "); }
    return $return;
}
echo initialize_item($_POST['lid'], $_POST['uid'], $_POST['vid'], $_POST['iid']);lp_ajax_last_update_status.php000064400000011711152003363470012667 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This script contains the server part of the xajax interaction process. The
 * client part is located in lp_api.php or other api's.
 * This script exists exclusively to comply with the SCORM 1.2 rules notes that
 * say that if the SCO doesn't give a status throughout its execution, then the
 * status should be automatically set to 'completed', then the score should be
 * evaluated against mastery_score and, if a raw score and mastery_score value
 * are set, assign the final status based on that.
 * As such, this script will:
 * 1 - check the current status for the given element is still 'not attempted'
 * 2 - check if there is a mastery score
 * 3 - check if there is a raw score
 * 4 - if 2 or 3 are false, change the status to 'completed', else compare
 * whether the raw score is higher than the mastery score. If not, the status
 * will be set to 'failed', if yes, the status will be set to 'passed'
 * 5 - update the status in the table of contents
 * @package chamilo.learnpath
 * @author Yannick Warnier <yannick.warnier@beeznest.com>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php'
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Writes an item's new values into the database and returns the operation result
 * @param   integer Learnpath ID
 * @param   integer User ID
 * @param   integer View ID
 * @param   integer Item ID
 * @return  string  JavaScript operations to execute as soon as returned
 */
function last_update_status($lp_id, $user_id, $view_id, $item_id) {
    error_log(__LINE__);
    global $_configuration;
    $debug = 0;
    $return = '';
    if ($debug > 0) { error_log('In last_update_status('.$lp_id.','.$user_id.','.$view_id.','.$item_id.')', 0); }
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 2) { error_log(print_r($oLP, true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code,$lp_id,$user_id);
        } else {
            if ($debug > 2) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    error_log(__LINE__);

    // This function should only be used for SCORM paths.
    if ($mylp->get_type() != 2) {
        return;
    }
    $prereq_check = $mylp->prerequisites_match($item_id);
    $mystatus = '';
    if ($prereq_check === true) {
        error_log(__LINE__);
        // Launch the prerequisites check and set error if needed.
        $mylpi =& $mylp->items[$item_id];

        $mystatus_in_db = $mylpi->get_status(true);
        error_log($mystatus_in_db);
        if ($mystatus_in_db == 'not attempted' or $mystatus_in_db == '') {
        error_log(__LINE__);
            $mystatus = 'completed';
            $mastery_score = $mylpi->get_mastery_score();
            if ($mastery_score != -1) {
        error_log(__LINE__);
                $score = $mylpi->get_score();
                if ($score != 0 && $score >= $mastery_score) {
        error_log(__LINE__);
                    $mystatus = 'passed';
                } else {
        error_log(__LINE__);
                    $mystatus = 'failed';
                }
            }
            error_log(__LINE__);
            $mylpi->set_status($mystatus);
            $mylp->save_item($item_id, false);
        } else {
            error_log(__LINE__);
            return $return;
        }
    } else {
        error_log(__LINE__);
        return $return;
    }
    error_log(__LINE__);
    $mytotal = $mylp->get_total_items_count_without_chapters();
    $mycomplete = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = ($myprogress_mode==''?'%':$myprogress_mode);
    $return .= "update_toc('".$mystatus."','".$item_id."','no');";
    error_log('Return is now '.$return);
    $update_list = $mylp->get_update_queue();
    foreach ($update_list as $my_upd_id => $my_upd_status) {
        if ($my_upd_id != $item_id) { // Only update the status from other items (i.e. parents and brothers), do not update current as we just did it already.
            $return .= "update_toc('".$my_upd_status."','".$my_upd_id."','no');";
        }
    }
    $return .= "update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');";
    $return .="update_stats();";
    return $return;
    //return $objResponse;
}
error_log(__LINE__);
echo last_update_status(
            $_REQUEST['lid'],
            $_REQUEST['uid'],
            $_REQUEST['vid'],
            $_REQUEST['iid']);
lp_ajax_log.php000064400000002567152003363470007551 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This script, in particular, enables the process of SCORM messages logging.
 * It stores the SCORM interaction logs right into a temporary file on disk.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Write a log with the current message
 * @param   string Message
 * @param   int    Debug level (if 0, do not log)
 */
function lp_ajax_log($msg, $level) {
    $debug = 0;
    $return = '';
    if ($debug > 0) {error_log('In log('.$msg.')', 0); }
    if ($level == 0) {
        //error_log('Logging level too low, not writing files in '.__FILE__);
        return $return;
    }
    $msg = str_replace('<br />', "\r\n", $msg);
    $file = sys_get_temp_dir().DIRECTORY_SEPARATOR.session_id().'.'.date('Ymd').'-'.api_get_user_id().'.scorm.log';
    $fh = @fopen($file, 'a');
    @fwrite($fh,'['.date('Y-m-d H:m:s').'] '.$msg."\r\n");
    @fclose($fh);
    return $return;
}

echo lp_ajax_log($_POST['msg'], $_POST['debug']);
lp_ajax_save_item.php000064400000030017152003363470010733 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This is a first attempt at using xajax and AJAX in general, so the code might be a bit unsettling.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */

use \ChamiloSession as Session;

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'scorm.class.php';
require_once 'aicc.class.php';
require_once 'learnpathItem.class.php';
require_once 'scormItem.class.php';
require_once 'aiccItem.class.php';

/**
 * Writes an item's new values into the database and returns the operation result
 * @param   integer Learnpath ID
 * @param   integer User ID
 * @param   integer View ID
 * @param   integer Item ID
 * @param   double  Current score
 * @param   double  Maximum score
 * @param   double  Minimum score
 * @param   string  Lesson status
 * @param   string  Session time
 * @param   string  Suspend data
 * @param   string  Lesson location
 * @param   array   Interactions array
 * @param   string  Core exit SCORM string
 */
function save_item($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1, $min = -1, $status = '', $time = 0, $suspend = '', $location = '', $interactions = array(), $core_exit = 'none') {
    $return = null;

    if ($debug > 0) {
        error_log('lp_ajax_save_item.php : save_item() params: ');
        error_log("item_id: $item_id");
        error_log("lp_id: $lp_id - user_id: - $user_id - view_id: $view_id - item_id: $item_id");
        error_log("score: $score - max:$max - min: $min - status:$status - time:$time - suspend: $suspend - location: $location - core_exit: $core_exit");
    }

    $mylp = null;
    $lpobject = Session::read('lpobject');
    if (isset($lpobject)) {
        $oLP = unserialize($lpobject);
        if ($debug) error_log("lpobject was set");
        if (!is_object($oLP)) {
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code, $lp_id, $user_id);
            if ($debug) error_log("Creating learnpath");
        } else {
            $mylp = $oLP;
            if ($debug) error_log("Loading learnpath from unserialize");
        }
    } else {
        if ($debug) {
            error_log("lpobject was not set");
        }
    }

    if (!is_a($mylp, 'learnpath')) {
        if ($debug) {
            error_log("mylp variable is not an learnpath object");
        }
        return null;
    }

    $prereq_check = $mylp->prerequisites_match($item_id);
    $mylpi = $mylp->items[$item_id];

    if (empty($mylpi)) {
        if ($debug > 0) {
            error_log("item #$item_id not found in the items array: ".print_r($mylp->items, 1));
        }
        return false;
    }

    //This functions sets the $this->db_item_view_id variable needed in get_status() see BT#5069
    $mylpi->set_lp_view($view_id);

    if ($prereq_check === true) {
        if ($debug > 1) { error_log('Prereq are check'); }

        // Launch the prerequisites check and set error if needed
        if (isset($max) && $max != -1)  {
            $mylpi->max_score = $max;
            $mylpi->set_max_score($max);
            if ($debug > 1) { error_log("Setting max_score: $max"); }
        }

        if (isset($min) && $min != -1 && $min != 'undefined') {
            $mylpi->min_score = $min;
            if ($debug > 1) { error_log("Setting min_score: $min"); }
        }

        //set_score function already saves the status
        if (isset($score) && $score != -1) {
            if ($debug > 1) { error_log('Calling set_score('.$score.')', 0); }
            if ($debug > 1) { error_log('set_score changes the status to failed/passed if mastery score is provided', 0); }
            $mylpi->set_score($score);
            if ($debug > 1) { error_log('Done calling set_score '.$mylpi->get_score(), 0); }
        } else {
            if ($debug > 1) { error_log("Score not updated"); }

            //Default behaviour
            if (isset($status) && $status != '' && $status != 'undefined') {
                if ($debug > 1) { error_log('Calling set_status('.$status.')', 0); }
                $mylpi->set_status($status);
                if ($debug > 1) { error_log('Done calling set_status: checking from memory: '.$mylpi->get_status(false), 0); }
            } else {
                if ($debug > 1) { error_log("Status not updated"); }
            }
        }

        // Hack to set status to completed for hotpotatoes if score > 80%.
        $my_type = $mylpi->get_type();

        if ($my_type == 'hotpotatoes') {
            if ((empty($status) || $status == 'undefined' || $status == 'not attempted') && $max > 0) {
                if (($score/$max) > 0.8) {
                    $mystatus = 'completed';
                    if ($debug > 1) { error_log('Calling set_status('.$mystatus.') for hotpotatoes', 0); }
                    $mylpi->set_status($mystatus);
                    if ($debug > 1) { error_log('Done calling set_status for hotpotatoes - now '.$mylpi->get_status(false), 0); }
                }
            } elseif ($status == 'completed' && $max > 0 && ($score/$max) < 0.8) {
                $mystatus = 'failed';
                if ($debug > 1) { error_log('Calling set_status('.$mystatus.') for hotpotatoes', 0); }
                $mylpi->set_status($mystatus);
                if ($debug > 1) { error_log('Done calling set_status for hotpotatoes - now '.$mylpi->get_status(false), 0); }
            }
        }

        if (isset($time) && $time != '' && $time != 'undefined') {
            // If big integer, then it's a timestamp, otherwise it's normal scorm time.
            if ($debug > 1) { error_log('Calling set_time('.$time.') ', 0); }
            if ($time == intval(strval($time)) && $time > 1000000) {
                if ($debug > 1) { error_log("Time is INT"); }
                $real_time = time() - $time;
                if ($debug > 1) { error_log('Calling $real_time '.$real_time.' ', 0); }
                $mylpi->set_time($real_time, 'int');
            } else {
                if ($debug > 1) { error_log("Time is in SCORM format"); }
                if ($debug > 1) { error_log('Calling $time '.$time.' ', 0); }
                $mylpi->set_time($time, 'scorm');
            }
            //if ($debug > 1) { error_log('Done calling set_time - now '.$mylpi->get_total_time(), 0); }
        } else {
            $time = $mylpi->get_total_time();
        }

        if (isset($suspend) && $suspend != '' && $suspend != 'undefined') {
            $mylpi->current_data = $suspend; //escapetxt($suspend);
        }

        if (isset($location) && $location != '' && $location!='undefined') {
            $mylpi->set_lesson_location($location);
        }

        // Deal with interactions provided in arrays in the following format:
        // id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
        if (is_array($interactions) && count($interactions) > 0) {
            foreach ($interactions as $index => $interaction) {
                //$mylpi->add_interaction($index,$interactions[$index]);
                //fix DT#4444
                $clean_interaction = str_replace('@.|@', ',', $interactions[$index]);
                $mylpi->add_interaction($index, $clean_interaction);
            }
        }

        if ($core_exit != 'undefined') {
            $mylpi->set_core_exit($core_exit);
        }
        $mylp->save_item($item_id, false);
    } else {
        if ($debug) {
            error_log("prereq_check: ".intval($prereq_check));
        }
        return $return;
    }

    $mystatus_in_db = $mylpi->get_status(true);
    if ($debug) error_log("Status in DB: $mystatus_in_db");

    if ($mystatus_in_db != 'completed' && $mystatus_in_db != 'passed' && $mystatus_in_db != 'browsed' && $mystatus_in_db != 'failed') {
         $mystatus_in_memory = $mylpi->get_status(false);
         if ($mystatus_in_memory != $mystatus_in_db) {
             $mystatus = $mystatus_in_memory;
         } else {
             $mystatus = $mystatus_in_db;
         }
    } else {
        $mystatus = $mystatus_in_db;
    }

    $mytotal         = $mylp->get_total_items_count_without_chapters();
    $mycomplete      = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = $myprogress_mode == '' ? '%' : $myprogress_mode;

    if ($debug > 1) { error_log("mystatus: $mystatus", 0); }
    if ($debug > 1) { error_log("myprogress_mode: $myprogress_mode", 0); }
    if ($debug > 1) { error_log("progress: $mycomplete / $mytotal", 0); }

    //$_SESSION['lpobject'] = serialize($mylp);

    if ($mylpi->get_type() != 'sco') {
        // If this object's JS status has not been updated by the SCORM API, update now.
        $return .= "olms.lesson_status='".$mystatus."';";
    }
    $return .= "update_toc('".$mystatus."','".$item_id."');";
    $update_list = $mylp->get_update_queue();

    foreach ($update_list as $my_upd_id => $my_upd_status)  {
        if ($my_upd_id != $item_id) { // Only update the status from other items (i.e. parents and brothers), do not update current as we just did it already.
            $return .= "update_toc('".$my_upd_status."','".$my_upd_id."');";
        }
    }
    $return .= "update_progress_bar('$mycomplete', '$mytotal', '$myprogress_mode');";

    if ($debug > 0) {
        $return .= "logit_lms('Saved data for item ".$item_id.", user ".$user_id." (status=".$mystatus.")',2);";
    }

    if (!isset($_SESSION['login_as'])) {
        // If $_SESSION['login_as'] is set, then the user is an admin logged as the user.
        $tbl_track_login = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_LOGIN);

        $sql_last_connection = "SELECT login_id, login_date
            FROM $tbl_track_login
            WHERE login_user_id='".api_get_user_id()."'
            ORDER BY login_date DESC LIMIT 0,1";

        $q_last_connection = Database::query($sql_last_connection);
        if (Database::num_rows($q_last_connection) > 0) {
            $current_time = api_get_utc_datetime();
            $row = Database::fetch_array($q_last_connection);
            $i_id_last_connection = $row['login_id'];
            $s_sql_update_logout_date = "UPDATE $tbl_track_login SET logout_date='".$current_time."' WHERE login_id='$i_id_last_connection'";
            Database::query($s_sql_update_logout_date);
        }
    }

    if ($mylp->get_type() == 2) {
         $return .= "update_stats();";
    }

    //To be sure progress is updated
    $mylp->save_last();

    Session::write('lpobject', serialize($mylp));
    if ($debug > 0) { error_log('---------------- lp_ajax_save_item.php : save_item end ----- '); }
    return $return;
}

$interactions = array();
if (isset($_REQUEST['interact'])) {
    if (is_array($_REQUEST['interact'])) {
        foreach ($_REQUEST['interact'] as $idx => $interac) {
            $interactions[$idx] = split(',', substr($interac, 1, -1));
            if(!isset($interactions[$idx][7])){ // Make sure there are 7 elements.
                $interactions[$idx][7] = '';
            }
        }
    }
}

echo save_item(
            (!empty($_REQUEST['lid'])?$_REQUEST['lid']:null),
            (!empty($_REQUEST['uid'])?$_REQUEST['uid']:null),
            (!empty($_REQUEST['vid'])?$_REQUEST['vid']:null),
            (!empty($_REQUEST['iid'])?$_REQUEST['iid']:null),
            (!empty($_REQUEST['s'])?$_REQUEST['s']:null),
            (!empty($_REQUEST['max'])?$_REQUEST['max']:null),
            (!empty($_REQUEST['min'])?$_REQUEST['min']:null),
            (!empty($_REQUEST['status'])?$_REQUEST['status']:null),
            (!empty($_REQUEST['t'])?$_REQUEST['t']:null),
            (!empty($_REQUEST['suspend'])?$_REQUEST['suspend']:null),
            (!empty($_REQUEST['loc'])?$_REQUEST['loc']:null),
            $interactions,
            (!empty($_REQUEST['core_exit'])?$_REQUEST['core_exit']:''));lp_ajax_save_objectives.php000064400000005706152003363470012141 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This is a first attempt at using xajax and AJAX in general, so the code might be a bit unsettling.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Writes an item's new values into the database and returns the operation result
 * @param   integer Learnpath ID
 * @param   integer User ID
 * @param   integer View ID
 * @param   integer Item ID
 * @param   array   Objectives array
 */
function save_objectives($lp_id, $user_id, $view_id, $item_id, $objectives = array()) {
    global $_configuration;
    $debug = 0;
    $return = '';
    if ($debug > 0) { error_log('In xajax_save_objectives('.$lp_id.','.$user_id.','.$view_id.','.$item_id.',"'.(count($objectives) > 0 ? count($objectives) : '').'")', 0); }
    //$objResponse = new xajaxResponse();
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP =unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 2) { error_log(print_r($oLP,true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code, $lp_id, $user_id);
        }else{
            if ($debug > 2) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    $mylpi =& $mylp->items[$item_id];
    //error_log(__FILE__.' '.__LINE__.' '.print_r($objectives,true), 0);
    if(is_array($objectives) && count($objectives)>0){
        foreach($objectives as $index=>$objective){
            //error_log(__FILE__.' '.__LINE__.' '.$objectives[$index][0], 0);
            $mylpi->add_objective($index,$objectives[$index]);
        }
        $mylpi->write_objectives_to_db();
    }
    //return $objResponse;
    return $return;
}
$objectives = array();
if (isset($_REQUEST['objectives'])) {
    if (is_array($_REQUEST['objectives'])) {
        foreach ($_REQUEST['objectives'] as $idx => $ob) {
            $objectives[$idx] = split(',', substr($ob, 1, -1));
            if (!isset($objectives[$idx][4])) { // Make sure there are 7 elements.
                $objectives[$idx][4] = '';
            }
        }
    }
}
echo save_objectives($_REQUEST['lid'], $_REQUEST['uid'], $_REQUEST['vid'], $_REQUEST['iid'], $objectives);
lp_ajax_start_timer.php000064400000000674152003363470011322 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * Start a timer and hand it back to the JS by assigning the current time (of start) to
 * var asset_timer
 * @return string JavaScript time intializer
 */
function start_timer() {
    $time = time();
    return $time; //"olms.asset_timer='$time'; olms.asset_timer_total = 0;";
}
echo start_timer();lp_ajax_switch_item.php000064400000023001152003363470011271 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This is a first attempt at using xajax and AJAX in general, so the code might be a bit unsettling.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php
$use_anonymous = true;

// Name of the language file that needs to be included
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Get one item's details
 * @param   integer LP ID
 * @param   integer user ID
 * @param   integer View ID
 * @param   integer Current item ID
 * @param   integer New item ID
 */
function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_item) {
    $debug = 0;
    $return = '';
    if ($debug > 0) { error_log('In xajax_switch_item_details('.$lp_id.','.$user_id.','.$view_id.','.$current_item.','.$next_item.')', 0); }
    //$objResponse = new xajaxResponse();
    /*$item_id may be one of:
     * -'next'
     * -'previous'
     * -'first'
     * -'last'
     * - a real item ID
     */
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 1) { error_log(print_r($oLP,true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code,$lp_id,$user_id);
        } else {
            if ($debug > 1) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    $new_item_id = 0;
    switch ($next_item) {
        case 'next':
            $mylp->set_current_item($current_item);
            $mylp->next();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {next} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'previous':
            $mylp->set_current_item($current_item);
            $mylp->previous();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {previous} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'first':
            $mylp->set_current_item($current_item);
            $mylp->first();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {first} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'last':
            break;
        default:
            // Should be filtered to check it's not hacked.
            if($next_item == $current_item){
                // If we're opening the same item again.
                $mylp->items[$current_item]->restart();
            }
            $new_item_id = $next_item;
            $mylp->set_current_item($new_item_id);
            if ($debug > 1) { error_log('In {default} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
    }
    $mylp->start_current_item(true);
    if ($mylp->force_commit) {
        $mylp->save_current();
    }
    //$objResponse->addAlert(api_get_path(REL_CODE_PATH).'newscorm/learnpathItem.class.php');
    if (is_object($mylp->items[$new_item_id])) {
        $mylpi = $mylp->items[$new_item_id];
    } else {
        if ($debug > 1) { error_log('In switch_item_details - generating new item object', 0); }
        $mylpi = new learnpathItem($new_item_id, $user_id);
        $mylpi->set_lp_view($view_id);
    }
    /*
     * now get what's needed by the SCORM API:
     * -score
     * -max
     * -min
     * -lesson_status
     * -session_time
     * -suspend_data
     */
    $myscore = $mylpi->get_score();
    $mymax = $mylpi->get_max();
    if ($mymax === '') { $mymax = "''"; }
    $mymin = $mylpi->get_min();
    $mylesson_status = $mylpi->get_status();
    $mylesson_location = $mylpi->get_lesson_location();
    $mytotal_time = $mylpi->get_scorm_time('js');
    $mymastery_score = $mylpi->get_mastery_score();
    $mymax_time_allowed = $mylpi->get_max_time_allowed();
    $mylaunch_data = $mylpi->get_launch_data();
    /*
    if ($mylpi->get_type() == 'asset') {
        // Temporary measure to save completion of an asset. Later on, Chamilo should trigger something on unload, maybe... (even though that would mean the last item cannot be completed)
        $mylesson_status = 'completed';
        $mylpi->set_status('completed');
        $mylpi->save();
    }
    */
    $mysession_time = $mylpi->get_total_time();
    $mysuspend_data = $mylpi->get_suspend_data();
    $mylesson_location = $mylpi->get_lesson_location();
    $myic = $mylpi->get_interactions_count();
    $myistring = '';
    for ($i = 0; $i < $myic; $i++) {
        $myistring .= ",[".$i.",'','','','','','','']";
    }
    if (!empty($myistring)) {
        $myistring = substr($myistring, 1);
    }
    /*
     * The following lines should reinitialize the values for the SCO
     * However, due to many complications, we are now relying more on the
     * LMSInitialize() call and its underlying lp_ajax_initialize.php call
     * so this code is technically deprecated (but the change of item_id should
     * remain). However, due to numerous technical issues with SCORM, we prefer
     * leaving it as a double-lock security. If removing, please test carefully
     * with both SCORM and proper learning path tracking.
     */
    $return .=
            "olms.score=".$myscore.";" .
            "olms.max=".$mymax.";" .
            "olms.min=".$mymin.";" .
            "olms.lesson_status='".$mylesson_status."';" .
            "olms.lesson_location='".$mylesson_location."';" .
            "olms.session_time='".$mysession_time."';" .
            "olms.suspend_data='".$mysuspend_data."';" .
            "olms.total_time = '".$mytotal_time."';" .
            "olms.mastery_score = '".$mymastery_score."';" .
            "olms.max_time_allowed = '".$mymax_time_allowed."';" .
            "olms.launch_data = '".$mylaunch_data."';" .
            "olms.interactions = new Array(".$myistring.");" .
            "olms.item_objectives = new Array();" .
            "olms.G_lastError = 0;" .
            "olms.G_LastErrorMessage = 'No error';" ;
    /*
     * and re-initialise the rest
     * -lms_lp_id
     * -lms_item_id
     * -lms_old_item_id
     * -lms_new_item_id
     * -lms_initialized
     * -lms_progress_bar_mode
     * -lms_view_id
     * -lms_user_id
     */
    $mytotal = $mylp->get_total_items_count_without_chapters();
    $mycomplete = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = ($myprogress_mode == '' ? '%' : $myprogress_mode);
    $mynext = $mylp->get_next_item_id();
    $myprevious = $mylp->get_previous_item_id();
    $myitemtype = $mylpi->get_type();
    $mylesson_mode = $mylpi->get_lesson_mode();
    $mycredit = $mylpi->get_credit();
    $mylaunch_data = $mylpi->get_launch_data();
    $myinteractions_count = $mylpi->get_interactions_count();
    $myobjectives_count = $mylpi->get_objectives_count();
    $mycore_exit = $mylpi->get_core_exit();

    $return .=
            //"saved_lesson_status='not attempted';" .
            "olms.lms_lp_id=".$lp_id.";" .
            "olms.lms_item_id=".$new_item_id.";" .
            "olms.lms_old_item_id=0;" .
            //"lms_been_synchronized=0;" .
            "olms.lms_initialized=0;" .
            //"lms_total_lessons=".$mytotal.";" .
            //"lms_complete_lessons=".$mycomplete.";" .
            //"lms_progress_bar_mode='".$myprogress_mode."';" .
            "olms.lms_view_id=".$view_id.";" .
            "olms.lms_user_id=".$user_id.";" .
            "olms.next_item=".$new_item_id.";" . // This one is very important to replace possible literal strings.
            "olms.lms_next_item=".$mynext.";" .
            "olms.lms_previous_item=".$myprevious.";" .
            "olms.lms_item_type = '".$myitemtype."';" .
            "olms.lms_item_credit = '".$mycredit."';" .
            "olms.lms_item_lesson_mode = '".$mylesson_mode."';" .
            "olms.lms_item_launch_data = '".$mylaunch_data."';" .
            "olms.lms_item_interactions_count = '".$myinteractions_count."';" .
            "olms.lms_item_objectives_count = '".$myinteractions_count."';" .
            "olms.lms_item_core_exit = '".$mycore_exit."';" .
            "olms.asset_timer = 0;";

    $return .= "update_toc('unhighlight','".$current_item."');".
                "update_toc('highlight','".$new_item_id."');".
                "update_toc('$mylesson_status','".$new_item_id."');".
                "update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');";

    $mylp->set_error_msg('');
    $mylp->prerequisites_match(); // Check the prerequisites are all complete.
    if ($debug > 1) { error_log('Prereq_match() returned '.htmlentities($mylp->error), 0); }
    $_SESSION['scorm_item_id'] = $new_item_id; // Save the new item ID for the exercise tool to use.
    $_SESSION['lpobject'] = serialize($mylp);
    return $return;
    //return $objResponse;
}

echo switch_item_details($_REQUEST['lid'], $_REQUEST['uid'], $_REQUEST['vid'], $_REQUEST['iid'], $_REQUEST['next']);
lp_ajax_switch_item_toc.php000064400000017223152003363470012147 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This script updated the TOC of the SCORM without updating the SCO's attributes
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Get one item's details
 * @param   integer LP ID
 * @param   integer user ID
 * @param   integer View ID
 * @param   integer Current item ID
 * @param   integer New item ID
 */
function switch_item_toc($lp_id, $user_id, $view_id, $current_item, $next_item) {
    $debug = 0;
    $return = '';
    if ($debug > 0) { error_log('In xajax_switch_item_toc('.$lp_id.','.$user_id.','.$view_id.','.$current_item.','.$next_item.')', 0); }
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 1) { error_log(print_r($oLP, true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code, $lp_id, $user_id);
        } else {
            if ($debug > 1) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    $new_item_id = 0;
    switch ($next_item) {
        case 'next':
            $mylp->set_current_item($current_item);
            $mylp->next();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {next} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'previous':
            $mylp->set_current_item($current_item);
            $mylp->previous();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {previous} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'first':
            $mylp->set_current_item($current_item);
            $mylp->first();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {first} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'last':
            break;
        default:
            // Should be filtered to check it's not hacked
            if($next_item == $current_item){
                // If we're opening the same item again.
                $mylp->items[$current_item]->restart();
            }
            $new_item_id = $next_item;
            $mylp->set_current_item($new_item_id);
            if ($debug > 1) { error_log('In {default} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
    }
    $mylp->start_current_item(true);
    if ($mylp->force_commit) {
        $mylp->save_current();
    }    
    if (is_object($mylp->items[$new_item_id])) {
        $mylpi = $mylp->items[$new_item_id];
    } else {
        if ($debug > 1) { error_log('In switch_item_details - generating new item object', 0); }
        $mylpi = new learnpathItem($new_item_id, $user_id);
        $mylpi->set_lp_view($view_id);
    }
    /*
     * now get what's needed by the SCORM API:
     * -score
     * -max
     * -min
     * -lesson_status
     * -session_time
     * -suspend_data
     */
    $myscore = $mylpi->get_score();
    $mymax = $mylpi->get_max();
    if ($mymax === '') { $mymax = "''"; }
    $mymin = $mylpi->get_min();
    $mylesson_status = $mylpi->get_status();
    $mylesson_location = $mylpi->get_lesson_location();
    $mytotal_time = $mylpi->get_scorm_time('js');
    $mymastery_score = $mylpi->get_mastery_score();
    $mymax_time_allowed = $mylpi->get_max_time_allowed();
    $mylaunch_data = $mylpi->get_launch_data();
    /*
    if ($mylpi->get_type() == 'asset') {
        // Temporary measure to save completion of an asset. Later on, Chamilo should trigger something on unload, maybe... (even though that would mean the last item cannot be completed)
        $mylesson_status = 'completed';
        $mylpi->set_status('completed');
        $mylpi->save();
    }
    */
    $mysession_time = $mylpi->get_total_time();
    $mysuspend_data = $mylpi->get_suspend_data();
    $mylesson_location = $mylpi->get_lesson_location();
    $myic = $mylpi->get_interactions_count();
    $myistring = '';
    for ($i = 0; $i < $myic; $i++) {
        $myistring .= ",[".$i.",'','','','','','','']";
    }
    if (!empty($myistring)) {
        $myistring = substr($myistring, 1);
    }
    $mytotal = $mylp->get_total_items_count_without_chapters();
    $mycomplete = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = ($myprogress_mode == '' ? '%' : $myprogress_mode);
    $mynext = $mylp->get_next_item_id();
    $myprevious = $mylp->get_previous_item_id();
    $myitemtype = $mylpi->get_type();
    $mylesson_mode = $mylpi->get_lesson_mode();
    $mycredit = $mylpi->get_credit();
    $mylaunch_data = $mylpi->get_launch_data();
    $myinteractions_count = $mylpi->get_interactions_count();
    $myobjectives_count = $mylpi->get_objectives_count();
    $mycore_exit = $mylpi->get_core_exit();

    $return .=
            //"saved_lesson_status='not attempted';" .
            "olms.lms_lp_id=".$lp_id.";" .
            "olms.lms_item_id=".$new_item_id.";" .
            "olms.lms_old_item_id=0;" .
            //"lms_been_synchronized=0;" .
            "olms.lms_initialized=0;" .
            //"lms_total_lessons=".$mytotal.";" .
            //"lms_complete_lessons=".$mycomplete.";" .
            //"lms_progress_bar_mode='".$myprogress_mode."';" .
            "olms.lms_view_id=".$view_id.";" .
            "olms.lms_user_id=".$user_id.";" .
            "olms.next_item=".$new_item_id.";" . // This one is very important to replace possible literal strings.
            "olms.lms_next_item=".$mynext.";" .
            "olms.lms_previous_item=".$myprevious.";" .
            "olms.lms_item_type = '".$myitemtype."';" .
            "olms.lms_item_credit = '".$mycredit."';" .
            "olms.lms_item_lesson_mode = '".$mylesson_mode."';" .
            "olms.lms_item_launch_data = '".$mylaunch_data."';" .
            "olms.lms_item_interactions_count = '".$myinteractions_count."';" .
            "olms.lms_item_objectives_count = '".$myinteractions_count."';" .
            "olms.lms_item_core_exit = '".$mycore_exit."';" .
            "olms.asset_timer = 0;";

    $return .= "update_toc('unhighlight','".$current_item."');".
                "update_toc('highlight','".$new_item_id."');".
                "update_toc('$mylesson_status','".$new_item_id."');".
                "update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');";

    $mylp->set_error_msg('');
    $mylp->prerequisites_match(); // Check the prerequisites are all complete.
    if ($debug > 1) { error_log('Prereq_match() returned '.htmlentities($mylp->error), 0); }
    $_SESSION['scorm_item_id'] = $new_item_id; // Save the new item ID for the exercise tool to use.
    $_SESSION['lpobject'] = serialize($mylp);
    return $return;    
}
echo switch_item_toc($_POST['lid'], $_POST['uid'], $_POST['vid'], $_POST['iid'], $_POST['next']);lp_build.php000064400000014003152003363470007050 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @package chamilo.learnpath
 */

/**
 * INIT SECTION 
*/

$_SESSION['whereami'] = 'lp/build';
$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

// The main_api.lib.php, database.lib.php and display.lib.php
// libraries are included by default.

include 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
include 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
// Name of the language file that needs to be included.
$language_file = 'learnpath';

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
$tbl_lp_view = Database::get_course_table(TABLE_LP_VIEW);

$isStudentView  = (int) $_REQUEST['isStudentView'];
$learnpath_id   = (int) $_REQUEST['lp_id'];
$submit			= $_POST['submit_button'];

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action=="add" and $type=="learnpathitem") {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_build.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
}
// From here on, we are admin because of the previous condition, so don't check anymore.

/* The learnpath has been just created, go get the last id. */
$is_new = false;

$course_id = api_get_course_int_id();

if ($learnpath_id == 0) {
    $is_new = true;

    $sql        = "SELECT id FROM " . $tbl_lp . " WHERE c_id = $course_id ORDER BY id DESC LIMIT 0, 1";
    $result     = Database::query($sql);
    $row        = Database::fetch_array($result);
    $learnpath_id = $row['id'];
}

$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";

$result = Database::query($sql_query);
$therow = Database::fetch_array($result);

/* SHOWING THE ADMIN TOOLS */

if (!empty($_GET['gradebook']) && $_GET['gradebook'] == 'view') {
    $_SESSION['gradebook'] = Security::remove_XSS($_GET['gradebook']);
    $gradebook = $_SESSION['gradebook'];
} elseif (empty($_GET['gradebook'])) {
    unset($_SESSION['gradebook']);
    $gradebook = '';
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
        'url' => '../gradebook/' . $_SESSION['gradebook_dest'],
        'name' => get_lang('ToolGradebook')
    );
}
$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => '#', "name" => $therow['name']);

// Theme calls.
$lp_theme_css=$_SESSION['oLP']->get_theme();
$show_learn_path = true;
Display::display_header('', 'Path');
$suredel = trim(get_lang('AreYouSureToDelete'));

?>
<script type='text/javascript'>
/* <![CDATA[ */
function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}
function confirmation(name) {
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
        return true;
    } else {
        return false;
    }
}
</script>
<?php

/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid">';
echo '<div class="span4">';
// Build the tree with the menu items in it.
echo $_SESSION['oLP']->return_new_tree();    
echo '</div>';        
echo '<div class="span8">';

if (isset($is_success) && $is_success === true) {
    Display::display_confirmation_message(get_lang('ItemRemoved'));
} else {
    if ($is_new) {
        Display::display_normal_message(get_lang('LearnpathAdded'), false);
    }
    // Display::display_normal_message(get_lang('LPCreatedAddChapterStep'), false);
    $gradebook = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;

    echo Display::page_subheader(get_lang('LearnPathAddedTitle'));
    
    echo '<ul id="lp_overview" class="thumbnails">';
    
    echo show_block('lp_controller.php?'.api_get_cidreq().'&amp;gradebook='.$gradebook.'&amp;action=add_item&amp;type=step&amp;lp_id=' . $_SESSION['oLP']->lp_id, get_lang("NewStep"), get_lang('NewStepComment'), 'tools.png');
    
//    echo show_block('lp_controller.php?'.api_get_cidreq().'&amp;gradebook='.$gradebook.'&amp;action=admin_view&amp;updateaudio=true&amp;lp_id=' . $_SESSION['oLP']->lp_id, get_lang("BasicOverview"), get_lang('BasicOverviewComment'), 'audio.png');
    
    echo show_block('lp_controller.php?'.api_get_cidreq().'&amp;gradebook='.$gradebook.'&amp;action=view&amp;lp_id=' . $_SESSION['oLP']->lp_id, get_lang("Display"), get_lang('DisplayComment'), 'view.png');
    
    //echo show_block('lp_controller.php?'.api_get_cidreq().'&amp;gradebook='.$gradebook.'&amp;action=edit&amp;lp_id=' . $_SESSION['oLP']->lp_id, get_lang("Settings"), null, 'reference.png');
    
    echo '</ul>';    
}
echo '</div>';
echo '</div>';


function show_block($link, $title, $subtitle, $icon) {
    $html = '<li class="span4">';
        $html .=  '<div class="thumbnail">';
        $html .=  '<a href="'.$link.'" title="'.$title.'">';
        $html .=  Display::return_icon($icon, $title, array(), ICON_SIZE_BIG);
        $html .=  '</a>';
        $html .=  '<div class="caption">';
        $html .=  '<strong>'.$title.'</strong></a> '.$subtitle;
        $html .=  '</div>';
        $html .=  '</div>';
    $html .=  '</li>';
    return $html;
}

/* FOOTER */

Display::display_footer();
lp_comm.common.php000064400000001615152003363470010200 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This is a first attempt at using xajax and AJAX in general, so the code might be a bit unsettling.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';

require '../inc/lib/xajax/xajax.inc.php';
$xajax = new xajax(api_get_path(WEB_CODE_PATH).'newscorm/lp_comm.server.php');
$xajax->registerFunction('save_item');
$xajax->registerFunction('save_objectives');
$xajax->registerFunction('switch_item_details');
$xajax->registerFunction('backup_item_details');
$xajax->registerFunction('start_timer');
lp_comm.server.php000064400000044026152003363470010221 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This script contains the server part of the xajax interaction process. The client part is located
 * in lp_api.php or other api's.
 * This is a first attempt at using xajax and AJAX in general, so the code might be a bit unsettling.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';

/**
 * Backup an item's values into the javascript API as "old" values (so we still have them at hand)
 * @param	integer	Learnpath ID
 * @param	integer	User ID
 * @param	integer View ID
 * @param	integer	Item ID
 * @param	double	Current score
 * @param	double	Maximum score
 * @param	double	Minimum score
 * @param	string	Lesson status
 * @param	string	Session time
 * @param	string	Suspend data
 * @param	string	Lesson location
 */
function backup_item_details($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1, $min = -1, $status = '', $time = '', $suspend = '', $location = '') {
    $objResponse = new xajaxResponse();
    $objResponse->addScript(
            "old_score=".$score.";" .
            "old_max=".$max.";" .
            "old_min=".$min.";" .
            "old_lesson_status='".$status."';" .
            "old_session_time='".$time."';" .
            "lms_old_item_id='".$item_id."';" .
            "old_suspend_data='".$suspend."';" .
            "old_lesson_location='".$location."';");
    //$objResponse->addAlert('data for item '.$item_id.', user '.$user_id.' backed up');
    return $objResponse;
}

/**
 * Writes an item's new values into the database and returns the operation result
 * @param	integer	Learnpath ID
 * @param	integer	User ID
 * @param	integer View ID
 * @param	integer	Item ID
 * @param	double	Current score
 * @param	double	Maximum score
 * @param	double	Minimum score
 * @param	string	Lesson status
 * @param	string	Session time
 * @param	string	Suspend data
 * @param	string	Lesson location
 * @param	string	Core exit SCORM string
 */
function save_item($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1, $min = -1, $status = '', $time = 0, $suspend = '', $location = '', $interactions = array(), $core_exit = 'none') {
    global $_configuration;
    $debug = 0;
    if ($debug > 0) { error_log('In xajax_save_item('.$lp_id.','.$user_id.','.$view_id.','.$item_id.','.$score.','.$max.','.$min.',"'.$status.'",'.$time.',"'.$suspend.'","'.$location.'","'.(count($interactions)>0?$interactions[0]:'').'","'.$core_exit.'")', 0); }
    $objResponse = new xajaxResponse();
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 2) { error_log(print_r($oLP, true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code, $lp_id, $user_id);
        } else {
            if ($debug > 2) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    //$objResponse->addAlert(api_get_path(REL_CODE_PATH).'newscorm/learnpathItem.class.php');


    $prereq_check = $mylp->prerequisites_match($item_id);
    if ($prereq_check === true) { // Launch the prerequisites check and set error if needed.

        $mylpi =& $mylp->items[$item_id];
        //$mylpi->set_lp_view($view_id);
        if ($max != -1) {
            $mylpi->max_score = $max;
        }
        if ($min != -1) {
            $mylpi->min_score = $min;
        }
        if ($score != -1) {
            $mylpi->set_score($score);
        }
        if ($status != '') {
            if ($debug > 1) { error_log('Calling set_status('.$status.') from xajax', 0); }
            $mylpi->set_status($status);
            if ($debug > 1) { error_log('Done calling set_status from xajax', 0); }
        }
        if ($time != '') {
            // If big integer, then it's a timestamp, otherwise it's normal scorm time.
            if ($time == intval(strval($time)) && $time > 1000000) {
                $real_time = time() - $time;
                //$real_time += $mylpi->get_total_time();
                $mylpi->set_time($real_time, 'int');
            } else {
                $mylpi->set_time($time);
            }
        }
        if ($suspend != '') {
            $mylpi->current_data = $suspend; //escapetxt($suspend);
        }
        if ($location != '') {
            $mylpi->set_lesson_location($location);
        }
        // Deal with interactions provided in arrays in the following format
        // id(0), type(1), time(2), weighting(3), correct_responses(4), student_response(5), result(6), latency(7)
        if (is_array($interactions) && count($interactions) > 0) {
            foreach ($interactions as $index => $interaction) {
                $mylpi->add_interaction($index, $interactions[$index]);
            }
        }
        $mylpi->set_core_exit($core_exit);
        $mylp->save_item($item_id, false);
    } else {
        return $objResponse;
    }

    $mystatus           = $mylpi->get_status(false);
    $mytotal            = $mylp->get_total_items_count_without_chapters();
    $mycomplete         = $mylp->get_complete_items_count();
    $myprogress_mode    = $mylp->get_progress_bar_mode();
    $myprogress_mode    = ($myprogress_mode == '' ? '%' : $myprogress_mode);
    
    //$mylpi->write_to_db();
    $_SESSION['lpobject'] = serialize($mylp);
    if ($mylpi->get_type()!='sco'){
        // If this object's JS status has not been updated by the SCORM API, update now.
        $objResponse->addScript("lesson_status='".$mystatus."';");
    }
    $objResponse->addScript("update_toc('".$mystatus."','".$item_id."');");
    $update_list = $mylp->get_update_queue();
    foreach ($update_list as $my_upd_id => $my_upd_status) {
        if ($my_upd_id != $item_id) { // Only update the status from other items (i.e. parents and brothers), do not update current as we just did it already.
            $objResponse->addScript("update_toc('".$my_upd_status."','".$my_upd_id."');");
        }
    }
    $objResponse->addScript("update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');");

    if ($debug > 0) {
        $objResponse->addScript("logit_lms('Saved data for item ".$item_id.", user ".$user_id." (status=".$mystatus.")',2)");
        if ($debug > 1) { error_log('End of xajax_save_item()', 0); }
    }

    if (!isset($_SESSION['login_as'])) {
        // If $_SESSION['login_as'] is set, then the user is an admin logged as the user.

        $tbl_track_login = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_LOGIN);

        $sql_last_connection = "SELECT login_id, login_date FROM $tbl_track_login 
                                WHERE login_user_id='".api_get_user_id()."' ORDER BY login_date DESC LIMIT 0,1";

        $q_last_connection = Database::query($sql_last_connection);
        if (Database::num_rows($q_last_connection) > 0) {
            $row = Database::fetch_array($q_last_connection);
            $i_id_last_connection = $row['login_id'];
            $s_sql_update_logout_date = "UPDATE $tbl_track_login SET logout_date=NOW() WHERE login_id='$i_id_last_connection'";
            Database::query($s_sql_update_logout_date);
        }

    }

    return $objResponse;
}

/**
 * Writes an item's new values into the database and returns the operation result
 * @param	integer	Learnpath ID
 * @param	integer	User ID
 * @param	integer View ID
 * @param	integer	Item ID
 * @param	array	Objectives array
 */
function save_objectives($lp_id, $user_id, $view_id, $item_id, $objectives = array()) {
    global $_configuration;
    $debug = 0;
    if ($debug > 0) { error_log('In xajax_save_objectives('.$lp_id.','.$user_id.','.$view_id.','.$item_id.',"'.(count($objectives) > 0 ? count($objectives) : '').'")', 0); }
    $objResponse = new xajaxResponse();
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 2) { error_log(print_r($oLP, true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code,$lp_id,$user_id);
        } else {
            if ($debug > 2) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    $mylpi =& $mylp->items[$item_id];
    //error_log(__FILE__.' '.__LINE__.' '.print_r($objectives,true), 0);
    if(is_array($objectives) && count($objectives)>0){
        foreach($objectives as $index=>$objective){
            //error_log(__FILE__.' '.__LINE__.' '.$objectives[$index][0], 0);
            $mylpi->add_objective($index,$objectives[$index]);
        }
        $mylpi->write_objectives_to_db();
    }
    return $objResponse;
}

/**
 * Get one item's details
 * @param	integer	LP ID
 * @param	integer	user ID
 * @param	integer	View ID
 * @param	integer	Current item ID
 * @param	integer New item ID
 */
function switch_item_details($lp_id, $user_id, $view_id, $current_item, $next_item) {
    global $charset;

    $debug = 0;
    if ($debug > 0) { error_log('In xajax_switch_item_details('.$lp_id.','.$user_id.','.$view_id.','.$current_item.','.$next_item.')', 0); }
    $objResponse = new xajaxResponse();
    /*$item_id may be one of:
     * -'next'
     * -'previous'
     * -'first'
     * -'last'
     * - a real item ID
     */
    require_once 'learnpath.class.php';
    require_once 'scorm.class.php';
    require_once 'aicc.class.php';
    require_once 'learnpathItem.class.php';
    require_once 'scormItem.class.php';
    require_once 'aiccItem.class.php';
    $mylp = '';
    if (isset($_SESSION['lpobject'])) {
        if ($debug > 1) { error_log('$_SESSION[lpobject] is set', 0); }
        $oLP = unserialize($_SESSION['lpobject']);
        if (!is_object($oLP)) {
            if ($debug > 1) { error_log(print_r($oLP, true), 0); }
            if ($debug > 2) { error_log('Building new lp', 0); }
            unset($oLP);
            $code = api_get_course_id();
            $mylp = new learnpath($code,$lp_id,$user_id);
        } else {
            if ($debug > 1) { error_log('Reusing session lp', 0); }
            $mylp = $oLP;
        }
    }
    $new_item_id = 0;
    switch ($next_item) {
        case 'next':
            $mylp->set_current_item($current_item);
            $mylp->next();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {next} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'previous':
            $mylp->set_current_item($current_item);
            $mylp->previous();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {previous} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'first':
            $mylp->set_current_item($current_item);
            $mylp->first();
            $new_item_id = $mylp->get_current_item_id();
            if ($debug > 1) { error_log('In {first} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
        case 'last':
            break;
        default:
            // Should be filtered to check it's not hacked.
            if($next_item == $current_item){
                // If we're opening the same item again.
                $mylp->items[$current_item]->restart();
            }
            $new_item_id = $next_item;
            $mylp->set_current_item($new_item_id);
            if ($debug > 1) { error_log('In {default} - next item is '.$new_item_id.'(current: '.$current_item.')', 0); }
            break;
    }
    $mylp->start_current_item(true);
    if ($mylp->force_commit){
        $mylp->save_current();
    }
    //$objResponse->addAlert(api_get_path(REL_CODE_PATH).'newscorm/learnpathItem.class.php');
    if (is_object($mylp->items[$new_item_id])) {
        $mylpi = $mylp->items[$new_item_id];
    } else {
        if ($debug > 1) { error_log('In switch_item_details - generating new item object', 0); }
        $mylpi = new learnpathItem($new_item_id, $user_id);
        $mylpi->set_lp_view($view_id);
    }
    /*
     * now get what's needed by the SCORM API:
     * -score
     * -max
     * -min
     * -lesson_status
     * -session_time
     * -suspend_data
     */
    $myscore = $mylpi->get_score();
    $mymax = $mylpi->get_max();
    $mymin = $mylpi->get_min();
    $mylesson_status = $mylpi->get_status();
    $mylesson_location = $mylpi->get_lesson_location();
    $mytotal_time = $mylpi->get_scorm_time('js');
    $mymastery_score = $mylpi->get_mastery_score();
    $mymax_time_allowed = $mylpi->get_max_time_allowed();
    $mylaunch_data = $mylpi->get_launch_data();
    /*
    if ($mylpi->get_type() == 'asset') {
        // Temporary measure to save completion of an asset. Later on, Chamilo should trigger something on unload, maybe... (even though that would mean the last item cannot be completed)
        $mylesson_status = 'completed';
        $mylpi->set_status('completed');
        $mylpi->save();
    }
    */
    $mysession_time = $mylpi->get_total_time();
    $mysuspend_data = $mylpi->get_suspend_data();
    $mylesson_location = $mylpi->get_lesson_location();
    $objResponse->addScript(
            "score=".$myscore.";" .
            "max=".$mymax.";" .
            "min=".$mymin.";" .
            "lesson_status='".$mylesson_status."';" .
            "lesson_location='".$mylesson_location."';" .
            "session_time='".$mysession_time."';" .
            "suspend_data='".$mysuspend_data."';" .
            "lesson_location='".$mylesson_location."';" .
            "total_time = '".$mytotal_time."';" .
            "mastery_score = '".$mymastery_score."';" .
            "max_time_allowed = '".$mymax_time_allowed."';" .
            "launch_data = '".$mylaunch_data."';" .
            "interactions = new Array();" .
            "item_objectives = new Array();" .
            "G_lastError = 0;" .
            "G_LastErrorMessage = 'No error';");
    /*
     * and re-initialise the rest
     * -saved_lesson_status = 'not attempted'
     * -lms_lp_id
     * -lms_item_id
     * -lms_old_item_id
     * -lms_new_item_id
     * -lms_been_synchronized
     * -lms_initialized
     * -lms_total_lessons
     * -lms_complete_lessons
     * -lms_progress_bar_mode
     * -lms_view_id
     * -lms_user_id
     */
    $mytotal = $mylp->get_total_items_count_without_chapters();
    $mycomplete = $mylp->get_complete_items_count();
    $myprogress_mode = $mylp->get_progress_bar_mode();
    $myprogress_mode = ($myprogress_mode == '' ? '%' : $myprogress_mode);
    $mynext = $mylp->get_next_item_id();
    $myprevious = $mylp->get_previous_item_id();
    $myitemtype = $mylpi->get_type();
    $mylesson_mode = $mylpi->get_lesson_mode();
    $mycredit = $mylpi->get_credit();
    $mylaunch_data = $mylpi->get_launch_data();
    $myinteractions_count = $mylpi->get_interactions_count();
    $myobjectives_count = $mylpi->get_objectives_count();
    $mycore_exit = $mylpi->get_core_exit();
    $objResponse->addScript(
            "saved_lesson_status='not attempted';" .
            "lms_lp_id=".$lp_id.";" .
            "lms_item_id=".$new_item_id.";" .
            "lms_old_item_id=0;" .
            "lms_been_synchronized=0;" .
            "lms_initialized=0;" .
            "lms_total_lessons=".$mytotal.";" .
            "lms_complete_lessons=".$mycomplete.";" .
            "lms_progress_bar_mod='".$myprogress_mode."';" .
            "lms_view_id=".$view_id.";" .
            "lms_user_id=".$user_id.";" .
            "next_item=".$new_item_id.";" . // This one is very important to replace possible literal strings.
            "lms_next_item=".$mynext.";" .
            "lms_previous_item=".$myprevious.";" .
            "lms_item_type = '".$myitemtype."';" .
            "lms_item_credit = '".$mycredit."';" .
            "lms_item_lesson_mode = '".$mylesson_mode."';" .
            "lms_item_launch_data = '".$mylaunch_data."';" .
            "lms_item_interactions_count = '".$myinteractions_count."';" .
            "lms_item_objectives_count = '".$myinteractions_count."';" .
            "lms_item_core_exit = '".$mycore_exit."';" .
            "asset_timer = 0;"
            );
    $objResponse->addScript("update_toc('unhighlight','".$current_item."');");
    $objResponse->addScript("update_toc('highlight','".$new_item_id."');");
    $objResponse->addScript("update_toc('$mylesson_status','".$new_item_id."');");
    $objResponse->addScript("update_progress_bar('$mycomplete','$mytotal','$myprogress_mode');");

    $mylp->set_error_msg('');
    $mylp->prerequisites_match(); // Check the prerequisites are all complete.
    if ($debug > 1) { error_log('Prereq_match() returned '.api_htmlentities($mylp->error, ENT_QUOTES, $charset), 0); }
    $objResponse->addScript("update_message_frame('".str_replace("'", "\'", api_htmlentities($mylp->error, ENT_QUOTES, $charset))."');");
    $_SESSION['scorm_item_id'] = $new_item_id; // Save the new item ID for the exercise tool to use.
    $_SESSION['lpobject'] = serialize($mylp);
    return $objResponse;
}

/**
 * Start a timer and hand it back to the JS by assigning the current time (of start) to
 * var asset_timer
 */
function start_timer() {
    $objResponse = new xajaxResponse();
    $time = time();
    $objResponse->addScript("asset_timer='$time';asset_timer_total=0;");
    return $objResponse;
}

require 'lp_comm.common.php';
$xajax->processRequests();
lp_content.php000064400000007202152003363470007426 0ustar00<?php

/* For licensing terms, see /license.txt */

/**
 * Script that displays an error message when no content could be loaded
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Including the global initialization file.
require_once '../inc/global.inc.php';

$debug = 0;
if ($debug > 0) {
    error_log('New lp - In lp_content.php', 0);
}
if (empty($lp_controller_touched)) {
    if ($debug > 0) {
        error_log('New lp - In lp_content.php - Redirecting to lp_controller', 0);
    }
    header('location: lp_controller.php?action=content&lp_id='.Security::remove_XSS($_REQUEST['lp_id']).'&item_id='.Security::remove_XSS($_REQUEST['item_id']));
}
$_SESSION['oLP']->error = '';
$lp_type = $_SESSION['oLP']->get_type();
$lp_item_id = $_SESSION['oLP']->get_current_item_id();

/**
 * Get a link to the corresponding document
 */
$src = '';
if ($debug > 0) {
    error_log('New lp - In lp_content.php - Looking for file url', 0);
}

$list = $_SESSION['oLP']->get_toc();

$dokeos_chapter = false;

foreach ($list as $toc) {
    if ($toc['id'] == $lp_item_id && ($toc['type'] == 'dokeos_chapter' || $toc['type'] == 'dokeos_module' || $toc['type'] == 'dir')) {
        $dokeos_chapter = true;
    }
}

if ($dokeos_chapter) {
    $src = 'blank.php';
} else {
    switch ($lp_type) {
        case 1:
            $_SESSION['oLP']->stop_previous_item();
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http', $lp_item_id);
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php?error=prerequisites';
            }
            break;
        case 2:
            $_SESSION['oLP']->stop_previous_item();
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http', $lp_item_id);
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php?error=prerequisites';
            }
            break;
        case 3:
            // save old if asset
            $_SESSION['oLP']->stop_previous_item(); // save status manually if asset
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http', $lp_item_id);
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php';
            }
            break;
        case 4:
            break;
    }
}

if ($debug > 0) {
    error_log('New lp - In lp_content.php - File url is '.$src, 0);
}
$_SESSION['oLP']->set_previous_item($lp_item_id);

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array(
        'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
        'name' => get_lang('ToolGradebook')
    );
}
// Define the 'doc.inc.php' as language file.
$nameTools = $_SESSION['oLP']->get_name();
$interbreadcrumb[] = array('url' => './lp_list.php', 'name' => get_lang('Doc'));
// Update global setting to avoid displaying right menu.
$save_setting = api_get_setting('show_navigation_menu');
global $_setting;
$_setting['show_navigation_menu'] = false;
if ($debug > 0) {
    error_log('New LP - In lp_content.php - Loading '.$src, 0);
}

header("Location: ".urldecode($src));lp_controller.php000064400000120276152003363470010146 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Controller script. Prepares the common background variables to give to the scripts corresponding to
 * the requested action
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */

use \ChamiloSession as Session;

$debug = 0;
if ($debug > 0) error_log('New LP -+- Entered lp_controller.php -+- (action: '.$_REQUEST['action'].')', 0);

// Language files that needs to be included.
if (isset($_GET['action'])) {
    if ($_GET['action'] == 'export') {
        // Only needed on export.
        $language_file[] = 'hotspot';
    }
}
$language_file[] = 'course_home';
$language_file[] = 'scormdocument';
$language_file[] = 'document';
$language_file[] = 'scorm';
$language_file[] = 'learnpath';
$language_file[] = 'resourcelinker';
$language_file[] = 'registration';
$language_file[] = 'exercice';

// Including the global initialization file.
require_once '../inc/global.inc.php';
$current_course_tool  = TOOL_LEARNPATH;

if (api_get_setting('show_glossary_in_documents') == 'ismanual' || api_get_setting('show_glossary_in_documents') == 'isautomatic' ) {
    $htmlHeadXtra[] = '<script>
<!--
    var jQueryFrameReadyConfigPath = \''.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.min.js\';
-->
</script>';
    $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.frameready.js" type="text/javascript" language="javascript"></script>';
    $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js" type="text/javascript" language="javascript"></script>';
}

$htmlHeadXtra[] = '<script>
function setFocus(){
    $("#idTitle").focus();
}
$(window).load(function () {
    setFocus();
});
</script>
<style>
form .label {
    padding: 1px 3px 2px;
    font-size: 100%;
    font-weight: normal;
    color: #ffffff;
    text-transform: none;
    background: none;
    border-radius: none;
    color: #404040;
    float: left;
    line-height: 18px;
    padding-top: 6px;
    text-align: right;
    width: 150px;
    text-shadow:none;
}
</style>';

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Include class definitions before session_start() to ensure availability when touching
// session vars containing learning paths.
require_once 'learnpath.class.php';
if ($debug > 0) error_log('New LP - Included learnpath', 0);
require_once 'learnpathItem.class.php';
if ($debug > 0) error_log('New LP - Included learnpathItem', 0);
require_once 'scorm.class.php';
if ($debug > 0) error_log('New LP - Included scorm', 0);
require_once 'scormItem.class.php';
if ($debug > 0) error_log('New LP - Included scormItem', 0);
require_once 'aicc.class.php';
if ($debug > 0) error_log('New LP - Included aicc', 0);
require_once 'aiccItem.class.php';
if ($debug > 0) error_log('New LP - Included aiccItem', 0);

require_once 'back_compat.inc.php';
if ($debug > 0) error_log('New LP - Included back_compat', 0);

$session_id = api_get_session_id();

api_protect_course_script(true);

require_once api_get_path(LIBRARY_PATH).'fckeditor/fckeditor.php';
$lpfound = false;

$myrefresh = 0;
$myrefresh_id = 0;

if (!empty($_SESSION['refresh']) && $_SESSION['refresh'] == 1) {
    // Check if we should do a refresh of the oLP object (for example after editing the LP).
    // If refresh is set, we regenerate the oLP object from the database (kind of flush).
    Session::erase('refresh');
    $myrefresh = 1;
    if ($debug > 0) error_log('New LP - Refresh asked', 0);
}

if ($debug > 0) error_log('New LP - Passed refresh check', 0);

if (!empty($_REQUEST['dialog_box'])) {
    $dialog_box = stripslashes(urldecode($_REQUEST['dialog_box']));
}

$lp_controller_touched = 1;
$lp_found = false;

if (isset($_SESSION['lpobject'])) {
    if ($debug > 0) error_log('New LP - SESSION[lpobject] is defined', 0);
    $oLP = unserialize($_SESSION['lpobject']);
    if (isset($oLP) && is_object($oLP)) {
        if ($debug > 0) error_log('New LP - oLP is object', 0);
        if ($myrefresh == 1 OR empty($oLP->cc) OR $oLP->cc != api_get_course_id() OR $oLP->lp_view_session_id != $session_id OR $oLP->scorm_debug == '1') {
            if ($debug > 0) error_log('New LP - Course has changed, discard lp object', 0);
            if ($myrefresh == 1) { $myrefresh_id = $oLP->get_id(); }
            $oLP = null;
            Session::erase('oLP');
            Session::erase('lpobject');
        } else {
            $_SESSION['oLP'] = $oLP;
            $lp_found = true;
        }
    }
}

$course_id = api_get_course_int_id();

if ($debug>0) error_log('New LP - Passed data remains check', 0);

if (!$lp_found || (!empty($_REQUEST['lp_id']) && $_SESSION['oLP']->get_id() != $_REQUEST['lp_id'])) {
    if ($debug > 0) error_log('New LP - oLP is not object, has changed or refresh been asked, getting new', 0);
    // Regenerate a new lp object? Not always as some pages don't need the object (like upload?)
    if (!empty($_REQUEST['lp_id']) || !empty($myrefresh_id)) {
        if ($debug > 0) error_log('New LP - lp_id is defined', 0);
        // Select the lp in the database and check which type it is (scorm/dokeos/aicc) to generate the
        // right object.
        if (!empty($_REQUEST['lp_id'])) {
            $lp_id = intval($_REQUEST['lp_id']);
        } else {
            $lp_id = intval($myrefresh_id);
        }

        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
        if (is_numeric($lp_id)) {
            $sel = "SELECT lp_type FROM $lp_table WHERE c_id = $course_id AND id = $lp_id";
            if ($debug > 0) error_log('New LP - querying '.$sel, 0);
            $res = Database::query($sel);
            if (Database::num_rows($res)) {
                $row = Database::fetch_array($res);
                $type = $row['lp_type'];
                if ($debug > 0) error_log('New LP - found row - type '.$type. ' - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
                switch ($type) {
                    case 1:
                        if ($debug > 0) error_log('New LP - found row - type dokeos - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
                        $oLP = new learnpath(api_get_course_id(), $lp_id, api_get_user_id());
                        if ($oLP !== false) { $lp_found = true; } else { error_log($oLP->error, 0); }
                         break;
                    case 2:
                        if ($debug > 0) error_log('New LP - found row - type scorm - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
                        $oLP = new scorm(api_get_course_id(), $lp_id, api_get_user_id());
                        if ($oLP !== false) { $lp_found = true; } else { error_log($oLP->error, 0); }
                        break;
                    case 3:
                        if ($debug > 0) error_log('New LP - found row - type aicc - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
                        $oLP = new aicc(api_get_course_id(),$lp_id,api_get_user_id());
                        if ($oLP !== false) { $lp_found = true; } else { error_log($oLP->error, 0); }
                        break;
                    default:
                        if ($debug > 0) error_log('New LP - found row - type other - Calling constructor with '.api_get_course_id().' - '.$lp_id.' - '.api_get_user_id(), 0);
                        $oLP = new learnpath(api_get_course_id(),$lp_id,api_get_user_id());
                        if ($oLP !== false) { $lp_found = true; } else { error_log($oLP->error, 0); }
                        break;
                }
            }
        } else {
            if ($debug > 0) error_log('New LP - Request[lp_id] is not numeric', 0);
        }
    } else {
        if ($debug > 0) error_log('New LP - Request[lp_id] and refresh_id were empty', 0);
    }
    if ($lp_found) {
        $_SESSION['oLP'] = $oLP;
    }
}
if ($debug > 0) error_log('New LP - Passed oLP creation check', 0);

$is_allowed_to_edit = api_is_allowed_to_edit(false, true, false, false);


/**
 * Actions switching
 */
if (isset($_SESSION['oLP'])) {
    $_SESSION['oLP']->update_queue = array(); // Reinitialises array used by javascript to update items in the TOC.
    $_SESSION['oLP']->message = ''; // Should use ->clear_message() method but doesn't work.
}

if (isset($_GET['isStudentView']) && $_GET['isStudentView'] == 'true') {
    if ($_REQUEST['action'] != 'list' && $_REQUEST['action'] != 'view') {
        if (!empty($_REQUEST['lp_id'])) {
            $_REQUEST['action'] = 'view';
        } else {
            $_REQUEST['action'] = 'list';
        }
    }
} else {
    if ($is_allowed_to_edit) {
        if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'view' && !isset($_REQUEST['exeId'])) {
            $_REQUEST['action'] = 'build';
        }
    }
}

$action = (!empty($_REQUEST['action']) ? $_REQUEST['action'] : '');

switch ($action) {
    case 'add_item':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - add item action triggered', 0);

        if (!$lp_found) {
            //check if the learnpath ID was defined, otherwise send back to list
            if ($debug > 0) error_log('New LP - No learnpath given for add item', 0);
            require 'lp_list.php';
        } else {
            $_SESSION['refresh'] = 1;

            if (isset($_POST['submit_button']) && !empty($_POST['title'])) {
                // If a title was sumbitted:

                //Updating the lp.modified_on
                $_SESSION['oLP']->set_modified_on();

                if (isset($_SESSION['post_time']) && $_SESSION['post_time'] == $_POST['post_time']) {
                    // Check post_time to ensure ??? (counter-hacking measure?)
                    require 'lp_add_item.php';
                } else {
                    $_SESSION['post_time'] = $_POST['post_time'];
                    if ($_POST['type'] == TOOL_DOCUMENT) {
                        if (isset($_POST['path']) && $_GET['edit'] != 'true') {
                            $document_id = $_POST['path'];
                        } else {
                            $document_id = $_SESSION['oLP']->create_document($_course);
                        }
                        $new_item_id = $_SESSION['oLP']->add_item($_POST['parent'], $_POST['previous'], $_POST['type'], $document_id, $_POST['title'], $_POST['description'], $_POST['prerequisites']);
                    } else {
                        // For all other item types than documents, load the item using the item type and path rather than its ID.
                        $new_item_id = $_SESSION['oLP']->add_item($_POST['parent'], $_POST['previous'], $_POST['type'], $_POST['path'], $_POST['title'], $_POST['description'], $_POST['prerequisites'], $_POST['maxTimeAllowed']);
                    }
                    $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
                    header('Location: '.$url);
                    exit;
                }
            } else {
                require 'lp_add_item.php';
            }
        }
        break;
    case 'add_audio':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - add audio action triggered', 0);

        if (!$lp_found) {
            //check if the learnpath ID was defined, otherwise send back to list
            if ($debug > 0) error_log('New LP - No learnpath given for add audio', 0);
            require 'lp_list.php';
        } else {
            $_SESSION['refresh'] = 1;

            if (isset($_REQUEST['id'])) {
                $lp_item_obj = new learnpathItem($_REQUEST['id']);

                //Remove audio
                if (isset($_POST['delete_file']) && $_POST['delete_file'] == 1) {
                     $lp_item_obj->remove_audio();
                }

                //Upload audio
                if (isset($_FILES['file']) && !empty($_FILES['file'])) {
                    //Updating the lp.modified_on
                    $_SESSION['oLP']->set_modified_on();
                    $lp_item_obj->add_audio();
                }

                //Add audio file from documents
                if (isset($_REQUEST['document_id']) && !empty($_REQUEST['document_id'])) {
                    $_SESSION['oLP']->set_modified_on();
                    $lp_item_obj->add_audio_from_documents($_REQUEST['document_id']);
                }

                // Display.
                require 'lp_add_audio.php';
            } else {
                require 'lp_add_audio.php';
            }
        }
        break;
    case 'add_lp':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - add_lp action triggered', 0);
        if (isset($_REQUEST['lp_name']) && !empty($_REQUEST['lp_name'])) {
            $_REQUEST['lp_name'] = trim($_REQUEST['lp_name']);
            $_SESSION['refresh'] = 1;

            if (isset($_SESSION['post_time']) && $_SESSION['post_time'] == $_REQUEST['post_time']) {
                require 'lp_add.php';
            } else {
                $_SESSION['post_time'] = $_REQUEST['post_time'];

                if (isset($_REQUEST['activate_start_date_check']) && $_REQUEST['activate_start_date_check'] == 1) {
                	$publicated_on  = $_REQUEST['publicated_on'];
                	$publicated_on  = $publicated_on['Y'].'-'.$publicated_on['F'].'-'.$publicated_on['d'].' '.$publicated_on['H'].':'.$publicated_on['i'].':00';
                } else {
                	$publicated_on = null;
                }

                if (isset($_REQUEST['activate_end_date_check']) && $_REQUEST['activate_end_date_check'] == 1) {
                	$expired_on   = $_REQUEST['expired_on'];
                	$expired_on   = $expired_on['Y'].'-'.$expired_on['F'].'-'.$expired_on['d'].' '.$expired_on['H'].':'.$expired_on['i'].':00';
                } else {
                	$expired_on   = null;
                }

                $new_lp_id = learnpath::add_lp(api_get_course_id(), Security::remove_XSS($_REQUEST['lp_name']), '', 'chamilo', 'manual', '', $publicated_on, $expired_on);

                if (is_numeric($new_lp_id)) {
                    // TODO: Maybe create a first module directly to avoid bugging the user with useless queries
                    $_SESSION['oLP'] = new learnpath(api_get_course_id(),$new_lp_id,api_get_user_id());
                    //require 'lp_build.php';
                    $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($new_lp_id);
                    header('Location: '.$url);
                    exit;
                }
            }
        } else {
            require 'lp_add.php';
        }

        break;

    case 'admin_view':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - admin_view action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for admin_view', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            require 'lp_admin_view.php';
        }
        break;
    case 'auto_launch':
        if (api_get_course_setting('enable_lp_auto_launch') == 1) { //Redirect to a specific LP
            if (!$is_allowed_to_edit) {
                api_not_allowed(true);
            }
            if ($debug > 0) error_log('New LP - auto_launch action triggered', 0);
            if (!$lp_found) { error_log('New LP - No learnpath given for set_autolunch', 0); require 'lp_list.php'; }
            else {
                $_SESSION['oLP']->set_autolunch($_GET['lp_id'], $_GET['status']);
                require 'lp_list.php';
                exit;
            }
        }
        break;
    case 'build':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - build action triggered', 0);

        if (!$lp_found) { error_log('New LP - No learnpath given for build', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            //require 'lp_build.php';
            $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
            header('Location: '.$url);
            exit;
        }
        break;
    case 'edit_item':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - edit item action triggered', 0);

        if (!$lp_found) { error_log('New LP - No learnpath given for edit item', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            if (isset($_POST['submit_button']) && !empty($_POST['title'])) {

                //Updating the lp.modified_on
                $_SESSION['oLP']->set_modified_on();

                // TODO: mp3 edit
                $audio = array();
                if (isset($_FILES['mp3'])) {
                    $audio = $_FILES['mp3'];
                }
                $_SESSION['oLP']->edit_item($_REQUEST['id'], $_POST['parent'], $_POST['previous'], $_POST['title'], $_POST['description'], $_POST['prerequisites'], $audio, $_POST['maxTimeAllowed']);

                if (isset($_POST['content_lp'])) {
                    $_SESSION['oLP']->edit_document($_course);
                }
                $is_success = true;

                $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
                header('Location: '.$url);
                exit;
            }
            if (isset($_GET['view']) && $_GET['view'] == 'build') {
                require 'lp_edit_item.php';
            } else {
                require 'lp_admin_view.php';
            }
        }
        break;
    case 'edit_item_prereq':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - edit item prereq action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for edit item prereq', 0); require 'lp_list.php'; }
        else {
            if (isset($_POST['submit_button'])) {
                //Updating the lp.modified_on
                $_SESSION['oLP']->set_modified_on();
                $_SESSION['refresh'] = 1;

                if ($_SESSION['oLP']->edit_item_prereq($_GET['id'], $_POST['prerequisites'], $_POST['min_' . $_POST['prerequisites']], $_POST['max_' . $_POST['prerequisites']])) {
                    $is_success = true;
                }

                $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
                header('Location: '.$url);
                exit;
            } else {
                require 'lp_edit_item_prereq.php';
            }
        }
        break;

    case 'move_item':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - move item action triggered', 0);

        if (!$lp_found) { error_log('New LP - No learnpath given for move item', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            if (isset($_POST['submit_button'])) {
                //Updating the lp.modified_on
                $_SESSION['oLP']->set_modified_on();

                $_SESSION['oLP']->edit_item($_GET['id'], $_POST['parent'], $_POST['previous'], $_POST['title'], $_POST['description']);
                $is_success = true;
                $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id);
                header('Location: '.$url);
            }
            if (isset($_GET['view']) && $_GET['view'] == 'build') {
                require 'lp_move_item.php';
            } else {
                // Avoids weird behaviours see CT#967.
                $check = Security::check_token('get');
                if ($check) {
                    $_SESSION['oLP']->move_item($_GET['id'], $_GET['direction']);
                }
                Security::clear_token();
                require 'lp_admin_view.php';
            }
        }
        break;
    case 'view_item':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - view_item action triggered', 0);
        if (!$lp_found) {
            error_log('New LP - No learnpath given for view item', 0); require 'lp_list.php';
        } else {
            $_SESSION['refresh'] = 1;
            require 'lp_view_item.php';
        }
        break;
    case 'upload':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - upload action triggered', 0);
        $cwdir = getcwd();
        require 'lp_upload.php';
        // Reinit current working directory as many functions in upload change it.
        chdir($cwdir);
        require 'lp_list.php';
        break;

    case 'copy':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - export action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for copy', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->copy();
        }
        require 'lp_list.php';
        break;
    case 'export':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - export action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for export', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->scorm_export();
            exit();
            //require 'lp_list.php';
        }
        break;
    case 'export_to_pdf':

        if (!learnpath::is_lp_visible_for_student($_SESSION['oLP']->lp_id, api_get_user_id())) {
            api_not_allowed();
        }
        if ($debug > 0) error_log('New LP - export action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for export_to_pdf', 0); require 'lp_list.php';
        } else {
            $result = $_SESSION['oLP']->scorm_export_to_pdf($_GET['lp_id']);
            if (!$result) {
                require 'lp_list.php';
            }
            exit;
        }
        break;
    case 'delete':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - delete action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for delete', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            // Remove lp from homepage if it is there.
            //$_SESSION['oLP']->toggle_visibility((int)$_GET['lp_id'],'i');
            $_SESSION['oLP']->delete(null,(int)$_GET['lp_id'],'remove');
            Session::erase('oLP');
            require 'lp_list.php';
        }
        break;

    case 'toggle_visible': // Change lp visibility (inside lp tool).
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - visibility action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for visibility', 0); require 'lp_list.php'; }
        else {
            learnpath::toggle_visibility($_REQUEST['lp_id'], $_REQUEST['new_status']);
            require 'lp_list.php';
        }
        break;

    case 'toggle_publish': // Change lp published status (visibility on homepage).
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - publish action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for publish', 0); require 'lp_list.php'; }
        else {
            learnpath::toggle_publish($_REQUEST['lp_id'],$_REQUEST['new_status']);
            require 'lp_list.php';
        }
        break;

    case 'move_lp_up': // Change lp published status (visibility on homepage)
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - publish action triggered', 0);
        if (!$lp_found) {
            error_log('New LP - No learnpath given for publish', 0);
            require 'lp_list.php';
        } else {
            learnpath::move_up($_REQUEST['lp_id']);
            require 'lp_list.php';
        }
        break;

    case 'move_lp_down': //change lp published status (visibility on homepage)
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - publish action triggered', 0);
        if (!$lp_found) {
            error_log('New LP - No learnpath given for publish', 0);
            require 'lp_list.php';
        } else {
            learnpath::move_down($_REQUEST['lp_id']);
            require 'lp_list.php';
        }
        break;

    case 'edit':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - edit action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for edit', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            require 'lp_edit.php';
        }
        break;
    case 'update_lp':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - update_lp action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for edit', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            $lp_name = Security::remove_XSS($_REQUEST['lp_name']);
            $_SESSION['oLP']->set_name($lp_name);
            $author = $_REQUEST['lp_author'];
            // Fixing the author name (no body or html tags).
            $auth_init = stripos($author, '<p>');
            if ($auth_init === false) {
                $auth_init = stripos($author, '<body>');
                $auth_end = $auth_init + stripos(substr($author, $auth_init + 6), '</body>') + 7;
                $len = $auth_end - $auth_init + 6;
            } else {
                $auth_end = strripos($author, '</p>');
                $len = $auth_end - $auth_init + 4;
            }

            $author_fixed = substr($author, $auth_init, $len);
            //$author_fixed = $author;

            $_SESSION['oLP']->set_author($author_fixed);
            $_SESSION['oLP']->set_encoding($_REQUEST['lp_encoding']);  // TODO (as of Chamilo 1.8.8): Check in the future whether this field is needed.

            if (isset($_REQUEST['lp_maker'])) {
                $_SESSION['oLP']->set_maker($_REQUEST['lp_maker']);
            }
            if (isset($_REQUEST['lp_proximity'])) {
                $_SESSION['oLP']->set_proximity($_REQUEST['lp_proximity']);
            }
            $_SESSION['oLP']->set_theme($_REQUEST['lp_theme']);

            if (isset($_REQUEST['hide_toc_frame'])) {
                $_SESSION['oLP']->set_hide_toc_frame($_REQUEST['hide_toc_frame']);
            }

            $_SESSION['oLP']->set_prerequisite($_REQUEST['prerequisites']);
            $_SESSION['oLP']->set_use_max_score($_REQUEST['use_max_score']);

            if (isset($_REQUEST['activate_start_date_check']) && $_REQUEST['activate_start_date_check'] == 1) {
            	$publicated_on  = $_REQUEST['publicated_on'];
            	$publicated_on  = $publicated_on['Y'].'-'.$publicated_on['F'].'-'.$publicated_on['d'].' '.$publicated_on['H'].':'.$publicated_on['i'].':00';
            } else {
            	$publicated_on = null;
            }

            if (isset($_REQUEST['activate_end_date_check']) && $_REQUEST['activate_end_date_check'] == 1) {
            	$expired_on   = $_REQUEST['expired_on'];
            	$expired_on   = $expired_on['Y'].'-'.$expired_on['F'].'-'.$expired_on['d'].' '.$expired_on['H'].':'.$expired_on['i'].':00';
            } else {
            	$expired_on   = null;
            }

            $_SESSION['oLP']->set_modified_on();
            $_SESSION['oLP']->set_publicated_on($publicated_on);
            $_SESSION['oLP']->set_expired_on($expired_on);

            if (isset($_REQUEST['remove_picture']) && $_REQUEST['remove_picture']) {
                $_SESSION['oLP']->delete_lp_image();
            }

            if ($_FILES['lp_preview_image']['size'] > 0)
                $_SESSION['oLP']->upload_image($_FILES['lp_preview_image']);

            if (api_get_setting('search_enabled') === 'true') {
                require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
                $specific_fields = get_specific_field_list();
                foreach ($specific_fields as $specific_field) {
                    $_SESSION['oLP']->set_terms_by_prefix($_REQUEST[$specific_field['code']], $specific_field['code']);
                    $new_values = explode(',', trim($_REQUEST[$specific_field['code']]));
                    if (!empty($new_values)) {
                        array_walk($new_values, 'trim');
                        delete_all_specific_field_value(api_get_course_id(), $specific_field['id'], TOOL_LEARNPATH, $_SESSION['oLP']->lp_id);

                        foreach ($new_values as $value) {
                            if (!empty($value)) {
                                add_specific_field_value($specific_field['id'], api_get_course_id(), TOOL_LEARNPATH, $_SESSION['oLP']->lp_id, $value);
                            }
                        }
                    }
                }
            }
            $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id).'&'.api_get_cidreq();
            header('Location: '.$url);
            exit;
        }
        break;
    case 'add_sub_item': // Add an item inside a chapter.
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - add sub item action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for add sub item', 0); require 'lp_list.php'; }
        else {
            $_SESSION['refresh'] = 1;
            if (!empty($_REQUEST['parent_item_id'])) {
                $_SESSION['from_learnpath']='yes';
                $_SESSION['origintoolurl'] = 'lp_controller.php?action=admin_view&lp_id='.Security::remove_XSS($_REQUEST['lp_id']);
                require 'resourcelinker.php';
                //$_SESSION['oLP']->add_sub_item($_REQUEST['parent_item_id'], $_REQUEST['previous'], $_REQUEST['type'], $_REQUEST['path'], $_REQUEST['title']);
            } else {
                require 'lp_admin_view.php';
            }
        }
        break;
    case 'deleteitem':
    case 'delete_item':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - delete item action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for delete item', 0); require 'lp_list.php'; }
        else {
            //$_SESSION['refresh'] = 1;
            if (!empty($_REQUEST['id'])) {
                $_SESSION['oLP']->delete_item($_REQUEST['id']);
            }
            $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_REQUEST['lp_id']);
            header('Location: '.$url);
            exit;
        }
        break;
    case 'edititemprereq':
    case 'edit_item_prereq':
        if (!$is_allowed_to_edit) {
            api_not_allowed(true);
        }
        if ($debug > 0) error_log('New LP - edit item prereq action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for edit item prereq', 0); require 'lp_list.php'; }
        else {
            if (!empty($_REQUEST['id']) && !empty($_REQUEST['submit_item'])) {
                $_SESSION['refresh'] = 1;
                $_SESSION['oLP']->edit_item_prereq($_REQUEST['id'], $_REQUEST['prereq']);
            }
            require 'lp_admin_view.php';
        }
        break;

    case 'restart':
        if ($debug > 0) error_log('New LP - restart action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for restart', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->restart();
            require 'lp_view.php';
        }
        break;

    case 'last':
        if ($debug > 0) error_log('New LP - last action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for last', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->last();
            require 'lp_view.php';
        }
        break;

    case 'first':
        if ($debug > 0) error_log('New LP - first action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for first', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->first();
            require 'lp_view.php';
        }
        break;

    case 'next':
        if ($debug > 0) error_log('New LP - next action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for next', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->next();
            require 'lp_view.php';
        }
        break;
    case 'previous':
        if ($debug > 0) error_log('New LP - previous action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for previous', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->previous();
            require 'lp_view.php';
        }
        break;
    case 'content':
        if ($debug > 0) error_log('New LP - content action triggered', 0);
        if ($debug > 0) error_log('New LP - Item id is '.$_GET['item_id'], 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for content', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->save_last();
            $_SESSION['oLP']->set_current_item($_GET['item_id']);
            $_SESSION['oLP']->start_current_item();
            require 'lp_content.php';
        }
        break;
    case 'view':
        if ($debug > 0)
            error_log('New LP - view action triggered', 0);
        if (!$lp_found) {
            error_log('New LP - No learnpath given for view', 0);
            require 'lp_list.php';
        } else {
            if ($debug > 0) {error_log('New LP - Trying to set current item to ' . $_REQUEST['item_id'], 0); }
            if ( !empty($_REQUEST['item_id']) ) {
                $_SESSION['oLP']->set_current_item($_REQUEST['item_id']);
            }
            require 'lp_view.php';
        }
        break;
    case 'save':
        if ($debug > 0) error_log('New LP - save action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for save', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->save_item();
            require 'lp_save.php';
        }
        break;
    case 'stats':
        if ($debug > 0) error_log('New LP - stats action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for stats', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->save_current();
            $_SESSION['oLP']->save_last();
            require 'lp_stats.php';
        }
        break;
    case 'list':
        if ($debug > 0) error_log('New LP - list action triggered', 0);
        if ($lp_found) {
            $_SESSION['refresh'] = 1;
            $_SESSION['oLP']->save_last();
        }
        require 'lp_list.php';
        break;

    case 'mode':
        // Switch between fullscreen and embedded mode.
        if ($debug > 0) error_log('New LP - mode change triggered', 0);
        $mode = $_REQUEST['mode'];
        if ($mode == 'fullscreen') {
            $_SESSION['oLP']->mode = 'fullscreen';
        } elseif ($mode == 'embedded') {
            $_SESSION['oLP']->mode = 'embedded';
        } elseif ($mode == 'embedframe') {
        	$_SESSION['oLP']->mode = 'embedframe';
        }
        require 'lp_view.php';
        break;

    case 'switch_view_mode':
        if ($debug > 0) error_log('New LP - switch_view_mode action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for switch', 0); require 'lp_list.php'; }
        $_SESSION['refresh'] = 1;
        $_SESSION['oLP']->update_default_view_mode();
        require 'lp_list.php';
        break;

    case 'switch_force_commit':
        if ($debug > 0) error_log('New LP - switch_force_commit action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for switch', 0); require 'lp_list.php'; }
        $_SESSION['refresh'] = 1;
        $_SESSION['oLP']->update_default_scorm_commit();
        require 'lp_list.php';
        break;
    /* Those 2 switches have been replaced by switc_attempt_mode switch
    case 'switch_reinit':
        if ($debug > 0) error_log('New LP - switch_reinit action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for switch', 0); require 'lp_list.php'; }
        $_SESSION['refresh'] = 1;
        $_SESSION['oLP']->update_reinit();
		require 'lp_list.php';
		break;
	case 'switch_seriousgame_mode':
		if($debug>0) error_log('New LP - switch_seriousgame_mode action triggered',0);
		if(!$lp_found){ error_log('New LP - No learnpath given for switch',0); require 'lp_list.php'; }
		$_SESSION['refresh'] = 1;
		$_SESSION['oLP']->set_seriousgame_mode();
		require 'lp_list.php';
		break;
     */
	case 'switch_attempt_mode':
		if($debug>0) error_log('New LP - switch_reinit action triggered',0);
		if(!$lp_found){ error_log('New LP - No learnpath given for switch',0); require 'lp_list.php'; }
		$_SESSION['refresh'] = 1;
		$_SESSION['oLP']->switch_attempt_mode();
        require 'lp_list.php';
        break;

    case 'switch_scorm_debug':
        if ($debug > 0) error_log('New LP - switch_scorm_debug action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for switch', 0); require 'lp_list.php'; }
        $_SESSION['refresh'] = 1;
        $_SESSION['oLP']->update_scorm_debug();
        require 'lp_list.php';
        break;

    case 'intro_cmdAdd':
        if ($debug > 0) error_log('New LP - intro_cmdAdd action triggered', 0);
        // Add introduction section page.
        break;

    case 'js_api_refresh':
        if ($debug > 0) error_log('New LP - js_api_refresh action triggered', 0);
        if (!$lp_found) { error_log('New LP - No learnpath given for js_api_refresh', 0); require 'lp_message.php'; }
        if (isset($_REQUEST['item_id'])) {
            $htmlHeadXtra[] = $_SESSION['oLP']->get_js_info($_REQUEST['item_id']);
        }
        require 'lp_message.php';
        break;
    case 'return_to_course_homepage':
        if (!$lp_found) { error_log('New LP - No learnpath given for stats', 0); require 'lp_list.php'; }
        else {
            $_SESSION['oLP']->save_current();
            $_SESSION['oLP']->save_last();
            header('location: '.api_get_path(WEB_PATH).'main/formation/index.php'); // BAT /BAT
            exit;
        }
        break;
    case 'search':
        /* Include the search script, it's smart enough to know when we are
         * searching or not.
         */
        require 'lp_list_search.php';
        break;
    case 'impress':
        if ($debug > 0)
            error_log('New LP - view action triggered', 0);
        if (!$lp_found) {
            error_log('New LP - No learnpath given for view', 0);
            require 'lp_list.php';
        } else {
            if ($debug > 0) {error_log('New LP - Trying to impress this LP item to ' . $_REQUEST['item_id'], 0); }
            if (!empty($_REQUEST['item_id']) ) {
                $_SESSION['oLP']->set_current_item($_REQUEST['item_id']);
            }
            require 'lp_impress.php';
        }
        break;
    case 'set_previous_step_as_prerequisite':
        $_SESSION['oLP']->set_previous_step_as_prerequisite_for_all_items();
        $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id)."&message=ItemUpdated";
        header('Location: '.$url);
        break;
    case 'clear_prerequisites':
        $_SESSION['oLP']->clear_prerequisites();
        $url = api_get_self().'?action=add_item&type=step&lp_id='.intval($_SESSION['oLP']->lp_id)."&message=ItemUpdated";
        header('Location: '.$url);
        break;
    default:
        if ($debug > 0) error_log('New LP - default action triggered', 0);
        require 'lp_list.php';
        break;
}

if (!empty($_SESSION['oLP'])) {
    $_SESSION['lpobject'] = serialize($_SESSION['oLP']);
    if ($debug > 0) error_log('New LP - lpobject is serialized in session', 0);
}lp_edit.php000064400000022311152003363470006677 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Script allowing simple edition of learnpath information (title, description, etc)
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
*/

require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';

global $charset;

$show_description_field = false; //for now
$nameTools = get_lang('Doc');
$this_section = SECTION_COURSES;
event_access_tool(TOOL_LEARNPATH);

api_protect_course_script();

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}
$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=".$_SESSION['oLP']->get_id(), 'name' => $_SESSION['oLP']->get_name());
//$interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

$htmlHeadXtra[] = '<script>
function activate_start_date() {
	if(document.getElementById(\'start_date_div\').style.display == \'none\') {
		document.getElementById(\'start_date_div\').style.display = \'block\';
	} else {
		document.getElementById(\'start_date_div\').style.display = \'none\';
	}
}

function activate_end_date() {
    if(document.getElementById(\'end_date_div\').style.display == \'none\') {
        document.getElementById(\'end_date_div\').style.display = \'block\';
    } else {
        document.getElementById(\'end_date_div\').style.display = \'none\';
    }
}

</script>';


Display::display_header(get_lang('CourseSettings'), 'Path');

echo $_SESSION['oLP']->build_action_menu();

$gradebook = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;

$defaults=array();
$form = new FormValidator('form1', 'post', 'lp_controller.php');

// Form title
$form->addElement('header', get_lang('EditLPSettings'));

// Title
$form->addElement('text', 'lp_name', api_ucfirst(get_lang('LearnpathTitle')), array('size' => 43));
$form->applyFilter('lp_name', 'html_filter');
$form->addRule('lp_name', get_lang('ThisFieldIsRequired'), 'required');

// Metadata
//$clean_scorm_id=Security::remove_XSS($_GET['lp_id']);
//$metadata_link = '<a href="../metadata/index.php?eid='.urlencode('Scorm.'.$clean_scorm_id).'">'.get_lang('AddMetadata').'</a>';
//$form->addElement('static', null, get_lang('Metadata'), $metadata_link);

// Encoding
/* // Chamilo 1.8.8: Deprecated code.
$encoding_select = $form->addElement('select', 'lp_encoding', get_lang('Charset'));
$encodings = array('UTF-8','ISO-8859-1','ISO-8859-15','cp1251','cp1252','KOI8-R','BIG5','GB2312','Shift_JIS','EUC-JP');
foreach ($encodings as $encoding) {
    if (api_equal_encodings($encoding, $_SESSION['oLP']->encoding)) {
          $s_selected_encoding = $encoding;
      }
      $encoding_select->addOption($encoding,$encoding);
}
*/
$form->addElement('hidden', 'lp_encoding');

// Origin
/*
$origin_select = $form->addElement('select', 'lp_maker', get_lang('Origin'));
$lp_orig = $_SESSION['oLP']->get_maker();

include 'content_makers.inc.php';
foreach ($content_origins as $origin) {
    if ($lp_orig == $origin) {
        $s_selected_origin = $origin;
    }
    $origin_select->addOption($origin, $origin);
}

// Content proximity
$content_proximity_select = $form->addElement('select', 'lp_proximity', get_lang('ContentProximity'));
$lp_prox = $_SESSION['oLP']->get_proximity();
if ($lp_prox != 'local') {
    $s_selected_proximity = 'remote';
} else {
    $s_selected_proximity = 'local';
}
$content_proximity_select->addOption(get_lang('Local'), 'local');
$content_proximity_select->addOption(get_lang('Remote'), 'remote');
*/
//Hide toc frame
$hide_toc_frame = $form->addElement('checkbox', 'hide_toc_frame', null, get_lang('HideTocFrame'),array('onclick' => '$("#lp_layout_column").toggle()' ));
if (api_get_setting('allow_course_theme') == 'true') {
    $mycourselptheme = api_get_course_setting('allow_learning_path_theme');
    if (!empty($mycourselptheme) && $mycourselptheme!=-1 && $mycourselptheme== 1) {
        //LP theme picker
        $theme_select = $form->addElement('select_theme', 'lp_theme', get_lang('Theme'));
        $form->applyFilter('lp_theme', 'trim');

        $s_theme = $_SESSION['oLP']->get_theme();
        $theme_select ->setSelected($s_theme); //default
    }
}

// Author
$form->addElement('html_editor', 'lp_author', get_lang('Author'), array('size' => 80), array('ToolbarSet' => 'LearningPathAuthor', 'Width' => '100%', 'Height' => '150px') );
$form->applyFilter('lp_author', 'html_filter');

// LP image
$form->add_progress_bar();
if (strlen($_SESSION['oLP']->get_preview_image()) > 0) {
    $show_preview_image='<img src='.api_get_path(WEB_COURSE_PATH).api_get_course_path().'/upload/learning_path/images/'.$_SESSION['oLP']->get_preview_image().'>';
    $form->addElement('label', get_lang('ImagePreview'), $show_preview_image);
    $form->addElement('checkbox', 'remove_picture', null, get_lang('DelImage'));
}
$label = ($_SESSION['oLP']->get_preview_image() != '' ? get_lang('UpdateImage') : get_lang('AddImage'));
$form->addElement('file', 'lp_preview_image', array($label, get_lang('ImageWillResizeMsg')));

$form->addRule('lp_preview_image', get_lang('OnlyImagesAllowed'), 'filetype', array ('jpg', 'jpeg', 'png', 'gif'));

// Search terms (only if search is activated).
if (api_get_setting('search_enabled') === 'true') {
    $specific_fields = get_specific_field_list();
    foreach ($specific_fields as $specific_field) {
        $form -> addElement ('text', $specific_field['code'], $specific_field['name']);
        $filter = array('c_id'=> "'". api_get_course_int_id() ."'", 'field_id' => $specific_field['id'], 'ref_id' => $_SESSION['oLP']->lp_id, 'tool_id' => '\''. TOOL_LEARNPATH .'\'');
        $values = get_specific_field_values_list($filter, array('value'));
        if (!empty($values)) {
            $arr_str_values = array();
            foreach ($values as $value) {
                $arr_str_values[] = $value['value'];
            }
            $defaults[$specific_field['code']] = implode(', ', $arr_str_values);
        }
    }
}

$defaults['lp_encoding']    = Security::remove_XSS($_SESSION['oLP']->encoding);
$defaults['lp_name']        = Security::remove_XSS($_SESSION['oLP']->get_name());
$defaults['lp_author']      = Security::remove_XSS($_SESSION['oLP']->get_author());
$defaults['hide_toc_frame'] = Security::remove_XSS($_SESSION['oLP']->get_hide_toc_frame());

$expired_on     = $_SESSION['oLP'] ->expired_on;
$publicated_on  = $_SESSION['oLP'] ->publicated_on;

// Prerequisites
$form->addElement('html', '<div class="control-group"><label class="control-label">'.get_lang('LearnpathPrerequisites').'</label>
<div class="controls">'.$_SESSION['oLP']->display_lp_prerequisites_list().' <span class="help-block">'.get_lang('LpPrerequisiteDescription').'</span></div></div>');

//Start date
$form->addElement('checkbox', 'activate_start_date_check', null,get_lang('EnableStartTime'), array('onclick' => 'activate_start_date()'));
$display_date = 'none';
if ($publicated_on!='0000-00-00 00:00:00' && !empty($publicated_on)) {
	$display_date = 'block';
	$defaults['activate_start_date_check'] = 1;
}

$form->addElement('html','<div id="start_date_div" style="display:'.$display_date.';">');
$form->addElement('datepicker', 'publicated_on', get_lang('PublicationDate'), array('form_name'=>'form1'), 5);
$form->addElement('html','</div>');

//End date
$form->addElement('checkbox', 'activate_end_date_check',  null, get_lang('EnableEndTime'),  array('onclick' => 'activate_end_date()'));
$display_date = 'none';
if ($expired_on!='0000-00-00 00:00:00' && !empty($expired_on)) {
	$display_date = 'block';
	$defaults['activate_end_date_check'] = 1;
}

$form->addElement('html','<div id="end_date_div" style="display:'.$display_date.';">');
$form->addElement('datepicker', 'expired_on', get_lang('ExpirationDate'), array('form_name'=>'exercise_admin'), 5);
$form->addElement('html','</div>');

if (api_is_platform_admin()) {
    $form->addElement('checkbox', 'use_max_score', null, get_lang('UseMaxScore100'));
    $defaults['use_max_score'] = $_SESSION['oLP']->use_max_score;
}

//Submit button
$form->addElement('style_submit_button', 'Submit',get_lang('SaveLPSettings'),'class="save"');

// Hidden fields
$form->addElement('hidden', 'action', 'update_lp');
$form->addElement('hidden', 'lp_id', $_SESSION['oLP']->get_id());


$defaults['publicated_on']  = ($publicated_on!='0000-00-00 00:00:00' && !empty($publicated_on))? api_get_local_time($publicated_on) : date('Y-m-d 12:00:00');
$defaults['expired_on']     = ($expired_on   !='0000-00-00 00:00:00' && !empty($expired_on) )? api_get_local_time($expired_on): date('Y-m-d 12:00:00',time()+84600);

$form->setDefaults($defaults);

echo '<div class="row">';

if ($_SESSION['oLP']->get_hide_toc_frame() == 1) {
    echo '<div class="span12">';
    $form -> display();
    echo '</div>';
} else{
    echo '<div class="span6">';
    $form -> display();
    echo '</div>';
    echo '<div class="span6" align="center">';
    echo '<img src="../img/course_setting_layout.png" />';
    echo '</div>';
}
echo '</div>';
Display::display_footer();
lp_edit_item.php000064400000012656152003363470007730 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @author Julio Montoya  - Improving the list of templates
 * @package chamilo.learnpath
*/
/**
 * INIT SECTION 
 */

$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

include 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
include 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
// Name of the language file that needs to be included.
$language_file = 'learnpath';

/* Header and action code */

$htmlHeadXtra[] = '
<script>

function FCKeditor_OnComplete( editorInstance ) {
    document.getElementById(\'frmModel\').innerHTML = "<iframe height=890px; width=230px; frameborder=0 src=\''.api_get_path(WEB_LIBRARY_PATH).'fckeditor/editor/fckdialogframe.html \'>";
}

function InnerDialogLoaded() {
    if (document.all) {
        // if is iexplorer
        var B=new window.frames.content_lp___Frame.FCKToolbarButton(\'Templates\',window.content_lp___Frame.FCKLang.Templates);
    } else {
        var B=new window.frames[0].FCKToolbarButton(\'Templates\',window.frames[0].FCKLang.Templates);
    }
    return B.ClickFrame();
$};'.$_SESSION['oLP']->get_js_dropdown_array().'

</script>';

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);

$isStudentView  = (int) $_REQUEST['isStudentView'];
$learnpath_id   = (int) $_REQUEST['lp_id'];
$submit			= $_POST['submit_button'];

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_add_item.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
}
// From here on, we are admin because of the previous condition, so don't check anymore.

$course_id = api_get_course_int_id();
$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";
$result = Database::query($sql_query);
$therow = Database::fetch_array($result);

/*
    Course admin section
    - all the functions not available for students - always available in this case (page only shown to admin)
*/

/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
    );
}
$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", 'name' => stripslashes("{$therow['name']}"));
$interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

// Theme calls.
$show_learn_path = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

Display::display_header(get_lang('Edit'),'Path');
$suredel = trim(get_lang('AreYouSureToDelete'));

?>
<script>
/* <![CDATA[ */
function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}
function confirmation(name) {
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
        return true;
    } else {
        return false;
    }
}
</script>
<?php


/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid">';
echo '<div class="span3">';

$path_item = isset($_GET['path_item']) ? $_GET['path_item'] : 0;
$path_item = Database::escape_string($path_item);
$tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
$sql_doc = "SELECT path FROM " . $tbl_doc . " WHERE c_id = $course_id AND id = '". $path_item."' ";

$res_doc = Database::query($sql_doc);
$path_file = Database::result($res_doc, 0, 0);
$path_parts = pathinfo($path_file);

if (Database::num_rows($res_doc) > 0 && $path_parts['extension'] == 'html') {
    echo $_SESSION['oLP']->return_new_tree();
    
    // Show the template list
    echo '<p style="border-bottom:1px solid #999999; margin:0; padding:2px;"></p>';
    echo '<br />';
    echo '<div id="frmModel" style="display:block; height:890px;width:100px; position:relative;"></div>';
} else {
    echo $_SESSION['oLP']->return_new_tree();    
}
        
echo '</div>';
echo '<div class="span9">';

if (isset($is_success) && $is_success === true) {
    $msg = '<div class="lp_message" style="margin-bottom:10px;">';
    $msg .= 'The item has been edited.';
    $msg .= '</div>';
    echo $_SESSION['oLP']->display_item($_GET['id'], $msg);
} else {
    echo $_SESSION['oLP']->display_edit_item($_GET['id']);
}

echo '</div>';
echo '</div>';

/* FOOTER */
Display::display_footer();lp_edit_item_prereq.php000064400000007510152003363470011277 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @package chamilo.learnpath
 */
$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

include 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
include 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
// Name of the language file that needs to be included.
$language_file = 'learnpath';

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
$tbl_lp_view = Database::get_course_table(TABLE_LP_VIEW);

$isStudentView = (int) $_REQUEST['isStudentView'];
$learnpath_id = (int) $_REQUEST['lp_id'];
$submit = $_POST['submit_button'];

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' and $type == 'learnpathitem') {
    $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_edit_item_prereq.php');
    header('location:lp_controller.php?action=view&lp_id=' . $learnpath_id);
}
$course_id = api_get_course_int_id();


$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";
$result = Database::query($sql_query);
$therow = Database::fetch_array($result);

/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array(
        'url' => '../gradebook/' . $_SESSION['gradebook_dest'],
        'name' => get_lang('ToolGradebook')
    );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self() . "?action=build&lp_id=$learnpath_id", 'name' => stripslashes("{$therow['name']}"));
$interbreadcrumb[] = array('url' => api_get_self() . "?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

// Theme calls.
$show_learn_path = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

Display::display_header(get_lang('LearnpathPrerequisites'), 'Path');

$suredel = trim(get_lang('AreYouSureToDelete'));
?>
<script>
    /* <![CDATA[ */
    function stripslashes(str) {
        str=str.replace(/\\'/g,'\'');
        str=str.replace(/\\"/g,'"');
        str=str.replace(/\\\\/g,'\\');
        str=str.replace(/\\0/g,'\0');
        return str;
    }
    function confirmation(name) {
        name=stripslashes(name);
        if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
            return true;
        } else {
            return false;
        }
    }
</script>
<?php

/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid">';
echo '<div class="span3">';
echo $_SESSION['oLP']->return_new_tree();
echo '</div>';
echo '<div class="span9">';
if (isset($is_success) && $is_success == true) {
    echo $_SESSION['oLP']->display_manipulate($_GET['id'], null);
    echo Display::return_message(get_lang("PrerequisitesAdded"));
} else {
    echo $_SESSION['oLP']->display_manipulate($_GET['id'], null);
    echo $_SESSION['oLP']->display_item_prerequisites_form($_GET['id']);
}
echo '</div>';

Display::display_footer();lp_export.php000064400000001551152003363470007276 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Script to export the current path as a SCORM zip package.
 * This script cannot use the common controller lp_controller.php because we need to keep
 * the headers clean of any session prior to output-ing the resulting file.
 * As we still need to check the user's credentials (because he might not have access to this file),
 * we need to get some ID proof.
 * Once the ID is checked with the database info, generate the file, send the corresponding headers
 * to force the download and let the user do the rest. This script should not change the screen
 * at all, so the user will still be able to continue what he was doing.
 * @todo get some ID proof
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * The script takes three get parameters that enable the export.
 */


lp_impress.php000064400000004170152003363470007437 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
*
* @package chamilo.learnpath
*/
/**
 * Code
 */

$_SESSION['whereami'] = 'lp/impress';
$this_section = SECTION_COURSES;

/* Libraries */
require_once 'back_compat.inc.php';

//To prevent the template class
$show_learnpath = true;

api_protect_course_script();

/* 
 * Only activate impress if you're the admin 
 */

if (!api_is_platform_admin()) {
    exit;
}

$lp_id = intval($_GET['lp_id']);

// Check if the learning path is visible for student - (LP requisites) 
if (!api_is_allowed_to_edit(null, true) && !learnpath::is_lp_visible_for_student($lp_id, api_get_user_id())) {
    api_not_allowed();
}     

//Checking visibility (eye icon)
$visibility = api_get_item_visibility(api_get_course_info(), TOOL_LEARNPATH, $lp_id, $action, api_get_user_id(), api_get_session_id());
if (!api_is_allowed_to_edit(null, true) && intval($visibility) == 0 ) {
     api_not_allowed();
}

if (empty($_SESSION['oLP'])) {
    api_not_allowed();
}

$debug = 0;

if ($debug) { error_log('------ Entering lp_impress.php -------'); }

$course_code    = api_get_course_id();
$course_id      = api_get_course_int_id();

//$htmlHeadXtra[] = api_get_js('impress/impress.js'); //added in the template
$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/impress/impress-demo.css');

$list = $_SESSION['oLP']->get_toc();

$html = '';
$step = 1;
foreach ($list as $toc) {
    $x = 1000*$step;
    //data-scale="'.$step.'"
    //data-x="850" data-y="3000" data-rotate="90" data-scale="5"
    $html .= '<div id="step-'.$step.'" class="step slide" data-x="'.$x.'" data-y="-1500"  >';
    $html .= '<h2>'.$toc['title'].'</h2>';
    
    $src = $_SESSION['oLP']->get_link('http', $toc['id']);
    //just showing the src in a iframe ...
    $html .= '<iframe border="0" frameborder="0" style="width:100%;height:600px" src="'.$src.'"></iframe>';
    
    $html .= "</div>\n";
    $step ++;
}

//Setting the template
$tpl = new Template($tool_name, false, false, true);
$tpl->assign('html', $html);
$content = $tpl->fetch('default/learnpath/impress.tpl');
$tpl->assign('content', $content);
$tpl->display_one_col_template();lp_list.php000064400000063574152003363470006745 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
* This file was origially the copy of document.php, but many modifications happened since then ;
* the direct file view is not any more needed, if the user uploads a scorm zip file, a directory
* will be automatically created for it, and the files will be uncompressed there for example ;
*
* @package chamilo.learnpath
* @author Yannick Warnier <ywarnier@beeznest.org>
*/
use \ChamiloSession as Session;

$this_section = SECTION_COURSES;
//@todo who turns on $lp_controller_touched?
if (empty($lp_controller_touched) || $lp_controller_touched != 1) {
    header('location: lp_controller.php?action=list');
}

require_once 'back_compat.inc.php';
$courseDir   = api_get_course_path().'/scorm';
$baseWordDir = $courseDir;

require_once 'learnpathList.class.php';
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';

/**
 * Display initialisation and security checks
 */
// Extra javascript functions for in html head:
$htmlHeadXtra[] =
"<script>
function confirmation(name) {
    if (confirm(\" ".trim(get_lang('AreYouSureToDelete'))." \"+name+\"?\"))
        {return true;}
    else
        {return false;}
}
</script>";
$nameTools = get_lang('LearningPaths');
event_access_tool(TOOL_LEARNPATH);

api_protect_course_script();
//if (!$is_allowed_in_course) api_not_allowed();

/**
 * Display
 */
/* Require the search widget and prepare the header with its stuff. */
if (api_get_setting('search_enabled') == 'true') {
    require api_get_path(LIBRARY_PATH).'search/search_widget.php';
    search_widget_prepare($htmlHeadXtra);
}
Display::display_header($nameTools, 'Path');
$current_session = api_get_session_id();

/* Introduction section (editable by course admins) */

Display::display_introduction_section(TOOL_LEARNPATH, array(
        'CreateDocumentWebDir' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/',
        'CreateDocumentDir' => '../../courses/'.api_get_course_path().'/document/',
        'BaseHref' => api_get_path(WEB_COURSE_PATH).api_get_course_path().'/'
    )
);

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

if ($is_allowed_to_edit) {
    /* DIALOG BOX SECTION */

    if (!empty($dialog_box)) {
        switch ($_GET['dialogtype']) {
            case 'confirmation':
                Display::display_confirmation_message($dialog_box);
                break;
            case 'error':
                Display::display_error_message($dialog_box);
                break;
            case 'warning':
                Display::display_warning_message($dialog_box);
                break;
            default:
                Display::display_normal_message($dialog_box);
                break;
        }
    }
    if (api_failure::get_last_failure()) {
        Display::display_normal_message(api_failure::get_last_failure());
    }

    echo '<div class="actions">';
    echo '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=add_lp">'.Display::return_icon('new_learnpath.png', get_lang('LearnpathAddLearnpath'),'',ICON_SIZE_MEDIUM).'</a>' .
        str_repeat('&nbsp;', 3).
        '<a href="../upload/index.php?'.api_get_cidreq().'&curdirpath=/&tool='.TOOL_LEARNPATH.'">'.Display::return_icon('import_scorm.png', get_lang('UploadScorm'),'',ICON_SIZE_MEDIUM).'</a>';
    
    // BAT
    echo '<a href="' . api_get_path(WEB_PATH) . 'main/formation/upload.php?type=turbo" style="padding-top: 7px; font-weight: bold;">Turbo</a>'; 
    $_SESSION['mm_current_course_code'] = $_GET['cidReq'];
    echo '<a href="' . api_get_path(WEB_PATH) . 'main/formation/upload.php?type=video" style="padding-top: 7px; font-weight: bold;">Video</a>';
    // /BAT
    
    if (api_get_setting('service_ppt2lp', 'active') == 'true') {
        echo str_repeat('&nbsp;', 3).'<a href="../upload/upload_ppt.php?'.api_get_cidreq().'&curdirpath=/&tool='.TOOL_LEARNPATH.'">
		'.Display::return_icon('import_powerpoint.png', get_lang('PowerPointConvert'),'',ICON_SIZE_MEDIUM).'</a>';
           //echo  str_repeat('&nbsp;', 3).'<a href="../upload/upload_word.php?'.api_get_cidreq().'&curdirpath=/&tool='.TOOL_LEARNPATH.'"><img src="../img/word.gif" border="0" alt="'.get_lang('WordConvert').'" align="absmiddle">&nbsp;'.get_lang('WordConvert').'</a>';
    }
    echo '</div>';
}

/* DISPLAY SCORM LIST */
$list       = new LearnpathList(api_get_user_id());
$flat_list  = $list->get_flat_list();


if (!empty($flat_list)) {

    echo '<table class="data_table">';
    $is_allowed_to_edit ? $colspan = 9 : $colspan = 3;

    if (!empty($curDirPath)) {
        if (substr($curDirPath, 1, 1) == '/') {
            $tmpcurDirPath=substr($curDirPath,1,strlen($curDirPath));
        } else {
            $tmpcurDirPath = $curDirPath;
        }
        ?>
        <!-- current dir name -->
        <tr>
            <td colspan="<?php echo $colspan; ?>" align="left" bgcolor="#4171B5">
                <img src="../img/opendir.gif" align="absbottom" vspace="2" hspace="3" alt="open_dir" />
                <font color="#ffffff"><b><?php echo $tmpcurDirPath; ?></b></font>
            </td>
        </tr>
        <?php
    }

    /* CURRENT DIRECTORY */

    echo '<tr>';

    if ($is_allowed_to_edit) {
        echo '<th width="50%">'.get_lang('Title').'</th>';
        echo '<th>'.get_lang('PublicationDate').'</th>';
        echo '<th>'.get_lang('ExpirationDate').'</th>';
        echo '<th>'.get_lang('Progress')."</th>";
        echo '<th width="240px">'.get_lang('AuthoringOptions')."</th>";
    } else {
        echo '<th width="50%">'.get_lang('Title').'</th>';
        echo '<th>'.get_lang('Progress')."</th>";
        echo '<th>'.get_lang('Actions')."</th>";
    }
    echo '</tr>';

    $test_mode      = api_get_setting('server_type');
    $max            = count($flat_list);
    $counter        = 0;
    $current        = 0;
    $autolunch_exists = false;
    foreach ($flat_list as $id => $details) {

        // Validacion when belongs to a session
        $session_img = api_get_session_image($details['lp_session'], $_user['status']);

        if (!$is_allowed_to_edit && $details['lp_visibility'] == 0) {
            // This is a student and this path is invisible, skip.
            continue;
        }

        // Check if the learnpath is visible for student.
        if (!$is_allowed_to_edit && !learnpath::is_lp_visible_for_student($id, api_get_user_id())) {
            continue;
        }
        $start_time =  $end_time = '';
        if (!$is_allowed_to_edit) {
            $time_limits = false;

            //This is an old LP (from a migration 1.8.7) so we do nothing
            if ((empty($details['created_on']) ||  $details['created_on'] == '0000-00-00 00:00:00') && (empty($details['modified_on']) || $details['modified_on'] == '0000-00-00 00:00:00')) {
                $time_limits = false;
            }

            //Checking if expired_on is ON
            if ($details['expired_on'] != '' && $details['expired_on'] != '0000-00-00 00:00:00') {
                $time_limits = true;
            }
            if ($time_limits) {
                // check if start time
                if (!empty($details['publicated_on']) && $details['publicated_on'] != '0000-00-00 00:00:00' &&
                    !empty($details['expired_on'])    && $details['expired_on'] != '0000-00-00 00:00:00') {
                    $start_time = api_strtotime($details['publicated_on'],'UTC');
                    $end_time   = api_strtotime($details['expired_on'],'UTC');
                    $now        = time();
                    $is_actived_time = false;

                    if ($now > $start_time && $end_time > $now ) {
                        $is_actived_time = true;
                    }

                    if (!$is_actived_time) {
                    	continue;
                    }
                }
            }
            $start_time =  $end_time = '';
        } else {
            if (!empty($details['publicated_on'])) {
                $start_time = api_convert_and_format_date($details['publicated_on'], DATE_TIME_FORMAT_LONG);
            }
            if (!empty($details['expired_on'])) {
                $end_time   = api_convert_and_format_date($details['expired_on'], DATE_TIME_FORMAT_LONG);
            }
        }

        $counter++;
        if (($counter % 2) == 0) { $oddclass = 'row_odd'; } else { $oddclass = 'row_even'; }

        $url_start_lp = 'lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$id;
        $name = Security::remove_XSS($details['lp_name']);
        if ($is_allowed_to_edit) {
            $url_start_lp .= '&isStudentView=true';
            $dsp_desc = '<em>'.$details['lp_maker'].'</em>   '.(learnpath::is_lp_visible_for_student($id, api_get_user_id())?'':' - ('.get_lang('LPNotVisibleToStudent').')');
            $extra = '<div class ="lp_content_type_label">'.$dsp_desc .'</div>';
        }

        /*$image = '<img src="../img/icons/22/learnpath.png" border="0" align="absmiddle" alt="' . $name . '">';
        <div style="float: left; width: 35px; height: 22px;"><a href="'.$url_start_lp.'">' .
                $image . '</a></div>*/
        $my_title = $name;
        if ($details['lp_visibility'] == 0 ) {
            $my_title = Display::tag('font', $name, array('style'=>'color:grey'));
        }

        $dsp_line =	'<tr align="center" class="'.$oddclass.'">'.
            		'<td align="left" valign="top">'.Display::return_icon('learnpath.png', get_lang('LPName'),'',ICON_SIZE_SMALL).'
                     <a href="'.$url_start_lp.'">' . $my_title . '</a>' . $session_img .$extra."</td>";

        $dsp_desc = '';
        $dsp_export = '';
        $dsp_edit = '';
        $dsp_build = '';
        $dsp_edit_close = '';
        $dsp_delete = '';
        $dsp_visible = '';
        $dsp_default_view = '';
        $dsp_debug = '';
        $dsp_order = '';

        $progress = learnpath::get_db_progress($id, api_get_user_id(), '%', '', false, api_get_session_id());

        if ($is_allowed_to_edit) {
            $dsp_progress = '<td>'.$progress.'</td>';
        } else {
            $dsp_progress = '<td>'.learnpath::get_progress_bar('%',learnpath::get_db_progress($id, api_get_user_id(), '%', '', false, api_get_session_id())).'</td>';
        }

        $dsp_edit = '<td class="td_actions">';
        $dsp_edit_close = '</td>';

        if ($is_allowed_to_edit) {

            /*
              if ($current_session == $details['lp_session']) {
                    $dsp_desc = '<td valign="middle" style="color: grey; padding-top:1em;"><em>'.$details['lp_maker'].'</em>  &nbsp;&nbsp; '.$details['lp_proximity'].' &nbsp;&nbsp; '.$details['lp_encoding'].'<a href="lp_controller.php?'.api_get_cidreq().'&action=edit&lp_id='.$id.'">&nbsp;&nbsp;<img src="../img/edit.gif" border="0" title="'.get_lang('LearnpathEditLearnpath').'"></a></td>'."\n";
            } else {
                $dsp_desc = '<td valign="middle" style="color: grey; padding-top:1em;"><em>'.$details['lp_maker'].'</em>  &nbsp;&nbsp; '.$details['lp_proximity'].' &nbsp;&nbsp; '.$details['lp_encoding'].'<img src="../img/edit_na.gif" border="0" title="'.get_lang('LearnpathEditLearnpath').'"></td>'."	";
            }
            */

            /* // Deprecated code, Chamilo 1.8.8.
            $dsp_desc = '<td valign="middle" style="color: grey; padding-top:1em;"><em>'.$details['lp_maker'].'</em>  &nbsp;&nbsp; '.$details['lp_proximity'].' &nbsp;&nbsp; '.$details['lp_encoding'].'</td>'."\n";
            */

            //$dsp_desc = '<td valign="middle" style="color: grey; padding-top:1em;"><em>'.$details['lp_maker'].'</em>  &nbsp;&nbsp; '.$details['lp_proximity'].'<br />'.(learnpath::is_lp_visible_for_student($id,api_get_user_id())?'':'('.get_lang('LPNotVisibleToStudent').')').'</td>'."\n";

            /* Export */

            // Export is inside "Edit"
            // export not available for normal lps yet
            /*if ($details['lp_type'] == 1) {
                $dsp_export = '<td align="center">' .
                    "<a href='".api_get_self()."?".api_get_cidreq()."&action=export&lp_id=$id'>" .
                    "<img src=\"../img/cd.gif\" border=\"0\" title=\"".get_lang('Export')."\">" .
                    "</a>";
            } elseif ($details['lp_type'] == 2) {
                $dsp_export = '<td align="center">' .
                    "<a href='".api_get_self()."?".api_get_cidreq()."&action=export&lp_id=$id&export_name=".replace_dangerous_char($name,'strict').".zip'>" .
                    "<img src=\"../img/cd.gif\" border=\"0\" title=\"".get_lang('Export')."\">" .
                    "</a>";
            } else {
                $dsp_export = '<td align="center">' .
                    //"<a href='".api_get_self()."?".api_get_cidreq()."&action=export&lp_id=$id'>" .
                    "<img src=\"../img/cd_gray.gif\" border=\"0\" title=\"".get_lang('Export')."\">" .
                    //"</a>";
            }*/

            // EDIT LP
            if ($current_session == $details['lp_session']) {
                $dsp_edit_lp = '<a href="lp_controller.php?'.api_get_cidreq().'&action=edit&lp_id='.$id.'">'.Display::return_icon('settings.png', get_lang('CourseSettings'),'',ICON_SIZE_SMALL).'</a>';
            } else {
                $dsp_edit_lp = Display::return_icon('settings_na.png', get_lang('CourseSettings'),'',ICON_SIZE_SMALL);
            }

            // BUILD
            if ($current_session == $details['lp_session']) {
                if ($details['lp_type'] == 1 || $details['lp_type'] == 2) {
                    $dsp_build = '<a href="lp_controller.php?'.api_get_cidreq().'&amp;action=add_item&amp;type=step&amp;lp_id='.$id.'">'.Display::return_icon('edit.png', get_lang('LearnpathEditLearnpath'),'',ICON_SIZE_SMALL).'</a>';
                } else {
                    $dsp_build = Display::return_icon('edit_na.png', get_lang('LearnpathEditLearnpath'),'',ICON_SIZE_SMALL);
                }
            } else {
                $dsp_build = Display::return_icon('edit_na.png', get_lang('LearnpathEditLearnpath'),'',ICON_SIZE_SMALL);
            }

            /* VISIBILITY COMMAND */

            // Session test not necessary if we want to show base course learning paths inside the session (see http://support.chamilo.org/projects/chamilo-18/wiki/Tools_and_sessions).
            //if ($current_session == $details['lp_session']) {
            if ($details['lp_visibility'] == 0) {
                $dsp_visible =	"<a href=\"".api_get_self()."?".api_get_cidreq()."&lp_id=$id&action=toggle_visible&new_status=1\">".Display::return_icon('invisible.png', get_lang('Show'),'',ICON_SIZE_SMALL)."</a>";
            } else {
                $dsp_visible =	"<a href='".api_get_self()."?".api_get_cidreq()."&lp_id=$id&action=toggle_visible&new_status=0'>" .Display::return_icon('visible.png', get_lang('Hide'),'',ICON_SIZE_SMALL)."</a>";
            }
            //} else {
            //	$dsp_visible = '<img src="../img/invisible.gif" border="0" title="'.get_lang('Show').'" />';
            //}

            /* PUBLISH COMMAND */
            if ($current_session == $details['lp_session']) {
                if ($details['lp_published'] == "i") {
                    $dsp_publish =	"<a href=\"".api_get_self()."?".api_get_cidreq()."&lp_id=$id&action=toggle_publish&new_status=v\">" .
                    Display::return_icon('lp_publish_na.png', get_lang('LearnpathPublish'),'',ICON_SIZE_SMALL)."</a>";

                } else {
                    $dsp_publish =	"<a href='".api_get_self()."?".api_get_cidreq()."&lp_id=$id&action=toggle_publish&new_status=i'>" .Display::return_icon('lp_publish.png', get_lang('LearnpathDoNotPublish'),'',ICON_SIZE_SMALL)."</a>";
                }
            } else {
                $dsp_publish = Display::return_icon('lp_publish_na.png', get_lang('LearnpathDoNotPublish'),'',ICON_SIZE_SMALL);
            }

      /*  MULTIPLE ATTEMPTS OR SERIOUS GAME MODE

        SERIOUSGAME MODE is a special mode where :
           * If a user exits the learning path before finishing it, he comes back where he left next time he tries
           * When lp status is completed, user can still modify the attempt (adds/time change score, and browse it)
           * It is thus a mix betwenn multiple attempt and mono attempt
      */
            if ($current_session == $details['lp_session']) {
              if ($details['seriousgame_mode'] == 1 && $details['lp_prevent_reinit'] == 1) { //seriousgame mode | next = single
                $dsp_reinit = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_attempt_mode&lp_id='.$id.'">' .
                    Display::return_icon('reload.png', get_lang('PreventMultipleAttempts'),'',ICON_SIZE_SMALL).
                  '</a>';
              }
              if ($details['seriousgame_mode'] == 0 && $details['lp_prevent_reinit'] == 1) { //single mode | next = multiple
                $dsp_reinit = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_attempt_mode&lp_id='.$id.'">' .
                   Display::return_icon('reload_na.png', get_lang('AllowMultipleAttempts'),'',ICON_SIZE_SMALL).
                  '</a>';
              }
              if ($details['seriousgame_mode'] == 0 && $details['lp_prevent_reinit'] == 0) { //multiple mode | next = seriousgame
                $dsp_reinit = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_attempt_mode&lp_id='.$id.'">' .Display::return_icon('reload.png', get_lang('AllowMultipleAttempts'),'',ICON_SIZE_SMALL).
                  '</a>';
              }
            } else {
                $dsp_reinit = Display::return_icon('reload_na.png', get_lang('AllowMultipleAttempts'),'',ICON_SIZE_SMALL);
            }

            /* FUll screen VIEW */
            if ($current_session == $details['lp_session']) {

                /* Default view mode settings (fullscreen/embedded) */
                if ($details['lp_view_mode'] == 'fullscreen') {
                    $dsp_default_view = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_view_mode&lp_id='.$id.'">' .
                        Display::return_icon('view_fullscreen.png', get_lang('ViewModeFullScreen'),'',ICON_SIZE_SMALL).'</a>';
                } elseif ($details['lp_view_mode'] == 'embedded') {
                    $dsp_default_view = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_view_mode&lp_id='.$id.'">' .
                         Display::return_icon('view_left_right.png', get_lang('ViewModeEmbedded'),'',ICON_SIZE_SMALL).'</a>';
                } elseif ($details['lp_view_mode'] == 'embedframe') {
                    $dsp_default_view = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_view_mode&lp_id='.$id.'">' .
                        Display::return_icon('view_nofullscreen.png', get_lang('ViewModeEmbedFrame'),'',ICON_SIZE_SMALL).'</a>';
                }
            } else {
                if ($details['lp_view_mode'] == 'fullscreen'){
                    $dsp_default_view = Display::return_icon('view_fullscreen_na.png', get_lang('ViewModeEmbedded'),'',ICON_SIZE_SMALL);
				}
				else{
                    $dsp_default_view = Display::return_icon('view_left_right_na.png', get_lang('ViewModeEmbedded'),'',ICON_SIZE_SMALL);
				}
            }

            /* Increase SCORM recording */
            /*
            if ($details['lp_force_commit'] == 1) {
                $dsp_force_commit = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_force_commit&lp_id='.$id.'">' .
                    '<img src="../img/clock.gif" border="0" alt="Normal SCORM recordings" title="'.get_lang('MakeScormRecordingNormal').'"/>' .
                    '</a>&nbsp;';
            }else{
                $dsp_force_commit = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_force_commit&lp_id='.$id.'">' .
                    '<img src="../img/clock_gray.gif" border="0" alt="Extra SCORM recordings" title="'.get_lang('MakeScormRecordingExtra').'"/>' .
                    '</a>&nbsp;';
            }
            */

            /*  DEBUG  */

            if ($test_mode == 'test' or api_is_platform_admin()) {
                if ($details['lp_scorm_debug'] == 1) {
                    $dsp_debug = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_scorm_debug&lp_id='.$id.'">' .
                        Display::return_icon('bug.png', get_lang('HideDebug'),'',ICON_SIZE_SMALL).'</a>';
                }else{
                    $dsp_debug = '<a href="lp_controller.php?'.api_get_cidreq().'&action=switch_scorm_debug&lp_id='.$id.'">' .
					Display::return_icon('bug_na.png', get_lang('ShowDebug'),'',ICON_SIZE_SMALL).'</a>';
                }
             }


             /* Export */

            if ($details['lp_type'] == 1) {
                $dsp_disk = Display::url(Display::return_icon('cd.gif', get_lang('Export'), array(), ICON_SIZE_SMALL), api_get_self()."?".api_get_cidreq()."&action=export&lp_id=$id");

            } elseif ($details['lp_type'] == 2) {
                $dsp_disk = Display::url(Display::return_icon('cd.gif', get_lang('Export'), array(), ICON_SIZE_SMALL), api_get_self()."?".api_get_cidreq()."&action=export&lp_id=$id&export_name=".replace_dangerous_char($name, 'strict').".zip");
            } else {
                $dsp_disk = Display::return_icon('cd_gray.gif', get_lang('Export'), array(), ICON_SIZE_SMALL);
            }

            //Copy
            $copy = Display::url(Display::return_icon('cd_copy.png', get_lang('Copy'), array(), ICON_SIZE_SMALL), api_get_self()."?".api_get_cidreq()."&action=copy&lp_id=$id");

            /* Auto Lunch LP code*/
            $lp_auto_lunch_icon = '';
            if (api_get_course_setting('enable_lp_auto_launch') == 1) {
                if ($details['autolaunch'] == 1 && $autolunch_exists == false) {
                    $autolunch_exists = true;
                    $lp_auto_lunch_icon = '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=auto_launch&status=0&lp_id='.$id.'">
                        <img src="../img/launch.png" border="0" title="'.get_lang('DisableLPAutoLaunch').'" /></a>';
                } else {
                    $lp_auto_lunch_icon = '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=auto_launch&status=1&lp_id='.$id.'">
                        <img src="../img/launch_na.png" border="0" title="'.get_lang('EnableLPAutoLaunch').'" /></a>';
                }
            }

            //if (api_get_setting('pdf_export_watermark_enable') == 'true') {
            	  $export_icon = ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=export_to_pdf&lp_id='.$id.'">
				  '.Display::return_icon('pdf.png', get_lang('ExportToPDFOnlyHTMLAndImages'),'',ICON_SIZE_SMALL).'</a>';
            //}

            /* DELETE COMMAND */

            if ($current_session == $details['lp_session']) {
                $dsp_delete = "<a href=\"lp_controller.php?".api_get_cidreq()."&action=delete&lp_id=$id\" " .
                "onclick=\"javascript: return confirmation('".addslashes($name)."');\">" .
				Display::return_icon('delete.png', get_lang('LearnpathDeleteLearnpath'),'',ICON_SIZE_SMALL).'</a>';
            } else {
                $dsp_delete = Display::return_icon('delete_na.png', get_lang('LearnpathDeleteLearnpath'),'',ICON_SIZE_SMALL);
            }


            /* COLUMN ORDER	 */

            // Only active while session mode is not active

            if ($current_session == 0) {
                if ($details['lp_display_order'] == 1 && $max != 1) {
                    $dsp_order .= '<a href="lp_controller.php?'.api_get_cidreq().'&action=move_lp_down&lp_id='.$id.'">
                         '.Display::return_icon('down.png', get_lang('MoveDown'),'',ICON_SIZE_SMALL).'</a>
						<img src="../img/blanco.png" border="0" alt="" title="" />';
                } elseif ($current == $max-1 && $max != 1) {
                    $dsp_order .= '<img src="../img/blanco.png" border="0" alt="" title="" /><a href="lp_controller.php?'.api_get_cidreq().'&action=move_lp_up&lp_id='.$id.'">
					'.Display::return_icon('up.png', get_lang('MoveUp'),'',ICON_SIZE_SMALL).'</a>';
                } elseif ($max == 1) {
                    $dsp_order = '';
                } else {
                    $dsp_order .= '<a href="lp_controller.php?'.api_get_cidreq().'&action=move_lp_down&lp_id='.$id.'">' .
                        Display::return_icon('down.png', get_lang('MoveDown'),'',ICON_SIZE_SMALL).'</a>';
                    $dsp_order .= '<a href="lp_controller.php?'.api_get_cidreq().'&action=move_lp_up&lp_id='.$id.'">' .
                        Display::return_icon('up.png', get_lang('MoveUp'),'',ICON_SIZE_SMALL).'</a>';
                }
            }
            if ($is_allowed_to_edit) {
                $start_time = Display::tag('td', Display::div($start_time, array('class'=>'small')));
                $end_time   = Display::tag('td', Display::div($end_time,   array('class'=>'small')));
            } else {
                $start_time  = $end_time= '';
            }
        } else { // end if ($is_allowedToEdit)
            //Student
            $export_icon = ' <a href="'.api_get_self().'?'.api_get_cidreq().'&action=export_to_pdf&lp_id='.$id.'">'.Display::return_icon('pdf.png', get_lang('ExportToPDF'),'',ICON_SIZE_SMALL).'</a>';
        }

        echo $dsp_line.$start_time.$end_time.$dsp_progress.$dsp_desc.$dsp_export.$dsp_edit.$dsp_build.$dsp_edit_lp.$dsp_visible.$dsp_publish.$dsp_reinit.
             $dsp_default_view.$dsp_debug.$dsp_disk.$copy.$lp_auto_lunch_icon.$export_icon.$dsp_delete.$dsp_order.$dsp_edit_close;

        echo "</tr>";
        $current ++; //counter for number of elements treated
    } // end foreach ($flat_list)
    // TODO: Erint some user-friendly message if counter is still = 0 to tell nothing can be display yet.
    echo "</table>";
} else {
    if ($is_allowed_to_edit) {
        echo '<div id="no-data-view">';
        echo '<h2>'.get_lang('LearningPaths').'</h2>';
        echo Display::return_icon('scorms.png', '', array(), 64);
        echo '<div class="controls">';
        echo Display::url(get_lang('LearnpathAddLearnpath'), api_get_self().'?'.api_get_cidreq().'&action=add_lp' , array('class' => 'btn'));
        echo '</div>';
        echo '</div>';
    }
}
$course_info = api_get_course_info();
learnpath::generate_learning_path_folder($course_info);

//Deleting the objects
Session::erase('oLP');
Session::erase('lpobject');
Display::display_footer();lp_list_search.css000064400000000053152003363470010252 0ustar00/* css styles moved to search_widget.css */lp_list_search.php000064400000014766152003363470010271 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Script to draw the results from a query
 * @package chamilo.learnpath
 * @author Diego Escalante Urrelo <diegoe@gmail.com>
 * @author Marco Antonio Villegas Vega <marvil07@gmail.com>
 * @author Julio Montoya <gugli100@gmail.com> Lots of bug fixing
 * 
 */
/**
 * Code 
 */
require api_get_path(LIBRARY_PATH).'search/search_widget.php';
require api_get_path(LIBRARY_PATH).'search/ChamiloQuery.php';
require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';

$htmlHeadXtra[] = '<link rel="stylesheet" type="text/css" href="'. api_get_path(WEB_PATH) .'main/newscorm/lp_list_search.css" />';
event_access_tool(TOOL_SEARCH);

if (isset($_SESSION['gradebook'])){
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[]= array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => './index.php', 'name' => get_lang(ucfirst(TOOL_SEARCH)));
search_widget_prepare($htmlHeadXtra);
Display::display_header(null, 'Path');

if (api_get_setting('search_enabled') !== 'true') {
    Display::display_error_message(get_lang('SearchFeatureNotEnabledComment'));
} else {
    if (!empty($_GET['action'])) {
        search_widget_show($_GET['action']);
    } else {
        search_widget_show();
    }
}

// Initialize.
$op = 'or';
if (!empty($_REQUEST['operator']) && in_array($op, array('or', 'and'))) {
    $op = $_REQUEST['operator'];
}

$query = stripslashes(htmlspecialchars_decode($_REQUEST['query'], ENT_QUOTES));

$mode = 'default';
if (in_array($_GET['mode'], array('gallery', 'default'))) {
    $mode = $_GET['mode'];
}

$term_array = array();
$specific_fields = get_specific_field_list();
foreach ($specific_fields as $specific_field) {
    if (!empty($_REQUEST[ 'sf_'. $specific_field['code'] ])) {
        $values = $_REQUEST[ 'sf_'. $specific_field['code'] ];
        if (in_array('__all__', $values)) {
            $sf_terms_for_code = xapian_get_all_terms(1000, $specific_field['code']);
            foreach ($sf_terms_for_code as $term) {
                if (!empty($term)) {
                    $term_array[] = chamilo_get_boolean_query($term['name']); // Here name includes prefix.
                }
            }
        } else {
            foreach ($values as $term) {
                if (!empty($term)) {
                    $prefix = $specific_field['code'];
                    $term_array[] = chamilo_get_boolean_query($prefix . $term);
                }
            }
        }
    } else {
        $sf_terms_for_code = xapian_get_all_terms(1000, $specific_field['code']);        
        foreach ($sf_terms_for_code as $term) {
            if (!empty($term)) {
                $term_array[] = chamilo_get_boolean_query($term['name']); // Here name includes prefix.
            }
        }
    }
}

// Get right group of terms to show on multiple select.
$fixed_queries = array();
$course_filter = NULL;
if ( ($cid=api_get_course_id()) != -1 ) {    
    // Results only from actual course.
    $course_filter = chamilo_get_boolean_query(XAPIAN_PREFIX_COURSEID . $cid);
}

if (count($term_array)) {    
    $fixed_queries = chamilo_join_queries($term_array, null, $op);
    
    if ($course_filter != NULL) {
        $fixed_queries = chamilo_join_queries($fixed_queries, $course_filter, 'and');
    }
} else {
    if (!empty($query)) {        
        $fixed_queries = array($course_filter);
    }
}

//var_dump($fixed_queries);

list($count, $results) = chamilo_query_query(api_convert_encoding($query, 'UTF-8', $charset), 0, 1000, $fixed_queries);

// Prepare blocks to show.
$blocks = array();

if ($count > 0) {
    foreach ($results as $result) {
        // Fill the result array.
        if (empty($result['thumbnail'])) {
            $result['thumbnail'] = '../img/no_document_thumb.jpg';
        }

        if (!empty($result['url'])) {
            $a_prefix = '<a href="'.$result['url'].'">';
            $a_sufix = '</a>';
        } else {
            $a_prefix = '';
            $a_sufix = '';
        }

        if ($mode == 'gallery') {
            $title = $a_prefix.str_replace('_',' ',$result['title']). $a_sufix;
            $blocks[] = array(
                $a_prefix .'<img src="'.$result['thumbnail'].'" />'. $a_sufix .'<br />'.$title.'<br />'.$result['author'],
            );
        } else {
            $title = '<div style="text-align:left;">'. $a_prefix . $result['title']. $a_sufix .(!empty($result['author']) ? ' '.$result['author'] : '').'<div>';
            $blocks[] = array($title);
        }
    }
}

// Show results.
if (count($blocks) > 0) {
    $s = new SortableTableFromArray($blocks);
    $s->display_mode = $mode; // default
    $s->display_mode_params = 3;
    $s->per_page = 9;
    $additional_parameters = array (
        'mode' => $mode,
        'action' => 'search',
        'query' => Security::remove_XSS($_REQUEST['query']),
    );
    $get_params = '';
    foreach ($specific_fields as $specific_field) {
        if (isset($_REQUEST[ 'sf_'. $specific_field['code'] ])) {
            $values = $_REQUEST[ 'sf_'. $specific_field['code'] ];
            //Sortable additional_parameters doesn't accept multi dimensional arrays
            //$additional_parameters[ 'sf_'. $specific_field['code'] ] = $values;
            foreach ( $values as $value ) {
                $get_params .= '&sf_' . $specific_field['code'] .'[]='. $value;
            }
            $get_params .= '&';
        }
    }
    $additional_parameters['operator'] = $op;
    
    $s->additional_parameters = $additional_parameters;

    if ($mode == 'default') {
        $s->set_header(0, get_lang(ucfirst(TOOL_SEARCH)), false);
    }

    $search_link = '<a href="%ssearch/index.php?mode=%s&action=search&query=%s%s">';

    $mode_selector = '<div id="mode-selector">';
    $mode_selector .= sprintf($search_link, api_get_path(WEB_CODE_PATH), 'gallery', $_REQUEST['query'], $get_params);
    $mode_selector .= '<img src="../img/'. (($mode=='gallery')?'ButtonGallOn':'ButtonGallOff') .'.png" /></a>';
    $mode_selector .= sprintf($search_link, api_get_path(WEB_CODE_PATH), 'default', $_REQUEST['query'], $get_params);
    $mode_selector .= '<img src="../img/'.(($mode=='default')?'ButtonListOn':'ButtonListOff').'.png" /></a>';
    $mode_selector .= '</div>';

    echo '<div id="search-results-container">';
    echo $mode_selector;
    $s->display();
    echo '</div>';
}

Display::display_footer();
lp_move_item.php000064400000012763152003363470007750 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
* This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
*
* @author Patrick Cool
* @author Denes Nagy
* @author Roan Embrechts, refactoring and code cleaning
* @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
* @package chamilo.learnpath
*/
/**
 * Code
 */
/* INIT SECTION */

$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

// The main_api.lib.php, database.lib.php and display.lib.php
// libraries are included by default.

include 'learnpath_functions.inc.php';
//include '../resourcelinker/resourcelinker.inc.php';
include 'resourcelinker.inc.php';
// Rewrite the language file, sadly overwritten by resourcelinker.inc.php.
// Name of the language file that needs to be included.
$language_file = 'learnpath';

/* Header and action code */
$htmlHeadXtra[] = '<script type="text/javascript">'.
$_SESSION['oLP']->get_js_dropdown_array() .
'function load_cbo(id){' ."\n" .
  'if (!id) {return false;}'.
  'var cbo = document.getElementById(\'previous\');' .
  'for(var i = cbo.length - 1; i > 0; i--) {' .
    'cbo.options[i] = null;' .
 '}' ."\n" .
  'var k=0;' .
  'for(var i = 1; i <= child_name[id].length; i++){' ."\n" .
  '  cbo.options[i] = new Option(child_name[id][i-1], child_value[id][i-1]);' ."\n" .
  '  k=i;' ."\n" .
  '}' ."\n" .
  'cbo.options[k].selected = true;'."\n" .
'}'."\n" .
'$().ready(function() {'."\n" .
  'if ($(\'#previous\')) {'."\n" .
    'if(\'parent is\'+$(\'#idParent\').val()) {'.
      'load_cbo($(\'#idParent\').val());'."\n" .
  '}}'."\n" .
'});</script>'."\n" ;

/* Constants and variables */

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp      = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
$tbl_lp_view = Database::get_course_table(TABLE_LP_VIEW);

$isStudentView  = (int) $_REQUEST['isStudentView'];
$learnpath_id   = (int) $_REQUEST['lp_id'];
$submit			= $_POST['submit_button'];
/*
$chapter_id     = $_GET['chapter_id'];
$title          = $_POST['title'];
$description   = $_POST['description'];
$Submititem     = $_POST['Submititem'];
$action         = $_REQUEST['action'];
$id             = (int) $_REQUEST['id'];
$type           = $_REQUEST['type'];
$direction      = $_REQUEST['direction'];
$moduleid       = $_REQUEST['moduleid'];
$prereq         = $_REQUEST['prereq'];
$type           = $_REQUEST['type'];
*/

/* MAIN CODE */

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script language='JavaScript' type='text/javascript'> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_add_item.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
}
// From here on, we are admin because of the previous condition, so don't check anymore.

$course_id = api_get_course_int_id();
$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";

$result = Database::query($sql_query);
$therow = Database::fetch_array($result);

//$admin_output = '';
/*
    Course admin section
    - all the functions not available for students - always available in this case (page only shown to admin)
*/
/* SHOWING THE ADMIN TOOLS */

if (isset($_SESSION['gradebook'])){
    $gradebook=	$_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", 'name' => stripslashes("{$therow['name']}"));
$interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

// Theme calls
$show_learn_path = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

Display::display_header(get_lang('Move'), 'Path');
//api_display_tool_title($therow['name']);

$suredel = trim(get_lang('AreYouSureToDelete'));
//$suredelstep = trim(get_lang('AreYouSureToDeleteSteps'));
?>
<script type='text/javascript'>
/* <![CDATA[ */
function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}
function confirmation(name)
{
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?"))
    {
        return true;
    }
    else
    {
        return false;
    }
}
</script>
<?php

//echo $admin_output;

/* DISPLAY SECTION */

echo $_SESSION['oLP']->build_action_menu();

echo '<div class="row-fluid">';
echo '<div class="span3">';
    echo $_SESSION['oLP']->return_new_tree();
echo '</div>';

echo '<div class="span9">';

if (isset($is_success) && $is_success === true) {
    $msg = '<div class="lp_message" style="margin-bottom:10px;">';
        $msg .= 'The item has been moved.';
    $msg .= '</div>';
    echo $_SESSION['oLP']->display_item($_GET['id'], $msg);
} else {
    echo $_SESSION['oLP']->display_move_item($_GET['id']);
}
echo '</div>';
echo '</div>';

/* FOOTER */

Display::display_footer();
lp_nav.php000064400000004047152003363470006544 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Script opened in an iframe and containing the learning path's navigation and progress bar
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

// Language files that needs to be included.
$language_file[] = 'scormdocument';
$language_file[] = 'scorm';
$language_file[] = 'learnpath';

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'scorm.class.php';
require_once 'aicc.class.php';

$htmlHeadXtra[] = '<script>
      var chamilo_xajax_handler = window.parent.oxajax;
</script>';

$progress_bar = '';
$navigation_bar = '';
$display_mode = '';
$autostart = 'true';

if (isset($_SESSION['lpobject'])) {
    //if($debug>0) //error_log('New LP - in lp_nav.php - SESSION[lpobject] is defined',0);
    $oLP = unserialize($_SESSION['lpobject']);
    if (is_object($oLP)) {
        $_SESSION['oLP'] = $oLP;
    } else {
        //error_log('New LP - in lp_nav.php - SESSION[lpobject] is not object - dying',0);
        die('Could not instanciate lp object');
    }
    $display_mode = $_SESSION['oLP']->mode;
    $scorm_css_header = true;
    $lp_theme_css = $_SESSION['oLP']->get_theme();

    $my_style = api_get_visual_theme();

    //Setting up the CSS theme if exists

    $mycourselptheme = null;
    if (api_get_setting('allow_course_theme') == 'true') {
    	$mycourselptheme = api_get_course_setting('allow_learning_path_theme');
    }

    if (!empty($lp_theme_css) && !empty($mycourselptheme) && $mycourselptheme != -1 && $mycourselptheme == 1) {
        global $lp_theme_css;
    } else {
        $lp_theme_css = $my_style;
    }

    $progress_bar 	= $_SESSION['oLP']->get_progress_bar('', -1, '', true);
    $navigation_bar = $_SESSION['oLP']->get_navigation_bar();
    $mediaplayer 	= $_SESSION['oLP']->get_mediaplayer($autostart);
}
session_write_close();
?>
<span><?php echo (!empty($mediaplayer)) ? $mediaplayer : '&nbsp;' ?></span>
lp_save.php000064400000001763152003363470006720 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Script that handles the saving of item status
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * Initialization is to be done by lp_controller.php.
 */

/**
 * Switching within the field to update
 */
$msg = $_SESSION['oLP']->get_message();

error_log('New LP - Loaded lp_save : '.$_SERVER['REQUEST_URI'].' from '.$_SERVER['HTTP_REFERER'], 0);
?>
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo api_get_language_isocode(); ?>" lang="<?php echo api_get_language_isocode(); ?>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo api_get_system_encoding(); ?>" />
<script language='javascript'>
<?php
if ($_SESSION['oLP']->mode != 'fullscreen'){
}
?>
</script>

</head>
<body dir="<?php echo api_get_text_direction(); ?>">
</body></html>
lp_stats.php000064400000135337152003363470007125 0ustar00<?php

/* For licensing terms, see /license.txt */
/**
 * This script displays statistics on the current learning path (scorm)
 * This script must be included by lp_controller.php to get basic initialisation
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 * @todo clean this file like the exercise files J.M
 */
/**
 * Code
 */
require_once 'learnpath.class.php';
require_once 'resourcelinker.inc.php';
require_once '../exercice/exercise.lib.php';

$course_code = api_get_course_id();

if (empty($user_id)) {
    $user_id = api_get_user_id();
}

// Declare variables to be used in lp_stats.php
//When checking the reporting myspace/lp_tracking.php
//isset($_GET['lp_id']) &&
if (isset($lp_id) && !empty($lp_id)) {
    $lp_id = intval($lp_id);
    if (!isset($list)) {
        $list = learnpath::get_flat_ordered_items_list($lp_id);
    }
} else {
    if (isset($_SESSION['oLP'])) {
        $lp_id = $_SESSION['oLP']->get_id();
        $list = learnpath::get_flat_ordered_items_list($lp_id);
    }
}

$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

if (isset($_GET['course'])) {
    $course_code = Security::remove_XSS($_GET['course']);
}

$course_info = api_get_course_info($course_code);
$course_id = $course_info['real_id'];

if (isset($_GET['student_id'])) {
    $student_id = intval($_GET['student_id']);
}
$session_id = api_get_session_id();
$session_condition = api_get_session_condition($session_id);

//When origin is not set that means that the lp_stats are viewed from the "man running" icon
if (!isset($origin)) {
    $origin = 'learnpath';
}

//Origin = tracking means that teachers see that info in the Reporting tool
if ($origin != 'tracking') {
    Display::display_reduced_header();
}

$output = '';

//Extend all button
$extend_all_link = '';
$extend_all = 0;
if ($origin == 'tracking') {
    $url_suffix = '&session_id=' . $session_id . '&course=' . Security::remove_XSS($_GET['course']) . '&student_id=' . $student_id . '&lp_id=' . Security::remove_XSS($_GET['lp_id']) . '&origin=' . Security::remove_XSS($_GET['origin']) . $from_link;
} else {
    $url_suffix = '&lp_id=' . $lp_id;
}
if (!empty($_GET['extend_all'])) {
    $extend_all_link = '<a href="' . api_get_self() . '?action=stats' . $url_suffix . '"><img src="../img/view_less_stats.gif" alt="fold_view" border="0" title="' . get_lang('HideAllAttempts') . '"></a>';
    $extend_all = 1;
} else {
    $extend_all_link = '<a href="' . api_get_self() . '?action=stats&extend_all=1' . $url_suffix . '"><img src="../img/view_more_stats.gif" alt="extend_view" border="0" title="' . get_lang('ShowAllAttempts') . '"></a>';
}

if ($origin != 'tracking') {
    $output .= Display::page_header(get_lang('ScormMystatus'));
}

$output .= '<table class="data_table">
            <tr>
                <th width="16">' . $extend_all_link . '</th>
                <th colspan="4">
                    ' . get_lang('ScormLessonTitle') .'
                </th>
                <th colspan="2">
                    ' . get_lang('ScormStatus') . '
                </th>
                <th colspan="2">
                    ' . get_lang('ScormScore') . '
                </th>
                <th colspan="2">
                    ' . get_lang('ScormTime') . '
                </th>
                <th>
                    ' . get_lang('Actions') . '
                </th>
           </tr>';

// 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.

$TBL_LP_ITEM = Database :: get_course_table(TABLE_LP_ITEM);
$TBL_LP_ITEM_VIEW = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
$TBL_LP_VIEW = Database :: get_course_table(TABLE_LP_VIEW);
$tbl_quiz_questions = Database :: get_course_table(TABLE_QUIZ_QUESTION);
$TBL_QUIZ = Database :: get_course_table(TABLE_QUIZ_TEST);
$tbl_stats_exercices = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
$tbl_stats_attempts = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);

$sql = "SELECT max(view_count) FROM $TBL_LP_VIEW WHERE c_id = $course_id AND lp_id = $lp_id AND user_id = '" . $user_id . "' $session_condition";
$res = Database::query($sql);
$view = '';
$num = 0;
if (Database :: num_rows($res) > 0) {
    $myrow = Database :: fetch_array($res);
    $view = $myrow[0];
}

$counter = 0;
$total_score = 0;
$total_time = 0;
$h = get_lang('h');

if (!empty($export_csv)) {
    $csv_content[] = array(
        get_lang('ScormLessonTitle'),
        get_lang('ScormStatus'),
        get_lang('ScormScore'),
        get_lang('ScormTime')
    );
}

// Get attempts of a exercise.
if (isset($_GET['lp_id']) && isset($_GET['lp_item_id'])) {
    $clean_lp_item_id = Database::escape_string($_GET['lp_item_id']);
    $clean_lp_id = Database::escape_string($_GET['lp_id']);
    $clean_course_code = Database :: escape_string($course_code);
    $sql_path = "SELECT path FROM $TBL_LP_ITEM WHERE c_id = $course_id AND id = '$clean_lp_item_id' AND lp_id = '$clean_lp_id'";
    $res_path = Database::query($sql_path);
    $row_path = Database::fetch_array($res_path);

    if (Database::num_rows($res_path) > 0) {
        if ($origin != 'tracking') {
            $sql_attempts = 'SELECT * FROM ' . $tbl_stats_exercices . '
            				 WHERE  exe_exo_id="' . (int) $row_path['path'] . '" AND
                                    status <> "incomplete"  AND
                                    exe_user_id="' . api_get_user_id() . '" AND
                                    orig_lp_id = "' . (int) $clean_lp_id . '" AND
                                    orig_lp_item_id = "' . (int) $clean_lp_item_id . '" AND
                                    exe_cours_id="' . $clean_course_code . '"  AND
                                    session_id = ' . $session_id . '
                             ORDER BY exe_date';
        } else {
            $sql_attempts = 'SELECT * FROM ' . $tbl_stats_exercices . '
            				 WHERE  exe_exo_id="' . (int) $row_path['path'] . '" AND
                                    status <> "incomplete"  AND
                                    exe_user_id="' . $student_id . '" AND
                                    orig_lp_id = "' . (int) $clean_lp_id . '" AND
                                    orig_lp_item_id = "' . (int) $clean_lp_item_id . '" AND
                                    exe_cours_id="' . $clean_course_code . '"  AND
                                    session_id = ' . $session_id . '
                             ORDER BY exe_date';
        }
    }
    //var_dump($sql_attempts);
}

//Show lp items
if (is_array($list) && count($list) > 0) {
    foreach ($list as $my_item_id) {
        $extend_this = 0;
        $qry_order = 'DESC';
        if ((!empty($_GET['extend_id']) && $_GET['extend_id'] == $my_item_id) || $extend_all) {
            $extend_this = 1;
            $qry_order = 'ASC';
        }

        // Prepare statement to go through each attempt.
        if (!empty($view)) {
            $sql = "SELECT  iv.status as mystatus,
                            v.view_count as mycount,
                            iv.score as myscore,
                            iv.total_time as mytime,
                            i.id as myid,
                            i.lp_id as mylpid,
                            iv.lp_view_id as mylpviewid,
                            i.title as mytitle,
                            i.max_score as mymaxscore,
                            iv.max_score as myviewmaxscore,
                            i.item_type as item_type,
                            iv.view_count as iv_view_count,
                            iv.id as iv_id,
                            path
                    FROM $TBL_LP_ITEM as i
                    INNER JOIN $TBL_LP_ITEM_VIEW as iv ON (i.id = iv.lp_item_id  AND i.c_id = $course_id AND iv.c_id = $course_id)
                    INNER JOIN $TBL_LP_VIEW as v ON (iv.lp_view_id = v.id AND v.c_id = $course_id)
            WHERE
                i.id = $my_item_id AND
                i.lp_id = $lp_id  AND
                v.user_id = $user_id AND
                v.view_count = $view AND
                v.session_id = $session_id
            ORDER BY iv.view_count $qry_order ";
            //var_dump($sql);
        } else {
            $sql = "SELECT  iv.status as mystatus,
                            v.view_count as mycount,
                            iv.score as myscore,
                            iv.total_time as mytime,
                            i.id as myid,
                            i.lp_id as mylpid,
                            iv.lp_view_id as mylpviewid,
                            i.title as mytitle,
                            i.max_score as mymaxscore,
                            iv.max_score as myviewmaxscore,
                            i.item_type as item_type,
                            iv.view_count as iv_view_count,
                            iv.id as iv_id,
                            path
                    FROM $TBL_LP_ITEM as i
                    INNER JOIN $TBL_LP_ITEM_VIEW as iv ON (i.id = iv.lp_item_id  AND i.c_id = $course_id AND iv.c_id = $course_id)
                    INNER JOIN $TBL_LP_VIEW as v ON (iv.lp_view_id = v.id AND v.c_id = $course_id)
                    WHERE
                        i.id = $my_item_id AND
                        i.lp_id = $lp_id AND
                        v.user_id = $user_id AND
                        v.session_id = $session_id
                   ORDER BY iv.view_count $qry_order ";
        }
        $result = Database::query($sql);
        $num = Database :: num_rows($result);
        $time_for_total = 'NaN';

        //Extend all + extend scorm?
        if (($extend_this || $extend_all) && $num > 0) {
            $row = Database :: fetch_array($result);
            $result_disabled_ext_all = false;
            if ($row['item_type'] == 'quiz') {
                // Check results_disabled in quiz table.
                $my_path = Database::escape_string($row['path']);

                $sql = "SELECT results_disabled FROM $TBL_QUIZ WHERE c_id = $course_id AND id ='" . $my_path . "'";
                $res_result_disabled = Database::query($sql);
                $row_result_disabled = Database::fetch_row($res_result_disabled);

                if (Database::num_rows($res_result_disabled) > 0 && (int) $row_result_disabled[0] === 1) {
                    $result_disabled_ext_all = true;
                }
            }

            // If there are several attempts, and the link to extend has been clicked, show each attempt...
            if (($counter % 2) == 0) {
                $oddclass = 'row_odd';
            } else {
                $oddclass = 'row_even';
            }
            $extend_link = '';
            if (!empty($inter_num)) {
                $extend_link = '<a href="' . api_get_self() . '?action=stats&fold_id=' . $my_item_id . $url_suffix . '">
                                <img src="../img/visible.gif" alt="' . get_lang('HideAttemptView') . '" title="' . get_lang('HideAttemptView') . '"  border="0"></a>';
            }
            $title = $row['mytitle'];

            if (empty($title)) {
                $title = rl_get_resource_name(api_get_course_id(), $lp_id, $row['myid']);
            }

            if ($row['item_type'] != 'dokeos_chapter') {
                $correct_test_link = '-';
                $title = Security::remove_XSS($title);
                $output .= '<tr class="'.$oddclass.'">
                                <td>'.$extend_link.'</td>
                                <td colspan="4">
                                    '.$title.'
                                </td>
                                <td colspan="2"></td>
                                <td colspan="2"></td>
                                <td colspan="2"></td>
                                <td></td>
                            </tr>';
            }
            $counter++;

            do {
                // Check if there are interactions below.
                $extend_attempt_link = '';
                $extend_this_attempt = 0;

                if ((learnpath :: get_interactions_count_from_db($row['iv_id'], $course_id) > 0 || learnpath :: get_objectives_count_from_db($row['iv_id'], $course_id) > 0) && !$extend_all) {
                    if (!empty($_GET['extend_attempt_id']) && $_GET['extend_attempt_id'] == $row['iv_id']) {
                        // The extend button for this attempt has been clicked.
                        $extend_this_attempt = 1;
                        $extend_attempt_link = '<a href="' . api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&fold_attempt_id=' . $row['iv_id'] . $url_suffix . '"><img src="../img/visible.gif" alt="' . get_lang('HideAttemptView') . '" title="' . get_lang('HideAttemptView') . '" border="0"></a>';
                    } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
                        // The extend button for this attempt has not been clicked.
                        $extend_attempt_link = '<a href="' . api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix . '"><img src="../img/invisible.gif" alt="' . get_lang('ExtendAttemptView') . '" title="' . get_lang('ExtendAttemptView') . '"  border="0"></a>';
                    }
                }

                if (($counter % 2) == 0) {
                    $oddclass = 'row_odd';
                } else {
                    $oddclass = 'row_even';
                }

                $lesson_status = $row['mystatus'];
                $score = $row['myscore'];

                $time_for_total = $row['mytime'];

                $time = learnpathItem :: get_scorm_time('js', $row['mytime']);
                $scoIdentifier = $row['myid'];

                if ($score == 0) {
                    $maxscore = $row['mymaxscore'];
                } else {
                    if ($row['item_type'] == 'sco') {
                        if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
                            $maxscore = $row['myviewmaxscore'];
                        } elseif ($row['myviewmaxscore'] === '') {
                            $maxscore = 0;
                        } else {
                            $maxscore = $row['mymaxscore'];
                        }
                    } else {
                        $maxscore = $row['mymaxscore'];
                    }
                }

                // Remove "NaN" if any (@todo: locate the source of these NaN)
                $time = str_replace('NaN', '00' . $h . '00\'00"', $time);

                if ($row['item_type'] != 'dokeos_chapter') {
                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                        $view_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
                    } else {
                        switch ($row['item_type']) {
                            case 'sco':
                                if ($maxscore == 0) {
                                    $view_score = $score;
                                } else {
                                    $view_score = show_score($score, $maxscore, false);
                                }
                                break;
                            case 'document':
                                $view_score = ($score == 0 ? '/' : show_score($score, $maxscore, false));
                                break;
                            default:
                                $view_score = show_score($score, $maxscore, false);
                                break;
                        }
                    }
                    $output .= '<tr class="' . $oddclass . '">
                                    <td></td>
                                    <td>' . $extend_attempt_link . '</td>
                                    <td colspan="3">' . get_lang('Attempt') . ' ' . $row['iv_view_count'] . '</td>
                                    <td colspan="2">' . learnpathItem::humanize_status($lesson_status) . '</td>
                                    <td colspan="2">' . $view_score . '</td>
                                    <td colspan="2">' . $time . '</td>
                                    <td></td>
                                </tr>';

                    if (!empty($export_csv)) {
                        $temp = array();
                        $temp[] = $title = Security::remove_XSS($title);
                        $temp[] = Security::remove_XSS(learnpathItem::humanize_status($lesson_status, false));

                        if ($row['item_type'] == 'quiz') {
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                                $temp[] = '/';
                            } else {
                                $temp[] = ($score == 0 ? '0/' . $maxscore : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
                            }
                        } else {
                            $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
                        }
                        $temp[] = $time;
                        $csv_content[] = $temp;
                    }
                }

                $counter++;

                if ($extend_this_attempt OR $extend_all) {
                    $list1 = learnpath :: get_iv_interactions_array($row['iv_id']);

                    foreach ($list1 as $id => $interaction) {
                        if (($counter % 2) == 0) {
                            $oddclass = 'row_odd';
                        } else {
                            $oddclass = 'row_even';
                        }
                        $student_response = urldecode($interaction['student_response']); // Code added by Isaac Flores.
                        $content_student_response = array();
                        $content_student_response = explode('__|', $student_response);

                        if (count($content_student_response) > 0) {
                            if (count($content_student_response) >= 3) {
                                $new_content_student_response = array_pop($content_student_response); // Pop the element off the end of array.
                            }
                            $student_response = implode(',', $content_student_response);
                        }
                        $output .= '<tr class="'.$oddclass.'">
                                        <td></td>
                                        <td></td>
                                        <td></td>
                                        <td>'.$interaction['order_id'] . '</td>
                                        <td>'.$interaction['id'] . '</td>
                                        <td colspan="2">' . $interaction['type'].'</td>
                                        <td>'.$student_response . '</td>
                                        <td>'.$interaction['result'] . '</td>
                                        <td>'.$interaction['latency'] . '</td>
                                        <td>'.$interaction['time'] . '</td>
                                        <td></td>
                                    </tr>';
                        $counter++;
                    }
                    $list2 = learnpath :: get_iv_objectives_array($row['iv_id']);

                    foreach ($list2 as $id => $interaction) {
                        if (($counter % 2) == 0) {
                            $oddclass = 'row_odd';
                        } else {
                            $oddclass = 'row_even';
                        }
                        $output .= '<tr class="'.$oddclass.'">
                                        <td></td>
                                        <td></td>
                                        <td></td>
                                        <td>' . $interaction['order_id'] . '</td>
                                        <td colspan="2">' . $interaction['objective_id'] . '</td>
                                        <td colspan="2">' . $interaction['status'] .'</td>
                                        <td>' . $interaction['score_raw'] . '</td>
                                        <td>' . $interaction['score_max'] . '</td>
                                        <td>' . $interaction['score_min'] . '</td>
                                        <td></td>
                                     </tr>';
                        $counter++;
                    }
                }
            } while ($row = Database :: fetch_array($result));
        } elseif ($num > 0) {
            //Not extended

            $row = Database :: fetch_array($result, 'ASSOC');
            $my_id = $row['myid'];
            $my_lp_id = $row['mylpid'];
            $my_lp_view_id = $row['mylpviewid'];
            $my_path = $row['path'];

            $result_disabled_ext_all = false;

            if ($row['item_type'] == 'quiz') {
                // Check results_disabled in quiz table.
                $my_path = Database::escape_string($my_path);

                $sql = "SELECT results_disabled FROM $TBL_QUIZ WHERE c_id = $course_id AND id ='" . (int) $my_path . "'";
                $res_result_disabled = Database::query($sql);
                $row_result_disabled = Database::fetch_row($res_result_disabled);

                if (Database::num_rows($res_result_disabled) > 0 && (int) $row_result_disabled[0] === 1) {
                    $result_disabled_ext_all = true;
                }
            }

            // Check if there are interactions below
            $extend_this_attempt = 0;

            $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
            $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);

            $extend_attempt_link = '';
            if (($inter_num > 0 || $objec_num > 0)) {
                if (!empty($_GET['extend_attempt_id']) && $_GET['extend_attempt_id'] == $row['iv_id']) {
                    // The extend button for this attempt has been clicked.
                    $extend_this_attempt = 1;
                    $extend_attempt_link = '<a href="' . api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&fold_attempt_id=' . $row['iv_id'] . $url_suffix . '"><img src="../img/visible.gif" alt="' . get_lang('HideAttemptView') . '" title="' . get_lang('HideAttemptView') . '" border="0"></a>' . "\n";
                } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
                    // The extend button for this attempt has not been clicked.
                    $extend_attempt_link = '<a href="' . api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix . '"><img src="../img/invisible.gif" alt="' . get_lang('ExtendAttemptView') . '" title="' . get_lang('ExtendAttemptView') . '" border="0"></a>' . "\n";
                }
            }

            if (($counter % 2) == 0) {
                $oddclass = 'row_odd';
            } else {
                $oddclass = 'row_even';
            }

            $extend_link = '';
            if ($inter_num > 1) {
                $extend_link = '<a href="' . api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix . '"><img src="../img/invisible.gif" alt="' . get_lang('ExtendAttemptView') . '" title="' . get_lang('ExtendAttemptView') . '"  border="0"></a>';
            }

            $lesson_status = $row['mystatus'];
            $score = $row['myscore'];
            $subtotal_time = $row['mytime'];

            while ($tmp_row = Database :: fetch_array($result)) {
                $subtotal_time += $tmp_row['mytime'];
            }
            $scoIdentifier = $row['myid'];
            $title = $row['mytitle'];

            // Selecting the exe_id from stats attempts tables in order to look the max score value.
            if ($origin != 'tracking') {
                $sql_last_attempt = 'SELECT * FROM ' . $tbl_stats_exercices . '
                                     WHERE  exe_exo_id="' . $row['path'] . '" AND
                                            exe_user_id="' . api_get_user_id() . '" AND
                                            orig_lp_id = "' . $lp_id . '" AND
                                            orig_lp_item_id = "' . $row['myid'] . '" AND
                                            exe_cours_id="' . $course_code . '" AND
                                            status <> "incomplete" AND
                                            session_id = ' . $session_id . '
                                     ORDER BY exe_date DESC limit 1';
            } else {
                $sql_last_attempt = 'SELECT * FROM ' . $tbl_stats_exercices . '
                                     WHERE  exe_exo_id="' . $row['path'] . '" AND
                                            exe_user_id="' . $student_id . '" AND
                                            orig_lp_id = "' . $lp_id . '" AND
                                            orig_lp_item_id = "' . $row['myid'] . '" AND
                                            exe_cours_id="' . $course_code . '" AND
                                            status <> "incomplete" AND
                                            session_id = ' . $session_id . '
                                     ORDER BY exe_date DESC limit 1';
            }

            $resultLastAttempt = Database::query($sql_last_attempt);
            $num = Database :: num_rows($resultLastAttempt);
            if ($num > 0) {
                while ($rowLA = Database :: fetch_array($resultLastAttempt)) {
                    $id_last_attempt = $rowLA['exe_id'];
                }
            }

            //var_dump($row['path'] .' '.$score);
            if ($score == 0) {
                $maxscore = $row['mymaxscore'];
            } else {
                if ($row['item_type'] == 'sco') {
                    if (!empty($row['myviewmaxscore']) and $row['myviewmaxscore'] > 0) {
                        $maxscore = $row['myviewmaxscore'];
                    } elseif ($row['myviewmaxscore'] === '') {
                        $maxscore = 0;
                    } else {
                        $maxscore = $row['mymaxscore'];
                    }
                } else {
                    if ($row['item_type'] == 'quiz') {
                        // Get score and total time from last attempt of a exercise en lp.
                        $sql = "SELECT score FROM $TBL_LP_ITEM_VIEW
                                WHERE c_id = $course_id AND lp_item_id = '" . (int) $my_id . "' AND lp_view_id = '" . (int) $my_lp_view_id . "'
                                ORDER BY view_count DESC limit 1";
                        $res_score = Database::query($sql);
                        $row_score = Database::fetch_array($res_score);

                        $sql = "SELECT SUM(total_time) as total_time FROM $TBL_LP_ITEM_VIEW
                                WHERE c_id = $course_id AND lp_item_id = '" . (int) $my_id . "' AND lp_view_id = '" . (int) $my_lp_view_id . "'";
                        $res_time = Database::query($sql);
                        $row_time = Database::fetch_array($res_time);

                        if (Database::num_rows($res_score) > 0 && Database::num_rows($res_time) > 0) {
                            $score = (float) $row_score['score'];
                            $subtotal_time = (int) $row_time['total_time'];
                        } else {
                            $score = 0;
                            $subtotal_time = 0;
                        }
                        //echo $subtotal_time ;
                        //$time = learnpathItem :: get_scorm_time('js', $subtotal_time);
                        // Selecting the max score from an attempt.
                        $sql = "SELECT SUM(t.ponderation) as maxscore
                                FROM (
                        			SELECT distinct question_id, marks, ponderation
                                    FROM $tbl_stats_attempts as at INNER JOIN  $tbl_quiz_questions as q
                        			ON (q.id = at.question_id AND q.c_id = $course_id
                                    )
                                WHERE exe_id ='$id_last_attempt' ) as t";

                        $result = Database::query($sql);
                        $row_max_score = Database :: fetch_array($result);
                        $maxscore = $row_max_score['maxscore'];
                    } else {
                        $maxscore = $row['mymaxscore'];
                    }
                }
            }

            $time_for_total = $subtotal_time;
            $time = learnpathItem :: get_scorm_time('js', $subtotal_time);
            if (empty($title)) {
                $title = rl_get_resource_name(api_get_course_id(), $lp_id, $row['myid']);
            }
            // Remove "NaN" if any (@todo: locate the source of these NaN)
            //$time = str_replace('NaN', '00'.$h.'00\'00"', $time);

            if ($row['item_type'] != 'dokeos_chapter') {
                if ($row['item_type'] == 'quiz') {
                    $correct_test_link = '';
                    $my_url_suffix = '';

                    if ($origin != 'tracking' && $origin != 'tracking_course') {
                        $my_url_suffix = '&course=' . api_get_course_id() . '&student_id=' . api_get_user_id() . '&lp_id=' . Security::remove_XSS($row['mylpid']);
                        $sql_last_attempt = 'SELECT * FROM ' . $tbl_stats_exercices . '
                                             WHERE  exe_exo_id="' . $row['path'] . '" AND
                                                    exe_user_id="' . api_get_user_id() . '" AND
                                                    orig_lp_id = "' . $lp_id . '" AND
                                                    orig_lp_item_id = "' . $row['myid'] . '" AND
                                                    exe_cours_id="' . $course_code . '" AND
                                                    status <> "incomplete" AND
                                                    session_id = ' . $session_id . '
                                            ORDER BY exe_date DESC ';
                    } else {
                        $my_url_suffix = '&course=' . Security::remove_XSS($_GET['course']) . '&student_id=' . $student_id . '&lp_id=' . Security::remove_XSS($row['mylpid']) . '&origin=' . Security::remove_XSS($_GET['origin'] . $from_link);
                        $sql_last_attempt = 'SELECT * FROM ' . $tbl_stats_exercices . '
                                             WHERE   exe_exo_id="' . $row['path'] . '" AND
                                                    exe_user_id="' . $student_id . '" AND
                                                    orig_lp_id = "' . $lp_id . '" AND
                                                    orig_lp_item_id = "' . $row['myid'] . '" AND
                                                    exe_cours_id="' . Database :: escape_string($_GET['course']) . '" AND
                                                    status <> "incomplete" AND
                                                    session_id = ' . $session_id . '
                                             ORDER BY exe_date DESC ';
                    }

                    $resultLastAttempt = Database::query($sql_last_attempt);
                    $num = Database :: num_rows($resultLastAttempt);
                    if ($num > 0) {
                        if (isset($_GET['extend_attempt']) && $_GET['extend_attempt'] == 1 && (isset($_GET['lp_id']) && $_GET['lp_id'] == $my_lp_id) && (isset($_GET['lp_item_id']) && $_GET['lp_item_id'] == $my_id)) {
                            $correct_test_link = '<a href="' . api_get_self() . '?action=stats' . $my_url_suffix . '&session_id=' . api_get_session_id() . '&lp_item_id=' . $my_id . '"><img src="../img/view_less_stats.gif" alt="fold_view" border="0" title="' . get_lang('HideAllAttempts') . '"></a>';
                        } else {
                            $correct_test_link = '<a href="' . api_get_self() . '?action=stats&extend_attempt=1' . $my_url_suffix . '&session_id=' . api_get_session_id() . '&lp_item_id=' . $my_id . '"><img src="../img/view_more_stats.gif" alt="extend_view" border="0" title="' . get_lang('ShowAllAttemptsByExercise') . '"></a>';
                        }
                    } else {
                        $correct_test_link = '-';
                    }
                } else {
                    $correct_test_link = '-';
                }

                $title = Security::remove_XSS($title);

                if ((isset($_GET['lp_id']) && $_GET['lp_id'] == $my_lp_id && false)) {

                    $output .= '<tr class =' . $oddclass . '>
                                    <td>' . $extend_link . '</td>
                                    <td colspan="4">' . $title . '</td>
                                    <td colspan="2">&nbsp;</td>
                                    <td colspan="2">&nbsp;</td>
                                    <td colspan="2">&nbsp;</td>
                                    <td>' . $correct_test_link . '</td>
                                </tr>';
                    $output .= '</tr>';
                } else {
                    if ((isset($_GET['lp_id']) && $_GET['lp_id'] == $my_lp_id ) && (isset($_GET['lp_item_id']) && $_GET['lp_item_id'] == $my_id)) {
                        $output .= "<tr class='$oddclass'>";
                    } else {
                        $output .= "<tr class='$oddclass'>";
                    }

                    if (($is_allowed_to_edit || api_is_drh()) && isset($_GET['lp_id']) && isset($course_code)) {
                        $lp = new learnpath($course_code, $_GET['lp_id'], api_get_user_id());
                        $lp->set_course_int_id($course_id);
                        $item_path_url = $lp->get_link('http', $my_id, false);
                        $item_path_url .= "&width=600";
                        $title = Display::url($title, $item_path_url, array('class' => 'ajax'));
                    }

                    $output .= '<td>'.$extend_link.'</td>
                                <td colspan="4">' . $title . '</td>
                                <td colspan="2">' . learnpathitem::humanize_status($lesson_status) .'</td>
                                <td colspan="2">';
                    if ($row['item_type'] == 'quiz') {
                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                            $output .= Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
                        } else {
                            $output .= show_score($score, $maxscore, false);
                        }
                    } else {
                        $output .= ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . $maxscore));
                    }
                    $output .= '</td>
                                <td colspan="2">'.$time.'</td>
                                 <td>'.$correct_test_link.'</td>';
                    $output .= '</tr>';
                }

                if (!empty($export_csv)) {
                    $temp = array();
                    $temp[] = api_html_entity_decode($title, ENT_QUOTES);
                    $temp[] = api_html_entity_decode($my_lesson_status, ENT_QUOTES);

                    if ($row['item_type'] == 'quiz') {
                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                            $temp[] = '/';
                        } else {
                            $temp[] = ($score == 0 ? '0/' . $maxscore : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
                        }
                    } else {
                        $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
                    }
                    $temp[] = $time;
                    $csv_content[] = $temp;
                }
            }

            $counter++;
            //var_dump($extend_this_attempt, $extend_all);
            if ($extend_this_attempt OR $extend_all) {
                $list1 = learnpath :: get_iv_interactions_array($row['iv_id']);

                foreach ($list1 as $id => $interaction) {
                    if (($counter % 2) == 0) {
                        $oddclass = 'row_odd';
                    } else {
                        $oddclass = 'row_even';
                    }
                    $output .= '<tr class="'.$oddclass.'">
                                    <td></td>
                                    <td></td>
                                    <td></td>
                                    <td>'.$interaction['order_id'].'</td>
                                    <td>'.$interaction['id'].'</td>
                                    <td colspan="2">' . $interaction['type'].'</td>
                                    <td>'.urldecode($interaction['student_response']).'</td>
                                    <td>'.$interaction['result'].'</td>
                                    <td>'.$interaction['latency'].'</td>
                                    <td>'.$interaction['time'].'</td>
                                    <td></td>
                               </tr>';
                    $counter++;
                }
                $list2 = learnpath :: get_iv_objectives_array($row['iv_id']);
                foreach ($list2 as $id => $interaction) {
                    if (($counter % 2) == 0) {
                        $oddclass = 'row_odd';
                    } else {
                        $oddclass = 'row_even';
                    }
                    $output .= '<tr class="'.$oddclass.'">
                                    <td></td>
                                    <td></td>
                                    <td></td>
                                    <td>' . $interaction['order_id'] . '</td>
                                    <td colspan="2">'.$interaction['objective_id'] . '</td>
                                    <td colspan="2">' . $interaction['status'] . '</td>
                                    <td>' . $interaction['score_raw'].'</td>
                                    <td>' . $interaction['score_max'] .'</td>
                                    <td>' . $interaction['score_min'].'</td>
                                    <td></td>
                               </tr>';
                    $counter++;
                }
            }

            // Attempts listing by exercise.
            if ((isset($_GET['lp_id']) && $_GET['lp_id'] == $my_lp_id) && (isset($_GET['lp_item_id']) && $_GET['lp_item_id'] == $my_id) && isset($_GET['extend_attempt'])) {

                $res_attempts = Database::query($sql_attempts);
                $num_attempts = Database :: num_rows($res_attempts);
                if ($row['item_type'] === 'quiz') {
                    if ($num_attempts > 0) {
                        $n = 1;
                        while ($row_attempts = Database :: fetch_array($res_attempts)) {
                            $my_score = $row_attempts['exe_result'];
                            $my_maxscore = $row_attempts['exe_weighting'];
                            $my_exe_id = $row_attempts['exe_id'];
                            $my_orig_lp = $row_attempts['orig_lp_id'];
                            $my_orig_lp_item = $row_attempts['orig_lp_item_id'];
                            $my_exo_exe_id = $row_attempts['exe_exo_id'];
                            $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
                            $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
                            if ($mktime_start_date && $mktime_exe_date) {
                                $mytime = ((int) $mktime_exe_date - (int) $mktime_start_date);
                                $time_attemp = learnpathItem :: get_scorm_time('js', $mytime);
                                $time_attemp = str_replace('NaN', '00' . $h . '00\'00"', $time_attemp);
                            } else {
                                $time_attemp = ' - ';
                            }
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                                $view_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
                            } else {
                                // Show only float when need it
                                if ($my_score == 0) {
                                    $view_score = show_score(0, $my_maxscore, false);
                                } else {
                                    if ($my_maxscore == 0) {
                                        $view_score = $my_score;
                                    } else {
                                        $view_score = show_score($my_score, $my_maxscore, false);
                                    }
                                }
                            }
                            $my_lesson_status = $row_attempts['status'];

                            if ($my_lesson_status == '') {
                                $my_lesson_status = learnpathitem::humanize_status('completed');
                            } elseif ($my_lesson_status == 'incomplete') {
                                $my_lesson_status = learnpathitem::humanize_status('incomplete');
                            }

                            $output .= '<tr class="' . $oddclass . '" >
                                            <td></td>
                                            <td>' . $extend_attempt_link . '</td>
                                            <td colspan="3">' . get_lang('Attempt').' '. $n.'</td>
                                            <td colspan="2">' . $my_lesson_status . '</td>
                                            <td colspan="2">'.$view_score . '</td>
                                            <td colspan="2">'.$time_attemp . '</td>';
                            if ($origin != 'tracking') {
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                                    $output .= '<td><img src="' . api_get_path(WEB_IMG_PATH) . 'quiz_na.gif" alt="' . get_lang('ShowAttempt') . '" title="' . get_lang('ShowAttempt') . '"></td>';
                                } else {
                                    $output .= '<td><a href="../exercice/exercise_show.php?origin=' . $origin . '&id=' . $my_exe_id . '&cidReq=' . $course_code . $from_link . '" target="_parent">
                                                    <img src="' . api_get_path(WEB_IMG_PATH) . 'quiz.gif" alt="' . get_lang('ShowAttempt') . '" title="' . get_lang('ShowAttempt') . '"></a></td>';
                                }
                            } else {
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
                                    $output .= '<td><img src="' . api_get_path(WEB_IMG_PATH) . 'quiz_na.gif" alt="' . get_lang('ShowAndQualifyAttempt') . '" title="' . get_lang('ShowAndQualifyAttempt') . '"></td>';
                                } else {
                                    $output .= '<td><a href="../exercice/exercise_show.php?cidReq=' . $course_code . '&origin=correct_exercise_in_lp&id=' . $my_exe_id . '" target="_parent">
                                                     <img src="' . api_get_path(WEB_IMG_PATH) . 'quiz.gif" alt="' . get_lang('ShowAndQualifyAttempt') . '" title="' . get_lang('ShowAndQualifyAttempt') . '"></a></td>';
                                }
                            }
                            $output .= '</tr>';
                            $n++;
                        }
                    }
                    $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
                }
            }
        }

        $total_time += $time_for_total;
        // QUIZZ IN LP
        $a_my_id = array();
        if (!empty($my_lp_id)) {
            $a_my_id[] = $my_lp_id;
        }
    }
}
//NOT Extend all "left green cross"
if (!empty($a_my_id)) {
    $my_studen_id = 0;
    $my_course_id = '';
    if ($origin == 'tracking') {
        $my_studen_id = $student_id;
        $my_course_id = Database::escape_string($_GET['course']);
    } else {
        $my_studen_id = intval(api_get_user_id());
        $my_course_id = Database::escape_string(api_get_course_id());
    }
    //var_dump($my_studen_id, $my_course_id,$a_my_id);
    if (isset($_GET['extend_attempt'])) {
        //"Right green cross" extended
        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, api_get_session_id(), false, false);
    } else {
        //"Left green cross" extended
        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, api_get_session_id(), false, true);
    }
} else {
    // Extend all "left green cross"
    if ($origin == 'tracking') {
        $my_course_id = Database::escape_string($_GET['course']);
        //    var_dump($student_id, $my_course_id );
        if (!empty($student_id) && !empty($my_course_id)) {
            $total_score = Tracking::get_avg_student_score($student_id, $my_course_id, array(intval($_GET['lp_id'])), api_get_session_id(), false, false);
        } else {
            $total_score = 0;
        }
    } else {
        $total_score = Tracking::get_avg_student_score(api_get_user_id(), api_get_course_id(), array(intval($_GET['lp_id'])), api_get_session_id(), false, false);
    }
}

$total_time = learnpathItem :: get_scorm_time('js', $total_time);
//$total_time = str_replace('NaN', '00:00:00' ,$total_time);
$total_time = str_replace('NaN', '00' . $h . '00\'00"', $total_time);
//$lp_type = learnpath :: get_type_static($lp_id);
//$total_percent = 0;

if (!$is_allowed_to_edit && $result_disabled_ext_all) {
    $final_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
} else {
    if (is_numeric($total_score))
        $final_score = $total_score . '%';
    else
        $final_score = $total_score;
}

if (($counter % 2) == 0) {
    $oddclass = 'row_odd';
} else {
    $oddclass = 'row_even';
}

//if (empty($extend_all)) {
$output .= '<tr class="'.$oddclass.'">
                <td></td>
                <td colspan="4">
                    <i>' . get_lang('AccomplishedStepsTotal') .'</i>
                </td>
                <td colspan="2"></td>
                <td colspan="2">
                    ' . $final_score.'
                </td>
                <td colspan="2">' . $total_time . '</div><td></td>
           </tr>';
//}

$output .= "</table>";

if (!empty($export_csv)) {
    $temp = array(
        '',
        '',
        '',
        ''
    );
    $csv_content[] = $temp;
    $temp = array(
        get_lang('AccomplishedStepsTotal'),
        '',
        $final_score,
        $total_time
    );
    $csv_content[] = $temp;
    ob_end_clean();
    Export :: export_table_csv($csv_content, 'reporting_learning_path_details');
    exit;
}

if ($origin != 'tracking') {
    $output .= "</body></html>";
}

if (empty($export_csv)) {
    echo $output;
}
lp_upload.php000064400000015742152003363470007250 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Script managing the learnpath upload. To best treat the uploaded file, make sure we can identify it.
 * @package chamilo.learnpath
 * @author Yannick Warnier <ywarnier@beeznest.org>
 */
/**
 * Code
 */
// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;
require_once 'back_compat.inc.php';
$course_dir = api_get_course_path().'/scorm';
$course_sys_dir = api_get_path(SYS_COURSE_PATH).$course_dir;
if (empty($_POST['current_dir'])) {
    $current_dir = '';
} else {
    $current_dir = replace_dangerous_char(trim($_POST['current_dir']), 'strict');
}
$uncompress = 1;

//error_log('New LP - lp_upload.php', 0);
/*
 * Check the request method in place of a variable from POST
 * because if the file size exceed the maximum file upload
 * size set in php.ini, all variables from POST are cleared !
 */

$user_file = Request::is_post() ? Request::file('user_file') : array();
$user_file = $user_file ? $user_file : array();
$is_error = isset($user_file['error']) ? $user_file['error'] : false;
if( Request::is_post() && $is_error){
    return api_failure::set_failure('upload_file_too_big');
    unset($_FILEs['user_file']);
} else if ($_SERVER['REQUEST_METHOD'] == 'POST' && count($_FILES) > 0 && !empty($_FILES['user_file']['name'])) {

    // A file upload has been detected, now deal with the file...

    // Directory creation.

    $stopping_error = false;

    $s = $_FILES['user_file']['name'];

    // Get name of the zip file without the extension.
    $info = pathinfo($s);
    $filename = $info['basename'];
    $extension = $info['extension'];
    $file_base_name = str_replace('.'.$extension, '', $filename);

    $new_dir = replace_dangerous_char(trim($file_base_name), 'strict');
    require_once 'learnpath.class.php';
    $type = learnpath::get_package_type($_FILES['user_file']['tmp_name'], $_FILES['user_file']['name']);

    $proximity = 'local';
    if (!empty($_REQUEST['content_proximity'])) {
    	$proximity = Database::escape_string($_REQUEST['content_proximity']);
    }
    $maker = 'Scorm';
    if (!empty($_REQUEST['content_maker'])) {
    	$maker = Database::escape_string($_REQUEST['content_maker']);
    }

    switch ($type) {
        case 'scorm':
            require_once 'scorm.class.php';
            $oScorm = new scorm();
            $manifest = $oScorm->import_package($_FILES['user_file'], $current_dir);
            if (!$manifest) { //if api_set_failure
                return api_failure::set_failure(api_failure::get_last_failure());
            }
            if (!empty($manifest)) {
                $oScorm->parse_manifest($manifest);
                $oScorm->import_manifest(api_get_course_id(), $_REQUEST['use_max_score']);
            } else {
                // Show error message stored in $oScrom->error_msg.
            }
            $oScorm->set_proximity($proximity);
            $oScorm->set_maker($maker);
            $oScorm->set_jslib('scorm_api.php');
            break;
        case 'aicc':
            require_once 'aicc.class.php';
            $oAICC = new aicc();
            $config_dir = $oAICC->import_package($_FILES['user_file']);
            if (!empty($config_dir)) {
                $oAICC->parse_config_files($config_dir);
                $oAICC->import_aicc(api_get_course_id());
            }
            $oAICC->set_proximity($proximity);
            $oAICC->set_maker($maker);
            $oAICC->set_jslib('aicc_api.php');
            break;
        case 'oogie':
            require_once 'openoffice_presentation.class.php';
            $take_slide_name = empty($_POST['take_slide_name']) ? false : true;
            $o_ppt = new OpenofficePresentation($take_slide_name);
            $first_item_id = $o_ppt->convert_document($_FILES['user_file']);
            break;
        case 'woogie':
            require_once 'openoffice_text.class.php';
            $split_steps = $_POST['split_steps'];
            $o_doc = new OpenofficeText($split_steps);
            $first_item_id = $o_doc->convert_document($_FILES['user_file']);
            break;
        case '':
        default:
            return api_failure::set_failure('not_a_learning_path');
    }
} elseif($_SERVER['REQUEST_METHOD'] == 'POST') {
    // end if is_uploaded_file

    // If file name given to get in claroline/upload/, try importing this way.

    // A file upload has been detected, now deal with the file...

    // Directory creation.

    $stopping_error = false;

    // Escape path with basename so it can only be directly into the claroline/upload directory.
    $s = api_get_path(SYS_ARCHIVE_PATH).basename($_POST['file_name']);
    // Get name of the zip file without the extension
    $info = pathinfo($s);
    $filename = $info['basename'];
    $extension = $info['extension'];
    $file_base_name = str_replace('.'.$extension, '', $filename);
    $new_dir = replace_dangerous_char(trim($file_base_name), 'strict');

    require_once 'learnpath.class.php';

    $result = learnpath::verify_document_size($s);
    if ($result == true) {
        return api_failure::set_failure('upload_file_too_big');

    }
    $type = learnpath::get_package_type($s, basename($s));

    switch ($type) {
        case 'scorm':
            require_once 'scorm.class.php';
            $oScorm = new scorm();
            $manifest = $oScorm->import_local_package($s, $current_dir);
            if ($manifest === false ) { //if ap i_set_failure
                return api_failure::set_failure(api_failure::get_last_failure());
            }
            if (!empty($manifest)) {
                $oScorm->parse_manifest($manifest);
                $oScorm->import_manifest(api_get_course_id(), $_REQUEST['use_max_score']);
            }

            $proximity = '';
            if (!empty($_REQUEST['content_proximity'])) { $proximity = Database::escape_string($_REQUEST['content_proximity']); }
            $maker = '';
            if (!empty($_REQUEST['content_maker'])) {$maker = Database::escape_string($_REQUEST['content_maker']); }
            $oScorm->set_proximity($proximity);
            $oScorm->set_maker($maker);
            $oScorm->set_jslib('scorm_api.php');
            break;
        case 'aicc':
            require_once 'aicc.class.php';
            $oAICC = new aicc();
            $config_dir = $oAICC->import_local_package($s, $current_dir);
            if (!empty($config_dir)) {
                $oAICC->parse_config_files($config_dir);
                $oAICC->import_aicc(api_get_course_id());
            }
            $proximity = '';
            if (!empty($_REQUEST['content_proximity'])) { $proximity = Database::escape_string($_REQUEST['content_proximity']); }
            $maker = '';
            if (!empty($_REQUEST['content_maker'])) { $maker = Database::escape_string($_REQUEST['content_maker']); }
            $oAICC->set_proximity($proximity);
            $oAICC->set_maker($maker);
            $oAICC->set_jslib('aicc_api.php');
            break;
        case '':
        default:
            return api_failure::set_failure('not_a_learning_path');
    }
}lp_view.lib.js000064400000004575152003363470007332 0ustar00/***********************************************
* IFrame SSI script II- Dynamic Drive DHTML code library (http://www.dynamicdrive.com)
* Visit DynamicDrive.com for hundreds of original DHTML scripts
* This notice must stay intact for legal use
***********************************************/

//Input the IDs of the IFRAMES you wish to dynamically resize to match its content height:
//Separate each ID with a comma. Examples: ["myframe1", "myframe2"] or ["myframe"] or [] for none:
var iframeid="lp_content_frame";

//Should script hide iframe from browsers that don't support this script (non IE5+/NS6+ browsers. Recommended):
var iframehide="no";

//var getFFVersion=navigator.userAgent.substring(navigator.userAgent.indexOf("Firefox")).split("/")[1];
var FFextraHeight=32; //parseFloat(getFFVersion)>=0.1? 16 : 0; //extra height in px to add to iframe in FireFox 1.0+ browsers

function resizeCaller() {
	if (document.getElementById)
		resizeIframe(iframeid);
	//reveal iframe for lower end browsers? (see var above):
	if ((document.all || document.getElementById) && iframehide=="no"){
		var tempobj=document.all? document.all[iframeid] : document.getElementById(iframeid);
		tempobj.style.display="block";
	}
}

function resizeIframe(frameid){
	var currentfr=document.getElementById(frameid);
	if (currentfr && !window.opera){
		currentfr.style.display="block";
		if (currentfr.contentDocument && currentfr.contentDocument.body.offsetHeight){ //ns6 syntax
			currentfr.height = currentfr.contentDocument.body.offsetHeight+FFextraHeight;
		}
		else if (currentfr.Document && currentfr.Document.body.scrollHeight) //ie5+ syntax
			currentfr.height = currentfr.Document.body.scrollHeight;

		if(currentfr.height < 580){
			currentfr.height = 580;
		}
		if (currentfr.addEventListener)
			currentfr.addEventListener("load", readjustIframe, false);
		else if (currentfr.attachEvent){
			currentfr.detachEvent("onload", readjustIframe); // Bug fix line
			currentfr.attachEvent("onload", readjustIframe);
		}
	}
}

function readjustIframe(loadevt) {
	var crossevt=(window.event)? event : loadevt;
	var iframeroot=(crossevt.currentTarget)? crossevt.currentTarget : crossevt.srcElement;
	if (iframeroot)
		resizeIframe(iframeroot.id);
}

if (window.addEventListener)
	window.addEventListener("load", resizeCaller, false);
else if (window.attachEvent)
	window.attachEvent("onload", resizeCaller);
else
	window.onload=resizeCaller;lp_view.php000064400000047572152003363470006744 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
* This file was originally the copy of document.php, but many modifications happened since then ;
* the direct file view is not needed anymore, if the user uploads a scorm zip file, a directory
* will be automatically created for it, and the files will be uncompressed there for example ;
*
* @package chamilo.learnpath
* @author Yannick Warnier <ywarnier@beeznest.org> - redesign
* @author Denes Nagy, principal author
* @author Isthvan Mandak, several new features
* @author Roan Embrechts, code improvements and refactoring
*/
/**
 * Code
 */

use \ChamiloSession as Session;

$_SESSION['whereami'] = 'lp/view';
$this_section = SECTION_COURSES;

if ($lp_controller_touched != 1) {
    header('location: lp_controller.php?action=view&item_id='.intval($_REQUEST['item_id']));
    exit;
}

/* Libraries */
require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';

//To prevent the template class
$show_learnpath = true;

api_protect_course_script();

$lp_id = intval($_GET['lp_id']);

// Check if the learning path is visible for student - (LP requisites)
if (!api_is_allowed_to_edit(null, true) && !learnpath::is_lp_visible_for_student($lp_id, api_get_user_id())) {
    api_not_allowed(true);
}

//Checking visibility (eye icon)
$visibility = api_get_item_visibility(api_get_course_info(), TOOL_LEARNPATH, $lp_id, $action, api_get_user_id(), api_get_session_id());
if (!api_is_allowed_to_edit(false, true, false, false) && intval($visibility) == 0) {
    api_not_allowed(true);
}

if (empty($_SESSION['oLP'])) {
    api_not_allowed(true);
}

$debug = 0;

if ($debug) { error_log('------ Entering lp_view.php -------'); }

$_SESSION['oLP']->error = '';
$lp_item_id = $_SESSION['oLP']->get_current_item_id();
$lp_type    = $_SESSION['oLP']->get_type();

$course_code    = api_get_course_id();
$course_id      = api_get_course_int_id();
$user_id        = api_get_user_id();
$platform_theme = api_get_setting('stylesheets'); // Plataform's css.
$my_style       = $platform_theme;

$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.lp_minipanel.js" type="text/javascript" language="javascript"></script>';
/*
 * already added in lp_controller.php
if (api_get_setting('show_glossary_in_documents') == 'ismanual' || api_get_setting('show_glossary_in_documents') == 'isautomatic' ) {
    $htmlHeadXtra[] = '<script type="text/javascript">
<!--
    var jQueryFrameReadyConfigPath = \''.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.min.js\';
-->
</script>';
    $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.frameready.js" type="text/javascript" language="javascript"></script>';
    $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js" type="text/javascript" language="javascript"></script>';
}*/

$htmlHeadXtra[] = '<script>
$(document).ready(function(){
	$("div#log_content_cleaner").bind("click", function() {
    	$("div#log_content").empty();
	});
});
var chamilo_xajax_handler = window.oxajax;
</script>';

if ($_SESSION['oLP']->mode == 'embedframe' || $_SESSION['oLP']->get_hide_toc_frame()==1 ) {
    $htmlHeadXtra[] = '<script>
        $(document).ready(function(){
            toogle_minipanel();
        });
        </script>';
}

// Prepare variables for the test tool (just in case) - honestly, this should disappear later on.
$_SESSION['scorm_view_id'] = $_SESSION['oLP']->get_view_id();
$_SESSION['scorm_item_id'] = $lp_item_id;
$_SESSION['lp_mode'] 	   = $_SESSION['oLP']->mode;

// Reinit exercises variables to avoid spacename clashes (see exercise tool)
if (isset($exerciseResult) || isset($_SESSION['exerciseResult'])) {
    Session::erase($exerciseResult);
}
unset($_SESSION['objExercise']);
unset($_SESSION['questionList']);

// additional APIs
$htmlHeadXtra[] = '<script>
chamilo_courseCode = "'.$course_code.'";
</script>';
// Document API
$htmlHeadXtra[] = '<script src="js/documentapi.js" type="text/javascript" language="javascript"></script>';
// Storage API
$htmlHeadXtra[] = '<script>
var sv_user = \''.api_get_user_id().'\';
var sv_course = chamilo_courseCode;
var sv_sco = \''.intval($_REQUEST['lp_id']).'\';
</script>'; // FIXME fetch sco and userid from a more reliable source directly in sotrageapi.js
$htmlHeadXtra[] = '<script type="text/javascript" src="js/storageapi.js"></script>';

/**
 * Get a link to the corresponding document.
 */

if ($debug) {
    error_log(" src: $src ");
    error_log(" lp_type: $lp_type ");
}

$get_toc_list = $_SESSION['oLP']->get_toc();
$type_quiz = false;

foreach ($get_toc_list as $toc) {
    if ($toc['id'] == $lp_item_id && ($toc['type']=='quiz')) {
        $type_quiz = true;
    }
}

if (!isset($src)) {
    $src = null;
    switch ($lp_type) {
        case 1:
            $_SESSION['oLP']->stop_previous_item();
            $htmlHeadXtra[] = '<script src="scorm_api.php" type="text/javascript" language="javascript"></script>';
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http', $lp_item_id, $get_toc_list);

                //Prevents FF 3.6 + Adobe Reader 9 bug see BT#794 when calling a pdf file in a LP.
                $file_info = parse_url($src);
                $file_info = pathinfo($file_info['path']);
                if (api_strtolower(substr($file_info['extension'], 0, 3) == 'pdf')) {
                    $src = api_get_path(WEB_CODE_PATH).'newscorm/lp_view_item.php?lp_item_id='.$lp_item_id;
                }
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php?error=prerequisites';
            }
            break;
        case 2:
            // save old if asset
            $_SESSION['oLP']->stop_previous_item(); // save status manually if asset
            $htmlHeadXtra[] = '<script src="scorm_api.php" type="text/javascript" language="javascript"></script>';
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http',$lp_item_id, $get_toc_list);
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php?error=prerequisites';
            }
            break;
        case 3:
            // aicc
            $_SESSION['oLP']->stop_previous_item(); // save status manually if asset
            $htmlHeadXtra[] = '<script src="'.$_SESSION['oLP']->get_js_lib().'" type="text/javascript" language="javascript"></script>';
            $prereq_check = $_SESSION['oLP']->prerequisites_match($lp_item_id);
            if ($prereq_check === true) {
                $src = $_SESSION['oLP']->get_link('http',$lp_item_id, $get_toc_list);
                $_SESSION['oLP']->start_current_item(); // starts time counter manually if asset
            } else {
                $src = 'blank.php';
            }
            break;
        case 4:
            break;
    }
}

$autostart = 'true';
// Update status, total_time from lp_item_view table when you finish the exercises in learning path.

if ($debug) {
    error_log('$type_quiz: '.$type_quiz);
    error_log('$_REQUEST[exeId]: '.intval($_REQUEST['exeId']));
    error_log('$lp_id: '.$lp_id);
    error_log('$_GET[lp_item_id]: '.intval($_GET['lp_item_id']));
}

if ($type_quiz && !empty($_REQUEST['exeId']) && isset($lp_id) && isset($_GET['lp_item_id'])) {
    global $src;

    $_SESSION['oLP']->items[$_SESSION['oLP']->current]->write_to_db();

    $TBL_TRACK_EXERCICES    = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
    $TBL_LP_ITEM_VIEW       = Database::get_course_table(TABLE_LP_ITEM_VIEW);
    $TBL_LP_ITEM            = Database::get_course_table(TABLE_LP_ITEM);
    $safe_item_id           = intval($_GET['lp_item_id']);
    $safe_id                = $lp_id;
    $safe_exe_id            = intval($_REQUEST['exeId']);

    if ($safe_id == strval(intval($safe_id)) && $safe_item_id == strval(intval($safe_item_id))) {

        $sql = 'SELECT start_date, exe_date, exe_result, exe_weighting FROM ' . $TBL_TRACK_EXERCICES . ' WHERE exe_id = '.$safe_exe_id;
        if ($debug) error_log($sql);
        $res = Database::query($sql);
        $row_dates = Database::fetch_array($res);

        $time_start_date = api_strtotime($row_dates['start_date'],'UTC');
        $time_exe_date   = api_strtotime($row_dates['exe_date'],'UTC');

        $mytime 	= ((int)$time_exe_date-(int)$time_start_date);
        $score 		= (float)$row_dates['exe_result'];
        $max_score 	= (float)$row_dates['exe_weighting'];

        $sql_upd_max_score = "UPDATE $TBL_LP_ITEM SET max_score = '$max_score' WHERE c_id = $course_id AND id = '".$safe_item_id."'";
        if ($debug) error_log($sql_upd_max_score);
        Database::query($sql_upd_max_score);

        $sql_last_attempt = "SELECT id FROM $TBL_LP_ITEM_VIEW  WHERE c_id = $course_id AND lp_item_id = '$safe_item_id' AND lp_view_id = '".$_SESSION['oLP']->lp_view_id."' order by id desc limit 1";
        $res_last_attempt = Database::query($sql_last_attempt);
        if ($debug) error_log($sql_last_attempt);

        if (Database::num_rows($res_last_attempt)) {
        	$row_last_attempt = Database::fetch_row($res_last_attempt);
        	$lp_item_view_id  = $row_last_attempt[0];
            $sql_upd_score = "UPDATE $TBL_LP_ITEM_VIEW SET status = 'completed' , score = $score, total_time = $mytime
                              WHERE id='".$lp_item_view_id."' AND c_id = $course_id ";

            if ($debug) error_log($sql_upd_score);
            Database::query($sql_upd_score);

            $update_query = "UPDATE $TBL_TRACK_EXERCICES SET orig_lp_item_view_id = $lp_item_view_id  WHERE exe_id = ".$safe_exe_id;
            Database::query($update_query);
        }
    }
    if (intval($_GET['fb_type']) > 0) {
        $src = 'blank.php?msg=exerciseFinished';
    } else {
        $src = api_get_path(WEB_CODE_PATH).'exercice/result.php?origin=learnpath&id='.$safe_exe_id;

        if ($debug) error_log('Calling URL: '.$src);
    }
    $autostart = 'false';
}

$_SESSION['oLP']->set_previous_item($lp_item_id);
$nameTools = Security :: remove_XSS($_SESSION['oLP']->get_name());

$save_setting = api_get_setting('show_navigation_menu');
global $_setting;
$_setting['show_navigation_menu'] = 'false';
$scorm_css_header = true;
$lp_theme_css = $_SESSION['oLP']->get_theme(); // Sets the css theme of the LP this call is also use at the frames (toc, nav, message).

if ($_SESSION['oLP']->mode == 'fullscreen') {
    $htmlHeadXtra[] = "<script>window.open('$src','content_id','toolbar=0,location=0,status=0,scrollbars=1,resizable=1');</script>";
}

// Not in fullscreen mode.
Display::display_reduced_header($nameTools);

// Check if audio recorder needs to be in studentview.
if (isset($_SESSION['status']) && $_SESSION['status'][$course_code] == 5) {
	$audio_recorder_studentview = true;
} else {
	$audio_recorder_studentview = false;
}

// Set flag to ensure lp_header.php is loaded by this script (flag is unset in lp_header.php).
$_SESSION['loaded_lp_view'] = true;

$display_none = '';
$margin_left = '305px';

//Media player code

$display_mode = $_SESSION['oLP']->mode;
$scorm_css_header = true;
$lp_theme_css = $_SESSION['oLP']->get_theme();

// Setting up the CSS theme if exists.
if (!empty ($lp_theme_css) && !empty ($mycourselptheme) && $mycourselptheme != -1 && $mycourselptheme == 1) {
    global $lp_theme_css;
} else {
    $lp_theme_css = $my_style;
}

$progress_bar   = $_SESSION['oLP']->get_progress_bar('', -1, '', true);
$navigation_bar = $_SESSION['oLP']->get_navigation_bar();
$mediaplayer    = $_SESSION['oLP']->get_mediaplayer($autostart);

$tbl_lp_item    = Database::get_course_table(TABLE_LP_ITEM);
$show_audioplayer = false;
// Getting all the information about the item.
$sql = "SELECT audio FROM " . $tbl_lp_item . " WHERE c_id = $course_id AND lp_id = '" . $_SESSION['oLP']->lp_id."'";
$res_media= Database::query($sql);

if (Database::num_rows($res_media) > 0) {
    while ($row_media= Database::fetch_array($res_media)) {
        if (!empty($row_media['audio'])) {$show_audioplayer = true; break;}
    }
}

/*
 *
 *    <?php  if (!empty($_SESSION['oLP']->scorm_debug) && api_is_platform_admin()) { //only show log  ?>
                <!-- log message layout -->
                <div id="lp_log_name" name="lp_log_name" class="lp_log" style="height:150px;overflow:auto;margin:4px">
                    <div id="log_content"></div>
                    <div id="log_content_cleaner" style="cursor: pointer; color:blue;"><?php echo get_lang('Clean'); ?></div>
                </div>
                <!-- end log message layout -->
                <?php } ?>
 */

echo '<div id="learning_path_main" style="width:100%; height:100%;">';
    $is_allowed_to_edit = api_is_allowed_to_edit(null, true, false, false);
    if ($is_allowed_to_edit) {
        echo '<div id="learning_path_breadcrumb_zone">';
        global $interbreadcrumb;
        $interbreadcrumb[] = array('url' => 'lp_controller.php?action=list&isStudentView=false', 'name' => get_lang('LearningPaths'));
        $interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=".$_SESSION['oLP']->lp_id."&isStudentView=false", 'name' => $_SESSION['oLP']->get_name());
        $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('Preview'));
        //$interbreadcrumb[] = array('type' => 'right', 'url' => api_get_self()."?action=add_item&type=step&lp_id=".$_SESSION['oLP']->lp_id."&isStudentView=false", 'name' => get_lang('Edit'), 'class' => 'btn btn-mini btn-warning');

        echo return_breadcrumb($interbreadcrumb, null, null);
        echo '</div>';
    }
    echo '<div id="learning_path_left_zone" style="background-color : #FABC34; '.$display_none.'"> ';
    echo '<div id="header">';
        echo '<a href="lp_controller.php?action=return_to_course_homepage&'.api_get_cidreq().
            '" target="_self" onclick="javascript: window.parent.API.save_asset();"><img src="../css/mm_home.png" style="width: 40px; height: 40px" /></a>';
        echo '<a href="lp_controller.php?action=return_to_course_homepage&'.api_get_cidreq().
            '" target="_self" onclick="javascript: window.parent.API.save_asset();" ' . 
            'style="color: #002D40; font-weight: bold; font-size: 1.2em; margin-left: 5px;">';
        echo 'Retour à la page d\'acceuil</a>';
    echo '</div><br/>';
?>
        <!-- end header -->

        <!-- Author image preview -->
        
        <?php echo $navigation_bar['previous']; ?> <!-- BAT /BAT -->
        <div id="progress_bar" style="width: 50%; display: inline-block; margin-bottom: 12px;"><?php echo $progress_bar; ?></div>
        <?php echo $navigation_bar['next']; ?>
        <?php if (isset($navigation_bar['fullscreen'])) echo '<div class="mm_scorm_button_next">' . $navigation_bar['fullscreen'] . '</div>'; ?>
        <div style="margin-bottom: 15px;"></div>
        <!-- end image preview Layout -->


        <!-- media player layout -->
        <?php
        if ($show_audioplayer) {
            echo '<div id="lp_media_file">';
            echo $mediaplayer;
            echo '</div>';
        }
        ?>
        <!-- end media player layout -->

        <!-- TOC layout -->
        <div id="toc_id" name="toc_name" style="overflow: auto; padding:0;margin-top:0px;width:100%;float:left">
            <div id="learning_path_toc">
                <?php echo $_SESSION['oLP']->get_html_toc($get_toc_list); ?>
            </div>
        </div>
        <!-- end TOC layout -->
        
         <!--BAT : tree -->
        <img src="../css/mm_tree.png" title="tree" alt="tree" style="position: absolute; bottom: -10px; width: 280px;"/>
    </div>
    <!-- end left zone -->

    <!-- right zone -->
    <div id="learning_path_right_zone" style="margin-left:<?php echo $margin_left;?>;height:100%">
    <?php
        // hub 26-05-2010 Fullscreen or not fullscreen
        $height = '100%';
        if ($_SESSION['oLP']->mode == 'fullscreen') {
            echo '<iframe id="content_id_blank" name="content_name_blank" src="blank.php" border="0" frameborder="0" style="width:100%;height:'.$height.'" ></iframe>';
        } else {
            echo '<iframe id="content_id" name="content_name" src="'.$src.'" border="0" frameborder="0" style="display: block; width:100%;height:'.$height.'"></iframe>';
        }
    ?>
    </div>
    <!-- end right Zone -->
</div>

<script>
    // Resize right and left pane to full height (HUB 20-05-2010).
    function updateContentHeight() {
        document.body.style.overflow = 'hidden';
        var IE = window.navigator.appName.match(/microsoft/i);
        var hauteurHeader = document.getElementById('header').offsetHeight;
        var hauteurAuthorImg = document.getElementById('author_image').offsetHeight;
        var hauteurAuthorName = document.getElementById('author_name').offsetHeight;

        var hauteurMedia = 0;
        if ($("#lp_media_file").length != 0) {
            hauteurMedia = document.getElementById('lp_media_file').offsetHeight;
        }

        var hauteurTitre = document.getElementById('scorm_title').offsetHeight;
        var hauteurAction = 0;
        if (document.getElementById('actions_lp')) hauteurAction = document.getElementById('actions_lp').offsetHeight;
        var hauteurHaut = hauteurHeader+hauteurAuthorImg+hauteurAuthorName+hauteurMedia+hauteurTitre+hauteurAction;
        var innerHauteur = (IE) ? document.body.clientHeight : window.innerHeight ;
        var debugsize = 0;
        // -40 is a static adjustement for margin, spaces on the page
        <?php if (!empty($_SESSION['oLP']->scorm_debug)) echo 'debugsize = 150;' ?>
        document.getElementById('inner_lp_toc').style.height = innerHauteur - hauteurHaut - 40 - debugsize + "px";
        if (document.getElementById('content_id')) {
            document.getElementById('content_id').style.height = innerHauteur + 'px';
        }

    // Loads the glossary library.
    <?php
      if (api_get_setting('show_glossary_in_extra_tools') == 'true') {
           if (api_get_setting('show_glossary_in_documents') == 'ismanual') {
                ?>
            $.frameReady(function(){
                   //  $("<div>I am a div courses</div>").prependTo("body");
         }, "top.content_name",
          { load: [
                  {type:"script", id:"_fr1", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery.min.js"},
                  {type:"script", id:"_fr4", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery-ui/smoothness/jquery-ui-1.8.21.custom.min.js"},
                  {type:"stylesheet", id:"_fr5", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery-ui/smoothness/jquery-ui-1.8.21.custom.css"},
                  {type:"script", id:"_fr2", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery.highlight.js"}

          ] }
          );
    <?php
        } elseif (api_get_setting('show_glossary_in_documents') == 'isautomatic') {
      ?>
    $.frameReady(function(){
        //  $("<div>I am a div courses</div>").prependTo("body");
      }, "top.content_name",
      { load: [
              {type:"script", id:"_fr1", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery.min.js"},
              {type:"script", id:"_fr4", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery-ui/smoothness/jquery-ui-1.8.21.custom.min.js"},
              {type:"stylesheet", id:"_fr5", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery-ui/smoothness/jquery-ui-1.8.21.custom.css"},
              {type:"script", id:"_fr2", src:"<?php echo api_get_path(WEB_LIBRARY_PATH); ?>javascript/jquery.highlight.js"}

          ] }
          );
      <?php
           }
      }
      ?>
}
    window.onload = updateContentHeight;
    window.onresize = updateContentHeight;
-->
</script>
<?php
// Restore a global setting.
$_setting['show_navigation_menu'] = $save_setting;

if ($debug) {
    error_log(' ------- end lp_view.php ------');
}
lp_view_item.php000064400000012144152003363470007745 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * This is a learning path creation and player tool in Chamilo - previously learnpath_handler.php
 *
 * @author Patrick Cool
 * @author Denes Nagy
 * @author Roan Embrechts, refactoring and code cleaning
 * @author Yannick Warnier <ywarnier@beeznest.org> - cleaning and update for new SCORM tool
 * @package chamilo.learnpath
 */
/**
 * Code
 */
// Prevents FF 3.6 + Adobe Reader 9 bug see BT#794 when calling a pdf file in a LP

// The main_api.lib.php, database.lib.php and display.lib.php
// libraries are included by default.

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';
require_once 'learnpath_functions.inc.php';
require_once 'resourcelinker.inc.php';
// Including the global initialization file.
require_once '../inc/global.inc.php';

api_protect_course_script();

if (isset($_GET['lp_item_id'])) {

    // Get parameter only came from lp_view.php.
    $lp_item_id  = intval($_GET['lp_item_id']);
    if (isset($_SESSION['lpobject'])) {
        $oLP = unserialize($_SESSION['lpobject']);
    }   
    if (is_object($oLP)) {
       $src = $oLP->get_link('http', $lp_item_id);
    }
    
    $url_info 		= parse_url($src);
    $real_url_info	= parse_url(api_get_path(WEB_PATH));

    // The host must be the same.
    if ($url_info['host'] == $real_url_info['host']) {
    	$url = Security::remove_XSS($src);    
    	header("Location: ".$url);
    	exit;
    } else {
        header("Location: blank.php?error=document_not_found");
        exit;
    }
}

$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : 'fullpage';

/* INIT SECTION */

$_SESSION['whereami'] = 'lp/build';
if (isset($_SESSION['oLP']) && isset($_GET['id'])) {
    $_SESSION['oLP'] -> current = intval($_GET['id']);
}
$this_section = SECTION_COURSES;

$language_file = "learnpath";

/* Header and action code */
/* Constants and variables */
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);

$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
$tbl_lp_view = Database::get_course_table(TABLE_LP_VIEW);

$isStudentView  = (empty($_REQUEST['isStudentView']) ? 0 : (int) $_REQUEST['isStudentView']);
$learnpath_id   = (int) $_REQUEST['lp_id'];

// Using the resource linker as a tool for adding resources to the learning path.
if ($action == 'add' && $type == 'learnpathitem') {
     $htmlHeadXtra[] = "<script> window.location=\"../resourcelinker/resourcelinker.php?source_id=5&action=$action&learnpath_id=$learnpath_id&chapter_id=$chapter_id&originalresource=no\"; </script>";
}
if ((!$is_allowed_to_edit) || ($isStudentView)) {
    error_log('New LP - User not authorized in lp_view_item.php');
    header('location:lp_controller.php?action=view&lp_id='.$learnpath_id);
    exit;
}
// From here on, we are admin because of the previous condition, so don't check anymore.

$course_id = api_get_course_int_id();
$sql_query = "SELECT * FROM $tbl_lp WHERE c_id = $course_id AND id = $learnpath_id";
$result=Database::query($sql_query);
$therow=Database::fetch_array($result);

/* SHOWING THE ADMIN TOOLS	*/

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

$interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
$interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=$learnpath_id", 'name' => $therow['name']);
$interbreadcrumb[] = array('url' => api_get_self()."?action=add_item&type=step&lp_id=$learnpath_id", 'name' => get_lang('NewStep'));

// Theme calls
$show_learn_path = true;
if (isset($_SESSION['oLP']) && is_object($_SESSION['oLP'])) {
	$lp_theme_css = $_SESSION['oLP']->get_theme();
}

if ($mode == 'fullpage') {
    Display::display_header(get_lang('Item'),'Path');
}

$suredel = trim(get_lang('AreYouSureToDelete'));
?>
<script>
/* <![CDATA[ */

function stripslashes(str) {
    str=str.replace(/\\'/g,'\'');
    str=str.replace(/\\"/g,'"');
    str=str.replace(/\\\\/g,'\\');
    str=str.replace(/\\0/g,'\0');
    return str;
}
function confirmation(name) {
    name=stripslashes(name);
    if (confirm("<?php echo $suredel; ?> " + name + " ?")) {
        return true;
    } else {
        return false;
    }
}
</script>
<?php

$id = (isset($new_item_id)) ? $new_item_id : $_GET['id'];
if (is_object($_SESSION['oLP'])) {
    switch ($mode) {
        case 'fullpage':
            echo $_SESSION['oLP']->build_action_menu();
            echo '<div class="row-fluid">';
            echo '<div class="span3">';
            echo $_SESSION['oLP']->return_new_tree();
            echo '</div>';
            echo '<div class="span9">';
                echo $_SESSION['oLP']->display_item($id);
            echo '</div>';
            echo '</div>';
            Display::display_footer();
            break;
        case 'preview_document':
            echo $_SESSION['oLP']->display_item($id, null, false);
            break; 
    }
	
}
openoffice_document.class.php000064400000020721152003363470012401 0ustar00<?php

/* For licensing terms, see /license.txt */

/**
 * Defines the OpenofficeDocument class, which is meant as a mother class
 * to help in the conversion of Office documents to learning paths
 * @package chamilo.learnpath
 * @author	Eric Marguin <eric.marguin@dokeos.com>
 * @license	GNU/GPL
 */

/**
 * Defines the "OpenofficeDocument" child of class "learnpath"
 */
abstract class OpenofficeDocument extends learnpath {

    public $first_item = 0;
    public $original_charset = 'utf-8';
    public $original_locale = 'en_US.UTF-8';

    /**
     * Class constructor. Based on the parent constructor.
     * @param	string	Course code
     * @param	integer	Learnpath ID in DB
     * @param	integer	User ID
     */
    public function __construct($course_code = null, $resource_id = null, $user_id = null) {
        if ($this->debug > 0) {
            error_log('In OpenofficeDocument::OpenofficeDocument()', 0);
        }
        if (!empty($course_code) && !empty($resource_id) && !empty($user_id)) {
            parent::__construct($course_code, $resource_id, $user_id);
        } else {
            // Do nothing but still build the presentation object.
        }
    }

    public function convert_document($file, $action_after_conversion = 'make_lp') {
        global $_course, $_user, $_configuration;
        $this->file_name = pathinfo($file['name'], PATHINFO_FILENAME);        
        // Create the directory
        $result = $this->generate_lp_folder($_course, $this->file_name);                        
                
         // Create the directory
        $this->base_work_dir = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
        ///learning_path/ppt_dirname directory
        $this->created_dir = substr($result['dir'], 0, strlen($result['dir']) -1);        
        $this->file_path = $this->created_dir.'/'.replace_dangerous_char($file['name'], 'strict');
        
        //var_dump($this->file_name, $this->file_path, $this->base_work_dir, $this->created_dir);
    
        /*
         * Original code
        global $_course, $_user, $_configuration;

        $this->file_name = (strrpos($file['name'], '.') > 0 ? substr($file['name'], 0, strrpos($file['name'], '.')) : $file['name']);
        $this->file_name = replace_dangerous_char($this->file_name, 'strict');
        $this->file_name = strtolower($this->file_name);

        $visio_dir = ($action_after_conversion == 'add_docs_to_visio') ? VIDEOCONF_UPLOAD_PATH : '';

        $this->file_path = $visio_dir.'/'.$this->file_name.'.'.pathinfo($file['name'], PATHINFO_EXTENSION);

        $dir_name = $visio_dir.'/'.$this->file_name;


        // Create the directory.
        $this->base_work_dir = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';

        $this->created_dir = create_unexisting_directory($_course, $_user['user_id'], api_get_session_id(), 0, 0, $this->base_work_dir, $dir_name);
       
            var_dump($this->file_name, $this->file_path, $this->base_work_dir, $this->created_dir);
        
        */

        $ppt2lp_host = api_get_setting('service_ppt2lp', 'host');

        if ($ppt2lp_host == 'localhost') {
            move_uploaded_file($file['tmp_name'], $this->base_work_dir.'/'.$this->file_path);
            //var_dump( $this->base_work_dir.$this->created_dir.$this->file_path);
            $perm = api_get_setting('permissions_for_new_files');

            if (IS_WINDOWS_OS) { // IS_WINDOWS_OS has been defined in main_api.lib.php
                $converter_path = str_replace('/', '\\', api_get_path(SYS_PATH) . 'main/inc/lib/ppt2png');
                $class_path = $converter_path . ';' . $converter_path . '/jodconverter-2.2.2.jar;' . $converter_path . '/jodconverter-cli-2.2.2.jar';
                //$cmd = 'java -cp "'.$class_path.'" DokeosConverter';
                $cmd = 'java -Dfile.encoding=UTF-8 -cp "' . $class_path . '" DokeosConverter';
            } else {
                $converter_path = api_get_path(SYS_PATH) . 'main/inc/lib/ppt2png';
                //$class_path = '-cp .:jodconverter-2.2.1.jar:jodconverter-cli-2.2.1.jar';
                $class_path = ' -Dfile.encoding=UTF-8 -cp .:jodconverter-2.2.2.jar:jodconverter-cli-2.2.2.jar';
                $cmd = 'cd ' . $converter_path . ' && java ' . $class_path . ' DokeosConverter';
            }

            $cmd .= ' -p ' . api_get_setting('service_ppt2lp', 'port');
            // Call to the function implemented by child.
            $cmd .= $this->add_command_parameters();
            // To allow openoffice to manipulate docs.            
            @chmod($this->base_work_dir, 0777);
            @chmod($this->base_work_dir.$this->created_dir, 0777);
            @chmod($this->base_work_dir.$this->file_path, 0777);

            $locale = $this->original_locale; // TODO: Improve it because we're not sure this locale is present everywhere.
            putenv('LC_ALL=' . $locale);

            $files = array();
            $return = 0;
            $shell = exec($cmd, $files, $return);
            
            if ($return != 0) { // If the java application returns an error code.
                switch ($return) {
                    // Can't connect to openoffice.
                    case 1: $this->error = get_lang('CannotConnectToOpenOffice');
                        break;
                    // Conversion failed in openoffice.
                    case 2: $this->error = get_lang('OogieConversionFailed');
                        break;
                    // Conversion can't be launch because command failed.
                    case 255: $this->error = get_lang('OogieUnknownError');
                        break;
                }
                DocumentManager::delete_document($_course, $this->created_dir, $this->base_work_dir);
                return false;
            }
        } else {
            // get result from webservices
            $result = $this->_get_remote_ppt2lp_files($file);
            $result = unserialize(base64_decode($result));

            // Save remote images to server
            chmod($this->base_work_dir.$this->created_dir, api_get_permissions_for_new_directories());
            if (!empty($result['images'])) {
                foreach ($result['images'] as $image => $img_data) {
                    $image_path = $this->base_work_dir.$this->created_dir;
                    @file_put_contents($image_path . '/' . $image, base64_decode($img_data));
                    @chmod($image_path . '/' . $image, 0777);
                }
            }

            // files info
            $files = $result['files'];
        }
        
        if (!empty($files)) {
            // Create lp
            $this->lp_id = learnpath::add_lp($_course['id'], $this->file_name, '', 'guess', 'manual');

            // Call to the function implemented by child following action_after_conversion parameter.
            switch ($action_after_conversion) {
                case 'make_lp':
                    $this->make_lp($files);
                    break;
                case 'add_docs_to_visio':
                    $this->add_docs_to_visio($files);
                    break;
            }
            chmod($this->base_work_dir, api_get_permissions_for_new_directories());
        }
        return $this->first_item;
    }

    /**
     * Get images files from remote host (with webservices)
     * @param   array current ppt file
     * @return  array images files
     */
    private function _get_remote_ppt2lp_files($file) {
        require_once api_get_path(LIBRARY_PATH) . 'nusoap/nusoap.php';
        // host
        $ppt2lp_host = api_get_setting('service_ppt2lp', 'host');

        // secret key
        $secret_key = sha1(api_get_setting('service_ppt2lp', 'ftp_password'));

        // client
        $client = new nusoap_client($ppt2lp_host, true);

        $result = '';
        $err = $client->getError();
        if (!$err) {

            $file_data = base64_encode(file_get_contents($file['tmp_name']));
            $file_name = $file['name'];
            $service_ppt2lp_size = api_get_setting('service_ppt2lp', 'size');

            $params = array(
                'secret_key' => $secret_key,
                'file_data' => $file_data,
                'file_name' => $file_name,
                'service_ppt2lp_size' => $service_ppt2lp_size,
            );

            $result = $client->call('ws_convert_ppt', array('convert_ppt' => $params));
        } else {
            return false;
        }
        return $result;
    }

    abstract function make_lp();

    abstract function add_docs_to_visio();

    abstract function add_command_parameters();
}
openoffice_presentation.class.php000064400000023127152003363470013301 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Defines the OpenofficeDocument class, which is meant as a conversion
 * tool from Office presentations (.ppt, .sxi, .odp, .pptx) to
 * learning paths
 * @package chamilo.learnpath
 * @author  Eric Marguin <eric.marguin@dokeos.com>
 * @license GNU/GPL
 */

/**
 * Defines the "OpenofficePresentation" child of class "OpenofficeDocument"
 */
require_once 'openoffice_document.class.php';
if (api_get_setting('search_enabled') == 'true') {
    require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
    require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
    require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
}

class OpenofficePresentation extends OpenofficeDocument {

    public $take_slide_name;

    public function __construct($take_slide_name = false, $course_code = null, $resource_id = null, $user_id = null) {
        $this->take_slide_name = $take_slide_name;
        parent::__construct($course_code, $resource_id, $user_id);
    }


    public function make_lp($files = array()) {
        global $_course;
        $previous = 0;
        $i = 0;

        if (!is_dir($this->base_work_dir.$this->created_dir))
            return false;

        foreach ($files as $file) {

            list($slide_name, $file_name, $slide_body) = explode('||', $file); // '||' is used as separator between fields: slide name (with accents) || file name (without accents) || all slide text (to be indexed).

            // Filename is utf8 encoded, but when we decode, some chars are not translated (like quote &rsquo;).
            // so we remove these chars by translating it in htmlentities and the reconvert it in want charset.
            $slide_name = api_htmlentities($slide_name, ENT_COMPAT, $this->original_charset);
            $slide_name = str_replace('&rsquo;', '\'', $slide_name);
            $slide_name = api_convert_encoding($slide_name, api_get_system_encoding(), $this->original_charset);
            $slide_name = api_html_entity_decode($slide_name, ENT_COMPAT, api_get_system_encoding());

            if ($this->take_slide_name === true) {
                $slide_name = str_replace('_', ' ', $slide_name);
                $slide_name = api_ucfirst($slide_name);
            } else {
                $slide_name = 'slide'.str_repeat('0', 2 - strlen($i)).$i;
            }

            $i++;
            // Add the png to documents.
            $document_id = add_document($_course, $this->created_dir.'/'.urlencode($file_name), 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$file_name), $slide_name);
            api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', api_get_user_id(), 0, 0, null, null, api_get_session_id());

            // Generating the thumbnail.
            $image = $this->base_work_dir.$this->created_dir .'/'. $file_name;

            $pattern = '/(\w+)\.png$/';
            $replacement = '${1}_thumb.png';
            $thumb_name = preg_replace($pattern, $replacement, $file_name);
            
            // Calculate thumbnail size.
            $image_size = api_getimagesize($image);
            $width  = $image_size['width'];
            $height = $image_size['height'];
            
            $thumb_width = 300;
            $thumb_height = floor($height * ($thumb_width / $width));
  
            $my_new_image = new Image($image);
            $my_new_image->resize($thumb_width, $thumb_height);
            $my_new_image->send_image($this->base_work_dir.$this->created_dir .'/'. $thumb_name, -1, 'png');
  
            // Adding the thumbnail to documents.
            $document_id_thumb = add_document($_course, $this->created_dir.'/'.urlencode($thumb_name), 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$thumb_name), $slide_name);
            api_item_property_update($_course, TOOL_THUMBNAIL, $document_id_thumb, 'DocumentAdded', api_get_user_id(), 0, 0);

            // Create an html file.
            $html_file = $file_name.'.html';
            $fp = fopen($this->base_work_dir.$this->created_dir.'/'.$html_file, 'w+');

            $slide_src = api_get_path(REL_COURSE_PATH).$_course['path'].'/document/'.$this->created_dir.'/'.utf8_encode($file_name);
            $slide_src = str_replace('//', '/', $slide_src);
            fwrite($fp,
'<html>
    <head>
    </head>
    <body>
        <img src="'.$slide_src.'" />
    </body>
</html>');  // This indentation is to make the generated html files to look well.

            fclose($fp);
            $document_id = add_document($_course,$this->created_dir.'/'.urlencode($html_file), 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$html_file), $slide_name);
            if ($document_id) {

                // Put the document in item_property update.
                api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', api_get_user_id(), 0, 0, null, null, api_get_session_id());

                $previous = $this->add_item(0, $previous, 'document', $document_id, $slide_name, '');
                if ($this->first_item == 0) {
                    $this->first_item = $previous;
                }
            }
            // Code for text indexing.
            if (api_get_setting('search_enabled') == 'true') {

                if (isset($_POST['index_document']) && $_POST['index_document']) {                    
                    $di = new ChamiloIndexer();
                    isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : $lang = 'english';
                    $di->connectDb(NULL, NULL, $lang);
                    $ic_slide = new IndexableChunk();
                    $ic_slide->addValue('title', $slide_name);
                    $specific_fields = get_specific_field_list();
                    $all_specific_terms = '';
                    foreach ($specific_fields as $specific_field) {
                        if (isset($_REQUEST[$specific_field['code']])) {
                            $sterms = trim($_REQUEST[$specific_field['code']]);
                            $all_specific_terms .= ' '. $sterms;
                            if (!empty($sterms)) {
                                $sterms = explode(',', $sterms);
                                foreach ($sterms as $sterm) {
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
                                }
                            }
                        }
                    }
                    $slide_body = $all_specific_terms .' '. $slide_body;
                    $ic_slide->addValue('content', $slide_body);
                    /* FIXME:  cidReq:lp_id:doc_id al indexar  */
                    // Add a comment to say terms separated by commas.
                    $courseid = api_get_course_id();
                    $ic_slide->addCourseId($courseid);
                    $ic_slide->addToolId(TOOL_LEARNPATH);
                    $lp_id = $this->lp_id;
                    $xapian_data = array(
                        SE_COURSE_ID => $courseid,
                        SE_TOOL_ID => TOOL_LEARNPATH,
                        SE_DATA => array('lp_id' => $lp_id, 'lp_item' => $previous, 'document_id' => $document_id),
                        SE_USER => (int)api_get_user_id(),
                    );
                    $ic_slide->xapian_data = serialize($xapian_data);
                    $di->addChunk($ic_slide);
                    // Index and return search engine document id.
                    $did = $di->index();
                    if ($did) {
                        // Save it to db.
                        $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
                        $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did)
                            VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)';
                        $sql = sprintf($sql, $tbl_se_ref, api_get_course_id(), TOOL_LEARNPATH, $lp_id, $previous, $did);
                        Database::query($sql);
                    }
                }
            }
        }
    }

    function add_command_parameters() {
        if (empty($this->slide_width) || empty($this->slide_height))
            list($this->slide_width, $this->slide_height) = explode('x', api_get_setting('service_ppt2lp', 'size'));
        return ' -w '.$this->slide_width.' -h '.$this->slide_height.' -d oogie "'.$this->base_work_dir.'/'.$this->file_path.'"  "'.$this->base_work_dir.$this->created_dir.'.html"';
    }

    function set_slide_size($width, $height) {
        $this->slide_width = $width;
        $this->slide_height = $height;
    }

    function add_docs_to_visio($files = array()) {
        global $_course;
        foreach ($files as $file) {
            list($slide_name,$file_name) = explode('||',$file); // '||' is used as separator between slide name (with accents) and file name (without accents).
            $slide_name = api_htmlentities($slide_name, ENT_COMPAT, $this->original_charset);
            $slide_name = str_replace('&rsquo;', '\'', $slide_name);
            $slide_name = api_convert_encoding($slide_name, api_get_system_encoding(), $this->original_charset);
            $slide_name = api_html_entity_decode($slide_name, ENT_COMPAT, api_get_system_encoding());
            $did = add_document($_course, $this->created_dir.'/'.urlencode($file_name), 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$file_name), $slide_name);
            if ($did) {
                api_item_property_update($_course, TOOL_DOCUMENT, $did, 'DocumentAdded', $_SESSION['_uid'], 0, null, null, null, api_get_session_id());
            }
        }
    }
}
openoffice_text.class.php000064400000033426152003363470011555 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Defines the OpenofficeDocument class, which is meant as a conversion
 * tool from Office text documents (.doc, .sxw, .odt, .docx) to
 * learning paths
 * @package chamilo.learnpath
 * @author  Eric Marguin <eric.marguin@dokeos.com>
 * @license GNU/GPL
 */

/**
 * Defines the "OpenofficeText" child of class "learnpath"
 */
require_once 'openoffice_document.class.php';
if (api_get_setting('search_enabled') == 'true') {
    require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
    require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
    require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
}
/**
 * @package chamilo.learnpath.OpenofficeDocument
 */
class OpenofficeText extends OpenofficeDocument {

    public $split_steps;

    /**
     * Class constructor. Calls the parent class and initialises the local attribute split_steps
     * @param	boolean	Whether to split steps (true) or make one large page (false)
     * @param	string	Course code
     * @param	integer	Resource ID
     * @param	integer Creator user id
     * @return	void
     */
    function OpenofficeText($split_steps = false, $course_code = null, $resource_id = null, $user_id = null) {
        $this -> split_steps = $split_steps;
        parent::__construct($course_code, $resource_id, $user_id);
    }

    /**
     * Gets html pages and compose them into a learning path
     * @param	array	The files that will compose the generated learning path. Unused so far.
     * @return	boolean	False if file does not exit. Nothing otherwise.
     */
    function make_lp($files = array()) {

        global $_course;
        // We get a content where ||page_break|| indicates where the page is broken.
        if (!file_exists($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html')) { return false; }
        $content = file_get_contents($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html');

        unlink($this->base_work_dir.'/'.$this->file_path);
        unlink($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html');

        // The file is utf8 encoded and it seems to make problems with special quotes.
        // Then we htmlentities that, we replace these quotes and html_entity_decode that in good charset.
        $charset = api_get_system_encoding();
        $content = api_htmlentities($content, ENT_COMPAT, $this->original_charset);
        $content = str_replace('&rsquo;', '\'', $content);
        $content = api_convert_encoding($content, $charset, $this->original_charset);
        $content = str_replace($this->original_charset, $charset, $content);
        $content = api_html_entity_decode($content, ENT_COMPAT, $charset);

        // Set the path to pictures to absolute (so that it can be modified in fckeditor).
        $content = preg_replace("|src=\"([^\"]*)|i", "src=\"".api_get_path(REL_COURSE_PATH).$_course['path'].'/document'.$this->created_dir."/\\1", $content);

        list($header, $body) = explode('<BODY', $content);

        $body = '<BODY'.$body;

        // Remove font-family styles.
        $header = preg_replace("|font\-family[^;]*;|i", '', $header);

        // Chamilo styles.
        $my_style = api_get_setting('stylesheets');
        if (empty($my_style)) { $my_style = 'chamilo'; }
        $style_to_import = "<style type=\"text/css\">\r\n";
        $style_to_import .= '@import "'.api_get_path(WEB_CODE_PATH).'css/'.$my_style.'/default.css";'."\n";        
        $style_to_import .= "</style>\r\n";
        $header = preg_replace("|</head>|i", "\r\n$style_to_import\r\n\\0", $header);

        // Line break before and after picture.
        $header = str_replace('p {', 'p {clear:both;', $header);

        $header = str_replace('absolute', 'relative', $header);

        switch ($this->split_steps) {
            case 'per_page': $this -> dealPerPage($header, $body); break;
            case 'per_chapter': $this -> dealPerChapter($header, $body); break;
        }
    }

    /**
     * Manages chapter splitting
     * @param	string	Chapter header
     * @param	string	Content
     * @return	void
     */
    function dealPerChapter($header, $content) {

        global $_course;

        $content = str_replace('||page_break||', '', $content);

        // Get all the h1.
        preg_match_all("|<h1[^>]*>([^(h1)+]*)</h1>|is", $content, $matches_temp);

        // Empty the fake chapters.
        $new_index = 0;
        for ($i = 0; $i < count($matches_temp[0]); $i++) {

            if (trim($matches_temp[1][$i]) !== '') {
                $matches[0][$new_index] = $matches_temp[0][$i];
                $matches[1][$new_index] = $matches_temp[1][$i];
                $new_index++;
            }

        }

        // Add intro item.
        $intro_content = substr($content, 0, strpos($content, $matches[0][0]));
        $items_to_create[get_lang('Introduction')] = $intro_content;


        for ($i = 0; $i < count($matches[0]); $i++) {

            if (empty($matches[1][$i]))
                continue;

            $content = strstr($content,$matches[0][$i]);
            if ($i + 1 !== count($matches[0])) {
                $chapter_content = substr($content, 0, strpos($content, $matches[0][$i + 1]));
            } else {
                $chapter_content = $content;
            }
            $items_to_create[$matches[1][$i]] = $chapter_content;

        }

        $i = 0;
        foreach ($items_to_create as $item_title => $item_content) {
            $i++;
            $page_content = $this->format_page_content($header, $item_content);

            $html_file = $this->created_dir.'-'.$i.'.html';
            $handle = fopen($this->base_work_dir.$this->created_dir.'/'.$html_file, 'w+');
            fwrite($handle, $page_content);
            fclose($handle);

            $document_id = add_document($_course, $this->created_dir.'/'.$html_file, 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$html_file), $html_file);

            if ($document_id){

                // Put the document in item_property update.
                api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $_SESSION['_uid'], 0, 0, null, null, api_get_session_id());

                $infos = pathinfo($this->filepath);
                $slide_name = strip_tags(nl2br($item_title));
                $slide_name = str_replace(array("\r\n", "\r", "\n"), '', $slide_name);
                $slide_name = html_entity_decode($slide_name);
                $previous = learnpath::add_item(0, $previous, 'document', $document_id, $slide_name, '');
                if ($this->first_item == 0) {
                    $this->first_item = $previous;
                }
            }
        }
    }

    /**
     * Manages page splitting
     * @param	string	Page header
     * @param	string	Page body
     * @return	void
     */
    function dealPerPage($header, $body) {
        global $_course;
        // Split document to pages.
        $pages = explode('||page_break||', $body);

        $first_item = 0;

        foreach ($pages as $key => $page_content) {
            // For every pages, we create a new file.

            $key += 1;

            $page_content = $this->format_page_content($header, $page_content, $this->base_work_dir.$this->created_dir);
            $html_file = $this->created_dir.'-'.$key.'.html';
            $handle = fopen($this->base_work_dir.$this->created_dir.'/'.$html_file, 'w+');
            fwrite($handle, $page_content);
            fclose($handle);

            $document_id = add_document($_course, $this->created_dir.$html_file, 'file', filesize($this->base_work_dir.$this->created_dir.$html_file), $html_file);

            $slide_name = '';

            if ($document_id) {

                // Put the document in item_property update.
                api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $_SESSION['_uid'], 0, 0, null, null, api_get_session_id());

                $infos = pathinfo($this->filepath);
                $slide_name = 'Page '.str_repeat('0', 2 - strlen($key)).$key;
                $previous = learnpath::add_item(0, $previous, 'document', $document_id, $slide_name, '');
                if ($this->first_item == 0) {
                    $this->first_item = $previous;
                }
                // Code for text indexing.
                if (api_get_setting('search_enabled') == 'true') {
                    if (isset($_POST['index_document']) && $_POST['index_document']) {
                        //Display::display_normal_message(print_r($_POST));
                        $di = new ChamiloIndexer();
                        isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : $lang = 'english';
                        $di->connectDb(NULL, NULL, $lang);
                        $ic_slide = new IndexableChunk();
                        $ic_slide->addValue('title', $slide_name);
                        $specific_fields = get_specific_field_list();
                        $all_specific_terms = '';
                        foreach ($specific_fields as $specific_field) {
                            if (isset($_REQUEST[$specific_field['code']])) {
                                $sterms = trim($_REQUEST[$specific_field['code']]);
                                $all_specific_terms .= ' '. $sterms;
                                if (!empty($sterms)) {
                                    $sterms = explode(',', $sterms);
                                    foreach ($sterms as $sterm) {
                                        $ic_slide->addTerm(trim($sterm), $specific_field['code']);
                                    }
                                }
                            }
                        }
                        $page_content = $all_specific_terms .' '. $page_content;
                        $ic_slide->addValue('content', $page_content);
                        // Add a comment to say terms separated by commas.
                        $courseid=api_get_course_id();
                        $ic_slide->addCourseId($courseid);
                        $ic_slide->addToolId(TOOL_LEARNPATH);
                        $lp_id = $this->lp_id;
                        $xapian_data = array(
                            SE_COURSE_ID => $courseid,
                            SE_TOOL_ID => TOOL_LEARNPATH,
                            SE_DATA => array('lp_id' => $lp_id, 'lp_item'=> $previous, 'document_id' => $document_id),
                            SE_USER => (int)api_get_user_id(),
                        );
                        $ic_slide->xapian_data = serialize($xapian_data);
                        $di->addChunk($ic_slide);
                        // Index and return search engine document id.
                        $did = $di->index();
                        if ($did) {
                            // Save it to db.
                            $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
                            $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did)
                                    VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)';
                            $sql = sprintf($sql, $tbl_se_ref, api_get_course_id(), TOOL_LEARNPATH, $lp_id, $previous, $did);
                            Database::query($sql);
                        }
                    }
                }
            }
        }
    }

    /**
     * Returns additional Java command parameters
     * @return	string	The additional parameters to be used in the Java call
     */
    function add_command_parameters(){
        return ' -d woogie "'.$this->base_work_dir.'/'.$this->file_path.'"  "'.$this->base_work_dir.$this->created_dir.'/'.$this->file_name.'.html"';
    }

    /**
     * Formats a page content by reorganising the HTML code a little
     * @param	string	Page header
     * @param	string	Page content
     * @return	string	Formatted page content
     */
    function format_page_content($header, $content) {
        // Limit the width of the doc.
        list($max_width, $max_height) = explode('x',api_get_setting('service_ppt2lp','size'));

        $content = preg_replace("|<body[^>]*>|i", "\\0\r\n<div style=\"width:".$max_width."\">", $content, -1, $count);
        if ($count < 1) {
            $content = '<body><div style="width:'.$max_width.'">'.$content;
        }

        $content = preg_replace('|</body>|i', '</div>\\0', $content, -1, $count);
        if ($count < 1) {
            $content = $content.'</div></body>';
        }

        // Add the headers.
        $content = $header.$content;

        // Resize all the picture to the max_width-10
        preg_match_all("|<img[^src]*src=\"([^\"]*)\"[^>]*>|i", $content, $images);

        foreach ($images[1] as $key => $image) {
            // Check if the <img tag soon has a width attribute.
            $defined_width = preg_match("|width=([^\s]*)|i", $images[0][$key], $img_width);
            $img_width = $img_width[1];
            if (!$defined_width) {

                list($img_width, $img_height, $type) = getimagesize($this->base_work_dir.$this->created_dir.'/'.$image);

                $new_width = $max_width - 10;
                if ($img_width > $new_width) {
                    $picture_resized = str_ireplace('<img', '<img width="'.$new_width.'" ', $images[0][$key]);
                    $content = str_replace($images[0][$key], $picture_resized, $content);
                }

            } elseif ($img_width > $max_width - 10) {
                $picture_resized = str_ireplace('width='.$img_width, 'width="'.($max_width-10).'"', $images[0][$key]);
                $content = str_replace($images[0][$key], $picture_resized, $content);
            }
        }

        return $content;
    }

    /**
     * Add documents to the visioconference (to be implemented)
     */
    function add_docs_to_visio() {

    }
}
openoffice_text_document.class.php000064400000033162152003363470013450 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Defines the OpenOfficeDocument class, which is meant as a conversion
 * tool from Office text documents (.doc, .sxw, .odt, .docx) to
 * learning paths
 * @package chamilo.learnpath
 * @author  Eric Marguin <eric.marguin@dokeos.com>
 * @license GNU/GPL
 */
/**
 * Defines the "OpenOfficeTextDocument" child of class "learnpath"
 */
require_once 'openoffice_document.class.php';
require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';
require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
/**
 * @package chamilo.learnpath.openofficedocument
 */
class OpenOfficeTextDocument extends OpenofficeDocument {

    public $split_steps;

    /**
     * Class constructor. Calls the parent class and initialises the local attribute split_steps
     * @param	boolean	Whether to split steps (true) or make one large page (false)
     * @param	string	Course code
     * @param	integer	Resource ID
     * @param	integer Creator user id
     * @return	void
     */
    public function __construct($split_steps = false, $course_code = null, $resource_id = null, $user_id = null) {
        $this -> split_steps = $split_steps;
        parent::__construct($course_code, $resource_id, $user_id);
    }

    /**
     * Gets html pages and compose them into a learning path
     * @param	array	The files that will compose the generated learning path. Unused so far.
     * @return	boolean	False if file does not exit. Nothing otherwise.
     */
    public function make_lp($files = array()) {

        global $_course;
        // We get a content where ||page_break|| indicates where the page is broken.
        if (!file_exists($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html')) { return false; }
        $content = file_get_contents($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html');

        unlink($this->base_work_dir.'/'.$this->file_path);
        unlink($this->base_work_dir.'/'.$this->created_dir.'/'.$this->file_name.'.html');

        // The file is utf8 encoded and it seems to make problems with special quotes.
        // then we htmlentities that, we replace these quotes and html_entity_decode that in good charset.
        $charset = api_get_system_encoding();
        $content = api_htmlentities($content, ENT_COMPAT, $this->original_charset);
        $content = str_replace('&rsquo;', '\'', $content);
        $content = api_convert_encoding($content, $charset, $this->original_charset);
        $content = str_replace($this->original_charset, $charset, $content);
        $content = api_html_entity_decode($content, ENT_COMPAT, $charset);

        // Set the path to pictures to absolute (so that it can be modified in fckeditor).
        $content = preg_replace("|src=\"([^\"]*)|i", "src=\"".api_get_path(REL_COURSE_PATH).$_course['path'].'/document'.$this->created_dir."/\\1", $content);

        list($header, $body) = explode('<BODY', $content);

        $body = '<BODY'.$body;

        // Remove font-family styles.
        $header = preg_replace("|font\-family[^;]*;|i", '', $header);

        // Chamilo styles.
        $my_style = api_get_setting('stylesheets');
        if (empty($my_style)) { $my_style = 'chamilo'; }
        $style_to_import = "<style type=\"text/css\">\r\n";
        $style_to_import .= '@import "'.api_get_path(WEB_CODE_PATH).'css/'.$my_style.'/default.css";'."\n";        
        $style_to_import .= "</style>\r\n";
        $header = preg_replace("|</head>|i", "\r\n$style_to_import\r\n\\0", $header);

        // Line break before and after picture.
        $header = str_replace('p {', 'p {clear:both;', $header);

        $header = str_replace('absolute', 'relative', $header);

        switch ($this->split_steps) {
            case 'per_page': $this -> dealPerPage($header, $body); break;
            case 'per_chapter': $this -> dealPerChapter($header, $body); break;
        }
    }

    /**
     * Manages chapter splitting
     * @param	string	Chapter header
     * @param	string	Content
     * @return	void
     */
    function dealPerChapter($header, $content) {

        global $_course;

        $content = str_replace('||page_break||', '', $content);

        // Get all the h1.
        preg_match_all("|<h1[^>]*>([^(h1)+]*)</h1>|is", $content, $matches_temp);

        // Empty the fake chapters.
        $new_index = 0;
        for ($i = 0; $i < count($matches_temp[0]); $i++) {
            if (trim($matches_temp[1][$i]) !== '') {
                $matches[0][$new_index] = $matches_temp[0][$i];
                $matches[1][$new_index] = $matches_temp[1][$i];
                $new_index++;
            }
        }

        // Add intro item.
        $intro_content = api_substr($content, 0, api_strpos($content, $matches[0][0]));
        $items_to_create[get_lang('Introduction')] = $intro_content;

        for ($i = 0; $i<count($matches[0]); $i++) {

            if (empty($matches[1][$i]))
                continue;

            $content = api_strstr($content, $matches[0][$i]);
            if ($i + 1 !== count($matches[0])) {
                $chapter_content = api_substr($content, 0, api_strpos($content, $matches[0][$i + 1]));
            } else {
                $chapter_content = $content;
            }
            $items_to_create[$matches[1][$i]] = $chapter_content;
        }

        $i = 0;
        foreach ($items_to_create as $item_title => $item_content) {
            $i++;
            $page_content = $this->format_page_content($header, $item_content);

            $html_file = $this->created_dir.'-'.$i.'.html';
            $handle = fopen($this->base_work_dir.$this->created_dir.'/'.$html_file, 'w+');
            fwrite($handle, $page_content);
            fclose($handle);

            $document_id = add_document($_course, $this->created_dir.'/'.$html_file, 'file', filesize($this->base_work_dir.$this->created_dir.'/'.$html_file), $html_file);

            if ($document_id) {

                // Put the document in item_property update.
                api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $_SESSION['_uid'], 0, 0, null, null, api_get_session_id());

                $infos = pathinfo($this->filepath);
                $slide_name = strip_tags(nl2br($item_title));
                $slide_name = str_replace(array("\r\n", "\r", "\n"), '', $slide_name);
                $slide_name = api_html_entity_decode($slide_name, ENT_COMPAT, api_get_system_encoding());
                $previous = learnpath::add_item(0, $previous, 'document', $document_id, $slide_name, '');
                if ($this->first_item == 0) {
                    $this->first_item = $previous;
                }
            }
        }
    }

    /**
     * Manages page splitting
     * @param	string	Page header
     * @param	string	Page body
     * @return	void
     */
    function dealPerPage($header, $body) {
        global $_course;
        // Split document to pages.
        $pages = explode('||page_break||', $body);

        $first_item = 0;

        foreach($pages as $key => $page_content) {
            // For every pages, we create a new file.

            $key += 1;

            $page_content = $this->format_page_content($header, $page_content, $this->base_work_dir.$this->created_dir);
            $html_file = $this->created_dir.'-'.$key.'.html';
            $handle = fopen($this->base_work_dir.$this->created_dir.'/'.$html_file, 'w+');
            fwrite($handle, $page_content);
            fclose($handle);

            $document_id = add_document($_course, $this->created_dir.$html_file, 'file', filesize($this->base_work_dir.$this->created_dir.$html_file), $html_file);

            $slide_name = '';

            if ($document_id) {

                // Put the document in item_property update.
                api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $_SESSION['_uid'], 0, 0, null, null, api_get_session_id());

                $infos = pathinfo($this->filepath);
                $slide_name = 'Page '.str_repeat('0', 2 - strlen($key)).$key;
                $previous = learnpath::add_item(0, $previous, 'document', $document_id, $slide_name, '');
                if ($this->first_item == 0) {
                    $this->first_item = $previous;
                }
                // Code for text indexing.
                if (isset($_POST['index_document']) && $_POST['index_document']) {
                    //Display::display_normal_message(print_r($_POST));
                    $di = new ChamiloIndexer();
                    isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : $lang = 'english';
                    $di->connectDb(NULL, NULL, $lang);
                    $ic_slide = new IndexableChunk();
                    $ic_slide->addValue('title', $slide_name);
                    $specific_fields = get_specific_field_list();
                    $all_specific_terms = '';
                    foreach ($specific_fields as $specific_field) {
                        if (isset($_REQUEST[$specific_field['code']])) {
                            $sterms = trim($_REQUEST[$specific_field['code']]);
                            $all_specific_terms .= ' '. $sterms;
                            if (!empty($sterms)) {
                                $sterms = explode(',', $sterms);
                                foreach ($sterms as $sterm) {
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
                                }
                            }
                        }
                    }
                    $page_content = $all_specific_terms .' '. $page_content;
                    $ic_slide->addValue('content', $page_content);
                    // Add a comment to say terms separated by commas.
                    $courseid = api_get_course_id();
                    $ic_slide->addCourseId($courseid);
                    $ic_slide->addToolId(TOOL_LEARNPATH);
                    $lp_id = $this->lp_id;
                    $xapian_data = array(
                        SE_COURSE_ID => $courseid,
                        SE_TOOL_ID => TOOL_LEARNPATH,
                        SE_DATA => array('lp_id' => $lp_id, 'lp_item' => $previous, 'document_id' => $document_id),
                        SE_USER => (int)api_get_user_id(),
                    );
                    $ic_slide->xapian_data = serialize($xapian_data);
                    $di->addChunk($ic_slide);
                    // Index and return search engine document id.
                    $did = $di->index();
                    if ($did) {
                        // Save it to db.
                        $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
                        $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did)
                                VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)';
                        $sql = sprintf($sql, $tbl_se_ref, api_get_course_id(), TOOL_LEARNPATH, $lp_id, $previous, $did);
                        Database::query($sql);
                    }
                }
            }
        }
    }

    /**
     * Returns additional Java command parameters
     * @return	string	The additional parameters to be used in the Java call
     */
    function add_command_parameters() {
        return ' -d woogie "'.$this->base_work_dir.'/'.$this->file_path.'"  "'.$this->base_work_dir.$this->created_dir.'/'.$this->file_name.'.html"';
    }

    /**
     * Formats a page content by reorganising the HTML code a little
     * @param	string	Page header
     * @param	string	Page content
     * @return	string	Formatted page content
     */
    function format_page_content($header, $content) {

        // Limit the width of the doc.
        list($max_width, $max_height) = explode('x', api_get_setting('service_ppt2lp', 'size'));

        $content = preg_replace("|<body[^>]*>|i", "\\0\r\n<div style=\"width:".$max_width."\">", $content, -1, $count);
        if ($count < 1) {
            $content = '<body><div style="width:'.$max_width.'">'.$content;
        }

        $content = preg_replace('|</body>|i','</div>\\0', $content, -1, $count);
        if ($count < 1) {
            $content = $content.'</div></body>';
        }

        // Add the headers.
        $content = $header.$content;

        // Resize all the picture to the max_width-10
        preg_match_all("|<img[^src]*src=\"([^\"]*)\"[^>]*>|i", $content, $images);

        foreach ($images[1] as $key => $image) {
            // Check if the <img tag soon has a width attribute.
            $defined_width = preg_match("|width=([^\s]*)|i", $images[0][$key], $img_width);
            $img_width = $img_width[1];
            if (!$defined_width) {

                $image_size = api_getimagesize($this->base_work_dir.$this->created_dir.'/'.$image);
                $img_width  = $image_size['width']; 
                $img_height = $image_size['height'];

                $new_width = $max_width - 10;
                if ($img_width > $new_width) {
                    $picture_resized = str_ireplace('<img', '<img width="'.$new_width.'" ', $images[0][$key]);
                    $content = str_replace($images[0][$key], $picture_resized, $content);
                }

            } elseif ($img_width > $max_width - 10) {
                $picture_resized = str_ireplace('width='.$img_width, 'width="'.($max_width-10).'"', $images[0][$key]);
                $content = str_replace($images[0][$key], $picture_resized, $content);
            }
        }

        return $content;
    }

    /**
     * Add documents to the visioconference (to be implemented)
     */
    function add_docs_to_visio() {

    }
}
js/documentapi.js000064400000001067152003363470010035 0ustar00// JS interface enabling scrom content to use main/document/remote.php easily
// CBlue SPRL, Arnaud Ligot <arnaud@cblue.be>


lms_documents_list = function(path) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		datatype: "json",
		url: "../document/remote.php",
		data: {
			action: "list",
			cwd: path,
			cidReq: chamilo_courseCode,
		},
		success: function(data) {
			result = eval("("+data+")");
		}
	});
	return result;
}

// Accessor object
function DOCUMENTAPIobject() {
	this.list = lms_documents_list;
}
var DOCUMENTAPI = new DOCUMENTAPIobject();
js/api_wrapper.js000064400000060335152003363470010041 0ustar00/**
 * Wrapper to the SCORM API provided by Chamilo
 * The complete set of functions and variables are in this file to avoid unnecessary file
 * accesses.
 * Only event triggers and answer data are inserted into the final document.
 * @author	Yannick Warnier  - inspired by the ADLNet documentation on SCORM content-side API
 * @package scorm.js
 */
/**
 * Initialisation of the SCORM API section.
 * Find the SCO functions (startTimer, computeTime, etc in the second section)
 * Find the Chamilo-proper functions (checkAnswers, etc in the third section)
 */
var _debug = false;
var findAPITries = 0;
var _apiHandle = null; //private variable
var errMsgLocate = "Unable to locate the LMS's API implementation";

var _NoError = 0;
var _GeneralException = 101;
var _ServerBusy = 102;
var _InvalidArgumentError = 201;
var _ElementCannotHaveChildren = 202;
var _ElementIsNotAnArray = 203;
var _NotInitialized = 301;
var _NotImplementedError = 401;
var _InvalidSetValue = 402;
var _ElementIsReadOnly = 403;
var _ElementIsWriteOnly = 404;
var _IncorrectDataType = 405;
/**
 * Gets the API handle right into the local API object and ensure there is only one.
 * Using the singleton pattern to ensure there's only one API object.
 * @return	object The API object as given by the LMS
 */
var API = new function ()
{
    if (_apiHandle == null)
    {
        _apiHandle = getAPI();
    }
    return _apiHandle;
}

/**
 * Finds the API on the LMS side or gives up giving an error message
 * @param	object	The window/frame object in which we are searching for the SCORM API
 * @return	object	The API object recovered from the LMS's implementation of the SCORM API
 */
function findAPI(win)
{
    while((win.API == null) && (win.parent != null) && (win.parent != win))
    {
        findAPITries++;
        if(findAPITries>10)
        {
            alert("Error finding API - too deeply nested");
            return null;
        }
        win = win.parent
    }
    return win.API;
}
/**
 * Gets the API from the current window/frame or from parent objects if not found
 * @return	object	The API object recovered from the LMS's implementation of the SCORM API
 */
function getAPI()
{
    //window is the global/root object of the current window/frame
    var MyAPI = findAPI(window);
    //look through parents if any
    if((MyAPI == null) && (window.opener != null) && (typeof(window.opener) != "undefined"))
    {
        MyAPI = findAPI(window.opener);
    }
    //still not found? error message
    if(MyAPI == null)
    {
        alert("Unable to find SCORM API adapter.\nPlease check your LMS is considering this page as SCORM and providing the right JavaScript interface.")
    }
    return MyAPI;
}
/**
 * Handles error codes (prints the error if it has a description)
 * @return	int	Error code from LMS's API
 */
function ErrorHandler()
{
    if(API == null)
    {
        alert("Unable to locate the LMS's API. Cannot determine LMS error code");
        return;
    }
    var errCode = API.LMSGetLastError().toString();
    if(errCode != _NoError)
    {
        if(errCode == _NotImplementedError)
        {
            var errDescription = "The LMS doesn't support this feature";
            if(_debug)
            {
                errDescription += "\n";
                errDescription += api.LMSGetDiagnostic(null);
            }
            alert (errDescription);
        }
        else
        {
            var errDescription = API.LMSGetErrorString(errCode);
            if(_debug)
            {
                errDescription += "\n";
                errDescription += api.LMSGetDiagnostic(null);
            }
            alert (errDescription);
        }
    }
    return errCode;
}
/**
 * Calls the LMSInitialize method of the LMS's API object
 * @return string	The string value of the LMS returned value or false if error (should be "true" otherwise)
 */
function doLMSInitialize()
{
    if(API == null)
    {
        alert(errMsgLocate + "\nLMSInitialize failed");
        return false;
    }
    var result = API.LMSInitialize("");
    if(result.toString() != "true")
    {
        var err = ErrorHandler();
    }
    return result.toString();
}
/**
 * Calls the LMSFinish method of the LMS's API object
 * @return	string	The string value of the LMS return value, or false if error (should be "true" otherwise)
 */
function doLMSFinish()
{
    if(API == null)
    {
        alert(errMsgLocate + "\nLMSFinish failed");
        return false;
    }
    else
    {
        var result = API.LMSFinish('');
        if(result.toString() != "true")
        {
            var err = ErrorHandler();
        }
    }
    return result.toString();
}
/**
 * Calls the LMSGetValue method
 * @param	string	The name of the SCORM parameter to get
 * @return	string	The value returned by the LMS
 */
function doLMSGetValue(name)
{
    if (API == null)
    {
        alert(errMsgLocate + "\nLMSGetValue was not successful.");
        return "";
    }
    else
    {
        var value = API.LMSGetValue(name);
        var errCode = API.LMSGetLastError().toString();
        if (errCode != _NoError)
        {
            // an error was encountered so display the error description
            var errDescription = API.LMSGetErrorString(errCode);
            alert("LMSGetValue("+name+") failed. \n"+ errDescription);
            return "";
        }
        else
        {
            return value.toString();
        }
    }
}
/**
 * Calls the LMSSetValue method of the API object
 * @param	string	The name of the SCORM parameter to set
 * @param	string	The value to set the parameter to
 * @return  void
 */
function doLMSSetValue(name, value)
{
   if (API == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSSetValue was not successful.");
      return;
   }
   else
   {
      var result = API.LMSSetValue(name, value);
      if (result.toString() != "true")
      {
         var err = ErrorHandler();
      }
   }
   return;
}
/**
 * Calls the LMSCommit method
 */
function doLMSCommit()
{
    if(API == null)
    {
        alert(errMsgLocate +"\nLMSCommit was not successful.");
        return "false";
    }
    else
    {
        var result = API.LMSCommit("");
        if (result != "true")
        {
            var err = ErrorHandler();
        }
    }
    return result.toString();
}
/**
 * Calls GetLastError()
 */
function doLMSGetLastError()
{
    if (API == null)
    {
        alert(errMsgLocate + "\nLMSGetLastError was not successful.");      //since we can't get the error code from the LMS, return a general error
        return _GeneralError;
    }
    return API.LMSGetLastError().toString();
}
/**
 * Calls LMSGetErrorString()
 */
function doLMSGetErrorString(errorCode)
{
   if (API == null)
   {
      alert(errMsgLocate + "\nLMSGetErrorString was not successful.");
   }

   return API.LMSGetErrorString(errorCode).toString();
}
/**
 * Calls LMSGetDiagnostic()
 */
function doLMSGetDiagnostic(errorCode)
{
   if (API == null)
   {
      alert(errMsgLocate + "\nLMSGetDiagnostic was not successful.");
   }

   return API.LMSGetDiagnostic(errorCode).toString();
}

/**
 * Second section. The SCO functions are located here (handle time and score messaging to SCORM API)
 * Initialisation
 */
var startTime;
var exitPageStatus;
/**
 * Initialise page values
 */
function loadPage()
{
    var result = doLMSInitialize();
    if(result)
    {
        var status = doLMSGetValue("cmi.core.lesson_status");
        if(status == "not attempted")
        {
            doLMSSetValue("cmi.core.lesson_status","incomplete");
        }
        exitPageStatus = false;
        startTimer();
    }
}
/**
 * Starts the local timer
 */
function startTimer()
{
    startTime = new Date().getTime();
}
/**
 * Calculates the total time and sends the result to the LMS
 */
function computeTime()
{
       if ( startTime != 0 )
       {
          var currentDate = new Date().getTime();
          var elapsedSeconds = ( (currentDate - startTime) / 1000 );
          var formattedTime = convertTotalSeconds( elapsedSeconds );
       }
       else
       {
          formattedTime = "00:00:00.0";
       }

       doLMSSetValue( "cmi.core.session_time", formattedTime );
}
/**
 * Formats the time in a SCORM time format
 */
function convertTotalSeconds(ts)
{
    var sec = (ts % 60);
    ts -= sec;
    var tmp = (ts % 3600);  //# of seconds in the total # of minutes
    ts -= tmp;              //# of seconds in the total # of hours

    // convert seconds to conform to CMITimespan type (e.g. SS.00)
    sec = Math.round(sec*100)/100;
    var strSec = new String(sec);
    var strWholeSec = strSec;
    var strFractionSec = "";

    if (strSec.indexOf(".") != -1)
    {
        strWholeSec =  strSec.substring(0, strSec.indexOf("."));
        strFractionSec = strSec.substring(strSec.indexOf(".")+1, strSec.length);
    }
    if (strWholeSec.length < 2)
    {
        strWholeSec = "0" + strWholeSec;
    }
    strSec = strWholeSec;
    if (strFractionSec.length)
    {
        strSec = strSec+ "." + strFractionSec;
    }
    if ((ts % 3600) != 0 )
        var hour = 0;
    else var hour = (ts / 3600);
    if ( (tmp % 60) != 0 )
        var min = 0;
    else var min = (tmp / 60);
    if ((new String(hour)).length < 2)
        hour = "0"+hour;
    if ((new String(min)).length < 2)
        min = "0"+min;
    var rtnVal = hour+":"+min+":"+strSec;
    return rtnVal
}
/**
 * Handles the use of the back button (saves data and closes SCO)
 */
function doBack()
{
    checkAnswers(true);
    doLMSSetValue( "cmi.core.exit", "suspend" );
    computeTime();
    exitPageStatus = true;
    var result;
    result = doLMSCommit();
    result = doLMSFinish();
}
/**
 * Handles the closure of the current SCO before an interruption. This is only useful if the LMS
 * deals with the cmi.core.exit, cmi.core.lesson_status and cmi.core.lesson_mode *and* the SCO
 * sends some kind of value for cmi.core.exit, which is not the case here (yet).
 */
function doContinue(status)
{
    // Reinitialize Exit to blank
    doLMSSetValue( "cmi.core.exit", "" );
    var mode = doLMSGetValue( "cmi.core.lesson_mode" );
    if ( mode != "review"  &&  mode != "browse" )
    {
        doLMSSetValue( "cmi.core.lesson_status", status );
    }
    computeTime();
    exitPageStatus = true;
    var result;
    result = doLMSCommit();
    result = doLMSFinish();
}
/**
 * handles the recording of everything on a normal shutdown
 */
function doQuit()
{
    checkAnswers();
    computeTime();
    exitPageStatus = true;
    var result;
    result = doLMSCommit();
    result = doLMSFinish();
}
/**
 * Called upon unload event from body element
 */
function unloadPage(status)
{
    if (!exitPageStatus)
    {
           // doQuit( status );
    }
}
/**
 * Third section - depending on Chamilo - check answers and set score
 */
var questions = new Array();
var questions_answers = new Array();
var questions_answers_correct = new Array();
var questions_types = new Array();
var questions_score_max = new Array();
var questions_answers_ponderation = new Array();
/**
 * Checks the answers on the test formular page
 */
function checkAnswers(interrupted)
{
    var tmpScore = 0;
    var status = 'not attempted';
    var scoreMax = 0;
    for(var i=0; i<questions.length;i++) {
        if(questions[i] != undefined && questions[i] != null){
            var idQuestion = questions[i];
            var type = questions_types[idQuestion];
            var interactionScore = 0;
            var interactionAnswers = '';
            var interactionCorrectResponses = '';
            var interactionType = '';

            if (type == 'mcma') {
                var interactionType = 'choice';
                var myScore = 0;
                for(var j=0; j<questions_answers[idQuestion].length;j++)
                {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer = document.getElementById('question_'+(idQuestion)+'_multiple_'+(idAnswer));
                    if(answer.checked)
                    {
                        interactionAnswers += idAnswer+'__|';// changed by isaac flores
                        myScore +=questions_answers_ponderation[idQuestion][idAnswer];

                        /*for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                        {
                            if(questions_answers_correct[idQuestion][k] == idAnswer)
                            {
                                if(questions_answers_ponderation[idQuestion][idAnswer])
                                {
                                    myScore += questions_answers_ponderation[idQuestion][idAnswer];
                                }
                                else
                                {
                                    myScore ++;
                                }
                            }
                        }*/
                    }
                }
                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                //{
                //	interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
                //}
                scoreMax += questions_score_max[idQuestion];
            }
            else if(type == 'mcua')
            {
                var interactionType = 'choice';
                var myScore = 0;
                for(var j=0; j<questions_answers[idQuestion].length;j++)
                {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer = document.getElementById('question_'+(idQuestion)+'_unique_'+(idAnswer));
                    if(answer.checked)
                    {
                        interactionAnswers += idAnswer;
                        if(questions_answers_correct[idQuestion] == idAnswer)
                        {
                            if(questions_answers_ponderation[idQuestion][idAnswer])
                            {
                                myScore += questions_answers_ponderation[idQuestion][idAnswer];
                            }
                            else
                            {
                                myScore ++;
                            }
                        }
                    }
                }
                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //interactionCorrectResponses += questions_answers_correct[idQuestion].toString();
                scoreMax += questions_score_max[idQuestion];
            }
            else if(type == 'tf')
            {
                var interactionType = 'true-false';
                var myScore = 0;
                for(var j=0; j<questions_answers[idQuestion].length;j++)
                {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer = document.getElementById('question_'+(idQuestion)+'_tf_'+(idAnswer));
                    if(answer.checked.value)
                    {
                        interactionAnswers += idAnswer;
                        for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                        {
                            if(questions_answers_correct[idQuestion][k] == idAnswer)
                            {
                                if(questions_answers_ponderation[idQuestion][idAnswer])
                                {
                                    myScore += questions_answers_ponderation[idQuestion][idAnswer];
                                }
                                else
                                {
                                    myScore ++;
                                }
                            }
                        }
                    }
                }
                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //interactionCorrectResponses += questions_answers_correct[idQuestion].toString();
                scoreMax += questions_score_max[idQuestion];
            }
            else if(type == 'fib')
            {
                var interactionType = 'fill-in';
                var myScore = 0;
                for(var j=0; j<questions_answers[idQuestion].length;j++)
                {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer = document.getElementById('question_'+(idQuestion)+'_fib_'+(idAnswer));
                    if(answer.value)
                    {
                        interactionAnswers += answer.value+'__|';//changed by isaac flores
                        for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                        {
                            if(questions_answers_correct[idQuestion][k] == answer.value)
                            {
                                if(questions_answers_ponderation[idQuestion][idAnswer])
                                {
                                    myScore += questions_answers_ponderation[idQuestion][idAnswer];
                                }
                                else
                                {
                                    myScore ++;
                                }
                            }
                        }
                    }
                }
                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                //{
                //	interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
                //}
                scoreMax += questions_score_max[idQuestion];
            }
            else if(type == 'matching')
            {
                var interactionType = 'matching';
                var myScore = 0;
                for(var j=0; j<questions_answers[idQuestion].length;j++)
                {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer = document.getElementById('question_'+(idQuestion)+'_matching_'+(idAnswer));
                    if(answer && answer.value)
                    {
                        interactionAnswers += answer.value+'__|';//changed by isaac flores
                        for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                        {
                            var left = questions_answers_correct[idQuestion][k][0];
                            var right = questions_answers_correct[idQuestion][k][1];
                            if(left == idAnswer && right == answer.value)
                            {
                                if(questions_answers_ponderation[idQuestion][idAnswer])
                                {
                                    myScore += questions_answers_ponderation[idQuestion][idAnswer];
                                }
                                else
                                {
                                    myScore ++;
                                }
                            }
                        }
                    }
                }
                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                //{
                //	interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
                //}
                scoreMax += questions_score_max[idQuestion];
            } else if(type == 'free') {
                //ignore for now as a score cannot be given
                var interactionType = 'likert';
                interactionScore = 0;
                //interactionAnswers = document.getElementById('question_'+(idQuestion)+'_free').value;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //interactionCorrectResponses += questions_answers_correct[idQuestion].toString();
            } else if(type == 'hotspot') {
                var interactionType = 'sequencing';
                interactionScore = 0;
                //if(question_score && question_score[idQuestion]){
                //	interactionScore = question_score[idQuestion];
                //} //else, 0
                //interactionAnswers = document.getElementById('question_'+(idQuestion)+'_free').innerHTML;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                //{
                //	interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
                //}
            } else if(type == 'exact') {
                var interactionType = 'exact';
                interactionScore = 0;
                // not yet implemented see scorm_classes.php ScormAnswerMultipleChoice::export() function
                /*

                var myScore = 0;
                var real_answers = new Array();

                for(var j=0; j<questions_answers[idQuestion].length;j++) {
                    var idAnswer = questions_answers[idQuestion][j];
                    var answer   = document.getElementById('question_'+(idQuestion)+'_multiple_'+(idAnswer));
                    if (answer.checked) {
                        if(questions_answers_ponderation[idQuestion][idAnswer] != 0 ) {
                            real_answers[j] = true;
                        } else {
                            real_answers[j] = false;
                        }
                    } else {
                        if(questions_answers_ponderation[idQuestion][idAnswer] != 0) {
                            real_answers[j] = false;
                        } else {
                            real_answers[j] = true;
                        }
                    }
                    //alert(real_answers[j] +' ' + answer.checked + ' ' + questions_answers_ponderation[idQuestion][idAnswer]);
                }

                var final_answer = true;
                for(var z=0; z<real_answers.length ;z++) {
                     if (!real_answers[z]) {
                         final_answer = false;
                     }
                 }

                 if (final_answer) {
                     //getting only the first score where we save the weight of all the question
                    myScore += questions_answers_ponderation[idQuestion][1];
                }

                interactionScore = myScore;
                //correct responses work by pattern, see SCORM Runtime Env Doc
                //for(k=0;k<questions_answers_correct[idQuestion].length;k++)
                //{
                //	interactionCorrectResponses += questions_answers_correct[idQuestion][k].toString()+',';
                //}
                scoreMax += questions_score_max[idQuestion];
                */
            }
            else
            {
                //
            }
            tmpScore += interactionScore;

            doLMSSetValue('cmi.interactions.'+idQuestion+'.id','Q'+idQuestion);
            doLMSSetValue('cmi.interactions.'+idQuestion+'.type',interactionType);
            doLMSSetValue('cmi.interactions.'+idQuestion+'.student_response',interactionAnswers);
            doLMSSetValue('cmi.interactions.'+idQuestion+'.result',interactionScore);
            //correct responses work by pattern, see SCORM Runtime Env Doc
            //doLMSSetValue('cmi.interactions.'+idQuestion+'.correct_responses',questions_answers_correct[idQuestion]);
            //doLMSSetValue('cmi.interactions.'+idQuestion+'.correct_responses',interactionCorrectResponses);
        }
    }
    doLMSSetValue('cmi.core.score.min',0);
    doLMSSetValue('cmi.core.score.max',scoreMax);
    doLMSSetValue('cmi.core.score.raw',tmpScore);
    //doLMSSetValue('cmi.student_data.mastery_score',(scoreMax*0.7));
    //get status
    var mastery_score = doLMSGetValue('cmi.student_data.mastery_score');
    if(mastery_score <= 0)
    {
        mastery_score = (scoreMax*0.80);
    }
    if(tmpScore > mastery_score)
    {
        status = 'passed';
    }
    else
    {
        status = 'failed';
    }
    doLMSSetValue('cmi.core.lesson_status',status);

    if (interrupted && (status != 'completed') && (status != 'passed'))
    {
        doLMSSetValue('cmi.core.exit','suspended');
    }
    else
    {
    }
    return false; //do not submit the form
}
js/HOWTO-storageapi000064400000003617152003363470010151 0ustar00HOWTO use Storage API in your content.

Include the following JavaScript function in your content:
--------------------javascript code--------------------
function FindSTAPI(win) {
	var g_nFindAPITries = 0;
	while ((win.STAPI == null) && (win.parent != null) && (win.parent != win)) {
		g_nFindAPITries ++;
		if (g_nFindAPITries > 500) {
			return null;
		}
		win = win.parent;
	}
	return win.STAPI;
}
--------------------javascript code--------------------

This function returns a facade object which has the following methods :

- testCall(message) : outputs message in an alertbox, you can use it to make sure the API is reachable
- getAllUsers : get a list of users info (user ids, usernames, firstnames, lastnames)

Basic storage:
- setValue(sv_key, sv_value): set the given value for the given key for the current user
- getValue(sv_key): get the value stored for the key sv_key for the current user
- getAll(): get all stored key/values pair for the current user

Stack storage (you can store a stack of values instead of a single value for each key):
- stack_push(sv_key, sv_value): push a new value in the stack for sv_key for the current user
- stack_pop(sv_key): pop the most recently pushed value from the sv_key stack for the current user
- stack_length(sv_key): get the number of stacked values for the key sv_key for the current user
- stack_clear(sv_key): erase all values from the storage stack for the key sv_key for the current user
- stack_getAll(sv_key): get all values from the storage stack for the key sv_key for the current user

These storage values have superuser counterparts which allow platform administrators to alter values for any user :
- setValue_user(sv_key, sv_value, user_id)
- getValue(sv_key, user_id)
- getAll(user_id)
- stack_push(sv_key, sv_value, user_id)
- stack_pop(sv_key, sv_value, user_id)
- stack_length(sv_key, user_id)
- stack_clear(sv_key, user_id)
- stack_getAll(sv_key, user_id)
js/storageapi.js000064400000014047152003363470007665 0ustar00// Storage API
// JavaScript API
// CBlue SPRL, Jean-Karim Bockstael <jeankarim@cblue.be>


lms_storage_testCall = function(content) {
	alert(content);
}

lms_storage_setValue_user = function(sv_key, sv_value, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "set",
			svkey: sv_key,
			svvalue: sv_value,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = (data != '0');
		}
	});
	return result;
}

lms_storage_getValue_user = function(sv_key, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "get",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = data;
		}
	});
	return result;
}

lms_storage_getPosition_user = function(sv_key, sv_user, sv_asc) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "getposition",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco,
			svasc: sv_asc
		},
		success: function(data) {
			result = data;
		}
	});
	return result;
}

lms_storage_getLeaders_user = function(sv_key, sv_user, sv_asc, sv_length) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "getleaders",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco,
			svasc: sv_asc,
			svlength: sv_length
		},
		success: function(data) {
			result = eval("("+data+")");
		}
	});
	return result;
}



lms_storage_getAll_user = function(sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "getall",
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = eval("("+data+")");
		}
	});
	return result;
}

lms_storage_stack_push_user = function(sv_key, sv_value, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "stackpush",
			svvalue: sv_value,
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = (data != '0');
		}
	});
	return result;
}

lms_storage_stack_pop_user = function(sv_key, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "stackpop",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = data;
		}
	});
	return result;
}

lms_storage_stack_length_user = function(sv_key, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "stacklength",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = data;
		}
	});
	return result;
}

lms_storage_stack_clear_user = function(sv_key, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "stackclear",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = data;
		}
	});
	return result;
}

lms_storage_stack_getAll_user = function(sv_key, sv_user) {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "stackgetall",
			svkey: sv_key,
			svuser: sv_user,
			svcourse: sv_course,
			svsco: sv_sco
		},
		success: function(data) {
			result = eval("("+data+")");
		}
	});
	return result;
}

lms_storage_getAllUsers = function() {
	var result;
	$.ajax({
		async: false,
		type: "POST",
		url: "storageapi.php",
		data: {
			action: "usersgetall"
		},
		success: function(data) {
			result = eval("("+data+")");
		}
	});
	return result;
}

lms_storage_setValue = function(sv_key, sv_value) {
	return lms_storage_setValue_user(sv_key, sv_value, sv_user);
}

lms_storage_getValue = function(sv_key) {
	return lms_storage_getValue_user(sv_key, sv_user);
}

lms_storage_getPosition = function(sv_key, sv_asc) {
	return lms_storage_getPosition_user(sv_key, sv_user, sv_asc);
}

lms_storage_getLeaders = function(sv_key, sv_asc, sv_length) {
	return lms_storage_getLeaders_user(sv_key, sv_user, sv_asc, sv_length);
}

lms_storage_getAll = function() {
	return lms_storage_getAll_user(sv_user);
}

lms_storage_stack_push = function(sv_key, sv_value) {
	return lms_storage_stack_push_user(sv_key, sv_value, sv_user);
}

lms_storage_stack_pop = function(sv_key) {
	return lms_storage_stack_pop(sv_key, sv_user);
}

lms_storage_stack_length = function(sv_key) {
	return lms_storage_stack_length_user(sv_key, sv_user);
}

lms_storage_stack_clear = function(sv_key) {
	return lms_storage_stack_clear_user(sv_key, sv_user);
}

lms_storage_stack_getAll = function(sv_key) {
	return lms_storage_stack_getAll_user(sv_key, sv_user);
}


// Accessor object
function STORAGEAPIobject() {
	this.testCall = lms_storage_testCall;
	this.setValue = lms_storage_setValue;
	this.setValue_user = lms_storage_setValue_user;
	this.getValue = lms_storage_getValue;
	this.getValue_user = lms_storage_getValue_user;
	this.getAll = lms_storage_getAll;
	this.getAll_user = lms_storage_getAll_user;
	this.getPosition_user = lms_storage_getPosition_user;
	this.getPosition = lms_storage_getPosition;
	this.getLeaders_user = lms_storage_getLeaders_user;
	this.getLeaders = lms_storage_getLeaders;
	this.stack_push = lms_storage_stack_push;
	this.stack_push_user = lms_storage_stack_push_user;
	this.stack_pop = lms_storage_stack_pop;
	this.stack_pop_user = lms_storage_stack_pop_user;
	this.stack_length = lms_storage_stack_length;
	this.stack_length_user = lms_storage_stack_length_user;
	this.stack_clear = lms_storage_stack_clear;
	this.stack_clear_user = lms_storage_stack_clear_user;
	this.stack_getAll = lms_storage_stack_getAll;
	this.stack_getAll_user = lms_storage_stack_getAll_user;
	this.getAllUsers = lms_storage_getAllUsers;
	this.sv_user = sv_user;
	this.sv_course = sv_course;
	this.sv_sco = sv_sco;
}
var STORAGEAPI = new STORAGEAPIobject();
var STAPI = STORAGEAPI;
resourcelinker.inc.php000064400000305537152003363470011101 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 *	@author Patrick Cool - original version
 *	@author Denes Nagy - further improvements for learning path builder
 *	@author Roan Embrechts - refactoring to improve code organisation
 *	@package chamilo.resourcelinker
 *	@todo use the constants for the tools
 *	@todo use Database API instead of creating table names locally.
 */
/**
 * Code
 */
/* INIT SECTION */

//$language_file = 'resourcelinker';

// Flag to allow for anonymous user - needs to be set before global.inc.php.

use \ChamiloSession as Session;

$use_anonymous = true;

require_once 'back_compat.inc.php';
require_once api_get_path(SYS_CODE_PATH).'lang/english/resourcelinker.inc.php';
if (!empty($_course['language'])){
    $resource_linker_file =  api_get_path(SYS_CODE_PATH).'lang/'.$_course['language'].'/resourcelinker.inc.php';
    if (file_exists($resource_linker_file)) {
        require_once $resource_linker_file;
    }
}
require_once '../exercice/hotpotatoes.lib.php';

/* FUNCTIONS */

function unset_session_resources() {
    $_SESSION['addedresource'] = '';
    $_SESSION['addedresourceid'] = '';
    Session::erase(addedresource);
    Session::erase(addedresourceid);
}

/**
 * Insert description here.
 */
function show_folder_up() {
    global $folder;
    global $source_id, $action, $learnpath_id, $chapter_id, $originalresource;

    $level = get_levels($folder);

    if ($level == 1) {
        echo "<a href='".api_get_self()."?content=Document&amp;source_forum=".$_GET['source_forum']."&amp;source_id=$source_id&amp;action=$action&amp;learnpath_id=$learnpath_id&amp;chapter_id=$chapter_id&amp;originalresource=no'><img src='../img/folder_up.gif' border='0' />".get_lang('LevelUp')."</a>";
    }
    if ($level && $level != 0 && $level != 1) {
        $folder_up=$folder;
        $folder_temp=explode('/',$folder);
        $last=count($folder_temp)-1;
        unset($folder_temp[$last]);
        $folder_up=implode('/',$folder_temp);
        echo "<a href='".api_get_self()."?content=Document&amp;source_forum=".$_GET['source_forum']."&amp;folder=$folder_up&amp;source_id=$source_id&amp;action=$action&amp;learnpath_id=$learnpath_id&amp;chapter_id=$chapter_id&amp;originalresource=no'><img src='../img/folder_up.gif' border='0' />".get_lang('LevelUp')."</a>";
    }
}

/**
 * Shows the documents of the document tool
 * @param $folder
 */
function show_documents($folder) {
    global $_course;
    global $source_id, $action, $learnpath_id, $chapter_id, $originalresource;

    // Documents are a special case: The teacher can add an invisible document (it will be viewable by the user)
    // other tools do not have this feature.
    if (api_is_allowed_to_edit()) {
        $visibility = "ip.visibility<>'2'";
    } else {
        $visibility = "ip.visibility='1'";
    }
    
    $course_id = api_get_course_int_id();

    $item_property_table = Database::get_course_table(TABLE_ITEM_PROPERTY);
    $document_table = Database::get_course_table(TABLE_DOCUMENT);
    $sql = "SELECT * from $document_table docs , $item_property_table ip 
            WHERE   docs.c_id = $course_id AND 
                    ip.c_id = $course_id AND 
                    docs.id=ip.ref AND ip.tool = '".TOOL_DOCUMENT."' AND $visibility AND ip.to_group_id = 0 AND ip.to_user_id IS NULL  ORDER BY docs.path ASC";
    $result = Database::query($sql);
    while ($row = Database::fetch_array($result)) {
        if (!$folder) {
            if (get_levels($row['path'])-1 == 1) {
                // showing the right icon.
                if (file_or_folder($row['path'])) {
                        echo '<img src="../img/file.gif" align="middle" />';
                } else {
                    $image = choose_image($row['path']);
                    echo "<img src=\"../img/$image\" align=\"middle\" />";
                }

                // Folders should be clickable.
                if (file_or_folder($row['path'])) {
                    echo "<a href='".api_get_self()."?content=Document";
                    echo "&folder=".substr($row['path'], 1)."&source_id=$source_id&source_forum=".$_GET['source_forum']."&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".substr($row['path'], 1).'</a><br />';
                } else {
                    echo substr($row['path'], 1).' ';
                    echo showorhide_addresourcelink('Document',$row['id']);
                    echo '<br />';
                }
            }
        } else {
            // We calculate the level we are in by using the $folder in the url.
            // We put +1 because it does not start with an / and in the database it does
            $level = get_levels($folder) + 1;

            // We calculate each level of the database entry.
            $file_level=get_levels($row['path'])-1;
            // If the level of the database entry is equal to the level we ar in, we put it into an array
            // as this is a potential good entry.
            if ($file_level == $level) {
                $good_paths[] = $row['path'];
                $good_ids[] = $row['id'];
            }
            //$haystack=$row['path'];
            //$conform_folder=strstr($haystack, $folder);
            //if (str_replace($folder.'/', '', $conform_folder) !== $folder) {
            //	$good_folders[] = $row['path'];
                //echo str_replace($folder.'/', '', $conform_folder);
            //	echo '<br />';
            //	}// if (str_replace($folder.'/','',$conform_folder)!==$folder)
        } // else (if (!$folder))
    } //while ($row=Database::fetch_array($result))

    // This is code for the case that we are in a subfolder.
    if ($good_paths) {
        // We have all the potential good database entries, the good ones are those that start with $folder
        foreach ($good_paths as $path) {
            if (strstr($path, $folder)) {
                $good_key = key($good_paths);
                // Showing the right icon.
                if (file_or_folder($path)) {
                    echo '<img src="../img/file.gif" align="middle" />';
                } else {
                    $image = choose_image($path);
                    echo "<img src=\"../img/$image\" align=\"middle\" />";
                }

                // Folders should be clickable
                if (file_or_folder($path)) {
                    $path = substr($path, 1); // Remove the first / in folder_up.
                    $uri = str_replace($folder, $path, $_SERVER['REQUEST_URI']);
                    $newuri = str_replace('add=', 'addnot=', $uri);
                    // Using the correct name of the folder.
                    $folder_name = str_replace($folder.'/', '', $path);
                    echo "<a href='$newuri'>".$folder_name.'</a><br />';
                } else {
                    echo str_replace("/$folder/", '', $path).' ';
                    echo showorhide_addresourcelink('Document', $good_ids[$good_key]);
                    echo '<br />';
                }
            }
            next($good_paths);
        }
    }
}

/**
 * Checks wether something is a file or a folder
 * 0 means file, 1 means folder
 * @param $filefolder
 * @todo use true and false instead of 1 and 0.
 */
function file_or_folder($filefolder) {
    global $_course;
    global $baseServDir;
    $courseDir   = $_course['path'].'/document';
    $baseWorkDir = api_get_path(SYS_COURSE_PATH).$courseDir;
    return (is_dir($baseWorkDir.$filefolder) ? 1 : 0);
}

/**
 * Inserts a resource into the database
 *
 * @param $source_type
 * @param $source_id
 */
function store_resources($source_type, $source_id) {
    global $_course;
    $resource_table = Database::get_course_table(TABLE_LINKED_RESOURCES);
    $course_id = api_get_course_int_id();
    
    $addedresource = $_SESSION['addedresource'];
    $addedresourceid = $_SESSION['addedresourceid'];
    if ($_SESSION['addedresource']) {
        foreach ($addedresource as $resource_type) {
            $sql="INSERT INTO $resource_table (c_id, source_type, source_id, resource_type, resource_id) VALUES 
            ($course_id, '$source_type', '$source_id', '$resource_type', '".$addedresourceid[key($addedresource)]."')";
            Database::query($sql);
            $i=key($addedresource);
            next($addedresource);
        }
        $_SESSION['addedresource']='';
        $_SESSION['addedresourceid']='';
    }
}

/**
 * DEPRECATED - use rl_get_resource_link() instead - DEPRECATED
 * Displays the link that opens a new browser window that views the added resource.
 *
 * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
 * @param $type the type of the tool
 * @param $id the id of the resource
 * @param $style this is used to style the link (for instance when a resource is hidden => the added resources should also be styled like they are hidden)
 * @todo use the constants for the type definitions.
 */
function display_addedresource_link($type, $id, $style = '') {
    global $_course;
    $course_id = api_get_course_int_id();

    // Styling the link of the added resource.
    if ($style != '') {
        $styling = ' class="'.$style.'"';
    }

    switch ($type) {
        case 'Agenda':
            $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
            $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            echo '<img src="../img/agenda.gif" align="middle" /> <a href="../calendar/agenda.php"'.$styling.'>'.$myrow['title']."</a><br />\n";
            break;
        case 'Ad_Valvas':
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            echo '<img src="../img/valves.gif" align="middle" /> <a href="../announcements/announcements.php"'.$styling.'>'.$myrow['title']."</a><br />\n";
            break;
        case 'Link':
            $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            echo '<img src="../img/links.gif" align="middle" /> <a href="#" onclick="javascript:window.open(\'../link/link_goto.php?link_id='.$myrow['id'].'&amp;link_url='.urlencode($myrow['url'])."','MyWindow','width=500,height=400,top='+((screen.height-400)/2)+',left='+((screen.width-500)/2)+',scrollbars=1,resizable=1,menubar=1'); return false;\"".$styling.'>'.$myrow['title']."</a><br />\n";
            break;
        case 'Exercise':
            $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
            $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            echo '<img src="../img/quiz.gif" align="middle" /> <a href="../exercice/exercise_submit.php?exerciseId='.$myrow['id'].'"'.$styling.'>'.$myrow['title']."</a><br />\n";
            break;
        case 'Forum':           
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
            $myrow = Database::fetch_array($result);
            echo '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewforum.php?forum='.$myrow['forum_id'].'&amp;md5='.$myrow['md5'].'"'.$styling.'>'.$myrow['forum_name']."</a><br />\n";
            break;
        case 'Thread':  //=topics
            //@deprecated bb_posts, bb_posts_text, bb_forums
            /*
            $tbl_posts		= $_course['dbNameGlu'].'bb_posts';
            $tbl_posts_text	= $_course['dbNameGlu'].'bb_posts_text';
            $TBL_FORUMS		= $_course['dbNameGlu'].'bb_forums';
            $result = Database::query("SELECT * FROM $tbl_posts posts, $TBL_FORUMS forum WHERE forum.forum_id=posts.forum_id and post_id=$id");
            $myrow = Database::fetch_array($result);
            // grabbing the title of the post
            $sql_title = "SELECT * FROM $tbl_posts_text WHERE c_id = $course_id AND post_id=".$myrow["post_id"];
            $result_title = Database::query($sql_title);
            $myrow_title = Database::fetch_array($result_title);
            echo '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewtopic.php?topic='.$myrow['topic_id'].'&amp;forum='.$myrow['forum_id'].'&amp;md5='.$myrow['md5'].'"'.$styling.'>'.$myrow_title['post_title']."</a><br />\n";
             */
            break;
        case 'Post':
            $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
            $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND post_id = $id";
            $result = Database::query($sql);
            $post = Database::fetch_object($result);
            echo '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewtopic.php?topic='.$post->topic_id.'&amp;forum='.$post->forum_id.'"'.$styling.'>'.$post->post_title."</a><br />\n";
            break;
        case 'Document':
            $dbTable = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM $dbTable WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $pathname = explode('/',$myrow['path']); // Making a correct name for the link.
            $last = count($pathname) - 1;  // Making a correct name for the link.
            $filename = $pathname[$last];  // Making a correct name for the link.
            $image = choose_image($filename);
            $ext = explode('.', $filename);
            $ext = strtolower($ext[sizeof($ext)-1]);
            $myrow['path'] = rawurlencode($myrow['path']);
			
			$array_ext=array('htm', 'html', 'gif', 'jpg', 'jpeg', 'png');
			
			if (api_browser_support('svg')){				
				$array_ext[]='svg';
			}
			if (api_browser_support('ogg')){
				$array_ext[]='ogg';
			}
							
            $in_frames = in_array($ext, $array_ext);

            echo '<img src="../img/'.$image.'" align="middle" /> <a href="../document/'.($in_frames ? 'showinframes.php?file=' : 'download.php?doc_url=').$myrow['path'].'"'.$styling.'>'.$filename."</a><br />\n";
            break;
        case 'Externallink':
            echo '<img src="../img/links.gif" align="middle" /> <a href="'.$id.'"'.$styling.'>'.$id."</a><br />\n";
            break;
    }
}

/**
 * This function is to display the added resources (lessons) in the learning path player and builder
 * this function is a modification of display_addedresource_link($type, $id) function
 * the two ids are a bit confusing, I admit, but I did not want to change Patrick's work, I was
 * building upon it. - Denes
 *
 * Parameters:
 * @param completed   - if ="completed" then green presentation with checkbox
 * @param id_in_path  - if onclick then this lesson will be considered completed, that is the unique index in the items table
 * @param id          - that is the correspondent id in the mirror tool (like Agenda item 2)
 * @param type        - that is the correspondent type in the mirror tool (like this is a Link item)
 * @param builder     - if ="builder" then onclick shows in new window
 * @param icon        - if ="icon" then the small icon will appear
 *                      if ="wrap" then wrapped settings are used (and no icon is displayed)
 *                      if ="nolink" then only the name is returned with no href and no icon (note:only in this case, the result is not displayed, but returned)
 * @todo this function is too long, rewrite
 */
function display_addedresource_link_in_learnpath($type, $id, $completed, $id_in_path, $builder, $icon, $level = 0) {
    global $_course, $learnpath_id, $tbl_learnpath_item, $items;
    global $curDirPath, $_configuration, $enableDocumentParsing, $_course, $_user, $_cid;
    
    $course_id = api_get_course_int_id();
    

    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
    $hyperlink_target_parameter = ''; //or e.g. 'target="_blank"'

    $length = ((($builder == 'builder') && ($icon == 'nolink')) ? 65 : 32);

    if ($builder != 'builder') $origin = 'learnpath';	//origin = learnpath in student view
    $linktype = $type;
    if (($type == 'Link _self') or ($type == 'Link _blank')) $type = 'Link';

    // YW switched litteral tool names to use of constants declared in main_api.lib.php
    switch ($type) {
        case TOOL_CALENDAR_EVENT:
        case 'Agenda':
            $TABLEAGENDA 		= Database::get_course_table(TABLE_AGENDA);
            $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['title'] = $row['title']; }
            $desc = $row['description'];
            $agenda_id = $row['item_id'];
            echo str_repeat("&nbsp;&gt;", $level);
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow['title'] == '') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($myrow['title'], $length)); }
            if ($icon == 'icon') { echo "<img src='../img/agenda.gif' align=\"absmiddle\" alt='agenda'>"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Agenda&origin=$origin&agenda_id=$agenda_id#$id_in_path\" class='$completed'>".shorten($myrow['title'], ($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Agenda&origin=$origin&agenda_id=$agenda_id#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../calendar/agenda.php?origin=$origin&agenda_id=$agenda_id\" class='$completed' target='_blank'>".shorten($myrow['title'], ($length-3*$level))."</a>";
            }
            break;

        case TOOL_ANNOUNCEMENT:
        case 'Ad_Valvas':
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);
            if ($row['title'] != '') {
                 $myrow['content'] = $row['title']; 
            }
            $desc = $row['description'];
            $ann_id = $row['item_id'];
            echo str_repeat("&nbsp;&gt;", $level);

            // The title and the text are in the content field and we only want to display the title.
            list($title, $text) = split('<br>', $myrow['content']);
            if ($title == '') { $title = $myrow['content']; }
            $title = $myrow['title'];
            $text = $myrow['content'];
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($title == '') {
                $type = 'Announcement';
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>";
                return(true);
            }

            if ($icon == 'nolink') { return(shorten($title,$length)); }
            if ($icon == 'icon') { echo "<img src='../img/valves.gif' align=\"absmiddle\" alt='ad valvas'>"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Ad_Valvas&origin=$origin&ann_id=$ann_id#$id_in_path\" class='$completed'>".shorten($title, ($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Ad_Valvas&origin=$origin&ann_id=$ann_id#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../announcements/announcements.php?origin=$origin&ann_id=$ann_id\" class='$completed' target='_blank'>".shorten($title, ($length-3*$level))."</a>";
            }
            break;

        case TOOL_LINK:
        case 'Link':
            $TABLETOOLLINK	= Database::get_course_table(TABLE_LINK);
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['title'] = $row['title']; }
            $desc=$row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow['title'] == '') {
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>";
                return(true);
            }

            if ($icon == 'nolink') { return(shorten($myrow['title'], $length)); }
            if ($icon == 'icon') {
                if ($linktype == 'Link _self') { echo "<img src='../img/links.gif' align=\"absmiddle\" alt='links'>"; }
                   else { echo "<img src='../img/link_blank.gif' align=\"absmiddle\" alt='blank links'>"; }
            }
            $thelink = $myrow['url'];
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=$linktype&origin=$origin&thelink=$thelink#$id_in_path\" class='$completed'>".shorten($myrow['title'], ($length-3*$level))."</a>";
                $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=$linktype&origin=$origin&thelink=$thelink#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"$thelink\" class='$completed' target='_blank'>".shorten($myrow['title'], ($length-3*$level))."</a>";
            }
            break;

        case TOOL_QUIZ:
        case 'Exercise':
            $TBL_EXERCICES  = Database::get_course_table(TABLE_QUIZ_TEST);
            $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            if ($builder == 'builder') { $origin = 'builder'; }
              // This is needed for the exercise_submit.php can delete the session info about tests.

            $sql = "select * from $tbl_lp_item where id=$id_in_path";
            $result = Database::query($sql);
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['title'] = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                } else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow['title'] == '') {
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>";
                return(true);
            }

            if ($icon == 'nolink') { return(shorten($myrow['title'], $length)); }
            if ($icon == 'icon') { echo "<img src='../img/quiz.gif' align=\"absmiddle\" alt='quizz'>"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Exercise&origin=$origin&exerciseId=".$myrow["id"]."#$id_in_path\" class='$completed'>".shorten($myrow['title'], ($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Exercise&origin=$origin&exerciseId=".$myrow["id"]."#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../exercice/exercise_submit.php?origin=$origin&exerciseId=".$myrow['id']."\" class='$completed' target='_blank'>".shorten($myrow['title'], ($length-3*$level))."</a>";
            }
            break;

        case 'hotpotatoes':
        case 'HotPotatoes':
            $TBL_DOCUMENT  = Database::get_course_table(TABLE_DOCUMENT);
            $documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
            $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $path = $myrow['path'];
            $name = GetQuizName($path, $documentPath);

            if ($builder == 'builder') { $origin='builder'; }
              // This is needed for the exercise_submit.php can delete the session info about tests.

            $sql = "select * from $tbl_lp_item where id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $name=$row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name=='') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name,$length)); }
            if ($icon == 'icon') { echo "<img src='../img/jqz.jpg' align=\"absmiddle\" alt='hot potatoes'>"; }

            $cid = $_course['official_code'];

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=HotPotatoes&origin=$origin&id=$id#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=HotPotatoes&origin=$origin&id=$id#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "&nbsp;<a href=\"../exercice/showinframes.php?file=$path&cid=$cid&uid=".$_user['user_id']."\" class='$completed' target='_blank'>".shorten($name,($length-3*$level))."</a>";
            }
            break;

        case TOOL_FORUM:
        case 'Forum':
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow["forum_name"] = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow["forum_name"]=='') { $type="Forum"; echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($myrow['forum_name'], $length)); }
            if ($icon == 'icon') { echo "<img src='../img/forum.gif' align=\"absmiddle\" alt='forum'>"; }
            $forumparameters = "forum=".$myrow["forum_id"]."&md5=".$myrow["md5"];
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Forum&origin=$origin&forumparameters=$forumparameters#$id_in_path\" class='$completed'>".shorten($myrow["forum_name"],($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Forum&origin=$origin&forumparameters=$forumparameters#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../phpbb/viewforum.php?$forumparameters\" class='$completed' target='_blank'>".shorten($myrow["forum_name"],($length-3*$level))."</a>";
            }
            break;

        case TOOL_THREAD:
        case 'Thread':  //forum post
            $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD);
            $sql = "SELECT * FROM $tbl_topics where c_id = $course_id AND topic_id=$id";
            $result = Database::query($sql);
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['topic_title'] = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow["topic_title"] == '') { $type = "Forum Post"; echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($myrow['topic_title'], $length)); }
            if ($icon == 'icon') { echo "<img src='../img/forum.gif' align=\"absmiddle\" alt='forum'>"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Thread&origin=$origin&topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"]."#$id_in_path\" class='$completed'>".shorten($myrow["topic_title"], ($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Thread&origin=$origin&topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"]."#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../phpbb/viewtopic.php?topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"]."\" class='$completed' target='_blank'>".shorten($myrow["topic_title"],($length-3*$level))."</a>";
            }
            break;

        case TOOL_POST:
        case 'Post':
        //deprecated
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $result = Database::query("SELECT * FROM $tbl_posts where c_id = $course_id AND post_id=$id");
            $myrow = Database::fetch_array($result);
            // Grabbing the title of the post.
            
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row=Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['post_title'] = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            $posternom = $myrow['nom'];				$posterprenom = $myrow['prenom'];
            $posttime = $myrow['post_time'];			$posttext = $myrow['post_text'];
            $posttitle = $myrow['post_title'];
            $posttext = str_replace('"', "'", $posttext);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($myrow["post_title"] == '') {
                $type = 'Forum';
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true);
            }

            if ($icon == 'nolink') { return(shorten($myrow["post_title"],$length)); }
            if ($icon == 'icon') { echo "<img src='../img/forum.gif' align=\"absmiddle\" alt='forum'>"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".intval($_GET['source_forum'])."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Post&origin=$origin&posternom=$posternom&posterprenom=$posterprenom&posttime=$posttime&posttext=$posttext&posttitle=$posttitle#$id_in_path\" class='$completed'>".shorten($myrow["post_title"],($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".intval($_GET['source_forum'])."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Post&origin=$origin&posternom=$posternom&posterprenom=$posterprenom&posttime=$posttime&posttext=$posttext&posttitle=$posttitle#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../phpbb/viewtopic.php?topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"]."\" class='$completed' target='_blank'>".shorten($myrow["post_title"],($length-3*$level))."</a>";
            }
            break;

        case TOOL_DOCUMENT:
        case 'Document':
            $dbTable = Database::get_course_table(TABLE_DOCUMENT);
            $my_sql = "SELECT * FROM $dbTable WHERE c_id = $course_id AND id=$id";
            //error_log('New LP - Querying document table: '.$my_sql,0);
            $result = Database::query($my_sql);
            $myrow = Database::fetch_array($result);

            $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
            $last = count($pathname) - 1;  // Making a correct name for the link.
            $filename=$pathname[$last];  // Making a correct name for the link.
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }

            echo str_repeat("&nbsp;&gt;", $level);

            if ($icon != 'nolink') {
                if ($completed=='completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                } else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }
            $image = choose_image($filename);

            $sql = "select * from $tbl_lp_item where id=$id_in_path";
            //error_log('New LP - Querying lp_item table: '.$sql, 0);
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $filename = $row['title']; }
            $desc=$row['description'];

            if (($myrow['path'] == '') && ($filename == '')) {
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>";
                return(true);
            }

            if ($icon == 'nolink') { return(shorten($filename, $length)); }
            if ($icon == 'icon') { echo "<img src='../img/$image' align=\"absmiddle\" alt='$image'>"; }
            if ($builder != 'builder')
            {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Document&origin=$origin&docurl=".$myrow["path"]."#$id_in_path\" class='$completed'>".shorten($filename,($length-3*$level))."</a>";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc,($length-3*$level))."</div>";
                    }
                } $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Document&origin=$origin&docurl=".$myrow["path"]."#$id_in_path";
            } else {
                $enableDocumentParsing=yes;
                if (!$enableDocumentParsing) {
                    // This is the solution for the non-parsing version in the builder.
                    $file = urlencode($myrow['path']);
                    echo "<a href='../document/showinframes.php?file=$file' class='$completed' $hyperlink_target_parameter>".shorten($filename,($length-3*$level))."</a>";
                } else {
                    echo "<a href=\"../document/download.php?doc_url=".$myrow['path']."\" class='$completed' $hyperlink_target_parameter>".shorten($filename,($length-3*$level))."</a>";
                }
            }
            break;

        case 'assignments':
        case 'Assignments':
            $name = get_lang('Assignments');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $name=$row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name=='') {
                echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true);
            }

            if ($icon == 'nolink') { return(shorten($name, $length)); }
            if ($icon == 'icon') { echo "<img src='../img/works.gif' align=\"absmiddle\">"; }
            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Assignments&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Assignments&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>";
                    } else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>";
                    }
                }
            } else {
                echo "<a href=\"../work/work.php\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;

        case TOOL_DROPBOX:
        case 'Dropbox':
            $name = get_lang('Dropbox');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $name = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name == '') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name, $length)); }
            if ($icon == 'icon') { echo "<img src='../img/dropbox.gif' align=\"absmiddle\">"; }

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Dropbox&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Dropbox&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>"; }
                    else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>"; }
                }
            } else {
                echo "<a href=\"../dropbox/index.php\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;

        case 'introduction_text':
        case 'Introduction_text':
            $name = get_lang('IntroductionText');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $name = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                } else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name == '') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name, $length)); }
            if ($icon == 'icon') { echo "<img src='../img/introduction.gif' align=\"absmiddle\" alt='introduction'>"; }

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Introduction_text&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>";
                $items[] = api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Introduction_text&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>"; }
                    else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>"; }
                }
            } else {
                $s = api_get_path(WEB_COURSE_PATH)."$_cid/index.php?intro_cmdEdit=1";
                echo "<a href=\"$s\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;

        case TOOL_COURSE_DESCRIPTION:
        case 'Course_description':
            $name = get_lang('CourseDescription');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $name = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                }	else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name == '') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name,$length)); }
            if ($icon == 'icon') { echo "<img src='../img/info.gif' align=\"absmiddle\" alt='info'>"; }

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Course_description&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Course_description&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>"; }
                    else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>"; }
                }
            } else {
                $s=api_get_path(WEB_CODE_PATH)."course_description";
                echo "<a href=\"$s\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;

        case TOOL_GROUP:
        case 'Groups':
            $name = get_lang('Groups');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $name=$row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                } else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name=='') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name, $length)); }
            if ($icon == 'icon') { echo "<img src='../img/group.gif' align=\"absmiddle\" alt='group'>"; }

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Groups&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Groups&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>"; }
                    else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>"; }
                }
            } else {
                echo "<a href=\"../group/group.php?origin=$origin\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;

        case TOOL_USER:
        case 'Users':
            $name = get_lang('Users');
            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $name = $row['title']; }
            $desc = $row['description'];
            echo str_repeat("&nbsp;&gt;", $level);

            if (($builder != 'builder') && ($icon != 'wrap')) { echo "<td>"; }
            if ($icon != 'nolink') {
                if ($completed == 'completed') {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on'>";
                } else {
                    echo "<img src='../img/checkbox_on2.gif' border='0' width='13' height='11' alt='on' style='visibility: hidden'>";
                    //echo "&nbsp;";
                }
            }
            if (($builder != 'builder') && ($icon != 'wrap')) { echo "</td><td>"; }

            if ($name=='') { echo "<span class='messagesmall'>".get_lang('StepDeleted1')." $type ".get_lang('StepDeleted2')."</span>"; return(true); }

            if ($icon == 'nolink') { return(shorten($name,$length)); }
            if ($icon == 'icon') { echo "<img src='../img/members.gif' align=\"absmiddle\" alt='members'>"; }

            if ($builder != 'builder') {
                echo "<a href=\"".api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Users&origin=$origin#$id_in_path\" class='$completed'>".shorten($name,($length-3*$level))."</a>"; $items[]=api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Users&origin=$origin#$id_in_path";
                if ($desc != '') {
                    if ($icon != 'wrap') {
                        echo "</tr><tr><td></td><td></td><td><div class='description'>&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div></td></tr>"; }
                    else {
                        echo "<div class='description'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".shorten($desc, ($length-3*$level))."</div>"; }
                }
            } else {
                echo "<a href=\"../user/user.php?origin=$origin\" class='$completed' target='_blank'>".shorten($name, ($length-3*$level))."</a>";
            }
            break;
    }//end huge switch-statement
}

/**
 * This function is to create and return a link to the added resources (lessons).
 * It returns the same thing as display_addedresource_link_in_learnpath() but doesn't display
 * anything.
 *
 * Parameters:
 * @param type        - that is the correspondent type in the mirror tool (like this is a Link item)
 * @param id          - that is the correspondent id in the mirror tool (like Agenda item 2)
 * @param id_in_path  - the unique index in the items table
 */
function get_addedresource_link_in_learnpath($type, $id, $id_in_path) {
    global $_course, $learnpath_id, $tbl_learnpath_item, $items;
    global $curDirPath, $_configuration, $enableDocumentParsing, $_user, $_cid;

    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);
    $course_id = api_get_course_int_id();
    $hyperlink_target_parameter = ''; // or e.g. target='_blank'
    $builder = 'player';
    $origin = 'learnpath';

    $linktype = $type;
    if (($type == 'Link _self') || ($type == 'Link _blank')) { $type = 'Link'; }

    $link = '';

    switch ($type) {
        case 'Agenda':
            $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
            $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['title'] = $row['title']; }
            $desc = $row['description'];
            $agenda_id = $row['item_id'];

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Agenda&origin=$origin&agenda_id=$agenda_id#$id_in_path";
            } else {
                $link .= "../calendar/agenda.php?origin=$origin&agenda_id=$agenda_id";
            }
            break;

        case 'Ad_Valvas':
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Ad_Valvas&origin=$origin&ann_id=$id#$id_in_path";
            } else {
                $link .= "../announcements/announcements.php?origin=$origin&ann_id=$id";
            }
            break;

        case 'Link':
            $TABLETOOLLINK	= Database::get_course_table(TABLE_LINK);
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);

            $thelink = $myrow['url'];
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=$linktype&origin=$origin&thelink=$thelink#$id_in_path";
            } else {
                $link .= $thelink;
            }
            break;

        case 'Exercise':
            $TBL_EXERCICES  = Database::get_course_table(TABLE_QUIZ_TEST);
            $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);

            if ($builder == 'builder') { $origin = 'builder'; }
              // This is needed for the exercise_submit.php can delete the session info about tests.

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['title'] = $row['title']; }

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Exercise&origin=$origin&exerciseId=".$myrow["id"]."#$id_in_path";
            } else {
                $link .= "../exercice/exercise_submit.php?origin=$origin&exerciseId=".$myrow["id"];
            }
            break;

        case 'HotPotatoes':
              $TBL_DOCUMENT  = Database::get_course_table(TABLE_DOCUMENT);
            $documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
            $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $path = $myrow['path'];
              $name = GetQuizName($path, $documentPath);

            if ($builder=='builder') { $origin='builder'; }

            $cid = $_course['official_code'];

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=HotPotatoes&origin=$origin&id=$id#$id_in_path";
            } else {
                $link .= "../exercice/showinframes.php?file=$path&cid=$cid&uid=".$_user['user_id'];
            }
            break;

        case 'Forum':
        //deprecated
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);  // TODO: This is the old table name, it should be corrected.
            $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
            $myrow = Database::fetch_array($result);

            if ($builder == 'builder') { $origin = 'builder'; }

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['forum_name'] = $row['title']; }

            if ($myrow['forum_name'] == '') { $type = 'Forum'; }

            $forumparameters = "forum=".$myrow["forum_id"]."&md5=".$myrow["md5"];
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Forum&origin=$origin&forumparameters=$forumparameters#$id_in_path";
            } else {
                $link .= "../phpbb/viewforum.php?$forumparameters";
            }
            break;

        case 'Thread':  //forum post
        //deprecated
            $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD);
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $sql = "SELECT * FROM $tbl_topics where c_id = $course_id AND topic_id=$id";
            $result = Database::query($sql);
            $myrow = Database::fetch_array($result);

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	$row = Database::fetch_array($result);

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Thread&origin=$origin&topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"]."#$id_in_path";
            } else {
                $link .= "../forum/viewtopic.php?topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"];
            }
            break;

        case 'Post':
        //deprecated
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
            
            $result = Database::query("SELECT * FROM $tbl_posts where c_id = $course_id AND post_id=$id");
            $myrow = Database::fetch_array($result);
            // Grabbing the title of the post.

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);
            if ($row['title'] != '') { $myrow['post_title'] = $row['title']; }
            $desc = $row['description'];
            //$link .= str_repeat("&nbsp;&gt;", $level);

            $posternom = $myrow['nom'];				$posterprenom = $myrow['prenom'];
            $posttime = $myrow['post_time'];		$posttext = $myrow['post_text'];
            $posttitle = $myrow['post_title'];
            $posttext = str_replace('"', "'", $posttext);

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Post&origin=$origin&posternom=$posternom&posterprenom=$posterprenom&posttime=$posttime&posttext=$posttext&posttitle=$posttitle#$id_in_path";
            } else {
                $link .= "../phpbb/viewtopic.php?topic=".$myrow["topic_id"]."&forum=".$myrow["forum_id"]."&md5=".$myrow["md5"];
            }
            break;

        case 'Document':
            $dbTable = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM $dbTable WHERE id=$id");
            $myrow = Database::fetch_array($result);

            $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
            $last = count($pathname) - 1;  // Making a correct name for the link.
            $filename = $pathname[$last];  // Making a correct name for the link.

            $sql = "select * from $tbl_lp_item where c_id = $course_id AND id=$id_in_path";
            $result = Database::query($sql);	
            $row = Database::fetch_array($result);

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Document&origin=$origin&docurl=".$myrow["path"]."#$id_in_path";
            } else {
                $enableDocumentParsing = yes;
                if (!$enableDocumentParsing) {
                    // This is the solution for the non-parsing version in the builder.
                    $file = urlencode($myrow['path']);
                    $link .= "../document/showinframes.php?file=$file";
                } else {
                    $link .= "../document/download.php?doc_url=".$myrow['path'];
                }
            }
            break;

        case 'Assignments':
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Assignments&origin=$origin#$id_in_path";
            } else {
                $link .= "../work/work.php";
            }
            break;
        case 'Dropbox':
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Dropbox&origin=$origin#$id_in_path";
            } else {
                $link .= "../dropbox/index.php";
            }
            break;
        case 'Introduction_text':
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Introduction_text&origin=$origin#$id_in_path";
            } else {
                $s = api_get_path(WEB_COURSE_PATH)."$_cid/index.php?intro_cmdEdit=1";
                $link .= $s;
            }
            break;
        case 'Course_description':
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Course_description&origin=$origin#$id_in_path";
            } else {
                $s = api_get_path(WEB_CODE_PATH).'course_description';
                $link .= $s;
            }
            break;
        case 'Groups':

            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Groups&origin=$origin#$id_in_path";
            } else {
                $link .= "../group/group.php?origin=$origin";
            }
            break;
        case 'Users':
            if ($builder != 'builder') {
                $link .= api_get_self()."?action=closelesson&source_forum=".$_GET['source_forum']."&how=complete&id_in_path=$id_in_path&learnpath_id=$learnpath_id&type=Users&origin=$origin#$id_in_path";
            } else {
                $link .= "../user/user.php?origin=$origin";
            }
            break;
    }//end huge switch-statement
    return $link;
}

/**
 * This function is to remove an resource item from the array
 */
function remove_resource($resource_key) {
    $addedresource = $_SESSION['addedresource'];
    $addedresourceid = $_SESSION['addedresourceid'];
    unset($addedresource[$resource_key]);
    unset($addedresourceid[$resource_key]);
    $_SESSION['addedresource'] = $addedresource;
    $_SESSION['addedresourceid'] = $addedresourceid ;
}

/**
 * This function is to show the button "click to add resource" on the tool page
 * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
 */
function show_addresource_button($additionalparameters = '') {
    global $charset;
    echo '<label for="addresources"><img src="../img/attachment.gif" /></label><input class="link_alike" type="submit" name="addresources" id="addresources" value="'.api_htmlentities(get_lang('Attachment'), ENT_QUOTES, $charset).'" '.$additionalparameters.' />';
}

/**
 * This function is to delete ONE specific resource that were added to a specific item
 * Deprecated
 *//*
function delete_one_added_resource($source_type, $source_id, $resource_type, $resource_id) {
    //echo "delete_one_added_resource";
    global $_course;
    $TABLERESOURCE = $_course['dbNameGlu'].'resource';
    $sql = "DELETE FROM $TABLERESOURCE WHERE source_type='$source_type' and source_id='$source_id' and resource_type='$resource_type' and resource_id='$resource_id'";
    Database::query($sql);
}*/

/**
 * This function is to delete the resources that were added to a specific item
 */
function delete_added_resource($type, $id) {
    global $_course;
    $course_id = api_get_course_int_id();
    $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);
    $sql = "DELETE FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type' and source_id='$id'";
    Database::query($sql);
}

/**
 * This function is te delete all resources of a specific type (only used in announcements -- delete all)
 * Author : Frederik Vermeire <frederik.vermeire@pandora.be>
 */
function delete_all_resources_type($type) {
  global $_course;
  $course_id = api_get_course_int_id();
  $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);
  $sql = "DELETE FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type'";
  Database::query($sql);
}

/**
 * This function checks wether there are added resources or not
 */
function check_added_resources($type, $id) {
    global $_course, $origin;
    $course_id = api_get_course_int_id();
    $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);
    $sql = "SELECT * FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type' and source_id='$id'";
    $result = Database::query($sql);
    $number_added = Database::num_rows($result);
    if ($number_added != 0)
        return true;
    else
        return false;
}

/**
 * this function is to load the resources that were added to a specific item
 * into the session variables
 */
function edit_added_resources($type, $id) {
    global $_course;
    $course_id = api_get_course_int_id();
    $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);

    $sql="SELECT * FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type' and source_id=$id";
    $result=Database::query($sql);
    while ($row=Database::fetch_array($result))
    {
        $addedresource[]=$row["resource_type"];
        $addedresourceid[]=$row["resource_id"];
    }
    $_SESSION['addedresource']=$addedresource;
    $_SESSION['addedresourceid']=$addedresourceid;
}

/**
 * this function is store the modified resources
 * first we delete all the added resources in the database,
 * then we add all the resources from the session object.
 */
function update_added_resources($type, $id) {
    global $_course;
    $course_id = api_get_course_int_id();
    $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);
    // delete all the added resources for this item in the database;
    $sql="DELETE FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type' AND source_id='$id'";
    //echo $sql;
    Database::query($sql);

    // Store the resources from the session into the database.
    store_resources($type, $id);

    //delete_added_resource_($type, $id);
    unset_session_resources();
}

/**
 * this function is to display the resources that were added to a specific item
 */
function display_added_resources($type, $id, $style = '') {
    $course_id = api_get_course_int_id();
    // The array containing the icons
    $arr_icons = array('Agenda'=>'../img/agenda.gif', 'Ad Valvas'=>'../img/valves.gif', 'Link'=>'../img/links.gif', 'Exercise'=>'../img/quiz.gif' );

    global $_course, $origin;
    $TABLERESOURCE = Database::get_course_table(TABLE_LINKED_RESOURCES);

    $sql = "SELECT * FROM $TABLERESOURCE WHERE c_id = $course_id AND source_type='$type' and source_id='$id'";
    $result = Database::query($sql);
    while ($row=Database::fetch_array($result)) {
        if ($origin != 'learnpath') {
            display_addedresource_link($row['resource_type'], $row['resource_id'], $style) ;
        } else {
            display_addedresource_link_in_learnpath($row['resource_type'], $row['resource_id'],'agendaitems','','builder','icon') ; echo "<br>";
        }
    }
}

/**
 * This function is to show the added resources when adding an item
 * $showdeleteimg determine if the delete image should appear or not.
 * deleting an added resource is only possible through the resource linker file itself
 */
function display_resources($showdeleteimg) {
    global $action;
    global $resourceaction;
    global $id;
    global $locationkey;
    global $source_id, $action, $learnpath_id, $chapter_id, $originalresource;

    if ($resourceaction == 'removeresource') {
        remove_resource($locationkey);
    }
    $addedresource = $_SESSION['addedresource'];
    $addedresourceid = $_SESSION['addedresourceid'];
    if (is_array($addedresource)) {
        echo '<table>';
        foreach ($addedresource as $resource) {
            echo '<tr><td>';
            display_addedresource_link($resource, $addedresourceid[key($addedresource)]);
            echo '</td><td width="30">';

            // if $_SERVER['REQUEST_URI'] contains and ?id=xx we have an edit and the url for deleting a session added resource
            // should also contain this id.
            $test = parse_url($_SERVER['REQUEST_URI']);
            $output = array();
            parse_str($test[query], $output);

            if ($showdeleteimg == 1) {
                echo "<a href=".api_get_self()."?showresources=true&amp;source_forum=".$_GET['source_forum']."&amp;resourceaction=removeresource&amp;locationkey=".key($addedresource)."&amp;source_id=$source_id&amp;action=$action&amp;learnpath_id=$learnpath_id&amp;chapter_id=$chapter_id&amp;originalresource=no><img src='../img/delete.gif' border='0' alt='resource ".get_lang('Delete')."' /></a><br />";
            }
            echo '</td></tr>';
            next($addedresource);
        }
        echo '</table>';
    }
    else { // it is a string
        echo '';
    }
} // end of the display_resources function


/**
* This function checks wether the link add resource should be displayed next the item in the linker page
* So we have to check if the specific id of that tool is already in the array of the added resources
* if it is already in, the link should not be showed since it would make it possible to add
* the same resource a second time (=duplication of added resources)
*/
function showorhide_addresourcelink($type, $id) {
    global $from_learnpath, $source_id, $action, $learnpath_id, $chapter_id, $originalresource, $folder, $content, $target;
    $addedresource = $_SESSION['addedresource'];
    $addedresourceid = $_SESSION['addedresourceid'];

    if (is_array($_SESSION['addedresource'])) {
        foreach ($addedresource as $toolcompare) {
            if ($toolcompare==$type && $addedresourceid[key($addedresource)] == $id) {
                $show = 0;
            }
            next($addedresource);
        }
        if ($from_learnpath) { $lang_add_it_or_resource = get_lang('AddIt'); } else { $lang_add_it_or_resource = get_lang('AddResource'); }
        if ($show !== 0) {
            if ($type == 'Document') {
                echo "<a href=".api_get_self()."?content=".$type."&folder=".$folder."&source_forum=".$_GET['source_forum']."&add=".$id."&source_id=$source_id&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no>".$lang_add_it_or_resource."</a>";
            } else {
                echo "<a href='".api_get_self()."?content=".$type."&source_forum=".$_GET['source_forum']."&add=".$id."&source_id=$source_id&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no&target=$target'>".$lang_add_it_or_resource."</a>";
            }
        }
    }
    else { // if it is not an array, it is a string
        if ($_SESSION['addedresource'] !== $type || $_SESSION['addedresourceid'] !== $id) {
            if ($from_learnpath) { $lang_add_it_or_resource = get_lang('AddIt'); } else { $lang_add_it_or_resource = get_lang('AddResource'); }
            echo "<a href='".api_get_self()."?content=".$type."&folder=".$folder."&source_forum=".$_GET['source_forum']."&add=".$id."&source_id=$source_id&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no&target=$target'>".$lang_add_it_or_resource."</a>";
        }
    }
}

/**
 * Gets the link to the given added resource.
 *
 * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
 * @author Yannick Warnier <ywarnier@beeznest.org>, Dokeos - rebranding
 * @param string 	Course code
 * @param string 	The tool type (using constants declared in main_api.lib.php)
 * @param integer 	The resource ID
 * @param string	Resource style (e.g. when a resource is hidden => the added resources should also be styled like they are hidden)
 * @param boolean	Open in a new window (true) or in the current frame/window (false)?
 * @todo use the constants for the type definitions.
 */
function rl_get_html_resource_link($course_code, $type, $id, $style='', $new_window = true) {
    $_course = Database::get_course_info($course_code);
    
    $course_id = api_get_course_int_id();

    // Styling the link of the added resource
    if ($style != '') $styling = ' class="'.$style.'"';
    if ($new_window) { $target = ' target = "_blank" '; } else { $target = ' target = "_self" '; }

    $output = '';

    switch ($type) {
        case TOOL_CALENDAR_EVENT:
            $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
            $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = '<img src="../img/agenda.gif" align="middle" /> <a href="../calendar/agenda.php"'.$styling.' '.$target.'>'.$myrow['title']."</a><br />\n";
            break;
        case TOOL_ANNOUNCEMENT:
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = '<img src="../img/valves.gif" align="middle" /> <a href="../announcements/announcements.php"'.$styling.' '.$target.'>'.$myrow['title']."</a><br />\n";
            break;
        case TOOL_LINK:
            //doesn't take $target into account
            $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = '<img src="../img/links.gif" align="middle" /> <a href="#" onclick="javascript:window.open(\'../link/link_goto.php?link_id='.$myrow['id'].'&amp;link_url='.urlencode($myrow['url'])."','MyWindow','width=500,height=400,top='+((screen.height-400)/2)+',left='+((screen.width-500)/2)+',scrollbars=1,resizable=1,menubar=1'); return false;\"".$styling.'>'.$myrow['title']."</a><br />\n";
            break;
        case TOOL_QUIZ:
            $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
            $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = '<img src="../img/quiz.gif" align="middle" /> <a href="../exercice/exercise_submit.php?exerciseId='.$myrow['id'].'"'.$styling.' '.$target.'>'.$myrow['title']."</a><br />\n";
            break;
        case TOOL_FORUM:
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
            $myrow = Database::fetch_array($result);
            $output = '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewforum.php?forum='.$myrow['forum_id'].'&md5='.$myrow['md5'].'"'.$styling.' '.$target.'>'.$myrow['forum_name']."</a><br />\n";
            break;
        case TOOL_THREAD:  //=topics
            //$tbl_forum 		= Database::get_course_table(TABLE_FORUM);
            //$tbl_thread 	= Database::get_course_table(TABLE_FORUM_THREAD);
            $tbl_post 		= Database::get_course_table(TABLE_FORUM_POST);
            // grabbing the title of the post
            $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id;
            $result_title = Database::query($sql_title);
            $myrow_title = Database::fetch_array($result_title);
            $output = '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewtopic.php?topic='.$myrow_title['thread_id'].'&forum='.$myrow_title['forum_id'].'" '.$styling.' '.$target.'>'.$myrow_title['post_title']."</a><br />\n";
            break;
        case TOOL_POST:
            $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
            //$tbl_post_text = Database::get_course_table(FORUM_POST_TEXT_TABLE);
            $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id";
            $result = Database::query($sql);
            $post = Database::fetch_array($result);
            $output = '<img src="../img/forum.gif" align="middle" /> <a href="../phpbb/viewtopic.php?topic='.$post['thread_id'].'&forum='.$post['forum_id'].'"'.$styling.' '.$target.'>'.$post['post_title']."</a><br />\n";
            break;
        case TOOL_DOCUMENT:
            $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
            $last = count($pathname) - 1;  // Making a correct name for the link.
            $filename = $pathname[$last];  // Making a correct name for the link.
            $image = choose_image($filename);
            $ext = explode('.', $filename);
            $ext = strtolower($ext[sizeof($ext) - 1]);
            $myrow['path'] = rawurlencode($myrow['path']);
            $in_frames = in_array($ext, array('htm', 'html', 'gif', 'jpg', 'jpeg', 'png'));
            $output = '<img src="../img/'.$image.'" align="middle" /> <a href="../document/'.($in_frames ? 'showinframes.php?file=' : 'download.php?doc_url=').$myrow['path'].'"'.$styling.' '.$target.'>'.$filename."</a><br />\n";
            break;
        /*
        case 'Externallink':
            $output = '<img src="../img/links.gif" align="middle" /> <a href="'.$id.'"'.$styling.' '.$target.'>'.$id."</a><br />\n";
            break;
        */
    }
    return $output;
}

/**
 * Returns an HTML-formatted link to a resource, to incorporate directly into
 * the new learning path tool.
 *
 * The function is a big switch on tool type.
 * In each case, we query the corresponding table for information and build the link
 * with that information.
 * @author	Yannick Warnier <ywarnier@beeznest.org> - rebranding based on previous work (display_addedresource_link_in_learnpath())
 * @param	string	Course code
 * @param	integer	The learning path ID (in lp table)
 * @param	integer	The database ID for that item in the lp_item table
 * @param id          - that is the correspondent id in the mirror tool (like Agenda item 2)
 * @param id_in_path  - the unique index in the items table
 */
function rl_get_resource_link_for_learnpath($course_id, $learnpath_id, $id_in_path) {
    $tbl_lp_item 	= Database::get_course_table(TABLE_LP_ITEM);
    
    $course_info = api_get_course_info_by_id($course_id);    
    $course_id = $course_info['real_id'];
    $course_code = $course_info['code'];
    
    $learnpath_id 	= intval($learnpath_id);
    $id_in_path		= intval($id_in_path);
    
    $sql_item = "SELECT * FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND id = $id_in_path";
    $res_item = Database::query($sql_item);
    if (Database::num_rows($res_item) < 1) return -1; //exit
    $row_item = Database::fetch_array($res_item);

    $type = strtolower($row_item['item_type']);
    $id = (strcmp($row_item['path'], '') == 0) ? '0' : $row_item['path'];
    $origin = 'learnpath';
    $main_dir_path = api_get_path(WEB_CODE_PATH);
    $main_course_path = api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/';

    $link = '';

    switch ($type) {
        case 'dokeos_chapter':
            $link .= $main_dir_path.'newscorm/blank.php';
        case TOOL_CALENDAR_EVENT:
            $link .= $main_dir_path.'calendar/agenda.php?origin='.$origin.'&agenda_id='.$id;
            break;
        case TOOL_ANNOUNCEMENT:
            $link .= $main_dir_path.'announcements/announcements.php?origin='.$origin.'&ann_id='.$id;
            break;
        case TOOL_LINK:
            $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
            $result= Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow=Database::fetch_array($result);
            $thelink=$myrow["url"];
            $link .= $thelink;
            break;
        case TOOL_QUIZ:
            if (!empty($id)) {
                $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
                $sql = "SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id";
                $result= Database::query($sql);
                $myrow=Database::fetch_array($result);

                if ($row_item['title'] != '') { $myrow['title'] = $row_item['title']; }
                //$link .= $main_dir_path.'exercice/exercise_submit.php?lp_init=1&origin='.$origin.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$id_in_path.'&exerciseId='.$id;
                $link .= $main_dir_path.'exercice/overview.php?lp_init=1&origin='.$origin.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$id_in_path.'&exerciseId='.$id;
            }
            break;
        case 'hotpotatoes': //lowercase because of strtolower above
            $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM ".$TBL_DOCUMENT." WHERE c_id = $course_id AND  id=$id");
            $myrow = Database::fetch_array($result);
            $path = $myrow['path'];
            $link .= $main_dir_path.'exercice/showinframes.php?file='.$path.'' .
                    '&origin='.$origin.'&cid='.$course_code.'&uid='.api_get_user_id().'' .
                    '&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$id_in_path;
            break;
        case TOOL_FORUM:
            $link .= $main_dir_path.'forum/viewforum.php?forum='.$id.'&lp=true&origin=learnpath';
            break;
        case TOOL_THREAD:  //forum post
            $tbl_topics = Database::get_course_table(TABLE_FORUM_THREAD);
            if (!empty($id)) {
                $sql = "SELECT * FROM $tbl_topics WHERE c_id = $course_id AND thread_id=$id";
                $result = Database::query($sql);
                $myrow = Database::fetch_array($result);
                $link .= $main_dir_path.'forum/viewthread.php?origin=learnpath&thread='.$id.'' .
                        '&forum='.$myrow['forum_id'].'&lp=true';
            }
            break;
        case TOOL_POST:
            $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
            $result = Database::query("SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=$id");
            $myrow = Database::fetch_array($result);
            $title = $myrow['post_title'];
            //$desc = $row_item['description'];
            $posternom = $myrow['poster_name'];
            $posttime = $myrow['post_date'];
            $posttext = $myrow['post_text'];
            $posttitle = $title;
            $posttext = str_replace('"', "'", $posttext);

            $link .= $main_dir_path.'forum/viewthread.php?post='.$id.'' .
                    '&thread='.$myrow['thread_id'].'&forum='.$myrow['forum_id'].'' .
                    '&lp=true';
            break;
        case TOOL_DOCUMENT:
            $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
            $sql = "SELECT * FROM $tbl_doc WHERE c_id = $course_id AND id=$id";
            $result = Database::query($sql);
            $myrow = Database::fetch_array($result);
            $docurl = str_replace('%2F', '/', urlencode($myrow['path']));
            $link .= $main_course_path.'document'.$docurl.'?'.api_get_cidreq();
            $openmethod = 2;
            $officedoc = false;
            Session::write('openmethod',$openmethod);
            Session::write('officedoc',$officedoc);
            break;
        case 'assignments':
            $link .= $main_dir_path.'work/work.php?origin='.$origin;
            break;
        case TOOL_DROPBOX:
            $link .= $main_dir_path.'dropbox/index.php?origin=learnpath';
            break;
        case 'introduction_text': //DEPRECATED
            $link .= '';
            break;
        case TOOL_COURSE_DESCRIPTION:
            $link .= $main_dir_path.'course_description?origin=learnpath';
            break;
        case TOOL_GROUP:
            $link .= $main_dir_path.'group/group.php?origin='.$origin;
            break;
        case TOOL_USER:
            $link .= $main_dir_path.'user/user.php?origin='.$origin;
            break;
        case 'student_publication' :
            $link .= $main_dir_path.'work/work.php?origin=learnpath';
            break;
    } //end switch
    return $link;
}

/**
 * Gets the name of a resource (generally used in learnpath when no name is provided)
 *
 * @author Yannick Warnier <ywarnier@beeznest.org>, Dokeos - rebranding
 * @param string 	Course code
 * @param string 	The tool type (using constants declared in main_api.lib.php)
 * @param integer 	The resource ID
 */
function rl_get_resource_name($course_code, $learnpath_id, $id_in_path) {
    $_course = Database::get_course_info($course_code);
    $course_id = $_course['real_id'];
    $tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

    $sql_item = "SELECT item_type, title, ref FROM $tbl_lp_item WHERE c_id = $course_id AND lp_id = $learnpath_id AND id = $id_in_path";
    $res_item = Database::query($sql_item);

    if (Database::num_rows($res_item) < 1) {
        return ''; //exit
    }
    $row_item = Database::fetch_array($res_item);
    $type = strtolower($row_item['item_type']);
    $id = $row_item['ref'];
    $output = '';

    switch ($type) {
        case TOOL_CALENDAR_EVENT:
            $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
            $result = Database::query("SELECT * FROM $TABLEAGENDA WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = $myrow['title'];
            break;
        case TOOL_ANNOUNCEMENT:
            $tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
            $result = Database::query("SELECT * FROM $tbl_announcement WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = $myrow['title'];
            break;
        case TOOL_LINK:
            // Doesn't take $target into account.
            $TABLETOOLLINK = Database::get_course_table(TABLE_LINK);
            $result = Database::query("SELECT * FROM $TABLETOOLLINK WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = $myrow['title'];
            break;
        case TOOL_QUIZ:
            $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
            $result = Database::query("SELECT * FROM $TBL_EXERCICES WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $output = $myrow['title'];
            break;
        case TOOL_FORUM:
            $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
            $result = Database::query("SELECT * FROM $TBL_FORUMS WHERE c_id = $course_id AND forum_id=$id");
            $myrow = Database::fetch_array($result);
            $output = $myrow['forum_name'];
            break;
        case TOOL_THREAD:  //=topics
            $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
            // Grabbing the title of the post.
            $sql_title = "SELECT * FROM $tbl_post WHERE c_id = $course_id AND post_id=".$id;
            $result_title = Database::query($sql_title);
            $myrow_title = Database::fetch_array($result_title);
            $output = $myrow_title['post_title'];
            break;
        case TOOL_POST:
            $tbl_post = Database::get_course_table(TABLE_FORUM_POST);
            //$tbl_post_text = Database::get_course_table(FORUM_POST_TEXT_TABLE);
            $sql = "SELECT * FROM $tbl_post p WHERE c_id = $course_id AND p.post_id = $id";
            $result = Database::query($sql);
            $post = Database::fetch_array($result);
            $output = $post['post_title'];
            break;
        case 'dokeos_chapter':
            $title = $row_item['title'];
            if (!empty($title)) {
                $output = $title;
            } else {
                $output = '-';
            }
            break;
        case TOOL_DOCUMENT:
            $title = $row_item['title'];
            if (!empty($title)) {
                $output = $title;
            } else {
                $output = '-';
            }
            break;
        case 'hotpotatoes':
            $tbl_doc = Database::get_course_table(TABLE_DOCUMENT);
            $result = Database::query("SELECT * FROM $tbl_doc WHERE c_id = $course_id AND id=$id");
            $myrow = Database::fetch_array($result);
            $pathname = explode('/', $myrow['path']); // Making a correct name for the link.
            $last = count($pathname) - 1;  // Making a correct name for the link.
            $filename = $pathname[$last];  // Making a correct name for the link.
            $image = choose_image($filename);
            $ext = explode('.', $filename);
            $ext = strtolower($ext[sizeof($ext) - 1]);
            $myrow['path'] = rawurlencode($myrow['path']);
            $in_frames = in_array($ext, array('htm', 'html', 'gif', 'jpg', 'jpeg', 'png'));
            $output = $filename;
            break;
        /*
        case 'externallink':
            $output = '<img src="../img/links.gif" align="middle" /> <a href="'.$id.'"'.$styling.' '.$target.'>'.$id."</a><br />\n";
            break;
        */
    }
    return stripslashes($output);
}
resourcelinker.php000064400000124773152003363470010332 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * @author Patrick Cool, original code
 * @author Denes Nagy - many bugfixes and improvements, adjusted for learning path
 * @author Roan Embrechts - refactoring, code cleaning
 * @package chamilo.resourcelinker
 * @todo reorganise code,
 * use Database API instead of creating table names locally.
 */
/**
 * Code
 */
/* INIT SECTION */

// Flag to allow for anonymous user - needs to be set before global.inc.php.

use \ChamiloSession as Session;

$use_anonymous = true;

// Name of the language file that needs to be included.
$language_file = 'resourcelinker';

require_once 'back_compat.inc.php';

$this_section = SECTION_COURSES;

api_protect_course_script();

/* Libraries */

include 'resourcelinker.inc.php';

/* Constants and variables */

$link_table = Database :: get_course_table(TABLE_LINK);
$item_property_table = Database :: get_course_table(TABLE_ITEM_PROPERTY);

//$tbl_learnpath_main = Database :: get_course_table(TABLE_LEARNPATH_MAIN);
//$tbl_learnpath_chapter = Database :: get_course_table(TABLE_LEARNPATH_CHAPTER);
//$tbl_learnpath_item = Database :: get_course_table(TABLE_LEARNPATH_ITEM);
$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
$tbl_lp_item = Database::get_course_table(TABLE_LP_ITEM);

$action = $_REQUEST['action'];
$add = $_REQUEST['add'];
$chapter_id = $_REQUEST['parent_item_id'];
$content = $_REQUEST['content'];
// Note by Patrick Cool: This has been solved belowd. This piece of code hacking produced too much errors.
/*
if (empty($content)) {
    // Adds a default to the item-type selection.
    $content = 'Document';
}
*/
$folder = $_REQUEST['folder'];
$id = $_REQUEST['id'];
$learnpath_id = $_REQUEST['lp_id'];
$originalresource = $_REQUEST['originalresource'];
$show_resources = $_REQUEST['show_resources'];
$source_forum = $_REQUEST['source_forum'];
$source_id = $_REQUEST['source_id'];
$target = $_REQUEST['target'];
$external_link = $_REQUEST['external_link'];

$from_learnpath = $_SESSION['from_learnpath'];

// This variable controls wether the link to add a chapter in a module or
// another chapter is shown. This allows to create multi-level learnpaths,
// but export features are not ready for this, yet, so use at your own risks
// default : false -> do not display link
// This setting should be moved to the platform configuration page in time...
$multi_level_learnpath = true;

/* MAIN CODE */

if ($from_learnpath == 'yes') {
    // Start from clear every time in LearnPath Builder.
    $_SESSION['addedresource'] = null;
    $_SESSION['addedresourceid'] = null;
    $_SESSION['addedresourceassigned'] = null;
    unset ($_SESSION['addedresource']);
    unset ($_SESSION['addedresourceid']);
    unset ($_SESSION['addedresourceassigned']);
}

$course_id = api_get_course_int_id();

// Process a new chapter?
if (!empty ($_POST['add_chapter']) && !empty ($_POST['title'])) {
    $title = $_POST['title'];
    $description = '';
    /*if (!empty ($_POST['description'])) {
        $description = $_POST['description'];
    }*/

    // Get max display_order so far in this parent chapter.
    $sql = "SELECT MAX(display_order) as maxi FROM $tbl_lp_item " .
            "WHERE c_id = $course_id AND lp_id = $learnpath_id ".
            " AND parent_item_id = $chapter_id";
    $res = Database::query($sql);
    $row = Database::fetch_array($res);
    $lastorder_item = $row['maxi'];
    if (empty($lastorder_item)) {
        $lastorder_item = 0;
        $previous = 0;
    } else {
        $sql = "SELECT id FROM $tbl_lp_item " .
                "WHERE lp_id = $learnpath_id AND parent_item_id=$chapter_id AND display_order = $lastorder_item";
        $result = Database::query($sql);
        $row = Database::fetch_array($result);
        $previous = $row['id'];
    }
    $order = $lastorder_item + 1;

    $sql = "INSERT INTO $tbl_lp_item "."(c_id, lp_id,item_type,title,parent_item_id,previous_item_id, next_item_id, display_order) " .
            "VALUES "."($course_id, $learnpath_id,'dokeos_chapter','$title', $chapter_id, $previous, 0, $order )";
    //error_log('New LP - Inserting new resource: '.$sql, 0);
    $res = Database::query($sql);
    $my_id = Database::insert_id($res);
    if ($previous > 0) {
        $sql = "UPDATE $tbl_lp_item SET next_item_id = $my_id WHERE id=$previous";
        $res = Database::query($sql);
    }
    if ($res !== false) {
        $title = '';
        $description = '';
    }
    $resource_added = true;
}

// This if when a external link is submitted.
if (!empty ($_POST['external_link_submit'])) {
    if ($external_link == '' || $external_link == 'http://') {
        $InvalidURL = 1;
    } else {
        $add = true;
        if ($add_2_links != 'niet toevoegen') {
            // Add external link to the links table.
            $pos = strpos($external_link, 'ttp:');
            if ($pos == '') {
                $external_link = 'http://'.$external_link;
            }

            $sql = "INSERT INTO $link_table (c_id, url, title, category_id) VALUES ($course_id, '$external_link','$external_link','$add_2_links')";
            $result = Database::query($sql);
            $addedresource[] = "Link";
            $addedresourceid[] = Database::insert_id();
            $_SESSION['addedresource'] = $addedresource;
            $_SESSION['addedresourceid'] = $addedresourceid;
        } else {
            // Do not add external link to the links table.
            $addedresource[] = "Externallink";
            $addedresourceid[] = $external_link;
            $_SESSION['addedresource'] = $addedresource;
            $_SESSION['addedresourceid'] = $addedresourceid;
        }
    }
}

// Loading the session variables into local array.
$addedresource = $_SESSION['addedresource'];
$addedresourceid = $_SESSION['addedresourceid'];

// This is when a resource was added to the session.
if ($add) {
    // Adding the new variable to the local array.
    if (empty($_POST['external_link_submit'])) {
        // That case is already arranged, see upwards.
        $addedresource[] = $content;
        $addedresourceid[] = $add;
    }
    $addedresourceassigned[] = 0;

    // Loading the local array into the session variable.
    $_SESSION['addedresource'] = $addedresource;
    $_SESSION['addedresourceid'] = $addedresourceid;

    // We assign to chapters immediately!
    $resource_added = false;
    if ($from_learnpath == 'yes') {
        $i = 0;
        // Calculating the last order of the items of this chapter.
        $sql = "SELECT MAX(display_order) as maxi FROM $tbl_lp_item " .
                "WHERE c_id = $course_id AND lp_id = $learnpath_id AND parent_item_id=$chapter_id";
        $result = Database::query($sql);
        $row = Database::fetch_array($result);
        $lastorder_item = $row['maxi'];
        if (empty($lastorder_item)) {
            $lastorder_item = 0;
            $previous = 0;
        } else {
            $sql = "SELECT id FROM $tbl_lp_item " .
                    "WHERE lp_id = $learnpath_id AND parent_item_id=$chapter_id AND display_order = $lastorder_item";
            //error_log('New LP - resourcelinker.php - '.$sql, 0);
            $result = Database::query($sql);
            $row = Database::fetch_array($result);
            $previous = $row['id'];
        }
        $lastorder = $lastorder_item + 1;
        foreach ($addedresource as $addedresource_item) {
            // In the case we added a chapter, add this into the chapters list with the correct parent_id.
            if ($addedresource_item == 'Chap') {
                $sql = "INSERT INTO $tbl_lp_item " .
                        "(c_id, lp_id,item_type,title,parent_item_id,previous_item_id,next_item_id,display_order) " .
                        "VALUES ($course_id, ".$learnpath_id.",'dokeos_chapter','".$learnpath_chapter_name."',".$chapter_id.",$previous,0,".$lastorder.")";
                //error_log('New LP - Inserting new resource: '.$sql, 0);
                $res = Database::query($sql);
                $my_id = Database::insert_id($res);
                if ($previous > 0) {
                    $sql = "UPDATE $tbl_lp_item SET next_item_id = $my_id WHERE id=$previous";
                    $res = Database::query($sql);
                }
            }

            if (!$addedresourceassigned[$i]) {
                // Not to assign it twice
                switch ($addedresource_item) {
                    case 'Assignment':
                    case 'Ass':
                        //set tool type
                        $addedresource_item = 'Assignments';
                        $title = get_lang('Assignments');
                        break;
                    case 'Drop':
                        //$addedresource_item = 'Dropbox';
                        $addedresource_item = TOOL_DROPBOX;
                        $title = get_lang('Dropbox');
                        break;
                    case 'Intro':
                        $addedresource_item = 'Introduction_text';
                        //$addedresource_item = TOOL_INTRO;
                        $title = get_lang('IntroductionText');
                        break;
                    case 'Course_desc':
                        //$addedresource_item = 'Course_description';
                        $addedresource_item = TOOL_COURSE_DESCRIPTION;
                        $title = get_lang('CourseDescription');
                        break;
                    case 'Group':
                        //$addedresource_item = 'Groups';
                        $addedresource_item = TOOL_GROUP;
                        $title = get_lang('Groups');
                        break;
                    case 'User':
                        //$addedresource_item = 'Users';
                        $addedresource_item = TOOL_USER;
                        $title = get_lang('Users');
                        break;
                    case 'Link':
                        /*
                        if ($target == '') {
                            $target = '_self';
                        }
                        */
                        //$addedresource_item .= ' '.$target;
                        //error_log('New LP - resourcelinker.php - In Link addition: '.$external_link);
                        $addedresource_item = TOOL_LINK;
                        $title = $external_link;
                        break;
                    case 'Document':
                        $addedresource_item = TOOL_DOCUMENT;
                        //get title from tool-type table
                        $tooltable = Database::get_course_table(TABLE_DOCUMENT);
                        $result = Database::query("SELECT * FROM $tooltable WHERE id=".$addedresourceid[$i]);
                        $myrow=Database::fetch_array($result);
                        $title = $myrow['title'];
                        break;
                    case 'Exercise':
                        $addedresource_item = TOOL_QUIZ;
                        //get title from tool-type table
                        $tooltable = Database::get_course_table(TABLE_QUIZ_TEST);
                        $result = Database::query("SELECT * FROM $tooltable WHERE id=".$addedresourceid[$i]);
                        $myrow=Database::fetch_array($result);
                        $title = $myrow['title'];
                        break;
                    case 'Forum':
                        $addedresource_item = TOOL_FORUM;
                        //TODO
                        break;
                    case 'Agenda':
                        $addedresource_item = TOOL_CALENDAR_EVENT;
                        //get title from tool-type table
                        $tooltable = Database::get_course_table(TABLE_AGENDA);
                        $result = Database::query("SELECT * FROM $tooltable WHERE id=".$addedresourceid[$i]);
                        $myrow=Database::fetch_array($result);
                        $title = $myrow['title'];
                        break;
                    case 'Ad_Valvas':
                        $addedresource_item = TOOL_ANNOUNCEMENT;
                        //get title from tool-type table
                        $tooltable = Database::get_course_table(TABLE_ANNOUNCEMENT);
                        $result = Database::query("SELECT * FROM $tooltable WHERE id=".$addedresourceid[$i]);
                        $myrow=Database::fetch_array($result);
                        $title = $myrow['title'];
                        break;

                }
                $sql = "INSERT INTO $tbl_lp_item (c_id, lp_id, title, parent_item_id, item_type, ref, previous_item_id, next_item_id, display_order) " .
                        "VALUES ($course_id, $learnpath_id, '$title','$chapter_id', '$addedresource_item','$addedresourceid[$i]',$previous,0,'".$lastorder."')";
                //error_log('New LP - Inserting new resource: '.$sql, 0);
                $result = Database::query($sql);
                $my_id = Database::insert_id($result);
                if ($previous > 0) {
                    $sql = "UPDATE $tbl_lp_item SET next_item_id = $my_id WHERE id = $previous";
                    //error_log($sql, 0);
                    $res = Database::query($sql);
                }
                $addedresourceassigned[$i] = 1;
                $resource_added = true;
            }
            $i ++;
            $lastorder ++;
        }
        //$_SESSION['addedresource']=null;
        //$_SESSION['addedresourceid']=null;
        // cleaning up the session once again
        $_SESSION['addedresource'] = null;
           $_SESSION['addedresourceid'] = null;
           $_SESSION['addedresourceassigned'] = null;
           unset ($_SESSION['addedresource']);
           unset ($_SESSION['addedresourceid']);
           unset ($_SESSION['addedresourceassigned']);
    }
}

/*
    BREADCRUMBS
    This part is to allow going back to the tool where you came from
    in a previous version I used the table tool_list, but since the forum can access the
    resource_linker from two different pages (newtopic.php and editpost.php) and this is different
    from the link field in tool_list, I decide to hardcode this stuff here.
    By doing this, you can easily control which pages can access the toollinker and which not.
*/

if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}

if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array (
            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
            'name' => get_lang('ToolGradebook')
        );
}

if ($_GET['source_id']) {
    switch ($_GET['source_id']) {
        case '1': // coming from Agenda
            if ($action == 'edit') {
                $url = "../calendar/agenda.php?action=edit&id=49&originalresource=$originalresource";
            }
            elseif ($action == 'add') {
                $url = "../calendar/agenda.php?action=add&originalresource=$originalresource";
            } else {
                $url = "../calendar/agenda.php?action=add";
            }
            $originaltoolname = get_lang('Agenda');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;
        case '2': // coming from forum: new topic
            $url = "../phpbb/newtopic.php?forum=$source_forum&md5=$md5";
            $originaltoolname = get_lang('ForumAddNewTopic');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;
        case '3': // coming from forum: edit topic
            $url = "../phpbb/editpost.php?post_id=$post_id&topic=$topic&forum=$forum&md5=$md5&originalresource=no";
            $originaltoolname = get_lang('ForumEditTopic');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;
        case '4': // coming from exercises: edit topic
            $url = "../exercice/admin.php?modifyAnswers=$modifyAnswers";
            $originaltoolname = get_lang('ExerciseAnswers');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;
        case '5': // coming from learning path
            $from_learnpath = 'yes';
            Session::write('from_learnpath',$from_learnpath);
            break;
        case '6': // coming from forum: reply
            $url = "../phpbb/reply.php?topic=$topic&forum=$forum&parentid=$parentid";
            $url = $_SESSION['origintoolurl'];
            $originaltoolname = get_lang('ForumReply');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;

        /* add Frederik.Vermeire@pandora.be */

        case '7': // coming from Ad_Valvas
            if ($action == 'edit') {
                $url = "../announcements/announcements.php?action=edit&id=49&originalresource=$originalresource";
            } elseif ($action == 'add') {
                $url = "../announcements/announcements.php?action=add&originalresource=$originalresource";
            } else {
                $url = "../announcements/announcements.php?action=add";
            }
            $originaltoolname = get_lang('AdValvas');
            $breadcrumbelement = array ('url' => $url, 'name' => $originaltoolname);
            session_unregister('from_learnpath');
            unset ($from_learnpath);
            break;

        /*  end add Frederik.Vermeire@pandora.be */

    }
    // We do not come from the learning path. We store the name of the tool & url in a session.
    if ($from_learnpath != 'yes') {
        if (!$_SESSION['origintoolurl'] || $_SESSION['origintoolurl'] != $interbreadcrumb['url']) {
            $_SESSION['origintoolurl'] = $breadcrumbelement['url'];
            $_SESSION['origintoolname'] = $breadcrumbelement['name'];
            $interbreadcrumb = '';
        }
    }

}

// This part of the code is the actual breadcrumb mechanism. If we do not come from the learning path we use
// the information from the session. Else we use the information of the learningpath itself.
if ($from_learnpath != 'yes') {
    $nameTools = get_lang('Attachment');
    $interbreadcrumb[] = array ('url' => $_SESSION['origintoolurl'], 'name' => $_SESSION['origintoolname']);
} else {
    $learnpath_select_query = "	SELECT * FROM $tbl_lp
                                          WHERE id=$learnpath_id";
    $sql_result = Database::query($learnpath_select_query);
    $therow = Database::fetch_array($sql_result);

    $learnpath_chapter_query = "	SELECT * FROM $tbl_lp_item
                                          WHERE (lp_id = '$learnpath_id' and id = '$chapter_id')";
    $sql_result = Database::query($learnpath_chapter_query);
    $therow2 = Database::fetch_array($sql_result);

    $from_learnpath = 'yes';
    session_register('from_learnpath');
    $interbreadcrumb[] = array ('url' => "../newscorm/lp_controller.php?action=list", 'name' => get_lang('LearningPath'));
    $interbreadcrumb[] = array ('url' => "../newscorm/lp_controller.php?action=admin_view&lp_id=$learnpath_id", 'name' => stripslashes("{$therow['name']}"));
    $interbreadcrumb[] = array ('url' => api_get_self()."?action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no", 'name' => "{$therow2['title']}");
}

$htmlHeadXtra[] = '<script type="text/javascript">
/* <![CDATA[ */
    function targetfunc(input)
    {
        window.location=window.location+"&amp;target="+document.learnpath_link.target.value;
    }
/* ]]> */
</script>';

Display :: display_header($nameTools);

echo "<h3>".$nameTools;
if ($from_learnpath == 'yes') {
    echo get_lang('AddResource')." - {$therow2['title']}";
}
echo "</h3>";

// We retrieve the tools that are active.
// We use this to check which resources a student may add (only the modules that are active).
// See http://www.dokeos.com/forum/viewtopic.php?t=4858
$active_modules = array();
$tool_table = Database::get_course_table(TABLE_TOOL_LIST);
$sql_select_active = "SELECT * FROM $tool_table WHERE visibility='1'";
$result_select_active = Database::query($sql_select_active);
while ($row=Database::fetch_array($result_select_active)) {
    $active_modules[] = $row['name'];
}
?>

<table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr>
    <td width="300" valign="top" style='padding-right:15px;'>
      <table width="300" border="0" cellspacing="0" cellpadding="0" style='border-right:1px solid grey;'>
<?php if ($from_learnpath != 'yes') { ?>
        <tr>
          <td width="26%"><b><?php echo get_lang('CourseResources'); ?></b></td>
        </tr>
        <?php
        if (api_is_allowed_to_edit() || in_array(TOOL_DOCUMENT, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Document&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Documents')."</a>"; ?></td>
        </tr>
        <?php
        }
        if (api_is_allowed_to_edit() || in_array(TOOL_CALENDAR_EVENT, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Agenda&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Agenda')."</a>"; ?></td>
        </tr>
        <?php
        }
        if (api_is_allowed_to_edit() || in_array(TOOL_ANNOUNCEMENT, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Ad_Valvas&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('AdValvas')."</a>"; ?></td>
        </tr>
        <?php
        }
        if (api_is_allowed_to_edit() || in_array(TOOL_BB_FORUM, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Forum&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Forum')."</a>"; ?></td>
        </tr>
        <?php
        }
        if (api_is_allowed_to_edit() || in_array(TOOL_LINK, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Link&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Links')."</a>"; ?></td>
        </tr>
        <?php
        }
        if (api_is_allowed_to_edit() || in_array(TOOL_QUIZ, $active_modules)) {
        ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Exercise&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Exercise')."</a>"; ?></td>
        </tr>
        <?php
        }
} else {
?>
        <!--tr>
          <td width="26%"><b><?php echo get_lang('ExportableCourseResources'); ?></b></td>
        </tr-->
<?php if ($multi_level_learnpath === true ) { ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=chapter&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Chapter')."</a>"; ?></td>
        </tr>
<?php } ?>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Document&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Document')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Exercise&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Exercise')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Link&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Link')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Forum&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Forum')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Agenda&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Agenda')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Ad_Valvas&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('AdValvas')."</a>"; ?></td>
        </tr>
        <!--tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Course_description&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('CourseDescription')."</a>"; ?></td>
        </tr-->
        <!--tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Introduction_text&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('IntroductionText')."</a>"; ?></td>
        </tr-->
        <!--tr>
          <td>&nbsp;</td>
        </tr-->
        <!--tr>
          <td width="26%"><b><?php echo get_lang('DokeosRelatedCourseMaterial'); ?></b></td>
        </tr-->
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Dropbox&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Dropbox')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Assignment&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Assignments')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Groups&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Groups')."</a>"; ?></td>
        </tr>
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Users&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('Users')."</a>"; ?></td>
        </tr>

        <?php
}
?>
        <!--tr>
          <td>&nbsp;</td>
        </tr-->
        <!--tr>
          <td><b><?php echo get_lang('ExternalResources'); ?></b></td>
        </tr-->
        <tr>
          <td><?php echo "<a href=\"".api_get_self()."?content=Externallink&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('ExternalLink')."</a>"; ?></td>
        </tr>
        <?php

if ($from_learnpath != 'yes') {
    echo "<tr><td>&nbsp;</td></tr>";
    echo "<tr><td><b>".get_lang('ResourcesAdded')." (";
    echo count($addedresource);
    echo ")</b></td></tr>";
    echo "<tr><td nowrap><a href=\"".api_get_self()."?showresources=true&action=$action&id=$id&lp_id=$learnpath_id&parent_item_id=$chapter_id&source_forum=$source_forum&originalresource=no\">".get_lang('ShowDelete')."</a>";
    echo "</td></tr>";
}
?>
        <tr>
          <td>&nbsp;</td>
        </tr>
        <!--<tr>
          <td><b><?php echo get_lang('BackTo'); ?></b></td>
        </tr>//-->
        <tr>
          <td>

          <?php

if ($from_learnpath != 'yes') {
    echo "<form method=\"post\" action=\"{$_SESSION['origintoolurl']}\" style=\"margin: 0px;\"><input type=\"submit\" value=\"".'  '.get_lang('Ok').'  '."\"></form>";
} else {
    echo "<form method=\"get\" action=\"lp_controller.php\" style=\"margin: 0px;\"><input type=\"hidden\" name=\"lp_id\" value=\"".htmlentities($learnpath_id)."\"><input type=\"hidden\" name=\"action\" value=\"admin_view\"><input type=\"submit\" value=\"".'  '.get_lang('Ok').'  '."\"></form>";
}
?>

          </td>
        </tr>
        <tr>
          <td>&nbsp;</td>
        </tr>
      </table>
    </td>
    <td valign="top">
      <?php

if ($resource_added) {
    Display :: display_normal_message(get_lang('ResourceAdded'));
}
if ($InvalidURL) {
    Display :: display_normal_message(get_lang('GiveURL'));
}

if ($from_learnpath != 'yes') {
    echo count($addedresource)." ".strtolower(get_lang('ResourcesAdded'))."<br />";
}
//echo "<hr />";

// Agenda items -->
if ($content == 'Agenda') {
    $TABLEAGENDA = Database::get_course_table(TABLE_AGENDA);
    $TABLE_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);

    $sql = "SELECT agenda.*, toolitemproperties.*
                    FROM ".$TABLEAGENDA." agenda, ".$TABLE_ITEM_PROPERTY." toolitemproperties
                    WHERE agenda.id = toolitemproperties.ref
                    AND toolitemproperties.tool='".TOOL_CALENDAR_EVENT."'
                    AND toolitemproperties.to_group_id='0'
                    AND toolitemproperties.visibility='1'";

    $result = Database::query($sql);

    while ($myrow = Database::fetch_array($result)) {
        echo "<table width=\"100%\"><tr><td bgcolor=\"#E6E6E6\">";
        echo "<img src='../img/agenda.gif' alt='agenda'>";
        echo api_convert_and_format_date($myrow['start_date'], null, date_default_timezone_get())."<br />";
        echo "<b>".$myrow['title']."</b></td></tr><tr><td>";
        echo $myrow['content']."<br />";
        showorhide_addresourcelink($content, $myrow['id']);
        echo "</td></tr></table><br />";
    }
} // end if ($_GET['resource'] == 'Agenda')

/* Chapter */

if ($content == 'chapter') {
    echo '<table><form name="add_chapter" action="'.'" method="POST">'."\n";
    echo '  <tr><td>'.get_lang('Title').'</td><td><input type="text" name="title" value="'.$title.'"></input></td></tr>'."\n";
    echo '  <tr><td>'.get_lang('Description').'</td><td><input type="text" name="description" value="'.$description.'"></input></td></tr>'."\n";
    echo '  <tr><td></td><td><input type="submit" name="add_chapter" value="'.get_lang('AddIt').'"/></td></tr>'."\n";
    echo '</form></table>'."\n";
    //echo "<hr />";
}

/* Documents */

// We show the documents in the following cases
// 1. the link to add documenets in the resource linker was clicked
// 2. we come to the resource linker for the first time (documents = default). In this case it can only be shown if
//  			a. one is a teacher (documents can be shown even if the tool is inactive)
//				b. one is a student AND the documents tool is active. Student cannot add documents if the documents tool is inactive (teacher can do this)
if ($content == 'Document' || (empty($content) && (api_is_allowed_to_edit() || in_array(TOOL_DOCUMENT, $active_modules))) && !$_GET['showresources']) {
    // setting variables for file locations
    $baseServDir = $_configuration['root_sys'];
    $courseDir = $_course['path'].'/document';
    $baseWorkDir = $baseServDir.$courseDir;
    // showing the link to move one folder up (when not in the root folder)
    show_folder_up();
    // showing the blue bar with the path in it when we are not in the root
    if (get_levels($folder)) {
        echo "<table width=\"100%\"><tr><td bgcolor=\"#4171B5\">";
        echo "<img src=\"../img/opendir.gif\" alt='directory'><font color=\"#ffffff\"><b>";
        echo $folder."</b></font></td></tr></table>";
    }

    // Showing the documents and subfolders of the folder we are in.
    show_documents($folder);
    //echo "<hr />";
}

/* Ad Valvas */

if ($content == 'Ad_Valvas') {
    $tbl_announcement = Database :: get_course_table(TABLE_ANNOUNCEMENT);
    $sql = "SELECT * FROM ".$tbl_announcement." a, ".$item_property_table." i  WHERE i.tool = '".TOOL_ANNOUNCEMENT."' AND a.id=i.ref AND i.visibility='1' AND i.to_group_id = 0 AND i.to_user_id IS NULL ORDER BY a.display_order ASC";
    //error_log($sql, 0);
    $result = Database::query($sql);
    while ($myrow = Database::fetch_array($result)) {
        echo "<table width=\"100%\"><tr><td>";
        echo "<img src='../img/valves.gif' alt='advalvas'>";
        echo api_convert_and_format_date($myrow['end_date'], DATE_FORMAT_LONG, date_default_timezone_get());
        echo "</td></tr><tr><td>";
        echo $myrow['title']."<br />";
        showorhide_addresourcelink($content, $myrow['id']);
        echo "</td></tr></table>";
    }
}

/* Forums */

if ($content == 'Forum') {
    $TBL_FORUMS = Database::get_course_table(TABLE_FORUM);
    $TBL_CATAGORIES = Database::get_course_table(TABLE_FORUM_CATEGORY);
    $TBL_FORUMTOPICS = Database::get_course_table(TABLE_FORUM_THREAD);
    $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);

    echo "<table width='100%'>";

    // Displaying the categories and the forums.
    if (!$forum and !$thread) {
        $sql = "SELECT * FROM ".$TBL_FORUMS." forums, ".$TBL_CATAGORIES." categories WHERE forums.forum_category=categories.cat_id ORDER BY forums.forum_category DESC";
        //error_log($sql, 0);
        $result = Database::query($sql);
        while ($myrow = Database::fetch_array($result)) {
            if ($myrow['cat_title'] !== $old_cat_title) {
                echo "<tr><td bgcolor='#4171B5' colspan='2'><font color='white'><b>".$myrow['cat_title']."</b></font></td></tr>";
            }
            $old_cat_title = $myrow['cat_title'];
            echo "<tr><td><img src='../img/forum.gif'><a href='".api_get_self()."?content=Forum&category=".$myrow['cat_id']."&forum=".$myrow['forum_id']."&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".$myrow['forum_name']."</td><td>";
            showorhide_addresourcelink('Forum', $myrow['forum_id']);
            echo "</td></tr>";
        }
    }
    // Displaying all the threads of one forum.
    if ($forum) {
        // Displaying the category title.
        $sql = "SELECT * FROM ".$TBL_CATAGORIES." WHERE cat_id=$category";
        $result = Database::query($sql);
        $myrow = Database::fetch_array($result);
        echo "<tr><td bgcolor='#4171B5' colspan='2'><font color='white'><b>".$myrow['cat_title']."</b></font></td></tr>";

        // Displaying the forum title.
        $sql = "SELECT * FROM ".$TBL_FORUMS." forums, ".$TBL_FORUMTOPICS." topics WHERE forums.forum_id=topics.forum_id";
        $result = Database::query($sql);
        $myrow = Database::fetch_array($result);
        echo "<tr><td bgcolor='#cccccc' colspan='2'><b>".$myrow['forum_name']."</b></td></tr>";

        if (!$thread) {
            // Displaying all the threads of this forum.
            $sql = "SELECT * FROM ".$TBL_FORUMTOPICS." WHERE forum_id=$forum";
            $result = Database::query($sql);
            while ($myrow = Database::fetch_array($result)) {
                echo "<tr><td><a href='".api_get_self()."?content=Forum&category=$category&forum=1&thread=".$myrow['topic_id']."&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".$myrow['topic_title']."</a>  (".$myrow['prenom']." ".$myrow['nom'].")</td><td>";
                showorhide_addresourcelink("Thread", $myrow['topic_id']);
                echo "</td></tr>";
            }
        } else {
            // Displaying all the replies.
            $sql = "SELECT * FROM ".$tbl_posts." post WHERE post.topic_id=$thread ORDER BY post.post_id ASC";
            $result = Database::query($sql);
            while ($myrow = Database::fetch_array($result)) {
                echo "<tr><td><b>".$myrow['post_title']."</b><br />";
                echo $myrow['post_text']."</td>";
                echo "<td>";
                showorhide_addresourcelink('Post', $myrow['post_id']);
                echo "</td></tr><tr><td colspan='2'><hr noshade></td></tr>";
            }

        }
    }
    echo "</table>";
}

/* Links */

if ($content == 'Link') {
    // Including the links language file.
    include "../lang/$language/link.inc.php";

    // Including the links functions file.
    require_once api_get_path(LIBRARY_PATH).'link.lib.php';

    $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
    if (($learnpath_id != '') and ($content == 'Link')) {
        echo "<form name='learnpath_link'><table>";
        echo "<tr></td><td align='left'>".get_lang('LinkTarget')." :</td><td align='left'><select name='target' onchange='javascript: targetfunc();'><option value='_self' ";
        if ($target == '_self') {
            echo "selected";
        }
        echo ">".get_lang('SameWindow')."</option><option value='_blank'";
        if ($target == '_blank') {
            echo "selected";
        }
        echo ">".get_lang('NewWindow')."</option></select></td></tr></table></form>";
    }

    // Showing the links that are in the root (having no category).
    $sql = "SELECT * FROM ".$link_table." l, ".$item_property_table." ip WHERE (l.category_id=0 or l.category_id IS NULL) AND ip.tool = '".TOOL_LINK."' AND l.id=ip.ref AND ip.visibility='1'";
    $result = Database::query($sql);
    if (Database::num_rows($result) > 0) {
        echo "<table width=\"100%\"><tr><td bgcolor=\"#E6E6E6\"><i>".get_lang('NoCategory')."</i></td></tr></table>";
        while ($myrow = Database::fetch_array($result)) {
            echo "<img src='../img/links.gif'>".$myrow['title'];
            echo "<br />";
            showorhide_addresourcelink($content, $myrow['id']);
            echo "<br /><br />";
        }
    }

    // Showing the categories and the links in it.
    $sqlcategories = "SELECT * FROM ".$tbl_categories." ORDER by display_order DESC";
    $resultcategories = Database::query($sqlcategories) or die;
    while ($myrow = @ Database::fetch_array($resultcategories)) {
        $sql_links = "SELECT * FROM ".$link_table." l, ".$item_property_table." ip WHERE l.category_id='".$myrow['id']."' AND ip.tool = '".TOOL_LINK."' AND l.id=ip.ref AND ip.visibility='1' ORDER BY l.display_order DESC";
        echo "<table width=\"100%\"><tr><td bgcolor=\"#E6E6E6\"><i>".$myrow['category_title']."</i></td></tr></table>";
        $result_links = Database::query($sql_links);
        while ($myrow = Database::fetch_array($result_links)) {
            echo "<img src='../img/links.gif' />".$myrow['title'];
            echo "<br />";
            showorhide_addresourcelink($content, $myrow['id']);
            echo "<br /><br />";
        }
    }
}

/* Exercise */

if (($content == 'Exercise') || ($content == 'HotPotatoes')) {
    $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
    $result = Database::query("SELECT * FROM ".$TBL_EXERCICES." WHERE active='1' ORDER BY id ASC");
    while ($myrow = Database::fetch_array($result)) {
        echo "<img src='../img/quiz.gif'>".$myrow['title']."<br />";
        showorhide_addresourcelink($content, $myrow["id"]);
        echo "<br /><br />";
    }

    if ($from_learnpath == 'yes') {
        $uploadPath = '/HotPotatoes_files';
        $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
        $documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
        $sql = "SELECT * FROM ".$TBL_DOCUMENT." WHERE (path LIKE '%htm%' OR path LIKE '%html%') AND path LIKE '".$uploadPath."/%/%' ORDER BY id ASC";
        $result = Database::query($sql);
        while ($myrow = Database::fetch_array($result)) {
            $path = $myrow['path'];
            echo "<img src='../img/jqz.jpg'>".GetQuizName($path, $documentPath)."<br />";
            showorhide_addresourcelink('HotPotatoes', $myrow['id']);
            echo "<br /><br />";

        }
    }
}

/* External Links */

if ($content == 'Externallink') {
?>
  <form name="form1" method="post" action="">
  <table width="80%" border="0" cellspacing="0" cellpadding="0">
    <tr>
      <td align="right"><?php echo get_lang('ExternalLink'); ?> : &nbsp;</td>
      <td align="left"><input name="external_link" type="text" id="external_link" value="http://"></td>
      <?php

    if ($learnpath_id != '') {
        echo "</tr><tr><td align='right'>".get_lang('LinkTarget')." :</td><td align='left'><select name='target'><option value='_self'>".get_lang('SameWindow')."</option><option value='_blank'>".get_lang('NewWindow')."</option></select></td>";
    }
?>
    </tr>
    <tr>
      <td><?php if ($is_allowedToEdit) {echo get_lang('AddToLinks');} ?></td>
      <td>
        <?php if ($is_allowedToEdit){?>
      <select name="add_2_links" id="add_2_links">
      <option value="niet toevoegen" selected="selected">-<?php echo get_lang('DontAdd'); ?>-</option>
      <option value="0"><?php echo get_lang('MainCategory'); ?></option>
        <?php

    $tbl_categories = Database::get_course_table(TABLE_LINK_CATEGORY);
    $sql = "SELECT * FROM $tbl_categories ORDER BY display_order ASC";
    echo $sql;
    $result = Database::query($sql);
    while ($row = Database::fetch_array($result)) {
        echo "<option value='".$row['id']."'>".$row['category_title']."</option>";
    }
?>

      </select><?php } ?></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td><input name="external_link_submit" type="submit" id="external_link_submit" value="<?php echo get_lang('AddIt'); ?>"></td>
    </tr>
    <tr>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td colspan="2">&nbsp;</td>
    </tr>
  </table>
</form>
    <?php

}

/* Assignments */

if ($content == 'Assignment') {
    echo "<a href=".api_get_self()."?content=Ass&add=Ass&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no>".get_lang('AddAssignmentPage')."</a>";
}

/* Dropbox */

if ($content == 'Dropbox') {
    echo "<a href='".api_get_self()."?content=Drop&add=Drop&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".get_lang('DropboxAdd')."</a>";
}

/* Introduction text */

if ($content == 'Introduction_text') {
    echo "<a href='".api_get_self()."?content=Intro&add=Intro&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".get_lang('IntroductionTextAdd')."</a>";
}

/* Course description */

if ($content == 'Course_description') {
    echo "<a href='".api_get_self()."?content=Course_desc&add=Course_desc&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".get_lang('CourseDescriptionAdd')."</a>";
}

/* Groups */

if ($content == 'Groups') {
    echo "<a href='".api_get_self()."?content=Group&add=Group&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".get_lang('GroupsAdd')."</a>";
}

/* Users */

if ($content == 'Users') {
    echo "<a href='".api_get_self()."?content=User&add=User&action=$action&lp_id=$learnpath_id&parent_item_id=$chapter_id&originalresource=no'>".get_lang('UsersAdd')."</a>";
}

if ($showresources) {
    //echo "<h4>".get_lang('ResourceAdded')."</h4>";
    display_resources(1);
}

echo "</td></tr></table>";

/* FOOTER */

Display :: display_footer();
scorm.class.php000064400000130453152003363470007515 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Defines the scorm class, which is meant to contain the scorm items (nuclear elements)
 * @package chamilo.learnpath.scorm
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * Includes
 */
require_once 'scormItem.class.php';
require_once 'scormMetadata.class.php';
require_once 'scormOrganization.class.php';
require_once 'scormResource.class.php';

/**
 * Defines the "scorm" child of class "learnpath"
 * @package chamilo.learnpath
 */
class scorm extends learnpath {
    public $manifest = array();
    public $resources = array();
    public $resources_att = array();
    public $organizations = array();
    public $organizations_att = array();
    public $metadata = array();
    public $idrefs = array(); // Will hold the references to resources for each item ID found.
    public $refurls = array(); // For each resource found, stores the file url/uri.
    public $subdir = ''; // Path between the scorm/ directory and the imsmanifest.xml e.g. maritime_nav/maritime_nav. This is the path that will be used in the lp_path when importing a package.
    public $items = array();
    public $zipname = ''; // Keeps the zipfile safe for the object's life so that we can use it if no title avail.
    public $lastzipnameindex = 0; // Keeps an index of the number of uses of the zipname so far.
    public $manifest_encoding = 'UTF-8';
    public $debug = false;

    /**
     * Class constructor. Based on the parent constructor.
     * @param	string	Course code
     * @param	integer	Learnpath ID in DB
     * @param	integer	User ID
     */
    function __construct($course_code = null, $resource_id = null, $user_id = null) {
        if ($this->debug > 0) { error_log('New LP - scorm::scorm('.$course_code.','.$resource_id.','.$user_id.') - In scorm constructor', 0); }
        if (!empty($course_code) && !empty($resource_id) && !empty($user_id)) {
            parent::__construct($course_code, $resource_id, $user_id);
        } else {
            // Do nothing but still build the scorm object.
        }
    }

    /**
     * Opens a resource
     * @param	integer	Database ID of the resource
     */
    function open($id) {
        if ($this->debug > 0) { error_log('New LP - scorm::open() - In scorm::open method', 0); }
        // redefine parent method
    }

    /**
     * Possible SCO status: see CAM doc 2.3.2.5.1: passed, completed, browsed, failed, not attempted, incomplete
     */

    /**
     * Prerequisites: see CAM doc 2.3.2.5.1 for pseudo-code
     */

    /**
     * Parses an imsmanifest.xml file and puts everything into the $manifest array
     * @param	string	Path to the imsmanifest.xml file on the system. If not defined, uses the base path of the course's scorm dir
     * @return	array	Structured array representing the imsmanifest's contents
     */
    function parse_manifest($file = '') {
        if ($this->debug > 0) { error_log('In scorm::parse_manifest('.$file.')', 0); }
        if (empty($file)) {
            // Get the path of the imsmanifest file.
        }
        if (is_file($file) && is_readable($file) && ($xml = @file_get_contents($file))) {

            // Parsing using PHP5 DOMXML methods.

            if ($this->debug > 0) { error_log('In scorm::parse_manifest() - Parsing using PHP5 method', 0); }

            //$this->manifest_encoding = api_detect_encoding_xml($xml); // This is the usual way for reading the encoding.
            $this->manifest_encoding = self::detect_manifest_encoding($xml); // This method reads the encoding, it tries to be correct even in cases of wrong or missing encoding declarations.

            $xml = api_utf8_encode_xml($xml, $this->manifest_encoding); // UTF-8 is supported by DOMDocument class, this is for sure.

            $doc = new DOMDocument();
            $res = @$doc->loadXML($xml);
            if ($res === false) {
                if ($this->debug > 0) { error_log('New LP - In scorm::parse_manifest() - Exception thrown when loading '.$file.' in DOMDocument', 0); }
                // Throw exception?
                return null;
            }

            if ($this->debug > 1) { error_log('New LP - Called  (encoding:'.$doc->xmlEncoding.' - saved: '.$this->manifest_encoding.')', 0); }

            $root = $doc->documentElement;
            if ($root->hasAttributes()) {
                $attributes = $root->attributes;
                if ($attributes->length !== 0) {
                    foreach ($attributes as $attrib) {
                        // <manifest> element attributes
                        $this->manifest[$attrib->name] = $attrib->value;
                    }
                }
            }
            $this->manifest['name'] = $root->tagName;
            if ($root->hasChildNodes()) {
                $children = $root->childNodes;
                if ($children->length !== 0) {
                    foreach ($children as $child) {
                        // <manifest> element children (can be <metadata>, <organizations> or <resources> )
                        if ($child->nodeType == XML_ELEMENT_NODE) {
                            switch ($child->tagName) {
                                case 'metadata':
                                    // Parse items from inside the <metadata> element.
                                    $this->metadata = new scormMetadata('manifest',$child);
                                    break;
                                case 'organizations':
                                    // Contains the course structure - this element appears 1 and only 1 time in a package imsmanifest. It contains at least one 'organization' sub-element.
                                    $orgs_attribs = $child->attributes;
                                    foreach ($orgs_attribs as $orgs_attrib) {
                                        // Attributes of the <organizations> element.
                                        if ($orgs_attrib->nodeType == XML_ATTRIBUTE_NODE) {
                                            $this->manifest['organizations'][$orgs_attrib->name] = $orgs_attrib->value;
                                        }
                                    }
                                    $orgs_nodes = $child->childNodes;
                                    $i = 0;
                                    $found_an_org = false;
                                    foreach ($orgs_nodes as $orgnode) {
                                        // <organization> elements - can contain <item>, <metadata> and <title>
                                        // Here we are at the 'organization' level. There might be several organization tags but
                                        // there is generally only one.
                                        // There are generally three children nodes we are looking for inside and organization:
                                        // -title
                                        // -item (may contain other item tags or may appear several times inside organization)
                                        // -metadata (relative to the organization)
                                        $found_an_org = false;
                                        switch ($orgnode->nodeType) {
                                            case XML_TEXT_NODE:
                                                // Ignore here.
                                                break;
                                            case XML_ATTRIBUTE_NODE:
                                                // Just in case there would be interesting attributes inside the organization tag. There shouldn't
                                                // as this is a node-level, not a data level.
                                                //$manifest['organizations'][$i][$orgnode->name] = $orgnode->value;
                                                //$found_an_org = true;
                                                break;
                                            case XML_ELEMENT_NODE:
                                                // <item>, <metadata> or <title> (or attributes)
                                                $organizations_attributes = $orgnode->attributes;
                                                foreach ($organizations_attributes as $orgs_attr) {
                                                    $this->organizations_att[$orgs_attr->name] = $orgs_attr->value;
                                                }
                                                $oOrganization = new scormOrganization('manifest', $orgnode, $this->manifest_encoding);
                                                if ($oOrganization->identifier != '') {
                                                    $name = $oOrganization->get_name();
                                                    if (empty($name)) {
                                                        // If the org title is empty, use zip file name.
                                                        $myname = $this->zipname;
                                                        if ($this->lastzipnameindex != 0) {
                                                            $myname = $myname + $this->lastzipnameindex;
                                                            $this->lastzipnameindex++;
                                                        }
                                                        $oOrganization->set_name($this->zipname);
                                                    }
                                                    $this->organizations[$oOrganization->identifier] = $oOrganization;
                                                }
                                                break;
                                        }
                                    }
                                    break;
                                case 'resources':
                                    if ($child->hasAttributes()) {
                                        $resources_attribs = $child->attributes;
                                        foreach ($resources_attribs as $res_attr) {
                                            if ($res_attr->type == XML_ATTRIBUTE_NODE) {
                                                $this->manifest['resources'][$res_attr->name] = $res_attr->value;
                                            }
                                        }
                                    }
                                    if ($child->hasChildNodes()) {
                                        $resources_nodes = $child->childNodes;
                                        $i = 0;
                                        foreach ($resources_nodes as $res_node) {
                                            $oResource = new scormResource('manifest', $res_node);
                                            if ($oResource->identifier != '') {
                                                $this->resources[$oResource->identifier] = $oResource;
                                                $i++;
                                            }
                                        }
                                    }
                                    // Contains links to physical resources.
                                    break;
                                case 'manifest':
                                    // Only for sub-manifests.
                                    break;
                            }
                        }
                    }
                }
            }
            unset($doc);

            // End parsing using PHP5 DOMXML methods.

        } else {
            if ($this->debug > 1) { error_log('New LP - Could not open/read file '.$file, 0); }
            $this->set_error_msg("File $file could not be read");
            return null;
        }
        // TODO: Close the DOM handler.
        return $this->manifest;
    }

    /**
     * Detects the encoding of a given manifest (a xml-text).
     * It is possible the encoding of the manifest to be wrongly declared or
     * not to be declared at all. The proposed method tries to resolve these problems.
     * @param string $xml    The input xml-text.
     * @return string        The detected value of the input xml.
     */
    private function detect_manifest_encoding(& $xml) {

        if (api_is_valid_utf8($xml)) {
            return 'UTF-8';
        }

        if (preg_match(_PCRE_XML_ENCODING, $xml, $matches)) {
            $declared_encoding = api_refine_encoding_id($matches[1]);
        } else {
            $declared_encoding = '';
        }

        if (!empty($declared_encoding) && !api_is_utf8($declared_encoding)) {
            return $declared_encoding;
        }

        $test_string = '';
        if (preg_match_all('/<langstring[^>]*>(.*)<\/langstring>/m', $xml, $matches)) {
            $test_string = implode("\n", $matches[1]);
            unset($matches);
        }
        if (preg_match_all('/<title[^>]*>(.*)<\/title>/m', $xml, $matches)) {
            $test_string .= "\n".implode("\n", $matches[1]);
            unset($matches);
        }
        if (empty($test_string)) {
            $test_string = $xml;
        }
        return api_detect_encoding($test_string);

    }

    /**
     * Import the scorm object (as a result from the parse_manifest function) into the database structure
     * @param	string	Unique course code
     * @return	bool	Returns -1 on error
     */
    function import_manifest($course_code, $use_max_score = 1) {
        if ($this->debug > 0) { error_log('New LP - Entered import_manifest('.$course_code.')', 0); }
        $course_info = api_get_course_info($course_code);
        $course_id = $course_info['real_id'];

        // Get table names.
        $new_lp = Database::get_course_table(TABLE_LP_MAIN);
        $new_lp_item = Database::get_course_table(TABLE_LP_ITEM);
        $use_max_score = intval($use_max_score);

        foreach ($this->organizations as $id => $dummy) {
            $is_session = api_get_session_id();
            $is_session != 0 ? $session_id = $is_session : $session_id = 0;

            $oOrganization =& $this->organizations[$id];
            // Prepare and execute insert queries:
            // -for learnpath
            // -for items
            // -for views?
            $get_max = "SELECT MAX(display_order) FROM $new_lp WHERE c_id = $course_id ";
            $res_max = Database::query($get_max);
            $dsp = 1;
            if (Database::num_rows($res_max) > 0) {
                $row = Database::fetch_array($res_max);
                $dsp = $row[0] + 1;
            }
            $myname = $oOrganization->get_name();
            $myname = api_utf8_decode($myname);

            $sql = "INSERT INTO $new_lp (c_id, lp_type, name, ref, description, path, force_commit, default_view_mod, default_encoding, js_lib,display_order, session_id, use_max_score)" .
                    "VALUES ($course_id , 2,'".$myname."', '".$oOrganization->get_ref()."','','".$this->subdir."', 0, 'embedded', '".$this->manifest_encoding."', 'scorm_api.php', $dsp, $session_id, $use_max_score)";
            if ($this->debug > 1) { error_log('New LP - In import_manifest(), inserting path: '. $sql, 0); }

            $res = Database::query($sql);
            $lp_id = Database::insert_id();
            $this->lp_id = $lp_id;

            // Insert into item_property.
            api_item_property_update(api_get_course_info($course_code), TOOL_LEARNPATH, $this->lp_id, 'LearnpathAdded', api_get_user_id());
            api_item_property_update(api_get_course_info($course_code), TOOL_LEARNPATH, $this->lp_id, 'visible', api_get_user_id());

            // Now insert all elements from inside that learning path.
            // Make sure we also get the href and sco/asset from the resources.
            $list = $oOrganization->get_flat_items_list();
            $parents_stack = array(0);
            $parent = 0;
            $previous = 0;
            $level = 0;

            foreach ($list as $item) {
                if ($item['level'] > $level) {
                    // Push something into the parents array.
                    array_push($parents_stack, $previous);
                    $parent = $previous;
                } elseif ($item['level'] < $level) {
                    $diff = $level - $item['level'];
                    // Pop something out of the parents array.
                    for ($j = 1; $j <= $diff; $j++) {
                        $outdated_parent = array_pop($parents_stack);
                    }
                    $parent = array_pop($parents_stack); // Just save that value, then add it back.
                    array_push($parents_stack,$parent);
                }
                $path = '';
                $type = 'dir';
                if (isset($this->resources[$item['identifierref']])) {
                    $oRes =& $this->resources[$item['identifierref']];
                    $path = @$oRes->get_path();
                    if (!empty($path)) {
                        $temptype = $oRes->get_scorm_type();
                        if (!empty($temptype)) {
                            $type = $temptype;
                        }
                    }
                }
                $level = $item['level'];
                $field_add = '';
                $value_add = '';
                if (!empty($item['masteryscore'])) {
                    $field_add .= 'mastery_score, ';
                    $value_add .= $item['masteryscore'].',';
                }
                if (!empty($item['maxtimeallowed'])) {
                    $field_add .= 'max_time_allowed, ';
                    $value_add .= "'".$item['maxtimeallowed']."',";
                }
                $title = Database::escape_string($item['title']);
                $title = api_utf8_decode($title);

                $max_score = Database::escape_string($item['max_score']);

                if ($max_score == 0 || is_null($max_score) || $max_score == '') {
                    //If max score is not set The use_max_score parameter is check in order to use 100 (chamilo style) or '' (strict scorm)
                    if ($use_max_score) {
                        $max_score = "'100'";
                    } else {
                        $max_score = "NULL";
                    }
                } else {
                    //Otherwise save the max score
                    $max_score = "'$max_score'";
                }

                $identifier = Database::escape_string($item['identifier']);

                if (empty($title)) {
                    $title = get_lang('Untitled');
                }

                $prereq = Database::escape_string($item['prerequisites']);

                $sql_item = "INSERT INTO $new_lp_item (c_id, lp_id,item_type,ref,title, path,min_score,max_score, $field_add parent_item_id,previous_item_id,next_item_id, prerequisite,display_order,launch_data, parameters) VALUES " .
                        "($course_id, $lp_id, '$type','$identifier', '$title', '$path' , 0, $max_score, $value_add" .
                        "$parent, $previous, 0, " .
                        "'$prereq', ".$item['rel_order'] .", '".$item['datafromlms']."'," .
                        "'".$item['parameters']."'" .
                        ")";

                $res_item = Database::query($sql_item);
                if ($this->debug > 1) { error_log('New LP - In import_manifest(), inserting item : '.$sql_item.' : '.Database::error(), 0); }
                $item_id = Database::insert_id();
                // Now update previous item to change next_item_id.
                $upd = "UPDATE $new_lp_item SET next_item_id = $item_id WHERE c_id = $course_id AND id = $previous";
                $upd_res = Database::query($upd);
                // Update previous item id.
                $previous = $item_id;

                // Code for indexing, now only index specific fields like terms and the title.
                if (!empty($_POST['index_document'])) {
                    require_once api_get_path(LIBRARY_PATH).'search/ChamiloIndexer.class.php';
                    require_once api_get_path(LIBRARY_PATH).'search/IndexableChunk.class.php';
                    require_once api_get_path(LIBRARY_PATH).'specific_fields_manager.lib.php';

                    $di = new ChamiloIndexer();
                    isset($_POST['language']) ? $lang = Database::escape_string($_POST['language']) : $lang = 'english';
                    $di->connectDb(NULL, NULL, $lang);
                    $ic_slide = new IndexableChunk();
                    $ic_slide->addValue('title', $title);
                    $specific_fields = get_specific_field_list();
                    $all_specific_terms = '';
                    foreach ($specific_fields as $specific_field) {
                        if (isset($_REQUEST[$specific_field['code']])) {
                            $sterms = trim($_REQUEST[$specific_field['code']]);
                            $all_specific_terms .= ' '. $sterms;
                            if (!empty($sterms)) {
                                $sterms = explode(',', $sterms);
                                foreach ($sterms as $sterm) {
                                    $ic_slide->addTerm(trim($sterm), $specific_field['code']);
                                }
                            }
                        }
                    }
                    $body_to_index = $all_specific_terms .' '. $title;
                    $ic_slide->addValue("content", $body_to_index);
                    // TODO: Add a comment to say terms separated by commas.
                    $courseid = api_get_course_id();
                    $ic_slide->addCourseId($courseid);
                    $ic_slide->addToolId(TOOL_LEARNPATH);
                    $xapian_data = array(
                        SE_COURSE_ID => $courseid,
                        SE_TOOL_ID => TOOL_LEARNPATH,
                        SE_DATA => array('lp_id' => $lp_id, 'lp_item'=> $previous, 'document_id' => ''), // TODO: Unify with other lp types.
                        SE_USER => (int)api_get_user_id(),
                    );
                    $ic_slide->xapian_data = serialize($xapian_data);
                    $di->addChunk($ic_slide);
                    // Index and return search engine document id.
                    $did = $di->index();
                    if ($did) {
                        // Save it to db.
                        $tbl_se_ref = Database::get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
                        $sql = 'INSERT INTO %s (id, course_code, tool_id, ref_id_high_level, ref_id_second_level, search_did)
                                VALUES (NULL , \'%s\', \'%s\', %s, %s, %s)';
                        $sql = sprintf($sql, $tbl_se_ref, api_get_course_id(), TOOL_LEARNPATH, $lp_id, $previous, $did);
                        Database::query($sql);
                    }
                }
            }
        }
    }

    /**
     * Intermediate to import_package only to allow import from local zip files
     * @param	string	Path to the zip file, from the sys root
     * @param	string	Current path (optional)
     * @return string	Absolute path to the imsmanifest.xml file or empty string on error
     */
    function import_local_package($file_path, $current_dir = '') {
        // TODO: Prepare info as given by the $_FILES[''] vector.
        $file_info = array();
        $file_info['tmp_name'] = $file_path;
        $file_info['name'] = basename($file_path);
        // Call the normal import_package function.
        return $this->import_package($file_info, $current_dir);
    }

    /**
     * Imports a zip file into the Chamilo structure
     * @param	string	Zip file info as given by $_FILES['userFile']
     * @return	string	Absolute path to the imsmanifest.xml file or empty string on error
     */
    function import_package($zip_file_info, $current_dir = '') {
        if ($this->debug > 0) { error_log('In scorm::import_package('.print_r($zip_file_info,true).',"'.$current_dir.'") method', 0); }

        $maxFilledSpace = DocumentManager :: get_course_quota();

        $zip_file_path = $zip_file_info['tmp_name'];
        $zip_file_name = $zip_file_info['name'];

        if ($this->debug > 1) { error_log('New LP - import_package() - zip file path = '.$zip_file_path.', zip file name = '.$zip_file_name, 0); }
        $course_rel_dir     = api_get_course_path().'/scorm'; // scorm dir web path starting from /courses
        $course_sys_dir     = api_get_path(SYS_COURSE_PATH).$course_rel_dir; // Absolute system path for this course.
        $current_dir        = replace_dangerous_char(trim($current_dir),'strict'); // Current dir we are in, inside scorm/
        if ($this->debug > 1) { error_log('New LP - import_package() - current_dir = '.$current_dir, 0); }

         //$uploaded_filename = $_FILES['userFile']['name'];
        // Get name of the zip file without the extension.
        if ($this->debug > 1) { error_log('New LP - Received zip file name: '.$zip_file_path, 0); }
        $file_info = pathinfo($zip_file_name);
        $filename = $file_info['basename'];
        $extension = $file_info['extension'];
        $file_base_name = str_replace('.'.$extension,'',$filename); // Filename without its extension.
        $this->zipname = $file_base_name; // Save for later in case we don't have a title.

        if ($this->debug > 1) { error_log("New LP - base file name is : ".$file_base_name, 0); }
        $new_dir = replace_dangerous_char(trim($file_base_name),'strict');
        $this->subdir = $new_dir;
        if ($this->debug > 1) { error_log("New LP - subdir is first set to : ".$this->subdir, 0); }

        $zipFile = new PclZip($zip_file_path);

        // Check the zip content (real size and file extension).

        $zipContentArray = $zipFile->listContent();

        $package_type = '';
        $at_root = false;
        $manifest = '';
        $manifest_list = array();

        // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
        foreach ($zipContentArray as $thisContent) {
            $thisContent['filename'];
            //error_log('Looking at  '.$thisContent['filename'], 0);
            if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
                $this->set_error_msg("File $file contains a PHP script");
                //return api_failure::set_failure('php_file_in_zip_file');
            } elseif (stristr($thisContent['filename'], 'imsmanifest.xml')) {
                //error_log('Found imsmanifest at '.$thisContent['filename'], 0);
                if ($thisContent['filename'] == basename($thisContent['filename'])) {
                    $at_root = true;
                } else {
                    //$this->subdir .= '/'.dirname($thisContent['filename']);
                    if ($this->debug > 2) { error_log("New LP - subdir is now ".$this->subdir, 0); }
                }
                $package_type = 'scorm';
                $manifest_list[] = $thisContent['filename'];
                $manifest = $thisContent['filename']; //just the relative directory inside scorm/
            } else {
                // Do nothing, if it has not been set as scorm somewhere else, it stays as '' default.
            }
            $realFileSize += $thisContent['size'];
        }

        // Now get the shortest path (basically, the imsmanifest that is the closest to the root).
        $shortest_path = $manifest_list[0];
        $slash_count = substr_count($shortest_path, '/');
        foreach ($manifest_list as $manifest_path) {
            $tmp_slash_count = substr_count($manifest_path, '/');
            if ($tmp_slash_count<$slash_count) {
                $shortest_path = $manifest_path;
                $slash_count = $tmp_slash_count;
            }
        }
        $this->subdir .= '/'.dirname($shortest_path); // Do not concatenate because already done above.
        $manifest = $shortest_path;

        if ($this->debug > 1) { error_log('New LP - Package type is now '.$package_type, 0); }

        if ($package_type== '')
        // && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM)
        {
            if ($this->debug > 1) { error_log('New LP - Package type is empty', 0); }
            return api_failure::set_failure('not_scorm_content');
        }

        if (!enough_size($realFileSize, $course_sys_dir, $maxFilledSpace)) {
            if ($this->debug > 1) { error_log('New LP - Not enough space to store package', 0); }
            return api_failure::set_failure('not_enough_space');
        }

        // It happens on Linux that $new_dir sometimes doesn't start with '/'
        if ($new_dir[0] != '/') {
            $new_dir = '/'.$new_dir;
        }

        if ($new_dir[strlen($new_dir)-1] == '/') {
            $new_dir = substr($new_dir,0,-1);
        }

        /* Uncompressing phase */

        /*
            We need to process each individual file in the zip archive to
            - add it to the database
            - parse & change relative html links
            - make sure the filenames are secure (filter funny characters or php extensions)
        */
        if (is_dir($course_sys_dir.$new_dir) OR @mkdir($course_sys_dir.$new_dir, api_get_permissions_for_new_directories())) {

            // PHP method - slower...
            if ($this->debug >= 1) { error_log('New LP - Changing dir to '.$course_sys_dir.$new_dir, 0); }
            $saved_dir = getcwd();
            chdir($course_sys_dir.$new_dir);
            $unzippingState = $zipFile->extract();

            for ($j = 0; $j < count($unzippingState); $j++) {
                $state = $unzippingState[$j];

                // TODO: Fix relative links in html files (?)
                $extension = strrchr($state['stored_filename'], '.');
                if ($this->debug >= 1) { error_log('New LP - found extension '.$extension.' in '.$state['stored_filename'], 0); }
            }

            if (!empty($new_dir)) {
                $new_dir = $new_dir.'/';
            }
            // Rename files, for example with \\ in it.

            if ($this->debug >= 1) { error_log('New LP - try to open: '.$course_sys_dir.$new_dir, 0); }

            if ($dir = @opendir($course_sys_dir.$new_dir)) {
                if ($this->debug >= 1) { error_log('New LP - Opened dir '.$course_sys_dir.$new_dir, 0); }
                while ($file=readdir($dir)) {
                    if ($file != '.' && $file != '..') {
                        $filetype = 'file';

                        if (is_dir($course_sys_dir.$new_dir.$file)) $filetype = 'folder';

                        // TODO: RENAMING FILES CAN BE VERY DANGEROUS SCORM-WISE, avoid that as much as possible!
                        //$safe_file = replace_dangerous_char($file, 'strict');
                        $find_str = array('\\', '.php', '.phtml');
                        $repl_str = array('/', '.txt', '.txt');
                        $safe_file = str_replace($find_str, $repl_str, $file);

                        if ($this->debug >= 1) { error_log('Comparing:  '.$safe_file, 0); }
                        if ($this->debug >= 1) { error_log('and:  '.$file, 0); }

                        if ($safe_file != $file) {
                            //@rename($course_sys_dir.$new_dir, $course_sys_dir.'/'.$safe_file);
                            $mydir = dirname($course_sys_dir.$new_dir.$safe_file);

                            if (!is_dir($mydir)) {
                                $mysubdirs = split('/', $mydir);
                                $mybasedir = '/';
                                foreach ($mysubdirs as $mysubdir) {
                                    if (!empty($mysubdir)) {
                                        $mybasedir = $mybasedir.$mysubdir.'/';
                                        if (!is_dir($mybasedir)) {
                                            @mkdir($mybasedir, api_get_permissions_for_new_directories());
                                            if ($this->debug >= 1) { error_log('New LP - Dir '.$mybasedir.' doesnt exist. Creating.', 0); }
                                        }
                                    }
                                }
                            }
                            @rename($course_sys_dir.$new_dir.$file,$course_sys_dir.$new_dir.$safe_file);
                            if ($this->debug >= 1) { error_log('New LP - Renaming '.$course_sys_dir.$new_dir.$file.' to '.$course_sys_dir.$new_dir.$safe_file, 0); }
                        }
                        //set_default_settings($course_sys_dir, $safe_file, $filetype);
                    }
                }

                closedir($dir);
                chdir($saved_dir);

                api_chmod_R($course_sys_dir.$new_dir, api_get_permissions_for_new_directories());
                if ($this->debug > 1) { error_log('New LP - changed back to init dir: '.$course_sys_dir.$new_dir, 0); }
            }
        } else {
            return '';
        }
        return $course_sys_dir.$new_dir.$manifest;
    }

    /**
     * Sets the proximity setting in the database
     * @param	string	Proximity setting
     */
    function set_proximity($proxy = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In scorm::set_proximity('.$proxy.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET content_local = '$proxy' WHERE c_id = ".$course_id." AND id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the theme setting in the database
     * @param	string	theme setting
     */
    function set_theme($theme = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In scorm::set_theme('.$theme.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET theme = '$theme' WHERE c_id = ".$course_id." AND id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the image setting in the database
     * @param	string preview_image setting
     */
    function set_preview_image($preview_image = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In scorm::set_theme('.$preview_image.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET preview_image = '$preview_image' WHERE c_id = ".$course_id." AND id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the author  setting in the database
     * @param	string preview_image setting
     */
    function set_author($author = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In scorm::set_author('.$author.') method', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET author = '$author' WHERE c_id = ".$course_id." AND id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Sets the content maker setting in the database
     * @param	string	Proximity setting
     */
    function set_maker($maker = '') {
        $course_id = api_get_course_int_id();
        if ($this->debug > 0) { error_log('In scorm::set_maker method('.$maker.')', 0); }
        $lp = $this->get_id();
        if ($lp != 0) {
            $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
            $sql = "UPDATE $tbl_lp SET content_maker = '$maker' WHERE c_id = ".$course_id." AND id = ".$lp;
            $res = Database::query($sql);
            return $res;
        } else {
            return false;
        }
    }

    /**
     * Exports the current SCORM object's files as a zip. Excerpts taken from learnpath_functions.inc.php::exportpath()
     * @param	integer	Learnpath ID (optional, taken from object context if not defined)
     */
    function export_zip($lp_id = null) {
        if ($this->debug > 0) { error_log('In scorm::export_zip method('.$lp_id.')', 0); }
        if (empty($lp_id)) {
            if (!is_object($this)) {
                return false;
            } else {
                $id = $this->get_id();
                if (empty($id)) {
                    return false;
                } else {
                    $lp_id = $this->get_id();
                }
            }
        }
        //error_log('New LP - in export_zip()',0);
        //zip everything that is in the corresponding scorm dir
        //write the zip file somewhere (might be too big to return)
        require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
        require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
        require_once api_get_path(LIBRARY_PATH).'document.lib.php';

        require_once 'learnpath_functions.inc.php';
        $course_id = api_get_course_int_id();
        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
        $_course = Database::get_course_info(api_get_course_id());

        $sql = "SELECT * FROM $tbl_lp WHERE c_id = ".$course_id." AND id=".$lp_id;
        $result = Database::query($sql);
        $row = Database::fetch_array($result);
        $LPname = $row['path'];
        $list = split('/', $LPname);
        $LPnamesafe = $list[0];
        //$zipfoldername = '/tmp';
        //$zipfoldername = '../../courses/'.$_course['directory'].'/temp/'.$LPnamesafe;
        $zipfoldername = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/temp/'.$LPnamesafe;
        $scormfoldername = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/scorm/'.$LPnamesafe;
        $zipfilename = $zipfoldername.'/'.$LPnamesafe.'.zip';

        // Get a temporary dir for creating the zip file.

        //error_log('New LP - cleaning dir '.$zipfoldername, 0);
        deldir($zipfoldername); // Make sure the temp dir is cleared.
        $res = mkdir($zipfoldername, api_get_permissions_for_new_directories());
        //error_log('New LP - made dir '.$zipfoldername, 0);

        // Create zipfile of given directory.
        $zip_folder = new PclZip($zipfilename);
        $zip_folder->create($scormfoldername.'/', PCLZIP_OPT_REMOVE_PATH, $scormfoldername.'/');

        //$zipfilename = '/var/www/chamilo/courses/TEST2/scorm/example_document.html';
        //This file sending implies removing the default mime-type from php.ini
        //DocumentManager :: file_send_for_download($zipfilename, true, $LPnamesafe.'.zip');
        DocumentManager :: file_send_for_download($zipfilename, true);

        // Delete the temporary zip file and directory in fileManage.lib.php
        my_delete($zipfilename);
        my_delete($zipfoldername);

        return true;
    }

    /**
     * Gets a resource's path if available, otherwise return empty string
     * @param	string	Resource ID as used in resource array
     * @return string	The resource's path as declared in imsmanifest.xml
     */
    function get_res_path($id) {
        if ($this->debug > 0) { error_log('In scorm::get_res_path('.$id.') method', 0); }
        $path = '';
        if (isset($this->resources[$id])) {
            $oRes =& $this->resources[$id];
            $path = @$oRes->get_path();
        }
        return $path;
    }

    /**
     * Gets a resource's type if available, otherwise return empty string
     * @param	string	Resource ID as used in resource array
     * @return string	The resource's type as declared in imsmanifest.xml
     */
    function get_res_type($id) {
        if ($this->debug > 0) { error_log('In scorm::get_res_type('.$id.') method', 0); }
        $type = '';
        if (isset($this->resources[$id])) {
            $oRes =& $this->resources[$id];
            $temptype = $oRes->get_scorm_type();
            if (!empty($temptype)) {
                $type = $temptype;
            }
        }
        return $type;
    }

    /**
     * Gets the default organisation's title
     * @return	string	The organization's title
     */
    function get_title() {
        if ($this->debug > 0) { error_log('In scorm::get_title() method', 0); }
        $title = '';
        if (isset($this->manifest['organizations']['default'])) {
            $title = $this->organizations[$this->manifest['organizations']['default']]->get_name();
        } elseif (count($this->organizations)==1) {
            // This will only get one title but so we don't need to know the index.
            foreach($this->organizations as $id => $value) {
                $title = $this->organizations[$id]->get_name();
                break;
            }
        }
        return $title;
    }

    /**
     * // TODO @TODO Implement this function to restore items data from an imsmanifest,
     * updating the existing table... This will prove very useful in case initial data
     * from imsmanifest were not imported well enough
     * @param	string	course Code
     * @param string	LP ID (in database)
     * @param string	Manifest file path (optional if lp_id defined)
     * @return	integer	New LP ID or false on failure
     * TODO @TODO Implement imsmanifest_path parameter
     */
    function reimport_manifest($course, $lp_id = null, $imsmanifest_path = '') {
        if ($this->debug > 0) { error_log('In scorm::reimport_manifest() method', 0); }
        global $_course;
        // RECOVERING PATH FROM DB
        $main_table = Database::get_main_table(TABLE_MAIN_COURSE);
        $course = Datbase::escape_string($course);
        $sql = "SELECT * FROM $main_table WHERE code = '$course'";
        if ($this->debug > 2) { error_log('New LP - scorm::reimport_manifest() '.__LINE__.' - Querying course: '.$sql, 0); }
        //$res = Database::query($sql);
        $res = Database::query($sql);
        if (Database::num_rows($res) > 0) {
            $this->cc = $course;
        } else {
            $this->error = 'Course code does not exist in database ('.$sql.')';
            return false;
        }

        // TODO: Make it flexible to use any course_code (still using env course code here)
        //$lp_table = Database::get_course_table(LEARNPATH_TABLE);
        $course_id = api_get_course_int_id();
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
        $lp_id = intval($lp_id);
        $sql = "SELECT * FROM $lp_table WHERE c_id = ".$course_id." AND id = '$lp_id'";
        if ($this->debug > 2) { error_log('New LP - scorm::reimport_manifest() '.__LINE__.' - Querying lp: '.$sql, 0); }
        $res = Database::query($sql);
        if (Database::num_rows($res) > 0) {
            $this->lp_id = $lp_id;
            $row = Database::fetch_array($res);
            $this->type = $row['lp_type'];
            $this->name = stripslashes($row['name']);
            $this->encoding = $row['default_encoding'];
            $this->proximity = $row['content_local'];
            $this->maker = $row['content_maker'];
            $this->prevent_reinit = $row['prevent_reinit'];
            $this->license = $row['content_license'];
            $this->scorm_debug = $row['debug'];
            $this->js_lib = $row['js_lib'];
            $this->path = $row['path'];
            if ($this->type == 2) {
                if ($row['force_commit'] == 1) {
                    $this->force_commit = true;
                }
            }
            $this->mode = $row['default_view_mod'];
            $this->subdir = $row['path'];
        }
        // Parse the manifest (it is already in this lp's details).
        $manifest_file = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/scorm/'.$this->subdir.'/imsmanifest.xml';
        if ($this->subdir == '') {
            $manifest_file = api_get_path(SYS_COURSE_PATH).$_course['directory'].'/scorm/imsmanifest.xml';
        }
        echo $manifest_file;
        if (is_file($manifest_file) && is_readable($manifest_file)) {
            // Re-parse the manifest file.
            if ($this->debug > 1) { error_log('New LP - In scorm::reimport_manifest() - Parsing manifest '.$manifest_file, 0); }
            $manifest = $this->parse_manifest($manifest_file);
            // Import new LP in DB (ignore the current one).
            if ($this->debug > 1) { error_log('New LP - In scorm::reimport_manifest() - Importing manifest '.$manifest_file, 0); }
            $this->import_manifest(api_get_course_id());
        } else {
            if ($this->debug > 0) { error_log('New LP - In scorm::reimport_manifest() - Could not find manifest file at '.$manifest_file, 0); }
        }
        return false;
    }
}
scormItem.class.php000064400000023245152003363470010334 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the scormItem class that deals with <item> elements in an imsmanifest file
 * @package	chamilo.learnpath.scorm
 * @author	Yannick Warnier	<ywarnier@beeznest.org>
 */

/**
 * This class handles the <item> elements from an imsmanifest file.
 */
require_once 'learnpathItem.class.php';
class scormItem extends learnpathItem {
    public $identifier = '';
    public $identifierref = '';
    public $isvisible = '';
    public $parameters = '';
    public $title = '';
    public $sub_items = array();
    public $metadata;
    //public $prerequisites = ''; - defined in learnpathItem.class.php
    // Modified by Ivan Tcholakov, 06-FEB-2010.
    //public $max_time_allowed = ''; //should be something like HHHH:MM:SS.SS
    public $max_time_allowed = '00:00:00';
    //
    public $timelimitaction = '';
    public $datafromlms = '';
    public $mastery_score = '';
    public $scorm_contact;

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormItem
     * object from database records or from the DOM element given as parameter
     * @param	string	Type of construction needed ('db' or 'manifest', default = 'manifest')
     * @param	mixed	Depending on the type given, DB id for the lp_item or reference to the DOM element
     */
    public function __construct($type = 'manifest', &$element, $course_id = '') {
        if (isset($element)) {

            // Parsing using PHP5 DOMXML methods.

            switch ($type) {
                case 'db':
                    parent::__construct($element,api_get_user_id(), $course_id);
                    $this->scorm_contact = false;
                    // TODO: Implement this way of metadata object creation.
                    return false;
                case 'manifest': // Do the same as the default.
                default:
                     //if ($first_item->type == XML_ELEMENT_NODE) this is already check prior to the call to this function.
                     $children = $element->childNodes;
                    foreach ($children as $child) {
                         switch ($child->nodeType) {
                             case XML_ELEMENT_NODE:
                                switch ($child->tagName) {
                                     case 'title':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->title = $child->firstChild->nodeValue;
                                         }
                                         break;
                                     case 'max_score':
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->max_score = $child->firstChild->nodeValue;
                                         }
                                         break;
                                     case 'maxtimeallowed':
                                     case 'adlcp:maxtimeallowed':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->max_time_allowed = $child->firstChild->nodeValue;
                                         }
                                         break;
                                    case 'prerequisites':
                                    case 'adlcp:prerequisites':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->prereq_string = $child->firstChild->nodeValue;
                                         }
                                         break;
                                    case 'timelimitaction':
                                    case 'adlcp:timelimitaction':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->timelimitaction = $child->firstChild->nodeValue;
                                         }
                                         break;
                                    case 'datafromlms':
                                    case 'adlcp:datafromlms':
                                    case 'adlcp:launchdata': //in some cases (Wouters)
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->datafromlms = $child->firstChild->nodeValue;
                                         }
                                         break;
                                    case 'masteryscore':
                                    case 'adlcp:masteryscore':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->mastery_score = $child->firstChild->nodeValue;
                                         }
                                         break;
                                     case 'item':
                                         $oItem = new scormItem('manifest',$child);
                                         if ($oItem->identifier != '') {
                                             $this->sub_items[$oItem->identifier] = $oItem;
                                         }
                                        break;
                                     case 'metadata':
                                         $this->metadata = new scormMetadata('manifest', $child);
                                         break;
                                 }
                                 break;
                             case XML_TEXT_NODE:
                                 // This case is actually treated by looking into ELEMENT_NODEs above.
                                 break;
                         }
                     }
                     if ($element->hasAttributes()) {
                         $attributes = $element->attributes;
                         //$keep_href = '';
                         foreach ($attributes as $attrib) {
                             switch($attrib->name){
                                 case 'identifier':
                                     $this->identifier = $attrib->value;
                                     break;
                                 case 'identifierref':
                                     $this->identifierref = $attrib->value;
                                     break;
                                 case 'isvisible':
                                     $this->isvisible = $attrib->value;
                                     break;
                                 case 'parameters':
                                     $this->parameters = $attrib->value;
                                     break;
                             }
                         }
                     }
                    return true;
            }

            // End parsing using PHP5 DOMXML methods.

        }
        return false;
    }

    /**
     * Builds a flat list with the current item and calls itself recursively on all children
     * @param	array	Reference to the array to complete with the current item
     * @param	integer	Optional absolute order (pointer) of the item in this learning path
     * @param	integer	Optional relative order of the item at this level
     * @param	integer	Optional level. If not given, assumes it's level 0
     */
    public function get_flat_list(&$list, &$abs_order, $rel_order = 1, $level = 0) {
        $list[] = array(
            'abs_order' => $abs_order,
            'datafromlms' => $this->datafromlms,
            'identifier' => $this->identifier,
            'identifierref' => $this->identifierref,
            'isvisible' => $this->isvisible,
            'level' => $level,
            'masteryscore' => $this->mastery_score,
            'maxtimeallowed' => $this->max_time_allowed,
            'metadata' => $this->metadata,
            'parameters' => $this->parameters,
            'prerequisites' => (!empty($this->prereq_string) ? $this->prereq_string : ''),
            'rel_order' => $rel_order,
            'timelimitaction' => $this->timelimitaction,
            'title' => $this->title,
            'max_score' => $this->max_score
        );
        $abs_order++;
        $i = 1;
        foreach($this->sub_items as $id => $dummy) {
            $oSubitem =& $this->sub_items[$id];
            $oSubitem->get_flat_list($list, $abs_order, $i, $level + 1);
            $i++;
        }
    }

    /**
     * Save function. Uses the parent save function and adds a layer for SCORM.
     * @param	boolean	Save from URL params (1) or from object attributes (0)
     */
    public function save($from_outside = true, $prereqs_complete = false) {
        parent::save($from_outside, $prereqs_complete);
        // Under certain conditions, the scorm_contact should not be set, because no scorm signal was sent.
        $this->scorm_contact = true;
        if (!$this->scorm_contact){
            //error_log('New LP - was expecting SCORM message but none received', 0);
        }
    }
}
scormMetadata.class.php000064400000012011152003363470011143 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the scormMetadata class, setup to hold information about the <metadata> element in imsmanifest files
 * @package chamilo.learnpath.scorm
 */

/**
 * scormMetadata class, handling each <metadata> element found in an imsmanifest file
 */
class scormMetadata {
    public	$lom = '';
    public $schema = '';
    public $schemaversion = '';
    public $location = '';
    public $text = '';
    public $attribs = array();

    /**
     * Class constructor. Works in two different ways defined by the first element, being 'db' or 'manifest'.
     * If 'db', then it is built using the information available in the Chamilo database. If 'manifest', then it
     * is built using the element given as a parameter, expecting it to be a <metadata> element pointer from the
     * DOM parser.
     * @param	string	Type of creation required. Can be 'db' or 'manifest' (default)
     * @param	mixed	Depending on the type, can be the DB ID of the learnpath item or the pointer to the <metadata> element in the imsmanifest.xml file
     * @return	boolean	True on success, false on failure
     */
    public function __construct($type = 'manifest', &$element) {
        if (isset($element)) {

            // Parsing using PHP5 DOMXML methods.

            switch ($type) {
                case 'db':
                    // TODO: Implement this way of metadata object creation.
                    return false;
                    //break;
                case 'manifest': // Do the same as the default.
                     $children = $element->childNodes;
                    foreach ($children as $child) {
                         switch ($child->nodeType) {
                             case XML_ELEMENT_NODE:
                                // Could be 'lom', 'schema', 'schemaversion' or 'location'.
                                 switch ($child->tagName) {
                                     case 'lom':
                                         $childchildren = $child->childNodes;
                                         foreach ($childchildren as $childchild) {
                                             $this->lom = $childchild->nodeValue;
                                         }
                                         break;
                                     case 'schema':
                                         $childchildren = $child->childNodes;
                                         foreach ($childchildren as $childchild) {
                                             // There is generally only one child here.
                                             $this->schema = $childchild->nodeValue;
                                         }
                                         break;
                                     case 'schemaversion':
                                         $childchildren = $child->childNodes;
                                         foreach ($childchildren as $childchild) {
                                             // There is generally only one child here.
                                             $this->schemaversion = $childchild->nodeValue;
                                         }
                                         break;
                                     case 'location':
                                         $childchildren = $child->childNodes;
                                         foreach ($childchildren as $childchild) {
                                             // There is generally only one child here.
                                             $this->location = $childchild->nodeValue;
                                         }
                                         break;
                                 }
                                 break;
                             case XML_TEXT_NODE:
                                 if (trim($child->textContent) != '') {
                                     if (count($children == 1)) {
                                         // If this is the only child at this level and it is a content... save differently.
                                         $this->text = $child->textContent;
                                     } else {
                                         $this->text[$element->tagName] = $child->textContent;
                                     }
                                 }
                                 break;
                         }
                     }
                     $attributes = $element->attributes;
                     //$keep_href = '';
                     if (is_array($attributes)) {
                         foreach ($attributes as $attrib) {
                             if (trim($attrib->value) != ''){
                                 $this->attribs[$attrib->name] = $attrib->value;
                             }
                         }
                     }
                    return true;
                    //break;
            }

            // End parsing using PHP5 DOMXML methods.

        }
        return false;
    }
}
scormOrganization.class.php000064400000012000152003363470012065 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the scormOrganization class
 * @package chamilo.learnpath.scorm
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * Class defining the <organization> tag in an imsmanifest.xml file
 */
class scormOrganization {
    public $identifier = '';
    public $structure = '';
    public $title = '';
    public $items = array();
    public $metadata;

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormOrganization
     * object from database records or from the DOM element given as parameter
     * @param	string	Type of construction needed ('db' or 'manifest', default = 'manifest')
     * @param	mixed	Depending on the type given, DB id for the lp_item or reference to the DOM element
     */
    public function __construct($type = 'manifest', &$element, $scorm_charset = 'UTF-8') {
        if (isset($element)) {

            // Parsing using PHP5 DOMXML methods.

            switch ($type) {
                case 'db':
                    // TODO: Implement this way of metadata object creation.
                    return false;
                case 'manifest': // Do the same as the default.
                default:
                    //if ($first_item->type == XML_ELEMENT_NODE) this is already check prior to the call to this function.
                    $children = $element->childNodes;
                    foreach ($children as $child) {
                         switch ($child->nodeType) {
                             case XML_ELEMENT_NODE:
                                switch ($child->tagName) {
                                     case 'item':
                                         $oItem = new scormItem('manifest', $child);
                                         if ($oItem->identifier != '') {
                                            $this->items[$oItem->identifier] = $oItem;
                                         }
                                        break;
                                     case 'metadata':
                                         $this->metadata = new scormMetadata('manifest', $child);
                                         break;
                                     case 'title':
                                         $tmp_children = $child->childNodes;
                                         if ($tmp_children->length == 1 && $child->firstChild->nodeValue != '') {
                                             $this->title = api_utf8_decode(api_html_entity_decode($child->firstChild->nodeValue, ENT_QUOTES, 'UTF-8'));
                                         }
                                         break;
                                 }
                                 break;
                             case XML_TEXT_NODE:
                                 break;
                         }
                     }
                    if ($element->hasAttributes()) {
                         $attributes = $element->attributes;
                         //$keep_href = '';
                         foreach ($attributes as $attrib) {
                             switch ($attrib->name) {
                                 case 'identifier':
                                     $this->identifier = $attrib->value;
                                     break;
                                 case 'structure':
                                     $this->structure = $attrib->value;
                                     break;
                             }
                         }
                    }
                    return true;
            }

            // End parsing using PHP5 DOMXML methods.

        }
        return false;
    }

    /**
     * Get a flat list of items in the organization
     * @return	array	Array containing an ordered list of all items with their level and all information related to each item
     */
    public function get_flat_items_list() {
        $list = array();
        $i = 1;
        foreach ($this->items as $id => $dummy) {
            $abs_order = 0;
            $this->items[$id]->get_flat_list($list,$abs_order, $i, 0); // Passes the array as a pointer so it is modified in $list directly.
            $i++;
        }
        return $list;
    }

    /**
     * Name getter
     * @return	string	Name or empty string
     */
    public function get_name() {
        if (!empty($this->title)) {
            return Database::escape_string($this->title);
        } else {
            return '';
        }
    }

    /**
     * Reference identifier getter
     * @return	string	Identifier or empty string
     */
    public function get_ref() {
        if (!empty($this->identifier)) {
            return Database::escape_string($this->identifier);
        } else {
            return '';
        }
    }

    /**
     * Sets the title element
     * @param	string	New title to set
     */
    public function set_name($title) {
        if (!empty($title)) {
            $this->title = Database::escape_string($title);
        }
    }
}
scormResource.class.php000064400000012576152003363470011232 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 * Container for the scormResource class
 * @package chamilo.learnpath.scorm
 * @author	Yannick Warnier <ywarnier@beeznest.org>
 */

/**
 * Class defining the <resource> tag in an imsmanifest.xml file
 *
 */
class scormResource {
    public $identifier = '';
    public $type = 'webcontent';
    //public $identifierref = '';
    public $scormtype = 'sco'; // Fix problems with ENI content where asset is not defined.
    public $base = '';
    public $href = '';
    public $metadata;
    //public $file_href;
    //public $file_metadata;
    public $files = array();
    public $dependencies = array();

    /**
     * Class constructor. Depending of the type of construction called ('db' or 'manifest'), will create a scormResource
     * object from database records or from the DOM element given as parameter
     * @param	string	Type of construction needed ('db' or 'manifest', default = 'manifest')
     * @param	mixed	Depending on the type given, DB id for the lp_item or reference to the DOM element
     */
    public function __construct($type = 'manifest', &$element) {
        /*
        echo "<pre>Analysing resource:<br />\n";
        var_dump($element);
        echo "</pre><br />\n";
        */
        if (isset($element)) {

            // Parsing using PHP5 DOMXML methods.

            switch ($type) {
                case 'db':
                    // TODO: Implement this way of metadata object creation.
                    return false;
                case 'manifest': // Do the same as the default.
                default:
                    //if ($first_item->type == XML_ELEMENT_NODE) this is already check prior to the call to this function.
                    $children = $element->childNodes;
                    if (is_array($children)) {
                        foreach ($children as $child) {
                            switch ($child->nodeType) {
                                case XML_ELEMENT_NODE:
                                    switch ($child->tagName) {
                                        case 'file':
                                            //echo "Child is a file tag<br />\n";
                                            $this->files[] = $child->getAttribute('href');
                                            break;
                                        case 'metadata':
                                            //echo "Child is a metadata tag<br />\n";
                                            $this->metadata = new scormMetadata('manifest', $child);
                                            break;
                                        case 'dependency':
                                            // Need to get identifierref attribute inside dependency node.
                                            // dependencies[] array represents all <dependency identifierref='x'> tags united.
                                            $this->dependencies[] = $child->getAttribute('identifierref');
                                            break;
                                    }
                                    break;
                            }
                        }
                    }
                    //$keep_href = '';
                    if ($element->hasAttributes()){ //in some cases we get here with an empty attributes array
                    // TODO: Find when and why we get such a case (empty array).
                        $attributes = $element->attributes;
                        foreach ($attributes as $attrib) {
                            switch ($attrib->name) {
                                case 'identifier':
                                    $this->identifier = $attrib->value;
                                    break;
                                case 'type':
                                    if (!empty($attrib->value)) {
                                        $this->type = $attrib->value;
                                    }
                                    break;
                                case 'scormtype':
                                    if (!empty($attrib->value)) {
                                        $this->scormtype = $attrib->value;
                                    }
                                    break;
                                case 'base':
                                    $this->base = $attrib->value;
                                    break;
                                case 'href':
                                    $this->href = $attrib->value;
                                    break;
                            }
                        }
                    }
                    return true;
            }

            // End parsing using PHP5 DOMXML methods.

        }
        return false;
    }

    /**
     * Path getter
     * @return	string	Path for this resource
     */
    public function get_path() {
        if (!empty($this->href)) {            
            return Database::escape_string($this->href);
        } else {
            return '';
        }
    }

    /**
     * Scorm type getter
     * @return	string	generally 'asset' or 'sco' as these are the only two values defined in SCORM 1.2
     */
    public function get_scorm_type() {
        if (!empty($this->scormtype)) {            
            return Database::escape_string($this->scormtype);
        } else {
            return '';
        }
    }
}
scorm_api.php000064400000220106152003363470007235 0ustar00<?php
/* For licensing terms, see /license.txt */

/**
 *	API event handler functions for Scorm 1.1 and 1.2 and 1.3
 *
 *	@author   Denes Nagy <darkden@freemail.hu>
 *   @author   Yannick Warnier <ywarnier@beeznest.org>
 *	@version  v 1.0
 *	@access   public
 *   @package  chamilo.learnpath.scorm
 */

/**
 * This script is divided into three sections.
 * The first section (below) is the initialisation part.
 * The second section is the SCORM object part
 * The third section defines the event handlers for Chamilo's internal messaging
 * and frames refresh
 */

/* INIT SECTION */

// If you open the imsmanifest.xml via local machine (f.ex.: file://c:/...), then the Apiwrapper.js
// of Maritime Navigation when trying to execute this row
//    var result = api.LMSInitialize("");
// get the error response : you are not authorized to call this function

// Flag to allow for anonymous user - needs to be set before global.inc.php.
$use_anonymous = true;

require_once 'back_compat.inc.php';
require_once 'learnpath.class.php';
require_once 'learnpathItem.class.php';
require_once 'scorm.class.php';

$file   = (empty($_SESSION['file'])?'':$_SESSION['file']);
$oLP    = unserialize($_SESSION['lpobject']);
$oItem 	= $oLP->items[$oLP->current];

if (!is_object($oItem)) {
    error_log('New LP - scorm_api - Could not load oItem item',0);
    exit;
}
$autocomplete_when_80pct = 0;
$user = api_get_user_info();
header('Content-type: text/javascript');

?>var scorm_logs=<?php echo ((empty($oLP->scorm_debug) or (!api_is_course_admin() && !api_is_platform_admin()) )?'0':'3');?>; //debug log level for SCORM. 0 = none, 1=light, 2=a lot, 3=all - displays logs in log frame
var lms_logs=0; //debug log level for LMS actions. 0=none, 1=light, 2=a lot, 3=all

// API Object initialization (eases access later on)
function APIobject() {
    this.LMSInitialize=LMSInitialize;  //for Scorm 1.2
    this.Initialize=LMSInitialize;     //for Scorm 1.3
    this.LMSGetValue=LMSGetValue;
    this.GetValue=LMSGetValue;
    this.LMSSetValue=LMSSetValue;
    this.SetValue=LMSSetValue;
    this.LMSCommit=LMSCommit;
    this.Commit=LMSCommit;
    this.LMSFinish=LMSFinish;
    this.Finish=LMSFinish;
    this.LMSGetLastError=LMSGetLastError;
    this.GetLastError=LMSGetLastError;
    this.LMSGetErrorString=LMSGetErrorString;
    this.GetErrorString=LMSGetErrorString;
    this.LMSGetDiagnostic=LMSGetDiagnostic;
    this.GetDiagnostic=LMSGetDiagnostic;
    this.Terminate=Terminate;  //only in Scorm 1.3
    this.save_asset = lms_save_asset;
    this.void_save_asset = chamilo_void_save_asset;
}

// it is not sure that the scos use the above declarations. The following
// multiple declarations are to make sure we have an API object for each type of
// SCORM
var API = new APIobject(); //for scorm 1.2
var api = API;
var API_1484_11 = new APIobject();  //for scorm 1.3
var api_1484_11 = API_1484_11;

// SCORM-specific Error codes
var G_NoError = 0;
var G_GeneralException = 101;
var G_ServerBusy                = 102; // this is not in the Scorm1.2_Runtime document
var G_InvalidArgumentError = 201;
var G_ElementCannotHaveChildren = 202;
var G_ElementIsNotAnArray = 203;
var G_NotInitialized = 301;
var G_NotImplementedError = 401;
var G_InvalidSetValue = 402;
var G_ElementIsReadOnly = 403;
var G_ElementIsWriteOnly = 404;
var G_IncorrectDataType = 405;

// SCORM-specific Error messages
var G_NoErrorMessage                    = '';
var G_GeneralExceptionMessage           = 'General Exception';
var G_ServerBusyMessage                 = 'Server busy'; // this is not in the Scorm1.2_Runtime document
var G_InvalidArgumentErrorMessage       = 'Invalid argument error';
var G_ElementCannotHaveChildrenMessage  = 'Element cannot have children';
var G_ElementIsNotAnArrayMessage        = 'Element not an array.  Cannot have count';
var G_NotInitializedMessage             = 'Not initialized';
var G_NotImplementedErrorMessage        = 'Not implemented error';
var G_InvalidSetValueMessage            = 'Invalid set value, element is a keyword';
var G_ElementIsReadOnlyMessage          = 'Element is read only';
var G_ElementIsWriteOnlyMessage         = 'Element is write only';
var G_IncorrectDataTypeMessage          = 'Incorrect Data Type';

var olms = new Object();

//the last recorded error message was:
olms.G_LastError = G_NoError ;
olms.G_LastErrorMessage = 'No error';

//this is not necessary and is only provided to make bad Articulate contents shut up (and not trigger useless JS messages)
olms.G_LastErrorString = 'No error';

//these variables are provided for better control of the current status in the
// SCORM exchange
olms.commit = false;

// informative array helping to select variables to save, later on
olms.scorm_variables = new Array(
    'cmi.core.score.raw',
    'cmi.core.score.max',
    'cmi.core.score.min',
    'cmi.core.lesson_location',
    'cmi.core.lesson_status',
    'cmi.completion_status',
    'cmi.core.session_time',
    'cmi.score.scaled',
    'cmi.success_status',
    'cmi.suspend_data',
    'cmi.core.exit',
    'interactions'
);

// manage variables to save or not
olms.variable_to_send=new Array();

// temporary list of variables (gets set to true when set through LMSSetValue)
olms.updatable_vars_list = new Array();

// Strictly scorm variables
olms.score=<?php echo $oItem->get_score();?>;
olms.max='<?php echo $oItem->get_max();?>';
olms.min='<?php echo $oItem->get_min();?>';
olms.lesson_status='<?php echo $oItem->get_status();?>';
olms.session_time='<?php echo $oItem->get_scorm_time('js');?>';
olms.suspend_data = '<?php echo $oItem->get_suspend_data();?>';
olms.lesson_location = '<?php echo $oItem->get_lesson_location();?>';
olms.total_time = '<?php echo $oItem->get_scorm_time('js');?>';
olms.mastery_score = '<?php echo $oItem->get_mastery_score();?>';
olms.launch_data = '<?php echo $oItem->get_launch_data();?>';
olms.max_time_allowed = '<?php echo $oItem->get_max_time_allowed();?>';
olms.interactions = new Array(<?php echo $oItem->get_interactions_js_array();?>);
olms.item_objectives = new Array();
olms.info_lms_item=new Array();

// Chamilo internal variables (not SCORM)
// olms.saved_lesson_status = 'not attempted';
olms.lms_lp_id = <?php echo $oLP->get_id();?>;
olms.lms_item_id = <?php echo $oItem->get_id();?>;
olms.lms_initialized = 0;

//olms.lms_total_lessons = <?php echo $oLP->get_total_items_count(); ?>;
//olms.lms_complete_lessons = <?php echo $oLP->get_complete_items_count();?>;
//olms.lms_progress_bar_mode = '<?php echo $oLP->progress_bar_mode;?>';
//if(lms_progress_bar_mode == ''){lms_progress_bar_mode='%';}

olms.lms_view_id = '<?php echo $oLP->get_view();?>';
if(olms.lms_view_id == ''){ olms.lms_view_id = 1;}
olms.lms_user_id = '<?php echo $_user['user_id'];?>';
olms.lms_next_item = '<?php echo $oLP->get_next_item_id();?>';
olms.lms_previous_item = '<?php echo $oLP->get_previous_item_id();?>';
olms.lms_lp_type = '<?php echo $oLP->get_type();?>';
olms.lms_item_type = '<?php echo $oItem->get_type();?>';
olms.lms_item_credit = '<?php echo $oItem->get_credit();?>';
olms.lms_item_lesson_mode = '<?php echo $oItem->get_lesson_mode();?>';
olms.lms_item_launch_data = '<?php echo $oItem->get_launch_data();?>';
olms.lms_item_core_exit = '<?php echo $oItem->get_core_exit();?>';
<?php echo $oLP->get_items_details_as_js('olms.lms_item_types');?>

olms.asset_timer = 0;
olms.userfname = '<?php echo str_replace("'","\\'",$user['firstname']); ?>';
olms.userlname = '<?php echo str_replace("'","\\'",$user['lastname']); ?>';

olms.execute_stats = false;

/**
 * Add the "addListeners" function to the "onload" event of the window and
 * start the timer if necessary (asset)
 */
addEvent(window, 'load', addListeners, false);

// Initialize stuff when the page is loaded
$(document).ready(function() {
    logit_lms('document.ready start');

    olms.info_lms_item[0] = '<?php echo $oItem->get_id();?>';
    olms.info_lms_item[1] = '<?php echo $oItem->get_id();?>';

    $("#content_id").load(function() {
        logit_lms('#content_id on load executing: ');
        olms.info_lms_item[0] = olms.info_lms_item[1];

        if (olms.lms_item_types['i'+olms.info_lms_item[1]] != 'sco') {
            LMSInitialize();
        } else {
            logit_lms('Cant execute LMSInitialize() ');
        }
    });
});

//Seems that this objs are not used
//oXAJAX = new XAJAXobject();
//oxajax = new XAJAXobject();

// This code was moved inside LMSInitialize()
if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
    xajax_start_timer();
}

/**
 * The following section represents a set of mandatory functions for SCORM
 */
/**
 * Function called mandatorily by the SCORM content to start the SCORM comm
 * This is the initialize function of all APIobjects
 * @return  string  'true' or 'false'. Returning a string is mandatory (SCORM).
 */
function LMSInitialize() {
    /* load info for this new item by calling the js_api_refresh command in
     * the message frame. The message frame will update the JS variables by
     * itself, in JS, by doing things like top.lesson_status = 'not attempted'
     * and that kind of stuff, so when the content loads in the content frame
     * it will have all the correct variables set
     */

    logit_scorm('LMSInitialize()');

    olms.G_LastError = G_NoError ;
    olms.G_LastErrorMessage = 'No error';

    olms.lms_initialized = 0;
    // if there are more parameters than ""
    if (arguments.length > 1) {
        olms.G_LastError 		= G_InvalidArgumentError;
        olms.G_LastErrorMessage 	= G_InvalidArgumentErrorMessage;
        logit_scorm('Error '+ G_InvalidArgumentError + G_InvalidArgumentErrorMessage, 0);
        return('false');
    } else {
        //reinit the list of modified variables
        reinit_updatable_vars_list();

        // Get LMS values for this item
        var params = {
            'lid': olms.lms_lp_id,
            'uid': olms.lms_user_id,
            'vid': olms.lms_view_id,
            'iid': olms.lms_item_id
        };

        $.ajax({
            type: "POST",
            url: "lp_ajax_initialize.php",
            data: params,
            dataType: 'script',
            async: false
        });

        olms.lms_initialized = 1;

        // log a more complete object dump when initializing, so we know what data hasn't been cleaned
        var log = '\nitem             : '+ olms.lms_item_id
                 + '\nitem_type       : '+ olms.lms_item_type
                 + '\nscore           : '+ olms.score
                 + '\nmax             : '+ olms.max
                 + '\nmin             : '+ olms.min
                 + '\nlesson_status   : '+ olms.lesson_status
                 + '\nsession_time    : '+ olms.session_time
                 + '\nlesson_location : '+ olms.lesson_location
                 + '\nsuspend_data    : '+ olms.suspend_data
                 + '\ntotal_time      : '+ olms.total_time
                 + '\nmastery_score   : '+ olms.mastery_score
                 + '\nmax_time_allowed: '+ olms.max_time_allowed
                 + '\ncredit          : '+ olms.lms_item_credit
                 + '\nlms_lp_id       : '+ olms.lms_lp_id
                 + '\nlms_user_id     : '+ olms.lms_user_id
                 + '\nlms_view_id     : '+ olms.lms_view_id
                ;

        logit_scorm('LMSInitialize() with params: '+log);

        if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
            xajax_start_timer();
        }

        if (olms.lms_item_type == 'quiz') {
            update_toc(olms.lesson_status, olms.lms_item_id);
        }

        <?php if (api_get_setting('show_glossary_in_documents') == 'ismanual') { ?>
            if (olms.lms_item_type == 'sco') {
                attach_glossary_into_scorm('automatic');
            } else {
                attach_glossary_into_scorm('manual');
            }
        <?php } elseif (api_get_setting('show_glossary_in_documents') == 'isautomatic') { ?>
            attach_glossary_into_scorm('automatic');
        <?php } ?>
        return('true');
    }
}

/**
 * Twin sister of LMSInitialize(). Only provided for backwards compatibility.
 * this is the initialize function of all APIobjects
 */
function Initialize() {
    return LMSInitialize();
}

/**
 * Gets a value in the current SCORM context and returns it to the calling SCO
 * @param   string  The name of the value we want
 * @return  string  All return values must be string (see SCORM)
 */
function LMSGetValue(param) {
    olms.G_LastError = G_NoError ;
    olms.G_LastErrorMessage = 'No error';
    var result='';

    // the LMSInitialize is missing
    if (olms.lms_initialized == 0) {
         olms.G_LastError 		= G_NotInitialized;
         olms.G_LastErrorMessage = G_NotInitializedMessage;
         logit_scorm('LMSGetValue('+param+'):<br />=> Error '+ G_NotInitialized + ' ' +G_NotInitializedMessage, 0);
         return '';
    }

    //Chamilo does not support these SCO object properties

    if (param == 'cmi.student_preference.text' ||
        param == 'cmi.student_preference.language' ||
        param == 'cmi.student_preference.speed' ||
        param == 'cmi.student_preference.audio' ||
        param == 'cmi.student_preference._children' ||
        param == 'cmi.student_data.time_limit_action' ||
        param == 'cmi.comments' ||
        param == 'cmi.comments_from_lms' ||
/* The following properties were part of SCORM 1.0 or never implemented at all
 but seem to react badly to Captivate content producer when not defined */
        param == 'cmi.student_demographics._children' ||
        param == 'cmi.student_demographics.city' ||
        param == 'cmi.student_demographics.class' ||
        param == 'cmi.student_demographics.company' ||
        param == 'cmi.student_demographics.country' ||
        param == 'cmi.student_demographics.experience' ||
        param == 'cmi.student_demographics.familiar_name' ||
        param == 'cmi.student_demographics.instructor_name' ||
        param == 'cmi.student_demographics.title' ||
        param == 'cmi.student_demographics.native_language' ||
        param == 'cmi.student_demographics.state' ||
        param == 'cmi.student_demographics.street_address' ||
        param == 'cmi.student_demographics.telephone' ||
        param == 'cmi.student_demographics.years_experience' ) {
        // the value is not supported
        olms.G_LastError = G_NotImplementedError  ;
        olms.G_LastErrorString = G_NotImplementedErrorMessage;
        logit_scorm("LMSGetValue  ('"+param+"') Error '"+G_NotImplementedErrorMessage+"'",1);
        result = '';
        return result;
    }
    if (param=='cmi.student_demographics.first_name') {
        result=olms.userfname;
    } else if(param=='cmi.student_demographics.last_name') {
        result=olms.userlname;
        // ---- cmi.core._children
    } else if(param=='cmi.core._children' || param=='cmi.core_children'){
        result='entry, exit, lesson_status, student_id, student_name, lesson_location, total_time, credit, lesson_mode, score, session_time';
    } else if(param == 'cmi.core.entry'){
        // ---- cmi.core.entry
        if(olms.lms_item_core_exit=='none') {
            result='ab-initio';
        } else if(olms.lms_item_core_exit=='suspend') {
            result='resume';
        } else {
            result='';
        }
    } else if(param == 'cmi.core.exit'){
        // ---- cmi.core.exit
        result='';
        olms.G_LastError = G_ElementIsWriteOnly;
    } else if(param == 'cmi.core.session_time'){
        result='';
        olms.G_LastError = G_ElementIsWriteOnly;
    } else if(param == 'cmi.core.lesson_status'){
        // ---- cmi.core.lesson_status
        if(olms.lesson_status != '') {
            result=olms.lesson_status;
        } else {
            //result='not attempted';
        }
    } else if(param == 'cmi.core.student_id'){
        // ---- cmi.core.student_id
        result='<?php echo $_user['user_id']; ?>';
    } else if(param == 'cmi.core.student_name'){
        // ---- cmi.core.student_name
        <?php
          $who = addslashes(api_get_person_name($_user['firstName'], $_user['lastName']));
          echo "result='$who';";
        ?>
    } else if(param == 'cmi.core.lesson_location'){
        // ---- cmi.core.lesson_location
        result=olms.lesson_location;
    } else if(param == 'cmi.core.total_time'){
        // ---- cmi.core.total_time
        result=olms.total_time;
    } else if(param == 'cmi.core.score._children'){
        // ---- cmi.core.score._children
        result='raw,min,max';
    } else if(param == 'cmi.core.score.raw'){
        // ---- cmi.core.score.raw
        result=olms.score;
    } else if(param == 'cmi.core.score.max'){
        // ---- cmi.core.score.max
        result=olms.max;
    } else if(param == 'cmi.core.score.min'){
        // ---- cmi.core.score.min
        result=olms.min;
    } else if(param == 'cmi.core.score'){
        // ---- cmi.core.score -- non-standard item, provided as cmi.core.score.raw just in case
        result=olms.score;
    } else if(param == 'cmi.core.credit'){
        // ---- cmi.core.credit
        result = olms.lms_item_credit;
    } else if(param == 'cmi.core.lesson_mode'){
        // ---- cmi.core.lesson_mode
        result = olms.lms_item_lesson_mode;
    } else if(param == 'cmi.suspend_data'){
    // ---- cmi.suspend_data
        result = olms.suspend_data;
    } else if(param == 'cmi.launch_data'){
    // ---- cmi.launch_data
        result = olms.lms_item_launch_data;
    } else if(param == 'cmi.objectives._children'){
    // ---- cmi.objectives._children
        result = 'id,score,status';
    } else if(param == 'cmi.objectives._count'){
    // ---- cmi.objectives._count
        //result='<?php echo $oItem->get_view_count();?>';
        result = olms.item_objectives.length;
    } else if(param.substring(0,15)== 'cmi.objectives.'){
        var myres = '';
        if(myres = param.match(/cmi.objectives.(\d+).(id|score|status|_children)(.*)/)) {
            var obj_id = myres[1];
            var req_type = myres[2];
            if(olms.item_objectives[obj_id]==null) {
                if(req_type == 'id') {
                    result = '';
                } else if(req_type == '_children'){
                    result = 'id,score,status';
                } else if(req_type == 'score'){
                    if(myres[3]==null) {
                        result = '';
                        olms.G_LastError = G_NotImplementedError;
                        olms.G_LastErrorString = 'Not implemented yet';
                    }else if (myres[3] == '._children'){
                        result = 'raw,min,max'; //non-standard, added for NetG
                    }else if (myres[3] == '.raw'){
                        result = '';
                    }else if (myres[3] == '.max'){
                        result = '';
                    }else if (myres[3] == '.min'){
                        result = '';
                    }else{
                        result = '';
                        olms.G_LastError = G_NotImplementedError;
                        olms.G_LastErrorString = 'Not implemented yet';
                    }
                }else if(req_type == 'status'){
                    result = 'not attempted';
                }
           } else {
                //the object is not null
                if(req_type == 'id') {
                    result = olms.item_objectives[obj_id][0];
                } else if(req_type == '_children'){
                    result = 'id,score,status';
                } else if(req_type == 'score'){
                    if(myres[3]==null) {
                        result = '';
                        olms.G_LastError = G_NotImplementedError;
                        olms.G_LastErrorString = 'Not implemented yet';
                    } else if (myres[3] == '._children'){
                        result = 'raw,min,max'; //non-standard, added for NetG
                    } else if (myres[3] == '.raw'){
                        if(olms.item_objectives[obj_id][2] != null)
                        {
                            result = olms.item_objectives[obj_id][2];
                        }else{
                            result = '';
                        }
                    } else if (myres[3] == '.max'){
                        if(olms.item_objectives[obj_id][3] != null) {
                            result = olms.item_objectives[obj_id][3];
                        }else{
                            result = '';
                        }
                    } else if (myres[3] == '.min') {
                        if(olms.item_objectives[obj_id][4] != null) {
                            result = olms.item_objectives[obj_id][4];
                        } else {
                            result = '';
                        }
                    } else{
                        result = '';
                        olms.G_LastError = G_NotImplementedError;
                        olms.G_LastErrorString = 'Not implemented yet';
                    }
                } else if(req_type == 'status'){
                    if(olms.item_objectives[obj_id][1] != null) {
                        result = olms.item_objectives[obj_id][1];
                    } else {
                        result = 'not attempted';
                    }
                }
            }
        }
    } else if(param == 'cmi.student_data._children'){
        // ---- cmi.student_data._children
        result = 'mastery_score,max_time_allowed';
    } else if(param == 'cmi.student_data.mastery_score'){
        // ---- cmi.student_data.mastery_score
        result = olms.mastery_score;
    } else if(param == 'cmi.student_data.max_time_allowed'){
        // ---- cmi.student_data.max_time_allowed
        result = olms.max_time_allowed;
    } else if(param == 'cmi.interactions._count'){
        // ---- cmi.interactions._count
        result = olms.interactions.length;
    } else if(param == 'cmi.interactions._children'){
        // ---- cmi.interactions._children
        result = 'id,time,type,correct_responses,weighting,student_response,result,latency';
    } else{
        // ---- anything else
        // Invalid argument error
        olms.G_LastError = G_InvalidArgumentError ;
        olms.G_LastErrorString = G_InvalidArgumentErrorMessage;
        logit_scorm("LMSGetValue  ('"+param+"') Error '"+G_InvalidArgumentErrorMessage+"'",1);
        result = '';
        return result;
    }
    logit_scorm("LMSGetValue\n\t('"+param+"') returned '"+result+"'",1);
    return result;
}

/**
 * Twin sister of LMSGetValue(). Only provided for backwards compatibility.
 */
function GetValue(param) {
    return LMSGetValue(param);
}

/**
 * Sets a SCORM variable's value through a call from the SCO.
 * @param   string  The SCORM variable's name
 * @param   string  The SCORM variable's new value
 * @param   string  'true','false' or an error code
 */
function LMSSetValue(param, val) {
    logit_scorm("LMSSetValue ('"+param+"','"+val+"')",0);
    logit_scorm("Checking olms.lms_item_id " + olms.lms_item_id);

    olms.commit = true; //value has changed, need to re-commit
    olms.G_LastError = G_NoError ;
    olms.G_LastErrorMessage = 'No error';
    return_value = 'false';

    if ( param == "cmi.core.score.raw" ) {
        olms.score= val;
        olms.updatable_vars_list['cmi.core.score.raw']=true;
        return_value='true';
    } else if ( param == "cmi.core.score.max" ) {
        olms.max = val;
        olms.updatable_vars_list['cmi.core.score.max']=true;
        return_value='true';
    } else if ( param == "cmi.core.score.min" ) {
        olms.min = val;
        olms.updatable_vars_list['cmi.core.score.min']=true;
        return_value='true';
    } else if ( param == "cmi.core.lesson_location" ) {
        olms.lesson_location = val;
        olms.updatable_vars_list['cmi.core.lesson_location']=true;
        return_value='true';
    } else if ( param == "cmi.core.lesson_status" ) {
        olms.lesson_status = val;
        olms.updatable_vars_list['cmi.core.lesson_status'] = true;
        return_value='true';
    } else if ( param == "cmi.completion_status" ) {
        olms.lesson_status = val;
        olms.updatable_vars_list['cmi.completion_status']=true;
        return_value='true'; //1.3
    } else if ( param == "cmi.core.session_time" ) {
        olms.session_time = val;
        olms.updatable_vars_list['cmi.core.session_time']=true;
        return_value='true';
    } else if ( param == "cmi.score.scaled") { //1.3
        if (val<=1 && val>=-1) {
            olms.score = val ;
            olms.updatable_vars_list['cmi.score.scaled']=true;
            return_value='true';
        } else {
            return_value='false';
        }
    } else if ( param == "cmi.success_status" ) {
        success_status = val;
        olms.updatable_vars_list['cmi.success_status']=true;
        return_value='true'; //1.3
    } else if ( param == "cmi.suspend_data" ) {
        olms.suspend_data = val;
        olms.updatable_vars_list['cmi.suspend_data']=true;
        return_value='true';
    } else if ( param == "cmi.core.exit" ) {
        olms.lms_item_core_exit = val;
        olms.updatable_vars_list['cmi.core.exit']=true;
        return_value='true';
    } else if ( param == "cmi.core.student_id" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.core.student_name" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.core.credit" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.core.entry" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.core.total_time" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.core.lesson_mode" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.comments_from_lms" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.student_data.time_limit_action" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.student_data.mastery_score" ) {
       olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.student_data.max_time_allowed" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.student_preference._children" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else if ( param == "cmi.launch_data" ) {
        olms.G_LastError = G_ElementIsReadOnly;
    } else {
        var myres = new Array();
        if(myres = param.match(/cmi.interactions.(\d+).(id|time|type|correct_responses|weighting|student_response|result|latency)(.*)/)) {
            olms.updatable_vars_list['interactions']=true;
            elem_id = myres[1];
             //interactions setting should start at 0
            if(elem_id > olms.interactions.length) {
                /*
                olms.G_LastError = G_InvalidArgumentError;
                olms.G_LastErrorString = 'Invalid argument (interactions)';
                return_value = false;
                */
                olms.interactions[0] = ['0','','','','','','',''];
            }
            if(olms.interactions[elem_id] == null) {
                olms.interactions[elem_id] = ['','','','','','','',''];
                //id(0), type(1), time(2), weighting(3),correct_responses(4),student_response(5),result(6),latency(7)
                olms.interactions[elem_id][4] = new Array();
            }
            elem_attrib = myres[2];
            switch (elem_attrib) {
                case "id":
                    olms.interactions[elem_id][0] = val;
                    logit_scorm("Interaction "+elem_id+"'s id updated",2);
                    return_value='true';
                    break;
                case "time":
                    olms.interactions[elem_id][2] = val;
                    logit_scorm("Interaction "+elem_id+"'s time updated",2);
                    return_value='true';
                    break;
                case "type":
                    olms.interactions[elem_id][1] = val;
                    logit_scorm("Interaction "+elem_id+"'s type updated",2);
                    return_value='true';
                    break;
                case "correct_responses":
                    //do nothing yet
                    olms.interactions[elem_id][4].push(val);
                    logit_scorm("Interaction "+elem_id+"'s correct_responses not updated",2);
                    return_value='true';
                    break;
                case "weighting":
                    olms.interactions[elem_id][3] = val;
                    logit_scorm("Interaction "+elem_id+"'s weighting updated",2);
                    return_value='true';
                    break;
                case "student_response":
                    olms.interactions[elem_id][5] = ''+val;
                    logit_scorm("Interaction "+elem_id+"'s student_response updated",2);
                    return_value='true';
                    break;
                case "result":
                    olms.interactions[elem_id][6] = val;
                    logit_scorm("Interaction "+elem_id+"'s result updated",2);
                    return_value='true';
                    break;
                case "latency":
                    olms.interactions[elem_id][7] = val;
                    logit_scorm("Interaction "+elem_id+"'s latency updated",2);
                    return_value='true';
                    break;
                default:
                    olms.G_LastError = G_NotImplementedError;
                    olms.G_LastErrorString = 'Not implemented yet';
            }
        } else if(param.substring(0,15)== 'cmi.objectives.') {
            var myres = '';
            olms.updatable_vars_list['objectives']=true;
            if(myres = param.match(/cmi.objectives.(\d+).(id|score|status)(.*)/)) {
                obj_id = myres[1];
                //objectives setting should start at 0
                if(obj_id > olms.item_objectives.length) {
                    olms.G_LastError = G_InvalidArgumentError;
                    olms.G_LastErrorString = 'Invalid argument (objectives)';
                    return_value = false;
                } else {
                    req_type = myres[2];
                    if(obj_id == null || obj_id == '') {
                        ;//do nothing
                    } else {
                        if(olms.item_objectives[obj_id]==null) {
                            olms.item_objectives[obj_id] = ['','','','',''];
                        }
                        if( req_type == "id" ) {
                                //olms.item_objectives[obj_id][0] = val.substring(51,57);
                                olms.item_objectives[obj_id][0] = val;
                                logit_scorm("Objective "+obj_id+"'s id updated",2);
                                return_value = 'true';
                        } else if ( req_type == "score" ) {
                            if (myres[3] == '._children'){
                                return_value = '';
                                olms.G_LastError = G_InvalidSetValue;
                                olms.G_LastErrorString = 'Invalid set value, element is a keyword';
                            } else if (myres[3] == '.raw'){
                                olms.item_objectives[obj_id][2] = val;
                                logit_scorm("Objective "+obj_id+"'s score raw updated",2);
                                return_value = 'true';
                            } else if (myres[3] == '.max'){
                                olms.item_objectives[obj_id][3] = val;
                                logit_scorm("Objective "+obj_id+"'s score max updated",2);
                                return_value = 'true';
                            } else if (myres[3] == '.min'){
                                olms.item_objectives[obj_id][4] = val;
                                logit_scorm("Objective "+obj_id+"'s score min updated",2);
                                return_value = 'true';
                            } else{
                                return_value = '';
                                olms.G_LastError = G_NotImplementedError;
                                olms.G_LastErrorString = 'Not implemented yet';
                            }
                        } else if ( req_type == "status" ) {
                                olms.item_objectives[obj_id][1] = val;
                                logit_scorm("Objective "+obj_id+"'s status updated",2);
                                return_value = 'true';
                        } else {
                                olms.G_LastError = G_NotImplementedError;
                                olms.G_LastErrorString = 'Not implemented yet';
                        }
                    }
                }
            }
        } else {
            olms.G_LastError = G_NotImplementedError;
            olms.G_LastErrorString = G_NotImplementedErrorMessage;
        }
    }
    <?php
    if ($oLP->force_commit == 1){
        echo " var mycommit = LMSCommit('force');";
    }
    ?>
    return(return_value);
}

/**
 * Twin sister of LMSSetValue(). Only provided for backwards compatibility.
 */
function SetValue(param, val) {
    return LMSSetValue(param, val);
}

/**
 * Saves the current data from JS memory to the LMS database
 * @param   string  The origin of the call to save the data ('commit','finish', 'unload' or 'terminate')
 * @note    origin actually seems deprecated now
 */
function savedata(origin) {
    //origin can be 'commit', 'finish' or 'terminate' (depending on the calling function)
    logit_lms('function savedata() with origin: ' + origin);

    //Status is NOT modified here see the lp_ajax_save_item.php file

    if (olms.lesson_status != '') {
        olms.updatable_vars_list['cmi.core.lesson_status'] = true;
    }

    old_item_id = olms.info_lms_item[0];

    var item_to_save = olms.lms_item_id;
    logit_lms('item_to_save original: ' + item_to_save);

    //If saving session_time value we asume that is from the old item not the current one
    if (olms.session_time != '' && olms.session_time != '0') {
        logit_lms('item_to_save changed to: ' + old_item_id);
        item_to_save = old_item_id;
    }

    logit_lms('item_to_save final: ' + item_to_save);

    //Original behaviour
    //xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, old_item_id);

    //Modified version
    xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, item_to_save);

    olms.info_lms_item[1] = olms.lms_item_id;
    if (olms.item_objectives.length > 0) {
        xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id, old_item_id, olms.item_objectives);
    }
    olms.execute_stats = false;
    //clean array
    olms.variable_to_send = new Array();
}

/**
 * Send the Commit signal to the LMS (save the data for this element without
 * closing the current process)
 * From SCORM 1.2 RTE: If the API Adapter is caching values received from the
 * SCO via an LMSSetValue(), this call requires that any values not yet
 * persisted by the LMS be persisted.
 * @param   string      Must be empty string for conformance with SCORM 1.2
 */
function LMSCommit(val) {
    logit_scorm('LMSCommit() + val');

    olms.G_LastError = G_NoError ;
    olms.G_LastErrorMessage = 'No error';
    savedata('commit');

    reinit_updatable_vars_list();
    //commit = 'false' ; //now changes have been commited, no need to update until next SetValue()
    return('true');
}

/**
 * Twin sister of LMSCommit(). Only provided for backwards compatibility.
 */
function Commit(val) {
    return LMSCommit(val);
}

/**
 * Send the closure signal to the LMS. This saves the data and closes the current SCO.
 * From SCORM 1.2 RTE: The SCO must call this when it has determined that it no
 * longer needs to communicate with the LMS, if it successfully called
 * LMSInitialize at any previous point.  This call signifies two things:
 * 1.The SCO can be assured that any data set using LMSSetValue() calls has been persisted by the LMS.
 * 2.The SCO has finished communicating with the LMS.
 * @param   string
 */
function LMSFinish(val) {
    olms.G_LastError = G_NoError ;
    olms.G_LastErrorMessage = 'No error';
    // if olms.commit == false, then the SCORM didn't ask for a commit, so we
    // should at least report that
    if ( !olms.commit ) {
        logit_scorm('LMSFinish() (no LMSCommit())',1);
    }

    //if ( olms.commit ) {
        logit_scorm('LMSFinish() called',1);
        savedata('finish');
        olms.commit = false;
    //}

    //reinit the list of modified variables
    reinit_updatable_vars_list();
    return('true');
}

/**
 * Twin sister of LMSFinish(). Only provided for backwards compatibility.
 */
function Finish(val) {
    return LMSFinish(val);
}

/**
 * Returns the last error code as a string
 * @return  string  Error code
 */
function LMSGetLastError() {
    logit_scorm('LMSGetLastError()',1);
    return(olms.G_LastError.toString());
}

/**
 * Twin sister of LMSGetLastError(). Only provided for backwards compatibility.
 */
function GetLastError() {
    return LMSGetLastError();
}

/**
 * Returns the last error code literal for a given error code
 * @param   int     Error code
 * @return  string  Last error
 */
function LMSGetErrorString(errCode){
    logit_scorm('LMSGetErrorString()',1);
    return(olms.G_LastErrorString);
}

/**
 * Twin sister of LMSGetErrorString(). Only provided for backwards compatibility.
 */
function GetErrorString(errCode){
    return LMSGetErrorString(errCode);
}

/**
 * Returns a more explanatory, full English, error message
 * @param   int     Error code
 * @return  string  Diagnostic
 */
function LMSGetDiagnostic(errCode){
    logit_scorm('LMSGetDiagnostic()',1);
    return(API.LMSGetLastError());
}

/**
 * Twin sister of LMSGetDiagnostic(). Only provided for backwards compatibility.
 */
function GetDiagnostic(errCode){
    return LMSGetDiagnostic(errCode);
}

/**
 * Acts as a "commit"
 * This function is not standard SCORM 1.2 and is probably deprecated in all
 * meanings of the term.
 * @return  string  'true' or 'false', depening on whether the LMS has initialized the SCORM process or not
 */
function Terminate() {
    if (olms.lms_initialized == 0) {
        olms.G_LastError 		= G_NotInitialized;
        olms.G_LastErrorMessage = G_NotInitializedMessage;
        logit_scorm('Error '+ G_NotInitialized + G_NotInitializedMessage, 0);
        return('false');
    } else {
        logit_scorm('Terminate()',0);
        olms.G_LastError = G_NoError ;
        olms.G_LastErrorMessage = 'No error';
        olms.commit = true;
        savedata('terminate');
        return ('true');
    }
}

/**
 * LMS-specific code that deals with event handling and inter-frames
 * messaging/refreshing.
 * Note that from now on, the LMS JS code in this library will act as
 * a controller, of the MVC pattern, and receive all requests for frame
 * updates, then redispatch to any frame concerned.
 */
/**
 * Defining the AJAX-object class to be made available from other frames
 */
function XAJAXobject() {
    this.xajax_switch_item_details = xajax_switch_item_details;
    this.switch_item = switch_item;
    this.xajax_save_objectives = xajax_save_objectives;
    this.xajax_save_item = xajax_save_item;
}

/**
 * Cross-browser event handling by Scott Andrew
 * @param	element	Element that needs an event attached
 * @param   string	Event type (load, unload, click, keyDown, ...)
 * @param   string	Function name (the event handler)
 * @param   string	used in addEventListener
 */
function addEvent(elm, evType, fn, useCapture){
    if (elm.addEventListener){
        elm.addEventListener(evType, fn, useCapture);
        return true;
    } else if (elm.attachEvent) {
        var r = elm.attachEvent('on' + evType, fn);
    } else{
        elm['on'+evType] = fn;
    }
}

/**
 * Add listeners to the page objects. This has to be defined for
 * the current context as it acts on objects that should exist
 * on the page
 * possibly deprecated
 * @todo Try to use $(document).unload(lms_save_asset()) instead of the addEvent() method
 */
function addListeners(){
    //exit if the browser doesn't support ID or tag retrieval
    logit_lms('Entering addListeners()',2);
    if (!document.getElementsByTagName){
        logit_lms("getElementsByTagName not available",2);
        return;
    }
    if (!document.getElementById){
        logit_lms("getElementById not available",2);
        return;
    }
    //assign event handlers to objects
    if (olms.lms_lp_type==1 || olms.lms_item_type=='asset'){
        logit_lms('Chamilo LP or asset',2);
        //if this path is a Chamilo learnpath, then start manual save
        //when something is loaded in there
        addEvent(window, 'unload', lms_save_asset,false);
        logit_lms('Added event listener lms_save_asset() on window unload',2);
    }
    logit_lms('Quitting addListeners()',2);
}

/**
 * Save a Chamilo learnpath item's time and mark as completed upon
 * leaving it
 */
function lms_save_asset() {
    // only for Chamilo lps
    if (olms.execute_stats) {
        olms.execute_stats = false;
    } else {
        olms.execute_stats = true;
    }

    //For scorms do not show stats
    if (olms.lms_lp_type == 2) {
       olms.execute_stats = false;
    }

    if (olms.lms_item_type == 'quiz') {
        olms.execute_stats = true;
    }

    if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
        logit_lms('lms_save_asset');
        logit_lms('execute_stats :'+ olms.execute_stats);
        xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location, olms.interactions, olms.lms_item_core_exit);
        if (olms.item_objectives.length>0) {
            xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,olms.item_objectives);
        }
    }
}

/**
 * Save a Chamilo learnpath item's time and mark as completed upon leaving it.
 * Same function as lms_save_asset() but saves it with empty params
 * to use values set from another side in the database. Only used by Chamilo quizzes.
 * Also save the score locally because it hasn't been done through SetValue().
 * Saving the status will be dealt with by the XAJAX function.
 */
function chamilo_void_save_asset(score, max, min, status) {
    logit_lms('chamilo_void_save_asset',2);
    olms.score = score;
    if ((max == null) || (max == '')){
        max = 100;
    }

    if ((min == null) || (min == '')){
        min = 0;
    }

    //assume a default of 100, otherwise the score will not get saved (see lpi->set_score())
    xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, score, max, min, status);
}

/**
 * Logs information about SCORM messages into the log frame
 * @param	string	Message to log
 * @param	integer Priority (0 for top priority, 3 for lowest)
 */
function logit_scorm(message, priority) {
    if (scorm_logs) {
        log_in_log("SCORM: " + message);
    }
    return false;
}

function log_in_log(message) {
    var ua = $.browser;
    if (ua.mozilla) {
        console.log(message);
    } else {
        if (window.console) {
            window.console.log(message);
        }
    }
}

/**
 * Logs information about LMS activity into the log frame
 * @param	string	Message to log
 * @param	integer Priority (0 for top priority, 3 for lowest)
 */
function logit_lms(message, priority){
    if (scorm_logs) {
        log_in_log("LMS: " + message);
    }
    return false;
}

/**
 * Update the Table Of Contents frame, by changing CSS styles, mostly
 * @param	string	Action to be taken
 * @param	integer	Item id to update
 */
function update_toc(update_action, update_id, change_ids) {
    if (!change_ids || change_ids != 'no') {
        change_ids = 'yes';
    }
    var myelem = $("#toc_"+update_id);
    var myelemimg = $("#toc_img_"+update_id);
    logit_lms('update_toc("'+update_action+'", '+update_id+')',2);

    if (update_id != 0) {
        switch (update_action) {
            case 'unhighlight':
                if (update_id%2==0) {
                    myelem.attr('class',"scorm_item_2");
                } else {
                    myelem.attr('class',"scorm_item_1");
                }
                break;
            case 'highlight':
                if (change_ids=='yes') {
                   olms.lms_next_item = update_id;
                   olms.lms_previous_item = update_id;
                }
                myelem.attr('class',"scorm_item_highlight");
                break;
            case 'not attempted':
                if( myelemimg.attr('src') != '../img/notattempted.gif') {
                    myelemimg.attr('src','../img/notattempted.gif');
                    myelemimg.attr('alt','n');
                }
                break;
            case 'incomplete':
                if( myelemimg.attr('src') != '../img/incomplete.png') {
                    myelemimg.attr('src','../img/incomplete.png');
                    myelemimg.attr('alt','i');
                }
                break;
            case 'completed':
                if( myelemimg.attr('src') != '../img/completed.png') {
                    myelemimg.attr('src','../img/completed.png');
                    myelemimg.attr('alt','c');
                }
                break;
            case 'failed':
                if( myelemimg.attr('src') != '../img/delete.png') {
                    myelemimg.attr('src','../img/delete.png');
                    myelemimg.attr('alt','f');
                }
                break;
            case 'passed':
                if( myelemimg.attr('src') != '../img/completed.png' && myelemimg.attr('alt') != 'passed') {
                    myelemimg.attr('src','../img/completed.png');
                    myelemimg.attr('alt','p');
                }
                break;
            case 'browsed':
                if( myelemimg.attr('src') != '../img/completed.png' && myelemimg.attr('alt') != 'browsed') {
                    myelemimg.attr('src','../img/completed.png');
                    myelemimg.attr('alt','b');
                }
                break;
            default:
                logit_lms('Update action unknown',2);
                break;
        }
    }
    return true;
}

/**
 * Update the stats frame using a reload of the frame to avoid unsynched data
 */
function update_stats() {
    logit_lms('update_stats()');
    if (olms.execute_stats) {
        try {
            cont_f = document.getElementById('content_id');
            cont_f.src = "lp_controller.php?action=stats";
            cont_f.reload();
        } catch (e) {
            return false;
        }
    }
    olms.execute_stats = false;
}

/**
 * Update the stats frame using a reload of the frame to avoid unsynched data
 */
function update_stats_page() {
    logit_lms('update_stats_page');
    var myframe = document.getElementById('content_id');
    var mysrc = myframe.location.href;
    if(mysrc == 'lp_controller.php?action=stats'){
        if(myframe && myframe.src){
            var mysrc = myframe.src;
            myframe.src = mysrc;
        }
        // = mysrc; //refresh page
    }
    return true;
}

/**
 * Updates the progress bar with the new status. Prevents the need of a page refresh and flickering
 * @param	integer	Number of completed items
 * @param	integer	Number of items in total
 * @param	string  Display mode (absolute 'abs' or percentage '%').Defaults to %
 */
function update_progress_bar(nbr_complete, nbr_total, mode) {
    logit_lms('update_progress_bar('+nbr_complete+', '+nbr_total+', '+mode+')',2);
    logit_lms('update_progress_bar with params: lms_lp_id= '+olms.lms_lp_id+', lms_view_id= '+olms.lms_view_id+' lms_user_id= '+olms.lms_user_id,2);

    if (mode == '') {
        mode='%';
    }

    if (nbr_total == 0) {
        nbr_total=1;
    }

    var percentage = (nbr_complete/nbr_total)*100;
    percentage = Math.round(percentage);

    var pr_text = $("#progress_text");
    var progress_bar = $("#progress_bar_value");
    progress_bar.css('width', percentage + "%");

    var mytext = '';
    switch(mode){
        case 'abs':
            mytext = nbr_complete + '/' + nbr_total;
            break;
        case '%':
        default:
            mytext = percentage + '%';
            break;
    }
    pr_text.html(mytext);
    return true;
}

/**
 * Analyses the variables that have been modified through this SCO's life and
 * put them into an array for later shipping to lp_ajax_save_item.php
 * @return  array   Array of SCO variables
 */
function process_scorm_values() {
    logit_scorm('process_scorm_values()');
    for (i=0; i<olms.scorm_variables.length; i++) {
        if (olms.updatable_vars_list[olms.scorm_variables[i]]) {
            olms.variable_to_send.push(olms.scorm_variables[i]);
        }
    }
    return olms.variable_to_send;
}

/**
 * Reinitializes the SCO's modified variables to an empty list.
 * @return  void
 * @uses The global updatable_vars_list array to register this
 */
function reinit_updatable_vars_list() {
    logit_scorm('Cleaning updatable_vars_list: reinit_updatable_vars_list');
    for (i=0;i<olms.scorm_variables.length;i++) {
        if (olms.updatable_vars_list[olms.scorm_variables[i]]) {
            olms.updatable_vars_list[olms.scorm_variables[i]]=false;
        }
    }
    olms.lesson_status='';
}

/**
 * Function that handles the saving of an item and switching from an item to another.
 * Once called, this function should be able to do the whole process of
 * (1) saving the current item,
 * (2) refresh all the values inside the SCORM API object,
 * (3) open the new item into the content_id frame,
 * (4) refresh the table of contents
 * (5) refresh the progress bar (completion)
 * (6) refresh the message frame
 * @param	integer		Chamilo ID for the current item
 * @param	string		This parameter can be a string specifying the next
 *						item (like 'next', 'previous', 'first' or 'last') or the id to the next item
 */
function switch_item(current_item, next_item){
    // backup these params
    var orig_current_item   = current_item;
    var orig_next_item      = next_item;
    var orig_lesson_status  = olms.lesson_status;
    var orig_item_type      = olms.lms_item_types['i'+current_item];
    var next_item_type      = olms.lms_item_types['i'+next_item];

    logit_lms('switch_item() called with params '+olms.lms_item_id+' and '+next_item+'',0);

    /*
     There are four "cases" for switching items:
     (1) asset switching to asset
         We need to save, then switch
     (2) asset switching to sco
         We need to save, switching not necessary (LMSInitialize does the job)
     (3) sco switching to asset
         We need to switch the document in the content frame, but we cannot
         switch the item details, otherwise the LMSFinish() call (that *must*
         be triggered by the SCO when it unloads) will use bad values. However,
         we need to load the new asset's context once the SCO has unloaded
     (4) sco switching to sco
         We don't neet to switch nor commit, LMSFinish() on unload and
         LMSInitialize on load will do the job
     In any case, we need to change the current document frame.
     These cases, although clear here, are however very difficult to implement
    */

    if (orig_item_type != 'sco') {
        if (next_item_type != 'sco' ) {
            //case 1
            logit_lms('Case 1');
            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
            xajax_switch_item_details(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, next_item);
        } else {
            logit_lms('Case 2');
            //case 2
            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
            xajax_switch_item_details(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,next_item);
        }
    } else {
        if (next_item_type != 'sco') {
            logit_lms('Case 3');
            //case 3
            xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id);
            reinit_updatable_vars_list();
            xajax_switch_item_toc(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,next_item);
        } else {
            logit_lms('Case 4');
            //case 4
            xajax_save_item_scorm(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id);
            reinit_updatable_vars_list();
            xajax_switch_item_toc(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,next_item);
        }
        if (olms.item_objectives.length>0) {
            xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,olms.item_objectives);
        }
    }

    /**
     * Because of SCORM 1.2's special rule about unsent commits and the fact
     * that a SCO should be SET TO 'completed' IF NO STATUS WAS SENT (and
     * then some checks have to be done on score), we have to force a
     * special commit here to avoid getting to the next element with a
     * missing prerequisite. The 'onunload' event is treated with
     * savedata_onunload(), and doesn't need to be triggered at any
     * particular time, but here we are in the case of switching to another
     * item, so this is particularly important to complete the element in
     * time.
     * However, this cannot be initiated from the JavaScript, mainly
     * because another onunload event can be triggered by the SCO itself,
     * which can set, for example, the status to incomplete while the
     * status has already been set to "completed" by the hand-made
     * savedata(unload) (and then the status cannot be "incompleted"
     * anymore)
     */

    /*
    if (olms.lms_item_type=='sco' && olms.lesson_status != 'completed' && olms.lesson_status != 'passed' && olms.lesson_status != 'browsed' && olms.lesson_status != 'incomplete' && olms.lesson_status != 'failed') {
        // savedata('finish') treats the special condition and saves the
        // new status to the database, so switch_item_details() enjoys the
        // new status
        savedata('finish');
    }
    xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
    */

    olms.execute_stats = false;

    // Considering info_lms_item[0] is initially the oldest and info_lms_item[1]
    // is the newest item, and considering we are done switching the items now,
    // we need to update these markers so that the new item is loaded when
    // changing the document in the content frame
    if (olms.info_lms_item[1]==next_item && next_item!='next' && next_item!='previous') {
        olms.info_lms_item[0]=next_item;
        olms.info_lms_item[1]=next_item;
    } else {
        if (next_item!='next' && next_item!='previous') {
            olms.info_lms_item[0]=olms.info_lms_item[1];
            olms.info_lms_item[1]=next_item;
        }
    }

    if (olms.info_lms_item[0]==next_item && next_item!='next' && next_item!='previous') {
        olms.info_lms_item[0]=next_item;
        olms.info_lms_item[1]=next_item;
    } else {
        if (next_item!='next' && next_item!='previous') {
            olms.info_lms_item[0]=olms.info_lms_item[0];
            olms.info_lms_item[1]=next_item;
        }
    }

    //(3) open the new item in the content_id frame
    switch (next_item){
        case 'next':
            next_item = olms.lms_next_item;
            olms.info_lms_item[0] = olms.info_lms_item[1];
            olms.info_lms_item[1] = olms.lms_next_item;
            break;
        case 'previous':
            next_item = olms.lms_previous_item;
            olms.info_lms_item[0] = olms.info_lms_item[1];
            olms.info_lms_item[1] = olms.lms_previous_item;
            break;
        default:
            break;
    }

    var mysrc = 'lp_controller.php?action=content&lp_id='+olms.lms_lp_id+'&item_id='+next_item;
    var cont_f = $("#content_id");

    <?php if($oLP->mode == 'fullscreen'){ ?>
    cont_f = window.open(''+mysrc,'content_id','toolbar=0,location=0,status=0,scrollbars=1,resizable=1');
    cont_f.onload=function(){
        olms.info_lms_item[0]=olms.info_lms_item[1];
    }
    cont_f.onunload=function(){
        olms.info_lms_item[0]=olms.info_lms_item[1];
    }

    <?php } else { ?>
            cont_f.attr("src",mysrc);
    <?php } ?>

    if (olms.lms_lp_type==1 || olms.lms_item_type == 'asset'){
        xajax_start_timer();
    }

    //(4) refresh the audio player if needed
    $.ajax({
        type: "POST",
        url: "lp_nav.php",
        data: "",
        success: function(tmp_data) {
            if ($("#lp_media_file").length != 0) {
                $("#lp_media_file").html(tmp_data);
            }
        }
    });
    return true;
}

/**
 * Save a specific item (with its interactions, if any) into the LMS through
 * an AJAX call to lp_ajax_save_item.php.
 * Because of the need to pass an array, we have to build the parameters
 * manually into GET[].
 * This function has a twin sister for SCO elements (xajax_save_item_scorm)
 * which takes into account the interactions.
 * @param   int     ID of the learning path (for the LMS)
 * @param   int     ID of the user
 * @param   int     ID of the view of this learning path
 * @param   int     ID of the item currently looked at
 * @param   float   Score
 * @param   float   Max score
 * @param   float   Min score
 * @param   string  Lesson status
 * @param   string  Current session time (in 'xxxx:xx:xx.xx' format)
 * @param   string  Suspend data (maximum 255 chars)
 * @param   string  Lesson location (which page we've reached in the SCO)
 * @param   array   Interactions
 * @param   string  Core exit value (up to 4096 chars)
 * @return  void
 * @uses lp_ajax_save_item.php through an AJAX call
 */
function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location, interactions, lms_item_core_exit) {
    var params = '';
    params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
    params += '&iid='+lms_item_id+'&s='+score+'&max='+max+'&min='+min;
    params += '&status='+lesson_status+'&t='+session_time;
    params += '&suspend='+suspend_data+'&loc='+lesson_location;
    params += '&core_exit='+lms_item_core_exit;
    //console.info(session_time);
    if (olms.lms_lp_type == 1) {
        logit_lms('xajax_save_item with params:' + params);
        $.ajax({
            type:"POST",
            data: params,
            url: "lp_ajax_save_item.php",
            dataType: "script",
            async: false
        });
    }
}

/**
 * Save a SCORM item's variables, getting its SCORM values from
 * updatable_vars_list. Takes interactions into account and considers whether
 * variables have been modified or not.
 * @param   int     ID of the learning path
 * @param   int     ID of the user
 * @param   int     ID of the view
 * @param   int     ID of the item
 * @return void
 * @uses olms.updatable_vars_list
 * @uses lp_ajax_save_item.php through an AJAX call
 */
function xajax_save_item_scorm(lms_lp_id, lms_user_id, lms_view_id, lms_item_id) {
    var is_interactions='false';
    var params = 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id+'&iid='+lms_item_id;
    var my_scorm_values = new Array();

    my_scorm_values = process_scorm_values();

    for (k=0; k<my_scorm_values.length; k++) {
        if (my_scorm_values[k]=='cmi.core.session_time') {
            params += '&t='+olms.session_time;
        } else if (my_scorm_values[k]=='cmi.core.lesson_status' && olms.lesson_status!='') {
             params += '&status='+olms.lesson_status;
        } else if (my_scorm_values[k]=='cmi.core.score.raw') {
             params += '&s='+olms.score;
        } else if (my_scorm_values[k]=='cmi.core.score.max') {
            params += '&max='+olms.max;
        } else if (my_scorm_values[k]=='cmi.core.score.min') {
            params += '&min='+olms.min;
        } else if (my_scorm_values[k]=='cmi.core.lesson_location') {
            params += '&loc='+olms.lesson_location;
        } else if (my_scorm_values[k]=='cmi.completion_status') {
        } else if (my_scorm_values[k]=='cmi.score.scaled') {
        } else if (my_scorm_values[k]=='cmi.suspend_data') {
            params += '&suspend='+olms.suspend_data;
        } else if (my_scorm_values[k]=='cmi.completion_status') {
        } else if (my_scorm_values[k]=='cmi.core.exit') {
            params += '&core_exit='+olms.lms_item_core_exit;
        }
        if (my_scorm_values[k]=='interactions') {
            is_interactions='true';
        } else {
            is_interactions='false';
        }
    }

    if (is_interactions == 'true')  {
        interact_string = '';
        temp = '';

        for (i in olms.interactions) {
            interact_string += '&interact['+i+']=';
            interact_temp = '[';
            for (j in olms.interactions[i]) {
                temp = olms.interactions[i][j];
                temp = ''+temp; // if temp == 1 there are problems with indexOf and an integer number
                //this fix when an interaction have ',' i.e:   {a,b,c,d} is replace to {a@.|@b@.|@c@.|@d} see DT#4444
                while(temp.indexOf(',') >= 0){
                    temp = temp.replace(',','@.|@');
                };
                interact_temp +=temp+',';
            }
            interact_temp = interact_temp.substr(0,(interact_temp.length-2)) + ']';
            //  interact_string += encodeURIComponent(interact_temp);

            interact_string += interact_temp;
        }
        //interact_string = encodeURIComponent(interact_string.substr(0,(interact_string.length-1)));
        params += interact_string;
        is_interactions='false';
    }

    logit_lms('xajax_save_item_scorm with params:' + params);

    $.ajax({
        type:"POST",
        data: params,
        url: "lp_ajax_save_item.php",
        dataType: "script",
        async: false
    });
    params = '';
    my_scorm_values = null;
}

/**
 * Starts the timer with the server clock time.
 * @return void
 * @todo check the timer stuff really works
 * @uses    lp_ajax_start_timer.php
 */
function xajax_start_timer() {
    logit_lms('xajax_start_timer() called');
    $.ajax({
        type: "GET",
        url: "lp_ajax_start_timer.php",
        dataType: "script",
        async: false,
        success: function(time) {
            olms.asset_timer = time;
            olms.asset_timer_total = 0;
            logit_lms('xajax_start_timer result: ' + time);

            var date = new Date(time * 1000);
            logit_lms('xajax_start_timer result: ' + date.toString());
        }
    });
}

/**
 * Save a specific item's objectives into the LMS through an Synch JAX call
 * @param   int     ID of the learning path
 * @param   int     ID of the user
 * @param   int     ID of the view
 * @param   int     ID of the item
 * @param   array   SCO's recorded objectives
 * @uses    lp_ajax_save_objectives.php
 */
function xajax_save_objectives(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,item_objectives) {
    var params = '';
    params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
    params += '&iid='+lms_item_id;
    obj_string = '';

    logit_lms('xajax_save_objectives with params:' + params);

    for (i in item_objectives){
        obj_string += '&objectives['+i+']=';
        obj_temp = '[';
        for (j in item_objectives[i]) {
            obj_temp += item_objectives[i][j]+',';
        }
        obj_temp = obj_temp.substr(0,(obj_temp.length-2)) + ']';
        obj_string += encodeURIComponent(obj_temp);
    }
    params += obj_string;
    $.ajax({
        type: "POST",
        data: params,
        url: "lp_ajax_save_objectives.php",
        dataType: "script",
        async: false
    });
}

/**
 * Switch between two items through an AJAX call.
 * @param   int     ID of the learning path
 * @param   int     ID of the user
 * @param   int     ID of the view
 * @param   int     ID of the item
 * @param   int     ID of the next item
 * @uses    lp_ajax_switch_item.php
 */
function xajax_switch_item_details(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,next_item) {
    var params = {
        'lid': lms_lp_id,
        'uid': lms_user_id,
        'vid': lms_view_id,
        'iid': lms_item_id,
        'next': next_item
    };

    logit_lms('xajax_switch_item_details with params:' + params);

    $.ajax({
        type: "POST",
        data: params,
        url: "lp_ajax_switch_item.php",
        dataType: "script",
        async: false
    });
}

/**
 * Switch between two items through an AJAX call, but only update the TOC and
 * progress bar.
 * @param   int     ID of the learning path
 * @param   int     ID of the user
 * @param   int     ID of the view
 * @param   int     ID of the item
 * @param   int     ID of the next item
 * @uses    lp_ajax_switch_toc.php
 */
function xajax_switch_item_toc(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, next_item) {
    var params = {
        'lid': lms_lp_id,
        'uid': lms_user_id,
        'vid': lms_view_id,
        'iid': lms_item_id,
        'next': next_item
    };
    logit_lms('xajax_switch_item_toc');

    $.ajax({
        type: "POST",
        data: params,
        url: "lp_ajax_switch_item_toc.php",
        dataType: "script",
        async: false
    });
}


/**
 * Allow attach the glossary terms into html document of scorm. This has
 * nothing to do with SCORM itself, and should not interfere w/ SCORM either.
  * @param   string     automatic or manual values are allowed
*/
function attach_glossary_into_scorm(type) {
    var f = $('#content_id')[0];
    //Prevents "f is undefined" javascript error
    if (f == null) {
        logit_lms('attach_glossary_into_scorm failed', 0);
        return false;
    }
    //logit_lms('attach_glossary_into_scorm', 0);

    try {
        var doc = f.contentWindow ? f.contentWindow.document : f.contentDocument ? f.contentDocument : f.document;
    } catch (ex) { }

    var $frame_content = $('body',doc);
    var my_text=$frame_content.html();

    my_protocol = location.protocol;
    my_pathname=location.pathname;
    work_path = my_pathname.substr(0,my_pathname.indexOf('/courses/'));

    if (type == 'automatic') {
        $.ajax({
            contentType: "application/x-www-form-urlencoded",
            beforeSend: function(object) {
            },
            type: "POST",
            url: my_protocol+"//"+location.host+work_path+"/main/glossary/glossary_ajax_request.php",
            data: "glossary_data=true",
            success: function(datas) {
                if (datas.length==0) {
                    return false;
                }
                // glossary terms
                data_terms=datas.split("[|.|_|.|-|.|]");
                var complex_array = new Array();
                var cp_complex_array = new Array();
                for(i=0;i<data_terms.length;i++) {
                    specific_terms= data_terms[i].split("__|__|");
                    var real_term = specific_terms[1]; // glossary term
                    var real_code = specific_terms[0]; // glossary id
                    complex_array[real_code] = real_term;
                    cp_complex_array[real_code] = real_term;
                }

                complex_array.reverse();

                for (var my_index in complex_array) {
                    n = complex_array[my_index];
                    if (n == null) {
                        n = '';
                    } else {
                        for (var cp_my_index in cp_complex_array) {
                            cp_data = cp_complex_array[cp_my_index];
                            if (cp_data == null) {
                                cp_data = '';
                            } else {
                                if (cp_data == n) {
                                    my_index = cp_my_index;
                                }
                            }
                        }
                        //alert(n + ' ' + my_index);
                        $("iframe").contents().find('body').removeHighlight().highlight(n,my_index)
                        //logit_lms(n+ ' - '+my_index, 0);
                    }
                }

                var complex_array = new Array();

                //$("iframe").contents().find("body .glossary-ajax").on("click", ".glossary-ajax", function() {
                $("iframe").contents().find("body").on("click", ".glossary-ajax", function() {

                div_show_id="div_show_id";
                div_content_id="div_content_id";

                $("iframe").contents().find("body").append('<div id="div_show_id"><div id="div_content_id">&nbsp;</div></div>');

                    show_dialog = $("iframe").contents().find("div#"+div_show_id);
                    show_description = $("iframe").contents().find("div#"+div_content_id);

                    var $target = $(this);

                    if ($("#learning_path_left_zone").is(':visible') ) {
                        var extra_left = $("#learning_path_left_zone").width() + 20;
                    } else {
                        var extra_left = 0;
                    }

                    //$("#"+div_show_id).dialog("destroy");
                    show_dialog.dialog({
                        autoOpen: false,
                        width: 600,
                        height: 200,
                        position:  { my: 'left top', at: 'right top', of: $target, offset: extra_left+", 0"},
                        close: function(){
                             show_dialog.remove();
                             show_description.remove();
                        }
                    });
                    notebook_id=$(this).attr("name");
                    data_notebook=notebook_id.split("link");

                    my_glossary_id=data_notebook[1];
                    $.ajax({
                        contentType: "application/x-www-form-urlencoded",
                        type: "POST",
                        url: "<?php echo api_get_path(WEB_PATH); ?>main/glossary/glossary_ajax_request.php",
                        data: "glossary_id="+my_glossary_id,
                        success: function(data) {
                            show_description.html(data);
                            show_dialog.dialog("open");
                        }
                    });
                });
            }
        });
    } else {
        if ('manual') {
            $("iframe").contents().find("body").on("click", ".glossary", function() {
                is_glossary_name = $(this).html();

                div_show_id="div_show_id";
                div_content_id="div_content_id";

                $("iframe").contents().find("body").append('<div id="div_show_id"><div id="div_content_id">&nbsp;</div></div>');

                show_dialog = $("iframe").contents().find("div#"+div_show_id);
                show_description = $("iframe").contents().find("div#"+div_content_id);

                var $target = $(this);

                if ($("#learning_path_left_zone").is(':visible') ) {
                    var extra_left = $("#learning_path_left_zone").width() + 20;
                } else {
                    var extra_left = 0;
                }

                //$("#"+div_show_id).dialog("destroy");
                show_dialog.dialog({
                    autoOpen: false,
                    width: 600,
                    height: 200,
                    position:  { my: 'left top', at: 'right top', of: $target, offset: extra_left+", 0"},
                    close: function(){
                         show_dialog.remove();
                         show_description.remove();
                    }
                });

				//$("iframe").contents().find("div#"+div_show_id).attr("style","display:inline;float:left;position:absolute;background-color:#F2F2F2;border-bottom: 1px solid #2E2E2E;border-right: 1px solid #2E2E2E;border-left: 1px solid #2E2E2E;border-top: 1px solid #2E2E2E;color:#305582;margin-left:5px;margin-right:5px;");
				//$("iframe").contents().find("div#"+div_content_id).attr("style","background-color:#F2F2F2;color:#0B3861;margin-left:8px;margin-right:8px;margin-top:5px;margin-bottom:5px;");

                $.ajax({
                    contentType: "application/x-www-form-urlencoded",
                    type: "POST",
                    url: "<?php echo api_get_path(WEB_PATH); ?>main/glossary/glossary_ajax_request.php",
                    data: "glossary_name="+is_glossary_name,
                    success: function(data) {
                         show_description.html(data);
                         show_dialog.dialog("open");
                    }
                });
            });
        }
    }
}scorm_manage.php000064400000000241152003363470007710 0ustar00<?php
/* For licensing terms, see /license.txt */
/**
 * Admin script for SCORM packages
 * @package	chamilo.learnpath.scorm
 */
/**
 * Here comes the script
 */storageapi.php000064400000022612152003363470007421 0ustar00<?php
// Storage API
// PHP Backend
// CBlue SPRL, Jean-Karim Bockstael, <jeankarim@cblue.be>

require_once('../inc/global.inc.php');

// variable cleaning...
foreach (Array("svkey", "svvalue") as $key)
	$_REQUEST[$key] = Database::escape_string($_REQUEST[$key]);

foreach (Array("svuser", "svcourse", "svsco", "svlength", "svasc") as $key)
	$_REQUEST[$key] = intval($_REQUEST[$key]);

switch ($_REQUEST['action']) {
	case "get":
		print storage_get($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey']);
		break;
	case "set":
		if (storage_can_set($_REQUEST['svuser'])) {
			print storage_set($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey'], $_REQUEST['svvalue']);
		}
		break;
	case "getall":
		print storage_getall($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco']);
		break;
	case "stackpush":
		if (storage_can_set($_REQUEST['svuser'])) {
			print storage_stack_push($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey'], $_REQUEST['svvalue']);
		}
		break;
	case "stackpop":
		if (storage_can_set($_REQUEST['svuser'])) {
			print storage_stack_pop($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey']);
		}
		break;
	case "stacklength":
		print storage_stack_length($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey']);
		break;
	case "stackclear":
		if (storage_can_set($_REQUEST['svuser'])) {
			print storage_stack_clear($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey']);
		}
		break;
	case "stackgetall":
		if (storage_can_set($_REQUEST['svuser'])) 
			print storage_stack_getall($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey']);
		break;
	case "getposition":
		print storage_get_position($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey'], $_REQUEST['svasc']);
		break;
	case "getleaders":
		print storage_get_leaders($_REQUEST['svuser'], $_REQUEST['svcourse'], $_REQUEST['svsco'], $_REQUEST['svkey'], $_REQUEST['svasc'], $_REQUEST['svlength']);
		break;
	case "usersgetall":
// security issue
		print "NOT allowed, security issue, see sources";
//		print storage_get_all_users();
		break;
	default:
		// Do nothing
}

function storage_can_set($sv_user) {
	// platform admin can change any user's stored values, other users can only change their own values
	$allowed = ((api_is_platform_admin()) || ($sv_user == api_get_user_id()));
	if (!$allowed) {
		print "ERROR : Not allowed";
	}
	return $allowed;
}

function storage_get($sv_user, $sv_course, $sv_sco, $sv_key) {
	$sql = "select sv_value
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)."
		where user_id= '$sv_user'
		and sco_id = '$sv_sco'
		and course_id = '$sv_course'
		and sv_key = '$sv_key'";
	$res = Database::query($sql);
	if (Database::num_rows($res) > 0) {
		$row = Database::fetch_assoc($res);
		if (get_magic_quotes_gpc()) {
			return stripslashes($row['sv_value']);
		}
		else {
			return $row['sv_value'];
		}
	}
	else {
		return null;
	}
}
			
function storage_get_leaders($sv_user, $sv_course, $sv_sco, $sv_key, $sv_asc, $sv_length) {

	// get leaders
	$sql_leaders = "select u.user_id, firstname, lastname, email, username, sv_value as value
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)." sv,
			".Database::get_main_table(TABLE_MAIN_USER)." u
		where u.user_id=sv.user_id
		and sco_id = '$sv_sco'
		and course_id = '$sv_course'
		and sv_key = '$sv_key'
		order by sv_value ".($sv_asc ? "ASC": "DESC")." limit $sv_length";
//	$sql_data = "select sv.user_id as user_id, sv_key as variable, sv_value as value
//		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)." sv
//		where sv.user_id in (select u2.user_id from ($sql_leaders) u2)
//		and sco_id = '$sv_sco'
//		and course_id = '$sv_course'";
//	$resData = Database::query($sql_data);
//	$data = Array();
//	while($row = Database::fetch_assoc($resData))
//		$data[] = $row; // fetching all data
//
	$resLeaders = Database::query($sql_leaders);
	$result = array();
	while ($row = Database::fetch_assoc($resLeaders)) {
		$row["values"] = array();
//		foreach($data as $dataRow) {
//			if ($dataRow["user_id"] = $row["user_id"])
//				$row["values"][$dataRow["variable"]] = $dataRow["value"];
//		}
		$result[] = $row;
	} 
	return json_encode($result);
}

function storage_get_position($sv_user, $sv_course, $sv_sco, $sv_key, $sv_asc, $sv_length) {
	$sql = "select count(list.user_id) as position 
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)." search,
			".Database::get_main_table(TABLE_TRACK_STORED_VALUES)." list
		where search.user_id= '$sv_user'
		and search.sco_id = '$sv_sco'
		and search.course_id = '$sv_course'
		and search.sv_key = '$sv_key'
		and list.sv_value ".($sv_asc ? "<=": ">=")." search.sv_value
		and list.sco_id = search.sco_id
		and list.course_id = search.course_id
		and list.sv_key = search.sv_key
		order by list.sv_value" ;
	$res = Database::query($sql);
	if (Database::num_rows($res) > 0) {
		$row = Database::fetch_assoc($res);
		return $row['position'];
	}
	else {
		return null;
	}
}

function storage_set($sv_user, $sv_course, $sv_sco, $sv_key, $sv_value) {
	$sv_value = Database::escape_string($sv_value);
	$sql = "replace into ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)."
		(user_id, sco_id, course_id, sv_key, sv_value)
		values
		('$sv_user','$sv_sco','$sv_course','$sv_key','$sv_value')";
	$res = Database::query($sql);
	return Database::affected_rows();
}

function storage_getall($sv_user, $sv_course, $sv_sco) {
	$sql = "select sv_key, sv_value
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES)."
		where user_id= '$sv_user'
		and sco_id = '$sv_sco'
		and course_id = '$sv_course'";
	$res = Database::query($sql);
	$data = array();
	while ($row = Database::fetch_assoc($res)) {
		if (get_magic_quotes_gpc()) {
			$row['sv_value'] = stripslashes($row['sv_value']);
		}
		$data[] = $row;
	}
	return json_encode($data);
}

function storage_stack_push($sv_user, $sv_course, $sv_sco, $sv_key, $sv_value) {
	$sv_value = Database::escape_string($sv_value);
	Database::query("start transaction");
	$sqlorder = "select ifnull((select max(stack_order) 
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'
		), 0) as stack_order";
	$resorder = Database::query($sqlorder);
	$row = Database::fetch_assoc($resorder);
	$stack_order = (1 + $row['stack_order']);
	$sqlinsert = "insert into ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		(user_id, sco_id, course_id, sv_key, stack_order, sv_value)
		values
		('$sv_user', '$sv_sco', '$sv_course', '$sv_key', '$stack_order', '$sv_value')";
	$resinsert = Database::query($sqlinsert);
	if ($resorder && $resinsert) {
		Database::query("commit");
		return 1;
	}
	else {
		Database::query("rollback");
		return 0;
	}
}

function storage_stack_pop($sv_user, $sv_course, $sv_sco, $sv_key) {
	Database::query("start transaction");
	$sqlselect = "select sv_value, stack_order
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'
		order by stack_order desc
		limit 1";
	$resselect = Database::query($sqlselect);
	$rowselect = Database::fetch_assoc($resselect);
	$stack_order = $rowselect['stack_order'];
	$sqldelete = "delete
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'
		and stack_order='$stack_order'";
	$resdelete = Database::query($sqldelete);
	if ($resselect && $resdelete) {
		Database::query("commit");
		if (get_magic_quotes_gpc()) {
			return stripslashes($rowselect['sv_value']);
		}
		else {
			return $rowselect['sv_value'];
		}
	}
	else {
		Database::query("rollback");
		return null;
	}
}

function storage_stack_length($sv_user, $sv_course, $sv_sco, $sv_key) {
	$sql = "select count(*) as length
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'";
	$res = Database::query($sql);
	$row = Database::fetch_assoc($res);
	return $row['length'];
}

function storage_stack_clear($sv_user, $sv_course, $sv_sco, $sv_key) {
	$sql = "delete
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'";
	$res = Database::query($sql);
	return Database::num_rows($res);
}

function storage_stack_getall($sv_user, $sv_course, $sv_sco, $sv_key) {
	$sql = "select stack_order as stack_order, sv_value as value
		from ".Database::get_main_table(TABLE_TRACK_STORED_VALUES_STACK)."
		where user_id= '$sv_user'
		and sco_id='$sv_sco'
		and course_id='$sv_course'
		and sv_key='$sv_key'";
	$res = Database::query($sql);
	$results = array();
	while ($row = Database::fetch_assoc($res)) {
		if (get_magic_quotes_gpc()) {
			$row['value'] = stripslashes($row['value']);
		}
		$results[] = $row;
	}
	return json_encode($results);
}

function storage_get_all_users() {
	$sql = "select user_id, username, firstname, lastname
		from ".Database::get_main_table(TABLE_MAIN_USER)."
		order by user_id asc";
	$res = Database::query($sql);
	$results = array();
	while ($row = Database::fetch_assoc($res)) {
		$results[] = $row;
	}
	return json_encode($results);
}
?>
packaging/adlcp_rootv1p2.xsd000064400000010632152003363470012054 0ustar00<?xml version="1.0"?>
<!-- filename=adlcp_rootv1p2.xsd -->
<!-- Conforms to w3c http://www.w3.org/TR/xmlschema-1/ 2000-10-24-->

<xsd:schema xmlns="http://www.adlnet.org/xsd/adlcp_rootv1p2"
            targetNamespace="http://www.adlnet.org/xsd/adlcp_rootv1p2"
            xmlns:xml="http://www.w3.org/XML/1998/namespace"
            xmlns:imscp="http://www.imsproject.org/xsd/imscp_rootv1p1p2"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="unqualified"
            version="ADL Version 1.2">

        <xsd:import namespace="http://www.imsproject.org/xsd/imscp_rootv1p1p2"
         schemaLocation="imscp_rootv1p1p2.xsd"/>

        <xsd:element name="location" type="locationType"/>
        <xsd:element name="prerequisites" type="prerequisitesType"/>
        <xsd:element name="maxtimeallowed" type="maxtimeallowedType"/>
        <xsd:element name="timelimitaction" type="timelimitactionType"/>
        <xsd:element name="datafromlms" type="datafromlmsType"/>
        <xsd:element name="masteryscore" type="masteryscoreType"/>


        <xsd:element name="schema" type="newSchemaType"/>
        <xsd:simpleType name="newSchemaType">
                <xsd:restriction base="imscp:schemaType">
                        <xsd:enumeration value="ADL SCORM"/>
                </xsd:restriction>
        </xsd:simpleType>

        <xsd:element name="schemaversion" type="newSchemaversionType"/>
        <xsd:simpleType name="newSchemaversionType">
                <xsd:restriction base="imscp:schemaversionType">
                        <xsd:enumeration value="1.2"/>
                </xsd:restriction>
        </xsd:simpleType>


        <xsd:attribute name="scormtype">
            <xsd:simpleType>
                <xsd:restriction base="xsd:string">
                   <xsd:enumeration value="asset"/>
                   <xsd:enumeration value="sco"/>
                </xsd:restriction>
            </xsd:simpleType>
        </xsd:attribute>

        <xsd:simpleType name="locationType">
                <xsd:restriction base="xsd:string">
                        <xsd:maxLength value="2000"/>
                </xsd:restriction>
        </xsd:simpleType>


        <xsd:complexType name="prerequisitesType">
           <xsd:simpleContent>
              <xsd:extension base="prerequisiteStringType">
                  <xsd:attributeGroup ref="attr.prerequisitetype"/>
              </xsd:extension>
           </xsd:simpleContent>
        </xsd:complexType>

        <xsd:attributeGroup name="attr.prerequisitetype">
                <xsd:attribute name="type" use="required">
                        <xsd:simpleType>
                                <xsd:restriction base="xsd:string">
                                   <xsd:enumeration value="aicc_script"/>
                                </xsd:restriction>
                        </xsd:simpleType>
                </xsd:attribute>
        </xsd:attributeGroup>

        <xsd:simpleType name="maxtimeallowedType">
                <xsd:restriction base="xsd:string">
                        <xsd:maxLength value="13"/>
                </xsd:restriction>
        </xsd:simpleType>

        <xsd:simpleType name="timelimitactionType">
                <xsd:restriction base="stringType">
                        <xsd:enumeration value="exit,no message"/>
                        <xsd:enumeration value="exit,message"/>
                        <xsd:enumeration value="continue,no message"/>
                        <xsd:enumeration value="continue,message"/>
                </xsd:restriction>
        </xsd:simpleType>

        <xsd:simpleType name="datafromlmsType">
                <xsd:restriction base="xsd:string">
                        <xsd:maxLength value="255"/>
                </xsd:restriction>
        </xsd:simpleType>

        <xsd:simpleType name="masteryscoreType">
                <xsd:restriction base="xsd:string">
                        <xsd:maxLength value="200"/>
                </xsd:restriction>
        </xsd:simpleType>

        <xsd:simpleType name="stringType">
                <xsd:restriction base="xsd:string"/>
        </xsd:simpleType>
        
        <xsd:simpleType name="prerequisiteStringType">
                <xsd:restriction base="xsd:string">
                   <xsd:maxLength value="200"/>
                </xsd:restriction>
        </xsd:simpleType>

</xsd:schema>packaging/imscp_rootv1p1p2.xsd000064400000035071152003363470012351 0ustar00<?xml version="1.0"?>

<!-- edited with XML Spy v3.5 (http://www.xmlspy.com) by Thomas Wason (private) -->
<!-- filename=ims_cp_rootv1p1p2.xsd -->
<!-- Copyright (2) 2001 IMS Global Learning Consortium, Inc. -->
<!-- edited by Thomas Wason  -->
<!-- Conforms to w3c http://www.w3.org/TR/xmlschema-1/ 2000-10-24-->

<xsd:schema xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" 
            targetNamespace="http://www.imsproject.org/xsd/imscp_rootv1p1p2" 
            xmlns:xml="http://www.w3.org/XML/1998/namespace" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            elementFormDefault="unqualified" version="IMS CP 1.1.2">

   <!-- ******************** -->
   <!-- ** Change History ** -->
   <!-- ******************** -->
   <xsd:annotation>
      <xsd:documentation xml:lang="en">DRAFT XSD for IMS Content Packaging version 1.1 DRAFT</xsd:documentation>
      <xsd:documentation> Copyright (c) 2001 IMS GLC, Inc. </xsd:documentation>
      <xsd:documentation>2000-04-21, Adjustments by T.D. Wason from CP 1.0.</xsd:documentation>
      <xsd:documentation>2001-02-22, T.D.Wason: Modify for 2000-10-24 XML-Schema version.  Modified to support extension.</xsd:documentation>
      <xsd:documentation>2001-03-12, T.D.Wason: Change filename, target and meta-data namespaces and meta-data fielname.  Add meta-data to itemType, fileType and organizationType.</xsd:documentation>
      <xsd:documentation>Do not define namespaces for xml in XML instances generated from this xsd.</xsd:documentation>
      <xsd:documentation>Imports IMS meta-data xsd, lower case element names.         </xsd:documentation>
      <xsd:documentation>This XSD provides a reference to the IMS meta-data root element as imsmd:record</xsd:documentation>
      <xsd:documentation>If the IMS meta-data is to be used in the XML instance then the instance must define an IMS meta-data prefix with a namespace.  The meta-data targetNamespace should be used.  </xsd:documentation>
      <xsd:documentation>2001-03-20, Thor Anderson: Remove manifestref, change resourceref back to identifierref, change manifest back to contained by manifest. --Tom Wason: manifest may contain _none_ or more manifests.</xsd:documentation>
      <xsd:documentation>2001-04-13 Tom Wason: corrected attirbute name structure.  Was misnamed type.  </xsd:documentation>
      <xsd:documentation>2001-05-14 Schawn Thropp: Made all complexType extensible with the group.any</xsd:documentation>
      <xsd:documentation>Added the anyAttribute to all complexTypes. Changed the href attribute on the fileType and resourceType to xsd:string</xsd:documentation>
      <xsd:documentation>Changed the maxLength of the href, identifierref, parameters, structure attributes to match the Information model.</xsd:documentation>
      <xsd:documentation>2001-07-25 Schawn Thropp: Changed the namespace for the Schema of Schemas to the 5/2/2001 W3C XML Schema</xsd:documentation> 
      <xsd:documentation>Recommendation. attributeGroup attr.imsmd deleted, was not used anywhere.  Any attribute declarations that have</xsd:documentation>
      <xsd:documentation>use = "default" changed to use="optional" - attr.structure.req.</xsd:documentation>
      <xsd:documentation>Any attribute declarations that have value="somevalue" changed to default="somevalue",</xsd:documentation>
      <xsd:documentation>attr.structure.req (hierarchical).  Removed references to IMS MD Version 1.1.</xsd:documentation>
      <xsd:documentation>Modified attribute group "attr.resourcetype.req" to change use from optional</xsd:documentation>
      <xsd:documentation>to required to match the information model.  As a result the default value also needed to be removed</xsd:documentation> 
      <xsd:documentation>Name change for XSD.  Changed to match version of CP Spec                                           </xsd:documentation> 
   </xsd:annotation>

   <xsd:annotation>
      <xsd:documentation>Inclusions and Imports</xsd:documentation>
   </xsd:annotation>

   <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="ims_xml.xsd"/>

   <xsd:annotation>
      <xsd:documentation>Attribute Declarations</xsd:documentation>
   </xsd:annotation>

   <!-- **************************** -->
   <!-- ** Attribute Declarations ** -->
   <!-- **************************** -->
   <xsd:attributeGroup name="attr.base">
      <xsd:attribute ref="xml:base" use="optional"/>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.default">
      <xsd:attribute name="default" type="xsd:IDREF" use="optional"/>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.href">
      <xsd:attribute name="href" use="optional">
         <xsd:simpleType>
            <xsd:restriction base="xsd:anyURI">
               <xsd:maxLength value="2000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.href.req">
      <xsd:attribute name="href" use="required">
         <xsd:simpleType>
            <xsd:restriction base="xsd:anyURI">
               <xsd:maxLength value="2000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup> 

   <xsd:attributeGroup name="attr.identifier.req">
      <xsd:attribute name="identifier" type="xsd:ID" use="required"/>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.identifier">
      <xsd:attribute name="identifier" type="xsd:ID" use="optional"/>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.isvisible">
      <xsd:attribute name="isvisible" type="xsd:boolean" use="optional"/>
   </xsd:attributeGroup>
   
   <xsd:attributeGroup name="attr.parameters">
      <xsd:attribute name="parameters" use="optional">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="1000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>
   
   <xsd:attributeGroup name="attr.identifierref">
      <xsd:attribute name="identifierref" use="optional">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="2000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>
   
   <xsd:attributeGroup name="attr.identifierref.req">
      <xsd:attribute name="identifierref" use="required">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="2000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>
                
   <xsd:attributeGroup name="attr.resourcetype.req">
      <xsd:attribute name="type" use="required">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="1000"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.structure.req">
      <xsd:attribute name="structure" use="optional" default="hierarchical">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="200"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>

   <xsd:attributeGroup name="attr.version">
      <xsd:attribute name="version" use="optional">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:maxLength value="20"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>

   <xsd:annotation>
       <xsd:documentation>element groups</xsd:documentation>
   </xsd:annotation>

   <xsd:group name="grp.any">
      <xsd:annotation>
         <xsd:documentation>Any namespaced element from any namespace may be included within an &quot;any&quot; element.  The namespace for the imported element must be defined in the instance, and the schema must be imported.  </xsd:documentation>
      </xsd:annotation>
      <xsd:sequence>
         <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:group>

   <!-- ************************** -->
   <!-- ** Element Declarations ** -->
   <!-- ************************** -->

   <xsd:element name="dependency" type="dependencyType"/>
   <xsd:element name="file" type="fileType"/>
   <xsd:element name="item" type="itemType"/>
   <xsd:element name="manifest" type="manifestType"/>
   <xsd:element name="metadata" type="metadataType"/>
   <xsd:element name="organization" type="organizationType"/>
   <xsd:element name="organizations" type="organizationsType"/>
   <xsd:element name="resource" type="resourceType"/>
   <xsd:element name="resources" type="resourcesType"/>
   <xsd:element name="schema" type="schemaType"/>
   <xsd:element name="schemaversion" type="schemaversionType"/>
   <xsd:element name="title" type="titleType"/>

   <!-- ******************* -->
   <!-- ** Complex Types ** -->
   <!-- ******************* -->

   <!-- **************** -->
   <!-- ** dependency ** -->
   <!-- **************** -->
   <xsd:complexType name="dependencyType">
      <xsd:sequence>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.identifierref.req"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ********** -->
   <!-- ** file ** -->
   <!-- ********** -->
   <xsd:complexType name="fileType">
      <xsd:sequence>
         <xsd:element ref="metadata" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.href.req"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ********** -->
   <!-- ** item ** -->
   <!-- ********** -->
   <xsd:complexType name="itemType">
      <xsd:sequence>
         <xsd:element ref="title" minOccurs="0"/>
         <xsd:element ref="item" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="metadata" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.identifier.req"/>
      <xsd:attributeGroup ref="attr.identifierref"/>
      <xsd:attributeGroup ref="attr.isvisible"/>
      <xsd:attributeGroup ref="attr.parameters"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ************** -->
   <!-- ** manifest ** -->
   <!-- ************** -->
   <xsd:complexType name="manifestType">
      <xsd:sequence>
         <xsd:element ref="metadata" minOccurs="0"/>
         <xsd:element ref="organizations"/>
         <xsd:element ref="resources"/>
         <xsd:element ref="manifest" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.identifier.req"/>
      <xsd:attributeGroup ref="attr.version"/>
      <xsd:attribute ref="xml:base"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ************** -->
   <!-- ** metadata ** -->
   <!-- ************** -->
   <xsd:complexType name="metadataType">
      <xsd:sequence>
         <xsd:element ref="schema" minOccurs="0"/>
         <xsd:element ref="schemaversion" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <!-- ******************* -->
   <!-- ** organizations ** -->
   <!-- ******************* -->
   <xsd:complexType name="organizationsType">
      <xsd:sequence>
         <xsd:element ref="organization" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.default"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ****************** -->
   <!-- ** organization ** -->
   <!-- ****************** -->
   <xsd:complexType name="organizationType">
      <xsd:sequence>
         <xsd:element ref="title" minOccurs="0"/>
         <xsd:element ref="item" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="metadata" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.identifier.req"/>
      <xsd:attributeGroup ref="attr.structure.req"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- *************** -->
   <!-- ** resources ** -->
   <!-- *************** -->
   <xsd:complexType name="resourcesType">
      <xsd:sequence>
          <xsd:element ref="resource" minOccurs="0" maxOccurs="unbounded"/>
          <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.base"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>
   
   <!-- ************** -->
   <!-- ** resource ** -->
   <!-- ************** -->
   <xsd:complexType name="resourceType">
      <xsd:sequence>
         <xsd:element ref="metadata" minOccurs="0"/>
         <xsd:element ref="file" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="dependency" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
      <xsd:attributeGroup ref="attr.identifier.req"/>
      <xsd:attributeGroup ref="attr.resourcetype.req"/>
      <xsd:attributeGroup ref="attr.base"/>
      <xsd:attributeGroup ref="attr.href"/>
      <xsd:anyAttribute namespace="##other" processContents="strict"/>
   </xsd:complexType>

   <!-- ****************** -->
   <!-- ** Simple Types ** -->
   <!-- ****************** -->

   <!-- ************ -->
   <!-- ** schema ** -->
   <!-- ************ -->
   <xsd:simpleType name="schemaType">
      <xsd:restriction base="xsd:string">
         <xsd:maxLength value="100"/>
      </xsd:restriction>
   </xsd:simpleType>
   
   <!-- ******************* -->
   <!-- ** schemaversion ** -->
   <!-- ******************* -->
   <xsd:simpleType name="schemaversionType">
      <xsd:restriction base="xsd:string">
         <xsd:maxLength value="20"/>
      </xsd:restriction>
   </xsd:simpleType>
   
   <!-- *********** -->
   <!-- ** title ** -->
   <!-- *********** -->
   <xsd:simpleType name="titleType">
      <xsd:restriction base="xsd:string">
         <xsd:maxLength value="200"/>
      </xsd:restriction>
   </xsd:simpleType>

</xsd:schema>
packaging/imsmd_rootv1p2p1.xsd000064400000054361152003363470012352 0ustar00<?xml version="1.0" encoding="UTF-8"?>
<!-- edited by Thomas Wason  -->
<xsd:schema targetNamespace="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1" 
            xmlns:xml="http://www.w3.org/XML/1998/namespace" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xmlns="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1" 
            elementFormDefault="qualified" 
            version="1.2:1.1 IMS:MD1.2">

   <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="ims_xml.xsd"/> 

   <!-- ******************** -->
   <!-- ** Change History ** -->
   <!-- ******************** -->
   <xsd:annotation>
      <xsd:documentation>2001-04-26 T.D.Wason. IMS meta-data 1.2 XML-Schema.                                  </xsd:documentation>
      <xsd:documentation>2001-06-07 S.E.Thropp. Changed the multiplicity on all elements to match the         </xsd:documentation>
      <xsd:documentation>Final 1.2 Binding Specification.                                                     </xsd:documentation>
      <xsd:documentation>Changed all elements that use the langstringType to a multiplicy of 1 or more        </xsd:documentation>
      <xsd:documentation>Changed centity in the contribute element to have a multiplicity of 0 or more.       </xsd:documentation>
      <xsd:documentation>Changed the requirement element to have a multiplicity of 0 or more.                 </xsd:documentation>
      <xsd:documentation> 2001-07-25 Schawn Thropp.  Updates to bring the XSD up to speed with the W3C        </xsd:documentation>
      <xsd:documentation> XML Schema Recommendation.  The following changes were made: Change the             </xsd:documentation>
      <xsd:documentation> namespace to reference the 5/2/2001 W3C XML Schema Recommendation,the base          </xsd:documentation>
      <xsd:documentation> type for the durtimeType, simpleType, was changed from timeDuration to duration.    </xsd:documentation>              
      <xsd:documentation> Any attribute declarations that have use="default" had to change to use="optional"  </xsd:documentation>
      <xsd:documentation> - attr.type.  Any attribute declarations that have value ="somevalue" had to change </xsd:documentation>
      <xsd:documentation> to default = "somevalue" - attr.type (URI)                                          </xsd:documentation>
      <xsd:documentation> 2001-09-04 Schawn Thropp                                                            </xsd:documentation>
      <xsd:documentation> Changed the targetNamespace and namespace of schema to reflect version change       </xsd:documentation>
   </xsd:annotation>

   <!-- *************************** -->
   <!-- ** Attribute Declaration ** -->
   <!-- *************************** -->

   <xsd:attributeGroup name="attr.type">
      <xsd:attribute name="type" use="optional" default="URI">
         <xsd:simpleType>
            <xsd:restriction base="xsd:string">
               <xsd:enumeration value="URI"/>
               <xsd:enumeration value="TEXT"/>
            </xsd:restriction>
         </xsd:simpleType>
      </xsd:attribute>
   </xsd:attributeGroup>

   <xsd:group name="grp.any">
      <xsd:annotation>
         <xsd:documentation>Any namespaced element from any namespace may be used for an &quot;any&quot; element.  The namespace for the imported element must be defined in the instance, and the schema must be imported.  </xsd:documentation>
      </xsd:annotation>
      <xsd:sequence>
         <xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:group>

   <!-- ************************* -->
   <!-- ** Element Declaration ** -->
   <!-- ************************* -->

   <xsd:element name="aggregationlevel" type="aggregationlevelType"/>
   <xsd:element name="annotation" type="annotationType"/>
   <xsd:element name="catalogentry" type="catalogentryType"/>
   <xsd:element name="catalog" type="catalogType"/>
   <xsd:element name="centity" type="centityType"/>
   <xsd:element name="classification" type="classificationType"/>
   <xsd:element name="context" type="contextType"/>
   <xsd:element name="contribute" type="contributeType"/>
   <xsd:element name="copyrightandotherrestrictions" type="copyrightandotherrestrictionsType"/>
   <xsd:element name="cost" type="costType"/>
   <xsd:element name="coverage" type="coverageType"/>
   <xsd:element name="date" type="dateType"/>
   <xsd:element name="datetime" type="datetimeType"/>
   <xsd:element name="description" type="descriptionType"/>
   <xsd:element name="difficulty" type="difficultyType"/>
   <xsd:element name="educational" type="educationalType"/>
   <xsd:element name="entry" type="entryType"/>
   <xsd:element name="format" type="formatType"/>
   <xsd:element name="general" type="generalType"/>
   <xsd:element name="identifier" type="xsd:string"/>
   <xsd:element name="intendedenduserrole" type="intendedenduserroleType"/>
   <xsd:element name="interactivitylevel" type="interactivitylevelType"/>
   <xsd:element name="interactivitytype" type="interactivitytypeType"/>
   <xsd:element name="keyword" type="keywordType"/>
   <xsd:element name="kind" type="kindType"/>
   <xsd:element name="langstring" type="langstringType"/>
   <xsd:element name="language" type="xsd:string"/>
   <xsd:element name="learningresourcetype" type="learningresourcetypeType"/>
   <xsd:element name="lifecycle" type="lifecycleType"/>
   <xsd:element name="location" type="locationType"/>
   <xsd:element name="lom" type="lomType"/>
   <xsd:element name="maximumversion" type="minimumversionType"/>
   <xsd:element name="metadatascheme" type="metadataschemeType"/>
   <xsd:element name="metametadata" type="metametadataType"/>
   <xsd:element name="minimumversion" type="maximumversionType"/>
   <xsd:element name="name" type="nameType"/>
   <xsd:element name="purpose" type="purposeType"/>
   <xsd:element name="relation" type="relationType"/>
   <xsd:element name="requirement" type="requirementType"/>
   <xsd:element name="resource" type="resourceType"/>
   <xsd:element name="rights" type="rightsType"/>
   <xsd:element name="role" type="roleType"/>
   <xsd:element name="semanticdensity" type="semanticdensityType"/>
   <xsd:element name="size" type="sizeType"/>
   <xsd:element name="source" type="sourceType"/>
   <xsd:element name="status" type="statusType"/>
   <xsd:element name="structure" type="structureType"/>
   <xsd:element name="taxon" type="taxonType"/>
   <xsd:element name="taxonpath" type="taxonpathType"/>
   <xsd:element name="technical" type="technicalType"/>
   <xsd:element name="title" type="titleType"/>
   <xsd:element name="type" type="typeType"/>
   <xsd:element name="typicalagerange" type="typicalagerangeType"/>
   <xsd:element name="typicallearningtime" type="typicallearningtimeType"/>
   <xsd:element name="value" type="valueType"/>
   <xsd:element name="person" type="personType"/>
   <xsd:element name="vcard" type="xsd:string"/>
   <xsd:element name="version" type="versionType"/>
   <xsd:element name="installationremarks" type="installationremarksType"/>
   <xsd:element name="otherplatformrequirements" type="otherplatformrequirementsType"/>
   <xsd:element name="duration" type="durationType"/>
   <xsd:element name="id" type="idType"/>

   <!-- ******************* -->
   <!-- ** Complex Types ** -->
   <!-- ******************* -->

   <xsd:complexType name="aggregationlevelType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="annotationType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="person" minOccurs="0"/>
         <xsd:element ref="date" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="catalogentryType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="catalog"/>
         <xsd:element ref="entry"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="centityType">
      <xsd:sequence>
         <xsd:element ref="vcard"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="classificationType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="purpose" minOccurs="0"/>
         <xsd:element ref="taxonpath" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="description" minOccurs="0"/>
         <xsd:element ref="keyword" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="contextType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="contributeType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="role"/>
         <xsd:element ref="centity" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="date" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="copyrightandotherrestrictionsType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="costType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="coverageType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="dateType">
      <xsd:sequence>
         <xsd:element ref="datetime" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="descriptionType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="difficultyType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="durationType">
      <xsd:sequence>
         <xsd:element ref="datetime" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="educationalType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="interactivitytype" minOccurs="0"/>
         <xsd:element ref="learningresourcetype" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="interactivitylevel" minOccurs="0"/>
         <xsd:element ref="semanticdensity" minOccurs="0"/>
         <xsd:element ref="intendedenduserrole" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="context" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="typicalagerange" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="difficulty" minOccurs="0"/>
         <xsd:element ref="typicallearningtime" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
         <xsd:element ref="language" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="entryType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="generalType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="identifier" minOccurs="0"/>
         <xsd:element ref="title" minOccurs="0"/>
         <xsd:element ref="catalogentry" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="language" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="description" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="keyword" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="coverage" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="structure" minOccurs="0"/>
         <xsd:element ref="aggregationlevel" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="installationremarksType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="intendedenduserroleType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="interactivitylevelType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="interactivitytypeType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="keywordType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="kindType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="langstringType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attribute ref="xml:lang"/>
         </xsd:extension>
      </xsd:simpleContent>
   </xsd:complexType>
   
   <xsd:complexType name="learningresourcetypeType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="lifecycleType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="version" minOccurs="0"/>
         <xsd:element ref="status" minOccurs="0"/>
         <xsd:element ref="contribute" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="locationType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attributeGroup ref="attr.type"/>
         </xsd:extension>
      </xsd:simpleContent>
   </xsd:complexType>
   
   <xsd:complexType name="lomType">
      <xsd:sequence>
         <xsd:element ref="general" minOccurs="0"/>
         <xsd:element ref="lifecycle" minOccurs="0"/>
         <xsd:element ref="metametadata" minOccurs="0"/>
         <xsd:element ref="technical" minOccurs="0"/>
         <xsd:element ref="educational" minOccurs="0"/>
         <xsd:element ref="rights" minOccurs="0"/>
         <xsd:element ref="relation" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="annotation" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="classification" minOccurs="0" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="metametadataType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="identifier" minOccurs="0"/>
         <xsd:element ref="catalogentry" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="contribute" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="metadatascheme" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="language" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="nameType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="otherplatformrequirementsType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="personType">
      <xsd:sequence>
         <xsd:element ref="vcard"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="purposeType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="relationType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="kind" minOccurs="0"/>
         <xsd:element ref="resource" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="requirementType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="type" minOccurs="0"/>
         <xsd:element ref="name" minOccurs="0"/>
         <xsd:element ref="minimumversion" minOccurs="0"/>
         <xsd:element ref="maximumversion" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="resourceType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="identifier" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
         <xsd:element ref="catalogentry" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="rightsType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="cost" minOccurs="0"/>
         <xsd:element ref="copyrightandotherrestrictions" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="roleType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="semanticdensityType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="sourceType">
      <xsd:sequence>
         <xsd:element ref="langstring"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="statusType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="stringType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attribute ref="xml:lang"/>
         </xsd:extension>
      </xsd:simpleContent>
   </xsd:complexType>
   
   <xsd:complexType name="structureType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="taxonpathType">
      <xsd:sequence>
         <xsd:element ref="source" minOccurs="0"/>
         <xsd:element ref="taxon" minOccurs="0" maxOccurs="1"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="taxonType">
      <xsd:sequence>
         <xsd:element ref="id" minOccurs="0"/>
         <xsd:element ref="entry" minOccurs="0"/>
         <xsd:element ref="taxon" minOccurs="0" maxOccurs="1"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="technicalType" mixed="true">
      <xsd:sequence>
         <xsd:element ref="format" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="size" minOccurs="0"/>
         <xsd:element ref="location" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="requirement" minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element ref="installationremarks" minOccurs="0"/>
         <xsd:element ref="otherplatformrequirements" minOccurs="0"/>
         <xsd:element ref="duration" minOccurs="0"/>
         <xsd:group ref="grp.any"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="titleType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="typeType">
      <xsd:sequence>
         <xsd:element ref="source"/>
         <xsd:element ref="value"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="typicalagerangeType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="typicallearningtimeType">
      <xsd:sequence>
         <xsd:element ref="datetime" minOccurs="0"/>
         <xsd:element ref="description" minOccurs="0"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="valueType">
      <xsd:sequence>
         <xsd:element ref="langstring"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <xsd:complexType name="versionType">
      <xsd:sequence>
         <xsd:element ref="langstring" minOccurs="1" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   
   <!-- ****************** -->
   <!-- ** Simple Types ** -->
   <!-- ****************** -->
   
   <xsd:simpleType name="formatType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="sizeType">
      <xsd:restriction base="xsd:int"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="datetimeType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="idType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="metadataschemeType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="catalogType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="minimumversionType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>
   
   <xsd:simpleType name="maximumversionType">
      <xsd:restriction base="xsd:string"/>
   </xsd:simpleType>

</xsd:schema>
packaging/ims_qtiasiv1p2.xsd000064400000252732152003363470012101 0ustar00<?xml version="1.0" encoding="UTF-8"?>
<!-- filename=ims_qtiasiv1p2.xsd -->
<!-- Copyright (2) 2002 IMS Global Learning Consortium, Inc. -->
<xs:schema targetNamespace="http://www.imsglobal.org/xsd/ims_qtiasiv1p2"
	 xmlns:x="http://www.w3.org/XML/1998/namespace" 
	 xmlns:xs="http://www.w3.org/2001/XMLSchema" 
	 xmlns="http://www.imsglobal.org/xsd/ims_qtiasiv1p2" 
	 elementFormDefault="qualified" 
	 version="IMS QTI ASI 1.2">
	<!-- **************************** -->
	<!-- ** Inclusions and Imports ** -->
	<!-- **************************** -->
	<xs:import namespace="http://www.w3.org/XML/1998/namespace" 
	schemaLocation="xml.xsd"/>
	<!-- ****************** -->
	<!-- ** Root Element ** -->
	<!-- ****************** -->
	<xs:element name="questestinterop" type="questestinteropType"/>
	<!-- ************************** -->
	<!-- ** Element Declarations ** -->
	<!-- ************************** -->
	<xs:element name="altmaterial" type="altmaterialType"/>
	<xs:element name="and" type="andType"/>
	<xs:element name="and_objects" type="and_objectsType"/>
	<xs:element name="and_selection" type="and_selectionType"/>
	<xs:element name="and_test" type="and_testType"/>
	<xs:element name="assessfeedback" type="assessfeedbackType"/>
	<xs:element name="assessment" type="assessmentType"/>
	<xs:element name="assessmentcontrol" type="assessmentcontrolType"/>
	<xs:element name="assessproc_extension" type="assessproc_extensionType"/>
	<xs:element name="conditionvar" type="conditionvarType"/>
	<xs:element name="decvar" type="decvarType"/>
	<xs:element name="displayfeedback" type="displayfeedbackType"/>
	<xs:element name="duration" type="xs:string"/>
	<xs:element name="durequal" type="durequalType"/>
	<xs:element name="durgt" type="durgtType"/>
	<xs:element name="durgte" type="durgteType"/>
	<xs:element name="durlt" type="durltType"/>
	<xs:element name="durlte" type="durlteType"/>
	<xs:element name="fieldentry" type="xs:string"/>
	<xs:element name="fieldlabel" type="xs:string"/>
	<xs:element name="flow" type="flowType"/>
	<xs:element name="flow_label" type="flow_labelType"/>
	<xs:element name="flow_mat" type="flow_matType"/>
	<xs:element name="hint" type="hintType"/>
	<xs:element name="hintmaterial" type="hintmaterialType"/>
	<xs:element name="interpretvar" type="interpretvarType"/>
	<xs:element name="item" type="itemType"/>
	<xs:element name="itemcontrol" type="itemcontrolType"/>
	<xs:element name="itemfeedback" type="itemfeedbackType"/>
	<xs:element name="itemmetadata" type="itemmetadataType"/>
	<xs:element name="itempostcondition" type="xs:string"/>
	<xs:element name="itemprecondition" type="xs:string"/>
	<xs:element name="itemproc_extension" type="itemproc_extensionType"/>
	<xs:element name="itemref" type="itemrefType"/>
	<xs:element name="itemrubric" type="itemrubricType"/>
	<xs:element name="map_input" type="map_inputType"/>
	<xs:element name="map_output" type="map_outputType"/>
	<xs:element name="mat_extension" type="mat_extensionType"/>
	<xs:element name="matapplet" type="matappletType"/>
	<xs:element name="matapplication" type="matapplicationType"/>
	<xs:element name="mataudio" type="mataudioType"/>
	<xs:element name="matbreak" type="matbreakType"/>
	<xs:element name="matemtext" type="matemtextType"/>
	<xs:element name="material" type="materialType"/>
	<xs:element name="material_ref" type="material_refType"/>
	<xs:element name="matimage" type="matimageType"/>
	<xs:element name="matref" type="matrefType"/>
	<xs:element name="mattext" type="mattextType"/>
	<xs:element name="matvideo" type="matvideoType"/>
	<xs:element name="not" type="notType"/>
	<xs:element name="not_objects" type="not_objectsType"/>
	<xs:element name="not_selection" type="not_selectionType"/>
	<xs:element name="not_test" type="not_testType"/>
	<xs:element name="objectbank" type="objectbankType"/>
	<xs:element name="objectives" type="objectivesType"/>
	<xs:element name="objects_condition" type="objects_conditionType"/>
	<xs:element name="objects_parameter" type="objects_parameterType"/>
	<xs:element name="objectscond_extension" type="xs:string"/>
	<xs:element name="or" type="orType"/>
	<xs:element name="or_objects" type="or_objectsType"/>
	<xs:element name="or_selection" type="or_selectionType"/>
	<xs:element name="or_test" type="or_testType"/>
	<xs:element name="order" type="orderType"/>
	<xs:element name="order_extension" type="order_extensionType"/>
	<xs:element name="other" type="xs:string"/>
	<xs:element name="outcomes" type="outcomesType"/>
	<xs:element name="outcomes_feedback_test" type="outcomes_feedback_testType"/>
	<xs:element name="outcomes_metadata" type="outcomes_metadataType"/>
	<xs:element name="outcomes_processing" type="outcomes_processingType"/>
	<xs:element name="presentation" type="presentationType"/>
	<xs:element name="presentation_material" type="presentation_materialType"/>
	<xs:element name="processing_parameter" type="processing_parameterType"/>
	<xs:element name="qmd_computerscored" type="xs:string"/>
	<xs:element name="qmd_feedbackpermitted" type="xs:string"/>
	<xs:element name="qmd_hintspermitted" type="xs:string"/>
	<xs:element name="qmd_itemtype" type="xs:string"/>
	<xs:element name="qmd_levelofdifficulty" type="xs:string"/>
	<xs:element name="qmd_material" type="xs:string"/>
	<xs:element name="qmd_maximumscore" type="xs:string"/>
	<xs:element name="qmd_renderingtype" type="xs:string"/>
	<xs:element name="qmd_responsetype" type="xs:string"/>
	<xs:element name="qmd_scoringpermitted" type="xs:string"/>
	<xs:element name="qmd_solutionspermitted" type="xs:string"/>
	<xs:element name="qmd_status" type="xs:string"/>
	<xs:element name="qmd_timedependence" type="xs:string"/>
	<xs:element name="qmd_timelimit" type="xs:string"/>
	<xs:element name="qmd_toolvendor" type="xs:string"/>
	<xs:element name="qmd_topic" type="xs:string"/>
	<xs:element name="qmd_typeofsolution" type="xs:string"/>
	<xs:element name="qmd_weighting" type="xs:string"/>
	<xs:element name="qticomment" type="qticommentType"/>
	<xs:element name="qtimetadata" type="qtimetadataType"/>
	<xs:element name="qtimetadatafield" type="qtimetadatafieldType"/>
	<xs:element name="reference" type="referenceType"/>
	<xs:element name="render_choice" type="render_choiceType"/>
	<xs:element name="render_fib" type="render_fibType"/>
	<xs:element name="render_hotspot" type="render_hotspotType"/>
	<xs:element name="render_slider" type="render_sliderType"/>
	<xs:element name="respcond_extension" type="respcond_extensionType"/>
	<xs:element name="respcondition" type="respconditionType"/>
	<xs:element name="render_extension" type="render_extensionType"/>
	<xs:element name="response_extension" type="response_extensionType"/>
	<xs:element name="response_grp" type="response_grpType"/>
	<xs:element name="response_labelType" type="response_labelType"/>
	<xs:element name="response_lid" type="response_lidType"/>
	<xs:element name="response_na" type="response_naType"/>
	<xs:element name="response_num" type="response_numType"/>
	<xs:element name="response_str" type="response_strType"/>
	<xs:element name="response_xy" type="response_xyType"/>
	<xs:element name="resprocessing" type="resprocessingType"/>
	<xs:element name="rubric" type="rubricType"/>
	<xs:element name="section" type="sectionType"/>
	<xs:element name="sectioncontrol" type="sectioncontrolType"/>
	<xs:element name="sectionfeedback" type="sectionfeedbackType"/>
	<xs:element name="sectionproc_extension" type="sectionproc_extensionType"/>
	<xs:element name="sectionpostcondition" type="xs:string"/>
	<xs:element name="sectionprecondition" type="xs:string"/>
	<xs:element name="sectionref" type="sectionrefType"/>
	<xs:element name="selection" type="selectionType"/>
	<xs:element name="selection_extension" type="selection_extensionType"/>
	<xs:element name="selection_metadata" type="selection_metadataType"/>
	<xs:element name="selection_number" type="xs:string"/>
	<xs:element name="selection_ordering" type="selection_orderingType"/>
	<xs:element name="sequence_parameter" type="sequence_parameterType"/>
	<xs:element name="setvar" type="setvarType"/>
	<xs:element name="solution" type="solutionType"/>
	<xs:element name="solutionmaterial" type="solutionmaterialType"/>
	<xs:element name="sourcebank_ref" type="xs:string"/>
	<xs:element name="test_variable" type="test_variableType"/>
	<xs:element name="unanswered" type="unansweredType"/>
	<xs:element name="var_extension" type="var_extensionType"/>
	<xs:element name="varequal" type="varequalType"/>
	<xs:element name="vargt" type="vargtType"/>
	<xs:element name="vargte" type="vargteType"/>
	<xs:element name="variable_test" type="variable_testType"/>
	<xs:element name="varinside" type="varinsideType"/>
	<xs:element name="varlt" type="varltType"/>
	<xs:element name="varlte" type="varlteType"/>
	<xs:element name="varsubset" type="varsubsetType"/>
	<xs:element name="varsubstring" type="varsubstringType"/>
	<xs:element name="vocabulary" type="vocabularyType"/>
	<!-- ******************************* -->
	<!-- ** Complex Type Declarations ** -->
	<!-- ******************************* -->
	<!-- ***************** -->
	<!-- ** altmaterial ** -->
	<!-- ***************** -->
	<xs:complexType name="altmaterialType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice maxOccurs="unbounded">
				<xs:element name="mattext" type="mattextType"/>
				<xs:element name="matemtext" type="matemtextType"/>
				<xs:element name="matimage" type="matimageType"/>
				<xs:element name="mataudio" type="mataudioType"/>
				<xs:element name="matvideo" type="matvideoType"/>
				<xs:element name="matapplet" type="matappletType"/>
				<xs:element name="matapplication" type="matapplicationType"/>
				<xs:element name="matref" type="matrefType"/>
				<xs:element ref="matbreak"/>
				<xs:element ref="mat_extension"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- ********* -->
	<!-- ** and ** -->
	<!-- ********* -->
	<xs:complexType name="andType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="not" type="notType"/>
			<xs:element name="and" type="andType"/>
			<xs:element name="or" type="orType"/>
			<xs:element name="unanswered" type="unansweredType"/>
			<xs:element ref="other"/>
			<xs:element name="varequal" type="varequalType"/>
			<xs:element name="varlt" type="varltType"/>
			<xs:element name="varlte" type="varlteType"/>
			<xs:element name="vargt" type="vargtType"/>
			<xs:element name="vargte" type="vargteType"/>
			<xs:element name="varsubset" type="varsubsetType"/>
			<xs:element name="varinside" type="varinsideType"/>
			<xs:element name="varsubstring" type="varsubstringType"/>
			<xs:element name="durequal" type="durequalType"/>
			<xs:element name="durlt" type="durltType"/>
			<xs:element name="durlte" type="durlteType"/>
			<xs:element name="durgt" type="durgtType"/>
			<xs:element name="durgte" type="durgteType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** and_objects ** -->
	<!-- ***************** -->
	<xs:complexType name="and_objectsType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="outcomes_metadata" type="outcomes_metadataType"/>
			<xs:element name="and_objects" type="and_objectsType"/>
			<xs:element name="or_objects" type="or_objectsType"/>
			<xs:element name="not_objects" type="not_objectsType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** and_selection ** -->
	<!-- ******************* -->
	<xs:complexType name="and_selectionType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="selection_metadata" type="selection_metadataType"/>
			<xs:element name="and_selection" type="and_selectionType"/>
			<xs:element name="or_selection" type="or_selectionType"/>
			<xs:element name="not_selection" type="not_selectionType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** and_test ** -->
	<!-- ************** -->
	<xs:complexType name="and_testType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="variable_test" type="variable_testType"/>
			<xs:element name="and_test" type="and_testType"/>
			<xs:element name="or_test" type="or_testType"/>
			<xs:element name="not_test" type="not_testType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ******************** -->
	<!-- ** assessfeedback ** -->
	<!-- ******************** -->
	<xs:complexType name="assessfeedbackType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
				<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** assessment ** -->
	<!-- **************** -->
	<xs:complexType name="assessmentType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element ref="duration" minOccurs="0"/>
			<xs:element name="qtimetadata" type="qtimetadataType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="objectives" type="objectivesType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="assessmentcontrol" type="assessmentcontrolType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="rubric" type="rubricType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="presentation_material" type="presentation_materialType" minOccurs="0"/>
			<xs:element name="outcomes_processing" type="outcomes_processingType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="assessproc_extension" minOccurs="0"/>
			<xs:element name="assessfeedback" type="assessfeedbackType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="selection_ordering" type="selection_orderingType" minOccurs="0"/>
			<xs:element name="reference" type="referenceType" minOccurs="0"/>
			<xs:choice maxOccurs="unbounded">
				<xs:element name="sectionref" type="sectionrefType"/>
				<xs:element name="section" type="sectionType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- *********************** -->
	<!-- ** assessmentcontrol ** -->
	<!-- *********************** -->
	<xs:complexType name="assessmentcontrolType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="hintswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="solutionswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="feedbackswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ************************** -->
	<!-- ** assessproc_extension ** -->
	<!-- ************************** -->
	<xs:complexType name="assessproc_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** conditionvar ** -->
	<!-- ****************** -->
	<xs:complexType name="conditionvarType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="not" type="notType"/>
			<xs:element name="and" type="andType"/>
			<xs:element name="or" type="orType"/>
			<xs:element name="unanswered" type="unansweredType"/>
			<xs:element ref="other"/>
			<xs:element name="varequal" type="varequalType"/>
			<xs:element name="varlt" type="varltType"/>
			<xs:element name="varlte" type="varlteType"/>
			<xs:element name="vargt" type="vargtType"/>
			<xs:element name="vargte" type="vargteType"/>
			<xs:element name="varsubset" type="varsubsetType"/>
			<xs:element name="varinside" type="varinsideType"/>
			<xs:element name="varsubstring" type="varsubstringType"/>
			<xs:element name="durequal" type="durequalType"/>
			<xs:element name="durlt" type="durltType"/>
			<xs:element name="durlte" type="durlteType"/>
			<xs:element name="durgt" type="durgtType"/>
			<xs:element name="durgte" type="durgteType"/>
			<xs:element ref="var_extension"/>
		</xs:choice>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** decvar ** -->
	<!-- ************ -->
	<xs:complexType name="decvarType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="varname" type="xs:string" default="SCORE"/>
				<xs:attribute name="vartype" default="Integer">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Integer"/>
							<xs:enumeration value="String"/>
							<xs:enumeration value="Decimal"/>
							<xs:enumeration value="Scientific"/>
							<xs:enumeration value="Boolean"/>
							<xs:enumeration value="Enumerated"/>
							<xs:enumeration value="Set"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
				<xs:attribute name="defaultval" type="xs:string"/>
				<xs:attribute name="minvalue" type="xs:string"/>
				<xs:attribute name="maxvalue" type="xs:string"/>
				<xs:attribute name="members" type="xs:string"/>
				<xs:attribute name="cutvalue" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ********************* -->
	<!-- ** displayfeedback ** -->
	<!-- ********************* -->
	<xs:complexType name="displayfeedbackType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="feedbacktype" default="Response">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Response"/>
							<xs:enumeration value="Solution"/>
							<xs:enumeration value="Hint"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
				<xs:attribute name="linkrefid" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** durequal ** -->
	<!-- ************** -->
	<xs:complexType name="durequalType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *********** -->
	<!-- ** durgt ** -->
	<!-- *********** -->
	<xs:complexType name="durgtType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** durgte ** -->
	<!-- ************ -->
	<xs:complexType name="durgteType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *********** -->
	<!-- ** durlt ** -->
	<!-- *********** -->
	<xs:complexType name="durltType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** durlte ** -->
	<!-- ************ -->
	<xs:complexType name="durlteType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ********** -->
	<!-- ** flow ** -->
	<!-- ********** -->
	<xs:complexType name="flowType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="flow" type="flowType"/>
			<xs:element name="material" type="materialType"/>
			<xs:element name="material_ref" type="material_refType"/>
			<xs:element name="response_lid" type="response_lidType"/>
			<xs:element name="response_xy" type="response_xyType"/>
			<xs:element name="response_str" type="response_strType"/>
			<xs:element name="response_num" type="response_numType"/>
			<xs:element name="response_grp" type="response_grpType"/>
			<xs:element ref="response_extension"/>
		</xs:choice>
		<xs:attribute name="class" type="xs:string" default="Block"/>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** flow_label ** -->
	<!-- **************** -->
	<xs:complexType name="flow_labelType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="flow_label" type="flow_labelType"/>
			<xs:element name="response_label" type="response_labelType"/>
		</xs:choice>
		<xs:attribute name="class" type="xs:string" default="Block"/>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** flow_mat ** -->
	<!-- ************** -->
	<xs:complexType name="flow_matType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="flow_mat" type="flow_matType"/>
			<xs:element name="material" type="materialType"/>
			<xs:element name="material_ref" type="material_refType"/>
		</xs:choice>
		<xs:attribute name="class" type="xs:string" default="Block"/>
	</xs:complexType>
	<!-- ********** -->
	<!-- ** hint ** -->
	<!-- ********** -->
	<xs:complexType name="hintType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="hintmaterial" type="hintmaterialType" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:attribute name="feedbackstyle" default="Complete">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Complete"/>
					<xs:enumeration value="Incremental"/>
					<xs:enumeration value="Multilevel"/>
					<xs:enumeration value="Proprietary"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** hintmaterial ** -->
	<!-- ****************** -->
	<xs:complexType name="hintmaterialType">
		<xs:choice>
			<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
			<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
		</xs:choice>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** interpretvar ** -->
	<!-- ****************** -->
	<xs:complexType name="interpretvarType">
		<xs:choice>
			<xs:element name="material" type="materialType"/>
			<xs:element name="material_ref" type="material_refType"/>
		</xs:choice>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="varname" type="xs:string" default="SCORE"/>
	</xs:complexType>
	<!-- ********** -->
	<!-- ** item ** -->
	<!-- ********** -->
	<xs:complexType name="itemType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element ref="duration" minOccurs="0"/>
			<xs:element name="itemmetadata" type="itemmetadataType" minOccurs="0"/>
			<xs:element name="objectives" type="objectivesType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="itemcontrol" type="itemcontrolType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="itemprecondition" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="itempostcondition" minOccurs="0" maxOccurs="unbounded"/>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="itemrubric" type="itemrubricType"/>
				<xs:element name="rubric" type="rubricType"/>
			</xs:choice>
			<xs:element name="presentation" type="presentationType" minOccurs="0"/>
			<xs:element name="resprocessing" type="resprocessingType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="itemproc_extension" minOccurs="0"/>
			<xs:element name="itemfeedback" type="itemfeedbackType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="reference" type="referenceType" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="maxattempts" type="xs:string"/>
		<xs:attribute name="label" type="xs:string"/>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** itemcontrol ** -->
	<!-- ***************** -->
	<xs:complexType name="itemcontrolType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="feedbackswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="hintswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="solutionswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** itemfeedback ** -->
	<!-- ****************** -->
	<xs:complexType name="itemfeedbackType">
		<xs:choice maxOccurs="unbounded">
			<xs:choice>
				<xs:element name="flow_mat" type="flow_matType"/>
				<xs:element name="material" type="materialType"/>
			</xs:choice>
			<xs:element name="solution" type="solutionType"/>
			<xs:element name="hint" type="hintType"/>
		</xs:choice>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** itemmetadata ** -->
	<!-- ****************** -->
	<xs:complexType name="itemmetadataType">
		<xs:sequence>
			<xs:element name="qtimetadata" type="qtimetadataType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="qmd_computerscored" minOccurs="0"/>
			<xs:element ref="qmd_feedbackpermitted" minOccurs="0"/>
			<xs:element ref="qmd_hintspermitted" minOccurs="0"/>
			<xs:element ref="qmd_itemtype" minOccurs="0"/>
			<xs:element ref="qmd_levelofdifficulty" minOccurs="0"/>
			<xs:element ref="qmd_maximumscore" minOccurs="0"/>
			<xs:element ref="qmd_renderingtype" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="qmd_responsetype" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="qmd_scoringpermitted" minOccurs="0"/>
			<xs:element ref="qmd_solutionspermitted" minOccurs="0"/>
			<xs:element ref="qmd_status" minOccurs="0"/>
			<xs:element ref="qmd_timedependence" minOccurs="0"/>
			<xs:element ref="qmd_timelimit" minOccurs="0"/>
			<xs:element ref="qmd_toolvendor" minOccurs="0"/>
			<xs:element ref="qmd_topic" minOccurs="0"/>
			<xs:element ref="qmd_weighting" minOccurs="0"/>
			<xs:element ref="qmd_material" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="qmd_typeofsolution" minOccurs="0"/>
		</xs:sequence>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** itemproc_extension ** -->
	<!-- ************************ -->
	<xs:complexType name="itemproc_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ************* -->
	<!-- ** itemref ** -->
	<!-- ************* -->
	<xs:complexType name="itemrefType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="linkrefid" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** itemrubric ** -->
	<!-- **************** -->
	<xs:complexType name="itemrubricType">
		<xs:sequence>
			<xs:element name="material" type="materialType"/>
		</xs:sequence>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** map_input ** -->
	<!-- *************** -->
	<xs:complexType name="map_inputType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="varname" type="xs:string" default="SCORE"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** map_output ** -->
	<!-- **************** -->
	<xs:complexType name="map_outputType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="varname" type="xs:string" default="SCORE"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** mat_extension ** -->
	<!-- ******************* -->
	<xs:complexType name="mat_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** matapplet ** -->
	<!-- *************** -->
	<xs:complexType name="matappletType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="y0" type="xs:string"/>
				<xs:attribute name="height" type="xs:string"/>
				<xs:attribute name="width" type="xs:string"/>
				<xs:attribute name="x0" type="xs:string"/>
				<xs:attribute name="embedded" type="xs:string" default="base64"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ******************** -->
	<!-- ** matapplication ** -->
	<!-- ******************** -->
	<xs:complexType name="matapplicationType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="apptype" type="xs:string"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="embedded" type="xs:string" default="base64"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** mataudio ** -->
	<!-- ************** -->
	<xs:complexType name="mataudioType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="audiotype" type="xs:string" default="audio/base"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="embedded" type="xs:string" default="base64"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** matbreak ** -->
	<!-- ************** -->
	<xs:complexType name="matbreakType"/>
	<!-- *************** -->
	<!-- ** matemtext ** -->
	<!-- *************** -->
	<xs:complexType name="matemtextType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="texttype" type="xs:string" default="text/plain"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="charset" type="xs:string" default="ascii-us"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute ref="x:space" default="default"/>
				<xs:attribute ref="x:lang"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
				<xs:attribute name="width" type="xs:string"/>
				<xs:attribute name="height" type="xs:string"/>
				<xs:attribute name="y0" type="xs:string"/>
				<xs:attribute name="x0" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** material ** -->
	<!-- ************** -->
	<xs:complexType name="materialType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice maxOccurs="unbounded">
				<xs:element name="mattext" type="mattextType"/>
				<xs:element name="matemtext" type="matemtextType"/>
				<xs:element name="matimage" type="matimageType"/>
				<xs:element name="mataudio" type="mataudioType"/>
				<xs:element name="matvideo" type="matvideoType"/>
				<xs:element name="matapplet" type="matappletType"/>
				<xs:element name="matapplication" type="matapplicationType"/>
				<xs:element name="matref" type="matrefType"/>
				<xs:element ref="matbreak"/>
				<xs:element ref="mat_extension"/>
			</xs:choice>
			<xs:element name="altmaterial" type="altmaterialType" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:attribute name="label" type="xs:string"/>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** material_ref ** -->
	<!-- ****************** -->
	<xs:complexType name="material_refType">
		<xs:attribute name="linkrefid" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** matimage ** -->
	<!-- ************** -->
	<xs:complexType name="matimageType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="imagtype" type="xs:string" default="image/jpeg"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="height" type="xs:string"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="embedded" type="xs:string" default="base64"/>
				<xs:attribute name="width" type="xs:string"/>
				<xs:attribute name="y0" type="xs:string"/>
				<xs:attribute name="x0" type="xs:string"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** matref ** -->
	<!-- ************ -->
	<xs:complexType name="matrefType">
		<xs:attribute name="linkrefid" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- ************* -->
	<!-- ** mattext ** -->
	<!-- ************* -->
	<xs:complexType name="mattextType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="texttype" type="xs:string" default="text/plain"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="charset" type="xs:string" default="ascii-us"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute ref="x:space" default="default"/>
				<xs:attribute ref="x:lang"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
				<xs:attribute name="width" type="xs:string"/>
				<xs:attribute name="height" type="xs:string"/>
				<xs:attribute name="y0" type="xs:string"/>
				<xs:attribute name="x0" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** matvideo ** -->
	<!-- ************** -->
	<xs:complexType name="matvideoType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="videotype" type="xs:string" default="video/avi"/>
				<xs:attribute name="label" type="xs:string"/>
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="width" type="xs:string"/>
				<xs:attribute name="height" type="xs:string"/>
				<xs:attribute name="y0" type="xs:string"/>
				<xs:attribute name="x0" type="xs:string"/>
				<xs:attribute name="embedded" type="xs:string" default="base64"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ********* -->
	<!-- ** not ** -->
	<!-- ********* -->
	<xs:complexType name="notType">
		<xs:choice>
			<xs:element name="and" type="andType"/>
			<xs:element name="or" type="orType"/>
			<xs:element name="not" type="notType"/>
			<xs:element name="unanswered" type="unansweredType"/>
			<xs:element ref="other"/>
			<xs:element name="varequal" type="varequalType"/>
			<xs:element name="varlt" type="varltType"/>
			<xs:element name="varlte" type="varlteType"/>
			<xs:element name="vargt" type="vargtType"/>
			<xs:element name="vargte" type="vargteType"/>
			<xs:element name="varsubset" type="varsubsetType"/>
			<xs:element name="varinside" type="varinsideType"/>
			<xs:element name="varsubstring" type="varsubstringType"/>
			<xs:element name="durequal" type="durequalType"/>
			<xs:element name="durlt" type="durltType"/>
			<xs:element name="durlte" type="durlteType"/>
			<xs:element name="durgt" type="durgtType"/>
			<xs:element name="durgte" type="durgteType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** not_objects ** -->
	<!-- ***************** -->
	<xs:complexType name="not_objectsType">
		<xs:choice>
			<xs:element name="outcomes_metadata" type="outcomes_metadataType"/>
			<xs:element name="and_objects" type="and_objectsType"/>
			<xs:element name="or_objects" type="or_objectsType"/>
			<xs:element name="not_objects" type="not_objectsType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** not_selection ** -->
	<!-- ******************* -->
	<xs:complexType name="not_selectionType">
		<xs:choice>
			<xs:element name="selection_metadata" type="selection_metadataType"/>
			<xs:element name="and_selection" type="and_selectionType"/>
			<xs:element name="or_selection" type="or_selectionType"/>
			<xs:element name="not_selection" type="not_selectionType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** not_test ** -->
	<!-- ************** -->
	<xs:complexType name="not_testType">
		<xs:choice>
			<xs:element name="variable_test" type="variable_testType"/>
			<xs:element name="and_test" type="and_testType"/>
			<xs:element name="or_test" type="or_testType"/>
			<xs:element name="not_test" type="not_testType"/>
		</xs:choice>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** objectbank ** -->
	<!-- **************** -->
	<xs:complexType name="objectbankType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="qtimetadata" type="qtimetadataType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:choice maxOccurs="unbounded">
				<xs:element name="section" type="sectionType"/>
				<xs:element name="item" type="itemType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="ident" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** objectives ** -->
	<!-- **************** -->
	<xs:complexType name="objectivesType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
				<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- *********************** -->
	<!-- ** objects_condition ** -->
	<!-- *********************** -->
	<xs:complexType name="objects_conditionType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice minOccurs="0">
				<xs:element name="outcomes_metadata" type="outcomes_metadataType"/>
				<xs:element name="and_objects" type="and_objectsType"/>
				<xs:element name="or_objects" type="or_objectsType"/>
				<xs:element name="not_objects" type="not_objectsType"/>
			</xs:choice>
			<xs:element name="objects_parameter" type="objects_parameterType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="map_input" type="map_inputType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="objectscond_extension" minOccurs="0"/>
		</xs:sequence>
	</xs:complexType>
	<!-- *********************** -->
	<!-- ** objects_parameter ** -->
	<!-- *********************** -->
	<xs:complexType name="objects_parameterType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="pname" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ******** -->
	<!-- ** or ** -->
	<!-- ******** -->
	<xs:complexType name="orType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="not" type="notType"/>
			<xs:element name="and" type="andType"/>
			<xs:element name="or" type="orType"/>
			<xs:element name="unanswered" type="unansweredType"/>
			<xs:element ref="other"/>
			<xs:element name="varequal" type="varequalType"/>
			<xs:element name="varlt" type="varltType"/>
			<xs:element name="varlte" type="varlteType"/>
			<xs:element name="vargt" type="vargtType"/>
			<xs:element name="vargte" type="vargteType"/>
			<xs:element name="varsubset" type="varsubsetType"/>
			<xs:element name="varinside" type="varinsideType"/>
			<xs:element name="varsubstring" type="varsubstringType"/>
			<xs:element name="durequal" type="durequalType"/>
			<xs:element name="durlt" type="durltType"/>
			<xs:element name="durlte" type="durlteType"/>
			<xs:element name="durgt" type="durgtType"/>
			<xs:element name="durgte" type="durgteType"/>
		</xs:choice>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** or_objects ** -->
	<!-- **************** -->
	<xs:complexType name="or_objectsType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="outcomes_metadata" type="outcomes_metadataType"/>
			<xs:element name="and_objects" type="and_objectsType"/>
			<xs:element name="or_objects" type="or_objectsType"/>
			<xs:element name="not_objects" type="not_objectsType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** or_selection ** -->
	<!-- ****************** -->
	<xs:complexType name="or_selectionType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="selection_metadata" type="selection_metadataType"/>
			<xs:element name="and_selection" type="and_selectionType"/>
			<xs:element name="or_selection" type="or_selectionType"/>
			<xs:element name="not_selection" type="not_selectionType"/>
		</xs:choice>
	</xs:complexType>
	<!-- ************* -->
	<!-- ** or_test ** -->
	<!-- ************* -->
	<xs:complexType name="or_testType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="variable_test" type="variable_testType"/>
			<xs:element name="and_test" type="and_testType"/>
			<xs:element name="or_test" type="or_testType"/>
			<xs:element name="not_test" type="not_testType"/>
		</xs:choice>
	</xs:complexType>
	<!-- *********** -->
	<!-- ** order ** -->
	<!-- *********** -->
	<xs:complexType name="orderType">
		<xs:sequence>
			<xs:element ref="order_extension" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="order_type" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- ********************* -->
	<!-- ** order_extension ** -->
	<!-- ********************* -->
	<xs:complexType name="order_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** outcomes ** -->
	<!-- ************** -->
	<xs:complexType name="outcomesType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:sequence maxOccurs="unbounded">
				<xs:element name="decvar" type="decvarType"/>
				<xs:element name="interpretvar" type="interpretvarType" minOccurs="0" maxOccurs="unbounded"/>
			</xs:sequence>
		</xs:sequence>
	</xs:complexType>
	<!-- **************************** -->
	<!-- ** outcomes_feedback_test ** -->
	<!-- **************************** -->
	<xs:complexType name="outcomes_feedback_testType">
		<xs:sequence>
			<xs:element name="test_variable" type="test_variableType"/>
			<xs:element name="displayfeedback" type="displayfeedbackType" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:attribute name="title" type="xs:string"/>
	</xs:complexType>
	<!-- *********************** -->
	<!-- ** outcomes_metadata ** -->
	<!-- *********************** -->
	<xs:complexType name="outcomes_metadataType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="mdname" type="xs:string" use="required"/>
				<xs:attribute name="mdoperator" use="required">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="EQ"/>
							<xs:enumeration value="NEQ"/>
							<xs:enumeration value="LT"/>
							<xs:enumeration value="LTE"/>
							<xs:enumeration value="GT"/>
							<xs:enumeration value="GTE"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************************* -->
	<!-- ** outcomes_processing ** -->
	<!-- ************************* -->
	<xs:complexType name="outcomes_processingType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="outcomes" type="outcomesType"/>
			<xs:element name="objects_condition" type="objects_conditionType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="processing_parameter" type="processing_parameterType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="map_output" type="map_outputType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="outcomes_feedback_test" type="outcomes_feedback_testType" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:attribute name="scoremodel" type="xs:string"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** presentation ** -->
	<!-- ****************** -->
	<xs:complexType name="presentationType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="flow" type="flowType"/>
				<xs:choice maxOccurs="unbounded">
					<xs:element name="material" type="materialType"/>
					<xs:element name="response_lid" type="response_lidType"/>
					<xs:element name="response_xy" type="response_xyType"/>
					<xs:element name="response_str" type="response_strType"/>
					<xs:element name="response_num" type="response_numType"/>
					<xs:element name="response_grp" type="response_grpType"/>
					<xs:element ref="response_extension"/>
				</xs:choice>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="label" type="xs:string"/>
		<xs:attribute ref="x:lang"/>
		<xs:attribute name="y0" type="xs:string"/>
		<xs:attribute name="x0" type="xs:string"/>
		<xs:attribute name="width" type="xs:string"/>
		<xs:attribute name="height" type="xs:string"/>
	</xs:complexType>
	<!-- *************************** -->
	<!-- ** presentation_material ** -->
	<!-- *************************** -->
	<xs:complexType name="presentation_materialType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
		</xs:sequence>
	</xs:complexType>
	<!-- ************************** -->
	<!-- ** processing_parameter ** -->
	<!-- ************************** -->
	<xs:complexType name="processing_parameterType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="pname" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** qticomment ** -->
	<!-- **************** -->
	<xs:complexType name="qticommentType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute ref="x:lang"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** qtimetadata ** -->
	<!-- ***************** -->
	<xs:complexType name="qtimetadataType">
		<xs:sequence>
			<xs:element name="vocabulary" type="vocabularyType" minOccurs="0"/>
			<xs:element name="qtimetadatafield" type="qtimetadatafieldType" maxOccurs="unbounded"/>
		</xs:sequence>
	</xs:complexType>
	<!-- ********************** -->
	<!-- ** qtimetadatafield ** -->
	<!-- ********************** -->
	<xs:complexType name="qtimetadatafieldType">
		<xs:sequence>
			<xs:element ref="fieldlabel"/>
			<xs:element ref="fieldentry"/>
		</xs:sequence>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- ********************* -->
	<!-- ** questestinterop ** -->
	<!-- ********************* -->
	<xs:complexType name="questestinteropType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="objectbank" type="objectbankType"/>
				<xs:element name="assessment" type="assessmentType"/>
				<xs:choice maxOccurs="unbounded">
					<xs:element name="section" type="sectionType"/>
					<xs:element name="item" type="itemType"/>
				</xs:choice>
			</xs:choice>
		</xs:sequence>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** reference ** -->
	<!-- *************** -->
	<xs:complexType name="referenceType">
		<xs:choice maxOccurs="unbounded">
			<xs:element name="qticomment" type="qticommentType"/>
			<xs:element name="material" type="materialType"/>
			<xs:element name="mattext" type="mattextType"/>
			<xs:element name="matemtext" type="matemtextType"/>
			<xs:element name="matimage" type="matimageType"/>
			<xs:element name="mataudio" type="mataudioType"/>
			<xs:element name="matvideo" type="matvideoType"/>
			<xs:element name="matapplet" type="matappletType"/>
			<xs:element name="matapplication" type="matapplicationType"/>
			<xs:element ref="matbreak"/>
			<xs:element ref="mat_extension"/>
		</xs:choice>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** render_choice ** -->
	<!-- ******************* -->
	<xs:complexType name="render_choiceType">
		<xs:sequence>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
				<xs:element name="response_label" type="response_labelType"/>
				<xs:element name="flow_label" type="flow_labelType"/>
			</xs:choice>
			<xs:element ref="response_na" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="shuffle" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="minnumber" type="xs:string"/>
		<xs:attribute name="maxnumber" type="xs:string"/>
	</xs:complexType>
	<!-- ********************** -->
	<!-- ** render_extension ** -->
	<!-- ********************** -->
	<xs:complexType name="render_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** render_fib ** -->
	<!-- **************** -->
	<xs:complexType name="render_fibType">
		<xs:sequence>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
				<xs:element name="response_label" type="response_labelType"/>
				<xs:element name="flow_label" type="flow_labelType"/>
			</xs:choice>
			<xs:element ref="response_na" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="encoding" type="xs:string" default="UTF_8"/>
		<xs:attribute name="fibtype" default="String">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="String"/>
					<xs:enumeration value="Integer"/>
					<xs:enumeration value="Decimal"/>
					<xs:enumeration value="Scientific"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rows" type="xs:string"/>
		<xs:attribute name="maxchars" type="xs:string"/>
		<xs:attribute name="prompt">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Box"/>
					<xs:enumeration value="Dashline"/>
					<xs:enumeration value="Asterisk"/>
					<xs:enumeration value="Underline"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="columns" type="xs:string"/>
		<xs:attribute name="charset" type="xs:string" default="ascii-us"/>
		<xs:attribute name="maxnumber" type="xs:string"/>
		<xs:attribute name="minnumber" type="xs:string"/>
	</xs:complexType>
	<!-- ******************** -->
	<!-- ** render_hotspot ** -->
	<!-- ******************** -->
	<xs:complexType name="render_hotspotType">
		<xs:sequence>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
				<xs:element name="response_label" type="response_labelType"/>
				<xs:element name="flow_label" type="flow_labelType"/>
			</xs:choice>
			<xs:element ref="response_na" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="maxnumber" type="xs:string"/>
		<xs:attribute name="minnumber" type="xs:string"/>
		<xs:attribute name="showdraw" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** render_slider ** -->
	<!-- ******************* -->
	<xs:complexType name="render_sliderType">
		<xs:sequence>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
				<xs:element name="response_label" type="response_labelType"/>
				<xs:element name="flow_label" type="flow_labelType"/>
			</xs:choice>
			<xs:element ref="response_na" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="orientation" default="Horizontal">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Horizontal"/>
					<xs:enumeration value="Vertical"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="lowerbound" type="xs:string" use="required"/>
		<xs:attribute name="upperbound" type="xs:string" use="required"/>
		<xs:attribute name="step" type="xs:string"/>
		<xs:attribute name="startval" type="xs:string"/>
		<xs:attribute name="steplabel" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="maxnumber" type="xs:string"/>
		<xs:attribute name="minnumber" type="xs:string"/>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** respcond_extension ** -->
	<!-- ************************ -->
	<xs:complexType name="respcond_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** respcondition ** -->
	<!-- ******************* -->
	<xs:complexType name="respconditionType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="conditionvar" type="conditionvarType"/>
			<xs:element name="setvar" type="setvarType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="displayfeedback" type="displayfeedbackType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="respcond_extension" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="continue" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="title" type="xs:string"/>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** response_extension ** -->
	<!-- ************************ -->
	<xs:complexType name="response_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** response_grp ** -->
	<!-- ****************** -->
	<xs:complexType name="response_grpType">
		<xs:sequence>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
			<xs:choice>
				<xs:element name="render_choice" type="render_choiceType"/>
				<xs:element name="render_hotspot" type="render_hotspotType"/>
				<xs:element name="render_slider" type="render_sliderType"/>
				<xs:element name="render_fib" type="render_fibType"/>
				<xs:element ref="render_extension"/>
			</xs:choice>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="rcardinality" default="Single">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Single"/>
					<xs:enumeration value="Multiple"/>
					<xs:enumeration value="Ordered"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="rtiming" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** response_lableType ** -->
	<!-- ************************ -->
	<xs:complexType name="response_labelType" mixed="true">
		<xs:choice minOccurs="0" maxOccurs="unbounded">
			<xs:element name="qticomment" type="qticommentType"/>
			<xs:element name="material" type="materialType"/>
			<xs:element name="material_ref" type="material_refType"/>
			<xs:element name="flow_mat" type="flow_matType"/>
		</xs:choice>
		<xs:attribute name="rshuffle" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rarea" default="Ellipse">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Ellipse"/>
					<xs:enumeration value="Rectangle"/>
					<xs:enumeration value="Bounded"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rrange" default="Exact">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Exact"/>
					<xs:enumeration value="Range"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="labelrefid" type="xs:string"/>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="match_group" type="xs:string"/>
		<xs:attribute name="match_max" type="xs:string"/>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** response_lid ** -->
	<!-- ****************** -->
	<xs:complexType name="response_lidType">
		<xs:sequence>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
			<xs:choice>
				<xs:element name="render_choice" type="render_choiceType"/>
				<xs:element name="render_hotspot" type="render_hotspotType"/>
				<xs:element name="render_slider" type="render_sliderType"/>
				<xs:element name="render_fib" type="render_fibType"/>
				<xs:element ref="render_extension"/>
			</xs:choice>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="rcardinality" default="Single">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Single"/>
					<xs:enumeration value="Multiple"/>
					<xs:enumeration value="Ordered"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rtiming" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** response_na ** -->
	<!-- ***************** -->
	<xs:complexType name="response_naType"/>
	<!-- ****************** -->
	<!-- ** response_num ** -->
	<!-- ****************** -->
	<xs:complexType name="response_numType">
		<xs:sequence>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
			<xs:choice>
				<xs:element name="render_choice" type="render_choiceType"/>
				<xs:element name="render_hotspot" type="render_hotspotType"/>
				<xs:element name="render_slider" type="render_sliderType"/>
				<xs:element name="render_fib" type="render_fibType"/>
				<xs:element ref="render_extension"/>
			</xs:choice>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="numtype" default="Integer">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Integer"/>
					<xs:enumeration value="Decimal"/>
					<xs:enumeration value="Scientific"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rcardinality" default="Single">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Single"/>
					<xs:enumeration value="Multiple"/>
					<xs:enumeration value="Ordered"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="rtiming" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** response_str ** -->
	<!-- ****************** -->
	<xs:complexType name="response_strType">
		<xs:sequence>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
			<xs:choice>
				<xs:element name="render_choice" type="render_choiceType"/>
				<xs:element name="render_hotspot" type="render_hotspotType"/>
				<xs:element name="render_slider" type="render_sliderType"/>
				<xs:element name="render_fib" type="render_fibType"/>
				<xs:element ref="render_extension"/>
			</xs:choice>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="rcardinality" default="Single">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Single"/>
					<xs:enumeration value="Multiple"/>
					<xs:enumeration value="Ordered"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="rtiming" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ***************** -->
	<!-- ** response_xy ** -->
	<!-- ***************** -->
	<xs:complexType name="response_xyType">
		<xs:sequence>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
			<xs:choice>
				<xs:element name="render_choice" type="render_choiceType"/>
				<xs:element name="render_hotspot" type="render_hotspotType"/>
				<xs:element name="render_slider" type="render_sliderType"/>
				<xs:element name="render_fib" type="render_fibType"/>
				<xs:element ref="render_extension"/>
			</xs:choice>
			<xs:choice minOccurs="0">
				<xs:element name="material" type="materialType"/>
				<xs:element name="material_ref" type="material_refType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="rcardinality" default="Single">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Single"/>
					<xs:enumeration value="Multiple"/>
					<xs:enumeration value="Ordered"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="rtiming" default="No">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** resprocessing ** -->
	<!-- ******************* -->
	<xs:complexType name="resprocessingType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="outcomes" type="outcomesType"/>
			<xs:choice maxOccurs="unbounded">
				<xs:element name="respcondition" type="respconditionType"/>
				<xs:element ref="itemproc_extension"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="scoremodel" type="xs:string"/>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** rubric ** -->
	<!-- ************ -->
	<xs:complexType name="rubricType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
				<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ************* -->
	<!-- ** section ** -->
	<!-- ************* -->
	<xs:complexType name="sectionType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element ref="duration" minOccurs="0"/>
			<xs:element name="qtimetadata" type="qtimetadataType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="objectives" type="objectivesType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="sectioncontrol" type="sectioncontrolType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="sectionprecondition" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="sectionpostcondition" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="rubric" type="rubricType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="presentation_material" type="presentation_materialType" minOccurs="0"/>
			<xs:element name="outcomes_processing" type="outcomes_processingType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element ref="sectionproc_extension" minOccurs="0"/>
			<xs:element name="sectionfeedback" type="sectionfeedbackType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="selection_ordering" type="selection_orderingType" minOccurs="0"/>
			<xs:element name="reference" type="referenceType" minOccurs="0"/>
			<xs:choice minOccurs="0" maxOccurs="unbounded">
				<xs:element name="itemref" type="itemrefType"/>
				<xs:element name="item" type="itemType"/>
				<xs:element name="sectionref" type="sectionrefType"/>
				<xs:element name="section" type="sectionType"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
		<xs:attribute ref="x:lang"/>
	</xs:complexType>
	<!-- ******************** -->
	<!-- ** sectioncontrol ** -->
	<!-- ******************** -->
	<xs:complexType name="sectioncontrolType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="feedbackswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="hintswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="solutionswitch" default="Yes">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Yes"/>
					<xs:enumeration value="No"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ********************* -->
	<!-- ** sectionfeedback ** -->
	<!-- ********************* -->
	<xs:complexType name="sectionfeedbackType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:choice>
				<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
				<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
			</xs:choice>
		</xs:sequence>
		<xs:attribute name="view" default="All">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="All"/>
					<xs:enumeration value="Administrator"/>
					<xs:enumeration value="AdminAuthority"/>
					<xs:enumeration value="Assessor"/>
					<xs:enumeration value="Author"/>
					<xs:enumeration value="Candidate"/>
					<xs:enumeration value="InvigilatorProctor"/>
					<xs:enumeration value="Psychometrician"/>
					<xs:enumeration value="Scorer"/>
					<xs:enumeration value="Tutor"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
		<xs:attribute name="ident" type="xs:string" use="required"/>
		<xs:attribute name="title" type="xs:string"/>
	</xs:complexType>
	<!-- *************************** -->
	<!-- ** sectionproc_extension ** -->
	<!-- *************************** -->
	<xs:complexType name="sectionproc_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** sectionref ** -->
	<!-- **************** -->
	<xs:complexType name="sectionrefType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="linkrefid" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** selection ** -->
	<!-- *************** -->
	<xs:complexType name="selectionType">
		<xs:sequence>
			<xs:element ref="sourcebank_ref" minOccurs="0"/>
			<xs:element ref="selection_number" minOccurs="0"/>
			<xs:element name="selection_metadata" type="selection_metadataType" minOccurs="0"/>
			<xs:choice minOccurs="0">
				<xs:element name="and_selection" type="and_selectionType"/>
				<xs:element name="or_selection" type="or_selectionType"/>
				<xs:element name="not_selection" type="not_selectionType"/>
				<xs:element ref="selection_extension"/>
			</xs:choice>
		</xs:sequence>
	</xs:complexType>
	<!-- ************************* -->
	<!-- ** selection_extension ** -->
	<!-- ************************* -->
	<xs:complexType name="selection_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** selection_metadata ** -->
	<!-- ************************ -->
	<xs:complexType name="selection_metadataType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="mdname" type="xs:string" use="required"/>
				<xs:attribute name="mdoperator" use="required">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="EQ"/>
							<xs:enumeration value="NEQ"/>
							<xs:enumeration value="LT"/>
							<xs:enumeration value="LTE"/>
							<xs:enumeration value="GT"/>
							<xs:enumeration value="GTE"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** selection_ordering ** -->
	<!-- ************************ -->
	<xs:complexType name="selection_orderingType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="sequence_parameter" type="sequence_parameterType" minOccurs="0"/>
			<xs:element name="selection" type="selectionType" minOccurs="0" maxOccurs="unbounded"/>
			<xs:element name="order" type="orderType" minOccurs="0"/>
		</xs:sequence>
		<xs:attribute name="sequence_type" type="xs:string"/>
	</xs:complexType>
	<!-- ************************ -->
	<!-- ** sequence_parameter ** -->
	<!-- ************************ -->
	<xs:complexType name="sequence_parameterType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="pname" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** setvar ** -->
	<!-- ************ -->
	<xs:complexType name="setvarType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="varname" type="xs:string" default="SCORE"/>
				<xs:attribute name="action" default="Set">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Set"/>
							<xs:enumeration value="Add"/>
							<xs:enumeration value="Subtract"/>
							<xs:enumeration value="Multiply"/>
							<xs:enumeration value="Divide"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** solution ** -->
	<!-- ************** -->
	<xs:complexType name="solutionType">
		<xs:sequence>
			<xs:element name="qticomment" type="qticommentType" minOccurs="0"/>
			<xs:element name="solutionmaterial" type="solutionmaterialType" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:attribute name="feedbackstyle" default="Complete">
			<xs:simpleType>
				<xs:restriction base="xs:NMTOKEN">
					<xs:enumeration value="Complete"/>
					<xs:enumeration value="Incremental"/>
					<xs:enumeration value="Multilevel"/>
					<xs:enumeration value="Proprietary"/>
				</xs:restriction>
			</xs:simpleType>
		</xs:attribute>
	</xs:complexType>
	<!-- ********************** -->
	<!-- ** solutionmaterial ** -->
	<!-- ********************** -->
	<xs:complexType name="solutionmaterialType">
		<xs:choice>
			<xs:element name="material" type="materialType" maxOccurs="unbounded"/>
			<xs:element name="flow_mat" type="flow_matType" maxOccurs="unbounded"/>
		</xs:choice>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** test_variable ** -->
	<!-- ******************* -->
	<xs:complexType name="test_variableType">
		<xs:choice>
			<xs:element name="variable_test" type="variable_testType"/>
			<xs:element name="and_test" type="and_testType"/>
			<xs:element name="or_test" type="or_testType"/>
			<xs:element name="not_test" type="not_testType"/>
		</xs:choice>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** unanswered ** -->
	<!-- **************** -->
	<xs:complexType name="unansweredType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** var_extension ** -->
	<!-- ******************* -->
	<xs:complexType name="var_extensionType" mixed="true">
		<xs:sequence>
			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
		</xs:sequence>
		<xs:anyAttribute processContents="lax"/>
	</xs:complexType>
	<!-- ************** -->
	<!-- ** varequal ** -->
	<!-- ************** -->
	<xs:complexType name="varequalType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="case" default="No">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Yes"/>
							<xs:enumeration value="No"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *********** -->
	<!-- ** vargt ** -->
	<!-- *********** -->
	<xs:complexType name="vargtType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** vargte ** -->
	<!-- ************ -->
	<xs:complexType name="vargteType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ******************* -->
	<!-- ** variable_test ** -->
	<!-- ******************* -->
	<xs:complexType name="variable_testType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="varname" type="xs:string" default="SCORE"/>
				<xs:attribute name="testoperator" use="required">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="EQ"/>
							<xs:enumeration value="NEQ"/>
							<xs:enumeration value="LT"/>
							<xs:enumeration value="LTE"/>
							<xs:enumeration value="GT"/>
							<xs:enumeration value="GTE"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** varinside ** -->
	<!-- *************** -->
	<xs:complexType name="varinsideType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="areatype" use="required">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Ellipse"/>
							<xs:enumeration value="Rectangle"/>
							<xs:enumeration value="Bounded"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *********** -->
	<!-- ** varlt ** -->
	<!-- *********** -->
	<xs:complexType name="varltType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ************ -->
	<!-- ** varlte ** -->
	<!-- ************ -->
	<xs:complexType name="varlteType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- *************** -->
	<!-- ** varsubset ** -->
	<!-- *************** -->
	<xs:complexType name="varsubsetType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="setmatch" default="Exact">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Exact"/>
							<xs:enumeration value="Partial"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
				<xs:attribute name="index" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- ****************** -->
	<!-- ** varsubstring ** -->
	<!-- ****************** -->
	<xs:complexType name="varsubstringType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="index" type="xs:string"/>
				<xs:attribute name="respident" type="xs:string" use="required"/>
				<xs:attribute name="case" default="No">
					<xs:simpleType>
						<xs:restriction base="xs:NMTOKEN">
							<xs:enumeration value="Yes"/>
							<xs:enumeration value="No"/>
						</xs:restriction>
					</xs:simpleType>
				</xs:attribute>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
	<!-- **************** -->
	<!-- ** vocabulary ** -->
	<!-- **************** -->
	<xs:complexType name="vocabularyType">
		<xs:simpleContent>
			<xs:extension base="xs:string">
				<xs:attribute name="uri" type="xs:string"/>
				<xs:attribute name="entityref" type="xs:ENTITY"/>
				<xs:attribute name="vocab_type" type="xs:string"/>
			</xs:extension>
		</xs:simpleContent>
	</xs:complexType>
</xs:schema>
packaging/ims_xml.xsd000064400000002354152003363470010667 0ustar00<?xml version="1.0" encoding="UTF-8"?>
<!-- filename=ims_xml.xsd -->
<xsd:schema xmlns="http://www.w3.org/XML/1998/namespace" 
            targetNamespace="http://www.w3.org/XML/1998/namespace" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            elementFormDefault="qualified">
	<!-- 2001-02-22 edited by Thomas Wason IMS Global Learning Consortium, Inc. -->
	<xsd:annotation>
		<xsd:documentation>In namespace-aware XML processors, the &quot;xml&quot; prefix is bound to the namespace name http://www.w3.org/XML/1998/namespace.</xsd:documentation>
		<xsd:documentation>Do not reference this file in XML instances</xsd:documentation>
                <xsd:documentation>Schawn Thropp: Changed the uriReference type to string type</xsd:documentation>
	</xsd:annotation>
	<xsd:attribute name="lang" type="xsd:language">
		<xsd:annotation>
			<xsd:documentation>Refers to universal  XML 1.0 lang attribute</xsd:documentation>
		</xsd:annotation>
	</xsd:attribute>
	<xsd:attribute name="base" type="xsd:string">
		<xsd:annotation>
			<xsd:documentation>Refers to XML Base: http://www.w3.org/TR/xmlbase</xsd:documentation>
		</xsd:annotation>
	</xsd:attribute>
	<xsd:attribute name="link" type="xsd:string"/>
</xsd:schema>
packaging/xml.xsd000064400000013305152003363470010015 0ustar00<?xml version='1.0'?>
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="en">

 <xs:annotation>
  <xs:documentation>
   See http://www.w3.org/XML/1998/namespace.html and
   http://www.w3.org/TR/REC-xml for information about this namespace.

    This schema document describes the XML namespace, in a form
    suitable for import by other schema documents.  

    Note that local names in this namespace are intended to be defined
    only by the World Wide Web Consortium or its subgroups.  The
    following names are currently defined in this namespace and should
    not be used with conflicting semantics by any Working Group,
    specification, or document instance:

    base (as an attribute name): denotes an attribute whose value
         provides a URI to be used as the base for interpreting any
         relative URIs in the scope of the element on which it
         appears; its value is inherited.  This name is reserved
         by virtue of its definition in the XML Base specification.

    id   (as an attribute name): denotes an attribute whose value
         should be interpreted as if declared to be of type ID.
         The xml:id specification is not yet a W3C Recommendation,
         but this attribute is included here to facilitate experimentation
         with the mechanisms it proposes.  Note that it is _not_ included
         in the specialAttrs attribute group.

    lang (as an attribute name): denotes an attribute whose value
         is a language code for the natural language of the content of
         any element; its value is inherited.  This name is reserved
         by virtue of its definition in the XML specification.
  
    space (as an attribute name): denotes an attribute whose
         value is a keyword indicating what whitespace processing
         discipline is intended for the content of the element; its
         value is inherited.  This name is reserved by virtue of its
         definition in the XML specification.

    Father (in any context at all): denotes Jon Bosak, the chair of 
         the original XML Working Group.  This name is reserved by 
         the following decision of the W3C XML Plenary and 
         XML Coordination groups:

             In appreciation for his vision, leadership and dedication
             the W3C XML Plenary on this 10th day of February, 2000
             reserves for Jon Bosak in perpetuity the XML name
             xml:Father
  </xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>This schema defines attributes and an attribute group
        suitable for use by
        schemas wishing to allow xml:base, xml:lang, xml:space or xml:id
        attributes on elements they define.

        To enable this, such a schema must import this schema
        for the XML namespace, e.g. as follows:
        &lt;schema . . .>
         . . .
         &lt;import namespace="http://www.w3.org/XML/1998/namespace"
                    schemaLocation="http://www.w3.org/2001/xml.xsd"/>

        Subsequently, qualified reference to any of the attributes
        or the group defined below will have the desired effect, e.g.

        &lt;type . . .>
         . . .
         &lt;attributeGroup ref="xml:specialAttrs"/>
 
         will define a type which will schema-validate an instance
         element with any of those attributes</xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>In keeping with the XML Schema WG's standard versioning
   policy, this schema document will persist at
   http://www.w3.org/2005/08/xml.xsd.
   At the date of issue it can also be found at
   http://www.w3.org/2001/xml.xsd.
   The schema document at that URI may however change in the future,
   in order to remain compatible with the latest version of XML Schema
   itself, or with the XML namespace itself.  In other words, if the XML
   Schema or XML namespaces change, the version of this document at
   http://www.w3.org/2001/xml.xsd will change
   accordingly; the version at
   http://www.w3.org/2005/08/xml.xsd will not change.
  </xs:documentation>
 </xs:annotation>

 <xs:attribute name="lang">
  <xs:annotation>
   <xs:documentation>Attempting to install the relevant ISO 2- and 3-letter
         codes as the enumerated possible values is probably never
         going to be a realistic possibility.  See
         RFC 3066 at http://www.ietf.org/rfc/rfc3066.txt and the IANA registry
         at http://www.iana.org/assignments/lang-tag-apps.htm for
         further information.

         The union allows for the 'un-declaration' of xml:lang with
         the empty string.</xs:documentation>
  </xs:annotation>
  <xs:simpleType>
   <xs:union memberTypes="xs:language">
    <xs:simpleType>    
     <xs:restriction base="xs:string">
      <xs:enumeration value=""/>
     </xs:restriction>
    </xs:simpleType>
   </xs:union>
  </xs:simpleType>
 </xs:attribute>

 <xs:attribute name="space">
  <xs:simpleType>
   <xs:restriction base="xs:NCName">
    <xs:enumeration value="default"/>
    <xs:enumeration value="preserve"/>
   </xs:restriction>
  </xs:simpleType>
 </xs:attribute>

 <xs:attribute name="base" type="xs:anyURI">
  <xs:annotation>
   <xs:documentation>See http://www.w3.org/TR/xmlbase/ for
                     information about this attribute.</xs:documentation>
  </xs:annotation>
 </xs:attribute>
 
 <xs:attribute name="id" type="xs:ID">
  <xs:annotation>
   <xs:documentation>See http://www.w3.org/TR/xml-id/ for
                     information about this attribute.</xs:documentation>
  </xs:annotation>
 </xs:attribute>

 <xs:attributeGroup name="specialAttrs">
  <xs:attribute ref="xml:base"/>
  <xs:attribute ref="xml:lang"/>
  <xs:attribute ref="xml:space"/>
 </xs:attributeGroup>

</xs:schema>
error_log000064400000076546152003363470006506 0ustar00[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:08:22 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:08:22 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 17:08:22 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:16:19 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 17:16:19 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 17:16:19 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 18:59:54 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 18:59:54 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 18:59:54 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 21:53:29 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[05-May-2026 21:53:29 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[05-May-2026 21:53:29 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[06-May-2026 01:01:40 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[06-May-2026 01:01:40 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[06-May-2026 01:01:40 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 05:13:11 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 05:13:11 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[11-May-2026 05:13:11 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1483
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1500
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1506
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1529
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1532
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1535
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1538
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 06:27:48 Europe/Paris] PHP Deprecated:  Array and string offset access syntax with curly braces is deprecated in /home/missmand/public_html/learning/old/main/inc/lib/internationalization.lib.php on line 1541
[11-May-2026 06:27:48 Europe/Paris] PHP Warning:  require_once(/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php): failed to open stream: No such file or directory in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101
[11-May-2026 06:27:48 Europe/Paris] PHP Fatal error:  require_once(): Failed opening required '/var/www/restricted/ssh/missm/www/main/inc/lib/database.lib.php' (include_path='.:/var/www/restricted/ssh/missm/www/main/inc/lib/pear:/opt/cpanel/ea-php74/root/usr/share/pear') in /home/missmand/public_html/learning/old/main/inc/global.inc.php on line 101

Hacked By AnonymousFox1.0, Coded By AnonymousFox