Merge pull request #4360 from AngelFQC/BT20086
Quiz: Add new question type Multiple Answer Dropdown - refs BT#20086pull/4370/head
@ -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; |
||||
} |
||||
} |
@ -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> </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 |
||||
} |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 499 B |
After Width: | Height: | Size: 479 B |
After Width: | Height: | Size: 728 B |
After Width: | Height: | Size: 705 B |
After Width: | Height: | Size: 996 B |
After Width: | Height: | Size: 973 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 10 KiB |