Fixing qti import total question weight bug see BT#7780

Fixing export to QTI
1.9.x
Julio Montoya 11 years ago
parent f437236b69
commit 4f9c0ebeea
  1. 45
      main/exercice/exercice.php
  2. 315
      main/exercice/export/exercise_import.inc.php
  3. 177
      main/exercice/export/qti2/qti2_classes.php
  4. 165
      main/exercice/export/qti2/qti2_export.php
  5. 22
      main/inc/lib/fckeditor/editor/plugins/ajaxfilemanager/inc/class.pagination.php

@ -1,5 +1,4 @@
<?php <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
/** /**
* Exercise list: This script shows the list of exercises for administrators and students. * Exercise list: This script shows the list of exercises for administrators and students.
@ -122,30 +121,40 @@ if (!empty($gradebook) && $gradebook == 'view') {
} }
$nameTools = get_lang('Exercices'); $nameTools = get_lang('Exercices');
$errorXmlExport = null;
if ($is_allowedToEdit && !empty($choice) && $choice == 'exportqti2') { if ($is_allowedToEdit && !empty($choice) && $choice == 'exportqti2') {
require_once 'export/qti2/qti2_export.php'; require_once 'export/qti2/qti2_export.php';
$export = export_exercise($exerciseId, true); $export = export_exercise($exerciseId, true);
require_once api_get_path(LIBRARY_PATH).'pclzip/pclzip.lib.php'; require_once api_get_path(LIBRARY_PATH).'pclzip/pclzip.lib.php';
$archive_path = api_get_path(SYS_ARCHIVE_PATH); $archive_path = api_get_path(SYS_ARCHIVE_PATH);
$temp_dir_short = api_get_unique_id(); $temp_dir_short = api_get_unique_id();
$temp_zip_dir = $archive_path."/".$temp_dir_short; $temp_zip_dir = $archive_path.$temp_dir_short;
if (!is_dir($temp_zip_dir)) if (!is_dir($temp_zip_dir)) {
mkdir($temp_zip_dir, api_get_permissions_for_new_directories()); mkdir($temp_zip_dir, api_get_permissions_for_new_directories());
}
$temp_zip_file = $temp_zip_dir."/".api_get_unique_id().".zip"; $temp_zip_file = $temp_zip_dir."/".api_get_unique_id().".zip";
$temp_xml_file = $temp_zip_dir."/qti2export_".$exerciseId.'.xml'; $temp_xml_file = $temp_zip_dir."/qti2export_".$exerciseId.'.xml';
file_put_contents($temp_xml_file, $export); file_put_contents($temp_xml_file, $export);
$zip_folder = new PclZip($temp_zip_file);
$zip_folder->add($temp_xml_file, PCLZIP_OPT_REMOVE_ALL_PATH); $xmlReader = new XMLReader();
$name = 'qti2_export_'.$exerciseId.'.zip'; $xmlReader->open($temp_xml_file);
$xmlReader->setParserProperty(XMLReader::VALIDATE, true);
//DocumentManager::string_send_for_download($export,true,'qti2export_'.$exerciseId.'.xml'); $isValid = $xmlReader->isValid();
DocumentManager :: file_send_for_download($temp_zip_file, true, $name);
unlink($temp_zip_file); if ($isValid) {
unlink($temp_xml_file); $zip_folder = new PclZip($temp_zip_file);
rmdir($temp_zip_dir); $zip_folder->add($temp_xml_file, PCLZIP_OPT_REMOVE_ALL_PATH);
exit; //otherwise following clicks may become buggy $name = 'qti2_export_'.$exerciseId.'.zip';
DocumentManager::file_send_for_download($temp_zip_file, true, $name);
unlink($temp_zip_file);
unlink($temp_xml_file);
rmdir($temp_zip_dir);
exit; //otherwise following clicks may become buggy
} else {
$errorXmlExport = Display :: return_message(get_lang('ErrorWritingXMLFile'), 'error');
}
} }
$htmlHeadXtra[] = '<script> $htmlHeadXtra[] = '<script>
@ -195,14 +204,16 @@ event_access_tool(TOOL_QUIZ);
// Tool introduction // Tool introduction
Display :: display_introduction_section(TOOL_QUIZ); Display :: display_introduction_section(TOOL_QUIZ);
if (!empty($errorXmlExport)) {
echo $errorXmlExport;
}
HotPotGCt($documentPath, 1, api_get_user_id()); HotPotGCt($documentPath, 1, api_get_user_id());
// only for administrator // only for administrator
if ($is_allowedToEdit) { if ($is_allowedToEdit) {
if (!empty($choice)) { if (!empty($choice)) {
// All test choice, clean all test's results // All test choice, clean all test's results
if ($choice == 'clean_all_test') { if ($choice == 'clean_all_test') {
$check = Security::check_token('get'); $check = Security::check_token('get');
@ -649,7 +660,7 @@ if (!empty($exercise_list)) {
} }
} }
// Export qti ... // Export qti ...
$actions .= Display::url(Display::return_icon('export_qti2.png', 'IMS/QTI', '', ICON_SIZE_SMALL), 'exercice.php?choice=exportqti2&exerciseId='.$row['id']); $actions .= Display::url(Display::return_icon('export_qti2.png', 'IMS/QTI', '', ICON_SIZE_SMALL), 'exercice.php?choice=exportqti2&exerciseId='.$row['id'].'&'.api_get_cidreq());
} else { } else {
// not session // not session
$actions = Display::return_icon('edit_na.png', get_lang('ExerciseEditionNotAvailableInSession')); $actions = Display::return_icon('edit_na.png', get_lang('ExerciseEditionNotAvailableInSession'));

@ -109,7 +109,7 @@ function import_exercise($file)
$file_found = false; $file_found = false;
$operation = false; $operation = false;
$result = false; $result = false;
$filePath = null;
// parse every subdirectory to search xml question files // parse every subdirectory to search xml question files
while (false !== ($file = readdir($exerciseHandle))) { while (false !== ($file = readdir($exerciseHandle))) {
if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") { if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") {
@ -118,23 +118,32 @@ function import_exercise($file)
while (false !== ($questionFile = readdir($questionHandle))) { while (false !== ($questionFile = readdir($questionHandle))) {
if (preg_match('/.xml$/i', $questionFile)) { if (preg_match('/.xml$/i', $questionFile)) {
$result = parse_file($baseWorkDir, $file, $questionFile); $result = parse_file($baseWorkDir, $file, $questionFile);
$filePath = $baseWorkDir.$file;
$file_found = true; $file_found = true;
} }
} }
} elseif (preg_match('/.xml$/i', $file)) { } elseif (preg_match('/.xml$/i', $file)) {
// Else ignore file
$result = parse_file($baseWorkDir, '', $file); $result = parse_file($baseWorkDir, '', $file);
$filePath = $baseWorkDir.'/'.$file;
$file_found = true; $file_found = true;
} // else ignore file }
} }
if (!$file_found) { if (!$file_found) {
Display :: display_error_message(get_lang('No XML file found in the zip')); Display :: display_error_message(get_lang('No XML file found in the zip'));
return false; return false;
} }
if ($result == false ) {
if ($result == false) {
return false; return false;
} }
$doc = new DOMDocument();
$doc->load($filePath);
$encoding = $doc->encoding;
// 1. Create exercise. // 1. Create exercise.
$exercise = new Exercise(); $exercise = new Exercise();
$exercise->exercise = $exercise_info['name']; $exercise->exercise = $exercise_info['name'];
@ -143,15 +152,16 @@ function import_exercise($file)
$last_exercise_id = $exercise->selectId(); $last_exercise_id = $exercise->selectId();
if (!empty($last_exercise_id)) { if (!empty($last_exercise_id)) {
// For each question found... // For each question found...
foreach ($exercise_info['question'] as $key => $question_array) { foreach ($exercise_info['question'] as $question_array) {
//2. Create question //2. Create question
$question = new Ims2Question(); $question = new Ims2Question();
$question->type = $question_array['type']; $question->type = $question_array['type'];
$question->setAnswer(); $question->setAnswer();
$question->updateTitle($question_array['title']); // question ... $question->updateTitle(formatText($question_array['title']));
//$question->updateDescription($question_array['title']);
$type = $question->selectType(); $type = $question->selectType();
$question->type = constant($type); // type ... $question->type = constant($type);
$question->save($last_exercise_id); // save computed grade $question->save($last_exercise_id);
$last_question_id = $question->selectId(); $last_question_id = $question->selectId();
//3. Create answer //3. Create answer
$answer = new Answer($last_question_id); $answer = new Answer($last_question_id);
@ -161,9 +171,9 @@ function import_exercise($file)
$split = explode('_', $key); $split = explode('_', $key);
$i = $split[1]; $i = $split[1];
// Answer // Answer
$answer->new_answer[$i] = $answers['value']; $answer->new_answer[$i] = formatText($answers['value']);
// Comment // Comment
$answer->new_comment[$i] = isset($answers['feedback']) ? $answers['feedback'] : null; $answer->new_comment[$i] = isset($answers['feedback']) ? formatText($answers['feedback']) : null;
// Position // Position
$answer->new_position[$i] = $i; $answer->new_position[$i] = $i;
// Correct answers // Correct answers
@ -188,6 +198,13 @@ function import_exercise($file)
} }
return $operation; return $operation;
} }
/**
* We assume the file charset is UTF8
**/
function formatText($text)
{
return api_html_entity_decode($text);
}
function parse_file($exercisePath, $file, $questionFile) function parse_file($exercisePath, $file, $questionFile)
{ {
@ -199,6 +216,7 @@ function parse_file($exercisePath, $file, $questionFile)
$questionTempDir = $exercisePath . '/' . $file . '/'; $questionTempDir = $exercisePath . '/' . $file . '/';
$questionFilePath = $questionTempDir . $questionFile; $questionFilePath = $questionTempDir . $questionFile;
if (!($fp = fopen($questionFilePath, 'r'))) { if (!($fp = fopen($questionFilePath, 'r'))) {
Display :: display_error_message(get_lang('Error opening question\'s XML file')); Display :: display_error_message(get_lang('Error opening question\'s XML file'));
return false; return false;
@ -245,15 +263,15 @@ function parse_file($exercisePath, $file, $questionFile)
"RESPONSECONDITION", "RESPONSECONDITION",
"RESPONSEIF" "RESPONSEIF"
); );
$question_format_supported = true;
$question_format_supported = true;
$xml_parser = xml_parser_create(); $xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, false); xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, false);
xml_set_element_handler($xml_parser, 'startElement', 'endElement'); xml_set_element_handler($xml_parser, 'startElement', 'endElement');
xml_set_character_data_handler($xml_parser, 'elementData'); xml_set_character_data_handler($xml_parser, 'elementData');
if (!xml_parse($xml_parser, $data, feof($fp))) { if (!xml_parse($xml_parser, $data, feof($fp))) {
// if reading of the xml file in not successfull : // if reading of the xml file in not successful :
// set errorFound, set error msg, break while statement // set errorFound, set error msg, break while statement
Display :: display_error_message(get_lang('Error reading XML file')); Display :: display_error_message(get_lang('Error reading XML file'));
return false; return false;
@ -329,143 +347,89 @@ function startElement($parser, $name, $attributes) {
$current_question_item_body .= "<BR/>"; $current_question_item_body .= "<BR/>";
} }
} }
} }
switch ($current_element) { switch ($current_element) {
case 'ASSESSMENTITEM' : case 'ASSESSMENTITEM' :
{ //retrieve current question
//retrieve current question $current_question_ident = $attributes['IDENTIFIER'];
$exercise_info['question'][$current_question_ident] = array ();
$current_question_ident = $attributes['IDENTIFIER']; $exercise_info['question'][$current_question_ident]['answer'] = array ();
$exercise_info['question'][$current_question_ident] = array (); $exercise_info['question'][$current_question_ident]['correct_answers'] = array ();
$exercise_info['question'][$current_question_ident]['answer'] = array (); $exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE'];
$exercise_info['question'][$current_question_ident]['correct_answers'] = array (); $exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir;
$exercise_info['question'][$current_question_ident]['title'] = $attributes['TITLE']; break;
$exercise_info['question'][$current_question_ident]['tempdir'] = $questionTempDir; case 'SECTION':
} //retrieve exercise name
break; $exercise_info['name'] = $attributes['TITLE'];
case 'SECTION' :
{
//retrieve exercise name
$exercise_info['name'] = $attributes['TITLE'];
}
break; break;
case 'RESPONSEDECLARATION':
case 'RESPONSEDECLARATION' : // Retrieve question type
{ if ("multiple" == $attributes['CARDINALITY']) {
//retrieve question type $exercise_info['question'][$current_question_ident]['type'] = 'MCMA';
$cardinality = 'multiple';
if ("multiple" == $attributes['CARDINALITY']) { }
$exercise_info['question'][$current_question_ident]['type'] = 'MCMA'; if ("single" == $attributes['CARDINALITY']) {
$cardinality = 'multiple'; $exercise_info['question'][$current_question_ident]['type'] = 'MCUA';
} $cardinality = 'single';
if ("single" == $attributes['CARDINALITY']) { }
$exercise_info['question'][$current_question_ident]['type'] = 'MCUA'; //needed for FIB
$cardinality = 'single'; $current_answer_id = $attributes['IDENTIFIER'];
}
//needed for FIB
$current_answer_id = $attributes['IDENTIFIER'];
}
break; break;
case 'INLINECHOICEINTERACTION' : case 'INLINECHOICEINTERACTION' :
{ $exercise_info['question'][$current_question_ident]['type'] = 'FIB';
$exercise_info['question'][$current_question_ident]['type'] = 'FIB'; $exercise_info['question'][$current_question_ident]['subtype'] = 'LISTBOX_FILL';
$exercise_info['question'][$current_question_ident]['subtype'] = 'LISTBOX_FILL'; $current_answer_id = $attributes['RESPONSEIDENTIFIER'];
$current_answer_id = $attributes['RESPONSEIDENTIFIER'];
}
break; break;
case 'INLINECHOICE' : case 'INLINECHOICE' :
{ $current_inlinechoice_id = $attributes['IDENTIFIER'];
$current_inlinechoice_id = $attributes['IDENTIFIER'];
}
break; break;
case 'TEXTENTRYINTERACTION':
case 'TEXTENTRYINTERACTION' : $exercise_info['question'][$current_question_ident]['type'] = 'FIB';
{ $exercise_info['question'][$current_question_ident]['subtype'] = 'TEXTFIELD_FILL';
$exercise_info['question'][$current_question_ident]['type'] = 'FIB'; $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
$exercise_info['question'][$current_question_ident]['subtype'] = 'TEXTFIELD_FILL'; //replace claroline tags
$exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
//replace claroline tags
}
break; break;
case 'MATCHINTERACTION':
case 'MATCHINTERACTION' : $exercise_info['question'][$current_question_ident]['type'] = 'MATCHING';
{
$exercise_info['question'][$current_question_ident]['type'] = 'MATCHING';
}
break; break;
case 'SIMPLEMATCHSET' : case 'SIMPLEMATCHSET' :
{ if (!isset ($current_match_set)) {
if (!isset ($current_match_set)) { $current_match_set = 1;
$current_match_set = 1; } else {
} else { $current_match_set++;
$current_match_set++; }
} $exercise_info['question'][$current_question_ident]['answer'][$current_match_set] = array ();
$exercise_info['question'][$current_question_ident]['answer'][$current_match_set] = array ();
}
break; break;
case 'SIMPLEASSOCIABLECHOICE':
case 'SIMPLEASSOCIABLECHOICE' : $currentAssociableChoice = $attributes['IDENTIFIER'];
{
$currentAssociableChoice = $attributes['IDENTIFIER'];
}
break; break;
//retrieve answers id for MCUA and MCMA questions //retrieve answers id for MCUA and MCMA questions
case 'SIMPLECHOICE':
case 'SIMPLECHOICE' :
$current_answer_id = $attributes['IDENTIFIER']; $current_answer_id = $attributes['IDENTIFIER'];
if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id])) { if (!isset($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id])) {
$exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = array (); $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id] = array ();
} }
break; break;
case 'MAPENTRY':
if ($parent_element == "MAPPING") {
$answer_id = $attributes['MAPKEY'];
case 'MAPENTRY' : if (!isset ($exercise_info['question'][$current_question_ident]['weighting'])) {
{ $exercise_info['question'][$current_question_ident]['weighting'] = array ();
if ($parent_element == "MAPPING") { }
$answer_id = $attributes['MAPKEY']; $exercise_info['question'][$current_question_ident]['weighting'][$answer_id] = $attributes['MAPPEDVALUE'];
}
if (!isset ($exercise_info['question'][$current_question_ident]['weighting'])) {
$exercise_info['question'][$current_question_ident]['weighting'] = array ();
}
$exercise_info['question'][$current_question_ident]['weighting'][$answer_id] = $attributes['MAPPEDVALUE'];
}
}
break; break;
case 'MAPPING':
case 'MAPPING' : if (isset ($attributes['DEFAULTVALUE'])) {
{ $exercise_info['question'][$current_question_ident]['default_weighting'] = $attributes['DEFAULTVALUE'];
if (isset ($attributes['DEFAULTVALUE'])) { }
$exercise_info['question'][$current_question_ident]['default_weighting'] = $attributes['DEFAULTVALUE']; case 'ITEMBODY':
} $record_item_body = true;
} $current_question_item_body = '';
case 'ITEMBODY' :
{
$record_item_body = true;
$current_question_item_body = '';
}
break; break;
case 'IMG':
case 'IMG' : $exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
{
$exercise_info['question'][$current_question_ident]['attached_file_url'] = $attributes['SRC'];
}
break; break;
} }
} }
@ -495,15 +459,13 @@ function endElement($parser, $name) {
} }
switch ($name) { switch ($name) {
case 'ITEMBODY' : case 'ITEMBODY':
{ $record_item_body = false;
$record_item_body = false; if ($exercise_info['question'][$current_question_ident]['type'] == 'FIB') {
if ($exercise_info['question'][$current_question_ident]['type'] == 'FIB') { $exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body;
$exercise_info['question'][$current_question_ident]['response_text'] = $current_question_item_body; } else {
} else { $exercise_info['question'][$current_question_ident]['statement'] = $current_question_item_body;
$exercise_info['question'][$current_question_ident]['statement'] = $current_question_item_body; }
}
}
break; break;
} }
array_pop($element_pile); array_pop($element_pile);
@ -541,68 +503,47 @@ function elementData($parser, $data) {
} }
switch ($current_element) { switch ($current_element) {
case 'SIMPLECHOICE' : case 'SIMPLECHOICE':
if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'])) {
if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'])) { $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] = trim($data);
$exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] = trim($data); } else {
} else { $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] .= ''.trim($data);
$exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['value'] .= ''.trim($data); }
}
break; break;
case 'FEEDBACKINLINE' : case 'FEEDBACKINLINE' :
{ if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'])) {
if (!isset ($exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'])) { $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] = trim($data);
$exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] = trim($data); } else {
} else { $exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] .= ' ' . trim($data);
$exercise_info['question'][$current_question_ident]['answer'][$current_answer_id]['feedback'] .= ' ' . trim($data); }
}
}
break; break;
case 'SIMPLEASSOCIABLECHOICE':
case 'SIMPLEASSOCIABLECHOICE' : $exercise_info['question'][$current_question_ident]['answer'][$current_match_set][$currentAssociableChoice] = trim($data);
{
$exercise_info['question'][$current_question_ident]['answer'][$current_match_set][$currentAssociableChoice] = trim($data);
}
break; break;
case 'VALUE' : case 'VALUE' :
{ if ($parent_element == "CORRECTRESPONSE") {
if ($parent_element == "CORRECTRESPONSE") { if ($cardinality == "single") {
if ($cardinality == "single") { $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id] = $data;
$exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id] = $data; } else {
} else { $exercise_info['question'][$current_question_ident]['correct_answers'][] = $data;
$exercise_info['question'][$current_question_ident]['correct_answers'][] = $data; }
} }
}
}
break; break;
case 'ITEMBODY' : case 'ITEMBODY' :
{ $current_question_item_body .= $data;
$current_question_item_body .= $data;
}
break; break;
case 'INLINECHOICE' : case 'INLINECHOICE' :
{ // if this is the right answer, then we must replace the claroline tags in the FIB text bye the answer between "[" and "]" :
$answer_identifier = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id];
// if this is the right answer, then we must replace the claroline tags in the FIB text bye the answer between "[" and "]" : if ($current_inlinechoice_id == $answer_identifier) {
$current_question_item_body = str_replace("**claroline_start**" . $current_answer_id . "**claroline_end**", "[" . $data . "]", $current_question_item_body);
$answer_identifier = $exercise_info['question'][$current_question_ident]['correct_answers'][$current_answer_id]; } else {
if (!isset ($exercise_info['question'][$current_question_ident]['wrong_answers'])) {
if ($current_inlinechoice_id == $answer_identifier) { $exercise_info['question'][$current_question_ident]['wrong_answers'] = array ();
}
$current_question_item_body = str_replace("**claroline_start**" . $current_answer_id . "**claroline_end**", "[" . $data . "]", $current_question_item_body); $exercise_info['question'][$current_question_ident]['wrong_answers'][] = $data;
} else // save wrong answers in an array }
{
if (!isset ($exercise_info['question'][$current_question_ident]['wrong_answers'])) {
$exercise_info['question'][$current_question_ident]['wrong_answers'] = array ();
}
$exercise_info['question'][$current_question_ident]['wrong_answers'][] = $data;
}
}
break; break;
} }
} }

@ -1,4 +1,4 @@
<?php // $Id: $ <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
/** /**
* @author Claro Team <cvs@claroline.net> * @author Claro Team <cvs@claroline.net>
@ -8,14 +8,6 @@
/** /**
* Code * Code
*/ */
if ( count( get_included_files() ) == 1 ) die( '---' );
if (!function_exists('mime_content_type')) {
require_once api_get_path(LIBRARY_PATH).'document.lib.php';
function mime_content_type($filename) {
return DocumentManager::file_get_mime_type((string)$filename);
}
}
require_once(api_get_path(SYS_CODE_PATH).'/exercice/answer.class.php'); require_once(api_get_path(SYS_CODE_PATH).'/exercice/answer.class.php');
require_once(api_get_path(SYS_CODE_PATH).'/exercice/exercise.class.php'); require_once(api_get_path(SYS_CODE_PATH).'/exercice/exercise.class.php');
@ -39,32 +31,40 @@ class Ims2Question extends Question
{ {
/** /**
* Include the correct answer class and create answer * Include the correct answer class and create answer
* @return Answer
*/ */
function setAnswer() public function setAnswer()
{ {
switch($this->type) { switch($this->type) {
case MCUA : case MCUA:
$answer = new ImsAnswerMultipleChoice($this->id); $answer = new ImsAnswerMultipleChoice($this->id);
return $answer; return $answer;
case MCMA : case MCMA:
$answer = new ImsAnswerMultipleChoice($this->id); $answer = new ImsAnswerMultipleChoice($this->id);
return $answer; return $answer;
case TF : case TF :
$answer = new ImsAnswerMultipleChoice($this->id); $answer = new ImsAnswerMultipleChoice($this->id);
return $answer; return $answer;
case FIB : case FIB:
$answer = new ImsAnswerFillInBlanks($this->id); $answer = new ImsAnswerFillInBlanks($this->id);
return $answer; return $answer;
case MATCHING : case MATCHING:
$answer = new ImsAnswerMatching($this->id); $answer = new ImsAnswerMatching($this->id);
return $answer; return $answer;
case FREE_ANSWER : case FREE_ANSWER:
$answer = new ImsAnswerFree($this->id); $answer = new ImsAnswerFree($this->id);
return $answer; return $answer;
case HOT_SPOT : case HOT_SPOT:
$answer = new ImsAnswerHotspot($this->id); $answer = new ImsAnswerHotspot($this->id);
return $answer; return $answer;
default : default:
$answer = null; $answer = null;
break; break;
} }
@ -92,21 +92,26 @@ class ImsAnswerMultipleChoice extends Answer
* Return the XML flow for the possible answers. * Return the XML flow for the possible answers.
* *
*/ */
function imsExportResponses($questionIdent, $questionStatment) public function imsExportResponses($questionIdent, $questionStatment)
{ {
// @todo getAnswersList() converts the answers using api_html_entity_decode()
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$out = ' <choiceInteraction responseIdentifier="' . $questionIdent . '" >' . "\n"; $out = ' <choiceInteraction responseIdentifier="' . $questionIdent . '" >' . "\n";
$out .= ' <prompt> ' . $questionStatment . ' </prompt>'. "\n"; $out .= ' <prompt><![CDATA['.formatExerciseQtiTitle($questionStatment) . ']]></prompt>'. "\n";
if (is_array($this->answerList)) { if (is_array($this->answerList)) {
foreach ($this->answerList as $current_answer) { foreach ($this->answerList as $current_answer) {
$out .= ' <simpleChoice identifier="answer_' . $current_answer['id'] . '" fixed="false">' . $current_answer['answer']; $out .= '<simpleChoice identifier="answer_' . $current_answer['id'] . '" fixed="false">
<![CDATA['.formatExerciseQtiTitle($current_answer['answer']).']]>';
if (isset($current_answer['comment']) && $current_answer['comment'] != '') { if (isset($current_answer['comment']) && $current_answer['comment'] != '') {
$out .= '<feedbackInline identifier="answer_' . $current_answer['id'] . '">' . $current_answer['comment'] . '</feedbackInline>'; $out .= '<feedbackInline identifier="answer_' . $current_answer['id'] . '">
<![CDATA['.formatExerciseQtiTitle($current_answer['comment']).']]>
</feedbackInline>';
} }
$out .= '</simpleChoice>'. "\n"; $out .= '</simpleChoice>'. "\n";
} }
} }
$out .= ' </choiceInteraction>'. "\n"; $out .= ' </choiceInteraction>'. "\n";
return $out; return $out;
} }
@ -114,7 +119,7 @@ class ImsAnswerMultipleChoice extends Answer
* Return the XML flow of answer ResponsesDeclaration * Return the XML flow of answer ResponsesDeclaration
* *
*/ */
function imsExportResponsesDeclaration($questionIdent) public function imsExportResponsesDeclaration($questionIdent)
{ {
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$type = $this->getQuestionType(); $type = $this->getQuestionType();
@ -122,7 +127,7 @@ class ImsAnswerMultipleChoice extends Answer
$out = ' <responseDeclaration identifier="' . $questionIdent . '" cardinality="' . $cardinality . '" baseType="identifier">' . "\n"; $out = ' <responseDeclaration identifier="' . $questionIdent . '" cardinality="' . $cardinality . '" baseType="identifier">' . "\n";
//Match the correct answers // Match the correct answers.
$out .= ' <correctResponse>'. "\n"; $out .= ' <correctResponse>'. "\n";
if (is_array($this->answerList)) { if (is_array($this->answerList)) {
@ -162,70 +167,20 @@ class ImsAnswerFillInBlanks extends Answer
* *
* *
*/ */
function imsExportResponses($questionIdent, $questionStatment) public function imsExportResponses($questionIdent, $questionStatment)
{ {
global $charset;
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$text = '';
//switch ($this->type) $text .= $this->answerText;
//{ if (is_array($this->answerList)) {
// case TEXTFIELD_FILL : foreach ($this->answerList as $key=>$answer) {
// { $key = $answer['id'];
$text = ''; $answer = $answer['answer'];
$text .= $this->answerText; $len = api_strlen($answer);
if (is_array($this->answerList)) { $text = str_replace('['.$answer.']','<textEntryInteraction responseIdentifier="fill_'.$key.'" expectedLength="'.api_strlen($answer).'"/>', $text);
foreach ($this->answerList as $key=>$answer) {
$key = $answer['id'];
$answer = $answer['answer'];
$len = api_strlen($answer);
$text = str_replace('['.$answer.']','<textEntryInteraction responseIdentifier="fill_'.$key.'" expectedLength="'.api_strlen($answer).'"/>', $text);
}
}
$out = $text;
// }
// break;
/*
case LISTBOX_FILL :
{
$text = $this->answerText;
foreach ($this->answerList as $answerKey=>$answer)
{
//build inlinechoice list
$inlineChoiceList = '';
//1-start interaction tag
$inlineChoiceList .= '<inlineChoiceInteraction responseIdentifier="fill_'.$answerKey.'" >'. "\n";
//2- add wrong answer array
foreach ($this->wrongAnswerList as $choiceKey=>$wrongAnswer)
{
$inlineChoiceList .= ' <inlineChoice identifier="choice_w_'.$answerKey.'_'.$choiceKey.'">'.$wrongAnswer.'</inlineChoice>'. "\n";
}
//3- add correct answers array
foreach ($this->answerList as $choiceKey=>$correctAnswer)
{
$inlineChoiceList .= ' <inlineChoice identifier="choice_c_'.$answerKey.'_'.$choiceKey.'">'.$correctAnswer.'</inlineChoice>'. "\n";
}
//4- finish interaction tag
$inlineChoiceList .= '</inlineChoiceInteraction>';
$text = str_replace('['.$answer.']',$inlineChoiceList, $text);
}
$out = $text;
} }
break; }
*/ $out = $text;
//}
return $out; return $out;
} }
@ -233,39 +188,19 @@ class ImsAnswerFillInBlanks extends Answer
/** /**
* *
*/ */
function imsExportResponsesDeclaration($questionIdent) public function imsExportResponsesDeclaration($questionIdent)
{ {
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$this->gradeList = $this->getGradesList(); $this->gradeList = $this->getGradesList();
$out = ''; $out = '';
if (is_array($this->answerList)) { if (is_array($this->answerList)) {
foreach ($this->answerList as $answerKey=>$answer) { foreach ($this->answerList as $answer) {
$answerKey = $answer['id']; $answerKey = $answer['id'];
$answer = $answer['answer']; $answer = $answer['answer'];
$out .= ' <responseDeclaration identifier="fill_' . $answerKey . '" cardinality="single" baseType="identifier">' . "\n"; $out .= ' <responseDeclaration identifier="fill_' . $answerKey . '" cardinality="single" baseType="identifier">' . "\n";
$out .= ' <correctResponse>'. "\n"; $out .= ' <correctResponse>'. "\n";
$out .= ' <value><![CDATA['.formatExerciseQtiTitle($answer).']]></value>'. "\n";
//if ($this->type==TEXTFIELD_FILL)
//{
$out .= ' <value>'.$answer.'</value>'. "\n";
//}
/*
else
{
//find correct answer key to apply in manifest and output it
foreach ($this->answerList as $choiceKey=>$correctAnswer)
{
if ($correctAnswer==$answer)
{
$out .= ' <value>choice_c_'.$answerKey.'_'.$choiceKey.'</value>'. "\n";
}
}
}
*/
$out .= ' </correctResponse>'. "\n"; $out .= ' </correctResponse>'. "\n";
if (isset($this->gradeList[$answerKey])) { if (isset($this->gradeList[$answerKey])) {
$out .= ' <mapping>'. "\n"; $out .= ' <mapping>'. "\n";
$out .= ' <mapEntry mapKey="'.$answer.'" mappedValue="'.$this->gradeList[$answerKey].'"/>'. "\n"; $out .= ' <mapEntry mapKey="'.$answer.'" mappedValue="'.$this->gradeList[$answerKey].'"/>'. "\n";
@ -289,7 +224,7 @@ class ImsAnswerMatching extends Answer
/** /**
* Export the question part as a matrix-choice, with only one possible answer per line. * Export the question part as a matrix-choice, with only one possible answer per line.
*/ */
function imsExportResponses($questionIdent, $questionStatment) public function imsExportResponses($questionIdent, $questionStatment)
{ {
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$maxAssociation = max(count($this->leftList), count($this->rightList)); $maxAssociation = max(count($this->leftList), count($this->rightList));
@ -304,7 +239,10 @@ class ImsAnswerMatching extends Answer
$out .= ' <simpleMatchSet>'. "\n"; $out .= ' <simpleMatchSet>'. "\n";
if (is_array($this->leftList)) { if (is_array($this->leftList)) {
foreach ($this->leftList as $leftKey=>$leftElement) { foreach ($this->leftList as $leftKey=>$leftElement) {
$out .= ' <simpleAssociableChoice identifier="left_'.$leftKey.'" >'. $leftElement['answer'] .'</simpleAssociableChoice>'. "\n"; $out .= '
<simpleAssociableChoice identifier="left_'.$leftKey.'" >
<![CDATA['.formatExerciseQtiTitle($leftElement['answer']).']]>
</simpleAssociableChoice>'. "\n";
} }
} }
@ -318,7 +256,9 @@ class ImsAnswerMatching extends Answer
if (is_array($this->rightList)) { if (is_array($this->rightList)) {
foreach($this->rightList as $rightKey=>$rightElement) { foreach($this->rightList as $rightKey=>$rightElement) {
$out .= ' <simpleAssociableChoice identifier="right_'.$i.'" >'. $rightElement['answer'] .'</simpleAssociableChoice>'. "\n"; $out .= '<simpleAssociableChoice identifier="right_'.$i.'" >
<![CDATA['.formatExerciseQtiTitle($rightElement['answer']).']]>
</simpleAssociableChoice>'. "\n";
$i++; $i++;
} }
} }
@ -331,7 +271,7 @@ class ImsAnswerMatching extends Answer
/** /**
* *
*/ */
function imsExportResponsesDeclaration($questionIdent) public function imsExportResponsesDeclaration($questionIdent)
{ {
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$out = ' <responseDeclaration identifier="' . $questionIdent . '" cardinality="single" baseType="identifier">' . "\n"; $out = ' <responseDeclaration identifier="' . $questionIdent . '" cardinality="single" baseType="identifier">' . "\n";
@ -363,7 +303,6 @@ class ImsAnswerMatching extends Answer
return $out; return $out;
} }
} }
/** /**
@ -373,12 +312,11 @@ class ImsAnswerMatching extends Answer
class ImsAnswerHotspot extends Answer class ImsAnswerHotspot extends Answer
{ {
/** /**
* TODO update this to match hotspots instead of copying matching * TODO update this to match hot spots instead of copying matching
* Export the question part as a matrix-choice, with only one possible answer per line. * Export the question part as a matrix-choice, with only one possible answer per line.
*/ */
function imsExportResponses($questionIdent, $questionStatment, $questionDesc='', $questionMedia='') public function imsExportResponses($questionIdent, $questionStatment, $questionDesc='', $questionMedia='')
{ {
global $charset;
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$questionMedia = api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/images/'.$questionMedia; $questionMedia = api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document/images/'.$questionMedia;
$mimetype = mime_content_type($questionMedia); $mimetype = mime_content_type($questionMedia);
@ -427,13 +365,12 @@ class ImsAnswerHotspot extends Answer
$out = $text; $out = $text;
return $out; return $out;
} }
/** /**
* *
*/ */
function imsExportResponsesDeclaration($questionIdent) public function imsExportResponsesDeclaration($questionIdent)
{ {
$this->answerList = $this->getAnswersList(true); $this->answerList = $this->getAnswersList(true);
$this->gradeList = $this->getGradesList(); $this->gradeList = $this->getGradesList();
@ -445,7 +382,7 @@ class ImsAnswerHotspot extends Answer
foreach ($this->answerList as $answerKey=>$answer) { foreach ($this->answerList as $answerKey=>$answer) {
$answerKey = $answer['id']; $answerKey = $answer['id'];
$answer = $answer['answer']; $answer = $answer['answer'];
$out .= ' <value>'.$answerKey.'</value>'. "\n"; $out .= '<value><![CDATA['.formatExerciseQtiTitle($answerKey).']]></value>';
} }
} }
$out .= ' </correctResponse>'. "\n"; $out .= ' </correctResponse>'. "\n";
@ -465,14 +402,14 @@ class ImsAnswerFree extends Answer
* TODO implement * TODO implement
* Export the question part as a matrix-choice, with only one possible answer per line. * Export the question part as a matrix-choice, with only one possible answer per line.
*/ */
function imsExportResponses($questionIdent, $questionStatment, $questionDesc='', $questionMedia='') public function imsExportResponses($questionIdent, $questionStatment, $questionDesc='', $questionMedia='')
{ {
return ''; return '';
} }
/** /**
* *
*/ */
function imsExportResponsesDeclaration($questionIdent) public function imsExportResponsesDeclaration($questionIdent)
{ {
return ''; return '';
} }

@ -1,4 +1,4 @@
<?php // $Id: $ <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
/** /**
* @author Claro Team <cvs@claroline.net> * @author Claro Team <cvs@claroline.net>
@ -8,7 +8,6 @@
/** /**
* Code * Code
*/ */
if ( count( get_included_files() ) == 1 ) die( '---' );
require dirname(__FILE__) . '/qti2_classes.php'; require dirname(__FILE__) . '/qti2_classes.php';
/** /**
* Classes * Classes
@ -26,19 +25,18 @@ require dirname(__FILE__) . '/qti2_classes.php';
*/ */
class ImsAssessmentItem class ImsAssessmentItem
{ {
var $question; public $question;
var $question_ident; public $question_ident;
var $answer; public $answer;
/** /**
* Constructor. * Constructor.
* *
* @param $question The Question object we want to export. * @param $question Ims2Question object we want to export.
*/ */
function ImsAssessmentItem($question) function ImsAssessmentItem($question)
{ {
$this->question = $question; $this->question = $question;
//$this->answer = new Answer($question->id);
$this->answer = $this->question->setAnswer(); $this->answer = $this->question->setAnswer();
$this->questionIdent = "QST_" . $question->id ; $this->questionIdent = "QST_" . $question->id ;
} }
@ -51,20 +49,13 @@ class ImsAssessmentItem
*/ */
function start_item() function start_item()
{ {
/*
return '<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p0 imsqti_v2p0.xsd"
identifier="'.$this->questionIdent.'"
title="'.htmlspecialchars($this->question->selectTitle()).'">'."\n";
*/
$string = '<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1" $string = '<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 imsqti_v2p1.xsd" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 imsqti_v2p1.xsd"
identifier="'.$this->questionIdent.'" identifier="'.$this->questionIdent.'"
title="'.htmlspecialchars($this->question->selectTitle()).'">'."\n"; title="'.htmlspecialchars(formatExerciseQtiTitle($this->question->selectTitle())).'">'."\n";
return $string;
return $string;
} }
/** /**
@ -101,11 +92,9 @@ class ImsAssessmentItem
function add_response_processing() function add_response_processing()
{ {
//return ' <responseProcessing template="http://www.imsglobal.org/question/qti_v2p0/rptemplates/map_response"/>' . "\n";
return ' <responseProcessing template="http://www.imsglobal.org/question/qti_v2p1/rptemplates/map_correct"/>' . "\n"; return ' <responseProcessing template="http://www.imsglobal.org/question/qti_v2p1/rptemplates/map_correct"/>' . "\n";
} }
/** /**
* Export the question as an IMS/QTI Item. * Export the question as an IMS/QTI Item.
* *
@ -116,24 +105,31 @@ class ImsAssessmentItem
*/ */
function export($standalone = false) function export($standalone = false)
{ {
global $charset;
$head = $foot = ""; $head = $foot = "";
if( $standalone ) if ($standalone) {
{ $head = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . "\n";
$head = '<?xml version="1.0" encoding="'.$charset.'" standalone="no"?>' . "\n";
} }
//TODO understand why answer might be a non-object sometimes //TODO understand why answer might be a non-object sometimes
if (!is_object($this->answer)) { return $head; } if (!is_object($this->answer)) {
return $head;
}
$res = $head $res = $head
. $this->start_item() .$this->start_item()
.$this->answer->imsExportResponsesDeclaration($this->questionIdent) .$this->answer->imsExportResponsesDeclaration($this->questionIdent)
. $this->start_item_body() .$this->start_item_body()
. $this->answer->imsExportResponses($this->questionIdent, $this->question->question, $this->question->description, $this->question->picture) .$this->answer->imsExportResponses(
. $this->end_item_body() $this->questionIdent,
. $this->add_response_processing() $this->question->question,
. $this->end_item() $this->question->description,
. $foot; $this->question->picture
)
.$this->end_item_body()
.$this->add_response_processing()
.$this->end_item()
.$foot;
return $res; return $res;
} }
} }
@ -168,7 +164,7 @@ class ImsSection
function start_section() function start_section()
{ {
$out = '<section ident="EXO_' . $this->exercise->selectId() . '" title="' . $this->exercise->selectTitle() . '">' . "\n"; $out = '<section ident="EXO_' . $this->exercise->selectId() . '" title="' .cleanAttribute(formatExerciseQtiDescription($this->exercise->selectTitle())) . '">' . "\n";
return $out; return $out;
} }
@ -185,9 +181,7 @@ class ImsSection
$minutes = floor($max_time / 60); $minutes = floor($max_time / 60);
$seconds = $max_time % 60; $seconds = $max_time % 60;
return '<duration>PT' . $minutes . 'M' . $seconds . "S</duration>\n"; return '<duration>PT' . $minutes . 'M' . $seconds . "S</duration>\n";
} } else {
else
{
return ''; return '';
} }
} }
@ -199,7 +193,7 @@ class ImsSection
function export_presentation() function export_presentation()
{ {
$out = "<presentation_material><flow_mat><material>\n" $out = "<presentation_material><flow_mat><material>\n"
. " <mattext><![CDATA[" . $this->exercise->selectDescription() . "]]></mattext>\n" . " <mattext><![CDATA[" . formatExerciseQtiDescription($this->exercise->selectDescription()) . "]]></mattext>\n"
. "</material></flow_mat></presentation_material>\n"; . "</material></flow_mat></presentation_material>\n";
return $out; return $out;
} }
@ -211,6 +205,7 @@ class ImsSection
*/ */
function export_ordering() function export_ordering()
{ {
$out = ''; $out = '';
if ($n = $this->exercise->getShuffle()) { if ($n = $this->exercise->getShuffle()) {
$out.= "<selection_ordering>" $out.= "<selection_ordering>"
@ -219,9 +214,7 @@ class ImsSection
. " </selection>\n" . " </selection>\n"
. ' <order order_type="Random" />' . ' <order order_type="Random" />'
. "\n</selection_ordering>\n"; . "\n</selection_ordering>\n";
} } else {
else
{
$out.= '<selection_ordering sequence_type="Normal">' . "\n" $out.= '<selection_ordering sequence_type="Normal">' . "\n"
. " <selection />\n" . " <selection />\n"
. "</selection_ordering>\n"; . "</selection_ordering>\n";
@ -237,8 +230,7 @@ class ImsSection
function export_questions() function export_questions()
{ {
$out = ""; $out = "";
foreach ($this->exercise->selectQuestionList() as $q) foreach ($this->exercise->selectQuestionList() as $q) {
{
$out .= export_question($q, false); $out .= export_question($q, false);
} }
return $out; return $out;
@ -253,11 +245,9 @@ class ImsSection
*/ */
function export($standalone) function export($standalone)
{ {
global $charset;
$head = $foot = ""; $head = $foot = "";
if ($standalone) { if ($standalone) {
$head = '<?xml version = "1.0" encoding = "' . $charset . '" standalone = "no"?>' . "\n" $head = '<?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>' . "\n"
. '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n" . '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n"
. "<questestinterop>\n"; . "<questestinterop>\n";
$foot = "</questestinterop>\n"; $foot = "</questestinterop>\n";
@ -289,6 +279,8 @@ class ImsSection
Feedback identifier :: <Question identifier> + "_F_" + <Response Id from the DB> Feedback identifier :: <Question identifier> + "_F_" + <Response Id from the DB>
*/ */
/** /**
* Class ImsItem
*
* An IMS/QTI item. It corresponds to a single question. * An IMS/QTI item. It corresponds to a single question.
* This class allows export from Claroline to IMS/QTI XML format. * This class allows export from Claroline to IMS/QTI XML format.
* It is not usable as-is, but must be subclassed, to support different kinds of questions. * It is not usable as-is, but must be subclassed, to support different kinds of questions.
@ -297,13 +289,15 @@ class ImsSection
* *
* warning: Attached files are NOT exported. * warning: Attached files are NOT exported.
* @author Amand Tihon <amand@alrj.org> * @author Amand Tihon <amand@alrj.org>
*
* @package chamilo.exercise * @package chamilo.exercise
*/ */
class ImsItem class ImsItem
{ {
var $question; public $question;
var $question_ident; public $question_ident;
var $answer; public $answer;
/** /**
* Constructor. * Constructor.
@ -327,7 +321,7 @@ class ImsItem
*/ */
function start_item() function start_item()
{ {
return '<item title="' . htmlspecialchars($this->question->selectTitle()) . '" ident="' . $this->questionIdent . '">' . "\n"; return '<item title="' . cleanAttribute(formatExerciseQtiDescription($this->question->selectTitle())) . '" ident="' . $this->questionIdent . '">' . "\n";
} }
/** /**
@ -344,14 +338,14 @@ class ImsItem
* Create the opening, with the question itself. * Create the opening, with the question itself.
* *
* This means it opens the <presentation> but doesn't close it, as this is the role of end_presentation(). * This means it opens the <presentation> but doesn't close it, as this is the role of end_presentation().
* Inbetween, the export_responses from the subclass should have been called. * In between, the export_responses from the subclass should have been called.
* *
* @author Amand Tihon <amand@alrj.org> * @author Amand Tihon <amand@alrj.org>
*/ */
function start_presentation() function start_presentation()
{ {
return '<presentation label="' . $this->questionIdent . '"><flow>' . "\n" return '<presentation label="' . $this->questionIdent . '"><flow>' . "\n"
. '<material><mattext><![CDATA[' . $this->question->selectDescription() . "]]></mattext></material>\n"; . '<material><mattext>' . formatExerciseQtiDescription($this->question->selectDescription()) . "</mattext></material>\n";
} }
/** /**
@ -384,7 +378,6 @@ class ImsItem
return "</resprocessing>\n"; return "</resprocessing>\n";
} }
/** /**
* Export the question as an IMS/QTI Item. * Export the question as an IMS/QTI Item.
* *
@ -399,8 +392,7 @@ class ImsItem
global $charset; global $charset;
$head = $foot = ""; $head = $foot = "";
if( $standalone ) if ($standalone) {
{
$head = '<?xml version = "1.0" encoding = "'.$charset.'" standalone = "no"?>' . "\n" $head = '<?xml version = "1.0" encoding = "'.$charset.'" standalone = "no"?>' . "\n"
. '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n" . '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n"
. "<questestinterop>\n"; . "<questestinterop>\n";
@ -408,36 +400,30 @@ class ImsItem
} }
return $head return $head
. $this->start_item() . $this->start_item()
. $this->start_presentation() . $this->start_presentation()
. $this->answer->imsExportResponses($this->questionIdent) . $this->answer->imsExportResponses($this->questionIdent)
. $this->end_presentation() . $this->end_presentation()
. $this->start_processing() . $this->start_processing()
. $this->answer->imsExportProcessing($this->questionIdent) . $this->answer->imsExportProcessing($this->questionIdent)
. $this->end_processing() . $this->end_processing()
. $this->answer->imsExportFeedback($this->questionIdent) . $this->answer->imsExportFeedback($this->questionIdent)
. $this->end_item() . $this->end_item()
. $foot; . $foot;
} }
} }
/*--------------------------------------------------------
Functions
--------------------------------------------------------*/
/** /**
* Send a complete exercise in IMS/QTI format, from its ID * Send a complete exercise in IMS/QTI format, from its ID
* *
* @param int $exerciseId The exercise to exporte * @param int $exerciseId The exercise to export
* @param boolean $standalone Wether it should include XML tag and DTD line. * @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. * @return The XML as a string, or an empty string if there's no exercise with given ID.
*/ */
function export_exercise($exerciseId, $standalone=true) function export_exercise($exerciseId, $standalone = true)
{ {
$exercise = new Exercise(); $exercise = new Exercise();
if (! $exercise->read($exerciseId)) if (!$exercise->read($exerciseId)) {
{
return ''; return '';
} }
$ims = new ImsSection($exercise); $ims = new ImsSection($exercise);
@ -448,15 +434,14 @@ function export_exercise($exerciseId, $standalone=true)
/** /**
* Returns the XML flow corresponding to one question * Returns the XML flow corresponding to one question
* *
* @param int The question ID * @param int $questionId
* @param bool standalone (ie including XML tag, DTD declaration, etc) * @param bool $standalone (ie including XML tag, DTD declaration, etc)
*/ */
function export_question($questionId, $standalone=true) function export_question($questionId, $standalone = true)
{ {
$question = new Ims2Question(); $question = new Ims2Question();
$qst = $question->read($questionId); $qst = $question->read($questionId);
if( !$qst or $qst->type == FREE_ANSWER) if (!$qst or $qst->type == FREE_ANSWER) {
{
return ''; return '';
} }
$question->id = $qst->id; $question->id = $qst->id;
@ -470,3 +455,27 @@ function export_question($questionId, $standalone=true)
return $ims->export($standalone); return $ims->export($standalone);
} }
/**
* Clean text like a description
**/
function formatExerciseQtiDescription($text)
{
$entities = api_html_entity_decode($text);
return htmlspecialchars($entities);
}
/**
* Clean titles
* @param $text
* @return string
*/
function formatExerciseQtiTitle($text)
{
return htmlspecialchars($text);
}
function cleanAttribute($text)
{
return $text;
}

@ -122,9 +122,9 @@ class pagination
function setUrl($value="") { function setUrl($value="") {
$protocol = "http://"; $protocol = "http://";
if (isset($_SERVER['HTTPS'])) { if (isset($_SERVER['HTTPS'])) {
$protocol = "https://"; $protocol = "https://";
} }
if(empty($value)) if(empty($value))
{ {
if($this->friendlyUrl) if($this->friendlyUrl)
@ -216,7 +216,7 @@ class pagination
/** /**
* get the first item number * get the first item number
* *
* @return interger the first item number displayed within current page * @return int the first item number displayed within current page
*/ */
function getFirstItem() function getFirstItem()
{ {
@ -232,7 +232,7 @@ class pagination
/** /**
* get the last item number displayed within current page * get the last item number displayed within current page
* *
* @return interger the last item number * @return int the last item number
*/ */
function getLastItem() function getLastItem()
{ {
@ -532,8 +532,8 @@ class pagination
{ {
case "2": case "2":
//comment while integrating with Chamilo //comment while integrating with Chamilo
//$output .= "<span class=\"pagination_summany\">" . $this->getFirstItem() . " to " . $this->getLastItem() . " of " . $this->getTotal() . " results.</span> "; //$output .= "<span class=\"pagination_summany\">" . $this->getFirstItem() . " to " . $this->getLastItem() . " of " . $this->getTotal() . " results.</span> ";
//if($previousUrl = $this->getPreviousUrl()) //if($previousUrl = $this->getPreviousUrl())
//{ //{
// $output .= " " . $previousUrl; // $output .= " " . $previousUrl;
@ -542,7 +542,7 @@ class pagination
//if($nextUrl = $this->getNextUrl()) //if($nextUrl = $this->getNextUrl())
{ {
// $output .= " " . $nextUrl; // $output .= " " . $nextUrl;
} }
break; break;
case 1: case 1:
//get full summary pagination //get full summary pagination
@ -575,13 +575,13 @@ class pagination
//{ //{
// $itemPerPage .= "<option value=\"" . $v . "\" " . ($v==$this->itemsPerPage?'selected="selected"':'') . ">" . $v . "</option>\n"; // $itemPerPage .= "<option value=\"" . $v . "\" " . ($v==$this->itemsPerPage?'selected="selected"':'') . ">" . $v . "</option>\n";
//} //}
//$itemPerPage .= "</select>\n"; //$itemPerPage .= "</select>\n";
$itemPerPage ="100000";//hack for Chamilo $itemPerPage ="100000";//hack for Chamilo
//$output .= "<span class=\"pagination_items_per_page\">"; //$output .= "<span class=\"pagination_items_per_page\">";
//$output .= sprintf(PAGINATION_ITEMS_PER_PAGE, $itemPerPage); //$output .= sprintf(PAGINATION_ITEMS_PER_PAGE, $itemPerPage);
//$output .= "</span>"; //$output .= "</span>";
//end comment while integrating with Chamilo //end comment while integrating with Chamilo
$output .= "<span class=\"pagination_parent\"><a href=\"#\" onclick=\"goParentFolder();\" id=\"pagination_parent_link\" title=\"" . PAGINATION_GO_PARENT . "\">&nbsp;".PAGINATION_GO_PARENT."</a></span>"; $output .= "<span class=\"pagination_parent\"><a href=\"#\" onclick=\"goParentFolder();\" id=\"pagination_parent_link\" title=\"" . PAGINATION_GO_PARENT . "\">&nbsp;".PAGINATION_GO_PARENT."</a></span>";
} }
@ -591,4 +591,4 @@ class pagination
} }
} }
?> ?>

Loading…
Cancel
Save