Actualización

This commit is contained in:
Xes
2025-04-10 12:49:05 +02:00
parent 4aff98e77b
commit 1cdd00920f
9151 changed files with 1800913 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This class handles the SCORM export of fill-in-the-blanks questions.
*/
class ScormAnswerFillInBlanks extends Answer
{
/**
* Export the text with missing words.
*
* As a side effect, it stores two lists in the class :
* the missing words and their respective weightings.
*/
public function export()
{
$js = '';
// get all enclosed answers
$blankList = [];
foreach ($this->answer as $i => $answer) {
$blankList[] = '['.$answer.']';
}
// splits text and weightings that are joined with the character '::'
$listAnswerInfo = FillBlanks::getAnswerInfo($answer);
//$switchableAnswerSet = $listAnswerInfo['switchable'];
// display empty [input] with the right width for student to fill it
$answer = '';
$answerList = [];
for ($i = 0; $i < count($listAnswerInfo['common_words']) - 1; $i++) {
// display the common words
$answer .= $listAnswerInfo['common_words'][$i];
// display the blank word
$attributes['style'] = 'width:'.$listAnswerInfo['input_size'][$i].'px';
$answer .= FillBlanks::getFillTheBlankHtml(
$this->questionJSId,
$this->questionJSId + 1,
'',
$attributes,
$answer,
$listAnswerInfo,
true,
$i,
'question_'.$this->questionJSId.'_fib_'.($i + 1)
);
$answerList[] = $i + 1;
}
// display the last common word
$answer .= $listAnswerInfo['common_words'][$i];
// because [] is parsed here we follow this procedure:
// 1. find everything between the [ and ] tags
$jstmpw = 'questions_answers_ponderation['.$this->questionJSId.'] = new Array();'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][0] = 0;'."\n";
foreach ($listAnswerInfo['weighting'] as $key => $weight) {
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.']['.($key + 1).'] = '.$weight.";\n";
}
$wordList = "'".implode("', '", $listAnswerInfo['words'])."'";
$answerList = "'".implode("', '", $answerList)."'";
$html = '<tr><td colspan="2"><table width="100%">';
$html .= '<tr>
<td>
'.$answer.'
</td>
</tr></table></td></tr>';
$js .= 'questions_answers['.$this->questionJSId.'] = new Array('.$answerList.');'."\n";
$js .= 'questions_answers_correct['.$this->questionJSId.'] = new Array('.$wordList.');'."\n";
$js .= 'questions_types['.$this->questionJSId.'] = \'fib\';'."\n";
$js .= $jstmpw;
return [$js, $html];
}
}

View File

@@ -0,0 +1,98 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This class handles the SCORM export of matching questions.
*/
class ScormAnswerMatching extends Answer
{
/**
* Export the question part as a matrix-choice, with only one possible answer per line.
*
* @author Amand Tihon <amand@alrj.org>
*/
public function export()
{
$js = '';
// prepare list of right proposition to allow
// - easiest display
// - easiest randomisation if needed one day
// (here I use array_values to change array keys from $code1 $code2 ... to 0 1 ...)
// get max length of displayed array
$nbrAnswers = $this->selectNbrAnswers();
$counter = 1;
$questionId = $this->questionJSId;
$jstmpw = 'questions_answers_ponderation['.$questionId.'] = new Array();'."\n";
$jstmpw .= 'questions_answers_ponderation['.$questionId.'][0] = 0;'."\n";
// Options (A, B, C, ...) that will be put into the list-box
$options = [];
$letter = 'A';
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
$answerCorrect = $this->isCorrect($answerId);
$answer = $this->selectAnswer($answerId);
$realAnswerId = $this->selectAutoId($answerId);
if (!$answerCorrect) {
$options[$realAnswerId]['Lettre'] = $letter;
// answers that will be shown at the right side
$options[$realAnswerId]['Reponse'] = $answer;
$letter++;
}
}
$html = [];
$jstmp = '';
$jstmpc = '';
// Answers
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
$identifier = 'question_'.$questionId.'_matching_';
$answer = $this->selectAnswer($answerId);
$answerCorrect = $this->isCorrect($answerId);
$weight = $this->selectWeighting($answerId);
$jstmp .= $answerId.',';
if ($answerCorrect) {
$html[] = '<tr class="option_row">';
$html[] = '<td width="40%" valign="top">&nbsp;'.$answer.'</td>';
$html[] = '<td width="20%" align="center">&nbsp;&nbsp;';
$html[] = '<select name="'.$identifier.$counter.'" id="'.$identifier.$counter.'">';
$html[] = ' <option value="0">--</option>';
// fills the list-box
foreach ($options as $key => $val) {
$html[] = '<option value="'.$key.'">'.$val['Lettre'].'</option>';
}
$html[] = '</select>&nbsp;&nbsp;</td>';
$html[] = '<td width="40%" valign="top">';
foreach ($options as $key => $val) {
$html[] = '<b>'.$val['Lettre'].'.</b> '.$val['Reponse'].'<br />';
}
$html[] = '</td></tr>';
$jstmpc .= '['.$answerCorrect.','.$counter.'],';
$myWeight = explode('@', $weight);
if (2 == count($myWeight)) {
$weight = $myWeight[0];
} else {
$weight = $myWeight[0];
}
$jstmpw .= 'questions_answers_ponderation['.$questionId.']['.$counter.'] = '.$weight.";\n";
$counter++;
}
}
$js .= 'questions_answers['.$questionId.'] = new Array('.substr($jstmp, 0, -1).');'."\n";
$js .= 'questions_answers_correct['.$questionId.'] = new Array('.substr($jstmpc, 0, -1).');'."\n";
$js .= 'questions_types['.$questionId.'] = \'matching\';'."\n";
$js .= $jstmpw;
$htmlResult = '<tr><td colspan="2"><table id="question_'.$questionId.'" width="100%">';
$htmlResult .= implode("\n", $html);
$htmlResult .= '</table></td></tr>'."\n";
return [$js, $htmlResult];
}
}

View File

@@ -0,0 +1,118 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This class handles the export to SCORM of a multiple choice question
* (be it single answer or multiple answers).
*/
class ScormAnswerMultipleChoice extends Answer
{
/**
* Return HTML code for possible answers.
*/
public function export()
{
$js = [];
$type = $this->getQuestionType();
$questionId = $this->questionJSId;
$jstmpw = 'questions_answers_ponderation['.$questionId.'] = new Array();';
$jstmpw .= 'questions_answers_ponderation['.$questionId.'][0] = 0;';
$jstmpw .= 'questions_answers_correct['.$questionId.'] = new Array();';
$html = [];
//not sure if we are going to export also the MULTIPLE_ANSWER_COMBINATION to SCORM
//if ($type == MCMA || $type == MULTIPLE_ANSWER_COMBINATION ) {
if (MCMA == $type) {
$id = 1;
$jstmp = '';
$jstmpc = '';
foreach ($this->answer as $i => $answer) {
$identifier = 'question_'.$questionId.'_multiple_'.$i;
$html[] =
'<tr>
<td align="center" width="5%">
<input name="'.$identifier.'" id="'.$identifier.'" value="'.$i.'" type="checkbox" />
</td>
<td width="95%">
<label for="'.$identifier.'">'.Security::remove_XSS($this->answer[$i]).'</label>
</td>
</tr>';
$jstmp .= $i.',';
if ($this->correct[$i]) {
$jstmpc .= $i.',';
}
$jstmpw .= 'questions_answers_ponderation['.$questionId.']['.$i.'] = '.$this->weighting[$i].';';
$jstmpw .= 'questions_answers_correct['.$questionId.']['.$i.'] = '.$this->correct[$i].';';
$id++;
}
$js[] = 'questions_answers['.$questionId.'] = new Array('.substr($jstmp, 0, -1).');'."\n";
$js[] = 'questions_types['.$questionId.'] = \'mcma\';'."\n";
$js[] = $jstmpw;
} elseif (MULTIPLE_ANSWER_COMBINATION == $type) {
$js = [];
$id = 1;
$jstmp = '';
$jstmpc = '';
foreach ($this->answer as $i => $answer) {
$identifier = 'question_'.$questionId.'_exact_'.$i;
$html[] =
'<tr>
<td align="center" width="5%">
<input name="'.$identifier.'" id="'.$identifier.'" value="'.$i.'" type="checkbox" />
</td>
<td width="95%">
<label for="'.$identifier.'">'.Security::remove_XSS($this->answer[$i]).'</label>
</td>
</tr>';
$jstmp .= $i.',';
if ($this->correct[$i]) {
$jstmpc .= $i.',';
}
$jstmpw .= 'questions_answers_ponderation['.$questionId.']['.$i.'] = '.$this->weighting[$i].';';
$jstmpw .= 'questions_answers_correct['.$questionId.']['.$i.'] = '.$this->correct[$i].';';
$id++;
}
$js[] = 'questions_answers['.$questionId.'] = new Array('.substr($jstmp, 0, -1).');';
$js[] = 'questions_types['.$questionId.'] = "exact";';
$js[] = $jstmpw;
} else {
$id = 1;
$jstmp = '';
$jstmpc = '';
foreach ($this->answer as $i => $answer) {
$identifier = 'question_'.$questionId.'_unique_'.$i;
$identifier_name = 'question_'.$questionId.'_unique_answer';
$html[] =
'<tr>
<td align="center" width="5%">
<input name="'.$identifier_name.'" id="'.$identifier.'" value="'.$i.'" type="checkbox"/>
</td>
<td width="95%">
<label for="'.$identifier.'">'.Security::remove_XSS($this->answer[$i]).'</label>
</td>
</tr>';
$jstmp .= $i.',';
if ($this->correct[$i]) {
$jstmpc .= $i;
}
$jstmpw .= 'questions_answers_ponderation['.$questionId.']['.$i.'] = '.$this->weighting[$i].';';
$jstmpw .= 'questions_answers_correct['.$questionId.']['.$i.'] = '.$this->correct[$i].';';
$id++;
}
$js[] = 'questions_answers['.$questionId.'] = new Array('.substr($jstmp, 0, -1).');';
$js[] = 'questions_types['.$questionId.'] = \'mcua\';';
$js[] = $jstmpw;
}
$htmlResult = '<tr><td colspan="2"><table id="question_'.$questionId.'" width="100%">';
$htmlResult .= implode("\n", $html);
$htmlResult .= '</table></td></tr>';
$js = implode("\n", $js);
return [$js, $htmlResult];
}
}

View File

@@ -0,0 +1,55 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This class handles the SCORM export of true/false questions.
*/
class ScormAnswerTrueFalse extends Answer
{
/**
* Return the XML flow for the possible answers.
* That's one <response_lid>, containing several <flow_label>.
*
* @author Amand Tihon <amand@alrj.org>
*/
public function export()
{
$js = '';
$html = '<tr><td colspan="2"><table width="100%">';
$identifier = 'question_'.$this->questionJSId.'_tf';
$identifier_true = $identifier.'_true';
$identifier_false = $identifier.'_false';
$html .=
'<tr>
<td align="center" width="5%">
<input name="'.$identifier_true.'" id="'.$identifier_true.'" value="'.$this->trueGrade.'" type="radio" />
</td>
<td width="95%">
<label for="'.$identifier_true.'">'.get_lang('True').'</label>
</td>
</tr>';
$html .=
'<tr>
<td align="center" width="5%">
<input name="'.$identifier_false.'" id="'.$identifier_false.'" value="'.$this->falseGrade.'" type="radio" />
</td>
<td width="95%">
<label for="'.$identifier_false.'">'.get_lang('False').'</label>
</td>
</tr></table></td></tr>';
$js .= 'questions_answers['.$this->questionJSId.'] = new Array(\'true\',\'false\');'."\n";
$js .= 'questions_types['.$this->questionJSId.'] = \'tf\';'."\n";
if ('TRUE' === $this->response) {
$js .= 'questions_answers_correct['.$this->questionJSId.'] = new Array(\'true\');'."\n";
} else {
$js .= 'questions_answers_correct['.$this->questionJSId.'] = new Array(\'false\');'."\n";
}
$jstmpw = 'questions_answers_ponderation['.$this->questionJSId.'] = new Array();'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][0] = 0;'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][1] = '.$this->weighting[1].";\n";
$js .= $jstmpw;
return [$js, $html];
}
}

View File

@@ -0,0 +1,159 @@
<?php
/* For licensing terms, see /license.txt */
/**
* A SCORM item. It corresponds to a single question.
* This class allows export from Chamilo SCORM 1.2 format of a single question.
* 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.
*
* Attached files are NOT exported.
*/
class ScormAssessmentItem
{
public $question;
public $question_ident;
public $answer;
/**
* Constructor.
*
* @param ScormQuestion $question the Question object we want to export
*/
public function __construct($question)
{
$this->question = $question;
$this->question->setAnswer();
$this->questionIdent = 'QST_'.$question->iid;
}
/**
* Start the XML flow.
*
* This opens the <item> block, with correct attributes.
*/
public function start_page()
{
return '';
}
/**
* End the XML flow, closing the </item> tag.
*/
public function end_page()
{
/*if ($this->standalone) {
return '</html>';
}*/
return '';
}
/**
* Start document header.
*/
public function start_header()
{
/*if ($this->standalone) {
return '<head>';
}*/
return '';
}
/**
* Print CSS inclusion.
*/
public function css()
{
return '';
}
/**
* End document header.
*/
public function end_header()
{
// if ($this->standalone) {
// return '</head>';
// }
return '';
}
/**
* Start the itemBody.
*/
public function start_js()
{
return '<script type="text/javascript" src="assets/api_wrapper.js"></script>';
}
/**
* End the itemBody part.
*/
public function end_js()
{
/*if ($this->standalone) {
return '</script>';
}*/
return '';
}
/**
* Start the itemBody.
*/
public function start_body()
{
/*if ($this->standalone) {
return '<body><form id="dokeos_scorm_form" method="post" action="">';
}*/
return '';
}
/**
* End the itemBody part.
*/
public function end_body()
{
/*if ($this->standalone) {
return '<br /><input class="btn" type="button" id="dokeos_scorm_submit" name="dokeos_scorm_submit" value="OK" /></form></body>';
}*/
return '';
}
/**
* Export the question as a SCORM Item.
* This is a default behaviour, some classes may want to override this.
*
* @return string|array a string, the XML flow for an Item
*/
public function export()
{
list($js, $html) = $this->question->export();
/*if ($this->standalone) {
$res = $this->start_page()
.$this->start_header()
.$this->css()
.$this->start_js()
.$this->common_js()
.$js
.$this->end_js()
.$this->end_header()
.$this->start_body()
.$html
.$this->end_body()
.$this->end_page();
return $res;
} else {
return [$js, $html];
}*/
return [$js, $html];
}
}

View File

@@ -0,0 +1,216 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Entity\CQuiz;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
/**
* This class represents an entire exercise to be exported in SCORM.
* It will be represented by a single <section> containing several <item>.
*
* Some properties cannot be exported, as SCORM does not support them :
* - type (one page or multiple pages)
* - start_date and end_date
* - max_attempts
* - show_answer
* - anonymous_attempts
*
* @author Julio Montoya
* @author Amand Tihon <amand@alrj.org>
*/
class ScormExercise
{
public $exercise;
public $standalone;
/**
* ScormExercise constructor.
*
* @param Exercise $exe
* @param bool $standalone
*/
public function __construct($exe, $standalone)
{
$this->exercise = $exe;
$this->standalone = $standalone;
}
/**
* Start the XML flow.
*
* This opens the <item> block, with correct attributes.
*/
public function startPage()
{
$charset = 'UTF-8';
return '<?xml version="1.0" encoding="'.$charset.'" standalone="no"?><html>';
}
/**
* End the XML flow, closing the </item> tag.
*/
public function end_page()
{
return '</html>';
}
/**
* Start document header.
*/
public function start_header()
{
return '<head>';
}
/**
* Common JS functions.
*/
public function common_js()
{
$js = file_get_contents(api_get_path(SYS_CODE_PATH).'exercise/export/scorm/common.js');
return $js."\n";
}
/**
* End the itemBody part.
*/
public function end_js()
{
return '</script>';
}
/**
* Start the itemBody.
*/
public function start_body()
{
return '<body>'.
'<h1>'.$this->exercise->selectTitle().'</h1><p>'.$this->exercise->selectDescription().'</p>'.
'<form id="chamilo_scorm_form" method="post" action="">'.
'<table width="100%">';
}
/**
* End the itemBody part.
*/
public function end_body()
{
$button = '<input
id="chamilo_scorm_submit"
class="btn btn-primary"
type="button"
name="chamilo_scorm_submit"
value="OK" />';
return '</table><br />'.$button.'</form></body>';
}
/**
* Export the question as a SCORM Item.
*
* This is a default behaviour, some classes may want to override this.
*
* @return string string, the XML flow for an Item
*/
public function export()
{
global $charset;
/*$head = '';
if ($this->standalone) {
$head = '<?xml version = "1.0" encoding = "'.$charset.'" standalone = "no"?>'."\n"
.'<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv2p1.dtd">'."\n";
}*/
list($js, $html) = $this->exportQuestions();
return $this->startPage()
.$this->start_header()
.$this->css()
.$this->globalAssets()
.$this->start_js()
.$this->common_js()
.$js
.$this->end_js()
.$this->end_header()
.$this->start_body()
.$html
.$this->end_body()
.$this->end_page();
}
/**
* Export the questions, as a succession of <items>.
*
* @author Amand Tihon <amand@alrj.org>
*/
public function exportQuestions()
{
$js = $html = '';
$encoders = [new JsonEncoder()];
$normalizers = [new ObjectNormalizer()];
$em = Database::getManager();
// Export cquiz data
/** @var CQuiz $exercise */
$exercise = $em->find('ChamiloCourseBundle:CQuiz', $this->exercise->iid);
$exercise->setDescription('');
$exercise->setTextWhenFinished('');
$serializer = new Serializer($normalizers, $encoders);
$jsonContent = $serializer->serialize($exercise, 'json');
$js .= "var exerciseInfo = JSON.parse('".$jsonContent."');\n";
$counter = 0;
$scormQuestion = new ScormQuestion();
foreach ($this->exercise->selectQuestionList() as $q) {
list($jstmp, $htmltmp) = $scormQuestion->exportQuestionToScorm($q, $counter);
$js .= $jstmp."\n";
$html .= $htmltmp."\n";
$counter++;
}
return [$js, $html];
}
/**
* Print CSS inclusion.
*/
private function css()
{
return '';
}
/**
* End document header.
*/
private function end_header()
{
return '</head>';
}
/**
* Start the itemBody.
*/
private function start_js()
{
return '<script>';
}
/**
* @return string
*/
private function globalAssets()
{
$assets = '<script type="text/javascript" src="assets/jquery/jquery.min.js"></script>'."\n";
$assets .= '<script type="text/javascript" src="assets/api_wrapper.js"></script>'."\n";
$assets .= '<link href="assets/bootstrap/bootstrap.min.css" rel="stylesheet" media="screen" type="text/css" />';
return $assets;
}
}

View File

@@ -0,0 +1,221 @@
<?php
/* For licensing terms, see /license.txt */
/**
* The ScormQuestion class is a gateway to getting the answers exported
* (the question is just an HTML text, while the answers are the most important).
* It is important to note that the SCORM export process is done in two parts.
* First, the HTML part (which is the presentation), and second the JavaScript
* part (the process).
* The two bits are separate to allow for a one-big-javascript and a one-big-html
* files to be built. Each export function thus returns an array of HTML+JS.
*
* @author Claro Team <cvs@claroline.net>
* @author Yannick Warnier <yannick.warnier@beeznest.com>
*/
class ScormQuestion extends Question
{
public $js_id;
public $answer;
/**
* ScormQuestion constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* Returns the HTML + JS flow corresponding to one question.
*
* @param int $questionId The question ID
* @param int $jsId The JavaScript ID for this question.
* Due to the nature of interactions, we must have a natural sequence for
* questions in the generated JavaScript.
*
* @return string|array
*/
public function exportQuestionToScorm(
$questionId,
$jsId
) {
$question = self::read($questionId);
if (!$question) {
return '';
}
$this->iid = $question->iid;
$this->js_id = $jsId;
$this->type = $question->type;
$this->question = $question->question;
$this->description = $question->description;
$this->weighting = $question->weighting;
$this->position = $question->position;
$this->picture = $question->picture;
$assessmentItem = new ScormAssessmentItem($this);
return $assessmentItem->export();
}
/**
* Include the correct answer class and create answer.
*/
public function setAnswer()
{
switch ($this->type) {
case MCUA:
$this->answer = new ScormAnswerMultipleChoice($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case MCMA:
case GLOBAL_MULTIPLE_ANSWER:
$this->answer = new ScormAnswerMultipleChoice($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case TF:
$this->answer = new ScormAnswerTrueFalse($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case FIB:
$this->answer = new ScormAnswerFillInBlanks($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case MATCHING:
case MATCHING_DRAGGABLE:
case DRAGGABLE:
$this->answer = new ScormAnswerMatching($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case ORAL_EXPRESSION:
case FREE_ANSWER:
$this->answer = new ScormAnswerFree($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case HOT_SPOT:
case HOT_SPOT_COMBINATION:
$this->answer = new ScormAnswerHotspot($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case MULTIPLE_ANSWER_COMBINATION:
$this->answer = new ScormAnswerMultipleChoice($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case HOT_SPOT_ORDER:
$this->answer = new ScormAnswerHotspot($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
case HOT_SPOT_DELINEATION:
$this->answer = new ScormAnswerHotspot($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
// not supported
case UNIQUE_ANSWER_NO_OPTION:
case MULTIPLE_ANSWER_TRUE_FALSE:
case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
case UNIQUE_ANSWER_IMAGE:
case CALCULATED_ANSWER:
$this->answer = new ScormAnswerMultipleChoice($this->iid);
$this->answer->questionJSId = $this->js_id;
break;
default:
$this->answer = new stdClass();
$this->answer->questionJSId = $this->js_id;
break;
}
return true;
}
/**
* @throws Exception
*
* @return array
*/
public function export()
{
$html = $this->getQuestionHTML();
$js = $this->getQuestionJS();
if (is_object($this->answer) && $this->answer instanceof Answer) {
list($js2, $html2) = $this->answer->export();
$js .= $js2;
$html .= $html2;
} else {
throw new \Exception('Question not supported. Exercise: '.$this->selectTitle());
}
return [$js, $html];
}
/**
* {@inheritdoc}
*/
public function createAnswersForm($form)
{
return true;
}
/**
* {@inheritdoc}
*/
public function processAnswersCreation($form, $exercise)
{
return true;
}
/**
* Returns an HTML-formatted question.
*/
public function getQuestionHTML()
{
$title = $this->selectTitle();
$description = $this->selectDescription();
$cols = 2;
return '<tr>
<td colspan="'.$cols.'" id="question_'.$this->iid.'_title" valign="middle" style="background-color:#d6d6d6;">
'.$title.'
</td>
</tr>
<tr>
<td valign="top" colspan="'.$cols.'">
<i>'.$description.'</i>
</td>
</tr>';
}
/**
* Return the JavaScript code bound to the question.
*/
public function getQuestionJS()
{
$weight = $this->selectWeighting();
$js = '
questions.push('.$this->js_id.');
$(function() {
if (exerciseInfo.randomAnswers == true) {
$("#question_'.$this->js_id.'").shuffleRows();
}
});';
$js .= "\n";
switch ($this->type) {
case ORAL_EXPRESSION:
/*$script = file_get_contents(api_get_path(LIBRARY_PATH) . 'javascript/rtc/RecordRTC.js');
$script .= file_get_contents(api_get_path(LIBRARY_PATH) . 'wami-recorder/recorder.js');
$script .= file_get_contents(api_get_path(LIBRARY_PATH) . 'wami-recorder/gui.js');
$js .= $script;*/
break;
case HOT_SPOT:
case HOT_SPOT_COMBINATION:
//put the max score to 0 to avoid discounting the points of
//non-exported quiz types in the SCORM
$weight = 0;
break;
}
$js .= 'questions_score_max['.$this->js_id.'] = '.$weight.';';
return $js;
}
}

View File

@@ -0,0 +1,25 @@
var questions = new Array();
var questions_answers = new Array();
var questions_answers_correct = new Array();
var questions_types = new Array();
var questions_score_max = new Array();
var questions_answers_ponderation = new Array();
/**
* Adds the event listener
*/
function addListeners(e) {
loadPage();
var myButton = document.getElementById('chamilo_scorm_submit');
addEvent(myButton, 'click', doQuit, false);
addEvent(myButton, 'click', disableButton, false);
addEvent(window, 'unload', unloadPage, false);
}
/** Disables the submit button on SCORM result submission **/
function disableButton() {
var mybtn = document.getElementById('chamilo_scorm_submit');
mybtn.setAttribute('disabled', 'disabled');
}
addEvent(window,'load', addListeners, false);

View File

@@ -0,0 +1,144 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This class handles the SCORM export of free-answer questions.
*/
class ScormAnswerFree extends Answer
{
/**
* Export the text with missing words.
*
* As a side effect, it stores two lists in the class :
* the missing words and their respective weightings.
*/
public function export()
{
$js = '';
$identifier = 'question_'.$this->questionJSId.'_free';
// currently the free answers cannot be displayed, so ignore the textarea
$html = '<tr><td colspan="2">';
$type = $this->getQuestionType();
if (ORAL_EXPRESSION == $type) {
/*
$template = new Template('');
$template->assign('directory', '/tmp/');
$template->assign('user_id', api_get_user_id());
$layout = $template->get_template('document/record_audio.tpl');
$html .= $template->fetch($layout);*/
$html = '<tr><td colspan="2">'.get_lang('ThisItemIsNotExportable').'</td></tr>';
return [$js, $html];
}
$html .= '<textarea minlength="20" name="'.$identifier.'" id="'.$identifier.'" ></textarea>';
$html .= '</td></tr>';
$js .= 'questions_answers['.$this->questionJSId.'] = new Array();';
$js .= 'questions_answers_correct['.$this->questionJSId.'] = "";';
$js .= 'questions_types['.$this->questionJSId.'] = \'free\';';
$jstmpw = 'questions_answers_ponderation['.$this->questionJSId.'] = "0";';
$js .= $jstmpw;
return [$js, $html];
}
}
/**
* This class handles the SCORM export of hotpot questions.
*/
class ScormAnswerHotspot extends Answer
{
/**
* Returns the javascript code that goes with HotSpot exercises.
*
* @return string The JavaScript code
*/
public function get_js_header()
{
$header = '<script>';
$header .= file_get_contents(api_get_path(SYS_CODE_PATH).'inc/lib/javascript/hotspot/js/hotspot.js');
$header .= '</script>';
if ($this->standalone) {
//because this header closes so many times the <script> tag, we have to reopen our own
$header .= '<script>';
$header .= 'questions_answers['.$this->questionJSId.'] = new Array();'."\n";
$header .= 'questions_answers_correct['.$this->questionJSId.'] = new Array();'."\n";
$header .= 'questions_types['.$this->questionJSId.'] = \'hotspot\';'."\n";
$jstmpw = 'questions_answers_ponderation['.$this->questionJSId.'] = new Array();'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][0] = 0;'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][1] = 0;'.";\n";
$header .= $jstmpw;
} else {
$header = '';
$header .= 'questions_answers['.$this->questionJSId.'] = new Array();'."\n";
$header .= 'questions_answers_correct['.$this->questionJSId.'] = new Array();'."\n";
$header .= 'questions_types['.$this->questionJSId.'] = \'hotspot\';'."\n";
$jstmpw = 'questions_answers_ponderation['.$this->questionJSId.'] = new Array();'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][0] = 0;'."\n";
$jstmpw .= 'questions_answers_ponderation['.$this->questionJSId.'][1] = 0;'."\n";
$header .= $jstmpw;
}
return $header;
}
/**
* Export the text with missing words.
*
* As a side effect, it stores two lists in the class :
* the missing words and their respective weightings.
*/
public function export()
{
$js = $this->get_js_header();
$html = '<tr><td colspan="2"><table width="100%">';
// some javascript must be added for that kind of questions
$html .= '';
// Get the answers, make a list
$nbrAnswers = $this->selectNbrAnswers();
$answerList = '<div
style="padding: 10px;
margin-left: -8px;
border: 1px solid #4271b5;
height: 448px;
width: 200px;"><b>'.get_lang('HotspotZones').'</b><ol>';
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
$answerList .= '<li>'.$this->selectAnswer($answerId).'</li>';
}
$answerList .= '</ol></div>';
$relPath = api_get_path(REL_PATH);
$html .= <<<HTML
<tr>
<td>
<div id="hotspot-{$this->questionJSId}"></div>
<script>
document.addEventListener('DOMContentListener', function () {
new HotspotQuestion({
questionId: {$this->questionJSId},
selector: '#hotspot-{$this->questionJSId}',
for: 'user',
relPath: '$relPath'
});
});
</script>
</td>
<td>
$answerList
</td>
<tr>
HTML;
$html .= '</table></td></tr>';
// currently the free answers cannot be displayed, so ignore the textarea
$html = '<tr><td colspan="2">'.get_lang('ThisItemIsNotExportable').'</td></tr>';
return [$js, $html];
}
}