/** * 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 } }
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; }
/** * 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 &) //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')); }
/** * 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; }
/** * 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; }
} // 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'; } }
// 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') . ' "' . $questionName . '"</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; }
} 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'); }
/** * 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(); } }
$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))) {
/** * 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; }
/** * 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=""> </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"> <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 .= ' </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%"> </td> <td width="20%"> </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; }
// 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
* 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}&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); } } } } }
/** * @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; } }
/** * 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; }
/** * 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; }
/** 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 . "&copy_question={$in_questionid}&course_id={$in_selected_course}&fromExercise={$from_exercice}"; $res = Display::url(Display::return_icon('cd.gif', get_lang('ReUseACopyInCurrentTest')), $url); break; default: $res = $in_action; break; } return $res; }
/** * 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); } } }
} } 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,
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'];
<?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';
$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);
/** * 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; }
/** * 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; }