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