ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
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';
7
15{
17
19
20 const ANSWER_TYPE_SINGLE_LINE = 'singleLine';
21 const ANSWER_TYPE_MULTI_LINE = 'multiLine';
22
23 const OPTION_LABEL_RIGHT_WRONG = 'right_wrong';
24 const OPTION_LABEL_PLUS_MINUS = 'plus_minus';
25 const OPTION_LABEL_APPLICABLE_OR_NOT = 'applicable_or_not';
26 const OPTION_LABEL_ADEQUATE_OR_NOT = 'adequate_or_not';
27 const OPTION_LABEL_CUSTOM = 'customlabel';
28
29 const DEFAULT_THUMB_SIZE = 150;
30 const THUMB_PREFIX = 'thumb.';
31
33
34 private $answerType;
35
36 private $thumbSize;
37
39
40 private $optionLabel;
41
43
45
47
48 private $answers;
49
50 public function __construct($title = '', $comment = '', $author = '', $owner = -1, $question = '')
51 {
52 parent::__construct($title, $comment, $author, $owner, $question);
53
54 $this->shuffleAnswersEnabled = true;
55 $this->answerType = self::ANSWER_TYPE_SINGLE_LINE;
56 $this->thumbSize = self::DEFAULT_THUMB_SIZE;
57 $this->scorePartialSolutionEnabled = true;
58 $this->optionLabel = self::OPTION_LABEL_RIGHT_WRONG;
59 $this->customTrueOptionLabel = '';
60 $this->customFalseOptionLabel = '';
61
62 require_once 'Modules/TestQuestionPool/classes/feedback/class.ilAssConfigurableMultiOptionQuestionFeedback.php';
64
65 $this->answers = array();
66
67 $this->setPoints('');
68 }
69
70 public function getQuestionType()
71 {
72 return 'assKprimChoice';
73 }
74
75 public function getAdditionalTableName()
76 {
77 return "qpl_qst_kprim";
78 }
79
80 public function getAnswerTableName()
81 {
82 return "qpl_a_kprim";
83 }
84
86 {
87 $this->shuffleAnswersEnabled = $shuffleAnswersEnabled;
88 }
89
90 public function isShuffleAnswersEnabled()
91 {
93 }
94
95 public function setAnswerType($answerType)
96 {
97 $this->answerType = $answerType;
98 }
99
100 public function getAnswerType()
101 {
102 return $this->answerType;
103 }
104
105 public function setThumbSize($thumbSize)
106 {
107 $this->thumbSize = $thumbSize;
108 }
109
110 public function getThumbSize()
111 {
112 return $this->thumbSize;
113 }
114
116 {
117 $this->scorePartialSolutionEnabled = $scorePartialSolutionEnabled;
118 }
119
121 {
123 }
124
126 {
127 $this->optionLabel = $optionLabel;
128 }
129
130 public function getOptionLabel()
131 {
132 return $this->optionLabel;
133 }
134
136 {
137 $this->customTrueOptionLabel = $customTrueOptionLabel;
138 }
139
140 public function getCustomTrueOptionLabel()
141 {
143 }
144
146 {
147 $this->customFalseOptionLabel = $customFalseOptionLabel;
148 }
149
151 {
153 }
154
156 {
157 $this->specificFeedbackSetting = $specificFeedbackSetting;
158 }
159
161 {
163 }
164
165 public function setAnswers($answers)
166 {
167 $this->answers = $answers;
168 }
169
170 public function getAnswers()
171 {
172 return $this->answers;
173 }
174
175 public function getAnswer($position)
176 {
177 foreach($this->getAnswers() as $answer)
178 {
179 if($answer->getPosition() == $position)
180 {
181 return $answer;
182 }
183 }
184
185 return null;
186 }
187
188 public function addAnswer(ilAssKprimChoiceAnswer $answer)
189 {
190 $this->answers[] = $answer;
191 }
192
193 public function loadFromDb($questionId)
194 {
195 $res = $this->db->queryF($this->buildQuestionDataQuery(), array('integer'), array($questionId));
196
197 while($data = $this->db->fetchAssoc($res))
198 {
199 $this->setId($questionId);
200
201 $this->setOriginalId($data['original_id']);
202
203 $this->setObjId($data['obj_fi']);
204
205 $this->setTitle($data['title']);
206 $this->setNrOfTries($data['nr_of_tries']);
207 $this->setComment($data['description']);
208 $this->setAuthor($data['author']);
209 $this->setPoints($data['points']);
210 $this->setOwner($data['owner']);
211 $this->setEstimatedWorkingTimeFromDurationString($data['working_time']);
212 $this->setLastChange($data['tstamp']);
213 require_once 'Services/RTE/classes/class.ilRTE.php';
214 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'], 1));
215
216 $this->setShuffleAnswersEnabled((bool)$data['shuffle_answers']);
217
218 if( $this->isValidAnswerType($data['answer_type']) )
219 {
220 $this->setAnswerType($data['answer_type']);
221 }
222
223 if( is_numeric($data['thumb_size']) )
224 {
225 $this->setThumbSize((int)$data['thumb_size']);
226 }
227
228 if( $this->isValidOptionLabel($data['opt_label']) )
229 {
230 $this->setOptionLabel($data['opt_label']);
231 }
232
233 $this->setCustomTrueOptionLabel($data['custom_true']);
234 $this->setCustomFalseOptionLabel($data['custom_false']);
235
236 if( $data['score_partsol'] !== null )
237 {
238 $this->setScorePartialSolutionEnabled((bool)$data['score_partsol']);
239 }
240
241 if( isset($data['feedback_setting']) )
242 {
243 $this->setSpecificFeedbackSetting((int)$data['feedback_setting']);
244 }
245
246 try
247 {
248 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
249 }
251 {
252 }
253 }
254
255 $this->loadAnswerData($questionId);
256
257 parent::loadFromDb($questionId);
258 }
259
260 private function loadAnswerData($questionId)
261 {
262 global $ilDB;
263
264 $res = $this->db->queryF(
265 "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
266 array('integer'), array($questionId)
267 );
268
269 require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
270 require_once 'Services/RTE/classes/class.ilRTE.php';
271
272 while($data = $ilDB->fetchAssoc($res))
273 {
274 $answer = new ilAssKprimChoiceAnswer();
275
276 $answer->setPosition($data['position']);
277
278 $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
279
280 $answer->setImageFile($data['imagefile']);
281 $answer->setThumbPrefix($this->getThumbPrefix());
282 $answer->setImageFsDir($this->getImagePath());
283 $answer->setImageWebDir($this->getImagePathWeb());
284
285 $answer->setCorrectness($data['correctness']);
286
287 $this->answers[$answer->getPosition()] = $answer;
288 }
289
290 for( $i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++ )
291 {
292 $answer = new ilAssKprimChoiceAnswer();
293
294 $answer->setPosition($i);
295
296 $this->answers[$answer->getPosition()] = $answer;
297 }
298 }
299
300 public function saveToDb($originalId = '')
301 {
302 $this->saveQuestionDataToDb($originalId);
303
306
307 parent::saveToDb($originalId);
308 }
309
311 {
312 $this->db->replace(
313 $this->getAdditionalTableName(),
314 array(
315 'question_fi' => array('integer', $this->getId())
316 ),
317 array(
318 'shuffle_answers' => array('integer', (int)$this->isShuffleAnswersEnabled()),
319 'answer_type' => array('text', $this->getAnswerType()),
320 'thumb_size' => array('integer', (int)$this->getThumbSize()),
321 'opt_label' => array('text', $this->getOptionLabel()),
322 'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
323 'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
324 'score_partsol' => array('integer', (int)$this->isScorePartialSolutionEnabled()),
325 'feedback_setting' => array('integer', (int)$this->getSpecificFeedbackSetting())
326 )
327 );
328 }
329
331 {
332 foreach($this->getAnswers() as $answer)
333 {
334 $this->db->replace(
335 $this->getAnswerTableName(),
336 array(
337 'question_fi' => array('integer', (int)$this->getId()),
338 'position' => array('integer', (int)$answer->getPosition())
339 ),
340 array(
341 'answertext' => array('text', $answer->getAnswertext()),
342 'imagefile' => array('text', $answer->getImageFile()),
343 'correctness' => array('integer', (int)$answer->getCorrectness())
344 )
345 );
346 }
347
348 $this->rebuildThumbnails();
349 }
350
351 public function isComplete()
352 {
353 foreach( array($this->title, $this->author, $this->question) as $text )
354 {
355 if( !strlen($text) )
356 {
357 return false;
358 }
359 }
360
361 if( $this->getMaximumPoints() <= 0 )
362 {
363 return false;
364 }
365
366 foreach( $this->getAnswers() as $answer )
367 {
368 /* @var ilAssKprimChoiceAnswer $answer */
369
370 if( is_null($answer->getCorrectness()) )
371 {
372 return false;
373 }
374
375 if( !strlen($answer->getAnswertext()) && !strlen($answer->getImageFile()) )
376 {
377 return false;
378 }
379 }
380
381 return true;
382 }
383
392 public function saveWorkingData($active_id, $pass = NULL)
393 {
395 global $ilDB;
396
397 if (is_null($pass))
398 {
399 include_once "./Modules/Test/classes/class.ilObjTest.php";
400 $pass = ilObjTest::_getPass($active_id);
401 }
402
403 $entered_values = 0;
404
405 $this->getProcessLocker()->requestUserSolutionUpdateLock();
406
407 $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
408 array('integer','integer','integer'),
409 array($active_id, $this->getId(), $pass)
410 );
411 $solutionSubmit = $this->getSolutionSubmit();
412
413 foreach($solutionSubmit as $answerIndex => $answerValue)
414 {
415 $next_id = $ilDB->nextId('tst_solutions');
416 $ilDB->insert("tst_solutions", array(
417 "solution_id" => array("integer", $next_id),
418 "active_fi" => array("integer", $active_id),
419 "question_fi" => array("integer", $this->getId()),
420 "value1" => array("clob", (int)$answerIndex),
421 "value2" => array("clob", (int)$answerValue),
422 "pass" => array("integer", $pass),
423 "tstamp" => array("integer", time())
424 ));
425 $entered_values++;
426 }
427
428 $this->getProcessLocker()->releaseUserSolutionUpdateLock();
429
430 if ($entered_values)
431 {
432 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
434 {
435 $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
436 }
437 }
438 else
439 {
440 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
442 {
443 $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
444 }
445 }
446
447 return true;
448 }
449
458 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
459 {
460 // nothing to do
461 }
462
473 public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
474 {
475 if( $returndetails )
476 {
477 throw new ilTestException('return details not implemented for '.__METHOD__);
478 }
479
480 global $ilDB;
481
482 $found_values = array();
483 if (is_null($pass))
484 {
485 $pass = $this->getSolutionMaxPass($active_id);
486 }
487 $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
488 array('integer','integer','integer'),
489 array($active_id, $this->getId(), $pass)
490 );
491 while ($data = $ilDB->fetchAssoc($result))
492 {
493 $found_values[(int)$data['value1']] = (int)$data['value2'];
494 }
495
496 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
497
498 return $points;
499 }
500
501 public function getValidAnswerTypes()
502 {
503 return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
504 }
505
507 {
508 $validTypes = $this->getValidAnswerTypes();
509 return in_array($answerType, $validTypes);
510 }
511
513 {
515 }
516
522 {
523 return array(
524 self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
525 self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
526 );
527 }
528
529 public function getValidOptionLabels()
530 {
531 return array(
532 self::OPTION_LABEL_RIGHT_WRONG,
533 self::OPTION_LABEL_PLUS_MINUS,
534 self::OPTION_LABEL_APPLICABLE_OR_NOT,
535 self::OPTION_LABEL_ADEQUATE_OR_NOT,
536 self::OPTION_LABEL_CUSTOM
537 );
538 }
539
541 {
542 return array(
543 self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
544 self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
545 self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
546 self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
547 self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
548 );
549 }
550
552 {
553 $validLabels = $this->getValidOptionLabels();
554 return in_array($optionLabel, $validLabels);
555 }
556
558 {
559 switch($optionLabel)
560 {
562 return $lng->txt('option_label_right');
563
565 return $lng->txt('option_label_plus');
566
568 return $lng->txt('option_label_applicable');
569
571 return $lng->txt('option_label_adequate');
572
574 return $this->getCustomTrueOptionLabel();
575 }
576 }
577
579 {
580 switch($optionLabel)
581 {
583 return $lng->txt('option_label_wrong');
584
586 return $lng->txt('option_label_minus');
587
589 return $lng->txt('option_label_not_applicable');
590
592 return $lng->txt('option_label_not_adequate');
593
595 return $this->getCustomFalseOptionLabel();
596 }
597 }
598
600 {
601 return sprintf(
602 $lng->txt('kprim_instruction_text'),
603 $this->getTrueOptionLabelTranslation($lng, $optionLabel),
604 $this->getFalseOptionLabelTranslation($lng, $optionLabel)
605 );
606 }
607
608 public function isCustomOptionLabel($labelValue)
609 {
610 return $labelValue == self::OPTION_LABEL_CUSTOM;
611 }
612
613 public function getThumbPrefix()
614 {
615 return self::THUMB_PREFIX;
616 }
617
618 public function rebuildThumbnails()
619 {
620 if( $this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize() )
621 {
622 foreach ($this->getAnswers() as $answer)
623 {
624 if (strlen($answer->getImageFile()))
625 {
626 $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
627 }
628 }
629 }
630 }
631
632 protected function generateThumbForFile($path, $file)
633 {
635 if (@file_exists($filename))
636 {
637 $thumbpath = $path . $this->getThumbPrefix() . $file;
638 $path_info = @pathinfo($filename);
639 $ext = "";
640 switch (strtoupper($path_info['extension']))
641 {
642 case 'PNG':
643 $ext = 'PNG';
644 break;
645 case 'GIF':
646 $ext = 'GIF';
647 break;
648 default:
649 $ext = 'JPEG';
650 break;
651 }
652 ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
653 }
654 }
655
656 public function handleFileUploads($answers, $files)
657 {
658 foreach($answers as $answer)
659 {
660 /* @var ilAssKprimChoiceAnswer $answer */
661
662 if( !isset($files[$answer->getPosition()]) )
663 {
664 continue;
665 }
666
667 $this->handleFileUpload($answer, $files[$answer->getPosition()]);
668 }
669 }
670
671 private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
672 {
673 $imagePath = $this->getImagePath();
674
675 if( !file_exists($imagePath) )
676 {
677 ilUtil::makeDirParents($imagePath);
678 }
679
680 $filename = $this->createNewImageFileName($fileData['name'], true);
681
682 $answer->setImageFsDir($imagePath);
683 $answer->setImageFile($filename);
684
685 if( !ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath()) )
686 {
687 return 2;
688 }
689
690 return 0;
691 }
692
693 public function removeAnswerImage($position)
694 {
695 $answer = $this->getAnswer($position);
696
697 if( file_exists($answer->getImageFsPath()) )
698 {
699 unlink($answer->getImageFsPath());
700 }
701
702 if( file_exists($answer->getThumbFsPath()) )
703 {
704 unlink($answer->getThumbFsPath());
705 }
706
707 $answer->setImageFile(null);
708 }
709
710 protected function getSolutionSubmit()
711 {
712 $solutionSubmit = array();
713 foreach($_POST as $key => $value)
714 {
715 $matches = null;
716
717 if(preg_match("/^kprim_choice_result_(\d+)/", $key, $matches))
718 {
719 if(strlen($value))
720 {
721 $solutionSubmit[$matches[1]] = $value;
722 }
723 }
724 }
725 return $solutionSubmit;
726 }
727
728 protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
729 {
730 $numCorrect = 0;
731
732 foreach($this->getAnswers() as $key => $answer)
733 {
734 if( !isset($found_values[$answer->getPosition()]) )
735 {
736 continue;
737 }
738
739 if( $found_values[$answer->getPosition()] == $answer->getCorrectness() )
740 {
741 $numCorrect++;
742 }
743 }
744
745 if( $numCorrect >= self::NUM_REQUIRED_ANSWERS )
746 {
747 $points = $this->getPoints();
748 }
749 elseif( $this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS )
750 {
751 $points = $this->getPoints() / 2;
752 }
753 else
754 {
755 $points = 0;
756 }
757
758 if($active_id)
759 {
760 include_once "./Modules/Test/classes/class.ilObjTest.php";
761 $mc_scoring = ilObjTest::_getMCScoring($active_id);
762 if(($mc_scoring == 0) && (count($found_values) == 0))
763 {
764 $points = 0;
765 }
766 }
767 return $points;
768 }
769
770 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
771 {
772 if ($this->id <= 0)
773 {
774 // The question has not been saved. It cannot be duplicated
775 return;
776 }
777 // duplicate the question in database
778 $this_id = $this->getId();
779 $thisObjId = $this->getObjId();
780
781 $clone = $this;
782 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
784 $clone->id = -1;
785
786 if( (int)$testObjId > 0 )
787 {
788 $clone->setObjId($testObjId);
789 }
790
791 if ($title)
792 {
793 $clone->setTitle($title);
794 }
795
796 if ($author)
797 {
798 $clone->setAuthor($author);
799 }
800 if ($owner)
801 {
802 $clone->setOwner($owner);
803 }
804
805 if ($for_test)
806 {
807 $clone->saveToDb($original_id);
808 }
809 else
810 {
811 $clone->saveToDb();
812 }
813
814 // copy question page content
815 $clone->copyPageOfQuestion($this_id);
816 // copy XHTML media objects
817 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
818 // duplicate the images
819 $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
820
821 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
822
823 return $clone->id;
824 }
825
826 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
827 {
828 if ($this->id <= 0)
829 {
830 // The question has not been saved. It cannot be duplicated
831 return;
832 }
833
834 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
835
836 $sourceQuestionId = $this->id;
837 $sourceParentId = $this->getObjId();
838
839 // duplicate the question in database
840 $clone = $this;
841 $clone->id = -1;
842
843 $clone->setObjId($targetParentId);
844
845 if ($targetQuestionTitle)
846 {
847 $clone->setTitle($targetQuestionTitle);
848 }
849
850 $clone->saveToDb();
851 // copy question page content
852 $clone->copyPageOfQuestion($sourceQuestionId);
853 // copy XHTML media objects
854 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
855 // duplicate the image
856 $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
857
858 $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
859
860 return $clone->id;
861 }
862
866 public function copyObject($target_questionpool_id, $title = "")
867 {
868 if ($this->id <= 0)
869 {
870 // The question has not been saved. It cannot be duplicated
871 return;
872 }
873 // duplicate the question in database
874 $clone = $this;
875 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
877 $clone->id = -1;
878 $source_questionpool_id = $this->getObjId();
879 $clone->setObjId($target_questionpool_id);
880 if ($title)
881 {
882 $clone->setTitle($title);
883 }
884 $clone->saveToDb();
885 // copy question page content
886 $clone->copyPageOfQuestion($original_id);
887 // copy XHTML media objects
888 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
889 // duplicate the image
890 $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
891
892 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
893
894 return $clone->id;
895 }
896
897 protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
898 {
899 parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
900
901 $question = self::_instanciateQuestion($origQuestionId);
902
903 foreach($question->getAnswers() as $answer)
904 {
905 $question->removeAnswerImage($answer->getPosition());
906 }
907 }
908
909 protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
910 {
911 parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
912
913 $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
914 }
915
916 protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
917 {
918 global $ilLog;
919
920 $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
921 $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
922
923 foreach($this->getAnswers() as $answer)
924 {
925 $filename = $answer->getImageFile();
926
927 if (strlen($filename))
928 {
929 if (!file_exists($targetPath))
930 {
931 ilUtil::makeDirParents($targetPath);
932 }
933
934 if (!@copy($sourcePath.$filename, $targetPath.$filename))
935 {
936 $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
937 $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
938 }
939
940 if (@file_exists($sourcePath.$this->getThumbPrefix().$filename))
941 {
942 if (!@copy($sourcePath.$this->getThumbPrefix().$filename, $targetPath.$this->getThumbPrefix().$filename))
943 {
944 $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
945 $ilLog->write("object: " . print_r($this, TRUE), $ilLog->ERROR);
946 }
947 }
948 }
949 }
950 }
951
952 protected function getRTETextWithMediaObjects()
953 {
954 $combinedText = parent::getRTETextWithMediaObjects();
955
956 foreach($this->getAnswers() as $answer)
957 {
958 $combinedText .= $answer->getAnswertext();
959 }
960
961 return $combinedText;
962 }
963
967 public function toJSON()
968 {
969 $this->lng->loadLanguageModule('assessment');
970
971 require_once './Services/RTE/classes/class.ilRTE.php';
972 $result = array();
973 $result['id'] = (int) $this->getId();
974 $result['type'] = (string) $this->getQuestionType();
975 $result['title'] = (string) $this->getTitle();
976 $result['question'] = $this->formatSAQuestion($this->getQuestion());
977 $result['instruction'] = $this->getInstructionTextTranslation(
978 $this->lng, $this->getOptionLabel()
979 );
980 $result['nr_of_tries'] = (int) $this->getNrOfTries();
981 $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
982 $result['feedback'] = array(
983 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
984 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
985 );
986
987 $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
988 $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
989
990 $result['num_allowed_failures'] = $this->getNumAllowedFailures();
991
992 $answers = array();
993 $has_image = false;
994
995 foreach( $this->getAnswers() as $key => $answer )
996 {
997 if( strlen((string)$answer->getImageFile()) )
998 {
999 $has_image = true;
1000 }
1001
1002 $answers[] = array(
1003 'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
1004 'correctness' => (bool) $answer->getCorrectness(),
1005 'order' => (int)$answer->getPosition(),
1006 'image' => (string)$answer->getImageFile(),
1007 'feedback' => $this->formatSAQuestion(
1008 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), $key)
1009 )
1010 );
1011 }
1012
1013 $result['answers'] = $answers;
1014
1015 if($has_image)
1016 {
1017 $result['path'] = $this->getImagePathWeb();
1018 $result['thumb'] = $this->getThumbSize();
1019 }
1020
1021 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1022 $result['mobs'] = $mobs;
1023
1024 return json_encode($result);
1025 }
1026
1027 private function getNumAllowedFailures()
1028 {
1029 if( $this->isScorePartialSolutionEnabled() )
1030 {
1031 return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
1032 }
1033
1034 return 0;
1035 }
1036
1037 public static function isObligationPossible($questionId)
1038 {
1039 return true;
1040 }
1041
1042 public function isAnswered($active_id, $pass = null)
1043 {
1044 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1045
1046 return $numExistingSolutionRecords >= 4;
1047 }
1048
1052 public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1053 {
1054 require_once 'Services/Excel/classes/class.ilExcelUtils.php';
1055
1056 $solution = $this->getSolutionValues($active_id, $pass);
1057
1058 $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1059 $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1060 $i = 1;
1061 foreach($this->getAnswers() as $id => $answer)
1062 {
1063 $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($answer->getAnswertext()), $format_bold);
1064 $correctness = FALSE;
1065 foreach($solution as $solutionvalue)
1066 {
1067 if($id == $solutionvalue['value1'])
1068 {
1069 $correctness = $solutionvalue['value2'];
1070 break;
1071 }
1072 }
1073 $worksheet->write($startrow + $i, 1, $correctness);
1074 $i++;
1075 }
1076 return $startrow + $i + 1;
1077 }
1078
1079 public function moveAnswerDown($position)
1080 {
1081 if( $position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1) )
1082 {
1083 return false;
1084 }
1085
1086 for($i = 0, $max = count($this->answers); $i < $max; $i++)
1087 {
1088 if( $i == $position )
1089 {
1090 $movingAnswer = $this->answers[$i];
1091 $targetAnswer = $this->answers[ $i + 1 ];
1092
1093 $movingAnswer->setPosition( $position + 1 );
1094 $targetAnswer->setPosition( $position );
1095
1096 $this->answers[ $i + 1 ] = $movingAnswer;
1097 $this->answers[$i] = $targetAnswer;
1098 }
1099 }
1100 }
1101
1102 public function moveAnswerUp($position)
1103 {
1104 if( $position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1) )
1105 {
1106 return false;
1107 }
1108
1109 for($i = 0, $max = count($this->answers); $i < $max; $i++)
1110 {
1111 if( $i == $position )
1112 {
1113 $movingAnswer = $this->answers[$i];
1114 $targetAnswer = $this->answers[ $i - 1 ];
1115
1116 $movingAnswer->setPosition( $position - 1 );
1117 $targetAnswer->setPosition( $position );
1118
1119 $this->answers[ $i - 1 ] = $movingAnswer;
1120 $this->answers[$i] = $targetAnswer;
1121 }
1122 }
1123
1124 return true;
1125 }
1126}
$result
print $file
$filename
Definition: buildRTE.php:89
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.
setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
{Creates an Excel worksheet for the detailed cumulated results of this question.}
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)
calculateReachedPoints($active_id, $pass=NULL, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
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)
reworkWorkingData($active_id, $pass, $obligationsAnswered)
Reworks the allready saved working data if neccessary.
isValidOptionLabel($optionLabel)
isCustomOptionLabel($labelValue)
isValidAnswerType($answerType)
getInstructionTextTranslation(ilLanguage $lng, $optionLabel)
setOptionLabel($optionLabel)
loadFromDb($questionId)
Loads the question from the database.
cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
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.
& getSolutionValues($active_id, $pass=NULL)
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="")
createNewImageFileName($image_filename, $unique=false)
saveWorkingData($active_id, $pass=NULL)
Saves the learners input of the question to the database.
getId()
Gets the id of the assQuestion object.
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)
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
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.
logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
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)
_convert_text($a_text, $a_target="has been removed")
language handling
_getLogLanguage()
retrieve the log language for assessment logging
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
_getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
_getPass($active_id)
Retrieves the actual pass of a given user for a given test.
_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 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.
$_POST['username']
Definition: cron.php:12
Interface ilObjAnswerScoringAdjustable.
Interface ilObjQuestionScoringAdjustable.
$path
Definition: index.php:22
global $ilDB
$mobs