ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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 {
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 if (is_null($answers)) {
169 return;
170 }
171 $clean_answer_text = function (ilAssKprimChoiceAnswer $answer) {
172 $answer->setAnswertext(
173 $this->getHtmlQuestionContentPurifier()->purify($answer->getAnswertext())
174 );
175 return $answer;
176 };
177 $this->answers = array_map($clean_answer_text, $answers);
178 }
179
180 public function getAnswers()
181 {
182 return $this->answers;
183 }
184
185 public function getAnswer($position)
186 {
187 foreach ($this->getAnswers() as $answer) {
188 if ($answer->getPosition() == $position) {
189 return $answer;
190 }
191 }
192
193 return null;
194 }
195
196 public function addAnswer(ilAssKprimChoiceAnswer $answer)
197 {
198 $answer->setAnswertext(
199 $this->getHtmlQuestionContentPurifier()->purify($answer->getAnswertext())
200 );
201 $this->answers[] = $answer;
202 }
203
204 public function loadFromDb($questionId)
205 {
206 $res = $this->db->queryF($this->buildQuestionDataQuery(), array('integer'), array($questionId));
207
208 while ($data = $this->db->fetchAssoc($res)) {
209 $this->setId($questionId);
210
211 $this->setOriginalId($data['original_id']);
212
213 $this->setObjId($data['obj_fi']);
214
215 $this->setTitle($data['title']);
216 $this->setNrOfTries($data['nr_of_tries']);
217 $this->setComment($data['description']);
218 $this->setAuthor($data['author']);
219 $this->setPoints($data['points']);
220 $this->setOwner($data['owner']);
221 $this->setEstimatedWorkingTimeFromDurationString($data['working_time']);
222 $this->setLastChange($data['tstamp']);
223 require_once 'Services/RTE/classes/class.ilRTE.php';
224 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'], 1));
225
226 $this->setShuffleAnswersEnabled((bool) $data['shuffle_answers']);
227
228 if ($this->isValidAnswerType($data['answer_type'])) {
229 $this->setAnswerType($data['answer_type']);
230 }
231
232 if (is_numeric($data['thumb_size'])) {
233 $this->setThumbSize((int) $data['thumb_size']);
234 }
235
236 if ($this->isValidOptionLabel($data['opt_label'])) {
237 $this->setOptionLabel($data['opt_label']);
238 }
239
240 $this->setCustomTrueOptionLabel($data['custom_true']);
241 $this->setCustomFalseOptionLabel($data['custom_false']);
242
243 if ($data['score_partsol'] !== null) {
244 $this->setScorePartialSolutionEnabled((bool) $data['score_partsol']);
245 }
246
247 if (isset($data['feedback_setting'])) {
248 $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
249 }
250
251 try {
252 $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
255 }
256
257 try {
258 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
260 }
261 }
262
263 $this->loadAnswerData($questionId);
264
265 parent::loadFromDb($questionId);
266 }
267
268 private function loadAnswerData($questionId)
269 {
270 global $DIC;
271 $ilDB = $DIC['ilDB'];
272
273 $res = $this->db->queryF(
274 "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
275 array('integer'),
276 array($questionId)
277 );
278
279 require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
280 require_once 'Services/RTE/classes/class.ilRTE.php';
281
282 while ($data = $ilDB->fetchAssoc($res)) {
283 $answer = new ilAssKprimChoiceAnswer();
284
285 $answer->setPosition($data['position']);
286
287 $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
288
289 $answer->setImageFile($data['imagefile']);
290 $answer->setThumbPrefix($this->getThumbPrefix());
291 $answer->setImageFsDir($this->getImagePath());
292 $answer->setImageWebDir($this->getImagePathWeb());
293
294 $answer->setCorrectness($data['correctness']);
295
296 $answers[$answer->getPosition()] = $answer;
297 }
298
299 $this->setAnswers($answers);
300
301 for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
302 $answer = new ilAssKprimChoiceAnswer();
303
304 $answer->setPosition($i);
305
306 $this->answers[$answer->getPosition()] = $answer;
307 }
308 }
309
310 public function saveToDb($originalId = '')
311 {
312 $this->saveQuestionDataToDb($originalId);
313
316
317 parent::saveToDb($originalId);
318 }
319
321 {
322 $this->db->replace(
323 $this->getAdditionalTableName(),
324 array(
325 'question_fi' => array('integer', $this->getId())
326 ),
327 array(
328 'shuffle_answers' => array('integer', (int) $this->isShuffleAnswersEnabled()),
329 'answer_type' => array('text', $this->getAnswerType()),
330 'thumb_size' => array('integer', (int) $this->getThumbSize()),
331 'opt_label' => array('text', $this->getOptionLabel()),
332 'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
333 'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
334 'score_partsol' => array('integer', (int) $this->isScorePartialSolutionEnabled()),
335 'feedback_setting' => array('integer', (int) $this->getSpecificFeedbackSetting())
336 )
337 );
338 }
339
341 {
342 foreach ($this->getAnswers() as $answer) {
343 $this->db->replace(
344 $this->getAnswerTableName(),
345 array(
346 'question_fi' => array('integer', (int) $this->getId()),
347 'position' => array('integer', (int) $answer->getPosition())
348 ),
349 array(
350 'answertext' => array('text', $answer->getAnswertext()),
351 'imagefile' => array('text', $answer->getImageFile()),
352 'correctness' => array('integer', (int) $answer->getCorrectness())
353 )
354 );
355 }
356
357 $this->rebuildThumbnails();
358 }
359
360 public function isComplete()
361 {
362 foreach (array($this->title, $this->author, $this->question) as $text) {
363 if (!strlen($text)) {
364 return false;
365 }
366 }
367
368 if ($this->getMaximumPoints() <= 0) {
369 return false;
370 }
371
372 foreach ($this->getAnswers() as $answer) {
373 /* @var ilAssKprimChoiceAnswer $answer */
374
375 if (is_null($answer->getCorrectness())) {
376 return false;
377 }
378
379 if (!strlen($answer->getAnswertext()) && !strlen($answer->getImageFile())) {
380 return false;
381 }
382 }
383
384 return true;
385 }
386
395 public function saveWorkingData($active_id, $pass = null, $authorized = true)
396 {
398 $ilDB = $GLOBALS['DIC']['ilDB'];
399
400 if (is_null($pass)) {
401 include_once "./Modules/Test/classes/class.ilObjTest.php";
402 $pass = ilObjTest::_getPass($active_id);
403 }
404
405 $entered_values = 0;
406
407 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
408 $this->removeCurrentSolution($active_id, $pass, $authorized);
409
410 $solutionSubmit = $this->getSolutionSubmit();
411
412 foreach ($solutionSubmit as $answerIndex => $answerValue) {
413 $this->saveCurrentSolution($active_id, $pass, (int) $answerIndex, (int) $answerValue, $authorized);
414 $entered_values++;
415 }
416 });
417
418 if ($entered_values) {
419 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
421 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
422 }
423 } else {
424 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
426 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
427 }
428 }
429
430 return true;
431 }
432
443 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
444 {
445 if ($returndetails) {
446 throw new ilTestException('return details not implemented for ' . __METHOD__);
447 }
448
449 global $DIC;
450 $ilDB = $DIC['ilDB'];
451
452 $found_values = array();
453 if (is_null($pass)) {
454 $pass = $this->getSolutionMaxPass($active_id);
455 }
456
457 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
458
459 while ($data = $ilDB->fetchAssoc($result)) {
460 $found_values[(int) $data['value1']] = (int) $data['value2'];
461 }
462
463 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
464
465 return $points;
466 }
467
468 public function getValidAnswerTypes()
469 {
470 return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
471 }
472
474 {
475 $validTypes = $this->getValidAnswerTypes();
476 return in_array($answerType, $validTypes);
477 }
478
480 {
482 }
483
489 {
490 return array(
491 self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
492 self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
493 );
494 }
495
496 public function getValidOptionLabels()
497 {
498 return array(
499 self::OPTION_LABEL_RIGHT_WRONG,
500 self::OPTION_LABEL_PLUS_MINUS,
501 self::OPTION_LABEL_APPLICABLE_OR_NOT,
502 self::OPTION_LABEL_ADEQUATE_OR_NOT,
503 self::OPTION_LABEL_CUSTOM
504 );
505 }
506
508 {
509 return array(
510 self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
511 self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
512 self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
513 self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
514 self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
515 );
516 }
517
519 {
520 $validLabels = $this->getValidOptionLabels();
521 return in_array($optionLabel, $validLabels);
522 }
523
525 {
526 switch ($optionLabel) {
528 return $lng->txt('option_label_right');
529
531 return $lng->txt('option_label_plus');
532
534 return $lng->txt('option_label_applicable');
535
537 return $lng->txt('option_label_adequate');
538
540 return $this->getCustomTrueOptionLabel();
541 }
542 }
543
545 {
546 switch ($optionLabel) {
548 return $lng->txt('option_label_wrong');
549
551 return $lng->txt('option_label_minus');
552
554 return $lng->txt('option_label_not_applicable');
555
557 return $lng->txt('option_label_not_adequate');
558
560 return $this->getCustomFalseOptionLabel();
561 }
562 }
563
565 {
566 return sprintf(
567 $lng->txt('kprim_instruction_text'),
568 $this->getTrueOptionLabelTranslation($lng, $optionLabel),
569 $this->getFalseOptionLabelTranslation($lng, $optionLabel)
570 );
571 }
572
573 public function isCustomOptionLabel($labelValue)
574 {
575 return $labelValue == self::OPTION_LABEL_CUSTOM;
576 }
577
578 public function getThumbPrefix()
579 {
580 return self::THUMB_PREFIX;
581 }
582
583 public function rebuildThumbnails()
584 {
585 if ($this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize()) {
586 foreach ($this->getAnswers() as $answer) {
587 if (strlen($answer->getImageFile())) {
588 $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
589 }
590 }
591 }
592 }
593
594 protected function generateThumbForFile($path, $file)
595 {
596 $filename = $path . $file;
597 if (@file_exists($filename)) {
598 $thumbpath = $path . $this->getThumbPrefix() . $file;
599 $path_info = @pathinfo($filename);
600 $ext = "";
601 switch (strtoupper($path_info['extension'])) {
602 case 'PNG':
603 $ext = 'PNG';
604 break;
605 case 'GIF':
606 $ext = 'GIF';
607 break;
608 default:
609 $ext = 'JPEG';
610 break;
611 }
612 ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
613 }
614 }
615
616 public function handleFileUploads($answers, $files)
617 {
618 foreach ($answers as $answer) {
619 /* @var ilAssKprimChoiceAnswer $answer */
620
621 if (!isset($files[$answer->getPosition()])) {
622 continue;
623 }
624
625 $this->handleFileUpload($answer, $files[$answer->getPosition()]);
626 }
627 }
628
629 private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
630 {
631 $imagePath = $this->getImagePath();
632
633 if (!file_exists($imagePath)) {
634 ilUtil::makeDirParents($imagePath);
635 }
636
637 $filename = $this->buildHashedImageFilename($fileData['name'], true);
638
639 $answer->setImageFsDir($imagePath);
640 $answer->setImageFile($filename);
641
642 if (!ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath())) {
643 return 2;
644 }
645
646 return 0;
647 }
648
649 public function removeAnswerImage($position)
650 {
651 $answer = $this->getAnswer($position);
652
653 if (file_exists($answer->getImageFsPath())) {
654 ilUtil::delDir($answer->getImageFsPath());
655 }
656
657 if (file_exists($answer->getThumbFsPath())) {
658 ilUtil::delDir($answer->getThumbFsPath());
659 }
660
661 $answer->setImageFile(null);
662 }
663
664 protected function getSolutionSubmit()
665 {
666 $solutionSubmit = array();
667 foreach ($_POST as $key => $value) {
668 $matches = null;
669
670 if (preg_match("/^kprim_choice_result_(\d+)/", $key, $matches)) {
671 if (strlen($value)) {
672 $solutionSubmit[$matches[1]] = $value;
673 }
674 }
675 }
676 return $solutionSubmit;
677 }
678
679 protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
680 {
681 $numCorrect = 0;
682
683 foreach ($this->getAnswers() as $key => $answer) {
684 if (!isset($found_values[$answer->getPosition()])) {
685 continue;
686 }
687
688 if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
689 $numCorrect++;
690 }
691 }
692
693 if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
694 $points = $this->getPoints();
695 } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
696 $points = $this->getPoints() / 2;
697 } else {
698 $points = 0;
699 }
700
701 if ($active_id) {
702 include_once "./Modules/Test/classes/class.ilObjTest.php";
703 $mc_scoring = ilObjTest::_getMCScoring($active_id);
704 if (($mc_scoring == 0) && (count($found_values) == 0)) {
705 $points = 0;
706 }
707 }
708 return $points;
709 }
710
711 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
712 {
713 if ($this->id <= 0) {
714 // The question has not been saved. It cannot be duplicated
715 return;
716 }
717 // duplicate the question in database
718 $this_id = $this->getId();
719 $thisObjId = $this->getObjId();
720
721 $clone = $this;
722 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
724 $clone->id = -1;
725
726 if ((int) $testObjId > 0) {
727 $clone->setObjId($testObjId);
728 }
729
730 if ($title) {
731 $clone->setTitle($title);
732 }
733
734 if ($author) {
735 $clone->setAuthor($author);
736 }
737 if ($owner) {
738 $clone->setOwner($owner);
739 }
740
741 if ($for_test) {
742 $clone->saveToDb($original_id);
743 } else {
744 $clone->saveToDb();
745 }
746
747 // copy question page content
748 $clone->copyPageOfQuestion($this_id);
749 // copy XHTML media objects
750 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
751 // duplicate the images
752 $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
753
754 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
755
756 return $clone->id;
757 }
758
759 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
760 {
761 if ($this->id <= 0) {
762 // The question has not been saved. It cannot be duplicated
763 return;
764 }
765
766 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
767
768 $sourceQuestionId = $this->id;
769 $sourceParentId = $this->getObjId();
770
771 // duplicate the question in database
772 $clone = $this;
773 $clone->id = -1;
774
775 $clone->setObjId($targetParentId);
776
777 if ($targetQuestionTitle) {
778 $clone->setTitle($targetQuestionTitle);
779 }
780
781 $clone->saveToDb();
782 // copy question page content
783 $clone->copyPageOfQuestion($sourceQuestionId);
784 // copy XHTML media objects
785 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
786 // duplicate the image
787 $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
788
789 $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
790
791 return $clone->id;
792 }
793
797 public function copyObject($target_questionpool_id, $title = "")
798 {
799 if ($this->id <= 0) {
800 // The question has not been saved. It cannot be duplicated
801 return;
802 }
803 // duplicate the question in database
804 $clone = $this;
805 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
807 $clone->id = -1;
808 $source_questionpool_id = $this->getObjId();
809 $clone->setObjId($target_questionpool_id);
810 if ($title) {
811 $clone->setTitle($title);
812 }
813 $clone->saveToDb();
814 // copy question page content
815 $clone->copyPageOfQuestion($original_id);
816 // copy XHTML media objects
817 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
818 // duplicate the image
819 $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
820
821 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
822
823 return $clone->id;
824 }
825
826 protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
827 {
828 parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
829
830 $question = self::_instanciateQuestion($origQuestionId);
831
832 foreach ($question->getAnswers() as $answer) {
833 $question->removeAnswerImage($answer->getPosition());
834 }
835 }
836
837 protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
838 {
839 parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
840
841 $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
842 }
843
844 protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
845 {
847 global $DIC;
848 $ilLog = $DIC['ilLog'];
849
850 $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
851 $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
852
853 foreach ($this->getAnswers() as $answer) {
854 $filename = $answer->getImageFile();
855
856 if (strlen($filename)) {
857 if (!file_exists($targetPath)) {
858 ilUtil::makeDirParents($targetPath);
859 }
860
861 if (file_exists($sourcePath . $filename)) {
862 if (!copy($sourcePath . $filename, $targetPath . $filename)) {
863 $ilLog->warning(sprintf(
864 "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
865 $sourcePath . $filename,
866 $targetPath . $filename,
867 $sourceQuestionId,
868 $targetQuestionId,
869 $sourceParentId,
870 $targetParentId
871 ));
872 }
873 }
874
875 if (file_exists($sourcePath . $this->getThumbPrefix() . $filename)) {
876 if (!copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename)) {
877 $ilLog->warning(sprintf(
878 "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
879 $sourcePath . $this->getThumbPrefix() . $filename,
880 $targetPath . $this->getThumbPrefix() . $filename,
881 $sourceQuestionId,
882 $targetQuestionId,
883 $sourceParentId,
884 $targetParentId
885 ));
886 }
887 }
888 }
889 }
890 }
891
892 protected function getRTETextWithMediaObjects()
893 {
894 $combinedText = parent::getRTETextWithMediaObjects();
895
896 foreach ($this->getAnswers() as $answer) {
897 $combinedText .= $answer->getAnswertext();
898 }
899
900 return $combinedText;
901 }
902
907 {
908 foreach ($this->getAnswers() as $answer) {
909 /* @var ilAssKprimChoiceAnswer $answer */
910 $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
911 }
912 }
913
917 public function toJSON()
918 {
919 $this->lng->loadLanguageModule('assessment');
920
921 require_once './Services/RTE/classes/class.ilRTE.php';
922 $result = array();
923 $result['id'] = (int) $this->getId();
924 $result['type'] = (string) $this->getQuestionType();
925 $result['title'] = (string) $this->getTitle();
926 $result['question'] = $this->formatSAQuestion($this->getQuestion());
927 $result['instruction'] = $this->getInstructionTextTranslation(
928 $this->lng,
929 $this->getOptionLabel()
930 );
931 $result['nr_of_tries'] = (int) $this->getNrOfTries();
932 $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
933 $result['feedback'] = array(
934 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
935 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
936 );
937
938 $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
939 $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
940
941 $result['num_allowed_failures'] = $this->getNumAllowedFailures();
942
943 $answers = array();
944 $has_image = false;
945
946 foreach ($this->getAnswers() as $key => $answer) {
947 if (strlen((string) $answer->getImageFile())) {
948 $has_image = true;
949 }
950
951 $answers[] = array(
952 'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
953 'correctness' => (bool) $answer->getCorrectness(),
954 'order' => (int) $answer->getPosition(),
955 'image' => (string) $answer->getImageFile(),
956 'feedback' => $this->formatSAQuestion(
957 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
958 )
959 );
960 }
961
962 $result['answers'] = $answers;
963
964 if ($has_image) {
965 $result['path'] = $this->getImagePathWeb();
966 $result['thumb'] = $this->getThumbSize();
967 }
968
969 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
970 $result['mobs'] = $mobs;
971
972 return json_encode($result);
973 }
974
975 private function getNumAllowedFailures()
976 {
977 if ($this->isScorePartialSolutionEnabled()) {
978 return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
979 }
980
981 return 0;
982 }
983
985 {
986 return 'feedback_correct_kprim';
987 }
988
989 public static function isObligationPossible($questionId)
990 {
991 return true;
992 }
993
994 public function isAnswered($active_id, $pass = null)
995 {
996 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
997
998 return $numExistingSolutionRecords >= 4;
999 }
1000
1004 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
1005 {
1006 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
1007
1008 $solution = $this->getSolutionValues($active_id, $pass);
1009
1010 $i = 1;
1011 foreach ($this->getAnswers() as $id => $answer) {
1012 $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
1013 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
1014 $correctness = false;
1015 foreach ($solution as $solutionvalue) {
1016 if ($id == $solutionvalue['value1']) {
1017 $correctness = $solutionvalue['value2'];
1018 break;
1019 }
1020 }
1021 $worksheet->setCell($startrow + $i, 1, $correctness);
1022 $i++;
1023 }
1024
1025 return $startrow + $i + 1;
1026 }
1027
1028 public function moveAnswerDown($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
1048 public function moveAnswerUp($position)
1049 {
1050 if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
1051 return false;
1052 }
1053
1054 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1055 if ($i == $position) {
1056 $movingAnswer = $this->answers[$i];
1057 $targetAnswer = $this->answers[ $i - 1 ];
1058
1059 $movingAnswer->setPosition($position - 1);
1060 $targetAnswer->setPosition($position);
1061
1062 $this->answers[ $i - 1 ] = $movingAnswer;
1063 $this->answers[$i] = $targetAnswer;
1064 }
1065 }
1066
1067 return true;
1068 }
1069}
$result
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
$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.
setLifecycle(ilAssQuestionLifecycle $lifecycle)
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 moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
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.
Interface ilObjAnswerScoringAdjustable.
Interface ilObjQuestionScoringAdjustable.
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
foreach($_POST as $key=> $value) $res
global $ilDB
$data
Definition: storeScorm.php:23
$mobs
$DIC
Definition: xapitoken.php:46