19 declare(strict_types=1);
96 $local_dic = QuestionPoolDIC::dic();
97 $this->request = $local_dic[
'request_data_collector'];
122 if ($this->title !==
'' 125 && $this->image_filename
126 && $this->answers !== []
144 $this->db->manipulateF(
145 'DELETE FROM qpl_a_imagemap WHERE question_fi = %s',
151 foreach ($this->answers as $key => $value) {
152 $answer_obj = $this->answers[$key];
153 $answer_obj->setOrder($key);
154 $next_id = $this->db->nextId(
'qpl_a_imagemap');
155 $this->db->manipulateF(
156 'INSERT INTO qpl_a_imagemap (answer_id, question_fi, answertext, points, aorder, coords, area, points_unchecked) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
157 [
'integer',
'integer',
'text',
'float',
'integer',
'text',
'text',
'float' ],
158 [ $next_id, $this->
id, $answer_obj->getAnswertext(
159 ), $answer_obj->getPoints(), $answer_obj->getOrder(
160 ), $answer_obj->getCoords(), $answer_obj->getArea(
161 ), $answer_obj->getPointsUnchecked() ]
168 $this->db->manipulateF(
174 $this->db->manipulateF(
176 ) .
' (question_fi, image_file, is_multiple_choice) VALUES (%s, %s, %s)',
177 [
'integer',
'text',
'integer' ],
180 $this->image_filename,
181 (
int) $this->is_multiple_choice
194 int $source_question_id,
195 int $source_parent_id,
196 int $target_question_id,
197 int $target_parent_id
199 $image_source_path = $this->
getImagePath($source_question_id, $source_parent_id);
200 $image_target_path = $this->
getImagePath($target_question_id, $target_parent_id);
202 if (!file_exists($image_target_path)) {
208 $src = opendir($image_source_path);
209 while ($src_file = readdir($src)) {
210 if ($src_file ===
'.' || $src_file ===
'..') {
214 $image_source_path . DIRECTORY_SEPARATOR . $src_file,
215 $image_target_path . DIRECTORY_SEPARATOR . $src_file
222 $result = $this->db->queryF(
227 if ($result->numRows() == 1) {
228 $data = $this->db->fetchAssoc($result);
229 $this->
setId($question_id);
232 $this->
setComment((
string) $data[
'description']);
253 $result = $this->db->queryF(
254 'SELECT * FROM qpl_a_imagemap WHERE question_fi = %s ORDER BY aorder ASC',
258 if ($result->numRows() > 0) {
259 while ($data = $this->db->fetchAssoc($result)) {
260 $image_map_question =
new ASS_AnswerImagemap($data[
'answertext'] ??
'', $data[
'points'], $data[
'aorder']);
261 $image_map_question->setCoords($data[
'coords']);
262 $image_map_question->setArea($data[
'area']);
263 $image_map_question->setPointsUnchecked($data[
'points_unchecked']);
264 array_push($this->answers, $image_map_question);
268 parent::loadFromDb($question_id);
275 if (count($shapes) > 0) {
276 foreach ($shapes as $shape) {
277 $this->
addAnswer($shape->getAnswertext(), 0.0, count($this->answers), $shape->getCoords(), $shape->getArea());
292 string $image_tempfilename =
'' 294 if (!empty($image_filename)) {
295 $image_filename = str_replace(
' ',
'_', $image_filename);
298 if (!empty($image_tempfilename)) {
300 if (!file_exists($imagepath)) {
304 $this->tpl->setOnScreenMessage(
'failure',
'The image could not be uploaded!');
307 $this->log->write(
'gespeichert: ' . $imagepath . $image_filename);
314 for ($i = 0; $i < count($this->answers); $i++) { 315 $imagemap_contents .= '<area alt=\
'' . $this->answers[$i]->getAnswertext() .
'\' '; 316 $imagemap_contents .= 'shape=\
'' . $this->answers[$i]->getArea() .
'\' '; 317 $imagemap_contents .= 'coords=\
'' . $this->answers[$i]->getCoords() .
'\' '; 318 $imagemap_contents .= "href=\"{$href}&selimage=" . $this->answers[$i]->getOrder() . "\" /> "; 320 $imagemap_contents .= '</map>
'; 321 return $imagemap_contents; 324 public function addAnswer( 325 string $answertext = '', 330 float $points_unchecked = 0.0 332 if (array_key_exists($order, $this->answers)) { 334 $answer = new ASS_AnswerImagemap($answertext, $points, $order, 0, -1); 335 $answer->setCoords($coords); 336 $answer->setArea($area); 337 $answer->setPointsUnchecked($points_unchecked); 338 for ($i = count($this->answers) - 1; $i >= $order; $i--) { 339 $this->answers[$i + 1] = $this->answers[$i]; 340 $this->answers[$i + 1]->setOrder($i + 1); 342 $this->answers[$order] = $answer; 345 $answer = new ASS_AnswerImagemap($answertext, $points, count($this->answers), 0, -1); 346 $answer->setCoords($coords); 347 $answer->setArea($area); 348 $answer->setPointsUnchecked($points_unchecked); 349 array_push($this->answers, $answer); 353 public function getAnswerCount(): int 355 return count($this->answers); 358 public function getAnswer(int $index = 0): ?ASS_AnswerImagemap 363 if (count($this->answers) < 1) { 366 if ($index >= count($this->answers)) { 369 return $this->answers[$index]; 372 public function &getAnswers(): array 374 return $this->answers; 377 public function deleteArea(int $index = 0): void 382 if (count($this->answers) < 1) { 385 if ($index >= count($this->answers)) { 388 unset($this->answers[$index]); 389 $this->answers = array_values($this->answers); 390 for ($i = 0; $i < count($this->answers); $i++) { 391 if ($this->answers[$i]->getOrder() > $index) { 392 $this->answers[$i]->setOrder($i); 397 public function flushAnswers(): void 402 public function getMaximumPoints(): float 405 foreach ($this->answers as $key => $value) { 406 if ($this->is_multiple_choice) { 407 if ($value->getPoints() > $value->getPointsUnchecked()) { 408 $points += $value->getPoints(); 410 $points += $value->getPointsUnchecked(); 413 if ($value->getPoints() > $points) { 414 $points = $value->getPoints(); 421 public function calculateReachedPoints( 424 bool $authorized_solution = true 426 if ($pass === null) { 427 $pass = $this->getSolutionMaxPass($active_id); 429 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized_solution); 431 while ($data = $this->db->fetchAssoc($result)) { 432 if ($data['value1
'] !== '') { 433 $found_values[] = $data['value1
']; 437 $points = $this->calculateReachedPointsForSolution($found_values); 442 public function calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $preview_session): float 444 $solution_data = $preview_session->getParticipantsSolution(); 446 $reached_points = $this->calculateReachedPointsForSolution(is_array($solution_data) ? array_values($solution_data) : []); 448 return $this->ensureNonNegativePoints($reached_points); 459 public function saveWorkingData( 462 bool $authorized = true 464 if (is_null($pass)) { 465 $pass = ilObjTest::_getPass($active_id); 468 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation( 469 function () use ($active_id, $pass, $authorized) { 471 // remove the dummy record of the intermediate solution 472 $this->deleteDummySolutionRecord($active_id, $pass); 474 // delete the authorized solution and make the intermediate solution authorized (keeping timestamps) 475 $this->removeCurrentSolution($active_id, $pass, true); 476 $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true); 480 $this->forceExistingIntermediateSolution( 483 $this->is_multiple_choice 486 if ($this->isReuseSolutionSelectionRequest()) { 487 $selection = $this->getReuseSolutionSelectionParameter(); 489 foreach ($selection as $selectedIndex) { 490 $this->saveCurrentSolution($active_id, $pass, (int) $selectedIndex, null, $authorized); 495 if ($this->isRemoveSolutionSelectionRequest()) { 496 $selection = $this->getRemoveSolutionSelectionParameter(); 497 $this->deleteSolutionRecordByValues($active_id, $pass, $authorized, [ 498 'value1
' => (int) $selection 503 if (!$this->isAddSolutionSelectionRequest()) { 506 $selection = $this->getAddSolutionSelectionParameter(); 508 if ($this->is_multiple_choice) { 509 $this->deleteSolutionRecordByValues($active_id, $pass, $authorized, [ 510 'value1
' => (int) $this->request->raw('selImage
') 513 $this->removeCurrentSolution($active_id, $pass, $authorized); 516 $this->saveCurrentSolution($active_id, $pass, $this->request->raw('selImage
'), null, $authorized); 523 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void 525 $solution = $previewSession->getParticipantsSolution(); 527 if ($this->is_multiple_choice 528 && $this->request->isset('remImage
')) { 529 unset($solution[$this->request->int('remImage
')]); 532 if ($this->request->isset('selImage
')) { 533 if (!$this->is_multiple_choice) { 537 $solution[$this->request->int('selImage
')] = $this->request->int('selImage
'); 540 $previewSession->setParticipantsSolution($solution); 551 public function getQuestionType(): string 564 public function getAdditionalTableName(): string 566 return 'qpl_qst_imagemap
'; 577 public function getAnswerTableName(): string 579 return 'qpl_a_imagemap
'; 586 public function getRTETextWithMediaObjects(): string 588 $text = parent::getRTETextWithMediaObjects(); 589 foreach ($this->answers as $index => $answer) { 590 $text .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->getId(), 0, $index); 598 public function deleteImage(): void 600 $file = $this->getImagePath() . $this->getImageFilename(); 602 $this->flushAnswers(); 603 $this->image_filename = ''; 609 public function toJSON(): string 612 $result['id'] = $this->getId(); 613 $result['type
'] = (string) $this->getQuestionType(); 614 $result['title
'] = $this->getTitleForHTMLOutput(); 615 $result['question
'] = $this->formatSAQuestion($this->getQuestion()); 616 $result['nr_of_tries
'] = $this->getNrOfTries(); 617 $result['shuffle
'] = $this->getShuffle(); 618 $result['is_multiple
'] = $this->getIsMultipleChoice(); 619 $result['feedback
'] = [ 620 'onenotcorrect
' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)), 621 'allcorrect
' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true)) 623 $result['image
'] = $this->getImagePathWeb() . $this->getImageFilename(); 627 foreach ($this->getAnswers() as $key => $answer_obj) { 628 array_push($answers, [ 629 'answertext
' => (string) $answer_obj->getAnswertext(), 630 'points
' => (float) $answer_obj->getPoints(), 631 'points_unchecked
' => (float) $answer_obj->getPointsUnchecked(), 633 'coords
' => $answer_obj->getCoords(), 634 'state
' => $answer_obj->getState(), 635 'area
' => $answer_obj->getArea(), 636 'feedback
' => $this->formatSAQuestion( 637 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key) 642 $result['answers
'] = $answers; 644 $mobs = ilObjMediaObject::_getMobsOfObject('qpl:
html', $this->getId()); 645 $result['mobs
'] = $mobs; 647 return json_encode($result); 650 protected function calculateReachedPointsForSolution(?array $found_values): float 652 if ($found_values === null) { 656 if (count($found_values) > 0) { 657 foreach ($this->answers as $key => $answer) { 658 if (in_array($key, $found_values)) { 659 $points += $answer->getPoints(); 660 } elseif ($this->getIsMultipleChoice()) { 661 $points += $answer->getPointsUnchecked(); 669 public function getOperators(string $expression): array 671 return ilOperatorsExpressionMapping::getOperatorsByExpression($expression); 674 public function getExpressionTypes(): array 677 iQuestionCondition::PercentageResultExpression, 678 iQuestionCondition::NumberOfResultExpression, 679 iQuestionCondition::EmptyAnswerExpression, 680 iQuestionCondition::ExclusiveResultExpression 684 public function getUserQuestionResult( 687 ): ilUserQuestionResult { 688 $result = new ilUserQuestionResult($this, $active_id, $pass); 690 $maxStep = $this->lookupMaxStep($active_id, $pass); 692 $data = $this->db->queryF( 693 'SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s
', 694 ['integer
', 'integer
', 'integer
', 'integer
'], 695 [$active_id, $pass, $this->getId(), $maxStep] 698 $data = $this->db->queryF( 699 'SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step IS
NULL', 700 ['integer
', 'integer
', 'integer
'], 701 [$active_id, $pass, $this->getId()] 705 while ($row = $this->db->fetchAssoc($data)) { 706 $result->addKeyValue($row['value1
'], $row['value1
']); 709 $points = $this->calculateReachedPoints($active_id, $pass); 710 $max_points = $this->getMaximumPoints(); 712 $result->setReachedPercentage(($points / $max_points) * 100); 724 public function getAvailableAnswerOptions($index = null) 726 if ($index !== null) { 727 return $this->getAnswer($index); 729 return $this->getAnswers(); 733 public function getTestOutputSolutions($activeId, $pass): array 735 $solution = parent::getTestOutputSolutions($activeId, $pass); 737 $this->currentSolution = []; 738 foreach ($solution as $record) { 739 $this->currentSolution[] = $record['value1
']; 744 protected function getAddSolutionSelectionParameter() 746 if (!$this->isAddSolutionSelectionRequest()) { 750 return $this->request->raw('selImage
'); 752 protected function isAddSolutionSelectionRequest(): bool 754 if (!$this->request->isset('selImage
')) { 758 if (!strlen($this->request->raw('selImage
'))) { 764 protected function getRemoveSolutionSelectionParameter() 766 if (!$this->isRemoveSolutionSelectionRequest()) { 770 return $this->request->raw('remImage
'); 772 protected function isRemoveSolutionSelectionRequest(): bool 774 if (!$this->is_multiple_choice) { 778 if (!$this->request->isset('remImage
')) { 782 if (!strlen($this->request->raw('remImage
'))) { 788 protected function getReuseSolutionSelectionParameter(): ?array 790 if (!$this->isReuseSolutionSelectionRequest()) { 794 return assQuestion::explodeKeyValues($this->request->raw('reuseSelection
')); 796 protected function isReuseSolutionSelectionRequest(): bool 798 if (!$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed()) { 802 if (!$this->request->isset('reuseSelection
')) { 806 if (!strlen($this->request->raw('reuseSelection
'))) { 810 if (!preg_match('/\d(,\d)*/
', $this->request->raw('reuseSelection
'))) { 817 public function toLog(AdditionalInformationGenerator $additional_info): array 820 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(), 821 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(), 822 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()), 823 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info 824 ->getTrueFalseTagForBool($this->getShuffle()), 825 AdditionalInformationGenerator::KEY_QUESTION_IMAGEMAP_MODE => $this->getIsMultipleChoice() 826 ? $additional_info->getTagForLangVar('tst_imap_qst_mode_mc
') 827 : $additional_info->getTagForLangVar('tst_imap_qst_mode_sc
'), 828 AdditionalInformationGenerator::KEY_QUESTION_IMAGEMAP_IMAGE => $this->getImagePathWeb() . $this->getImageFilename(), 829 AdditionalInformationGenerator::KEY_FEEDBACK => [ 830 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion( 831 $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false) 833 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion( 834 $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true) 841 foreach ($this->getAnswers() as $key => $answer_obj) { 842 array_push($answers, [ 843 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION => (string) $answer_obj->getAnswertext(), 844 AdditionalInformationGenerator::KEY_QUESTION_POINTS_CHECKED => (float) $answer_obj->getPoints(), 845 AdditionalInformationGenerator::KEY_QUESTION_POINTS_UNCHECKED => (float) $answer_obj->getPointsUnchecked(), 846 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_ORDER => $order, 847 AdditionalInformationGenerator::KEY_QUESTION_IMAGEMAP_ANSWER_OPTION_COORDS => $answer_obj->getCoords(), 848 AdditionalInformationGenerator::KEY_FEEDBACK => $this->formatSAQuestion( 849 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key) 854 $result[AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTIONS] = $answers; 859 protected function solutionValuesToLog( 860 AdditionalInformationGenerator $additional_info, 861 array $solution_values 863 $parsed_solution = []; 864 foreach ($this->getAnswers() as $id => $answer) { 865 $value = $additional_info->getTagForLangVar('unchecked'); 866 foreach ($solution_values as $solution) { 867 if ($solution['value1
'] == $id) { 868 $value = $additional_info->getTagForLangVar('checked'); 872 $parsed_solution["{$answer->getArea()}: {$answer->getCoords()}"] = $value; 874 return $parsed_solution; 877 public function solutionValuesToText(array $solution_values): array 879 $parsed_solution = []; 880 foreach ($this->getAnswers() as $id => $answer) { 882 foreach ($solution_values as $solution) { 883 if ($solution['value1
'] == $id) { 884 $value = $this->lng->txt('checked'); 888 $parsed_solution[] = "{$answer->getArea()}: {$answer->getCoords()} ({$value})"; 890 return $parsed_solution; 893 public function getCorrectSolutionForTextOutput(int $active_id, int $pass): array 896 fn(ASS_AnswerImagemap $v): string => "{$v->getArea()}: {$v->getCoords()}" 897 . "({$this->lng->txt('points
')} " 898 . "{$this->lng->txt('checked')}: {$v->getPoints()}, " 899 . "{$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...
setNrOfTries(int $a_nr_of_tries)
static getInstance($identifier)
get_imagemap_contents(string $href='#')
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
loadFromDb(int $question_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
removeAllImageFiles(string $image_target_path)
addAnswer(string $answertext='', float $points=0.0, int $order=0, string $coords='', string $area='', float $points_unchecked=0.0)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
unchecked()
description: > Example for rendering a Unchecked Glyph.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
setComment(string $comment="")
getIsMultipleChoice()
Returns true, if the imagemap question is a multiplechoice question.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
checked()
description: > Example for rendering a Checked Glyph.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
uploadImagemap(array $shapes)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const MODE_MULTIPLE_CHOICE
Class for image map questions.
__construct( $title='', $comment='', $author='', $owner=-1, $question='', $image_filename='')
assImagemapQuestion constructor
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
saveQuestionDataToDb(?int $original_id=null)
RequestDataCollector $request
saveToDb(?int $original_id=null)
cloneQuestionTypeSpecificProperties(\assQuestion $target)
setIsMultipleChoice($is_multiple_choice)
Set true if the Imagemapquestion is a multiplechoice Question.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
setImageFilename(string $image_filename, string $image_tempfilename='')
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
setTitle(string $title="")
setLifecycle(ilAssQuestionLifecycle $lifecycle)
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
setAuthor(string $author="")
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
copyImagemapFiles(int $source_question_id, int $source_parent_id, int $target_question_id, int $target_parent_id)
static getDraftInstance()
setQuestion(string $question="")