示例#1
0
 /**
  * constructor of the class
  *
  * @author 	Olivier Brouckaert
  * @param int $questionId that answers belong to
  * @param int $course_id
  */
 public function __construct($questionId, $course_id = null)
 {
     $this->questionId = intval($questionId);
     $this->answer = array();
     $this->correct = array();
     $this->comment = array();
     $this->weighting = array();
     $this->position = array();
     $this->hotspot_coordinates = array();
     $this->hotspot_type = array();
     $this->destination = array();
     // clears $new_* arrays
     $this->cancel();
     if (!empty($course_id)) {
         $courseInfo = api_get_course_info_by_id($course_id);
     } else {
         $courseInfo = api_get_course_info();
     }
     $this->course = $courseInfo;
     $this->course_id = $courseInfo['real_id'];
     // fills arrays
     $objExercise = new Exercise($this->course_id);
     $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
     $objExercise->read($exerciseId);
     if ($objExercise->random_answers == '1') {
         $this->readOrderedBy('rand()', '');
         // randomize answers
     } else {
         $this->read();
         // natural order
     }
 }
示例#2
0
    we delete the objExercise from the session in order to get the latest
    changes in the exercise */
if (api_is_allowed_to_edit(null, true) && isset($_GET['preview']) && $_GET['preview'] == 1) {
    Session::erase('objExercise');
}
// 1. Loading the $objExercise variable
if (!isset($_SESSION['objExercise']) || $_SESSION['objExercise']->id != $_REQUEST['exerciseId']) {
    // Construction of Exercise
    $objExercise = new Exercise();
    Session::write('firstTime', true);
    if ($debug) {
        error_log('1. Setting the $objExercise variable');
    }
    unset($_SESSION['questionList']);
    // if the specified exercise doesn't exist or is disabled
    if (!$objExercise->read($exerciseId) || !$objExercise->selectStatus() && !$is_allowedToEdit && $origin != 'learnpath') {
        if ($debug) {
            error_log('1.1. Error while reading the exercise');
        }
        unset($objExercise);
        $error = get_lang('ExerciseNotFound');
    } else {
        // Saves the object into the session
        Session::write('objExercise', $objExercise);
        if ($debug) {
            error_log('1.1. $_SESSION[objExercise] was unset - set now - end');
        }
    }
} else {
    Session::write('firstTime', false);
}
 /**
  * return the number of question of a category id in a test
  * input : test_id, category_id
  * return : integer
  * hubert.borderiou 07-04-2011
  */
 public static function getNumberOfQuestionsInCategoryForTest($exercise_id, $category_id)
 {
     $number_questions_in_category = 0;
     $exercise = new Exercise();
     $exercise->read($exercise_id);
     $question_list = $exercise->getQuestionList();
     // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ? ? ?
     foreach ($question_list as $question_id) {
         $category_in_question = Testcategory::getCategoryForQuestion($question_id);
         if (in_array($category_id, $category_in_question)) {
             $number_questions_in_category++;
         }
     }
     return $number_questions_in_category;
 }
示例#4
0
    /**
     * 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
     */
    function scorm_export()
    {
        global $_course;
        global $charset;
        if (!class_exists('DomDocument')) {
            error_log('DOM functions not supported for PHP version below 5.0', 0);
            $this->error = 'PHP DOM functions not supported for PHP versions below 5.0';
            return null;
        }
        //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);
            //}else{
            //error_log('Scorm export: could not change memory and time limits',0);
        }
        //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);
        } 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);
            $perm = api_get_setting('permissions_for_new_directories');
            $perm = octdec(!empty($perm) ? $perm : '0770');
            mkdir($dest_path_to_scorm_folder, $perm, 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', $this->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', 'dokeos_scorm_export');
        $organization = $xmldoc->createElement('organization');
        $organization->setAttribute('identifier', 'dokeos_scorm_export');
        //to set the title of the SCORM entity (=organization), we take the name given
        //in Dokeos and convert it to HTML entities using the Dokeos 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', htmlentities(api_htmlentities($this->get_name(), ENT_QUOTES, $charset)));
        $organization->appendChild($org_title);
        //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($item->get_title(), ENT_QUOTES, $this->encoding));
                $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 . '/');
                $my_xml_file_path = api_htmlentities($my_file_path, ENT_QUOTES, $this->encoding);
                $my_sub_dir = dirname($my_file_path);
                $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
                //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 or 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);
                                $file_path = realpath($abs_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 dokeos 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', '');
                                } else {
                                    if (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 = dirname($current_course_path.'/'.$item->get_file_path()).'/';
                                //the next lines fix a bug when using the "subdir" mode of Dokeos, 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) . '/';
                                    $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_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_orig_file_path = dirname($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 the dokeos 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', '');
                                } else {
                                    if (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()) . '/';
                                    $file_path = realpath($current_dir . $doc_info[0]);
                                    //error_log($file_path.' <-> '.$main_path,0);
                                    if (strstr($file_path, $main_path) !== false) {
                                        //the calculated real path is really inside the dokeos 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
                if ($item->type == 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($item->get_title(), ENT_QUOTES));
                    $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 id=' . $item->path;
                    $rs = Database::query($sql, __FILE__, __LINE__);
                    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($my_file_path, ENT_QUOTES, $this->encoding);
                        $my_sub_dir = dirname($my_file_path);
                        $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
                        //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);
                    }
                } elseif ($item->type == 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($item->get_title(), ENT_QUOTES, $this->encoding));
                    $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($my_file_path, ENT_QUOTES, $this->encoding);
                    $my_sub_dir = dirname($my_file_path);
                    $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
                    //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 or 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);
                                    $file_path = realpath($abs_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 dokeos 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', '');
                                    } else {
                                        if (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()) . '/';
                                    $file_path = realpath($doc_info[0]);
                                    $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 dokeos 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', '');
                                    } else {
                                        if (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()) . '/';
                                        $file_path = realpath($current_dir . $doc_info[0]);
                                        //error_log($file_path.' <-> '.$main_path,0);
                                        if (strstr($file_path, $main_path) !== false) {
                                            //the calculated real path is really inside the dokeos 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/DOKEOS/document/....
                                            $info_file_path = explode('/', $file_path);
                                            if ($info_file_path[0] == 'courses') {
                                                //add character "/" in file path
                                                $file_path_dest = '/' . $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;
                } else {
                    //get the path of the file(s) from the course directory root
                    $my_file_path = 'non_exportable.html';
                    $my_xml_file_path = api_htmlentities($my_file_path, ENT_COMPAT, $this->encoding);
                    $my_sub_dir = dirname($my_file_path);
                    $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_COMPAT, $this->encoding);
                    //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', 'document/' . $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);
                }
            }
        }
        $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']);
                    }
                    if (substr($old_new['dest'], 0, 1) == '/') {
                        $old_new['dest'] = substr($old_new['dest'], 1);
                    }
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
                    $string = str_replace(str_replace('https', 'http', $old_new['orig']), $old_new['dest'], $string);
                }
                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']);
                    }
                    if (substr($old_new['dest'], 0, 1) == '/') {
                        $old_new['dest'] = substr($old_new['dest'], 1);
                    }
                    $string = str_replace($old_new['orig'], $old_new['dest'], $string);
                    $string = str_replace(str_replace('https', 'http', $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 = '<html><body><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 = <<<EOD
<html>
\t<head>
\t\t<style>
\t\t\t.error-message {
\t\t\t\tfont-family: arial, verdana, helvetica, sans-serif;
\t\t\t\tborder-width: 1px;
\t\t\t\tborder-style: solid;
\t\t\t\tleft: 50%;
\t\t\t\tmargin: 10px auto;
\t\t\t\tmin-height: 30px;
\t\t\t\tpadding: 5px;
\t\t\t\tright: 50%;
\t\t\t\twidth: 500px;
\t\t\t\tbackground-color: #FFD1D1;
\t\t\t\tborder-color: #FF0000;
\t\t\t\tcolor: #000;
\t\t\t}
\t\t</style>
\t<body>
\t\t<div class="error-message">
\t\t\t{$lang_not_exportable}
\t\t</div>
\t</body>
</html>
EOD;
        if (!is_dir($archive_path . $temp_dir_short . '/document')) {
            @mkdir($archive_path . $temp_dir_short . '/document');
        }
        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
        $xmldoc->save($archive_path . '/' . $temp_dir_short . '/imsmanifest.xml');
        $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 = preg_replace('([^a-zA-Z0-9_\\.])', '-', html_entity_decode($this->get_name(), ENT_QUOTES)) . '.zip';
        DocumentManager::file_send_for_download($temp_zip_file, true, $name);
    }
}
if (empty($course_code)) {
    $course_code = 0;
}
$form->setDefaults(array('course_code' => (string) $course_code));
$course_info = api_get_course_info($course_code);
if (!empty($course_info)) {
    $list = new LearnpathList('', $course_code);
    $lp_list = $list->get_flat_list();
    $_course = $course_info;
    $main_question_list = array();
    foreach ($lp_list as $lp_id => $lp) {
        $exercise_list = ExerciseLib::get_all_exercises_from_lp($lp_id, $course_info['real_id']);
        foreach ($exercise_list as $exercise) {
            $my_exercise = new Exercise();
            $my_exercise->read($exercise['path']);
            $question_list = $my_exercise->selectQuestionList();
            $exercise_stats = get_all_exercise_event_from_lp($exercise['path'], $course_info['real_id'], $session_id);
            foreach ($question_list as $question_id) {
                $question_data = Question::read($question_id);
                $main_question_list[$question_id] = $question_data;
                $quantity_exercises = 0;
                $question_result = 0;
                foreach ($exercise_stats as $stats) {
                    if (!empty($stats['question_list'])) {
                        foreach ($stats['question_list'] as $my_question_stat) {
                            if ($question_id == $my_question_stat['question_id']) {
                                $question_result = $question_result + $my_question_stat['marks'];
                                $quantity_exercises++;
                            }
                        }
}
if (empty($objExercise)) {
    $objExercise = $_SESSION['objExercise'];
}
if (empty($remind_list)) {
    $remind_list = isset($_REQUEST['remind_list']) ? $_REQUEST['remind_list'] : null;
}
$exe_id = isset($_REQUEST['exe_id']) ? intval($_REQUEST['exe_id']) : 0;
if (empty($objExercise)) {
    // Redirect to the exercise overview
    // Check if the exe_id exists
    $objExercise = new Exercise();
    $exercise_stat_info = $objExercise->getStatTrackExerciseInfoByExeId($exe_id);
    if (!empty($exercise_stat_info) && isset($exercise_stat_info['exe_exo_id'])) {
        if ($exercise_stat_info['status'] == 'incomplete') {
            $objExercise->read($exercise_stat_info['exe_exo_id']);
        } else {
            header("Location: overview.php?exerciseId=" . $exercise_stat_info['exe_exo_id']);
            exit;
        }
    } else {
        api_not_allowed(true);
    }
}
$gradebook = '';
if (isset($_SESSION['gradebook'])) {
    $gradebook = $_SESSION['gradebook'];
}
if (!empty($gradebook) && $gradebook == 'view') {
    $interbreadcrumb[] = array('url' => '../gradebook/' . $_SESSION['gradebook_dest'], 'name' => get_lang('ToolGradebook'));
}
示例#7
0
 /**
  * return the number of question of a category id in a test
  * @param int $exerciseId
  * @param int $categoryId
  *
  * @return integer
  *
  * @author hubert.borderiou 07-04-2011
  */
 public static function getNumberOfQuestionsInCategoryForTest($exerciseId, $categoryId)
 {
     $nbCatResult = 0;
     $quiz = new Exercise();
     $quiz->read($exerciseId);
     $tabQuestionList = $quiz->selectQuestionList();
     // the array given by selectQuestionList start at indice 1 and not at indice 0 !!! ? ? ?
     for ($i = 1; $i <= count($tabQuestionList); $i++) {
         if (TestCategory::getCategoryForQuestion($tabQuestionList[$i]) == $categoryId) {
             $nbCatResult++;
         }
     }
     return $nbCatResult;
 }
示例#8
0
 /**
  * Shows the user detail progress (when clicking in the details link)
  * @param   int     $user_id
  * @param   string  $course_code
  * @param   int     $session_id
  * @return  string  html code
  */
 public static function show_course_detail($user_id, $course_code, $session_id)
 {
     $html = '';
     if (isset($course_code)) {
         $user_id = intval($user_id);
         $session_id = intval($session_id);
         $course = Database::escape_string($course_code);
         $course_info = CourseManager::get_course_information($course);
         $html .= Display::page_subheader($course_info['title']);
         $html .= '<table class="data_table" width="100%">';
         //Course details
         $html .= '
             <tr>
             <th class="head" style="color:#000">' . get_lang('Exercises') . '</th>
             <th class="head" style="color:#000">' . get_lang('Attempts') . '</th>
             <th class="head" style="color:#000">' . get_lang('BestAttempt') . '</th>
             <th class="head" style="color:#000">' . get_lang('Ranking') . '</th>
             <th class="head" style="color:#000">' . get_lang('BestResultInCourse') . '</th>
             <th class="head" style="color:#000">' . get_lang('Statistics') . ' ' . Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent'), array('align' => 'absmiddle', 'hspace' => '3px')) . '</th>
             </tr>';
         if (empty($session_id)) {
             $user_list = CourseManager::get_user_list_from_course_code($course, $session_id, null, null, STUDENT);
         } else {
             $user_list = CourseManager::get_user_list_from_course_code($course, $session_id, null, null, 0);
         }
         // Show exercise results of invisible exercises? see BT#4091
         $exercise_list = ExerciseLib::get_all_exercises($course_info, $session_id, false, null, false, 2);
         $to_graph_exercise_result = array();
         if (!empty($exercise_list)) {
             $score = $weighting = $exe_id = 0;
             foreach ($exercise_list as $exercices) {
                 $exercise_obj = new Exercise($course_info['real_id']);
                 $exercise_obj->read($exercices['id']);
                 $visible_return = $exercise_obj->is_visible();
                 $score = $weighting = $attempts = 0;
                 // Getting count of attempts by user
                 $attempts = Event::count_exercise_attempts_by_user(api_get_user_id(), $exercices['id'], $course_info['real_id'], $session_id);
                 $html .= '<tr class="row_even">';
                 $url = api_get_path(WEB_CODE_PATH) . "exercice/overview.php?cidReq={$course_info['code']}&id_session={$session_id}&exerciseId={$exercices['id']}";
                 if ($visible_return['value'] == true) {
                     $exercices['title'] = Display::url($exercices['title'], $url, array('target' => SESSION_LINK_TARGET));
                 }
                 $html .= Display::tag('td', $exercices['title']);
                 // Exercise configuration show results or show only score
                 if ($exercices['results_disabled'] == 0 || $exercices['results_disabled'] == 2) {
                     //For graphics
                     $best_exercise_stats = Event::get_best_exercise_results_by_user($exercices['id'], $course_info['real_id'], $session_id);
                     $to_graph_exercise_result[$exercices['id']] = array('title' => $exercices['title'], 'data' => $best_exercise_stats);
                     $latest_attempt_url = '';
                     $best_score = $position = $percentage_score_result = '-';
                     $graph = $normal_graph = null;
                     // Getting best results
                     $best_score_data = ExerciseLib::get_best_attempt_in_course($exercices['id'], $course_info['real_id'], $session_id);
                     $best_score = '';
                     if (!empty($best_score_data)) {
                         $best_score = ExerciseLib::show_score($best_score_data['exe_result'], $best_score_data['exe_weighting']);
                     }
                     if ($attempts > 0) {
                         $exercise_stat = ExerciseLib::get_best_attempt_by_user(api_get_user_id(), $exercices['id'], $course_info['real_id'], $session_id);
                         if (!empty($exercise_stat)) {
                             //Always getting the BEST attempt
                             $score = $exercise_stat['exe_result'];
                             $weighting = $exercise_stat['exe_weighting'];
                             $exe_id = $exercise_stat['exe_id'];
                             $latest_attempt_url .= api_get_path(WEB_CODE_PATH) . 'exercice/result.php?id=' . $exe_id . '&cidReq=' . $course_info['code'] . '&show_headers=1&id_session=' . $session_id;
                             $percentage_score_result = Display::url(ExerciseLib::show_score($score, $weighting), $latest_attempt_url);
                             $my_score = 0;
                             if (!empty($weighting) && intval($weighting) != 0) {
                                 $my_score = $score / $weighting;
                             }
                             //@todo this function slows the page
                             $position = ExerciseLib::get_exercise_result_ranking($my_score, $exe_id, $exercices['id'], $course_info['code'], $session_id, $user_list);
                             $graph = self::generate_exercise_result_thumbnail_graph($to_graph_exercise_result[$exercices['id']]);
                             $normal_graph = self::generate_exercise_result_graph($to_graph_exercise_result[$exercices['id']]);
                         }
                     }
                     $html .= Display::div($normal_graph, array('id' => 'main_graph_' . $exercices['id'], 'class' => 'dialog', 'style' => 'display:none'));
                     if (empty($graph)) {
                         $graph = '-';
                     } else {
                         $graph = Display::url('<img src="' . $graph . '" >', $normal_graph, array('id' => $exercices['id'], 'class' => 'expand-image'));
                     }
                     $html .= Display::tag('td', $attempts, array('align' => 'center'));
                     $html .= Display::tag('td', $percentage_score_result, array('align' => 'center'));
                     $html .= Display::tag('td', $position, array('align' => 'center'));
                     $html .= Display::tag('td', $best_score, array('align' => 'center'));
                     $html .= Display::tag('td', $graph, array('align' => 'center'));
                     //$html .= Display::tag('td', $latest_attempt_url,       array('align'=>'center', 'width'=>'25'));
                 } else {
                     // Exercise configuration NO results
                     $html .= Display::tag('td', $attempts, array('align' => 'center'));
                     $html .= Display::tag('td', '-', array('align' => 'center'));
                     $html .= Display::tag('td', '-', array('align' => 'center'));
                     $html .= Display::tag('td', '-', array('align' => 'center'));
                     $html .= Display::tag('td', '-', array('align' => 'center'));
                 }
                 $html .= '</tr>';
             }
         } else {
             $html .= '<tr><td colspan="5" align="center">' . get_lang('NoEx') . '</td></tr>';
         }
         $html .= '</table>';
         // LP table results
         $html .= '<table class="data_table">';
         $html .= Display::tag('th', get_lang('Learnpaths'), array('class' => 'head', 'style' => 'color:#000'));
         $html .= Display::tag('th', get_lang('LatencyTimeSpent'), array('class' => 'head', 'style' => 'color:#000'));
         $html .= Display::tag('th', get_lang('Progress'), array('class' => 'head', 'style' => 'color:#000'));
         $html .= Display::tag('th', get_lang('Score'), array('class' => 'head', 'style' => 'color:#000'));
         $html .= Display::tag('th', get_lang('LastConnexion'), array('class' => 'head', 'style' => 'color:#000'));
         $html .= '</tr>';
         $list = new LearnpathList(api_get_user_id(), $course_info['code'], $session_id, 'publicated_on ASC', true);
         $lp_list = $list->get_flat_list();
         if (!empty($lp_list) > 0) {
             foreach ($lp_list as $lp_id => $learnpath) {
                 $progress = Tracking::get_avg_student_progress($user_id, $course, array($lp_id), $session_id);
                 $last_connection_in_lp = Tracking::get_last_connection_time_in_lp($user_id, $course, $lp_id, $session_id);
                 $time_spent_in_lp = Tracking::get_time_spent_in_lp($user_id, $course, array($lp_id), $session_id);
                 $percentage_score = Tracking::get_avg_student_score($user_id, $course, array($lp_id), $session_id);
                 if (is_numeric($percentage_score)) {
                     $percentage_score = $percentage_score . '%';
                 } else {
                     $percentage_score = '0%';
                 }
                 $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
                 $html .= '<tr class="row_even">';
                 $url = api_get_path(WEB_CODE_PATH) . "newscorm/lp_controller.php?cidReq={$course_code}&id_session={$session_id}&lp_id={$lp_id}&action=view";
                 if ($learnpath['lp_visibility'] == 0) {
                     $html .= Display::tag('td', $learnpath['lp_name']);
                 } else {
                     $html .= Display::tag('td', Display::url($learnpath['lp_name'], $url, array('target' => SESSION_LINK_TARGET)));
                 }
                 $html .= Display::tag('td', $time_spent_in_lp, array('align' => 'center'));
                 if (is_numeric($progress)) {
                     $progress = $progress . '%';
                 }
                 $html .= Display::tag('td', $progress, array('align' => 'center'));
                 $html .= Display::tag('td', $percentage_score);
                 $last_connection = '-';
                 if (!empty($last_connection_in_lp)) {
                     $last_connection = api_convert_and_format_date($last_connection_in_lp, DATE_TIME_FORMAT_LONG);
                 }
                 $html .= Display::tag('td', $last_connection, array('align' => 'center', 'width' => '180px'));
                 $html .= "</tr>";
             }
         } else {
             $html .= '<tr>
                     <td colspan="4" align="center">
                         ' . get_lang('NoLearnpath') . '
                     </td>
                   </tr>';
         }
         $html .= '</table>';
     }
     return $html;
 }
示例#9
0
}
// no support for hot potatoes
if ($type == LINK_HOTPOTATOES) {
    $doExerciseUrl = api_get_path(WEB_CODE_PATH) . 'exercice/exercice.php?session_id=' . $session_id . '&cidReq=' . Security::remove_XSS($cidReq);
}
if (isset($_GET['doexercise'])) {
    header('Location: ' . $doExerciseUrl);
    exit;
} else {
    $url = api_get_path(WEB_CODE_PATH) . 'exercice/overview.php?session_id=' . $session_id . '&cidReq=' . Security::remove_XSS($cidReq);
    if (isset($_GET['gradebook'])) {
        $url .= '&gradebook=view&exerciseId=' . intval($_GET['exerciseId']);
        // Check if exercise is inserted inside a LP, if that's the case
        $exerciseId = $_GET['exerciseId'];
        $exercise = new Exercise();
        $exercise->read($exerciseId);
        if (!empty($exercise->id)) {
            if ($exercise->exercise_was_added_in_lp) {
                if (!empty($exercise->lpList)) {
                    $count = count($exercise->lpList);
                    if ($count == 1) {
                        // If the exercise was added once redirect to the LP
                        $firstLp = current($exercise->lpList);
                        if (isset($firstLp['lp_id'])) {
                            $url = api_get_path(WEB_CODE_PATH) . 'newscorm/lp_controller.php?' . api_get_cidreq() . '&lp_id=' . $firstLp['lp_id'] . '&action=view&isStudentView=true';
                        }
                    } else {
                        // If the exercise was added multiple times show the LP list
                        $url = api_get_path(WEB_CODE_PATH) . 'newscorm/lp_controller.php?' . api_get_cidreq() . '&action=list';
                    }
                }
示例#10
0
// get index row - i.e. user click to sort $sord = $_GET['sord'];
// get the direction
if (!$sidx) {
    $sidx = 1;
}
//2. Selecting the count FIRST
//@todo rework this
switch ($action) {
    case 'get_user_list_plugin_widescale':
        $count = UserManager::get_user_data(null, null, null, null, true);
        break;
    case 'get_question_list':
        require_once api_get_path(SYS_CODE_PATH) . 'exercice/exercise.class.php';
        $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
        $exercise = new Exercise(api_get_course_int_id());
        $exercise->read($exerciseId, false);
        $count = $exercise->getQuestionCount();
        break;
    case 'get_group_reporting':
        $course_id = isset($_REQUEST['course_id']) ? $_REQUEST['course_id'] : null;
        $group_id = isset($_REQUEST['gidReq']) ? $_REQUEST['gidReq'] : null;
        $count = Tracking::get_group_reporting($course_id, $group_id, 'count');
        break;
    case 'get_user_course_report_resumed':
        $count = CourseManager::get_count_user_list_from_course_code(true, 'ruc');
        break;
    case 'get_user_course_report':
        $count = CourseManager::get_count_user_list_from_course_code(false);
        break;
    case 'get_course_exercise_medias':
        $course_id = api_get_course_int_id();
/**
 * 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/exercise.class.php';
    require_once '../exercice/question.class.php';
    require_once '../exercice/answer.class.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 iid='{$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++;
        }
    }
    $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>";
    }
    // 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() {\n        alert('Test evaluated.');\n        }\n        ";
    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 = "\n        <p>{$exerciseDescription}</p>\n        <table width='100%' border='0' cellpadding='1' cellspacing='0'>\n         <form method='post' action=''><input type=\"hidden\" name=\"SQMSESSID\" value=\"36812c2dea7d8d6e708d5e6a2f09b0b9\" />\n         <input type='hidden' name='formSent' value='1' />\n         <input type='hidden' name='exerciseType' value='" . $exerciseType . "' />\n         <input type='hidden' name='questionNum' value='" . $questionNum . "' />\n         <input type='hidden' name='nbrQuestions' value='" . $nbrQuestions . "' />\n         <tr>\n          <td>\n          <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(). This basically displays the question in a table.
        $question_obj = Question::read($questionId);
        $test .= $objExercise->showQuestion($question_obj, 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;
}
示例#12
0
 } else {
     $headers = array(array('name' => get_lang('ExerciseName')), array('name' => get_lang('Status')));
 }
 $header_list = '';
 foreach ($headers as $header) {
     $params = isset($header['params']) ? $header['params'] : null;
     $header_list .= Display::tag('th', $header['name'], $params);
 }
 echo Display::tag('tr', $header_list);
 $autolaunch_setting_on = api_get_course_setting('enable_exercise_auto_launch') == 1;
 $count = 0;
 if (!empty($exercise_list)) {
     foreach ($exercise_list as $row) {
         $my_exercise_id = $row['iid'];
         $exercise_obj = new Exercise();
         $exercise_obj->read($my_exercise_id, false);
         $locked = $exercise_obj->is_gradebook_locked;
         //echo '<div  id="tabs-'.$i.'">';
         $i++;
         // Validation when belongs to a session
         $session_img = api_get_session_image($row['session_id'], $_user['status']);
         $time_limits = false;
         if ($row['start_time'] != '0000-00-00 00:00:00' || $row['end_time'] != '0000-00-00 00:00:00') {
             $time_limits = true;
         }
         if ($time_limits) {
             // check if start time
             $start_time = false;
             if ($row['start_time'] != '0000-00-00 00:00:00') {
                 $start_time = api_strtotime($row['start_time'], 'UTC');
             }
示例#13
0
 /**
  * Build the Quizzes
  */
 public function build_quizzes($session_id = 0, $course_code = '', $with_base_content = false, $id_list = array())
 {
     $course_info = api_get_course_info($course_code);
     $table_qui = Database::get_course_table(TABLE_QUIZ_TEST);
     $table_rel = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
     $table_doc = Database::get_course_table(TABLE_DOCUMENT);
     $course_id = $course_info['real_id'];
     if (!empty($course_code) && !empty($session_id)) {
         $session_id = intval($session_id);
         if ($with_base_content) {
             $session_condition = api_get_session_condition($session_id, true, true);
         } else {
             $session_condition = api_get_session_condition($session_id, true);
         }
         $sql = "SELECT * FROM {$table_qui} WHERE c_id = {$course_id} AND active >=0 {$session_condition}";
         //select only quizzes with active = 0 or 1 (not -1 which is for deleted quizzes)
     } else {
         $sql = "SELECT * FROM {$table_qui} WHERE c_id = {$course_id} AND active >=0 AND session_id = 0";
         //select only quizzes with active = 0 or 1 (not -1 which is for deleted quizzes)
     }
     $db_result = Database::query($sql);
     while ($obj = Database::fetch_object($db_result)) {
         if (strlen($obj->sound) > 0) {
             $sql = "SELECT id FROM {$table_doc} WHERE c_id = {$course_id} AND path = '/audio/" . $obj->sound . "'";
             $res = Database::query($sql);
             $doc = Database::fetch_object($res);
             $obj->sound = $doc->id;
         }
         $exercise_obj = new Exercise($course_id);
         $exercise_obj->read($obj->iid);
         $categories = $exercise_obj->get_categories_with_name_in_exercise();
         $obj->categories = $categories;
         $quiz = new Quiz($obj);
         $sql = 'SELECT * FROM ' . $table_rel . ' WHERE c_id = ' . $course_id . ' AND exercice_id = ' . $obj->iid;
         $db_result2 = Database::query($sql);
         while ($obj2 = Database::fetch_object($db_result2)) {
             $quiz->add_question($obj2->question_id, $obj2->question_order);
         }
         $this->course->add_resource($quiz);
     }
     if (!empty($course_code)) {
         $this->build_quiz_questions($course_code);
     } else {
         $this->build_quiz_questions();
     }
 }
示例#14
0
    $name = 'qti2_export_' . $qid . '.zip';
    DocumentManager::file_send_for_download($temp_zip_file, true, $name);
    unlink($temp_zip_file);
    unlink($temp_xml_file);
    rmdir($temp_zip_dir);
    //DocumentManager::string_send_for_download($export,true,'qti2export_q'.$_GET['questionId'].'.xml');
    exit;
    //otherwise following clicks may become buggy
}
// Initializes the Exercise object.
if (!is_object($objExercise)) {
    // construction of the Exercise object
    $objExercise = new Exercise();
    // creation of a new exercise if wrong or not specified exercise ID
    if ($exerciseId) {
        $objExercise->read($exerciseId, true);
    }
    // saves the object into the session
    Session::write('objExercise', $objExercise);
}
if ($objExercise->fastEdition) {
    $htmlHeadXtra[] = api_get_jqgrid_js();
}
$nbrQuestions = $objExercise->getQuestionCount();
// Initializes the Question object
if ($editQuestion || $newQuestion || $modifyQuestion || $modifyAnswers) {
    if ($editQuestion || $newQuestion) {
        // reads question data
        if ($editQuestion) {
            // question not found
            if (!($objQuestion = Question::read($editQuestion, null, $objExercise))) {
示例#15
0
 /**
  * Returns the exercise result
  * @param 	int		attempt id
  * @return 	float 	exercise result
  */
 public function get_exercise_result($exe_id)
 {
     $result = array();
     $track_exercise_info = ExerciseLib::get_exercise_track_exercise_info($exe_id);
     if (!empty($track_exercise_info)) {
         $totalScore = 0;
         $objExercise = new Exercise();
         $objExercise->read($track_exercise_info['exe_exo_id']);
         if (!empty($track_exercise_info['data_tracking'])) {
             $question_list = explode(',', $track_exercise_info['data_tracking']);
         }
         foreach ($question_list as $questionId) {
             $question_result = $objExercise->manage_answer($exe_id, $questionId, '', 'exercise_show', array(), false, true, false, $objExercise->selectPropagateNeg());
             $totalScore += $question_result['score'];
         }
         if ($objExercise->selectPropagateNeg() == 0 && $totalScore < 0) {
             $totalScore = 0;
         }
         $result = array('score' => $totalScore, 'weight' => $track_exercise_info['exe_weighting']);
     }
     return $result;
 }
示例#16
0
        /**
         * Exports an exercise as a SCO.
         * This method is intended to be called from the prepare method.
         *
         * @note There's a lot of nearly cut-and-paste from exercise.lib.php here
         *      because of some little differences...
         *      Perhaps something that could be refactorised ?
         *
         * @see prepare
         * @param $quizId The quiz
         * @param $raw_to_pass The needed score to attain
         * @return False on error, True if everything went well.
         * @author Thanos Kyritsis <*****@*****.**>
         * @author  Amand Tihon <*****@*****.**>
         */
        function prepareQuiz($quizId, $raw_to_pass = 50) {
            global $langQuestion, $langOk, $langScore, $claro_stylesheet, $clarolineRepositorySys;
            global $charset, $langExerciseDone;
            // those two variables are needed by display_attached_file()
            global $attachedFilePathWeb;
            global $attachedFilePathSys;
            $attachedFilePathWeb = 'Exercises';
            $attachedFilePathSys = $this->destDir . '/Exercises';

// Generate standard page header
            $pageHeader = '<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '">
<meta http-equiv="expires" content="Tue, 05 DEC 2000 07:00:00 GMT">
<meta http-equiv="Pragma" content="no-cache">
<link rel="stylesheet" type="text/css" href="bootstrap-custom.css" />
<link rel="stylesheet" type="text/css" href="' . $claro_stylesheet . '" media="screen, projection, tv" />
<script language="javascript" type="text/javascript" src="APIWrapper.js"></script>
<script language="javascript" type="text/javascript" src="scores.js"></script>
' . "\n";


            $pageBody = '<body onload="loadPage()">
    <div id="claroBody"><form id="quiz">
    <table class="table-default"><tr><td>' . "\n";


            // read the exercise
            $quiz = new Exercise();
            if (!$quiz->read($quizId)) {
                $this->error[] = $GLOBALS['langErrorLoadingExercise'];
                return false;
            }

            // Get the question list
            $questionList = $quiz->selectQuestionList();
            $questionCount = $quiz->selectNbrQuestions();

            // Keep track of raw scores (ponderation) for each question
            $questionPonderationList = array();

            // Keep track of correct texts for fill-in type questions
            $fillAnswerList = array();

            // Counter used to generate the elements' id. Incremented after every <input> or <select>
            $idCounter = 0;

            // Display each question
            $questionCount = 0;
            foreach ($questionList as $questionId) {
                // Update question number
                $questionCount++;

                // read the question, abort on error
                $question = new Question();
                if (!$question->read($questionId)) {
                    $this->error[] = $GLOBALS['langErrorLoadingQuestion'];
                    return false;
                }
                $qtype = $question->selectType();
                $qtitle = $question->selectTitle();
                $qdescription = $question->selectDescription();
                $questionPonderationList[$questionId] = $question->selectWeighting();

                // Generic display, valid for all kind of question
                $pageBody .= '<table class="table-default">
                <tr><th valign="top" colspan="2">' . $langQuestion . ' ' . $questionCount . '</th></tr>
                <tfoot>
                        <tr><td valign="top" colspan="2">' . $qtitle . '</td></tr>
                        <tr><td valign="top" colspan="2"><i>' . parse_user_text($qdescription) . '</i></td></tr>' . "\n";

                // Attached file, if it exists.
                //$attachedFile = $question->selectAttachedFile();
                if (!empty($attachedFile)) {
                    // copy the attached file
                    if (!claro_copy_file($this->srcDirExercise . '/' . $attachedFile, $this->destDir . '/Exercises')) {
                        $this->error[] = $GLOBALS['langErrorCopyAttachedFile'] . $attachedFile;
                        return false;
                    }

                    // Ok, if it was an mp3, we need to copy the flash mp3-player too.
                    $extension = substr(strrchr($attachedFile, '.'), 1);
                    if ($extension == 'mp3') {
                        $this->mp3Found = true;
                    }

                    $pageBody .= '<tr><td colspan="2">' . display_attached_file($attachedFile) . '</td></tr>' . "\n";
                }

                /*
                 * Display the possible answers
                 */

                $answer = new Answer($questionId);
                $answerCount = $answer->selectNbrAnswers();

                // Used for matching:
                $letterCounter = 'A';
                $choiceCounter = 1;
                $Select = array();

                for ($answerId = 1; $answerId <= $answerCount; $answerId++) {
                    $answerText = $answer->selectAnswer($answerId);
                    $answerCorrect = $answer->isCorrect($answerId);

                    // Unique answer
                    if ($qtype == UNIQUE_ANSWER || $qtype == TRUE_FALSE) {
                        // Construct the identifier
                        $htmlQuestionId = 'unique_' . $questionCount . '_x';

                        $pageBody .= '<tr><td width="5%" align="center">
                        <input type="radio" name="' . $htmlQuestionId . '"
                        id="scorm_' . $idCounter . '"
                        value="' . $answer->selectWeighting($answerId) . '"></td>
                    <td width="95%"><label for="scorm_' . $idCounter . '">' . $answerText . '</label>
                    </td></tr>';

                        $idCounter++;
                    }
                    // Multiple answers
                    elseif ($qtype == MULTIPLE_ANSWER) {
                        // Construct the identifier
                        $htmlQuestionId = 'multiple_' . $questionCount . '_' . $answerId;

                        // Compute the score modifier if this answer is checked
                        $raw = $answer->selectWeighting($answerId);

                        $pageBody .= '<tr><td width="5%" align="center">
                        <input type="checkbox" name="' . $htmlQuestionId . '"
                        id="scorm_' . $idCounter . '"
                        value="' . $raw . '"></td>
                    <td width="95%"><label for="scorm_' . $idCounter . '">' . $answerText . '</label>
                    </td></tr>';

                        $idCounter++;
                    }
                    // Fill in blanks
                    elseif ($qtype == FILL_IN_BLANKS || $qtype == FILL_IN_BLANKS_TOLERANT) {
                        $pageBody .= '<tr><td colspan="2">';

                        // We must split the text, to be able to treat each input independently
                        // separate the text and the scorings
                        $explodedAnswer = explode('::', $answerText);
                        $phrase = (isset($explodedAnswer[0])) ? $explodedAnswer[0] : '';
                        $weighting = (isset($explodedAnswer[1])) ? $explodedAnswer[1] : '';
                        $fillType = (!empty($explodedAnswer[2])) ? $explodedAnswer[2] : 1;
                        // default value if value is invalid
                        if ($fillType != TEXTFIELD_FILL && $fillType != LISTBOX_FILL) {
                            $fillType = TEXTFIELD_FILL;
                        }
                        $wrongAnswers = (!empty($explodedAnswer[3])) ? explode('[', $explodedAnswer[3]) : array();
                        // get the scorings as a list
                        $fillScoreList = explode(',', $weighting);
                        $fillScoreCounter = 0;
                        //listbox
                        if ($fillType == LISTBOX_FILL) {
                            // get the list of propositions (good and wrong) to display in lists
                            // add wrongAnswers in the list
                            $answerList = $wrongAnswers;
                            // add good answers in the list
                            // we save the answer because it will be modified
                            $temp = $phrase;
                            while (1) {
                                // quits the loop if there are no more blanks
                                if (($pos = strpos($temp, '[')) === false) {
                                    break;
                                }
                                // removes characters till '['
                                $temp = substr($temp, $pos + 1);
                                // quits the loop if there are no more blanks
                                if (($pos = strpos($temp, ']')) === false) {
                                    break;
                                }
                                // stores the found blank into the array
                                $answerList[] = substr($temp, 0, $pos);
                                // removes the character ']'
                                $temp = substr($temp, $pos + 1);
                            }
                            // alphabetical sort of the array
                            natcasesort($answerList);
                        }
                        // Split after each blank
                        $responsePart = explode(']', $phrase);
                        $acount = 0;
                        foreach ($responsePart as $part) {
                            // Split between text and (possible) blank
                            if (strpos($part, '[') !== false) {
                                list($rawtext, $blankText) = explode('[', $part);
                            } else {
                                $rawtext = $part;
                                $blankText = "";
                            }

                            $pageBody .= $rawtext;

                            // If there's a blank to fill-in after the text (this is usually not the case at the end)
                            if (!empty($blankText)) {
                                // Build the element's name
                                $name = 'fill_' . $questionCount . '_' . $acount;
                                // Keep track of the correspondance between element's name and correct value + scoring
                                $fillAnswerList[$name] = array($blankText, $fillScoreList[$fillScoreCounter]);
                                if ($fillType == LISTBOX_FILL) {// listbox
                                    $pageBody .= '<select name="' . $name . '" id="scorm_' . $idCounter . '">' . "\n"
                                            . '<option value="">&nbsp;</option>';

                                    foreach ($answerList as $answer) {
                                        $pageBody .= '<option value="' . htmlspecialchars($answer) . '">' . $answer . '</option>' . "\n";
                                    }

                                    $pageBody .= '</select>' . "\n";
                                } else {
                                    $pageBody .= '<input type="text" name="' . $name . '" size="10" id="scorm_' . $idCounter . '">';
                                }
                                $fillScoreCounter++;
                                $idCounter++;
                            }
                            $acount++;
                        }
                        $pageBody .= '</td></tr>' . "\n";
                    }
                    // Matching
                    elseif ($qtype == MATCHING) {
                        if (!$answer->isCorrect($answerId)) {
                            // Add the option as a possible answer.
                            $Select[$answerId] = $answerText;
                        } else {
                            $pageBody .= '<tr><td colspan="2">
                        <table border="0" cellpadding="0" cellspacing="0" width="99%">
                        <tr>
                            <td width="40%" valign="top"><b>' . $choiceCounter . '.</b> ' . $answerText . '</td>
                            <td width="20%" valign="center">&nbsp;<select name="matching_' . $questionCount . '_' .
                                    $answerId . '" id="scorm_' . $idCounter . '">
                            <option value="0">--</option>';

                            $idCounter++;

                            // fills the list-box
                            $letter = 'A';
                            foreach ($Select as $key => $val) {
                                $scoreModifier = ( $key == $answer->isCorrect($answerId) ) ? $answer->selectWeighting($answerId) : 0;
                                $pageBody .= '<option value="' . $scoreModifier . '">' . $letter++ .
                                        '</option>';
                            }

                            $pageBody .= '</select></td><td width="40%" valign="top">';
                            if (isset($Select[$choiceCounter])) {
                                $pageBody .= '<b>' . $letterCounter . '.</b> ' . $Select[$choiceCounter];
                            }
                            $pageBody .= '&nbsp;</td></tr></table></td></tr>' . "\n";

                            // Done with this one
                            $letterCounter++;
                            $choiceCounter++;

                            // If the left side has been completely displayed :
                            if ($answerId == $answerCount) {
                                // Add all possibly remaining answers to the right
                                while (isset($Select[$choiceCounter])) {
                                    $pageBody .= '<tr><td colspan="2">
                                <table border="0" cellpadding="0" cellspacing="0" width="99%">
                                <tr>
                                    <td width="40%">&nbsp;</td>
                                    <td width="20%">&nbsp;</td>
                                    <td width="40%"><b>' . $letterCounter . '.</b> ' . $Select[$choiceCounter] . '</td>
                                </tr>
                                </table>
                                </td></tr>' . "\n";

                                    $letterCounter++;
                                    $choiceCounter++;
                                } // end while
                            } // end if
                        } // else
                    } // end if (MATCHING)
                } // end for each answer
                // End of the question
                $pageBody .= '</tfoot></table>' . "\n\n";
            } // foreach($questionList as $questionId)
            // No more questions, add the button.
            $pageEnd = '</td></tr>
            <tr>
                <td align="center"><br><input class="btn btn-primary" type="button" value="' . $langOk . '" onClick="calcScore()"></td>
            </tr>
            </table>
            </form>
            </div></body></html>' . "\n";

            /* Generate the javascript that'll calculate the score
             * We have the following variables to help us :
             * $idCounter : number of elements to check. their id are "scorm_XY"
             * $raw_to_pass : score (on 100) needed to pass the quiz
             * $fillAnswerList : a list of arrays (text, score) indexed on <input>'s names
             *
             */
            $pageHeader .= '
<script type="text/javascript" language="javascript">
    var raw_to_pass = '******';
    var weighting = ' . array_sum($questionPonderationList) . ';
    var rawScore;
    var scoreCommited = false;
    var showScore = true;
    var fillAnswerList = new Array();' . "\n";

            // Add the data for fillAnswerList
            foreach ($fillAnswerList as $key => $val) {
                $pageHeader .= "    fillAnswerList['" . $key . "'] = new Array('" . $val[0] . "', '" . $val[1] . "');\n";
            }

            // This is the actual code present in every exported exercise.
            $pageHeader .= '

    function calcScore()
    {
		if( !scoreCommited )
		{
	        rawScore = CalculateRawScore(document, ' . $idCounter . ', fillAnswerList);
	        var score = Math.max(Math.round(rawScore * 100 / weighting), 0);
	        var oldScore = doGetValue("cmi.score.raw");

	        doSetValue("cmi.score.max", weighting);
	        doSetValue("cmi.score.min", 0);

	        computeTime();

	        if (score > oldScore) // Update only if score is better than the previous time.
	        {
	            doSetValue("cmi.score.raw", rawScore);
	        }

			var oldStatus = doGetValue( "cmi.success_status" )
			if (score >= raw_to_pass)
			{
				doSetValue("cmi.success_status", "passed");
			}
			else if (oldStatus != "passed" ) // If passed once, never mark it as failed.
			{
				doSetValue("cmi.success_status", "failed");
			}

	        doCommit();
	        doTerminate();
	        scoreCommited = true;
	        if(showScore) alert(\'' . clean_str_for_javascript($langScore) . ' :\n\' + rawScore + \'/\' + weighting + \'\n\' + \'' . clean_str_for_javascript($langExerciseDone) . '\');
		}
    }

</script>
';

            // Construct the HTML file and save it.
            $filename = "quiz_" . $quizId . ".html";

            $pageContent = $pageHeader
                    . $pageBody
                    . $pageEnd;

            if (!$f = fopen($this->destDir . '/' . $filename, 'w')) {
                $this->error[] = $GLOBALS['langErrorCreatingFile'] . $filename;
                return false;
            }
            fwrite($f, $pageContent);
            fclose($f);

            // Went well.
            return True;
        }
示例#17
0
// generate default content
$form->addElement('checkbox', 'is_content', null, get_lang('DefaultContent'), array('checked' => true));
// the submit button
$form->addElement('style_submit_button', 'SubmitCreateQuestion', get_lang('CreateQuestion'), 'class="add"');
// setting the rules
$form->addRule('exercice', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('exercice', get_lang('YouHaveToSelectATest'), 'numeric');
$form->registerRule('validquestiontype', 'callback', 'check_question_type');
$form->addRule('question_type_hidden', get_lang('InvalidQuestionType'), 'validquestiontype');
if ($form->validate()) {
    $values = $form->exportValues();
    $answer_type = $values['question_type_hidden'];
    // check feedback_type from current exercise for type of question delineation
    $exercise_id = intval($values['exercice']);
    $exercise = new Exercise();
    $exercise->read($exercise_id);
    $feedback_type = $exercise->feedback_type;
    // if question type does not belong to self-evaluation (immediate feedback) it'll send an error
    if ($answer_type == HOT_SPOT_DELINEATION && $feedback_type != 1 || $feedback_type == 1 && ($answer_type != HOT_SPOT_DELINEATION && $answer_type != UNIQUE_ANSWER)) {
        header('Location: question_create.php?' . api_get_cidreq() . '&error=true');
        exit;
    }
    header('Location: admin.php?exerciseId=' . $values['exercice'] . '&newQuestion=yes&isContent=' . $values['is_content'] . '&answerType=' . $answer_type);
    exit;
} else {
    // header
    Display::display_header($nameTools);
    echo '<div class="actions">';
    echo '<a href="exercice.php?show=test">' . Display::return_icon('back.png', get_lang('BackToExercisesList'), '', ICON_SIZE_MEDIUM) . '</a>';
    echo '</div>';
    // displaying the form
示例#18
0
 *                  e-mail: info@openeclass.org
 * ======================================================================== */
include 'exercise.class.php';
include 'question.class.php';
include 'answer.class.php';
$require_current_course = TRUE;
include '../../include/baseTheme.php';
require_once 'imsqtilib.php';
$head_content .= "\n<script>\n  \$(function() {\n    \$('.warnLink').click( function(e){\n          var modidyAllLink = \$(this).attr('href');\n          var modifyOneLink = modidyAllLink.concat('&clone=true');\n          \$('a#modifyAll').attr('href', modidyAllLink);\n          \$('a#modifyOne').attr('href', modifyOneLink); \n    });\n  });\n</script>\n";
$tool_content .= "<div id='dialog' style='display:none;'>{$langUsedInSeveralExercises}</div>";
$pageName = $langQuestionPool;
$navigation[] = array("url" => "index.php?course={$course_code}", "name" => $langExercices);
if (isset($_GET['fromExercise'])) {
    $objExercise = new Exercise();
    $fromExercise = intval($_GET['fromExercise']);
    $objExercise->read($fromExercise);
    $navigation[] = array("url" => "admin.php?course={$course_code}&amp;exerciseId={$fromExercise}", "name" => $langExerciseManagement);
}
if (isset($_GET['exerciseId'])) {
    $exerciseId = intval($_GET['exerciseId']);
}
if (isset($_GET['difficultyId'])) {
    $difficultyId = intval($_GET['difficultyId']);
}
if (isset($_GET['categoryId'])) {
    $categoryId = intval($_GET['categoryId']);
}
// maximum number of questions on a same page
define('QUESTIONS_PER_PAGE', 15);
if (!isset($_GET['page'])) {
    $page = 0;
 /**
  * Restore Quiz
  */
 function restore_quizzes($session_id = 0, $respect_base_content = false)
 {
     if ($this->course->has_resources(RESOURCE_QUIZ)) {
         $table_qui = Database::get_course_table(TABLE_QUIZ_TEST);
         $table_rel = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
         $table_doc = Database::get_course_table(TABLE_DOCUMENT);
         $resources = $this->course->resources;
         foreach ($resources[RESOURCE_QUIZ] as $id => $quiz) {
             if (isset($quiz->obj)) {
                 //For new imports
                 $quiz = $quiz->obj;
             } else {
                 //For backward compatibility
                 $quiz->obj = $quiz;
             }
             $doc = '';
             if (!empty($quiz->sound)) {
                 if (isset($this->course->resources[RESOURCE_DOCUMENT][$quiz->sound]) && $this->course->resources[RESOURCE_DOCUMENT][$quiz->sound]->is_restored()) {
                     $sql = "SELECT path FROM " . $table_doc . " WHERE c_id = " . $this->destination_course_id . "  AND id = " . $resources[RESOURCE_DOCUMENT][$quiz->sound]->destination_id;
                     $doc = Database::query($sql);
                     $doc = Database::fetch_object($doc);
                     $doc = str_replace('/audio/', '', $doc->path);
                 }
             }
             if ($id != -1) {
                 // check resources inside html from fckeditor tool and copy correct urls into recipient course
                 $quiz->description = DocumentManager::replace_urls_inside_content_html_from_copy_course($quiz->description, $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']);
                 global $_custom;
                 if (isset($_custom['exercises_clean_dates_when_restoring']) && $_custom['exercises_clean_dates_when_restoring']) {
                     $quiz->start_time = null;
                     $quiz->end_time = null;
                 }
                 $params = array('c_id' => $this->destination_course_id, 'title' => self::DBUTF8($quiz->title), 'description' => self::DBUTF8($quiz->description), 'type' => isset($quiz->quiz_type) ? $quiz->quiz_type : $quiz->type, 'random' => $quiz->random, 'active' => $quiz->active, 'sound' => self::DBUTF8($doc), 'max_attempt' => (int) $quiz->max_attempt, 'results_disabled' => (int) $quiz->results_disabled, 'access_condition' => $quiz->access_condition, 'start_time' => $quiz->start_time, 'pass_percentage' => $quiz->pass_percentage, 'end_time' => $quiz->end_time, 'feedback_type' => (int) $quiz->feedback_type, 'random_answers' => (int) $quiz->random_answers, 'random_by_category' => $quiz->random_by_category, 'review_answers' => $quiz->review_answers, 'propagate_neg' => $quiz->propagate_neg, 'text_when_finished' => $quiz->text_when_finished, 'expired_time' => (int) $quiz->expired_time, 'end_button' => (int) $quiz->end_button);
                 if ($respect_base_content) {
                     $my_session_id = $quiz->session_id;
                     if (!empty($quiz->session_id)) {
                         $my_session_id = $session_id;
                     }
                     $params['session_id'] = $my_session_id;
                 } else {
                     if (!empty($session_id)) {
                         $session_id = intval($session_id);
                         $params['session_id'] = $session_id;
                     }
                 }
                 $new_id = Database::insert($table_qui, $params);
             } else {
                 // $id = -1 identifies the fictionary test for collecting orphan questions. We do not store it in the database.
                 $new_id = -1;
             }
             if ($new_id && $new_id != -1) {
                 // Updates the question position
                 $exercise = new Exercise($this->destination_course_id);
                 $exercise->read($new_id);
                 $exercise->addExerciseToOrderTable();
                 $this->course->resources[RESOURCE_QUIZ][$id]->obj->destination_id = $new_id;
                 $order = 0;
                 if (!empty($quiz->question_ids)) {
                     foreach ($quiz->question_ids as $index => $question_id) {
                         $qid = $this->restore_quiz_question($question_id);
                         $question_order = $quiz->question_orders[$index] ? $quiz->question_orders[$index] : ++$order;
                         $sql = "INSERT IGNORE INTO " . $table_rel . " SET c_id = " . $this->destination_course_id . ", question_id = " . $qid . ", exercice_id = " . $new_id . ", question_order = " . $question_order;
                         Database::query($sql);
                     }
                 }
                 if (isset($quiz->categorie) && $quiz->categories) {
                     $exercise_obj = new Exercise($this->destination_course_id);
                     $exercise_obj->read($new_id);
                     $cats = array();
                     foreach ($quiz->categories as $cat) {
                         $cat_from_db = new Testcategory($cat['category_id']);
                         /*$cat_from_db = new Testcategory($cat['category_id']);
                           if ($cat_from_db && $cat_from_db->title == $cat['title']) {
                               echo '1';
                               //use the same id
                               $cats[$cat_from_db->id] = $cat['count_questions'];
                           } else {*/
                         $cat_from_db = $cat_from_db->get_category_by_title($cat['title'], $this->destination_course_id);
                         if (empty($cat_from_db)) {
                             //Create a new category in this portal
                             if ($cat['category_id'] == 0) {
                                 $category_c_id = 0;
                             } else {
                                 $category_c_id = $this->destination_course_id;
                             }
                             $new_cat = new Testcategory(null, $cat['title'], $cat['description'], null, 'simple', $category_c_id);
                             $new_cat_id = $new_cat->addCategoryInBDD();
                             $cats[$new_cat_id] = $cat['count_questions'];
                         } else {
                             $cats[$cat_from_db['iid']] = $cat['count_questions'];
                         }
                         //}
                     }
                     $exercise_obj->save_categories_in_exercise($cats);
                 }
             }
         }
     }
 }
示例#20
0
/**
 * @todo Fix tool_visible_by_default_at_creation labels
 * @todo Add sessionId parameter to avoid using context
 *
 * @param int $item_id
 * @param int $tool_id
 * @param int $group_id
 * @param array $courseInfo
 */
function api_set_default_visibility($item_id, $tool_id, $group_id = 0, $courseInfo = array())
{
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
    $courseId = $courseInfo['real_id'];
    $courseCode = $courseInfo['code'];
    $original_tool_id = $tool_id;
    switch ($tool_id) {
        case TOOL_LINK:
        case TOOL_LINK_CATEGORY:
            $tool_id = 'links';
            break;
        case TOOL_DOCUMENT:
            $tool_id = 'documents';
            break;
        case TOOL_LEARNPATH:
            $tool_id = 'learning';
            break;
        case TOOL_ANNOUNCEMENT:
            $tool_id = 'announcements';
            break;
        case TOOL_FORUM:
        case TOOL_FORUM_CATEGORY:
        case TOOL_FORUM_THREAD:
            $tool_id = 'forums';
            break;
        case TOOL_QUIZ:
            $tool_id = 'quiz';
            break;
    }
    $setting = api_get_setting('document.tool_visible_by_default_at_creation');
    $visibility = 'invisible';
    if (empty($group_id)) {
        $group_id = api_get_group_id();
    }
    if (isset($setting[$tool_id])) {
        if ($setting[$tool_id] == 'true') {
            $visibility = 'visible';
        }
    }
    // Read the portal and course default visibility
    if ($tool_id == 'documents') {
        $visibility = DocumentManager::getDocumentDefaultVisibility($courseCode);
    }
    api_item_property_update($courseInfo, $original_tool_id, $item_id, $visibility, api_get_user_id(), $group_id, null, null, null, api_get_session_id());
    // Fixes default visibility for tests
    switch ($original_tool_id) {
        case TOOL_QUIZ:
            $objExerciseTmp = new Exercise($courseId);
            $objExerciseTmp->read($item_id);
            if ($visibility == 'visible') {
                $objExerciseTmp->enable();
                $objExerciseTmp->save();
            } else {
                $objExerciseTmp->disable();
                $objExerciseTmp->save();
            }
            break;
    }
}
示例#21
0
/**
 * Send a complete exercise in IMS/QTI format, from its ID
 *
 * @param int $exerciseId The exercise to export
 * @param boolean $standalone Wether it should include XML tag and DTD line.
 * @return The XML as a string, or an empty string if there's no exercise with given ID.
 */
function export_exercise_to_qti($exerciseId, $standalone = true)
{
    $exercise = new Exercise();
    if (!$exercise->read($exerciseId)) {
        return '';
    }
    $ims = new ImsSection($exercise);
    $xml = $ims->export($standalone);
    return $xml;
}
示例#22
0
 /**
  * Direct course link see #5299
  *
  * You can send to your students an URL like this
  * http://chamilodev.beeznest.com/main/auth/inscription.php?c=ABC&e=3
  * Where "c" is the course code and "e" is the exercise Id, after a successful
  * registration the user will be sent to the course or exercise
  *
  */
 public static function redirectToCourse($form_data)
 {
     $course_code_redirect = Session::read('course_redirect');
     $_user = api_get_user_info();
     $user_id = api_get_user_id();
     if (!empty($course_code_redirect)) {
         $course_info = api_get_course_info($course_code_redirect);
         if (!empty($course_info)) {
             if (in_array($course_info['visibility'], array(COURSE_VISIBILITY_OPEN_PLATFORM, COURSE_VISIBILITY_OPEN_WORLD))) {
                 if (CourseManager::is_user_subscribed_in_course($user_id, $course_info['code'])) {
                     $form_data['action'] = $course_info['course_public_url'];
                     $form_data['message'] = sprintf(get_lang('YouHaveBeenRegisteredToCourseX'), $course_info['title']);
                     $form_data['button'] = Display::button('next', get_lang('GoToCourse', null, $_user['language']), array('class' => 'btn btn-primary btn-large'));
                     $exercise_redirect = intval(Session::read('exercise_redirect'));
                     // Specify the course id as the current context does not
                     // hold a global $_course array
                     $objExercise = new Exercise($course_info['real_id']);
                     $result = $objExercise->read($exercise_redirect);
                     if (!empty($exercise_redirect) && !empty($result)) {
                         $form_data['action'] = api_get_path(WEB_CODE_PATH) . 'exercice/overview.php?exerciseId=' . $exercise_redirect . '&cidReq=' . $course_info['code'];
                         $form_data['message'] .= '<br />' . get_lang('YouCanAccessTheExercise');
                         $form_data['button'] = Display::button('next', get_lang('Go', null, $_user['language']), array('class' => 'btn btn-primary btn-large'));
                     }
                     if (!empty($form_data['action'])) {
                         header('Location: ' . $form_data['action']);
                         exit;
                     }
                 }
             }
         }
     }
     return $form_data;
 }
示例#23
0
/**
   Return the <a> html code for delete, add, clone, edit a question
   in_action = the code of the action triggered by the button
   from_exercice = the id of the current exercice from which we click on question pool
   in_questionid = the id of the current question
   in_questiontype = the code of the type of the current question
   in_questionname = the name of the question
   in_selected_course = the if of the course chosen in the FILTERING MENU
   in_courseCategoryId = the id of the category chosen in the FILTERING MENU
   in_exerciseLevel = the level of the exercice chosen in the FILTERING MENU
   in_answerType = the code of the type of the question chosen in the FILTERING MENU
   in_session_id = the id of the session_id chosen in the FILTERING MENU
   in_exercice_id = the id of the exercice chosen in the FILTERING MENU
*/
function get_action_icon_for_question($in_action, $from_exercice, $in_questionid, $in_questiontype, $in_questionname, $in_selected_course, $in_courseCategoryId, $in_exerciseLevel, $in_answerType, $in_session_id, $in_exercice_id)
{
    $res = "";
    $getParams = "&selected_course={$in_selected_course}&courseCategoryId={$in_courseCategoryId}&exerciseId={$in_exercice_id}&exerciseLevel={$in_exerciseLevel}&answerType={$in_answerType}&session_id={$in_session_id}";
    switch ($in_action) {
        case "delete":
            $res = "<a href='" . api_get_self() . "?" . api_get_cidreq() . $getParams . "&delete={$in_questionid}' onclick='return confirm_your_choice()'>";
            $res .= Display::return_icon("delete.png", get_lang('Delete'));
            $res .= "</a>";
            break;
        case "edit":
            $res = get_a_tag_for_question(1, $from_exercice, $in_questionid, $in_questiontype, Display::return_icon("edit.png", get_lang('Modify')), $in_session_id);
            break;
        case "add":
            // add if question is not already in test
            $myObjEx = new Exercise();
            $myObjEx->read($from_exercice);
            if (!$myObjEx->isInList($in_questionid)) {
                $res = "<a href='" . api_get_self() . "?" . api_get_cidreq() . $getParams . "&recup={$in_questionid}&fromExercise={$from_exercice}'>";
                $res .= Display::return_icon("view_more_stats.gif", get_lang('InsertALinkToThisQuestionInTheExercise'));
                $res .= "</a>";
            } else {
                $res = "-";
            }
            unset($myObjEx);
            break;
        case "clone":
            $url = api_get_self() . "?" . api_get_cidreq() . $getParams . "&amp;copy_question={$in_questionid}&amp;course_id={$in_selected_course}&amp;fromExercise={$from_exercice}";
            $res = Display::url(Display::return_icon('cd.gif', get_lang('ReUseACopyInCurrentTest')), $url);
            break;
        default:
            $res = $in_action;
            break;
    }
    return $res;
}
示例#24
0
 /**
  * adds an exercise into the exercise list
  *
  * @author Olivier Brouckaert
  * @param integer $exerciseId - exercise ID
  * @param boolean $fromSave - from $this->save() or not
  */
 public function addToList($exerciseId, $fromSave = false)
 {
     $exerciseRelQuestionTable = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
     $id = $this->id;
     // checks if the exercise ID is not in the list
     if (!in_array($exerciseId, $this->exerciseList)) {
         $this->exerciseList[] = $exerciseId;
         $new_exercise = new Exercise();
         $new_exercise->read($exerciseId);
         $count = $new_exercise->selectNbrQuestions();
         $count++;
         $sql = "INSERT INTO {$exerciseRelQuestionTable} (c_id, question_id, exercice_id, question_order)\n                    VALUES ({$this->course['real_id']}, " . intval($id) . ", " . intval($exerciseId) . ", '{$count}')";
         Database::query($sql);
         // we do not want to reindex if we had just saved adnd indexed the question
         if (!$fromSave) {
             $this->search_engine_edit($exerciseId, TRUE);
         }
     }
 }
示例#25
0
    }
} else {
    $interbreadcrumb[] = array("url" => "exercise.php?" . api_get_cidreq(), "name" => get_lang('Exercises'));
    $objExerciseTmp = new Exercise();
    if ($objExerciseTmp->read($exercise_id)) {
        $nameTools = get_lang('Results') . ': ' . $objExerciseTmp->name;
    }
}
Display::display_header($nameTools);
// Clean all results for this test before the selected date
if (($is_allowedToEdit || $is_tutor || api_is_coach()) && isset($_GET['delete_before_date']) && $locked == false) {
    // ask for the date
    $check = Security::check_token('get');
    if ($check) {
        $objExerciseTmp = new Exercise();
        if ($objExerciseTmp->read($exercise_id)) {
            $count = $objExerciseTmp->clean_results(true, $_GET['delete_before_date'] . ' 23:59:59');
            Display::display_confirmation_message(sprintf(get_lang('XResultsCleaned'), $count));
        }
    }
}
// Security token to protect deletion
$token = Security::get_token();
$actions = Display::div($actions, array('class' => 'actions'));
$extra = '<script>
    $(document).ready(function() {
        $( "#dialog:ui-dialog" ).dialog( "destroy" );
        $( "#dialog-confirm" ).dialog({
                autoOpen: false,
                show: "blind",
                resizable: false,
示例#26
0
require_once 'include/lib/multimediahelper.class.php';
ModalBoxHelper::loadModalBox();

$pageName = $langResults;
$navigation[] = array('url' => "index.php?course=$course_code", 'name' => $langExercices);

if (isset($_GET['exerciseId'])) {
    $exerciseId = intval($_GET['exerciseId']);
}

// if the object is not in the session
if (!isset($_SESSION['objExercise'][$exerciseId])) {
    // construction of Exercise
    $objExercise = new Exercise();
    // if the specified exercise doesn't exist or is disabled
    if (!$objExercise->read($exerciseId) && (!$is_editor)) {
        $tool_content .= "<p>$langExerciseNotFound</p>";
        draw($tool_content, 2);
        exit();
    }
    if(!$objExercise->selectScore() &&  !$is_editor) {
        redirect_to_home_page("modules/exercise/index.php?course=$course_code");
    }
}

if (isset($_SESSION['objExercise'][$exerciseId])) {
    $objExercise = $_SESSION['objExercise'][$exerciseId];
}

if ($is_editor && isset($_GET['purgeAttempID'])) {
    $eurid = $_GET['purgeAttempID'];
示例#27
0
<?php

/* See license terms in /license.txt */
//require_once '../inc/global.inc.php';
$this_section = SECTION_COURSES;
$exercise_id = isset($_GET['exerciseId']) && !empty($_GET['exerciseId']) ? intval($_GET['exerciseId']) : 0;
// Access control
api_protect_course_script(true);
if (!api_is_allowed_to_edit()) {
    api_not_allowed();
}
$objExercise = new Exercise();
$result = $objExercise->read($exercise_id);
if (!$result) {
    api_not_allowed(true);
}
$interbreadcrumb[] = array("url" => "exercise.php?" . api_get_cidreq(), "name" => get_lang('Exercises'));
$interbreadcrumb[] = array("url" => "admin.php?exerciseId={$exercise_id}&" . api_get_cidreq(), "name" => $objExercise->name);
//Add the JS needed to use the jqgrid
$htmlHeadXtra[] = api_get_jqgrid_js();
// The header.
Display::display_header(get_lang('StudentsWhoAreTakingTheExerciseRightNow'));
//jqgrid will use this URL to do the selects
$minutes = 60;
$url = api_get_path(WEB_AJAX_PATH) . 'exercise.ajax.php?a=get_live_stats&exercise_id=' . $objExercise->id . '&minutes=' . $minutes;
//The order is important you need to check the the $column variable in the model.ajax.php file
$columns = array(get_lang('FirstName'), get_lang('LastName'), get_lang('Time'), get_lang('QuestionsAlreadyAnswered'), get_lang('Score'));
//Column config
$column_model = array(array('name' => 'firstname', 'index' => 'firstname', 'width' => '100', 'align' => 'left'), array('name' => 'lastname', 'index' => 'lastname', 'width' => '100', 'align' => 'left'), array('name' => 'start_date', 'index' => 'start_date', 'width' => '100', 'align' => 'left'), array('name' => 'question', 'index' => 'count_questions', 'width' => '60', 'align' => 'left', 'sortable' => 'false'), array('name' => 'score', 'index' => 'score', 'width' => '50', 'align' => 'left', 'sortable' => 'false'));
//Autowidth
$extra_params['autowidth'] = 'true';
示例#28
0
 $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 = (double) $row_dates['exe_result'];
 $max_score = (double) $row_dates['exe_weighting'];
 $sql = "UPDATE {$TBL_LP_ITEM} SET\n                    max_score = '{$max_score}'\n                WHERE c_id = {$course_id} AND id = '" . $safe_item_id . "'";
 Database::query($sql);
 $sql = "SELECT id FROM {$TBL_LP_ITEM_VIEW}\n                WHERE\n                    c_id = {$course_id} AND\n                    lp_item_id = '{$safe_item_id}' AND\n                    lp_view_id = '" . $learnPath->lp_view_id . "'\n                ORDER BY id DESC\n                LIMIT 1";
 $res_last_attempt = Database::query($sql);
 if (Database::num_rows($res_last_attempt) && !api_is_invitee()) {
     $row_last_attempt = Database::fetch_row($res_last_attempt);
     $lp_item_view_id = $row_last_attempt[0];
     $exercise = new Exercise(api_get_course_int_id());
     $exercise->read($row_dates['exe_exo_id']);
     $status = 'completed';
     if (!empty($exercise->pass_percentage)) {
         $status = 'failed';
         $success = ExerciseLib::is_success_exercise_result($score, $max_score, $exercise->pass_percentage);
         if ($success) {
             $status = 'passed';
         }
     }
     $sql = "UPDATE {$TBL_LP_ITEM_VIEW} SET\n                        status = '{$status}',\n                        score = {$score},\n                        total_time = {$mytime}\n                    WHERE id='" . $lp_item_view_id . "' AND c_id = {$course_id} ";
     if ($debug) {
         error_log($sql);
     }
     Database::query($sql);
     $sql = "UPDATE {$TBL_TRACK_EXERCICES} SET\n                        orig_lp_item_view_id = {$lp_item_view_id}\n                    WHERE exe_id = " . $safe_exe_id;
     Database::query($sql);
示例#29
0
 /**
  * Get all questions
  * @param Application $app
  * @param int $categoryId
  * @param int $exerciseId
  * @param int $courseId
  * @param array $options
  * @param bool $get_count
  * @return array
  */
 public static function getQuestions($app, $categoryId, $exerciseId, $courseId, $options, $get_count = false)
 {
     $questionTable = Database::get_course_table(TABLE_QUIZ_QUESTION);
     $questionPoolFields = array('question_session_id' => array('innerjoin' => " INNER JOIN " . Database::get_course_table(TABLE_QUIZ_TEST_QUESTION) . " as quiz_rel_question_session ON (quiz_rel_question_session.question_id = s.iid)\n                                 INNER JOIN " . Database::get_course_table(TABLE_QUIZ_TEST) . " as quizsession ON (quizsession.iid = quiz_rel_question_session.exercice_id)\n                                 INNER JOIN " . Database::get_main_table(TABLE_MAIN_SESSION) . " session ON (session.id = quizsession.session_id)", 'where' => 'session_id', 'inject_fields' => 'session.name as question_session_id, '), 'question_category_id' => array('innerjoin' => " INNER JOIN " . Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY) . " as quiz_rel_cat ON (quiz_rel_cat.question_id = s.iid)\n                                 INNER JOIN " . Database::get_course_table(TABLE_QUIZ_CATEGORY) . " as cat ON (cat.iid = quiz_rel_cat.category_id)", 'where' => 'quiz_rel_cat.category_id', 'inject_fields' => 'cat.title as question_category_id, '), 'question_exercise_id' => array('innerjoin' => " INNER JOIN " . Database::get_course_table(TABLE_QUIZ_TEST_QUESTION) . " as quiz_rel_question ON (quiz_rel_question.question_id = s.iid)\n                                 INNER JOIN " . Database::get_course_table(TABLE_QUIZ_TEST) . " as quizexercise ON (quizexercise.iid = quiz_rel_question.exercice_id) ", 'where' => 'quiz_rel_question.exercice_id', 'inject_fields' => 'quizexercise.title as question_exercise_id, '), 'question_c_id' => array('where' => 's.c_id', 'innerjoin' => " INNER JOIN " . Database::get_main_table(TABLE_MAIN_COURSE) . " as course ON (course.id = s.c_id) ", 'inject_fields' => 'course.title as question_c_id, '), 'question_question_type' => array('where' => 's.type ', 'inject_fields' => 's.type as question_question_type,'), 'question_difficulty' => array('where' => 's.level', 'inject_fields' => 's.level as question_difficulty, '));
     // Checking if you're looking for orphan questions.
     $isOrphanQuestion = false;
     if (isset($options['question'])) {
         foreach ($options['question'] as $option) {
             if (isset($option['field']) && $option['field'] == 'question_exercise_id') {
                 if ($option['data'] == 0) {
                     $isOrphanQuestion = true;
                     break;
                 }
             }
         }
     }
     // Special case for orphan questions.
     if ($isOrphanQuestion) {
         $questionPoolFields['question_exercise_id'] = array('innerjoin' => " LEFT JOIN " . Database::get_course_table(TABLE_QUIZ_TEST_QUESTION) . " as quiz_rel_question ON (quiz_rel_question.question_id = s.iid)\n                                 LEFT JOIN " . Database::get_course_table(TABLE_QUIZ_TEST) . " as quizexercise ON (quizexercise.iid = quiz_rel_question.exercice_id) ", 'where' => 'quiz_rel_question.exercice_id', 'inject_fields' => 'quizexercise.title as question_exercise_id, ');
     }
     $inject_extra_fields = null;
     $inject_joins = null;
     $where = $options['where'];
     $newQuestionPoolField = array();
     if (isset($options['question'])) {
         foreach ($options['question'] as $question) {
             if (isset($questionPoolFields[$question['field']])) {
                 $newQuestionPoolField[$question['field']] = $questionPoolFields[$question['field']];
             }
         }
     }
     $inject_question_fields = null;
     $questionPoolFields = $newQuestionPoolField;
     // Injecting inner joins.
     foreach ($questionPoolFields as $field => $option) {
         $where = str_replace($field, $option['where'], $where);
         if (isset($option['innerjoin']) && !empty($option['innerjoin'])) {
             $inject_joins .= $option['innerjoin'];
         }
         if (isset($option['inject_fields']) && !empty($option['inject_fields'])) {
             $inject_question_fields .= $option['inject_fields'];
         }
     }
     $options['where'] = $where;
     $extra_field = new ExtraField('question');
     $conditions = $extra_field->parseConditions($options);
     $inject_joins .= $conditions['inject_joins'];
     $where = $conditions['where'];
     $inject_where = $conditions['inject_where'];
     $inject_extra_fields = $conditions['inject_extra_fields'];
     $order = $conditions['order'];
     $limit = $conditions['limit'];
     if ($get_count == true) {
         $select = " SELECT count(*) as total_rows";
     } else {
         $select = " SELECT s.*, {$inject_extra_fields} {$inject_question_fields} 1 ";
     }
     $extraCondition = null;
     // Used by the question manager
     if (!empty($categoryId)) {
         $categoryRelQuestionTable = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
         $extraCondition = " INNER JOIN {$categoryRelQuestionTable} c ON (s.iid = c.question_id)";
         $categoryId = intval($categoryId);
         $where .= " AND category_id = {$categoryId} ";
     }
     /*if (!empty($exerciseId)) {
           $exerciseRelQuestionTable = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
           $extraCondition .= " INNER JOIN $exerciseRelQuestionTable e ON (s.iid = e.question_id)";
           $exerciseId = intval($exerciseId);
           $where .= " AND exercice_id = $exerciseId ";
       }*/
     // Orphan questions
     if ($isOrphanQuestion) {
         //$exerciseRelQuestionTable = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
         //$extraCondition .= " INNER JOIN $exerciseRelQuestionTable e ON (s.iid = e.question_id)";
         $where .= " OR quizexercise.active = -1 OR quiz_rel_question.exercice_id IS NULL";
     }
     if (!empty($courseId)) {
         $courseId = intval($courseId);
         $where .= " AND s.c_id = {$courseId} ";
     }
     if (isset($options['question'])) {
         $courseList = CourseManager::get_course_list_of_user_as_course_admin(api_get_user_id());
         foreach ($options['question'] as $questionOption) {
             if ($questionOption['field'] == 'question_c_id') {
                 if (isset($questionOption['data'])) {
                     if (!isset($courseList[$questionOption['data']])) {
                         return array();
                     }
                 }
             }
         }
     }
     //var_dump(CourseManager::get_teacher_list_from_course_code())
     //var_dump($inject_joins);
     $query = " {$select} FROM {$questionTable} s {$inject_joins} {$extraCondition} WHERE 1=1 {$where} {$inject_where} {$order} {$limit}";
     //echo $query.'<br />';
     //var_dump($extraCondition);
     //var_dump($where);
     $result = Database::query($query);
     $questions = array();
     $exerciseList = null;
     if (!empty($exerciseId)) {
         $exercise = new Exercise();
         $exercise->read($exerciseId);
         $exerciseList = $exercise->questionList;
     }
     if (Database::num_rows($result)) {
         $questions = Database::store_result($result, 'ASSOC');
         if ($get_count) {
             return $questions[0]['total_rows'];
         }
         $previewIcon = Display::return_icon('preview.gif', get_lang('View'), array(), ICON_SIZE_SMALL);
         $copyIcon = Display::return_icon('copy.png', get_lang('Copy'), array(), ICON_SIZE_SMALL);
         $reuseIcon = Display::return_icon('view_more_stats.gif', get_lang('InsertALinkToThisQuestionInTheExercise'), array(), ICON_SIZE_SMALL);
         $editIcon = Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL);
         //$deleteIcon = Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL);
         //var_dump($exerciseId);
         // Including actions
         foreach ($questions as &$question) {
             $type = self::get_question_type($question['type']);
             $question['type'] = get_lang($type[1]);
             $question['question_question_type'] = get_lang($type[1]);
             if (empty($exerciseId)) {
                 // View.
                 $actions = Display::url($previewIcon, $app['url_generator']->generate('admin_questions_show', array('id' => $question['iid'])));
                 // Edit.
                 $actions .= Display::url($editIcon, $app['url_generator']->generate('admin_questions_edit', array('id' => $question['iid'])));
             } else {
                 // View.
                 $actions = Display::url($previewIcon, $app['url_generator']->generate('question_show', array('cidReq' => api_get_course_id(), 'id_session' => api_get_session_id(), 'exerciseId' => $exerciseId, 'id' => $question['iid'])));
                 if (isset($exerciseList) && !empty($exerciseList) && in_array($question['iid'], $exerciseList)) {
                     // Copy.
                     //$actions .= $copyIconDisabled;
                 } else {
                     // Copy.
                     $actions .= Display::url($copyIcon, 'javascript:void(0);', array('onclick' => 'ajaxAction(this);', 'data-url' => $app['url_generator']->generate('exercise_copy_question', array('cidReq' => api_get_course_id(), 'id_session' => api_get_session_id(), 'questionId' => $question['iid'], 'exerciseId' => $exerciseId))));
                     // Reuse.
                     $actions .= Display::url($reuseIcon, 'javascript:void(0);', array('onclick' => 'ajaxAction(this);', 'data-url' => $app['url_generator']->generate('exercise_reuse_question', array('cidReq' => api_get_course_id(), 'id_session' => api_get_session_id(), 'questionId' => $question['iid'], 'exerciseId' => $exerciseId))));
                 }
                 // Edit.
                 $actions .= Display::url($editIcon, $app['url_generator']->generate('exercise_question_edit', array('cidReq' => api_get_course_id(), 'id_session' => api_get_session_id(), 'id' => $question['iid'])));
             }
             $question['actions'] = $actions;
         }
     }
     return $questions;
 }
示例#30
0
 /**
  * Send a complete exercise in SCORM format, from its ID
  *
  * @param int $exerciseId The exercise to exporte
  * @param boolean $standalone Wether it should include XML tag and DTD line.
  * @return The XML as a string, or an empty string if there's no exercise with given ID.
  */
 public static function export_exercise_to_scorm($exerciseId, $standalone = true)
 {
     $exercise = new Exercise();
     if (!$exercise->read($exerciseId)) {
         return '';
     }
     $ims = new ScormSection($exercise);
     $xml = $ims->export($standalone);
     return $xml;
 }