ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
class.assKprimChoice.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Modules/TestQuestionPool/classes/class.assQuestion.php';
5require_once 'Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
6require_once 'Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
7require_once 'Modules/TestQuestionPool/interfaces/interface.ilAssSpecificFeedbackOptionLabelProvider.php';
8
16{
18
20
21 const ANSWER_TYPE_SINGLE_LINE = 'singleLine';
22 const ANSWER_TYPE_MULTI_LINE = 'multiLine';
23
24 const OPTION_LABEL_RIGHT_WRONG = 'right_wrong';
25 const OPTION_LABEL_PLUS_MINUS = 'plus_minus';
26 const OPTION_LABEL_APPLICABLE_OR_NOT = 'applicable_or_not';
27 const OPTION_LABEL_ADEQUATE_OR_NOT = 'adequate_or_not';
28 const OPTION_LABEL_CUSTOM = 'customlabel';
29
30 const DEFAULT_THUMB_SIZE = 150;
31 const THUMB_PREFIX = 'thumb.';
32
34
35 private $answerType;
36
37 private $thumbSize;
38
40
41 private $optionLabel;
42
44
46
48
49 private $answers;
50
51 public function __construct($title = '', $comment = '', $author = '', $owner = -1, $question = '')
52 {
53 parent::__construct($title, $comment, $author, $owner, $question);
54
55 $this->shuffleAnswersEnabled = true;
56 $this->answerType = self::ANSWER_TYPE_SINGLE_LINE;
57 $this->thumbSize = self::DEFAULT_THUMB_SIZE;
58 $this->scorePartialSolutionEnabled = true;
59 $this->optionLabel = self::OPTION_LABEL_RIGHT_WRONG;
60 $this->customTrueOptionLabel = '';
61 $this->customFalseOptionLabel = '';
62
63 require_once 'Modules/TestQuestionPool/classes/feedback/class.ilAssConfigurableMultiOptionQuestionFeedback.php';
65
66 $this->answers = array();
67
68 $this->setPoints('');
69 }
70
71 public function getQuestionType()
72 {
73 return 'assKprimChoice';
74 }
75
76 public function getAdditionalTableName()
77 {
78 return "qpl_qst_kprim";
79 }
80
81 public function getAnswerTableName()
82 {
83 return "qpl_a_kprim";
84 }
85
87 {
88 $this->shuffleAnswersEnabled = $shuffleAnswersEnabled;
89 }
90
91 public function isShuffleAnswersEnabled()
92 {
94 }
95
96 public function setAnswerType($answerType)
97 {
98 $this->answerType = $answerType;
99 }
100
101 public function getAnswerType()
102 {
103 return $this->answerType;
104 }
105
106 public function setThumbSize($thumbSize)
107 {
108 $this->thumbSize = $thumbSize;
109 }
110
111 public function getThumbSize()
112 {
113 return $this->thumbSize;
114 }
115
117 {
118 $this->scorePartialSolutionEnabled = $scorePartialSolutionEnabled;
119 }
120
122 {
124 }
125
127 {
128 $this->optionLabel = $optionLabel;
129 }
130
131 public function getOptionLabel()
132 {
133 return $this->optionLabel;
134 }
135
137 {
138 $this->customTrueOptionLabel = $customTrueOptionLabel;
139 }
140
141 public function getCustomTrueOptionLabel()
142 {
144 }
145
147 {
148 $this->customFalseOptionLabel = $customFalseOptionLabel;
149 }
150
152 {
154 }
155
157 {
158 $this->specificFeedbackSetting = $specificFeedbackSetting;
159 }
160
162 {
164 }
165
166 public function setAnswers($answers)
167 {
168 $this->answers = $answers;
169 }
170
171 public function getAnswers()
172 {
173 return $this->answers;
174 }
175
176 public function getAnswer($position)
177 {
178 foreach ($this->getAnswers() as $answer) {
179 if ($answer->getPosition() == $position) {
180 return $answer;
181 }
182 }
183
184 return null;
185 }
186
187 public function addAnswer(ilAssKprimChoiceAnswer $answer)
188 {
189 $this->answers[] = $answer;
190 }
191
192 public function loadFromDb($questionId)
193 {
194 $res = $this->db->queryF($this->buildQuestionDataQuery(), array('integer'), array($questionId));
195
196 while ($data = $this->db->fetchAssoc($res)) {
197 $this->setId($questionId);
198
199 $this->setOriginalId($data['original_id']);
200
201 $this->setObjId($data['obj_fi']);
202
203 $this->setTitle($data['title']);
204 $this->setNrOfTries($data['nr_of_tries']);
205 $this->setComment($data['description']);
206 $this->setAuthor($data['author']);
207 $this->setPoints($data['points']);
208 $this->setOwner($data['owner']);
209 $this->setEstimatedWorkingTimeFromDurationString($data['working_time']);
210 $this->setLastChange($data['tstamp']);
211 require_once 'Services/RTE/classes/class.ilRTE.php';
212 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'], 1));
213
214 $this->setShuffleAnswersEnabled((bool) $data['shuffle_answers']);
215
216 if ($this->isValidAnswerType($data['answer_type'])) {
217 $this->setAnswerType($data['answer_type']);
218 }
219
220 if (is_numeric($data['thumb_size'])) {
221 $this->setThumbSize((int) $data['thumb_size']);
222 }
223
224 if ($this->isValidOptionLabel($data['opt_label'])) {
225 $this->setOptionLabel($data['opt_label']);
226 }
227
228 $this->setCustomTrueOptionLabel($data['custom_true']);
229 $this->setCustomFalseOptionLabel($data['custom_false']);
230
231 if ($data['score_partsol'] !== null) {
232 $this->setScorePartialSolutionEnabled((bool) $data['score_partsol']);
233 }
234
235 if (isset($data['feedback_setting'])) {
236 $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
237 }
238
239 try {
240 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
241 } catch (ilTestQuestionPoolException $e) {
242 }
243 }
244
245 $this->loadAnswerData($questionId);
246
247 parent::loadFromDb($questionId);
248 }
249
250 private function loadAnswerData($questionId)
251 {
252 global $DIC;
253 $ilDB = $DIC['ilDB'];
254
255 $res = $this->db->queryF(
256 "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
257 array('integer'),
258 array($questionId)
259 );
260
261 require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
262 require_once 'Services/RTE/classes/class.ilRTE.php';
263
264 while ($data = $ilDB->fetchAssoc($res)) {
265 $answer = new ilAssKprimChoiceAnswer();
266
267 $answer->setPosition($data['position']);
268
269 $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
270
271 $answer->setImageFile($data['imagefile']);
272 $answer->setThumbPrefix($this->getThumbPrefix());
273 $answer->setImageFsDir($this->getImagePath());
274 $answer->setImageWebDir($this->getImagePathWeb());
275
276 $answer->setCorrectness($data['correctness']);
277
278 $this->answers[$answer->getPosition()] = $answer;
279 }
280
281 for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
282 $answer = new ilAssKprimChoiceAnswer();
283
284 $answer->setPosition($i);
285
286 $this->answers[$answer->getPosition()] = $answer;
287 }
288 }
289
290 public function saveToDb($originalId = '')
291 {
292 $this->saveQuestionDataToDb($originalId);
293
296
297 parent::saveToDb($originalId);
298 }
299
301 {
302 $this->db->replace(
303 $this->getAdditionalTableName(),
304 array(
305 'question_fi' => array('integer', $this->getId())
306 ),
307 array(
308 'shuffle_answers' => array('integer', (int) $this->isShuffleAnswersEnabled()),
309 'answer_type' => array('text', $this->getAnswerType()),
310 'thumb_size' => array('integer', (int) $this->getThumbSize()),
311 'opt_label' => array('text', $this->getOptionLabel()),
312 'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
313 'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
314 'score_partsol' => array('integer', (int) $this->isScorePartialSolutionEnabled()),
315 'feedback_setting' => array('integer', (int) $this->getSpecificFeedbackSetting())
316 )
317 );
318 }
319
321 {
322 foreach ($this->getAnswers() as $answer) {
323 $this->db->replace(
324 $this->getAnswerTableName(),
325 array(
326 'question_fi' => array('integer', (int) $this->getId()),
327 'position' => array('integer', (int) $answer->getPosition())
328 ),
329 array(
330 'answertext' => array('text', $answer->getAnswertext()),
331 'imagefile' => array('text', $answer->getImageFile()),
332 'correctness' => array('integer', (int) $answer->getCorrectness())
333 )
334 );
335 }
336
337 $this->rebuildThumbnails();
338 }
339
340 public function isComplete()
341 {
342 foreach (array($this->title, $this->author, $this->question) as $text) {
343 if (!strlen($text)) {
344 return false;
345 }
346 }
347
348 if ($this->getMaximumPoints() <= 0) {
349 return false;
350 }
351
352 foreach ($this->getAnswers() as $answer) {
353 /* @var ilAssKprimChoiceAnswer $answer */
354
355 if (is_null($answer->getCorrectness())) {
356 return false;
357 }
358
359 if (!strlen($answer->getAnswertext()) && !strlen($answer->getImageFile())) {
360 return false;
361 }
362 }
363
364 return true;
365 }
366
375 public function saveWorkingData($active_id, $pass = null, $authorized = true)
376 {
378 $ilDB = $GLOBALS['DIC']['ilDB'];
379
380 if (is_null($pass)) {
381 include_once "./Modules/Test/classes/class.ilObjTest.php";
382 $pass = ilObjTest::_getPass($active_id);
383 }
384
385 $entered_values = 0;
386
387 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
388 $this->removeCurrentSolution($active_id, $pass, $authorized);
389
390 $solutionSubmit = $this->getSolutionSubmit();
391
392 foreach ($solutionSubmit as $answerIndex => $answerValue) {
393 $this->saveCurrentSolution($active_id, $pass, (int) $answerIndex, (int) $answerValue, $authorized);
394 $entered_values++;
395 }
396 });
397
398 if ($entered_values) {
399 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
401 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
402 }
403 } else {
404 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
406 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
407 }
408 }
409
410 return true;
411 }
412
423 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
424 {
425 if ($returndetails) {
426 throw new ilTestException('return details not implemented for ' . __METHOD__);
427 }
428
429 global $DIC;
430 $ilDB = $DIC['ilDB'];
431
432 $found_values = array();
433 if (is_null($pass)) {
434 $pass = $this->getSolutionMaxPass($active_id);
435 }
436
437 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
438
439 while ($data = $ilDB->fetchAssoc($result)) {
440 $found_values[(int) $data['value1']] = (int) $data['value2'];
441 }
442
443 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
444
445 return $points;
446 }
447
448 public function getValidAnswerTypes()
449 {
450 return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
451 }
452
454 {
455 $validTypes = $this->getValidAnswerTypes();
456 return in_array($answerType, $validTypes);
457 }
458
460 {
462 }
463
469 {
470 return array(
471 self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
472 self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
473 );
474 }
475
476 public function getValidOptionLabels()
477 {
478 return array(
479 self::OPTION_LABEL_RIGHT_WRONG,
480 self::OPTION_LABEL_PLUS_MINUS,
481 self::OPTION_LABEL_APPLICABLE_OR_NOT,
482 self::OPTION_LABEL_ADEQUATE_OR_NOT,
483 self::OPTION_LABEL_CUSTOM
484 );
485 }
486
488 {
489 return array(
490 self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
491 self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
492 self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
493 self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
494 self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
495 );
496 }
497
499 {
500 $validLabels = $this->getValidOptionLabels();
501 return in_array($optionLabel, $validLabels);
502 }
503
505 {
506 switch ($optionLabel) {
508 return $lng->txt('option_label_right');
509
511 return $lng->txt('option_label_plus');
512
514 return $lng->txt('option_label_applicable');
515
517 return $lng->txt('option_label_adequate');
518
520 return $this->getCustomTrueOptionLabel();
521 }
522 }
523
525 {
526 switch ($optionLabel) {
528 return $lng->txt('option_label_wrong');
529
531 return $lng->txt('option_label_minus');
532
534 return $lng->txt('option_label_not_applicable');
535
537 return $lng->txt('option_label_not_adequate');
538
540 return $this->getCustomFalseOptionLabel();
541 }
542 }
543
545 {
546 return sprintf(
547 $lng->txt('kprim_instruction_text'),
548 $this->getTrueOptionLabelTranslation($lng, $optionLabel),
549 $this->getFalseOptionLabelTranslation($lng, $optionLabel)
550 );
551 }
552
553 public function isCustomOptionLabel($labelValue)
554 {
555 return $labelValue == self::OPTION_LABEL_CUSTOM;
556 }
557
558 public function getThumbPrefix()
559 {
560 return self::THUMB_PREFIX;
561 }
562
563 public function rebuildThumbnails()
564 {
565 if ($this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize()) {
566 foreach ($this->getAnswers() as $answer) {
567 if (strlen($answer->getImageFile())) {
568 $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
569 }
570 }
571 }
572 }
573
574 protected function generateThumbForFile($path, $file)
575 {
576 $filename = $path . $file;
577 if (@file_exists($filename)) {
578 $thumbpath = $path . $this->getThumbPrefix() . $file;
579 $path_info = @pathinfo($filename);
580 $ext = "";
581 switch (strtoupper($path_info['extension'])) {
582 case 'PNG':
583 $ext = 'PNG';
584 break;
585 case 'GIF':
586 $ext = 'GIF';
587 break;
588 default:
589 $ext = 'JPEG';
590 break;
591 }
592 ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
593 }
594 }
595
597 {
598 foreach ($answers as $answer) {
599 /* @var ilAssKprimChoiceAnswer $answer */
600
601 if (!isset($files[$answer->getPosition()])) {
602 continue;
603 }
604
605 $this->handleFileUpload($answer, $files[$answer->getPosition()]);
606 }
607 }
608
609 private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
610 {
611 $imagePath = $this->getImagePath();
612
613 if (!file_exists($imagePath)) {
614 ilUtil::makeDirParents($imagePath);
615 }
616
617 $filename = $this->buildHashedImageFilename($fileData['name'], true);
618
619 $answer->setImageFsDir($imagePath);
620 $answer->setImageFile($filename);
621
622 if (!ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath())) {
623 return 2;
624 }
625
626 return 0;
627 }
628
629 public function removeAnswerImage($position)
630 {
631 $answer = $this->getAnswer($position);
632
633 if (file_exists($answer->getImageFsPath())) {
634 ilUtil::delDir($answer->getImageFsPath());
635 }
636
637 if (file_exists($answer->getThumbFsPath())) {
638 ilUtil::delDir($answer->getThumbFsPath());
639 }
640
641 $answer->setImageFile(null);
642 }
643
644 protected function getSolutionSubmit()
645 {
646 $solutionSubmit = array();
647 foreach ($_POST as $key => $value) {
648 $matches = null;
649
650 if (preg_match("/^kprim_choice_result_(\d+)/", $key, $matches)) {
651 if (strlen($value)) {
652 $solutionSubmit[$matches[1]] = $value;
653 }
654 }
655 }
656 return $solutionSubmit;
657 }
658
659 protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
660 {
661 $numCorrect = 0;
662
663 foreach ($this->getAnswers() as $key => $answer) {
664 if (!isset($found_values[$answer->getPosition()])) {
665 continue;
666 }
667
668 if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
669 $numCorrect++;
670 }
671 }
672
673 if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
674 $points = $this->getPoints();
675 } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
676 $points = $this->getPoints() / 2;
677 } else {
678 $points = 0;
679 }
680
681 if ($active_id) {
682 include_once "./Modules/Test/classes/class.ilObjTest.php";
683 $mc_scoring = ilObjTest::_getMCScoring($active_id);
684 if (($mc_scoring == 0) && (count($found_values) == 0)) {
685 $points = 0;
686 }
687 }
688 return $points;
689 }
690
691 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
692 {
693 if ($this->id <= 0) {
694 // The question has not been saved. It cannot be duplicated
695 return;
696 }
697 // duplicate the question in database
698 $this_id = $this->getId();
699 $thisObjId = $this->getObjId();
700
701 $clone = $this;
702 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
704 $clone->id = -1;
705
706 if ((int) $testObjId > 0) {
707 $clone->setObjId($testObjId);
708 }
709
710 if ($title) {
711 $clone->setTitle($title);
712 }
713
714 if ($author) {
715 $clone->setAuthor($author);
716 }
717 if ($owner) {
718 $clone->setOwner($owner);
719 }
720
721 if ($for_test) {
722 $clone->saveToDb($original_id);
723 } else {
724 $clone->saveToDb();
725 }
726
727 // copy question page content
728 $clone->copyPageOfQuestion($this_id);
729 // copy XHTML media objects
730 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
731 // duplicate the images
732 $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
733
734 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
735
736 return $clone->id;
737 }
738
739 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
740 {
741 if ($this->id <= 0) {
742 // The question has not been saved. It cannot be duplicated
743 return;
744 }
745
746 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
747
748 $sourceQuestionId = $this->id;
749 $sourceParentId = $this->getObjId();
750
751 // duplicate the question in database
752 $clone = $this;
753 $clone->id = -1;
754
755 $clone->setObjId($targetParentId);
756
757 if ($targetQuestionTitle) {
758 $clone->setTitle($targetQuestionTitle);
759 }
760
761 $clone->saveToDb();
762 // copy question page content
763 $clone->copyPageOfQuestion($sourceQuestionId);
764 // copy XHTML media objects
765 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
766 // duplicate the image
767 $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
768
769 $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
770
771 return $clone->id;
772 }
773
777 public function copyObject($target_questionpool_id, $title = "")
778 {
779 if ($this->id <= 0) {
780 // The question has not been saved. It cannot be duplicated
781 return;
782 }
783 // duplicate the question in database
784 $clone = $this;
785 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
787 $clone->id = -1;
788 $source_questionpool_id = $this->getObjId();
789 $clone->setObjId($target_questionpool_id);
790 if ($title) {
791 $clone->setTitle($title);
792 }
793 $clone->saveToDb();
794 // copy question page content
795 $clone->copyPageOfQuestion($original_id);
796 // copy XHTML media objects
797 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
798 // duplicate the image
799 $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
800
801 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
802
803 return $clone->id;
804 }
805
806 protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
807 {
808 parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
809
810 $question = self::_instanciateQuestion($origQuestionId);
811
812 foreach ($question->getAnswers() as $answer) {
813 $question->removeAnswerImage($answer->getPosition());
814 }
815 }
816
817 protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
818 {
819 parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
820
821 $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
822 }
823
824 protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
825 {
827 global $DIC;
828 $ilLog = $DIC['ilLog'];
829
830 $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
831 $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
832
833 foreach ($this->getAnswers() as $answer) {
834 $filename = $answer->getImageFile();
835
836 if (strlen($filename)) {
837 if (!file_exists($targetPath)) {
838 ilUtil::makeDirParents($targetPath);
839 }
840
841 if (file_exists($sourcePath . $filename)) {
842 if (!copy($sourcePath . $filename, $targetPath . $filename)) {
843 $ilLog->warning(sprintf(
844 "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
845 $sourcePath . $filename,
846 $targetPath . $filename,
847 $sourceQuestionId,
848 $targetQuestionId,
849 $sourceParentId,
850 $targetParentId
851 ));
852 }
853 }
854
855 if (file_exists($sourcePath . $this->getThumbPrefix() . $filename)) {
856 if (!copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename)) {
857 $ilLog->warning(sprintf(
858 "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
859 $sourcePath . $this->getThumbPrefix() . $filename,
860 $targetPath . $this->getThumbPrefix() . $filename,
861 $sourceQuestionId,
862 $targetQuestionId,
863 $sourceParentId,
864 $targetParentId
865 ));
866 }
867 }
868 }
869 }
870 }
871
872 protected function getRTETextWithMediaObjects()
873 {
874 $combinedText = parent::getRTETextWithMediaObjects();
875
876 foreach ($this->getAnswers() as $answer) {
877 $combinedText .= $answer->getAnswertext();
878 }
879
880 return $combinedText;
881 }
882
887 {
888 foreach ($this->getAnswers() as $answer) {
889 /* @var ilAssKprimChoiceAnswer $answer */
890 $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
891 }
892 }
893
897 public function toJSON()
898 {
899 $this->lng->loadLanguageModule('assessment');
900
901 require_once './Services/RTE/classes/class.ilRTE.php';
902 $result = array();
903 $result['id'] = (int) $this->getId();
904 $result['type'] = (string) $this->getQuestionType();
905 $result['title'] = (string) $this->getTitle();
906 $result['question'] = $this->formatSAQuestion($this->getQuestion());
907 $result['instruction'] = $this->getInstructionTextTranslation(
908 $this->lng,
909 $this->getOptionLabel()
910 );
911 $result['nr_of_tries'] = (int) $this->getNrOfTries();
912 $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
913 $result['feedback'] = array(
914 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
915 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
916 );
917
918 $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
919 $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
920
921 $result['num_allowed_failures'] = $this->getNumAllowedFailures();
922
923 $answers = array();
924 $has_image = false;
925
926 foreach ($this->getAnswers() as $key => $answer) {
927 if (strlen((string) $answer->getImageFile())) {
928 $has_image = true;
929 }
930
931 $answers[] = array(
932 'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
933 'correctness' => (bool) $answer->getCorrectness(),
934 'order' => (int) $answer->getPosition(),
935 'image' => (string) $answer->getImageFile(),
936 'feedback' => $this->formatSAQuestion(
937 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
938 )
939 );
940 }
941
942 $result['answers'] = $answers;
943
944 if ($has_image) {
945 $result['path'] = $this->getImagePathWeb();
946 $result['thumb'] = $this->getThumbSize();
947 }
948
949 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
950 $result['mobs'] = $mobs;
951
952 return json_encode($result);
953 }
954
955 private function getNumAllowedFailures()
956 {
957 if ($this->isScorePartialSolutionEnabled()) {
958 return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
959 }
960
961 return 0;
962 }
963
965 {
966 return 'feedback_correct_kprim';
967 }
968
969 public static function isObligationPossible($questionId)
970 {
971 return true;
972 }
973
974 public function isAnswered($active_id, $pass = null)
975 {
976 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
977
978 return $numExistingSolutionRecords >= 4;
979 }
980
984 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
985 {
986 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
987
988 $solution = $this->getSolutionValues($active_id, $pass);
989
990 $i = 1;
991 foreach ($this->getAnswers() as $id => $answer) {
992 $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
993 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
994 $correctness = false;
995 foreach ($solution as $solutionvalue) {
996 if ($id == $solutionvalue['value1']) {
997 $correctness = $solutionvalue['value2'];
998 break;
999 }
1000 }
1001 $worksheet->setCell($startrow + $i, 1, $correctness);
1002 $i++;
1003 }
1004
1005 return $startrow + $i + 1;
1006 }
1007
1008 public function moveAnswerDown($position)
1009 {
1010 if ($position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1)) {
1011 return false;
1012 }
1013
1014 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1015 if ($i == $position) {
1016 $movingAnswer = $this->answers[$i];
1017 $targetAnswer = $this->answers[ $i + 1 ];
1018
1019 $movingAnswer->setPosition($position + 1);
1020 $targetAnswer->setPosition($position);
1021
1022 $this->answers[ $i + 1 ] = $movingAnswer;
1023 $this->answers[$i] = $targetAnswer;
1024 }
1025 }
1026 }
1027
1028 public function moveAnswerUp($position)
1029 {
1030 if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
1031 return false;
1032 }
1033
1034 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1035 if ($i == $position) {
1036 $movingAnswer = $this->answers[$i];
1037 $targetAnswer = $this->answers[ $i - 1 ];
1038
1039 $movingAnswer->setPosition($position - 1);
1040 $targetAnswer->setPosition($position);
1041
1042 $this->answers[ $i - 1 ] = $movingAnswer;
1043 $this->answers[$i] = $targetAnswer;
1044 }
1045 }
1046
1047 return true;
1048 }
1049}
$result
$path
Definition: aliased.php:25
$filename
Definition: buildRTE.php:89
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
getQuestionType()
Returns the question type of the question.
const PARTIAL_SCORING_NUM_CORRECT_ANSWERS
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
toJSON()
Returns a JSON representation of the question.
beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
isSingleLineAnswerType($answerType)
setCustomTrueOptionLabel($customTrueOptionLabel)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
handleFileUploads($answers, $files)
setCustomFalseOptionLabel($customFalseOptionLabel)
getAnswerTableName()
Returns the name of the answer table in the database.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
addAnswer(ilAssKprimChoiceAnswer $answer)
generateThumbForFile($path, $file)
removeAnswerImage($position)
copyObject($target_questionpool_id, $title="")
Copies an assMultipleChoice object.
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
loadAnswerData($questionId)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
setSpecificFeedbackSetting($specificFeedbackSetting)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
getValidOptionLabelsTranslated(ilLanguage $lng)
getTrueOptionLabelTranslation(ilLanguage $lng, $optionLabel)
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
setAnswerType($answerType)
saveToDb($originalId='')
Saves the question to the database.
getFalseOptionLabelTranslation(ilLanguage $lng, $optionLabel)
calculateReachedPointsForSolution($found_values, $active_id=0)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{Creates an Excel worksheet for the detailed cumulated results of this question.object}
isValidOptionLabel($optionLabel)
isCustomOptionLabel($labelValue)
isValidAnswerType($answerType)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
getInstructionTextTranslation(ilLanguage $lng, $optionLabel)
setOptionLabel($optionLabel)
loadFromDb($questionId)
Loads the question from the database.
isComplete()
Returns true, if a question is complete for use.
getAnswerTypeSelectOptions(ilLanguage $lng)
setShuffleAnswersEnabled($shuffleAnswersEnabled)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
const OPTION_LABEL_APPLICABLE_OR_NOT
__construct($title='', $comment='', $author='', $owner=-1, $question='')
assQuestion constructor
setScorePartialSolutionEnabled($scorePartialSolutionEnabled)
Abstract basic class which is to be extended by the concrete assessment question type classes.
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
setId($id=-1)
Sets the id of the assQuestion object.
setOriginalId($original_id)
setObjId($obj_id=0)
Set the object id of the container object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
saveQuestionDataToDb($original_id="")
getId()
Gets the id of the assQuestion object.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
getObjId()
Get the object id of the container object.
setTitle($title="")
Sets the title string of the assQuestion object.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
setEstimatedWorkingTimeFromDurationString($durationString)
Sets the estimated working time of a question from a given datetime string.
buildImagePath($questionId, $parentObjectId)
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
removeCurrentSolution($active_id, $pass, $authorized=true)
buildHashedImageFilename($plain_image_filename, $unique=false)
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
setAuthor($author="")
Sets the authors name of the assQuestion object.
getPoints()
Returns the maximum available points for the question.
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
getTitle()
Gets the title string of the assQuestion object.
setPoints($a_points)
Sets the maximum available points for the question.
setComment($comment="")
Sets the comment string of the assQuestion object.
setNrOfTries($a_nr_of_tries)
getQuestion()
Gets the question string of the question object.
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
static _instanciateQuestion($question_id)
Creates an instance of a question with a given question id.
setQuestion($question="")
Sets the question string of the question object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setLastChange($lastChange)
language handling
static _getLogLanguage()
retrieve the log language for assessment logging
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _getMCScoring($active_id)
Gets the scoring type for multiple choice questions.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
Base Exception for all Exceptions relating to Modules/Test.
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static convertImage( $a_from, $a_to, $a_target_format="", $a_geometry="", $a_background_color="")
convert image
static makeDirParents($a_dir)
Create a new directory and all parent directories.
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
Interface ilObjAnswerScoringAdjustable.
Interface ilObjQuestionScoringAdjustable.
$files
Definition: metarefresh.php:49
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$mobs
$data
Definition: bench.php:6
$text
Definition: errorreport.php:18