Merge pull request #4360 from AngelFQC/BT20086

Quiz: Add new question type Multiple Answer Dropdown - refs BT#20086
pull/4370/head
Nicolas Ducoulombier 3 years ago committed by GitHub
commit aa2cea0751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 138
      main/exercise/MultipleAnswerDropdown.php
  2. 23
      main/exercise/admin.php
  3. 51
      main/exercise/exercise.class.php
  4. 1
      main/exercise/exercise_show.php
  5. 4
      main/exercise/export/exercise_import.inc.php
  6. 12
      main/exercise/export/qti2/qti2_classes.php
  7. 7
      main/exercise/export/qti2/qti2_export.php
  8. 185
      main/exercise/multiple_answer_dropdown_admin.php
  9. 27
      main/exercise/question.class.php
  10. 12
      main/exercise/question_admin.inc.php
  11. BIN
      main/exercise/quiz_template.xls
  12. 9
      main/exercise/upload_exercise.php
  13. BIN
      main/img/icons/128/mcma_dropdown.png
  14. BIN
      main/img/icons/128/mcma_dropdown_na.png
  15. BIN
      main/img/icons/16/mcma_dropdown.png
  16. BIN
      main/img/icons/16/mcma_dropdown_na.png
  17. BIN
      main/img/icons/22/mcma_dropdown.png
  18. BIN
      main/img/icons/22/mcma_dropdown_na.png
  19. BIN
      main/img/icons/32/mcma_dropdown.png
  20. BIN
      main/img/icons/32/mcma_dropdown_na.png
  21. BIN
      main/img/icons/48/mcma_dropdown.png
  22. BIN
      main/img/icons/48/mcma_dropdown_na.png
  23. BIN
      main/img/icons/64/mcma_dropdown.png
  24. BIN
      main/img/icons/64/mcma_dropdown_na.png
  25. 224
      main/img/icons/svg/mcma_dropdown.svg
  26. 223
      main/img/icons/svg/mcma_dropdown_na.svg
  27. 1
      main/inc/lib/api.lib.php
  28. 46
      main/inc/lib/exercise.lib.php
  29. 116
      main/inc/lib/exercise_show_functions.lib.php
  30. 20
      main/inc/lib/import.lib.php

@ -0,0 +1,138 @@
<?php
/* For licensing terms, see /license.txt */
class MultipleAnswerDropdown extends Question
{
public $typePicture = 'mcma_dropdown.png';
public $explanationLangVar = 'MultipleAnswerDropdown';
public function __construct()
{
parent::__construct();
$this->type = MULTIPLE_ANSWER_DROPDOWN;
}
public function createForm(&$form, $exercise)
{
global $text;
parent::createForm($form, $exercise);
$objExe = ChamiloSession::read('objExercise');
$form->addTextarea(
'list_text',
[get_lang('AnswerList'), get_lang('EnterListOfAnswersOneAnswerByLine')],
['rows' => 8]
);
$form->addFile(
'list_file',
['', get_lang('OrSelectCsvFileWithListOfAnswers')],
['accept' => 'text/csv']
);
$buttonGroup = [];
if ($objExe->edit_exercise_in_lp == true ||
(empty($this->exerciseList) && empty($objExe->iid))
) {
$buttonGroup[] = $form->addButton(
'submitQuestion',
$text,
'check',
'primary',
'default',
null,
['id' => 'submit-question'],
true
);
}
$form->addGroup($buttonGroup);
if (!empty($this->iid)) {
$objAnswer = new Answer($this->iid, 0, $exercise, false);
$optionData = array_column(
$objAnswer->getAnswers(),
'answer'
);
$form->setDefaults(
['list_text' => implode(PHP_EOL, $optionData)]
);
}
}
public function createAnswersForm($form)
{
}
public function processCreation($form, $exercise)
{
$listFile = $form->getSubmitValue('list_file');
$listText = $form->getSubmitValue('list_text');
parent::processCreation($form, $exercise);
$lines = [];
if (UPLOAD_ERR_OK === (int) $listFile['error']) {
$lines = Import::csvColumnToArray($listFile['tmp_name']);
} elseif (!empty($listText)) {
$lines = explode("\n", $listText);
}
$lines = array_map('trim', $lines);
$lines = array_filter($lines);
$objAnswer = new Answer($this->iid);
$i = 1;
foreach ($lines as $line) {
$isCorrect = 0;
if (isset($objAnswer->correct[$i])) {
$isCorrect = (int) $objAnswer->correct[$i];
}
$objAnswer->createAnswer($line, $isCorrect, '', 0, $i++);
}
$objAnswer->save();
}
/**
* @param FormValidator $form
* @param Exercise $exercise
*
* @return void
*/
public function processAnswersCreation($form, $exercise)
{
}
public function return_header(Exercise $exercise, $counter = null, $score = [])
{
$header = parent::return_header($exercise, $counter, $score);
$header .= '<table class="'.$this->question_table_class.'"><thead><tr>';
$header .= '<th class="text-center">'.get_lang('Choice').'</th>';
if ($exercise->showExpectedChoiceColumn()) {
$header .= '<th class="text-center">'.get_lang('ExpectedChoice').'</th>';
}
$header .= '<th>'.get_lang('Answer').'</th>';
if ($exercise->showExpectedChoice()) {
$header .= '<th class="text-center">'.get_lang('Status').'</th>';
}
$header .= '</tr></thead>';
return $header;
}
}

@ -101,8 +101,10 @@ Event::delete_all_incomplete_attempts(
$objExercise = Session::read('objExercise');
$objQuestion = Session::read('objQuestion');
if (isset($_REQUEST['convertAnswer'])) {
$objQuestion = $objQuestion->swapSimpleAnswerTypes();
if (isset($_REQUEST['convertAnswer']) || isset($_REQUEST['convertAnswerAlt'])) {
$objQuestion = $objQuestion->swapSimpleAnswerTypes(
isset($_REQUEST['convertAnswerAlt']) ? 1 : 0
);
Session::write('objQuestion', $objQuestion);
}
$objAnswer = Session::read('objAnswer');
@ -324,12 +326,12 @@ $inATest = isset($exerciseId) && $exerciseId > 0;
if ($inATest) {
echo '<div class="actions">';
if (isset($_GET['hotspotadmin']) || isset($_GET['newQuestion'])) {
if (isset($_GET['hotspotadmin']) || isset($_GET['mad_admin']) || isset($_GET['newQuestion'])) {
echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'">'.
Display::return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM).'</a>';
}
if (!isset($_GET['hotspotadmin']) && !isset($_GET['newQuestion']) && !isset($_GET['editQuestion'])) {
if (!isset($_GET['hotspotadmin']) && !isset($_GET['mad_admin']) && !isset($_GET['newQuestion']) && !isset($_GET['editQuestion'])) {
echo '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq().'">'.
Display::return_icon('back.png', get_lang('BackToExercisesList'), '', ICON_SIZE_MEDIUM).'</a>';
}
@ -476,7 +478,18 @@ if (isset($_GET['hotspotadmin'])) {
require 'hotspot_admin.inc.php';
}
if (!$newQuestion && !$modifyQuestion && !$editQuestion && !isset($_GET['hotspotadmin'])) {
if (isset($_GET['mad_admin'])) {
if (!is_object($objQuestion)) {
$objQuestion = Question::read($_GET['mad_admin']);
}
if (!$objQuestion) {
api_not_allowed();
}
require 'multiple_answer_dropdown_admin.php';
}
if (!$newQuestion && !$modifyQuestion && !$editQuestion && !isset($_GET['hotspotadmin']) && !isset($_GET['mad_admin'])) {
// question list management
require 'question_list_admin.inc.php';
}

@ -3914,6 +3914,48 @@ class Exercise
);
}
if (MULTIPLE_ANSWER_DROPDOWN == $answerType) {
$questionScore = $questionWeighting;
if ($from_database) {
$studentChoices = Database::store_result(
Database::query(
"SELECT answer FROM $TBL_TRACK_ATTEMPT WHERE exe_id = $exeId AND question_id = $questionId"
),
'ASSOC'
);
$studentChoices = array_column($studentChoices, 'answer');
} else {
$studentChoices = array_values($choice);
}
$correctChoices = array_filter(
$answerMatching,
function ($answerId) use ($objAnswerTmp) {
$index = array_search($answerId, $objAnswerTmp->iid);
return true === (bool) $objAnswerTmp->correct[$index];
},
ARRAY_FILTER_USE_KEY
);
$correctChoices = array_keys($correctChoices);
if (array_diff($studentChoices, $correctChoices) || array_diff($correctChoices, $studentChoices)) {
$questionScore = 0;
}
if ($show_result) {
echo ExerciseShowFunctions::displayMultipleAnswerDropdown(
$this,
$objAnswerTmp,
$correctChoices,
$studentChoices,
$showTotalScoreAndUserChoicesInLastAttempt
);
}
}
if ($debug) {
error_log('-- Start answer loop --');
}
@ -6182,9 +6224,14 @@ class Exercise
$questionDuration
);
}
} elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
} elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER || MULTIPLE_ANSWER_DROPDOWN == $answerType) {
if ($choice != 0) {
$reply = array_keys($choice);
if (MULTIPLE_ANSWER_DROPDOWN == $answerType) {
$reply = array_values($choice);
} else {
$reply = array_keys($choice);
}
for ($i = 0; $i < count($reply); $i++) {
$ans = $reply[$i];
Event::saveQuestionAttempt(

@ -448,6 +448,7 @@ foreach ($questionList as $questionId) {
case DRAGGABLE:
case READING_COMPREHENSION:
case MATCHING_DRAGGABLE:
case MULTIPLE_ANSWER_DROPDOWN:
$question_result = $objExercise->manage_answer(
$id,
$questionId,

@ -256,6 +256,10 @@ function import_exercise($file)
$totalCorrectWeight = $question_array['weighting'][0];
}
if (!empty($question_array['default_weighting'])) {
$totalCorrectWeight = (float) $question_array['default_weighting'];
}
$question->updateWeighting($totalCorrectWeight);
$question->save($exercise);
$answer->save();

@ -44,6 +44,7 @@ class Ims2Question extends Question
return $answer;
case MCMA:
case MULTIPLE_ANSWER_DROPDOWN:
$answer = new ImsAnswerMultipleChoice($this->iid);
return $answer;
@ -133,7 +134,7 @@ class ImsAnswerMultipleChoice extends Answer implements ImsAnswerInterface
{
$this->answerList = $this->getAnswersList(true);
$type = $this->getQuestionType();
if ($type == MCMA) {
if (in_array($type, [MCMA, MULTIPLE_ANSWER_DROPDOWN])) {
$cardinality = 'multiple';
} else {
$cardinality = 'single';
@ -154,7 +155,14 @@ class ImsAnswerMultipleChoice extends Answer implements ImsAnswerInterface
// Add the grading
if (is_array($this->answerList)) {
$out .= ' <mapping>'."\n";
$out .= ' <mapping';
if (MULTIPLE_ANSWER_DROPDOWN == $this->getQuestionType()) {
$out .= ' defaultValue="'.$question->selectWeighting().'"';
}
$out .= '>'."\n";
foreach ($this->answerList as $current_answer) {
if (isset($current_answer['grade'])) {
$out .= ' <mapEntry mapKey="answer_'.$current_answer['iid'].'" mappedValue="'.$current_answer['grade'].'" />'."\n";

@ -458,7 +458,12 @@ function export_question_qti($questionId, $standalone = true)
return '';
}
$isValid = $qst instanceof UniqueAnswer || $qst instanceof MultipleAnswer || $qst instanceof FreeAnswer;
$isValid = $qst instanceof UniqueAnswer
|| $qst instanceof MultipleAnswer
|| $qst instanceof FreeAnswer
|| $qst instanceof MultipleAnswerDropdown
;
if (!$isValid) {
return '';
}

@ -0,0 +1,185 @@
<?php
/* For licensing terms, see /license.txt */
use Symfony\Component\HttpFoundation\Request as HttpRequest;
$questionId = (int) $_GET['mad_admin'];
$exerciseId = $exerciseId ?? 0;
/** @var Exercise $objExercise */
$objExercise = $objExercise ?? null;
/** @var Question $objQuestion */
$objQuestion = $objQuestion ?? null;
if (!is_object($objQuestion)) {
$objQuestion = Question::read($questionId);
}
$objAnswer = new Answer($objQuestion->iid, 0, $objExercise);
$options = [];
for ($i = 1; $i <= $objAnswer->nbrAnswers; $i++) {
$options[$objAnswer->iid[$i]] = $objAnswer->answer[$i];
}
$webCodePath = api_get_path(WEB_CODE_PATH).'exercise/admin.php?'.api_get_cidreq().'&';
$adminUrl = $webCodePath.http_build_query(['exerciseId' => $exerciseId]);
$httpRequest = HttpRequest::createFromGlobals();
$submitAnswers = $httpRequest->request->has('submitAnswers');
if ($submitAnswers) {
$questionAnswers = array_map(
'intval',
(array) $httpRequest->request->get('answer', [])
);
$questionWeighting = (float) $httpRequest->request->get('weighting', 0);
$tblQuizAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
Database::query(
"UPDATE $tblQuizAnswer SET correct = 0 WHERE question_id = ".$objQuestion->iid
);
Database::query(
"UPDATE $tblQuizAnswer SET correct = 1
WHERE question_id = {$objQuestion->iid} AND iid IN (".implode(', ', $questionAnswers).")"
);
$objQuestion->updateWeighting($questionWeighting);
$objQuestion->save($objExercise);
echo '<script type="text/javascript">window.location.href="'.$adminUrl.'&message=ItemUpdated"</script>';
exit;
}
if ($questionId) {
$answers = [];
for ($i = 1; $i <= $objAnswer->nbrAnswers; $i++) {
if (false === (bool) $objAnswer->correct[$i]) {
continue;
}
$answers[] = [
$objAnswer->iid[$i],
$objAnswer->answer[$i],
];
}
$selfUrl = $webCodePath.http_build_query(['mad_admin' => $questionId, 'exerciseId' => $exerciseId]);
echo Display::page_header(
get_lang('Question').': '.$objQuestion->selectTitle()
); ?>
<form action="<?php echo $selfUrl; ?>" class="form-horizontal" method="post">
<div class="form-group">
<label for="option" class="col-sm-2 control-label"><?php echo get_lang('Answer'); ?></label>
<div class="col-sm-8">
<?php echo Display::select(
'option',
$options,
-1,
['data-live-search' => 'true', 'class' => 'form-control selectpicker']
); ?>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-default" name="add_answers">
<em class="fa fa-plus fa-fw" aria-hidden="true"></em>
<?php echo get_lang('Add'); ?>
</button>
</div>
</div>
<fieldset>
<legend><?php echo get_lang('Answers'); ?></legend>
<table class="table table-striped table-hover table-condensed">
<thead>
<tr>
<th class="text-right"><?php echo get_lang('Number'); ?></th>
<th style="width: 90%;"><?php echo get_lang('Answer'); ?></th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</fieldset>
<div class="form-group">
<label for="weighting" class="control-label col-sm-2"><?php echo get_lang('Weighting'); ?></label>
<div class="col-sm-8">
<input type="number" required min="0" class="form-control" step="any" id="weighting" name="weighting"
value="<?php echo $objQuestion->weighting; ?>">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" name="submitAnswers" value="submitAnswers">
<em class="fa fa-save fa-fw" aria-hidden="true"></em>
<?php echo get_lang('AddQuestionToExercise'); ?>
</button>
</div>
</div>
</form>
<script>
$(function () {
var lines = <?php echo json_encode($options); ?>;
var answers = <?php echo json_encode($answers); ?>;
var $txtOption = $('#option');
var $tBody = $('table tbody');
$('[name="add_answers"]').on('click', function (e) {
e.preventDefault();
var selected = $txtOption.val();
if ($txtOption.val() < 0) {
return;
}
answers.push([selected, lines[selected]]);
$txtOption.val(-1).selectpicker('refresh');
renderList();
});
$tBody.on('click', '.btn-remove', function (e) {
e.preventDefault();
var index = $(this).data('index');
answers.splice(index, 1);
renderList();
});
function renderList () {
var html = '';
$.each(answers, function (key, line) {
var counter = key + 1;
html += '<tr><td class="text-right">'
+ counter + "\n"
+ '<input type="hidden" name="counter[]" value="' + counter + '">'
+ '</td><td>'
+ line[1] + "\n"
+ '<input type="hidden" name="answer[]" value="' + line[0] + '">'
+ '</td><td class="text-right">'
+ '<button type="button" class="btn btn-default btn-remove" data-index="' + key + '" aria-label="<?php echo get_lang('Remove'); ?>">'
+ '<?php echo Display::returnFontAwesomeIcon('minus', '', true); ?>'
+ '</button>'
+ '</td></tr>';
});
$tBody.html(html);
}
renderList();
})
</script>
<?php
}

@ -73,6 +73,7 @@ abstract class Question
ANNOTATION => ['Annotation.php', 'Annotation'],
READING_COMPREHENSION => ['ReadingComprehension.php', 'ReadingComprehension'],
UPLOAD_ANSWER => ['UploadAnswer.php', 'UploadAnswer'],
MULTIPLE_ANSWER_DROPDOWN => ['MultipleAnswerDropdown.php', 'MultipleAnswerDropdown'],
];
/**
@ -1820,6 +1821,7 @@ abstract class Question
global $text;
switch ($this->type) {
case UNIQUE_ANSWER:
case MULTIPLE_ANSWER_DROPDOWN:
$buttonGroup = [];
$buttonGroup[] = $form->addButtonSave(
$text,
@ -1829,7 +1831,7 @@ abstract class Question
$buttonGroup[] = $form->addButton(
'convertAnswer',
get_lang('ConvertToMultipleAnswer'),
'dot-circle-o',
'check-square-o',
'default',
null,
null,
@ -1848,6 +1850,16 @@ abstract class Question
$buttonGroup[] = $form->addButton(
'convertAnswer',
get_lang('ConvertToUniqueAnswer'),
'dot-circle-o',
'default',
null,
null,
null,
true
);
$buttonGroup[] = $form->addButton(
'convertAnswerAlt',
get_lang('ConvertToMultipleAnswerDropdown'),
'check-square-o',
'default',
null,
@ -2549,13 +2561,14 @@ abstract class Question
*
* @return UniqueAnswer|MultipleAnswer
*/
public function swapSimpleAnswerTypes()
public function swapSimpleAnswerTypes($index = 0)
{
$oppositeAnswers = [
UNIQUE_ANSWER => MULTIPLE_ANSWER,
MULTIPLE_ANSWER => UNIQUE_ANSWER,
UNIQUE_ANSWER => [MULTIPLE_ANSWER],
MULTIPLE_ANSWER => [UNIQUE_ANSWER, MULTIPLE_ANSWER_DROPDOWN],
MULTIPLE_ANSWER_DROPDOWN => [MULTIPLE_ANSWER],
];
$this->type = $oppositeAnswers[$this->type];
$this->type = $oppositeAnswers[$this->type][$index];
Database::update(
Database::get_course_table(TABLE_QUIZ_QUESTION),
['type' => $this->type],
@ -2564,12 +2577,16 @@ abstract class Question
$answerClasses = [
UNIQUE_ANSWER => 'UniqueAnswer',
MULTIPLE_ANSWER => 'MultipleAnswer',
MULTIPLE_ANSWER_DROPDOWN => 'MultipleAnswerDropdown',
];
$swappedAnswer = new $answerClasses[$this->type]();
foreach ($this as $key => $value) {
$swappedAnswer->$key = $value;
}
$objAnswer = new Answer($swappedAnswer->iid);
$_POST['nb_answers'] = $objAnswer->nbrAnswers;
return $swappedAnswer;
}

@ -57,7 +57,15 @@ if (is_object($objQuestion)) {
$objQuestion->processAnswersCreation($form, $objExercise);
// TODO: maybe here is the better place to index this tool, including answers text
// redirect
if (!in_array($objQuestion->type, [HOT_SPOT, HOT_SPOT_GLOBAL, HOT_SPOT_DELINEATION])) {
if (in_array($objQuestion->type, [HOT_SPOT, HOT_SPOT_GLOBAL, HOT_SPOT_DELINEATION])) {
echo '<script type="text/javascript">window.location.href="admin.php?exerciseId='.$exerciseId.'&page='.$page.'&hotspotadmin='.$objQuestion->iid.'&'.api_get_cidreq(
).'"</script>';
} elseif (MULTIPLE_ANSWER_DROPDOWN == $objQuestion->type) {
$url = 'admin.php?'
.api_get_cidreq().'&'
.http_build_query(['exerciseId' => $exerciseId, 'page' => $page, 'mad_admin' => $objQuestion->iid]);
echo '<script type="text/javascript">window.location.href="'.$url.'"</script>';
} else {
if (isset($_GET['editQuestion'])) {
if (empty($exerciseId)) {
Display::addFlash(Display::return_message(get_lang('ItemUpdated')));
@ -75,8 +83,6 @@ if (is_object($objQuestion)) {
}
echo '<script type="text/javascript">window.location.href="admin.php?exerciseId='.$exerciseId.'&'.api_get_cidreq().'&page='.$page.'&message=ItemAdded"</script>';
}
} else {
echo '<script type="text/javascript">window.location.href="admin.php?exerciseId='.$exerciseId.'&page='.$page.'&hotspotadmin='.$objQuestion->iid.'&'.api_get_cidreq().'"</script>';
}
} else {
if (isset($questionName)) {

Binary file not shown.

@ -85,6 +85,7 @@ function lp_upload_quiz_main()
$tableList = [
UNIQUE_ANSWER => get_lang('UniqueSelect'),
MULTIPLE_ANSWER => get_lang('MultipleSelect'),
MULTIPLE_ANSWER_DROPDOWN => get_lang('MultipleAnswerDropdown'),
FILL_IN_BLANKS => get_lang('FillBlanks'),
FILL_IN_BLANKS_GLOBAL => get_lang('FillBlanksGlobal'),
MATCHING => get_lang('Matching'),
@ -329,6 +330,9 @@ function lp_upload_quiz_action_handling()
case MULTIPLE_ANSWER:
$answer = new MultipleAnswer();
break;
case MULTIPLE_ANSWER_DROPDOWN:
$answer = new MultipleAnswerDropdown();
break;
case FILL_IN_BLANKS:
case FILL_IN_BLANKS_GLOBAL:
$answer = new FillBlanks();
@ -362,6 +366,7 @@ function lp_upload_quiz_action_handling()
}
switch ($detectQuestionType) {
case GLOBAL_MULTIPLE_ANSWER:
case MULTIPLE_ANSWER_DROPDOWN:
case MULTIPLE_ANSWER:
case UNIQUE_ANSWER:
$total = 0;
@ -424,6 +429,9 @@ function lp_upload_quiz_action_handling()
//$total = $total - $score;
}
break;
case MULTIPLE_ANSWER_DROPDOWN:
$score = 0;
break;
}
$objAnswer->createAnswer(
@ -450,6 +458,7 @@ function lp_upload_quiz_action_handling()
if ($questionObj) {
switch ($detectQuestionType) {
case GLOBAL_MULTIPLE_ANSWER:
case MULTIPLE_ANSWER_DROPDOWN:
$questionObj->updateWeighting($globalScore);
break;
case UNIQUE_ANSWER:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,224 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
sodipodi:docname="mcma_dropdown.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
version="1.1"
viewBox="0 0 124 124"
id="svg7613"
height="124"
width="124"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs7615">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4799"
id="radialGradient7559"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.55758631,0,0,0.57073983,33.783815,180.81737)"
cx="55.21796"
cy="458.46527"
fx="55.21796"
fy="458.46527"
r="15.213428" />
<linearGradient
id="linearGradient4799">
<stop
style="stop-color:#ffde00;stop-opacity:1;"
offset="0"
id="stop4801" />
<stop
style="stop-color:#ffa700;stop-opacity:1;"
offset="1"
id="stop4803" />
</linearGradient>
<radialGradient
r="15.213428"
fy="458.46527"
fx="55.21796"
cy="458.46527"
cx="55.21796"
gradientTransform="matrix(0.55758631,0,0,0.57073983,33.783815,180.81737)"
gradientUnits="userSpaceOnUse"
id="radialGradient7646"
xlink:href="#linearGradient4799"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4799-9"
id="radialGradient7557"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.55758631,0,0,0.57073983,104.95785,180.81737)"
cx="55.21796"
cy="458.46527"
fx="55.21796"
fy="458.46527"
r="15.213428" />
<linearGradient
id="linearGradient4799-9">
<stop
style="stop-color:#ffde00;stop-opacity:1;"
offset="0"
id="stop4801-0" />
<stop
style="stop-color:#ffa700;stop-opacity:1;"
offset="1"
id="stop4803-9" />
</linearGradient>
<radialGradient
r="15.213428"
fy="458.46527"
fx="55.21796"
cy="458.46527"
cx="55.21796"
gradientTransform="matrix(1.0803234,0,0,1.1058083,-31.969647,523.39062)"
gradientUnits="userSpaceOnUse"
id="radialGradient7869"
xlink:href="#linearGradient4799-9"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.5443549"
inkscape:cx="30.188851"
inkscape:cy="73.638225"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="731"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata7618">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-928.36218)">
<g
id="g9959">
<rect
style="opacity:0;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.9375"
id="rect4141"
width="123.99998"
height="123.99998"
x="0.14107606"
y="928.7854" />
<rect
style="fill:#2d404e;fill-opacity:1;stroke:none;stroke-width:1.9375"
id="rect4143"
width="83.096649"
height="103.31303"
x="22.55817"
y="940.23615"
ry="6.2725754" />
<rect
ry="6.2725754"
y="937.75317"
x="20.075216"
height="103.31303"
width="83.096649"
id="rect4145"
style="fill:#ffffff;fill-opacity:1;stroke:#70acc7;stroke-width:1.9375;stroke-opacity:1" />
<path
sodipodi:nodetypes="csssscc"
inkscape:connector-curvature="0"
id="rect8010"
d="m 89.608699,962.37229 v 38.08361 c 0,3.9836 -2.942747,7.1904 -6.598084,7.1904 H 40.974066 c -3.655339,0 -6.598085,-3.2068 -6.598085,-7.1904 v -38.08361 z"
style="fill:#ffffff;fill-opacity:1;stroke:#2f7ea6;stroke-width:2.20385;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="3.8419447"
y="950.85077"
x="34.103207"
height="16.660213"
width="55.775013"
id="rect8000"
style="fill:#70acc7;fill-opacity:1;stroke:#2f7ea6;stroke-width:1.6506;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#2f7ea6;fill-opacity:1;stroke:none;stroke-width:1.26366"
d="m 77.713426,950.11975 8.18356,0.0392 c 2.685773,0.0129 4.839793,2.30745 4.825224,4.58237 l -0.05864,9.15791 c -0.01115,1.74067 -1.891887,4.27381 -4.60529,4.2696 l -8.344846,-0.0132 z"
id="path8006"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssscc" />
<path
style="fill:#ffffff;stroke:none;stroke-width:1.0791"
d="m 80.762274,958.05706 3.254053,4.39126 3.219109,-4.39207 z"
id="path8008"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.95532px;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#1a1a1a;fill-opacity:1;stroke:none;stroke-width:0.662943"
x="37.096527"
y="1066.9473"
id="text8002"
transform="scale(1.09082,0.91674152)"><tspan
sodipodi:role="line"
x="37.096527"
y="1066.9473"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2508">a</tspan><tspan
sodipodi:role="line"
x="37.096527"
y="1079.7917"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2512">b</tspan><tspan
sodipodi:role="line"
x="37.096527"
y="1092.6364"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2514">c</tspan></text>
<path
id="path4274"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.04538;marker:none;enable-background:accumulate"
d="m 82.984827,972.12652 -3.160165,4.05231 -1.215446,-1.24304 -0.996663,0.99448 1.774555,1.8397 a 0.70016745,0.71606856 0 0 0 1.045283,-0.0746 l 3.646339,-4.67385 z"
inkscape:connector-curvature="0" />
<path
id="path4274-9"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.04538;marker:none;enable-background:accumulate"
d="m 82.93607,995.93544 -3.160165,4.05234 -1.215446,-1.24307 -0.996663,0.99448 1.774554,1.83981 a 0.70016745,0.71606856 0 0 0 1.045284,-0.075 l 3.646339,-4.67396 z"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient7869);fill-opacity:1;stroke:#ff4100;stroke-width:1.62048;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 26.096234,1025.4956 -0.05503,-9.8333 c 0.06297,-2.1592 3.965636,-2.0353 4.229969,-0.055 v 9.7234 l 6.537221,-6.5921 c 1.604153,-1.5049 3.93762,1.1364 2.526985,2.6369 l -6.097739,6.8668 8.569795,0.055 c 2.499859,0.3246 2.184782,4.4543 0.109856,4.6145 l -8.295115,-0.055 6.262542,6.0428 c 1.478545,1.6554 -0.493966,4.523 -2.691788,3.0764 l -7.031632,-6.757 -0.05503,9.7234 c 0.2486,2.0648 -3.983171,2.455 -4.394773,0.055 l 0.05503,-9.229 -6.207614,6.2076 c -2.185306,0.5379 -3.979237,-1.0157 -3.131271,-2.9116 l 6.59215,-6.3174 -9.503689,0.11 c -2.341042,-0.4546 -2.284235,-4.3097 0.05502,-4.6145 l 9.72342,-0.055 -6.811881,-6.702 c -1.325327,-2.1093 0.948697,-4.2446 2.746736,-2.9665 z"
id="path5136"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccccccccccc" />
<path
style="fill:#81c2e7;fill-opacity:1;stroke:#8e8e8e;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.666536,980.48038 H 87.172848"
id="path5283" />
<path
style="fill:#81c2e7;fill-opacity:1;stroke:#8e8e8e;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.666537,993.39969 H 87.172846"
id="path5283-0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
sodipodi:docname="mcma_dropdown_na.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
version="1.1"
viewBox="0 0 124 124"
id="svg7613"
height="124"
width="124"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs7615">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4799"
id="radialGradient7559"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.55758631,0,0,0.57073983,33.783815,180.81737)"
cx="55.21796"
cy="458.46527"
fx="55.21796"
fy="458.46527"
r="15.213428" />
<linearGradient
id="linearGradient4799">
<stop
style="stop-color:#ffde00;stop-opacity:1;"
offset="0"
id="stop4801" />
<stop
style="stop-color:#ffa700;stop-opacity:1;"
offset="1"
id="stop4803" />
</linearGradient>
<radialGradient
r="15.213428"
fy="458.46527"
fx="55.21796"
cy="458.46527"
cx="55.21796"
gradientTransform="matrix(0.55758631,0,0,0.57073983,33.783815,180.81737)"
gradientUnits="userSpaceOnUse"
id="radialGradient7646"
xlink:href="#linearGradient4799"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4799-9"
id="radialGradient7557"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.55758631,0,0,0.57073983,104.95785,180.81737)"
cx="55.21796"
cy="458.46527"
fx="55.21796"
fy="458.46527"
r="15.213428" />
<linearGradient
id="linearGradient4799-9">
<stop
style="stop-color:#cfcfcf;stop-opacity:1"
offset="0"
id="stop4801-0" />
<stop
style="stop-color:#aeaeae;stop-opacity:1"
offset="1"
id="stop4803-9" />
</linearGradient>
<radialGradient
r="15.213428"
fy="458.46527"
fx="55.21796"
cy="458.46527"
cx="55.21796"
gradientTransform="matrix(1.0803234,0,0,1.1058083,-31.969647,523.39062)"
gradientUnits="userSpaceOnUse"
id="radialGradient7869"
xlink:href="#linearGradient4799-9"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.5062374"
inkscape:cx="-28.728325"
inkscape:cy="73.017825"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1366"
inkscape:window-height="731"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata7618">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-928.36218)">
<g
id="g9959">
<rect
style="opacity:0;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.9375"
id="rect4141"
width="123.99998"
height="123.99998"
x="0.14107606"
y="928.7854" />
<rect
style="fill:#3c3c3c;fill-opacity:1;stroke:none;stroke-width:1.9375"
id="rect4143"
width="83.096649"
height="103.31303"
x="22.55817"
y="940.23615"
ry="6.2725754" />
<rect
ry="6.2725754"
y="937.75317"
x="20.075216"
height="103.31303"
width="83.096649"
id="rect4145"
style="fill:#ffffff;fill-opacity:1;stroke:#9d9d9d;stroke-width:1.9375;stroke-opacity:1" />
<path
sodipodi:nodetypes="csssscc"
inkscape:connector-curvature="0"
id="rect8010"
d="m 89.608699,962.37229 v 38.08361 c 0,3.9836 -2.942747,7.1904 -6.598084,7.1904 H 40.974066 c -3.655339,0 -6.598085,-3.2068 -6.598085,-7.1904 v -38.08361 z"
style="fill:#ffffff;fill-opacity:1;stroke:#6b6b6b;stroke-width:2.20385;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
ry="3.8419447"
y="950.85077"
x="34.103207"
height="16.660213"
width="55.775013"
id="rect8000"
style="fill:#9d9d9d;fill-opacity:1;stroke:#6b6b6b;stroke-width:1.6506;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#6b6b6b;fill-opacity:1;stroke:none;stroke-width:1.26366"
d="m 77.713426,950.11975 8.18356,0.0392 c 2.685773,0.0129 4.839793,2.30745 4.825224,4.58237 l -0.05864,9.15791 c -0.01115,1.74067 -1.891887,4.27381 -4.60529,4.2696 l -8.344846,-0.0132 z"
id="path8006"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssscc" />
<path
style="fill:#ffffff;stroke:none;stroke-width:1.0791"
d="m 80.762274,958.05706 3.254053,4.39126 3.219109,-4.39207 z"
id="path8008"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0%;font-family:Arial;-inkscape-font-specification:'Arial Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#1a1a1a;fill-opacity:1;stroke:none;stroke-width:0.662943"
x="37.096527"
y="1066.9473"
id="text8002"
transform="scale(1.09082,0.91674152)"><tspan
sodipodi:role="line"
x="37.096527"
y="1066.9473"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2508">a</tspan><tspan
sodipodi:role="line"
x="37.096527"
y="1079.7917"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2512">b</tspan><tspan
sodipodi:role="line"
x="37.096527"
y="1092.6362"
style="font-size:10.2756px;line-height:1.25;stroke-width:0.662943"
id="tspan2514">c</tspan></text>
<path
id="path4274"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.04538;marker:none;enable-background:accumulate"
d="m 82.984827,972.12652 -3.160165,4.05231 -1.215446,-1.24304 -0.996663,0.99448 1.774555,1.8397 a 0.70016745,0.71606856 0 0 0 1.045283,-0.0746 l 3.646339,-4.67385 z"
inkscape:connector-curvature="0" />
<path
id="path4274-9"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.04538;marker:none;enable-background:accumulate"
d="m 82.93607,995.93544 -3.160165,4.05234 -1.215446,-1.24307 -0.996663,0.99448 1.774554,1.83981 a 0.70016745,0.71606856 0 0 0 1.045284,-0.075 l 3.646339,-4.67396 z"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient7869);fill-opacity:1;stroke:#727272;stroke-width:1.62048;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 26.096234,1025.4956 -0.05503,-9.8333 c 0.06297,-2.1592 3.965636,-2.0353 4.229969,-0.055 v 9.7234 l 6.537221,-6.5921 c 1.604153,-1.5049 3.93762,1.1364 2.526985,2.6369 l -6.097739,6.8668 8.569795,0.055 c 2.499859,0.3246 2.184782,4.4543 0.109856,4.6145 l -8.295115,-0.055 6.262542,6.0428 c 1.478545,1.6554 -0.493966,4.523 -2.691788,3.0764 l -7.031632,-6.757 -0.05503,9.7234 c 0.2486,2.0648 -3.983171,2.455 -4.394773,0.055 l 0.05503,-9.229 -6.207614,6.2076 c -2.185306,0.5379 -3.979237,-1.0157 -3.131271,-2.9116 l 6.59215,-6.3174 -9.503689,0.11 c -2.341042,-0.4546 -2.284235,-4.3097 0.05502,-4.6145 l 9.72342,-0.055 -6.811881,-6.702 c -1.325327,-2.1093 0.948697,-4.2446 2.746736,-2.9665 z"
id="path5136"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccccccccccc" />
<path
style="fill:#b3b3b3;fill-opacity:1;stroke:#8e8e8e;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.666536,980.48038 H 87.172848"
id="path5283" />
<path
style="fill:#b3b3b3;fill-opacity:1;stroke:#8e8e8e;stroke-width:0.3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.666537,993.39969 H 87.172846"
id="path5283-0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@ -528,6 +528,7 @@ define('MATCHING_GLOBAL', 24);
define('MATCHING_DRAGGABLE_GLOBAL', 25);
define('HOT_SPOT_GLOBAL', 26);
define('FILL_IN_BLANKS_GLOBAL', 27);
define('MULTIPLE_ANSWER_DROPDOWN', 28);
define('EXERCISE_CATEGORY_RANDOM_SHUFFLED', 1);
define('EXERCISE_CATEGORY_RANDOM_ORDERED', 2);

@ -122,6 +122,11 @@ class ExerciseLib
$objAnswerTmp = new Answer($questionId, $course_id, $exercise);
$nbrAnswers = $objAnswerTmp->selectNbrAnswers();
$quizQuestionOptions = Question::readQuestionOption($questionId, $course_id);
$selectableOptions = [];
for ($i = 1; $i <= $objAnswerTmp->nbrAnswers; $i++) {
$selectableOptions[$objAnswerTmp->iid[$i]] = $objAnswerTmp->answer[$i];
}
// For "matching" type here, we need something a little bit special
// because the match between the suggestions and the answers cannot be
@ -1451,9 +1456,49 @@ HTML;
$matching_correct_answer++;
}
break;
case MULTIPLE_ANSWER_DROPDOWN:
if ($debug_mark_answer) {
if ($answerCorrect) {
$s .= '<p>'.Display::returnFontAwesomeIcon('check-square-o', '', true);
$s .= Security::remove_XSS($objAnswerTmp->answer[$answerId]).'</p>';
}
}
break;
}
}
if (MULTIPLE_ANSWER_DROPDOWN == $answerType && !$debug_mark_answer) {
$userChoiceList = array_unique($userChoiceList);
$input_id = "choice-$questionId";
$s .= Display::input('hidden', "choice2[$questionId]", '0')
.'<p>'
.Display::select(
"choice[$questionId][]",
$selectableOptions,
$userChoiceList,
[
'id' => $input_id,
'multiple' => 'multiple',
],
false
)
.'</p>'
.'<script>$(function () {
$(\'#'.$input_id.'\').select2({
selectOnClose: false,
placeholder: {id: -2, text: "'.get_lang('None').'"}
});
});</script>'
.'<style>
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
display:block;
width: 100%;
white-space: break-spaces;
}</style>'
;
}
if ($show_comment) {
if (in_array(
$answerType,
@ -6143,6 +6188,7 @@ EOT;
UPLOAD_ANSWER,
MATCHING_GLOBAL,
FILL_IN_BLANKS_GLOBAL,
MULTIPLE_ANSWER_DROPDOWN,
];
$defaultTypes = [UNIQUE_ANSWER, MULTIPLE_ANSWER, UNIQUE_ANSWER_IMAGE];
$types = $defaultTypes;

@ -342,6 +342,122 @@ class ExerciseShowFunctions
echo $content;
}
public static function displayMultipleAnswerDropdown(
Exercise $exercise,
Answer $answer,
array $correctAnswers,
array $studentChoices,
bool $showTotalScoreAndUserChoices = true
): string {
if (true === $exercise->hideNoAnswer && empty($studentChoices)) {
return '';
}
$studentChoices = array_filter(
$studentChoices,
function ($studentAnswerId) {
return -1 !== (int) $studentAnswerId;
}
);
$allChoices = array_unique(
array_merge($correctAnswers, $studentChoices)
);
sort($allChoices);
$checkboxOn = Display::return_icon('checkbox_on.png', null, null, ICON_SIZE_TINY);
$checkboxOff = Display::return_icon('checkbox_off.png', null, null, ICON_SIZE_TINY);
$labelSuccess = Display::label(get_lang('Correct'), 'success');
$labelIncorrect = Display::label(get_lang('Incorrect'), 'danger');
$html = '';
foreach ($allChoices as $choice) {
$isStudentAnswer = in_array($choice, $studentChoices);
$isExpectedAnswer = in_array($choice, $correctAnswers);
$isCorrectAnswer = $isStudentAnswer && $isExpectedAnswer;
$answerPosition = array_search($choice, $answer->iid);
$hideExpectedAnswer = false;
switch ($exercise->selectResultsDisabled()) {
case RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER:
$hideExpectedAnswer = true;
if (!$isCorrectAnswer && empty($studentChoices)) {
continue 2;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ONLY:
if (0 == $exercise->getFeedbackType()) {
$hideExpectedAnswer = true;
}
break;
case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
$hideExpectedAnswer = true;
if ($showTotalScoreAndUserChoices) {
$hideExpectedAnswer = false;
}
break;
case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
if (false === $showTotalScoreAndUserChoices && empty($studentChoices)) {
continue 2;
}
break;
}
$studentAnswerClass = '';
if ($isCorrectAnswer
&& in_array(
$exercise->selectResultsDisabled(),
[
RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
]
)
) {
$studentAnswerClass = 'success';
}
$html .= '<tr class="'.$studentAnswerClass.'">';
$html .= '<td class="text-center">'.($isStudentAnswer ? $checkboxOn : $checkboxOff).'</td>';
if ($exercise->showExpectedChoiceColumn()) {
$html .= '<td class="text-center">';
if ($hideExpectedAnswer) {
$html .= '<span class="text-muted">&mdash;</span>';
} else {
$html .= $isExpectedAnswer ? $checkboxOn : $checkboxOff;
}
$html .= '</td>';
}
$answerText = $answer->answer[$answerPosition] ?? get_lang('None');
if ($exercise->export) {
$answerText = strip_tags_blacklist($answerText, ['title', 'head']);
// Fix answers that contains this tags
$tags = ['<html>', '</html>', '<body>', '</body>'];
$answerText = str_replace($tags, '', $answerText);
}
$html .= '<td>'.Security::remove_XSS($answerText).'</td>';
if ($exercise->showExpectedChoice()) {
$html .= '<td class="text-center">'.($isCorrectAnswer ? $labelSuccess : $labelIncorrect).'</td>';
}
$html .= '</tr>';
}
return $html;
}
/**
* Display the answers to a multiple choice question.
*

@ -69,6 +69,26 @@ class Import
return [];
}
public static function csvColumnToArray($filename, $columnIndex = 0): array
{
if (empty($filename)) {
return [];
}
$reader = Reader::createFromPath($filename, 'r');
if (!$reader) {
return [];
}
$reader->setDelimiter(';');
$reader->stripBom(true);
$iterator = $reader->fetchColumn($columnIndex);
return iterator_to_array($iterator);
}
/**
* @param string $filename
*

Loading…
Cancel
Save