ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 $ilDB;
253
254 $res = $this->db->queryF(
255 "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
256 array('integer'),
257 array($questionId)
258 );
259
260 require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
261 require_once 'Services/RTE/classes/class.ilRTE.php';
262
263 while ($data = $ilDB->fetchAssoc($res)) {
264 $answer = new ilAssKprimChoiceAnswer();
265
266 $answer->setPosition($data['position']);
267
268 $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
269
270 $answer->setImageFile($data['imagefile']);
271 $answer->setThumbPrefix($this->getThumbPrefix());
272 $answer->setImageFsDir($this->getImagePath());
273 $answer->setImageWebDir($this->getImagePathWeb());
274
275 $answer->setCorrectness($data['correctness']);
276
277 $this->answers[$answer->getPosition()] = $answer;
278 }
279
280 for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
281 $answer = new ilAssKprimChoiceAnswer();
282
283 $answer->setPosition($i);
284
285 $this->answers[$answer->getPosition()] = $answer;
286 }
287 }
288
289 public function saveToDb($originalId = '')
290 {
291 $this->saveQuestionDataToDb($originalId);
292
295
296 parent::saveToDb($originalId);
297 }
298
300 {
301 $this->db->replace(
302 $this->getAdditionalTableName(),
303 array(
304 'question_fi' => array('integer', $this->getId())
305 ),
306 array(
307 'shuffle_answers' => array('integer', (int) $this->isShuffleAnswersEnabled()),
308 'answer_type' => array('text', $this->getAnswerType()),
309 'thumb_size' => array('integer', (int) $this->getThumbSize()),
310 'opt_label' => array('text', $this->getOptionLabel()),
311 'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
312 'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
313 'score_partsol' => array('integer', (int) $this->isScorePartialSolutionEnabled()),
314 'feedback_setting' => array('integer', (int) $this->getSpecificFeedbackSetting())
315 )
316 );
317 }
318
320 {
321 foreach ($this->getAnswers() as $answer) {
322 $this->db->replace(
323 $this->getAnswerTableName(),
324 array(
325 'question_fi' => array('integer', (int) $this->getId()),
326 'position' => array('integer', (int) $answer->getPosition())
327 ),
328 array(
329 'answertext' => array('text', $answer->getAnswertext()),
330 'imagefile' => array('text', $answer->getImageFile()),
331 'correctness' => array('integer', (int) $answer->getCorrectness())
332 )
333 );
334 }
335
336 $this->rebuildThumbnails();
337 }
338
339 public function isComplete()
340 {
341 foreach (array($this->title, $this->author, $this->question) as $text) {
342 if (!strlen($text)) {
343 return false;
344 }
345 }
346
347 if ($this->getMaximumPoints() <= 0) {
348 return false;
349 }
350
351 foreach ($this->getAnswers() as $answer) {
352 /* @var ilAssKprimChoiceAnswer $answer */
353
354 if (is_null($answer->getCorrectness())) {
355 return false;
356 }
357
358 if (!strlen($answer->getAnswertext()) && !strlen($answer->getImageFile())) {
359 return false;
360 }
361 }
362
363 return true;
364 }
365
374 public function saveWorkingData($active_id, $pass = null, $authorized = true)
375 {
377 $ilDB = $GLOBALS['DIC']['ilDB'];
378
379 if (is_null($pass)) {
380 include_once "./Modules/Test/classes/class.ilObjTest.php";
381 $pass = ilObjTest::_getPass($active_id);
382 }
383
384 $entered_values = 0;
385
386 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
387 $this->removeCurrentSolution($active_id, $pass, $authorized);
388
389 $solutionSubmit = $this->getSolutionSubmit();
390
391 foreach ($solutionSubmit as $answerIndex => $answerValue) {
392 $this->saveCurrentSolution($active_id, $pass, (int) $answerIndex, (int) $answerValue, $authorized);
393 $entered_values++;
394 }
395 });
396
397 if ($entered_values) {
398 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
400 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
401 }
402 } else {
403 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
405 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
406 }
407 }
408
409 return true;
410 }
411
415 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
416 {
417 // nothing to rework!
418 }
419
430 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
431 {
432 if ($returndetails) {
433 throw new ilTestException('return details not implemented for ' . __METHOD__);
434 }
435
436 global $ilDB;
437
438 $found_values = array();
439 if (is_null($pass)) {
440 $pass = $this->getSolutionMaxPass($active_id);
441 }
442
443 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
444
445 while ($data = $ilDB->fetchAssoc($result)) {
446 $found_values[(int) $data['value1']] = (int) $data['value2'];
447 }
448
449 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
450
451 return $points;
452 }
453
454 public function getValidAnswerTypes()
455 {
456 return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
457 }
458
460 {
461 $validTypes = $this->getValidAnswerTypes();
462 return in_array($answerType, $validTypes);
463 }
464
466 {
468 }
469
475 {
476 return array(
477 self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
478 self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
479 );
480 }
481
482 public function getValidOptionLabels()
483 {
484 return array(
485 self::OPTION_LABEL_RIGHT_WRONG,
486 self::OPTION_LABEL_PLUS_MINUS,
487 self::OPTION_LABEL_APPLICABLE_OR_NOT,
488 self::OPTION_LABEL_ADEQUATE_OR_NOT,
489 self::OPTION_LABEL_CUSTOM
490 );
491 }
492
494 {
495 return array(
496 self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
497 self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
498 self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
499 self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
500 self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
501 );
502 }
503
505 {
506 $validLabels = $this->getValidOptionLabels();
507 return in_array($optionLabel, $validLabels);
508 }
509
511 {
512 switch ($optionLabel) {
514 return $lng->txt('option_label_right');
515
517 return $lng->txt('option_label_plus');
518
520 return $lng->txt('option_label_applicable');
521
523 return $lng->txt('option_label_adequate');
524
526 return $this->getCustomTrueOptionLabel();
527 }
528 }
529
531 {
532 switch ($optionLabel) {
534 return $lng->txt('option_label_wrong');
535
537 return $lng->txt('option_label_minus');
538
540 return $lng->txt('option_label_not_applicable');
541
543 return $lng->txt('option_label_not_adequate');
544
546 return $this->getCustomFalseOptionLabel();
547 }
548 }
549
551 {
552 return sprintf(
553 $lng->txt('kprim_instruction_text'),
554 $this->getTrueOptionLabelTranslation($lng, $optionLabel),
555 $this->getFalseOptionLabelTranslation($lng, $optionLabel)
556 );
557 }
558
559 public function isCustomOptionLabel($labelValue)
560 {
561 return $labelValue == self::OPTION_LABEL_CUSTOM;
562 }
563
564 public function getThumbPrefix()
565 {
566 return self::THUMB_PREFIX;
567 }
568
569 public function rebuildThumbnails()
570 {
571 if ($this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize()) {
572 foreach ($this->getAnswers() as $answer) {
573 if (strlen($answer->getImageFile())) {
574 $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
575 }
576 }
577 }
578 }
579
580 protected function generateThumbForFile($path, $file)
581 {
583 if (@file_exists($filename)) {
584 $thumbpath = $path . $this->getThumbPrefix() . $file;
585 $path_info = @pathinfo($filename);
586 $ext = "";
587 switch (strtoupper($path_info['extension'])) {
588 case 'PNG':
589 $ext = 'PNG';
590 break;
591 case 'GIF':
592 $ext = 'GIF';
593 break;
594 default:
595 $ext = 'JPEG';
596 break;
597 }
598 ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
599 }
600 }
601
603 {
604 foreach ($answers as $answer) {
605 /* @var ilAssKprimChoiceAnswer $answer */
606
607 if (!isset($files[$answer->getPosition()])) {
608 continue;
609 }
610
611 $this->handleFileUpload($answer, $files[$answer->getPosition()]);
612 }
613 }
614
615 private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
616 {
617 $imagePath = $this->getImagePath();
618
619 if (!file_exists($imagePath)) {
620 ilUtil::makeDirParents($imagePath);
621 }
622
623 $filename = $this->buildHashedImageFilename($fileData['name'], true);
624
625 $answer->setImageFsDir($imagePath);
626 $answer->setImageFile($filename);
627
628 if (!ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath())) {
629 return 2;
630 }
631
632 return 0;
633 }
634
635 public function removeAnswerImage($position)
636 {
637 $answer = $this->getAnswer($position);
638
639 if (file_exists($answer->getImageFsPath())) {
640 ilUtil::delDir($answer->getImageFsPath());
641 }
642
643 if (file_exists($answer->getThumbFsPath())) {
644 ilUtil::delDir($answer->getThumbFsPath());
645 }
646
647 $answer->setImageFile(null);
648 }
649
650 protected function getSolutionSubmit()
651 {
652 $solutionSubmit = array();
653 foreach ($_POST as $key => $value) {
654 $matches = null;
655
656 if (preg_match("/^kprim_choice_result_(\d+)/", $key, $matches)) {
657 if (strlen($value)) {
658 $solutionSubmit[$matches[1]] = $value;
659 }
660 }
661 }
662 return $solutionSubmit;
663 }
664
665 protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
666 {
667 $numCorrect = 0;
668
669 foreach ($this->getAnswers() as $key => $answer) {
670 if (!isset($found_values[$answer->getPosition()])) {
671 continue;
672 }
673
674 if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
675 $numCorrect++;
676 }
677 }
678
679 if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
680 $points = $this->getPoints();
681 } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
682 $points = $this->getPoints() / 2;
683 } else {
684 $points = 0;
685 }
686
687 if ($active_id) {
688 include_once "./Modules/Test/classes/class.ilObjTest.php";
689 $mc_scoring = ilObjTest::_getMCScoring($active_id);
690 if (($mc_scoring == 0) && (count($found_values) == 0)) {
691 $points = 0;
692 }
693 }
694 return $points;
695 }
696
697 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
698 {
699 if ($this->id <= 0) {
700 // The question has not been saved. It cannot be duplicated
701 return;
702 }
703 // duplicate the question in database
704 $this_id = $this->getId();
705 $thisObjId = $this->getObjId();
706
707 $clone = $this;
708 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
710 $clone->id = -1;
711
712 if ((int) $testObjId > 0) {
713 $clone->setObjId($testObjId);
714 }
715
716 if ($title) {
717 $clone->setTitle($title);
718 }
719
720 if ($author) {
721 $clone->setAuthor($author);
722 }
723 if ($owner) {
724 $clone->setOwner($owner);
725 }
726
727 if ($for_test) {
728 $clone->saveToDb($original_id);
729 } else {
730 $clone->saveToDb();
731 }
732
733 // copy question page content
734 $clone->copyPageOfQuestion($this_id);
735 // copy XHTML media objects
736 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
737 // duplicate the images
738 $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
739
740 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
741
742 return $clone->id;
743 }
744
745 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
746 {
747 if ($this->id <= 0) {
748 // The question has not been saved. It cannot be duplicated
749 return;
750 }
751
752 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
753
754 $sourceQuestionId = $this->id;
755 $sourceParentId = $this->getObjId();
756
757 // duplicate the question in database
758 $clone = $this;
759 $clone->id = -1;
760
761 $clone->setObjId($targetParentId);
762
763 if ($targetQuestionTitle) {
764 $clone->setTitle($targetQuestionTitle);
765 }
766
767 $clone->saveToDb();
768 // copy question page content
769 $clone->copyPageOfQuestion($sourceQuestionId);
770 // copy XHTML media objects
771 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
772 // duplicate the image
773 $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
774
775 $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
776
777 return $clone->id;
778 }
779
783 public function copyObject($target_questionpool_id, $title = "")
784 {
785 if ($this->id <= 0) {
786 // The question has not been saved. It cannot be duplicated
787 return;
788 }
789 // duplicate the question in database
790 $clone = $this;
791 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
793 $clone->id = -1;
794 $source_questionpool_id = $this->getObjId();
795 $clone->setObjId($target_questionpool_id);
796 if ($title) {
797 $clone->setTitle($title);
798 }
799 $clone->saveToDb();
800 // copy question page content
801 $clone->copyPageOfQuestion($original_id);
802 // copy XHTML media objects
803 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
804 // duplicate the image
805 $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
806
807 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
808
809 return $clone->id;
810 }
811
812 protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
813 {
814 parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
815
816 $question = self::_instanciateQuestion($origQuestionId);
817
818 foreach ($question->getAnswers() as $answer) {
819 $question->removeAnswerImage($answer->getPosition());
820 }
821 }
822
823 protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
824 {
825 parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
826
827 $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
828 }
829
830 protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
831 {
833 global $ilLog;
834
835 $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
836 $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
837
838 foreach ($this->getAnswers() as $answer) {
839 $filename = $answer->getImageFile();
840
841 if (strlen($filename)) {
842 if (!file_exists($targetPath)) {
843 ilUtil::makeDirParents($targetPath);
844 }
845
846 if (file_exists($sourcePath . $filename)) {
847 if (!copy($sourcePath . $filename, $targetPath . $filename)) {
848 $ilLog->warning(sprintf(
849 "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
850 $sourcePath . $filename,
851 $targetPath . $filename,
852 $sourceQuestionId,
853 $targetQuestionId,
854 $sourceParentId,
855 $targetParentId
856 ));
857 }
858 }
859
860 if (file_exists($sourcePath . $this->getThumbPrefix() . $filename)) {
861 if (!copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename)) {
862 $ilLog->warning(sprintf(
863 "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
864 $sourcePath . $this->getThumbPrefix() . $filename,
865 $targetPath . $this->getThumbPrefix() . $filename,
866 $sourceQuestionId,
867 $targetQuestionId,
868 $sourceParentId,
869 $targetParentId
870 ));
871 }
872 }
873 }
874 }
875 }
876
877 protected function getRTETextWithMediaObjects()
878 {
879 $combinedText = parent::getRTETextWithMediaObjects();
880
881 foreach ($this->getAnswers() as $answer) {
882 $combinedText .= $answer->getAnswertext();
883 }
884
885 return $combinedText;
886 }
887
892 {
893 foreach ($this->getAnswers() as $answer) {
894 /* @var ilAssKprimChoiceAnswer $answer */
895 $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
896 }
897 }
898
902 public function toJSON()
903 {
904 $this->lng->loadLanguageModule('assessment');
905
906 require_once './Services/RTE/classes/class.ilRTE.php';
907 $result = array();
908 $result['id'] = (int) $this->getId();
909 $result['type'] = (string) $this->getQuestionType();
910 $result['title'] = (string) $this->getTitle();
911 $result['question'] = $this->formatSAQuestion($this->getQuestion());
912 $result['instruction'] = $this->getInstructionTextTranslation(
913 $this->lng,
914 $this->getOptionLabel()
915 );
916 $result['nr_of_tries'] = (int) $this->getNrOfTries();
917 $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
918 $result['feedback'] = array(
919 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
920 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
921 );
922
923 $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
924 $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
925
926 $result['num_allowed_failures'] = $this->getNumAllowedFailures();
927
928 $answers = array();
929 $has_image = false;
930
931 foreach ($this->getAnswers() as $key => $answer) {
932 if (strlen((string) $answer->getImageFile())) {
933 $has_image = true;
934 }
935
936 $answers[] = array(
937 'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
938 'correctness' => (bool) $answer->getCorrectness(),
939 'order' => (int) $answer->getPosition(),
940 'image' => (string) $answer->getImageFile(),
941 'feedback' => $this->formatSAQuestion(
942 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), $key)
943 )
944 );
945 }
946
947 $result['answers'] = $answers;
948
949 if ($has_image) {
950 $result['path'] = $this->getImagePathWeb();
951 $result['thumb'] = $this->getThumbSize();
952 }
953
954 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
955 $result['mobs'] = $mobs;
956
957 return json_encode($result);
958 }
959
960 private function getNumAllowedFailures()
961 {
962 if ($this->isScorePartialSolutionEnabled()) {
963 return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
964 }
965
966 return 0;
967 }
968
970 {
971 return 'feedback_correct_kprim';
972 }
973
974 public static function isObligationPossible($questionId)
975 {
976 return true;
977 }
978
979 public function isAnswered($active_id, $pass = null)
980 {
981 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
982
983 return $numExistingSolutionRecords >= 4;
984 }
985
989 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
990 {
991 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
992
993 $solution = $this->getSolutionValues($active_id, $pass);
994
995 $i = 1;
996 foreach ($this->getAnswers() as $id => $answer) {
997 $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
998 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
999 $correctness = false;
1000 foreach ($solution as $solutionvalue) {
1001 if ($id == $solutionvalue['value1']) {
1002 $correctness = $solutionvalue['value2'];
1003 break;
1004 }
1005 }
1006 $worksheet->setCell($startrow + $i, 1, $correctness);
1007 $i++;
1008 }
1009
1010 return $startrow + $i + 1;
1011 }
1012
1013 public function moveAnswerDown($position)
1014 {
1015 if ($position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1)) {
1016 return false;
1017 }
1018
1019 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1020 if ($i == $position) {
1021 $movingAnswer = $this->answers[$i];
1022 $targetAnswer = $this->answers[ $i + 1 ];
1023
1024 $movingAnswer->setPosition($position + 1);
1025 $targetAnswer->setPosition($position);
1026
1027 $this->answers[ $i + 1 ] = $movingAnswer;
1028 $this->answers[$i] = $targetAnswer;
1029 }
1030 }
1031 }
1032
1033 public function moveAnswerUp($position)
1034 {
1035 if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
1036 return false;
1037 }
1038
1039 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1040 if ($i == $position) {
1041 $movingAnswer = $this->answers[$i];
1042 $targetAnswer = $this->answers[ $i - 1 ];
1043
1044 $movingAnswer->setPosition($position - 1);
1045 $targetAnswer->setPosition($position);
1046
1047 $this->answers[ $i - 1 ] = $movingAnswer;
1048 $this->answers[$i] = $targetAnswer;
1049 }
1050 }
1051
1052 return true;
1053 }
1054}
sprintf('%.4f', $callTime)
$worksheet
$result
$files
Definition: add-vimline.php:18
$_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.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{Reworks the allready saved working data if neccessary.}
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
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
Interface ilObjAnswerScoringAdjustable.
Interface ilObjQuestionScoringAdjustable.
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file
foreach($_POST as $key=> $value) $res
global $ilDB
$mobs
$text
Definition: errorreport.php:18