19 require_once
'./Modules/Test/classes/inc.AssessmentConstants.php';
95 $this->feedback_setting = 2;
106 if ($this->title !==
'' 107 && $this->author !== null && $this->author !==
'' 108 && $this->question !== null && $this->question !==
'' 109 && $this->answers !== []
111 foreach ($this->answers as $answer) {
112 if ($answer->getAnswertext() ===
'' && !$answer->hasImage()) {
132 $ilDB = $DIC[
'ilDB'];
143 $result =
$ilDB->queryF(
148 if ($result->numRows() == 1) {
150 $oldthumbsize =
$data[
'thumb_size'];
172 $ilDB = $DIC[
'ilDB'];
174 $result =
$ilDB->queryF(
179 if ($result->numRows() == 1) {
181 $this->
setId($question_id);
197 $this->lastChange =
$data[
'tstamp'];
198 $this->feedback_setting =
$data[
'feedback_setting'];
212 $result =
$ilDB->queryF(
213 "SELECT * FROM qpl_a_sc WHERE question_fi = %s ORDER BY aorder ASC",
218 if ($result->numRows() > 0) {
221 if (!file_exists($imagefilename)) {
222 $data[
"imagefile"] = null;
231 $data[
"imagefile"] ?
$data[
"imagefile"] : null,
234 $this->answers[] = $image;
238 parent::loadFromDb($question_id);
248 if ($this->
id <= 0) {
253 $this_id = $this->
getId();
257 $original_id = $this->questioninfo->getOriginalId($this->
id);
260 if ((
int) $testObjId > 0) {
261 $clone->setObjId($testObjId);
281 $clone->copyPageOfQuestion($this_id);
284 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
286 $clone->duplicateImages($this_id, $thisObjId);
288 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
300 if ($this->
getId() <= 0) {
301 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
305 $original_id = $this->questioninfo->getOriginalId($this->
id);
307 $source_questionpool_id = $this->
getObjId();
308 $clone->setObjId($target_questionpool_id);
318 $clone->copyImages(
$original_id, $source_questionpool_id);
320 $clone->onCopy($source_questionpool_id,
$original_id, $clone->getObjId(), $clone->getId());
327 if ($this->
getId() <= 0) {
328 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
332 $sourceParentId = $this->
getObjId();
338 $clone->setObjId($targetParentId);
340 if ($targetQuestionTitle) {
341 $clone->setTitle($targetQuestionTitle);
346 $clone->copyPageOfQuestion($sourceQuestionId);
348 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
350 $clone->copyImages($sourceQuestionId, $sourceParentId);
352 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
378 if (array_key_exists($order, $this->answers)) {
382 for ($i = 0; $i < $order; $i++) {
383 $newchoices[] = $this->answers[$i];
385 $newchoices[] = $answer;
386 for ($i = $order, $iMax = count($this->answers); $i < $iMax; $i++) {
387 $changed = $this->answers[$i];
388 $changed->setOrder($i + 1);
389 $newchoices[] = $changed;
391 $this->answers = $newchoices;
396 count($this->answers),
401 $this->answers[] = $answer;
414 return count($this->answers);
431 if (count($this->answers) < 1) {
434 if ($index >= count($this->answers)) {
438 return $this->answers[$index];
454 if (count($this->answers) < 1) {
457 if ($index >= count($this->answers)) {
460 $answer = $this->answers[$index];
461 if ($answer->hasImage()) {
464 unset($this->answers[$index]);
465 $this->answers = array_values($this->answers);
466 for ($i = 0, $iMax = count($this->answers); $i < $iMax; $i++) {
467 if ($this->answers[$i]->
getOrder() > $index) {
468 $this->answers[$i]->setOrder($i);
493 foreach ($this->answers as
$key => $value) {
494 if ($value->getPoints() >
$points) {
511 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution =
true, $returndetails =
false): float
513 if ($returndetails) {
514 throw new ilTestException(
'return details not implemented for ' . __METHOD__);
518 $ilDB = $DIC[
'ilDB'];
521 if (is_null($pass)) {
526 if (strcmp(
$data[
"value1"],
"") != 0) {
527 array_push($found_values,
$data[
"value1"]);
531 foreach ($this->answers as
$key => $answer) {
532 if (count($found_values) > 0) {
533 if (in_array(
$key, $found_values)) {
534 $points += $answer->getPoints();
548 foreach ($this->answers as
$key => $answer) {
549 if (is_numeric($participantSolution) &&
$key == $participantSolution) {
550 $points = $answer->getPoints();
569 $ilDB = $DIC[
'ilDB'];
570 $ilUser = $DIC[
'ilUser'];
572 if (is_null($pass)) {
578 $this->
getProcessLocker()->executeUserSolutionUpdateLockOperation(
function () use (&$entered_values,
$ilDB, $active_id, $pass, $authorized) {
582 if (
$ilDB->numRows($result)) {
583 $row =
$ilDB->fetchAssoc($result);
584 $update = $row[
"solution_id"];
587 $multiple_choice_result = $this->
http->wrapper()->post()->has(
'multiple_choice_result') ?
588 $this->
http->wrapper()->post()->retrieve(
'multiple_choice_result', $this->
refinery->kindlyTo()->string()) :
592 if ($multiple_choice_result !==
'') {
599 if ($multiple_choice_result !==
'') {
606 if ($entered_values) {
610 "log_user_entered_values",
612 ), $active_id, $this->
getId());
618 "log_user_not_entered_values",
620 ), $active_id, $this->
getId());
629 $mc_result_key =
'multiple_choice_result' . $this->
getId() .
'ID';
631 $this->
http->wrapper()->post()->has($mc_result_key) &&
632 ($mc_result = $this->
http->wrapper()->post()->retrieve($mc_result_key, $this->
refinery->kindlyTo()->string())) !==
'' 644 $ilDB = $DIC[
'ilDB'];
655 ) .
" (question_fi, shuffle, allow_images, thumb_size) VALUES (%s, %s, %s, %s)",
656 [
"integer",
"text",
"text",
"integer" ],
675 $ilDB = $DIC[
'ilDB'];
681 $result =
$ilDB->queryF(
682 "SELECT * FROM qpl_fb_specific WHERE question_fi = %s",
686 $db_feedback =
$ilDB->fetchAll($result);
691 $result =
$ilDB->queryF(
692 "SELECT answer_id, aorder FROM qpl_a_sc WHERE question_fi = %s",
696 $db_answers =
$ilDB->fetchAll($result);
699 $post_answer_order_for_id = [];
700 foreach ($this->answers as $answer) {
702 if ($answer->getId() !== null && !in_array($answer->getId(), array_keys($post_answer_order_for_id))) {
704 if ($answer->getId() == -1) {
707 $post_answer_order_for_id[$answer->getId()] = $answer->getOrder();
713 if (
sizeof($post_answer_order_for_id) >= 1) {
714 $db_answer_order_for_id = [];
715 $db_answer_id_for_order = [];
716 foreach ($db_answers as $db_answer) {
717 $db_answer_order_for_id[intval($db_answer[
'answer_id'])] = intval($db_answer[
'aorder']);
718 $db_answer_id_for_order[intval($db_answer[
'aorder'])] = intval($db_answer[
'answer_id']);
724 $db_answer_ids = array_keys($db_answer_order_for_id);
725 $post_answer_ids = array_keys($post_answer_order_for_id);
726 $diff_db_post_answer_ids = array_diff($db_answer_ids, $post_answer_ids);
727 $unused_answer_ids = array_keys($diff_db_post_answer_ids);
730 $this->feedbackOBJ->deleteSpecificAnswerFeedbacks($this->
getId(),
false);
732 foreach ($db_feedback as $feedback_option) {
734 if (in_array(intval($feedback_option[
'answer']), $unused_answer_ids)) {
739 $feedback_order_db = intval($feedback_option[
'answer']);
740 $db_answer_id = $db_answer_id_for_order[$feedback_order_db] ?? null;
744 if (is_null($db_answer_id) || $db_answer_id < 0) {
747 $feedback_order_post = $post_answer_order_for_id[$db_answer_id];
748 $feedback_option[
'answer'] = $feedback_order_post;
751 $next_id =
$ilDB->nextId(
'qpl_fb_specific');
753 "INSERT INTO qpl_fb_specific (feedback_id, question_fi, answer, tstamp, feedback, question) 754 VALUES (%s, %s, %s, %s, %s, %s)",
755 [
'integer',
'integer',
'integer',
'integer',
'text',
'integer'],
758 $feedback_option[
'question_fi'],
759 $feedback_option[
'answer'],
761 $feedback_option[
'feedback'],
762 $feedback_option[
'question']
771 "DELETE FROM qpl_a_sc WHERE question_fi = %s",
777 foreach ($this->answers as
$key => $value) {
779 $answer_obj = $this->answers[
$key];
780 $next_id =
$ilDB->nextId(
'qpl_a_sc');
782 "INSERT INTO qpl_a_sc (answer_id, question_fi, answertext, points, aorder, imagefile, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s)",
783 [
'integer',
'integer',
'text',
'float',
'integer',
'text',
'integer'],
788 $answer_obj->getPoints(),
789 $answer_obj->getOrder(),
790 $answer_obj->getImage(),
805 return "assSingleChoice";
840 if (empty($image_tempfilename)) {
844 $cleaned_image_filename = str_replace(
" ",
"_", $image_filename);
846 if (!file_exists($imagepath)) {
855 if (!preg_match(
"/^image/", $mimetype)) {
856 unlink($imagepath . $cleaned_image_filename);
861 $this->generateThumbForFile(
862 $cleaned_image_filename,
880 @unlink($imagepath . $image_filename);
881 $thumbpath = $imagepath . $this->getThumbPrefix() . $image_filename;
890 $imagepath_original = str_replace(
"/$this->id/images",
"/$question_id/images", $imagepath);
892 if ((
int) $objectId > 0) {
893 $imagepath_original = str_replace(
"/$this->obj_id/",
"/$objectId/", $imagepath_original);
896 foreach ($this->answers as $answer) {
897 if ($answer->hasImage()) {
899 if (!file_exists($imagepath)) {
902 if (!@copy($imagepath_original .
$filename, $imagepath . $filename)) {
903 $ilLog->write(
"image could not be duplicated!!!!",
$ilLog->ERROR);
904 $ilLog->write(
"object: " . print_r($this,
true),
$ilLog->ERROR);
906 if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
907 if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
908 $ilLog->write(
"image thumbnail could not be duplicated!!!!",
$ilLog->ERROR);
909 $ilLog->write(
"object: " . print_r($this,
true),
$ilLog->ERROR);
916 public function copyImages($question_id, $source_questionpool):
void 923 $imagepath_original = str_replace(
"/$this->id/images",
"/$question_id/images", $imagepath);
924 $imagepath_original = str_replace(
"/$this->obj_id/",
"/$source_questionpool/", $imagepath_original);
925 foreach ($this->answers as $answer) {
926 if ($answer->hasImage()) {
928 if (!file_exists($imagepath)) {
932 if (file_exists($imagepath_original .
$filename)) {
933 if (!copy($imagepath_original . $filename, $imagepath . $filename)) {
935 "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
936 $imagepath_original . $filename,
937 $imagepath . $filename,
940 $source_questionpool,
946 if (file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
947 if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
949 "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
950 $imagepath_original . $this->getThumbPrefix() . $filename,
951 $imagepath . $this->getThumbPrefix() . $filename,
954 $source_questionpool,
970 $question_id = $this->questioninfo->getOriginalId();
972 $imagepath_original = str_replace(
"/$this->id/images",
"/$question_id/images", $imagepath);
974 foreach ($this->answers as $answer) {
975 if ($answer->hasImage()) {
977 if (@file_exists($imagepath .
$filename)) {
978 if (!file_exists($imagepath)) {
981 if (!file_exists($imagepath_original)) {
984 if (!@copy($imagepath . $filename, $imagepath_original . $filename)) {
985 $ilLog->write(
"image could not be duplicated!!!!",
$ilLog->ERROR);
986 $ilLog->write(
"object: " . print_r($this,
true),
$ilLog->ERROR);
989 if (@file_exists($imagepath . $this->getThumbPrefix() . $filename)) {
990 if (!@copy($imagepath . $this->getThumbPrefix() . $filename, $imagepath_original . $this->getThumbPrefix() . $filename)) {
991 $ilLog->write(
"image thumbnail could not be duplicated!!!!",
$ilLog->ERROR);
992 $ilLog->write(
"object: " . print_r($this,
true),
$ilLog->ERROR);
1005 $text = parent::getRTETextWithMediaObjects();
1006 foreach ($this->answers as $index => $answer) {
1007 $text .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->
getId(), 0, $index);
1008 $answer_obj = $this->answers[$index];
1009 $text .= $answer_obj->getAnswertext();
1032 parent::setExportDetailsXLSX($worksheet, $startrow, $col, $active_id, $pass);
1037 $worksheet->
setCell($startrow + $i, $col, $answer->getAnswertext());
1040 count($solution) > 0 &&
1041 isset($solution[0]) &&
1042 is_array($solution[0]) &&
1043 strlen($solution[0][
'value1']) > 0 &&
$id == $solution[0][
'value1']
1045 $worksheet->
setCell($startrow + $i, $col + 2, 1);
1047 $worksheet->
setCell($startrow + $i, $col + 2, 0);
1052 return $startrow + $i + 1;
1072 $result[
'id'] = $this->
getId();
1079 $result[
'feedback'] = [
1080 'onenotcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
1081 'allcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
1087 if ((
string) $answer_obj->getImage()) {
1092 'html_id' => $this->
getId() .
'_' .
$key,
1093 "points" => (float) $answer_obj->getPoints(),
1094 "order" => (
int) $answer_obj->getOrder(),
1095 "image" => (string) $answer_obj->getImage(),
1097 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0,
$key)
1108 $result[
'mobs'] = $mobs;
1110 return json_encode($result);
1115 $answer = $this->answers[$index];
1116 if (is_object($answer)) {
1118 $answer->setImage(null);
1125 $ilUser = $DIC[
'ilUser'];
1127 $multilineAnswerSetting = $ilUser->getPref(
"tst_multiline_answers");
1128 if ($multilineAnswerSetting != 1) {
1129 $multilineAnswerSetting = 0;
1131 return $multilineAnswerSetting;
1137 $ilUser = $DIC[
'ilUser'];
1138 $ilUser->writePref(
"tst_multiline_answers", (
string) $a_setting);
1152 $this->feedback_setting = $a_feedback_setting;
1166 if ($this->feedback_setting) {
1175 return 'feedback_correct_sc_mc';
1231 $ilDB = $DIC[
'ilDB'];
1238 "SELECT * FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1239 [
"integer",
"integer",
"integer",
"integer"],
1240 [$active_id, $pass, $this->
getId(), $maxStep]
1244 "SELECT * FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1245 [
"integer",
"integer",
"integer"],
1246 [$active_id, $pass, $this->
getId()]
1254 $result->addKeyValue($row[
"value1"], $row[
"value1"]);
1260 $result->setReachedPercentage((
$points / $max_points) * 100);
1273 if ($index !== null) {
1285 parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
1287 $origImagePath = $this->questionFilesService->buildImagePath($origQuestionId, $origParentObjId);
1288 $dupImagePath = $this->questionFilesService->buildImagePath($dupQuestionId, $dupParentObjId);
1291 if (is_dir($dupImagePath)) {
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...
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
setNrOfTries(int $a_nr_of_tries)
getSpecificFeedbackAllCorrectOptionLabel()
static getInstance($identifier)
const PercentageResultExpression
removeAnswerImage($index)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const NumberOfResultExpression
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
Abstract basic class which is to be extended by the concrete assessment question type classes...
setAnswers(array $answers)
Class for answers with a binary state indicator.
setMultilineAnswerSetting($a_setting=0)
& getAnswers()
Returns a reference to the answers array.
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
{}
getAnswerCount()
Returns the number of answers.
getColumnCoord(int $a_col)
Get column "name" from number.
ensureNonNegativePoints($points)
bool $shuffle
Indicates whether the answers will be shuffled or not.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
isComplete()
Returns true, if a single choice question is complete for use.
setExportDetailsXLSX(ilAssExcelFormatHelper $worksheet, int $startrow, int $col, int $active_id, int $pass)
{}
copyObject($target_questionpool_id, $title="")
Copies an assSingleChoice object.
getQuestionType()
Returns the question type of the question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getAnswerTableName()
Returns the name of the answer table in the database.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
setThumbSize(int $a_size)
static rCopy(string $a_sdir, string $a_tdir, bool $preserveTimeAttributes=false)
Copies content of a directory $a_sdir recursively to a directory $a_tdir.
migrateToLmContent($content)
getAdditionalContentEditingMode()
loadFromDb($question_id)
Loads a assSingleChoice object from a database.
getParticipantsSolution()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
setSpecificFeedbackSetting($a_feedback_setting)
Sets the feedback settings in effect for the question.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
getAnswer($index=0)
Returns an answer with a given index.
setComment(string $comment="")
getOperators($expression)
Get all available operations for a specific question.
setIsSingleline(bool $isSingleline)
float $points
The maximum available points for the question.
Base Exception for all Exceptions relating to Modules/Test.
getMultilineAnswerSetting()
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
setFeedbackSetting(int $feedback_setting)
getSpecificFeedbackSetting()
Gets the current feedback settings in effect for the question.
updateCurrentSolution(int $solutionId, $value1, $value2, bool $authorized=true)
setParticipantsSolution($participantSolution)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
deleteImage($image_filename)
Deletes an image file.
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
setBold(string $a_coords)
Set cell(s) to bold.
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
static _enabledAssessmentLogging()
static http()
Fetches the global http state from ILIAS.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static logAction(string $logtext, int $active_id, int $question_id)
removeSolutionRecordById(int $solutionId)
Class for single choice questions.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
deleteAnswer($index=0)
Deletes an answer with a given index.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
toJSON()
Returns a JSON representation of the question.
__construct( $title="", $comment="", $author="", $owner=-1, $question="", $output_type=OUTPUT_ORDER)
assSingleChoice constructor
syncImages()
Sync images of a MC question on synchronisation with the original question.
flushAnswers()
Deletes all answers.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
string $question
The question text.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
static getOperatorsByExpression($expression)
duplicate(bool $for_test=true, string $title="", string $author="", int $owner=-1, $testObjId=null)
Duplicates an assSingleChoiceQuestion.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
saveQuestionDataToDb(int $original_id=-1)
getSolutionMaxPass(int $active_id)
setImageFile($image_filename, $image_tempfilename="")
Sets the image file and uploads the image to the object's image directory.
duplicateImages($question_id, $objectId=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setOriginalId(?int $original_id)
setTitle(string $title="")
getExpressionTypes()
Get all available expression types for a specific question.
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
addAnswer( $answertext="", $points=0.0, $order=0, $answerimage=null, $answer_id=-1)
Adds a possible answer for a single choice question.
ILIAS DI LoggingServices $ilLog
getHtmlQuestionContentPurifier()
lookupMaxStep(int $active_id, int $pass)
setAuthor(string $author="")
setShuffle(?bool $shuffle=true)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
static getDraftInstance()
static isObligationPossible(int $questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
savePreviewData(ilAssQuestionPreviewSession $previewSession)
setQuestion(string $question="")
const EmptyAnswerExpression