19 declare(strict_types=1);
79 private int $output_type = self::OUTPUT_ORDER
83 $this->shuffle =
true;
98 return $this->title !==
'' 99 && $this->author !==
'' 100 && $this->question !==
'' 115 $result = $this->db->queryF(
120 if ($result->numRows() == 1) {
121 $data = $this->db->fetchAssoc($result);
122 $this->
setId($question_id);
137 $this->is_singleline =
$data[
'allow_images'] ===
null ||
$data[
'allow_images'] ===
'0';
138 $this->lastChange =
$data[
'tstamp'];
140 if (isset(
$data[
'feedback_setting'])) {
141 $this->feedback_setting =
$data[
'feedback_setting'];
156 $result = $this->db->queryF(
157 "SELECT * FROM qpl_a_mc WHERE question_fi = %s ORDER BY aorder ASC",
161 if ($result->numRows() > 0) {
162 while (
$data = $this->db->fetchAssoc($result)) {
164 if (!file_exists($imagefilename)) {
175 $answer->setPointsUnchecked(
$data[
"points_unchecked"]);
176 $answer->setImage(
$data[
"imagefile"] ?
$data[
"imagefile"] :
null);
177 array_push($this->answers, $answer);
181 parent::loadFromDb($question_id);
212 string $answertext =
'',
214 float $points_unchecked = 0.0,
216 ?
string $answerimage =
null,
219 if (array_key_exists($order, $this->answers)) {
228 $answer->setPointsUnchecked($points_unchecked);
229 $answer->setImage($answerimage);
231 for ($i = 0; $i < $order; $i++) {
232 $newchoices[] = $this->answers[$i];
234 $newchoices[] = $answer;
235 for ($i = $order, $iMax = count($this->answers); $i < $iMax; $i++) {
236 $changed = $this->answers[$i];
237 $changed->setOrder($i + 1);
238 $newchoices[] = $changed;
240 $this->answers = $newchoices;
246 count($this->answers),
250 $answer->setPointsUnchecked($points_unchecked);
251 $answer->setImage($answerimage);
252 $this->answers[] = $answer;
264 return count($this->answers);
280 if (count($this->answers) < 1) {
283 if ($index >= count($this->answers)) {
287 return $this->answers[$index];
302 if (count($this->answers) < 1) {
305 if ($index >= count($this->answers)) {
308 $answer = $this->answers[$index];
309 if ($answer->hasImage()) {
310 $this->deleteImage($answer->getImage());
312 unset($this->answers[$index]);
313 $this->answers = array_values($this->answers);
314 for ($i = 0, $iMax = count($this->answers); $i < $iMax; $i++) {
315 if ($this->answers[$i]->
getOrder() > $index) {
316 $this->answers[$i]->setOrder($i);
338 $total_max_points = 0.0;
340 $total_max_points += max($answer->getPointsChecked(), $answer->getPointsUnchecked());
342 return $total_max_points;
348 bool $authorized_solution =
true 351 if ($pass ===
null) {
355 while (
$data = $this->db->fetchAssoc($result)) {
356 if (
$data[
'value1'] !==
'') {
357 array_push($found_values,
$data[
'value1']);
370 $failureMsg = sprintf(
371 $this->
lng->txt(
'ass_mc_sel_lim_exhausted_hint'),
376 $this->tpl->setOnScreenMessage(
'failure', $failureMsg,
true);
386 $tst_force_form_diff_input = $this->questionpool_request->strArray(
'tst_force_form_diff_input');
387 return !count($solutionSubmit) && !empty($tst_force_form_diff_input);
393 bool $authorized =
true 399 function () use ($answer, $active_id, $pass, $authorized) {
402 foreach ($answer as $value) {
421 if (!$this->is_singleline) {
430 'allow_images' => [
'text', $this->is_singleline ? 0 : 1],
435 [
'question_fi' => [
'integer', $this->
getId()]]
442 $result = $this->db->queryF(
443 "SELECT * FROM qpl_fb_specific WHERE question_fi = %s",
447 $db_feedback = $this->db->fetchAll($result);
452 $result = $this->db->queryF(
453 "SELECT answer_id, aorder FROM qpl_a_mc WHERE question_fi = %s",
457 $db_answers = $this->db->fetchAll($result);
460 $post_answer_order_for_id = [];
461 foreach ($this->answers as $answer) {
463 if ($answer->getId() !==
null && !in_array($answer->getId(), array_keys($post_answer_order_for_id))) {
465 if ($answer->getId() == -1) {
468 $post_answer_order_for_id[$answer->getId()] = $answer->getOrder();
474 if (
sizeof($post_answer_order_for_id) >= 1) {
475 $db_answer_order_for_id = [];
476 $db_answer_id_for_order = [];
477 foreach ($db_answers as $db_answer) {
478 $db_answer_order_for_id[intval($db_answer[
'answer_id'])] = intval($db_answer[
'aorder']);
479 $db_answer_id_for_order[intval($db_answer[
'aorder'])] = intval($db_answer[
'answer_id']);
485 $db_answer_ids = array_keys($db_answer_order_for_id);
486 $post_answer_ids = array_keys($post_answer_order_for_id);
487 $diff_db_post_answer_ids = array_diff($db_answer_ids, $post_answer_ids);
488 $unused_answer_ids = array_keys($diff_db_post_answer_ids);
491 $this->feedbackOBJ->deleteSpecificAnswerFeedbacks($this->
getId(),
false);
493 foreach ($db_feedback as $feedback_option) {
495 if (in_array(intval($feedback_option[
'answer']), $unused_answer_ids)) {
500 $feedback_order_db = intval($feedback_option[
'answer']);
501 $db_answer_id = $db_answer_id_for_order[$feedback_order_db];
505 if (is_null($db_answer_id) || $db_answer_id < 0) {
508 $feedback_order_post = $post_answer_order_for_id[$db_answer_id];
509 $feedback_option[
'answer'] = $feedback_order_post;
512 $next_id = $this->db->nextId(
'qpl_fb_specific');
513 $this->db->manipulateF(
514 "INSERT INTO qpl_fb_specific (feedback_id, question_fi, answer, tstamp, feedback, question) 515 VALUES (%s, %s, %s, %s, %s, %s)",
516 [
'integer',
'integer',
'integer',
'integer',
'text',
'integer'],
519 $feedback_option[
'question_fi'],
520 $feedback_option[
'answer'],
522 $feedback_option[
'feedback'],
523 $feedback_option[
'question']
531 $this->db->manipulateF(
532 "DELETE FROM qpl_a_mc WHERE question_fi = %s",
538 foreach ($this->answers as $key => $value) {
539 $answer_obj = $this->answers[$key];
540 $next_id = $this->db->nextId(
'qpl_a_mc');
541 $this->db->manipulateF(
542 "INSERT INTO qpl_a_mc (answer_id, question_fi, answertext, points, points_unchecked, aorder, imagefile, tstamp) 543 VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
544 [
'integer',
'integer',
'text',
'float',
'float',
'integer',
'text',
'integer'],
549 $answer_obj->getPoints(),
550 $answer_obj->getPointsUnchecked(),
551 $answer_obj->getOrder(),
552 $answer_obj->getImage(),
566 return "assMultipleChoice";
599 if (!empty($image_tempfilename)) {
600 $image_filename = str_replace(
" ",
"_", $image_filename);
602 if (!file_exists($imagepath)) {
609 if (!preg_match(
"/^image/", $mimetype)) {
610 unlink($imagepath . $image_filename);
615 $this->generateThumbForFile(
629 $text = parent::getRTETextWithMediaObjects();
630 foreach ($this->answers as $index => $answer) {
631 $text .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->
getId(), 0, $index);
632 $answer_obj = $this->answers[$index];
633 $text .= $answer_obj->getAnswertext();
668 $result[
'id'] = $this->
getId();
675 $result[
'feedback'] = [
676 'onenotcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
677 'allcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
682 foreach ($this->
getAnswers() as $key => $answer_obj) {
683 if ((
string) $answer_obj->getImage()) {
686 array_push($answers, [
688 "points_checked" => (
float) $answer_obj->getPointsChecked(),
689 "points_unchecked" => (float) $answer_obj->getPointsUnchecked(),
690 "order" => (
int) $answer_obj->getOrder(),
691 "image" => (string) $answer_obj->getImage(),
693 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
705 $result[
'mobs'] = $mobs;
707 return json_encode($result);
712 $answer = $this->answers[$index];
713 if (is_object($answer)) {
714 $this->deleteImage($answer->getImage());
715 $answer->setImage(
null);
721 return $this->current_user->getPref(
'tst_multiline_answers') ===
'1' ? 1 : 0;
726 $this->current_user->writePref(
'tst_multiline_answers', (
string) $setting);
754 if ($this->feedback_setting) {
763 return 'feedback_correct_sc_mc';
768 $solutionSubmit = [];
769 $post = $this->dic->http()->wrapper()->post();
772 if (
$post->has(
"multiple_choice_result_$index")) {
773 $value =
$post->retrieve(
"multiple_choice_result_$index", $this->dic->refinery()->kindlyTo()->string());
774 if (is_numeric($value)) {
775 $solutionSubmit[] = $value;
779 return $solutionSubmit;
783 ?array $found_values,
786 if ($found_values === []
787 && $active_id !== 0) {
791 $found_values ??= [];
793 foreach ($this->answers as $key => $answer) {
794 if (in_array($key, $found_values)) {
795 $points += $answer->getPoints();
798 $points += $answer->getPointsUnchecked();
806 return ilOperatorsExpressionMapping::getOperatorsByExpression($expression);
827 $data = $this->db->queryF(
828 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
829 [
"integer",
"integer",
"integer",
"integer"],
830 [$active_id, $pass, $this->
getId(), $maxStep]
833 $data = $this->db->queryF(
834 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
835 [
"integer",
"integer",
"integer"],
836 [$active_id, $pass, $this->
getId()]
840 while ($row = $this->db->fetchAssoc(
$data)) {
841 $result->addKeyValue($row[
"value1"], $row[
"value1"]);
847 $result->setReachedPercentage((
$points / $max_points) * 100);
860 if ($index !==
null) {
869 $config = parent::buildTestPresentationConfig();
870 $config->setUseUnchangedAnswerLabel($this->
lng->txt(
'tst_mc_label_none_above'));
882 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->
getQuestionType(),
885 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
888 AdditionalInformationGenerator::KEY_FEEDBACK => [
889 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
890 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
894 foreach ($this->
getAnswers() as $key => $answer_obj) {
895 $result[AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTIONS][$key + 1] = [
896 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION => $this->
formatSAQuestion($answer_obj->getAnswertext()),
897 AdditionalInformationGenerator::KEY_QUESTION_POINTS_CHECKED => (
float) $answer_obj->getPointsChecked(),
898 AdditionalInformationGenerator::KEY_QUESTION_POINTS_UNCHECKED => (float) $answer_obj->getPointsUnchecked(),
899 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_ORDER => (
int) $answer_obj->getOrder(),
900 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_IMAGE => (string) $answer_obj->getImage(),
902 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
912 array $solution_values
915 static fn(array $v):
string => $v[
'value1'],
918 $parsed_solutions = [];
921 if (in_array(
$id, $solution_ids)) {
924 $parsed_solutions[$answer->getAnswertext()] = $additional_info
927 return $parsed_solutions;
933 static fn(array $v):
string => $v[
'value1'],
939 $checked =
'unchecked';
940 if (in_array($v->
getId(), $solution_ids)) {
941 $checked =
'checked';
943 return "{$v->getAnswertext()} ({$this->lng->txt($checked)})";
953 .
"({$this->lng->txt('points')} " 954 .
"{$this->lng->txt('checked')}: {$v->getPointsChecked()}, " 955 .
"{$this->lng->txt('unchecked')}: {$v->getPointsUnchecked()})",
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
flushAnswers()
Deletes all answers.
setNrOfTries(int $a_nr_of_tries)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getInstance($identifier)
getRTETextWithMediaObjects()
const PercentageResultExpression
toJSON()
Returns a JSON representation of the question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
toLog(AdditionalInformationGenerator $additional_info)
const NumberOfResultExpression
const ExclusiveResultExpression
__construct(string $title="", string $comment="", string $author="", int $owner=-1, string $question="", private int $output_type=self::OUTPUT_ORDER)
assMultipleChoice constructor
solutionValuesToText(array $solution_values)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
getCorrectSolutionForTextOutput(int $active_id, int $pass)
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked, set/unset) and an image file.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getImagePathWeb()
Returns the web image path for web accessable images of a question.
setThumbSize(int $a_size)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
migrateToLmContent($content)
getQuestionType()
Returns the question type of the question.
getAdditionalContentEditingMode()
cloneQuestionTypeSpecificProperties(\assQuestion $target)
addAnswer(string $answertext='', float $points=0.0, float $points_unchecked=0.0, int $order=0, ?string $answerimage=null, int $answer_id=-1)
Adds a possible answer for a multiple choice question.
getUserQuestionResult(int $active_id, int $pass)
Get the user solution for a question by active_id and the test pass.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
setComment(string $comment="")
Class for multiple choice tests.
getSpecificFeedbackAllCorrectOptionLabel()
calculateReachedPointsForSolution(?array $found_values, int $active_id=0)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
getId()
Gets the answer id.
getMultilineAnswerSetting()
removeAnswerImage($index)
setSelectionLimit(?int $selection_limit)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
deleteAnswer($index=0)
Deletes an answer with a given index.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
setAnswers(array $answers)
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getAnswerCount()
Returns the number of answers.
setMultilineAnswerSetting($setting=0)
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
getSpecificFeedbackSetting()
Gets the current feedback settings in effect for the question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
getOperators(string $expression)
Get all available operations for a specific question.
loadFromDb(int $question_id)
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
getAnswer($index=0)
Returns an answer with a given index.
& getAnswers()
Returns a reference to the answers array.
saveQuestionDataToDb(?int $original_id=null)
buildTestPresentationConfig()
getExpressionTypes()
Get all available expression types for a specific question.
saveToDb(?int $original_id=null)
setSpecificFeedbackSetting(int $feedback_setting)
Sets the feedback settings in effect for the question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
getAnswertext()
Gets the answer text.
setImageFile($image_filename, $image_tempfilename="")
Sets the image file and uploads the image to the object's image directory.
getSolutionMaxPass(int $active_id)
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
setIsSingleline(bool $is_singleline)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(Container $dic, ilPlugin $plugin)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
setOriginalId(?int $original_id)
setTitle(string $title="")
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getAnswerTableName()
Returns the name of the answer table in the database.
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
getHtmlQuestionContentPurifier()
lookupMaxStep(int $active_id, int $pass)
setAuthor(string $author="")
setShuffle(?bool $shuffle=true)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
static getDraftInstance()
isForcedEmptySolution(array $solutionSubmit)
setQuestion(string $question="")
const EmptyAnswerExpression