[svn r13202] Added a few changes for exercise export (wrapping several questions in one exercise)

skala
Yannick Warnier 19 years ago
parent afc9ec85b7
commit 8a3031ff76
  1. 30
      main/exercice/export/scorm/scorm_classes.php
  2. 457
      main/exercice/export/scorm/scorm_export.php

@ -81,7 +81,6 @@ class ScormQuestion extends Question
function export()
{
//echo "<pre>".print_r($this,1)."</pre>";
$html = $this->getQuestionHTML();
$js = $this->getQuestionJS();
@ -91,7 +90,6 @@ class ScormQuestion extends Question
$js .= $js2;
$html .= $html2;
}
return array($js,$html);
}
@ -110,32 +108,16 @@ class ScormQuestion extends Question
{
$title = $this->selectTitle();
$description = $this->selectDescription();
$type = $this->selectType();
$cols = 0;
switch($type)
{
case MCUA:
case MCMA:
case TF:
case FIB:
case FREE_ANSWER:
case HOTSPOT:
$cols = 2;
break;
case MATCHING:
$cols = 3;
break;
}
$s='<tr>' .
'<td valign="top" colspan="'.$cols.'" id="question_'.$this->id.'_title">' . "\n" .
api_parse_tex($title).
api_parse_tex($title).
'</td>' . "\n" .
'</tr>' . "\n" .
'<tr>' . "\n" .
'<td valign="top" colspan="'.$cols.'">' . "\n" .
'<i>'.api_parse_tex($description).'</i>' . "\n" .
'</td>' . "\n" .
'</tr>' . "\n" .
'<tr>' . "\n" .
'<td valign="top" colspan="'.$cols.'">' . "\n" .
' <i>'.api_parse_tex($description).'</i>' . "\n" .
'</td>' . "\n" .
'</tr>' . "\n";
return $s;
}

@ -62,15 +62,13 @@ class ScormAssessmentItem
function start_page()
{
global $charset;
$head = $foot = "";
$head = "";
if( $this->standalone)
{
/*
$head = '<?xml version="1.0" encoding="'.$charset.'" standalone="no"?>' . "\n";
*/
$head .= '<html>'."\n";
}
return $head.'<html>'. "\n";
return $head;
}
/**
@ -79,14 +77,16 @@ class ScormAssessmentItem
*/
function end_page()
{
return '</html>';
if($this->standalone){return '</html>';}
return '';
}
/**
* Start document header
*/
function start_header()
{
return '<head>'. "\n";
if($this->standalone){return '<head>'. "\n";}
return '';
}
/**
@ -94,7 +94,8 @@ class ScormAssessmentItem
*/
function end_header()
{
return '</head>'. "\n";
if($this->standalone){return '</head>'. "\n";}
return '';
}
/**
* Start the itemBody
@ -102,7 +103,8 @@ class ScormAssessmentItem
*/
function start_js()
{
return '<script type="text/javascript" language="javascript">'. "\n";
if($this->standalone){return '<script type="text/javascript" language="javascript">'. "\n";}
return '';
}
/**
* Common JS functions
@ -139,12 +141,13 @@ class ScormAssessmentItem
*/
function addListeners(e) {
loadPage();
/*var my_form = document.getElementById(\'dokeos_scorm_form\');
/*
var my_form = document.getElementById(\'dokeos_scorm_form\');
addEvent(my_form,\'submit\',checkAnswers,false);
*/
var my_button = document.getElementById(\'dokeos_scorm_submit\');
addEvent(my_button,\'click\',checkAnswers,false);
addEvent(my_button,\'change\',checkAnswers,false);
//addEvent(my_button,\'change\',checkAnswers,false);
addEvent(window,\'unload\',unloadPage,false);
}'."\n";
@ -152,7 +155,8 @@ class ScormAssessmentItem
//$js .= 'addEvent(window,\'load\',loadPage,false);'."\n";
//$js .= 'addEvent(window,\'unload\',unloadPage,false);'."\n";
$js .= 'addEvent(window,\'load\',addListeners,false);'."\n";
return $js. "\n";
if($this->standalone){return $js. "\n";}
return '';
}
/**
* End the itemBody part.
@ -160,7 +164,8 @@ class ScormAssessmentItem
*/
function end_js()
{
return '</script>'. "\n";
if($this->standalone){return '</script>'. "\n";}
return '';
}
/**
* Start the itemBody
@ -168,7 +173,8 @@ class ScormAssessmentItem
*/
function start_body()
{
return '<body>'. "\n".'<form id="dokeos_scorm_form" method="post" action="">'."\n";
if($this->standalone){return '<body>'. "\n".'<form id="dokeos_scorm_form" method="post" action="">'."\n";}
return '';
}
/**
@ -177,7 +183,8 @@ class ScormAssessmentItem
*/
function end_body()
{
return '<br /><input type="button" id="dokeos_scorm_submit" name="dokeos_scorm_submit" value="OK" /></form>'."\n".'</body>'. "\n";
if($this->standalone){return '<br /><input type="button" id="dokeos_scorm_submit" name="dokeos_scorm_submit" value="OK" /></form>'."\n".'</body>'. "\n";}
return '';
}
/**
@ -188,27 +195,34 @@ class ScormAssessmentItem
* @param $standalone: Boolean stating if it should be exported as a stand-alone question
* @return A string, the XML flow for an Item.
*/
function export($standalone = false)
function export()
{
$js = $html = '';
list($js,$html) = $this->question->export();
//list($js,$html) = $this->question->answer->export();
$res = $this->start_page($standalone)
. $this->start_header()
. $this->start_js()
. $this->common_js()
. $js
. $this->end_js()
. $this->end_header()
. $this->start_body()
// .$this->answer->imsExportResponsesDeclaration($this->questionIdent)
// . $this->start_item_body()
// . $this->answer->scormExportResponses($this->questionIdent, $this->question->question, $this->question->description, $this->question->picture)
// .$question
.$html
. $this->end_body()
. $this->end_page();
return $res;
if($this->standalone)
{
$res = $this->start_page()
. $this->start_header()
. $this->start_js()
. $this->common_js()
. $js
. $this->end_js()
. $this->end_header()
. $this->start_body()
// .$this->answer->imsExportResponsesDeclaration($this->questionIdent)
// . $this->start_item_body()
// . $this->answer->scormExportResponses($this->questionIdent, $this->question->question, $this->question->description, $this->question->picture)
// .$question
. $html
. $this->end_body()
. $this->end_page();
return $res;
}
else
{
return array($js,$html);
}
}
}
@ -237,260 +251,187 @@ class ScormSection
$this->exercise = $exe;
}
function start_section()
{
$out = '<section ident="EXO_' . $this->exercise->selectId() . '" title="' . $this->exercise->selectTitle() . '">' . "\n";
return $out;
}
/**
* Start the XML flow.
*
* This opens the <item> block, with correct attributes.
*
*/
function start_page()
{
global $charset;
$head = $foot = "";
$head = '<?xml version="1.0" encoding="'.$charset.'" standalone="no"?>' . "\n".'<html>'."\n";
return $head;
}
/**
* End the XML flow, closing the </item> tag.
*
*/
function end_page()
{
return '</html>';
}
/**
* Start document header
*/
function start_header()
{
return '<head>'. "\n";
}
function end_section()
{
return "</section>\n";
}
function export_duration()
/**
* End document header
*/
function end_header()
{
return '</head>'. "\n";
}
/**
* Start the itemBody
*
*/
function start_js()
{
if ($max_time = $this->exercise->selectTimeLimit())
{
// return exercise duration in ISO8601 format.
$minutes = floor($max_time / 60);
$seconds = $max_time % 60;
return '<duration>PT' . $minutes . 'M' . $seconds . "S</duration>\n";
}
else
{
return '';
}
return '<script type="text/javascript" language="javascript">'. "\n";
}
/**
* Common JS functions
*/
function common_js()
{
$js = file_get_contents('../newscorm/js/api_wrapper.js');
$js .= 'var questions = new Array();' . "\n";
$js .= 'var questions_answers = new Array();' . "\n";
$js .= 'var questions_answers_correct = new Array();' . "\n";
$js .= 'var questions_types = new Array();' . "\n";
$js .= "\n" .
'/**
* Assigns any event handler to any element
* @param object Element on which the event is added
* @param string Name of event
* @param string Function to trigger on event
* @param boolean Capture the event and prevent
*/
function addEvent(elm, evType, fn, useCapture)
{ //by Scott Andrew
if(elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if(elm.attachEvent) {
var r = elm.attachEvent(\'on\' + evType, fn);
return r;
} else {
elm[\'on\' + evType] = fn;
}
}
/**
* Adds the event listener
*/
function addListeners(e) {
loadPage();
/*
var my_form = document.getElementById(\'dokeos_scorm_form\');
addEvent(my_form,\'submit\',checkAnswers,false);
*/
var my_button = document.getElementById(\'dokeos_scorm_submit\');
addEvent(my_button,\'click\',checkAnswers,false);
//addEvent(my_button,\'change\',checkAnswers,false);
addEvent(window,\'unload\',unloadPage,false);
}'."\n";
$js .= '';
//$js .= 'addEvent(window,\'load\',loadPage,false);'."\n";
//$js .= 'addEvent(window,\'unload\',unloadPage,false);'."\n";
$js .= 'addEvent(window,\'load\',addListeners,false);'."\n";
return $js. "\n";
}
/**
* Export the presentation (Exercise's description)
* @author Amand Tihon <amand@alrj.org>
* End the itemBody part.
*
*/
function export_presentation()
function end_js()
{
$out = "<presentation_material><flow_mat><material>\n"
. " <mattext><![CDATA[" . $this->exercise->selectDescription() . "]]></mattext>\n"
. "</material></flow_mat></presentation_material>\n";
return $out;
return '</script>'. "\n";
}
/**
* Export the ordering information.
* Either sequential, through all questions, or random, with a selected number of questions.
* @author Amand Tihon <amand@alrj.org>
* Start the itemBody
*
*/
function export_ordering()
function start_body()
{
$out = '';
if ($n = $this->exercise->getShuffle()) {
$out.= "<selection_ordering>"
. " <selection>\n"
. " <selection_number>" . $n . "</selection_number>\n"
. " </selection>\n"
. ' <order order_type="Random" />'
. "\n</selection_ordering>\n";
}
else
{
$out.= '<selection_ordering sequence_type="Normal">' . "\n"
. " <selection />\n"
. "</selection_ordering>\n";
}
return $out;
return '<body>'. "\n".
'<h1>'.$this->exercise->selectTitle().'</h1>'."\n".
'<form id="dokeos_scorm_form" method="post" action="">'."\n";
}
/**
* Export the questions, as a succession of <items>
* @author Amand Tihon <amand@alrj.org>
* End the itemBody part.
*
*/
function export_questions()
function end_body()
{
$out = "";
foreach ($this->exercise->selectQuestionList() as $q)
{
$out .= export_question($q, false);
}
return $out;
return '<br /><input type="button" id="dokeos_scorm_submit" name="dokeos_scorm_submit" value="OK" /></form>'."\n".'</body>'. "\n";
}
/**
* Export the exercise in SCORM.
* Export the question as a SCORM Item.
*
* @param bool $standalone Wether it should include XML tag and DTD line.
* @return a string containing the XML flow
* @author Amand Tihon <amand@alrj.org>
* This is a default behaviour, some classes may want to override this.
*
* @param $standalone: Boolean stating if it should be exported as a stand-alone question
* @return A string, the XML flow for an Item.
*/
function export($standalone)
function export()
{
global $charset;
$head = $foot = "";
if ($standalone) {
$head = "";
if ($this->standalone) {
$head = '<?xml version = "1.0" encoding = "' . $charset . '" standalone = "no"?>' . "\n"
. '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n"
. "<questestinterop>\n";
$foot = "</questestinterop>\n";
. '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n";
}
$out = $head
. $this->start_section()
. $this->export_duration()
. $this->export_presentation()
. $this->export_ordering()
. $this->export_questions()
. $this->end_section()
. $foot;
return $out;
}
}
/*
Some quick notes on identifiers generation.
The IMS format requires some blocks, like items, responses, feedbacks, to be uniquely
identified.
The unicity is mandatory in a single XML, of course, but it's prefered that the identifier stays
coherent for an entire site.
list($js,$html) = $this->export_questions();
//list($js,$html) = $this->question->answer->export();
$res = $this->start_page()
. $this->start_header()
. $this->start_js()
. $this->common_js()
. $js
. $this->end_js()
. $this->end_header()
. $this->start_body()
// .$this->answer->imsExportResponsesDeclaration($this->questionIdent)
// . $this->start_item_body()
// . $this->answer->scormExportResponses($this->questionIdent, $this->question->question, $this->question->description, $this->question->picture)
// .$question
.$html
. $this->end_body()
. $this->end_page();
return $res;
}
Here's the method used to generate those identifiers.
Question identifier :: "QST_" + <Question Id from the DB> + "_" + <Question numeric type>
Response identifier :: <Question identifier> + "_A_" + <Response Id from the DB>
Condition identifier :: <Question identifier> + "_C_" + <Response Id from the DB>
Feedback identifier :: <Question identifier> + "_F_" + <Response Id from the DB>
*/
/**
* A SCORM item. It corresponds to a single question.
* This class allows export from Dokeos to SCORM 1.2 format.
* It is not usable as-is, but must be subclassed, to support different kinds of questions.
*
* Every start_*() and corresponding end_*(), as well as export_*() methods return a string.
*
* @warning Attached files are NOT exported.
*/
class ScormItem
{
var $question;
var $question_ident;
var $answer;
/**
* Constructor.
*
* @param $question The Question object we want to export.
* @author Anamd Tihon
* Export the questions, as a succession of <items>
* @author Amand Tihon <amand@alrj.org>
*/
function ScormItem($question)
{
$this->question = $question;
$this->answer = $question->answer;
$this->questionIdent = "QST_" . $question->selectId() ;
}
/**
* Start the XML flow.
*
* This opens the <item> block, with correct attributes.
*
* @author Amand Tihon <amand@alrj.org>
*/
function start_item()
{
return '<item title="' . htmlspecialchars($this->question->selectTitle()) . '" ident="' . $this->questionIdent . '">' . "\n";
}
/**
* End the XML flow, closing the </item> tag.
*
* @author Amand Tihon <amand@alrj.org>
*/
function end_item()
{
return "</item>\n";
}
/**
* 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().
* Inbetween, the export_responses from the subclass should have been called.
*
* @author Amand Tihon <amand@alrj.org>
*/
function start_presentation()
{
return '<presentation label="' . $this->questionIdent . '"><flow>' . "\n"
. '<material><mattext><![CDATA[' . $this->question->selectDescription() . "]]></mattext></material>\n";
}
/**
* End the </presentation> part, opened by export_header.
*
* @author Amand Tihon <amand@alrj.org>
*/
function end_presentation()
{
return "</flow></presentation>\n";
}
/**
* Start the response processing, and declare the default variable, SCORE, at 0 in the outcomes.
*
* @author Amand Tihon <amand@alrj.org>
*/
function start_processing()
{
return '<resprocessing><outcomes><decvar vartype="Integer" defaultval="0" /></outcomes>' . "\n";
}
/**
* End the response processing part.
*
* @author Amand Tihon <amand@alrj.org>
*/
function end_processing()
{
return "</resprocessing>\n";
}
/**
* Export the question as a SCORM Item.
*
* This is a default behaviour, some classes may want to override this.
*
* @param $standalone: Boolean stating if it should be exported as a stand-alone question
* @return A string, the XML flow for an Item.
* @author Amand Tihon <amand@alrj.org>
*/
function export($standalone = False)
{
global $charset;
$head = $foot = "";
if( $standalone )
function export_questions()
{
$js = $html = "";
foreach ($this->exercise->selectQuestionList() as $q)
{
$head = '<?xml version = "1.0" encoding = "'.$charset.'" standalone = "no"?>' . "\n"
. '<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">' . "\n"
. "<questestinterop>\n";
$foot = "</questestinterop>\n";
list($jstmp,$htmltmp)= export_question($q, false);
$js .= $jstmp."\n";
$html .= $htmltmp."<br /><br />";
}
return $head
. $this->start_item()
. $this->start_presentation()
. $this->answer->imsExportResponses($this->questionIdent)
. $this->end_presentation()
. $this->start_processing()
. $this->answer->imsExportProcessing($this->questionIdent)
. $this->end_processing()
. $this->answer->imsExportFeedback($this->questionIdent)
. $this->end_item()
. $foot;
}
return array($js,$html);
}
}
/*--------------------------------------------------------
Functions
--------------------------------------------------------*/
@ -535,8 +476,8 @@ function export_question($questionId, $standalone=true)
$question->weighting=$qst->weighting;
$question->position=$qst->position;
$question->picture=$qst->picture;
$assessmentItem = new ScormAssessmentItem($question);
$assessmentItem = new ScormAssessmentItem($question,$standalone);
//echo "<pre>".print_r($scorm,1)."</pre>";exit;
return $assessmentItem->export($standalone);
return $assessmentItem->export();
}
?>
Loading…
Cancel
Save