/** hack to Just Get A Question from current dmid, using Excercise class * to do our factorying for us. * Obviously the factory code needs to be refactored to ElseWhere, but not on Sunday * :-P * (Sun 15 Jun 2008) */ function getQuestion( $dmid, $questionLanguages, $answerLanguages ) { if ( !$questionLanguages ) throw new Exception( "Vocview: no question (original) languages provided!" ); if ( !$answerLanguages ) throw new Exception( "Vocview: no answer (translation) languages provided!" ); /* 3 men walked into a bar^wExercise * fullset, fetcher, and subset */ /* our regular fetcher is provided by functions.php * I suppose if I were tidier, I could use fetchers for * persistence */ $fetcher = new OWFetcher(); /* fullset is a domdocument containing a <collection> of * empty <defined-meanings/> (just their defined-meaning-id * attribute is set) * This format makes sense on some days, it's just * massive nukular overkill today, specially since * we only have 1 element :-P * Still, if Exercise wants this as a dom, we can oblige. */ $xmlString = " <collection> <defined-meaning defined-meaning-id=\"$dmid\" /> </collection> "; $xml = simplexml_load_string( $xmlString ); $fullset = dom_import_simplexml( $xml )->ownerDocument; # et voila. /* subset is a selection of stuff we are actually * interested in, from the above, as an array of * dmid's .... Oh look! We have just one! */ $subset = array( $dmid ); # :-P # (ok, to be honest, it does also get auto-generated # from the fullset, if we say nothing... but # then where would the joke be? ) # and we already had the fetcher. # So the fullset said to the fetcher: let's do this! $exercise = new Exercise( $fetcher, $fullset, $subset ); $exercise->setQuestionLanguages( $questionLanguages ); $exercise->setAnswerLanguages( $answerLanguages ); # Ok, now let's see. which question did we need? # (and setting selfcheck to false) $question = $exercise->getQuestion( $dmid, false ); # Oh REALLY! And we needed to go through all that? # well, let's return it, before people start asking # more difficult questions. return $question; }
/** * 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 } }
public function exercise_get($id = null) { $user = new User($this->user_id); $exercise = new Exercise(); $exercise->where('id', $id); $exercise->get(); $logs = $exercise->exerciselog; $logs->get(); $response = array(); foreach ($logs as $log) { array_push($response, $log->getData()); } $this->response($response); }
protected function getContent() { $content = '<ol class="breadcrumb"> <li><a href="' . ROOT_DIR . 'admin/courseadmin">' . I18n::t('courseoverview.title') . '</a></li> <li><a href="' . ROOT_DIR . 'admin/lessonadmin/' . $this->course->getId() . '">' . $this->course->getName(I18n::getLang()) . '</a></li> <li class="active">' . $this->lesson->getName(I18n::getLang()) . '</li> </ol>'; $content .= '<form method="post"><input type="hidden" name="action" value="saveExercises" /> <table id="exercise-admin-table" class="table table-hover"> <thead> <tr> <th>' . I18n::t('admin.exerciseadmin.question') . '</th> <th>' . I18n::t('admin.exerciseadmin.answer') . ' (EN)</th> <th>' . I18n::t('admin.exerciseadmin.answer') . ' (DE)</th> <th> </th> </tr> </thead> <tbody>'; foreach ((array) Exercise::getMultipleExercises($this->lesson->getId(), 50, 0) as $exercise) { $content .= '<tr><input type="hidden" name="exercise_id[]" value="' . $exercise->getId() . '" /> <td><input type="text" name="question[]" value="' . $exercise->getQuestion() . '" /></td> <td><input type="text" name="answer_en[]" value="' . $exercise->getAnswer('en') . '" /></td> <td><input type="text" name="answer_de[]" value="' . $exercise->getAnswer('de') . '" /></td> </tr>'; } $content .= '</tbody> </table> <table width="100%"> <tr><td width="50%" align="left"><button id="exercise-add-btn" class="btn btn-default" type="button">' . I18n::t('button.add') . '</button></td> <td width="50%" align="right"><input type="submit" class="btn btn-default" value="' . I18n::t('button.save') . '" /></td></tr> </table> </form>'; return $content; }
public function checkCode() { Helpers::normalizeCode($this->code); if (!($this->exercise = Exercise::model()->findByAttributes(array('code' => trim($this->code))))) { $this->addError('code', Yii::t('swu', 'The code you entered does not exist.')); } }
function main() { $collection_id = 376317; # olpc dictionary.... WAY too big $fetcher = new OWFetcher(); echo "fullset...\n"; $fullSetXML = $fetcher->getFullSetXML_asString( $collection_id ); $fullSet = new DOMDocument(); $success = $fullSet->loadXML( $fullSetXML ); if ( !$success ) { throw new Exception( "Failed to load category XML from server" ); } $maxSubSet = dom2set( $fullSet ); # sort($maxSubSet); foreach ($maxSubSet as $dmid) {print "$dmid,";} # var_dump($fullSet->saveXML()); $exercise = new Exercise( $fetcher, $fullSet, $maxSubSet ); # pwease, not the max! # $exercise->setLanguages(array("eng","fra","deu")); $exercise->setQuestionLanguages( array( "deu" ) ); $exercise->setAnswerLanguages( array( "eng" ) ); # $question_dmid=$maxSubSet[array_rand($maxSubSet)]; echo "question...\n"; # $questionNode=$exercise->getQuestionNode($question_dmid); # dumpNode($questionNode); $runex = $exercise->randSubExercise( 10 ); dumpExercise( $runex, 5 ); echo "\n\n=== presistence test ===\n\n"; saveExercise( $runex ); $exid = mysql_insert_id(); $loadex = loadExercise( $exid ); $loadex->setFetcher( $fetcher ); dumpExercise( $loadex, 10 ); }
public function sendCodes($controller) { if (!($student = Student::model()->findByAttributes(array('email' => $this->email)))) { return false; } $exercises = Exercise::model()->with('assignment')->sortByDuedate()->findAllByAttributes(array('student_id' => $student->id)); foreach ($exercises as $exercise) { $exercise->link = Yii::app()->controller->createAbsoluteSslUrl('exercise/info', array('k' => $exercise->generateAckKey())); } $options = array(); if (Helpers::getYiiParam('addOriginatingIP')) { $options['originating_IP'] = sprintf('[%s]', Yii::app()->request->userHostAddress); } return MailTemplate::model()->mailFromTemplate('send_codes', array($student->email => $student->name), array('student' => $student, 'exercises' => $exercises), $options); }
public function checkCode() { if ($this->getError('verifyCode')) { return; // we don't provide any information about the code if the captcha is not valid } Helpers::normalizeCode($this->code); if ($this->exercise = Exercise::model()->findByAttributes(array('code' => trim($this->code)))) { if (!$this->byteacher and $this->exercise->duedate < date('Y-m-d H:m:s', time() - $this->exercise->assignment->grace * 24 * 60 * 60)) { $this->addError('code', Yii::t('swu', 'The code provided is not valid anymore (time expired on %date%).', array('%date%' => $this->exercise->duedate))); return; } } elseif ($this->code) { $this->addError('code', Yii::t('swu', 'The code provided is not valid.')); return; } }
public function get($functionName, $linkName, $params = array(), $checkSession = true) { $positive = function ($input) { //$input = $input[count($input)-1]; $result = Model::isEmpty(); $result['content'] = array(); foreach ($input as $inp) { if ($inp->getNumRows() > 0) { // extract exercise data from db answer $res = Exercise::ExtractExercise($inp->getResponse(), false); $result['content'] = array_merge($result['content'], is_array($res) ? $res : array($res)); $result['status'] = 200; } } return $result; }; $params = DBJson::mysql_real_escape_string($params); return $this->_component->call($linkName, $params, '', 200, $positive, array(), 'Model::isProblem', array(), 'Query'); }
public function saveExercises() { $successful = true; $updateAmount = isset($_POST['exercise_id']) && !empty($_POST['exercise_id']) ? count($_POST['exercise_id']) : 0; $i = 0; // update exercises that were present in DB already while ($i < $updateAmount) { $id = $this->secureString($_POST['exercise_id'][$i]); $question = $this->secureString($_POST['question'][$i]); $answerEN = $this->secureString($_POST['answer_en'][$i]); $answerDE = $this->secureString($_POST['answer_de'][$i]); // do not update exercises with empty fields if (empty($id) || empty($question) || empty($answerEN) || empty($answerDE)) { $i++; continue; } if (!Exercise::update($id, $question, $answerEN, $answerDE)) { $successful = false; } $i++; } // add new exercises to DB while ($i < count($_POST['question'])) { $question = $this->secureString($_POST['question'][$i]); $answerEN = $this->secureString($_POST['answer_en'][$i]); $answerDE = $this->secureString($_POST['answer_de'][$i]); // do not add exercises with empty fields if (empty($question) || empty($answerEN) || empty($answerDE)) { $i++; continue; } if (!Exercise::createExercise($this->lesson->getId(), $question, $answerEN, $answerDE)) { $successful = false; } $i++; } if ($successful) { $this->getView()->addSuccessMessage('Exercises were successfully saved!'); } else { $this->getView()->addErrorMessage('There was a problem saving one or more exercises..'); } $this->defaultAction(); }
function loadExercise( $exercise_id ) { if ( !is_int( $exercise_id ) ) throw new Exception( "persist; loadExercise exercise_id is not an integer" ); global $mysql_info; DBTools::connect( $mysql_info ); $exercise_id = mysql_real_escape_string( $exercise_id ); $row = DBTools::doQuery( "select * from exercises where id=\"$exercise_id\"" ); $exercise = new Exercise(); $exercise->setId( $exercise_id ); $exercise->loadXML( $row["exercise"] ); $exercise->setQuestionLanguages( explode( ",", $row["questionLanguages"] ) ); $exercise->setAnswerLanguages( explode( ",", $row["answerLanguages"] ) ); return $exercise; }
public function evaluate() { if (isset($_POST['answer']) && !empty($_POST['answer'])) { $input = $this->secureString($_POST['answer']); $correct = $_SESSION['currentExercise']->getAnswer(I18n::getLang()); // trim whitespaces, remove not relevant characters in order to be more flexible with user input $removedChars = '/(\\W+)|[0-9]|_/'; $input = trim($input); $input = preg_replace($removedChars, '', $input); $correct = trim($correct); $correct = preg_replace($removedChars, '', $correct); if (strtolower($input) === strtolower($correct)) { $updated = Exercise::addCorrectAnswer($_SESSION['user']->getEmail(), $_SESSION['currentExercise']->getId()); if (!$updated) { $this->getView()->addErrorMessage('Could not update DB.'); } $this->getView()->addSuccessMessage(I18n::t('exercise.correct')); } else { $this->getView()->addErrorMessage(I18n::t('exercise.wrong')); } } $this->defaultAction(); }
// general parameters passed via POST/GET if (empty($origin)) { $origin = Security::remove_XSS($_REQUEST['origin']); } /** @var Exercise $objExercise */ if (empty($objExercise)) { $objExercise = Session::read('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->get_stat_track_exercise_info_by_exe_id($exe_id); if (!empty($exercise_stat_info) && isset($exercise_stat_info['exe_exo_id'])) { header("Location: overview.php?exerciseId=" . $exercise_stat_info['exe_exo_id']); exit; } api_not_allowed(); } if (api_is_in_gradebook()) { $interbreadcrumb[] = array('url' => api_get_path(WEB_CODE_PATH) . 'gradebook/index.php?' . api_get_cidreq(), 'name' => get_lang('ToolGradebook')); } $nameTools = get_lang('Exercises'); $interbreadcrumb[] = array("url" => "exercise.php?" . api_get_cidreq(), "name" => get_lang('Exercises')); $htmlHeadXtra[] = '<script src="' . api_get_path(WEB_LIBRARY_JS_PATH) . 'hotspot/js/hotspot.js"></script>'; $htmlHeadXtra[] = '<link rel="stylesheet" href="' . api_get_path(WEB_LIBRARY_JS_PATH) . 'hotspot/css/hotspot.css">'; if ($origin != 'learnpath') {
} if (isset($_REQUEST['quId']) && is_numeric($_REQUEST['quId'])) { $quId = (int) $_REQUEST['quId']; } else { $quId = null; } if (isset($_REQUEST['filter'])) { $filter = $_REQUEST['filter']; } else { $filter = 'all'; } $categoryId = substr($filter, 0, 10) == 'categoryId' && is_numeric(substr($filter, 10)) ? substr($filter, 10) : null; /* * Init other vars */ $exercise = new Exercise(); if (!is_null($exId)) { $exercise->load($exId); } $dialogBox = new DialogBox(); /* * Execute commands */ // use question in exercise if ($cmd == 'rqUse' && !is_null($quId) && !is_null($exId)) { if ($exercise->addQuestion($quId)) { // TODO show confirmation and back link header('Location: ' . Url::Contextualize('edit_exercise.php?exId=' . $exId)); } } // delete question
<?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';
/** * 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); }
$interbreadcrumb[] = array("url" => "admin.php?exerciseId=" . $exercise_id . "&" . api_get_cidreq(), "name" => $objExerciseTmp->name); } } 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",
/** * 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; }
/** * 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; }
if ($is_allowed_to_edit) { if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'view' && !isset($_REQUEST['exeId'])) { $_REQUEST['action'] = 'build'; } //$_SESSION['studentview'] = null; } } $action = (!empty($_REQUEST['action']) ? $_REQUEST['action'] : ''); // format title to be displayed correctly if QUIZ $post_title = ""; if (isset($_POST['title'])) { $post_title = Security::remove_XSS($_POST['title']); if (isset($_POST['type']) && isset($_POST['title']) && $_POST['type'] == TOOL_QUIZ && !empty($_POST['title'])) { $post_title = Exercise::format_title_variable($_POST['title']); } } switch ($action) { case 'add_item': if (!$is_allowed_to_edit) { api_not_allowed(true); } if ($debug > 0) error_log('New LP - add item action triggered', 0); if (!$lp_found) { //check if the learnpath ID was defined, otherwise send back to list if ($debug > 0) error_log('New LP - No learnpath given for add item', 0); require 'lp_list.php'; } else {
} else { $cmd = null; } if (isset($item_list['1']) && is_numeric($item_list['1'])) { $quId = (int) $item_list['1']; } else { $quId = null; } if (isset($item_list['2']) && is_numeric($item_list['2'])) { $exId = (int) $item_list['2']; } else { $exId = null; } if ($cmd == 'download') { // find exercise informations $exercise = new Exercise(); if ($exercise->load($exId) || $is_allowedToEdit) { if ($exercise->getVisibility() == 'VISIBLE' || $is_allowedToEdit) { $question = new Question(); if ($question->load($quId)) { $attachmentFile = $question->getQuestionDirSys() . $question->getAttachment(); if (claro_send_file($attachmentFile)) { die; } else { $dialogBox->error(get_lang('Not found')); } } else { $dialogBox->error(get_lang('Not found')); } } else { $dialogBox->error(get_lang('Not allowed'));
} // general parameters passed via POST/GET if (empty($origin)) { $origin = Security::remove_XSS($_REQUEST['origin']); } 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'];
/** * 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; }
);*/ $new_result = array(); if (!empty($result)) { foreach ($result as $item) { $item['display_text'] = $item['displayText']; $item['field_type'] = $obj->get_field_type_by_id($item['fieldType']); $item['changeable'] = $item['changeable'] ? Display::return_icon('check-circle.png', get_lang('Invisible')) : Display::return_icon('closed-circle.png', get_lang('Visible'), null, ICON_SIZE_SMALL); $item['visible'] = $item['visible'] ? Display::return_icon('check-circle.png', get_lang('Invisible')) : Display::return_icon('closed-circle.png', get_lang('Visible'), null, ICON_SIZE_SMALL); $item['filter'] = $item['filter'] ? Display::return_icon('check-circle.png', get_lang('Invisible')) : Display::return_icon('closed-circle.png', get_lang('Visible'), null, ICON_SIZE_SMALL); $new_result[] = $item; } $result = $new_result; } break; case 'get_exercise_grade': $objExercise = new Exercise(); $exercises = $objExercise->getExercisesByCouseSession($_GET['course_id'], $_GET['session_id']); $cntExer = 4; if (!empty($exercises)) { $cntExer += count($exercises); } $columns = array(); //Get dynamic column names $i = 1; $column_names = array(); foreach (range(1, $cntExer) as $cnt) { switch ($cnt) { case 1: $columns[] = 'session'; $column_names[] = get_lang('Section'); break;
/** * 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); } } } } }
/** * 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; }
/** * Handles a given Excel spreadsheets as in the template provided */ function lp_upload_quiz_action_handling() { global $debug; $_course = api_get_course_info(); $courseId = $_course['real_id']; if (!isset($_POST['submit_upload_quiz'])) { return; } // Get the extension of the document. $path_info = pathinfo($_FILES['user_upload_quiz']['name']); // Check if the document is an Excel document if ($path_info['extension'] != 'xls') { return; } // Read the Excel document $data = new Spreadsheet_Excel_Reader(); // Set output Encoding. $data->setOutputEncoding(api_get_system_encoding()); // Reading the xls document. $data->read($_FILES['user_upload_quiz']['tmp_name']); $correctScore = isset($_POST['correct_score']) ? $_POST['correct_score'] : null; $incorrectScore = isset($_POST['incorrect_score']) ? $_POST['incorrect_score'] : null; $useCustomScore = isset($_POST['user_custom_score']) ? true : false; $propagateNegative = 0; if ($useCustomScore && !empty($incorrectScore)) { if ($incorrectScore < 0) { $propagateNegative = 1; } } // Variables $quiz_index = 0; $question_title_index = array(); $question_name_index_init = array(); $question_name_index_end = array(); $score_index = array(); $feedback_true_index = array(); $feedback_false_index = array(); $number_questions = 0; $question_description_index = array(); // Reading all the first column items sequentially to create breakpoints for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) { if ($data->sheets[0]['cells'][$i][1] == 'Quiz' && $i == 1) { $quiz_index = $i; // Quiz title position, only occurs once } elseif ($data->sheets[0]['cells'][$i][1] == 'Question') { $question_title_index[] = $i; // Question title position line $question_name_index_init[] = $i + 1; // Questions name 1st position line $number_questions++; } elseif ($data->sheets[0]['cells'][$i][1] == 'Score') { $question_name_index_end[] = $i - 1; // Question name position $score_index[] = $i; // Question score position } elseif ($data->sheets[0]['cells'][$i][1] == 'FeedbackTrue') { $feedback_true_index[] = $i; // FeedbackTrue position (line) } elseif ($data->sheets[0]['cells'][$i][1] == 'FeedbackFalse') { $feedback_false_index[] = $i; // FeedbackFalse position (line) } elseif ($data->sheets[0]['cells'][$i][1] == 'EnrichQuestion') { $question_description_index[] = $i; } } // Variables $quiz = array(); $question = array(); $new_answer = array(); $score_list = array(); $feedback_true_list = array(); $feedback_false_list = array(); $question_description = array(); // Getting questions. $k = $z = $q = $l = $m = 0; for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) { if (is_array($data->sheets[0]['cells'][$i])) { $column_data = $data->sheets[0]['cells'][$i]; // Fill all column with data to have a full array for ($x = 1; $x <= $data->sheets[0]['numCols']; $x++) { if (empty($column_data[$x])) { $data->sheets[0]['cells'][$i][$x] = ''; } } // Array filled with data $column_data = $data->sheets[0]['cells'][$i]; } else { $column_data = ''; } // Fill quiz data if ($quiz_index == $i) { // The title always in the first position $quiz = $column_data; } elseif (in_array($i, $question_title_index)) { //a complete line where 1st column is 'Question' $question[$k] = $column_data; $k++; } elseif (in_array($i, $score_index)) { //a complete line where 1st column is 'Score' $score_list[$z] = $column_data; $z++; } elseif (in_array($i, $feedback_true_index)) { //a complete line where 1st column is 'FeedbackTrue' $feedback_true_list[$q] = $column_data; $q++; } elseif (in_array($i, $feedback_false_index)) { //a complete line where 1st column is 'FeedbackFalse' for wrong answers $feedback_false_list[$l] = $column_data; $l++; } elseif (in_array($i, $question_description_index)) { //a complete line where 1st column is 'EnrichQuestion' $question_description[$m] = $column_data; $m++; } } // Get answers for ($i = 0; $i < count($question_name_index_init); $i++) { for ($j = $question_name_index_init[$i]; $j <= $question_name_index_end[$i]; $j++) { if (is_array($data->sheets[0]['cells'][$j])) { $column_data = $data->sheets[0]['cells'][$j]; // Fill all column with data for ($x = 1; $x <= $data->sheets[0]['numCols']; $x++) { if (empty($column_data[$x])) { $data->sheets[0]['cells'][$j][$x] = ''; } } $column_data = $data->sheets[0]['cells'][$j]; // Array filled of data if (is_array($data->sheets[0]['cells'][$j]) && count($data->sheets[0]['cells'][$j]) > 0) { $new_answer[$i][$j] = $data->sheets[0]['cells'][$j]; } } } } // Quiz title. $quiz_title = $quiz[2]; if ($quiz_title != '') { // Variables $type = 2; $random = $active = $results = $max_attempt = $expired_time = 0; // Make sure feedback is enabled (3 to disable), otherwise the fields // added to the XLS are not shown, which is confusing $feedback = 0; // Quiz object $exercise = new Exercise(); // $quiz_id = $exercise->createExercise($quiz_title, $expired_time, $type, $random, $active, $results, $max_attempt, $feedback, $propagateNegative); if ($quiz_id) { // insert into the item_property table api_item_property_update($_course, TOOL_QUIZ, $quiz_id, 'QuizAdded', api_get_user_id()); // Import questions. for ($i = 0; $i < $number_questions; $i++) { // Question name $question_title = $question[$i][2]; $question_description_text = "<p></p>"; if (isset($question_description[$i][2])) { // Question description. $question_description_text = "<p>" . $question_description[$i][2] . "</p>"; } // Unique answers are the only question types available for now // through xls-format import $question_id = null; $detectQuestionType = detectQuestionType($new_answer[$i], $score_list); /** @var Question $answer */ switch ($detectQuestionType) { case FREE_ANSWER: $answer = new FreeAnswer(); break; case GLOBAL_MULTIPLE_ANSWER: $answer = new GlobalMultipleAnswer(); break; case MULTIPLE_ANSWER: $answer = new MultipleAnswer(); break; case UNIQUE_ANSWER: default: $answer = new UniqueAnswer(); break; } if ($question_title != '') { $question_id = $answer->create_question($quiz_id, $question_title, $question_description_text, 0, $answer->type); } $total = 0; if (is_array($new_answer[$i]) && !empty($question_id)) { $id = 1; $answers_data = $new_answer[$i]; $globalScore = null; $objAnswer = new Answer($question_id, $courseId); $globalScore = $score_list[$i][3]; // Calculate the number of correct answers to divide the // score between them when importing from CSV $numberRightAnswers = 0; foreach ($answers_data as $answer_data) { if (strtolower($answer_data[3]) == 'x') { $numberRightAnswers++; } } foreach ($answers_data as $answer_data) { $answerValue = $answer_data[2]; $correct = 0; $score = 0; if (strtolower($answer_data[3]) == 'x') { $correct = 1; $score = $score_list[$i][3]; $comment = $feedback_true_list[$i][2]; } else { $comment = $feedback_false_list[$i][2]; $floatVal = (double) $answer_data[3]; if (is_numeric($floatVal)) { $score = $answer_data[3]; } } if ($useCustomScore) { if ($correct) { $score = $correctScore; } else { $score = $incorrectScore; } } // Fixing scores: switch ($detectQuestionType) { case GLOBAL_MULTIPLE_ANSWER: $score /= $numberRightAnswers; break; case UNIQUE_ANSWER: break; case MULTIPLE_ANSWER: if (!$correct) { //$total = $total - $score; } break; } $objAnswer->createAnswer($answerValue, $correct, $comment, $score, $id); $total += $score; $id++; } $objAnswer->save(); $questionObj = Question::read($question_id, $courseId); switch ($detectQuestionType) { case GLOBAL_MULTIPLE_ANSWER: $questionObj->updateWeighting($globalScore); break; case UNIQUE_ANSWER: case MULTIPLE_ANSWER: default: $questionObj->updateWeighting($total); break; } $questionObj->save(); } else { if ($detectQuestionType === FREE_ANSWER) { $questionObj = Question::read($question_id, $courseId); $globalScore = $score_list[$i][3]; $questionObj->updateWeighting($globalScore); $questionObj->save(); } } } } if (isset($_SESSION['lpobject'])) { if ($debug > 0) { error_log('New LP - SESSION[lpobject] is defined', 0); } $oLP = unserialize($_SESSION['lpobject']); if (is_object($oLP)) { if ($debug > 0) { error_log('New LP - oLP is object', 0); } if (empty($oLP->cc) or $oLP->cc != api_get_course_id()) { if ($debug > 0) { error_log('New LP - Course has changed, discard lp object', 0); } $oLP = null; Session::erase('oLP'); Session::erase('lpobject'); } else { $_SESSION['oLP'] = $oLP; } } } if (isset($_SESSION['oLP']) && isset($_GET['lp_id'])) { $previous = $_SESSION['oLP']->select_previous_item_id(); $parent = 0; // Add a Quiz as Lp Item $_SESSION['oLP']->add_item($parent, $previous, TOOL_QUIZ, $quiz_id, $quiz_title, ''); // Redirect to home page for add more content header('location: ../newscorm/lp_controller.php?' . api_get_cidreq() . '&action=add_item&type=step&lp_id=' . Security::remove_XSS($_GET['lp_id'])); exit; } else { // header('location: exercise.php?' . api_get_cidreq()); echo '<script>window.location.href = "' . api_get_path(WEB_CODE_PATH) . 'exercice/admin.php?' . api_get_cidReq() . '&exerciseId=' . $quiz_id . '&session_id=' . api_get_session_id() . '"</script>'; } } }
/** 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; }
/** * 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; }
$course_code = ''; } 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++; }