ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
class.assMultipleChoice.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/Test/classes/inc.AssessmentConstants.php';
6require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
7require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
8require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
9require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
10require_once 'Modules/TestQuestionPool/interfaces/interface.ilAssSpecificFeedbackOptionLabelProvider.php';
11
28{
36 public $answers;
37
47
51
53 protected $thumb_size;
54
58 protected $selectionLimit;
59
64 {
65 $this->isSingleline = $isSingleline;
66 }
67
71 public function getIsSingleline()
72 {
74 }
75
79 public function setLastChange($lastChange)
80 {
81 $this->lastChange = $lastChange;
82 }
83
87 public function getLastChange()
88 {
89 return $this->lastChange;
90 }
91
106 public function __construct(
107 $title = "",
108 $comment = "",
109 $author = "",
110 $owner = -1,
111 $question = "",
113 ) {
115 $this->output_type = $output_type;
116 $this->thumb_size = 150;
117 $this->answers = array();
118 $this->shuffle = 1;
119 $this->selectionLimit = null;
120 $this->feedback_setting = 0;
121 }
122
126 public function getSelectionLimit()
127 {
129 }
130
135 {
136 $this->selectionLimit = $selectionLimit;
137 }
138
145 public function isComplete()
146 {
147 if (strlen($this->title) and ($this->author) and ($this->question) and (count($this->answers)) and ($this->getMaximumPoints() > 0)) {
148 return true;
149 } else {
150 return false;
151 }
152 }
153
159 public function saveToDb($original_id = "")
160 {
164
165 $this->ensureNoInvalidObligation($this->getId());
166 parent::saveToDb($original_id);
167 }
168
172 protected function rebuildThumbnails()
173 {
174 if ($this->isSingleline && ($this->getThumbSize())) {
175 foreach ($this->getAnswers() as $answer) {
176 if (strlen($answer->getImage())) {
177 $this->generateThumbForFile($this->getImagePath(), $answer->getImage());
178 }
179 }
180 }
181 }
182
186 public function getThumbPrefix()
187 {
188 return "thumb.";
189 }
190
195 protected function generateThumbForFile($path, $file)
196 {
197 $filename = $path . $file;
198 if (@file_exists($filename)) {
199 $thumbpath = $path . $this->getThumbPrefix() . $file;
200 $path_info = @pathinfo($filename);
201 $ext = "";
202 switch (strtoupper($path_info['extension'])) {
203 case 'PNG':
204 $ext = 'PNG';
205 break;
206 case 'GIF':
207 $ext = 'GIF';
208 break;
209 default:
210 $ext = 'JPEG';
211 break;
212 }
213 ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
214 }
215 }
216
222 public function loadFromDb($question_id)
223 {
224 global $DIC;
225 $ilDB = $DIC['ilDB'];
226 $hasimages = 0;
227
228 $result = $ilDB->queryF(
229 "SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
230 array("integer"),
231 array($question_id)
232 );
233 if ($result->numRows() == 1) {
234 $data = $ilDB->fetchAssoc($result);
235 $this->setId($question_id);
236 $this->setObjId($data["obj_fi"]);
237 $this->setTitle($data["title"]);
238 $this->setNrOfTries($data['nr_of_tries']);
239 $this->setComment($data["description"]);
240 $this->setOriginalId($data["original_id"]);
241 $this->setAuthor($data["author"]);
242 $this->setPoints($data["points"]);
243 $this->setOwner($data["owner"]);
244 include_once("./Services/RTE/classes/class.ilRTE.php");
245 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
246 $shuffle = (is_null($data['shuffle'])) ? true : $data['shuffle'];
247 $this->setShuffle($shuffle);
248 $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
249 $this->setThumbSize($data['thumb_size']);
250 $this->isSingleline = ($data['allow_images']) ? false : true;
251 $this->lastChange = $data['tstamp'];
252 $this->setSelectionLimit((int) $data['selection_limit'] > 0 ? (int) $data['selection_limit'] : null);
253 $this->feedback_setting = $data['feedback_setting'];
254
255 try {
256 $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
259 }
260
261 try {
262 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
264 }
265 }
266
267 $result = $ilDB->queryF(
268 "SELECT * FROM qpl_a_mc WHERE question_fi = %s ORDER BY aorder ASC",
269 array('integer'),
270 array($question_id)
271 );
272 include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
273 if ($result->numRows() > 0) {
274 while ($data = $ilDB->fetchAssoc($result)) {
275 $imagefilename = $this->getImagePath() . $data["imagefile"];
276 if (!@file_exists($imagefilename)) {
277 $data["imagefile"] = "";
278 }
279 include_once("./Services/RTE/classes/class.ilRTE.php");
280 $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
281 array_push($this->answers, new ASS_AnswerMultipleResponseImage($data["answertext"], $data["points"], $data["aorder"], $data["points_unchecked"], $data["imagefile"]));
282 }
283 }
284
285 parent::loadFromDb($question_id);
286 }
287
291 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
292 {
293 if ($this->id <= 0) {
294 // The question has not been saved. It cannot be duplicated
295 return;
296 }
297 // duplicate the question in database
298 $this_id = $this->getId();
299 $thisObjId = $this->getObjId();
300
301 $clone = $this;
302 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
304 $clone->id = -1;
305
306 if ((int) $testObjId > 0) {
307 $clone->setObjId($testObjId);
308 }
309
310 if ($title) {
311 $clone->setTitle($title);
312 }
313
314 if ($author) {
315 $clone->setAuthor($author);
316 }
317 if ($owner) {
318 $clone->setOwner($owner);
319 }
320
321 if ($for_test) {
322 $clone->saveToDb($original_id);
323 } else {
324 $clone->saveToDb();
325 }
326
327 // copy question page content
328 $clone->copyPageOfQuestion($this_id);
329 // copy XHTML media objects
330 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
331 // duplicate the images
332 $clone->duplicateImages($this_id, $thisObjId);
333
334 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
335
336 return $clone->id;
337 }
338
342 public function copyObject($target_questionpool_id, $title = "")
343 {
344 if ($this->id <= 0) {
345 // The question has not been saved. It cannot be duplicated
346 return;
347 }
348 // duplicate the question in database
349 $clone = $this;
350 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
352 $clone->id = -1;
353 $source_questionpool_id = $this->getObjId();
354 $clone->setObjId($target_questionpool_id);
355 if ($title) {
356 $clone->setTitle($title);
357 }
358 $clone->saveToDb();
359 // copy question page content
360 $clone->copyPageOfQuestion($original_id);
361 // copy XHTML media objects
362 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
363 // duplicate the image
364 $clone->copyImages($original_id, $source_questionpool_id);
365
366 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
367
368 return $clone->id;
369 }
370
371 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
372 {
373 if ($this->id <= 0) {
374 // The question has not been saved. It cannot be duplicated
375 return;
376 }
377
378 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
379
380 $sourceQuestionId = $this->id;
381 $sourceParentId = $this->getObjId();
382
383 // duplicate the question in database
384 $clone = $this;
385 $clone->id = -1;
386
387 $clone->setObjId($targetParentId);
388
389 if ($targetQuestionTitle) {
390 $clone->setTitle($targetQuestionTitle);
391 }
392
393 $clone->saveToDb();
394 // copy question page content
395 $clone->copyPageOfQuestion($sourceQuestionId);
396 // copy XHTML media objects
397 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
398 // duplicate the image
399 $clone->copyImages($sourceQuestionId, $sourceParentId);
400
401 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
402
403 return $clone->id;
404 }
405
412 public function getOutputType()
413 {
414 return $this->output_type;
415 }
416
425 {
426 $this->output_type = $output_type;
427 }
428
442 public function addAnswer(
443 $answertext = "",
444 $points = 0.0,
445 $points_unchecked = 0.0,
446 $order = 0,
447 $answerimage = ""
448 ) {
449 include_once "./Modules/TestQuestionPool/classes/class.assAnswerMultipleResponseImage.php";
450 $answertext = $this->getHtmlQuestionContentPurifier()->purify($answertext);
451 if (array_key_exists($order, $this->answers)) {
452 // insert answer
453 $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, $order, $points_unchecked, $answerimage);
454 $newchoices = array();
455 for ($i = 0; $i < $order; $i++) {
456 array_push($newchoices, $this->answers[$i]);
457 }
458 array_push($newchoices, $answer);
459 for ($i = $order; $i < count($this->answers); $i++) {
460 $changed = $this->answers[$i];
461 $changed->setOrder($i + 1);
462 array_push($newchoices, $changed);
463 }
464 $this->answers = $newchoices;
465 } else {
466 // add answer
467 $answer = new ASS_AnswerMultipleResponseImage($answertext, $points, count($this->answers), $points_unchecked, $answerimage);
468 array_push($this->answers, $answer);
469 }
470 }
471
478 public function getAnswerCount()
479 {
480 return count($this->answers);
481 }
482
491 public function getAnswer($index = 0)
492 {
493 if ($index < 0) {
494 return null;
495 }
496 if (count($this->answers) < 1) {
497 return null;
498 }
499 if ($index >= count($this->answers)) {
500 return null;
501 }
502
503 return $this->answers[$index];
504 }
505
513 public function deleteAnswer($index = 0)
514 {
515 if ($index < 0) {
516 return;
517 }
518 if (count($this->answers) < 1) {
519 return;
520 }
521 if ($index >= count($this->answers)) {
522 return;
523 }
524 $answer = $this->answers[$index];
525 if (strlen($answer->getImage())) {
526 $this->deleteImage($answer->getImage());
527 }
528 unset($this->answers[$index]);
529 $this->answers = array_values($this->answers);
530 for ($i = 0; $i < count($this->answers); $i++) {
531 if ($this->answers[$i]->getOrder() > $index) {
532 $this->answers[$i]->setOrder($i);
533 }
534 }
535 }
536
542 public function flushAnswers()
543 {
544 $this->answers = array();
545 }
546
552 public function getMaximumPoints()
553 {
554 $points = 0;
555 $allpoints = 0;
556 foreach ($this->answers as $key => $value) {
557 if ($value->getPoints() > $value->getPointsUnchecked()) {
558 $allpoints += $value->getPoints();
559 } else {
560 $allpoints += $value->getPointsUnchecked();
561 }
562 }
563 return $allpoints;
564 }
565
577 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
578 {
579 if ($returndetails) {
580 throw new ilTestException('return details not implemented for ' . __METHOD__);
581 }
582
583 global $DIC;
584 $ilDB = $DIC['ilDB'];
585
586 $found_values = array();
587 if (is_null($pass)) {
588 $pass = $this->getSolutionMaxPass($active_id);
589 }
590 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
591 while ($data = $ilDB->fetchAssoc($result)) {
592 if (strcmp($data["value1"], "") != 0) {
593 array_push($found_values, $data["value1"]);
594 }
595 }
596
597 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
598
599 return $points;
600 }
601
602 public function validateSolutionSubmit()
603 {
604 $submit = $this->getSolutionSubmit();
605
606 if ($this->getSelectionLimit()) {
607 if (count($submit) > $this->getSelectionLimit()) {
608 $failureMsg = sprintf(
609 $this->lng->txt('ass_mc_sel_lim_exhausted_hint'),
610 $this->getSelectionLimit(),
611 $this->getAnswerCount()
612 );
613
614 ilUtil::sendFailure($failureMsg, true);
615 return false;
616 }
617 }
618
619 return true;
620 }
621
622 protected function isForcedEmptySolution($solutionSubmit)
623 {
624 if (!count($solutionSubmit) && !empty($_POST['tst_force_form_diff_input'])) {
625 return true;
626 }
627
628 return false;
629 }
630
639 public function saveWorkingData($active_id, $pass = null, $authorized = true)
640 {
642 global $DIC;
643 $ilDB = $DIC['ilDB'];
644
645 if (is_null($pass)) {
646 include_once "./Modules/Test/classes/class.ilObjTest.php";
647 $pass = ilObjTest::_getPass($active_id);
648 }
649
650 $entered_values = 0;
651
652 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
653 $this->removeCurrentSolution($active_id, $pass, $authorized);
654
655 $solutionSubmit = $this->getSolutionSubmit();
656
657 foreach ($solutionSubmit as $value) {
658 if (strlen($value)) {
659 $this->saveCurrentSolution($active_id, $pass, $value, null, $authorized);
660 $entered_values++;
661 }
662 }
663
664 // fau: testNav - write a dummy entry for the evil mc questions with "None of the above" checked
665 if ($this->isForcedEmptySolution($solutionSubmit)) {
666 $this->saveCurrentSolution($active_id, $pass, 'mc_none_above', null, $authorized);
667 $entered_values++;
668 }
669 // fau.
670 });
671
672 if ($entered_values) {
673 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
675 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
676 }
677 } else {
678 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
680 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
681 }
682 }
683
684 return true;
685 }
686
687 public function saveAdditionalQuestionDataToDb()
688 {
690 global $DIC;
691 $ilDB = $DIC['ilDB'];
692 $oldthumbsize = 0;
693 if ($this->isSingleline && ($this->getThumbSize())) {
694 // get old thumbnail size
695 $result = $ilDB->queryF(
696 "SELECT thumb_size FROM " . $this->getAdditionalTableName(
697 ) . " WHERE question_fi = %s",
698 array( "integer" ),
699 array( $this->getId() )
700 );
701 if ($result->numRows() == 1) {
702 $data = $ilDB->fetchAssoc($result);
703 $oldthumbsize = $data['thumb_size'];
704 }
705 }
706
707 if (!$this->isSingleline) {
709 }
710
711 // save additional data
712 $ilDB->replace(
713 $this->getAdditionalTableName(),
714 array(
715 'shuffle' => array('text', $this->getShuffle()),
716 'allow_images' => array('text', $this->isSingleline ? 0 : 1),
717 'thumb_size' => array('integer', strlen($this->getThumbSize()) ? $this->getThumbSize() : null),
718 'selection_limit' => array('integer', $this->getSelectionLimit()),
719 'feedback_setting' => array('integer', $this->getSpecificFeedbackSetting())
720 ),
721 array(
722 'question_fi' => array('integer', $this->getId())
723 )
724 );
725 }
726
727 public function saveAnswerSpecificDataToDb()
728 {
730 global $DIC;
731 $ilDB = $DIC['ilDB'];
732 $ilDB->manipulateF(
733 "DELETE FROM qpl_a_mc WHERE question_fi = %s",
734 array( 'integer' ),
735 array( $this->getId() )
736 );
737
738 foreach ($this->answers as $key => $value) {
739 $answer_obj = $this->answers[$key];
740 $next_id = $ilDB->nextId('qpl_a_mc');
741 $ilDB->manipulateF(
742 "INSERT INTO qpl_a_mc (answer_id, question_fi, answertext, points, points_unchecked, aorder, imagefile, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
743 array( 'integer', 'integer', 'text', 'float', 'float', 'integer', 'text', 'integer' ),
744 array(
745 $next_id,
746 $this->getId(),
747 ilRTE::_replaceMediaObjectImageSrc($answer_obj->getAnswertext(), 0),
748 $answer_obj->getPoints(),
749 $answer_obj->getPointsUnchecked(),
750 $answer_obj->getOrder(),
751 $answer_obj->getImage(),
752 time()
753 )
754 );
755 }
756 $this->rebuildThumbnails();
757 }
758
759 public function syncWithOriginal()
760 {
761 if ($this->getOriginalId()) {
762 $this->syncImages();
763 parent::syncWithOriginal();
764 }
765 }
766
772 public function getQuestionType()
773 {
774 return "assMultipleChoice";
775 }
776
782 public function getAdditionalTableName()
783 {
784 return "qpl_qst_mc";
785 }
786
792 public function getAnswerTableName()
793 {
794 return "qpl_a_mc";
795 }
796
804 public function setImageFile($image_filename, $image_tempfilename = "")
805 {
806 $result = 0;
807 if (!empty($image_tempfilename)) {
808 $image_filename = str_replace(" ", "_", $image_filename);
809 $imagepath = $this->getImagePath();
810 if (!file_exists($imagepath)) {
811 ilUtil::makeDirParents($imagepath);
812 }
813 if (!ilUtil::moveUploadedFile($image_tempfilename, $image_filename, $imagepath . $image_filename)) {
814 $result = 2;
815 } else {
816 include_once "./Services/MediaObjects/classes/class.ilObjMediaObject.php";
817 $mimetype = ilObjMediaObject::getMimeType($imagepath . $image_filename);
818 if (!preg_match("/^image/", $mimetype)) {
819 unlink($imagepath . $image_filename);
820 $result = 1;
821 } else {
822 // create thumbnail file
823 if ($this->isSingleline && ($this->getThumbSize())) {
824 $this->generateThumbForFile($imagepath, $image_filename);
825 }
826 }
827 }
828 }
829 return $result;
830 }
831
837 protected function deleteImage($image_filename)
838 {
839 $imagepath = $this->getImagePath();
840 @unlink($imagepath . $image_filename);
841 $thumbpath = $imagepath . $this->getThumbPrefix() . $image_filename;
842 @unlink($thumbpath);
843 }
844
845 public function duplicateImages($question_id, $objectId = null)
846 {
848 global $DIC;
849 $ilLog = $DIC['ilLog'];
850
851 $imagepath = $this->getImagePath();
852 $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
853
854 if ((int) $objectId > 0) {
855 $imagepath_original = str_replace("/$this->obj_id/", "/$objectId/", $imagepath_original);
856 }
857
858 foreach ($this->answers as $answer) {
859 $filename = $answer->getImage();
860 if (strlen($filename)) {
861 if (!file_exists($imagepath)) {
862 ilUtil::makeDirParents($imagepath);
863 }
864
865 if (file_exists($imagepath_original . $filename)) {
866 if (!copy($imagepath_original . $filename, $imagepath . $filename)) {
867 $ilLog->warning(sprintf(
868 "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
869 $imagepath_original . $filename,
870 $imagepath . $filename,
871 $question_id,
872 $this->id,
873 $objectId,
874 $this->obj_id
875 ));
876 }
877 }
878
879 if (file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
880 if (!copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
881 $ilLog->warning(sprintf(
882 "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
883 $imagepath_original . $this->getThumbPrefix() . $filename,
884 $imagepath . $this->getThumbPrefix() . $filename,
885 $question_id,
886 $this->id,
887 $objectId,
888 $this->obj_id
889 ));
890 }
891 }
892 }
893 }
894 }
895
896 public function copyImages($question_id, $source_questionpool)
897 {
898 global $DIC;
899 $ilLog = $DIC['ilLog'];
900 $imagepath = $this->getImagePath();
901 $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
902 $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
903 foreach ($this->answers as $answer) {
904 $filename = $answer->getImage();
905 if (strlen($filename)) {
906 if (!file_exists($imagepath)) {
907 ilUtil::makeDirParents($imagepath);
908 }
909 if (!@copy($imagepath_original . $filename, $imagepath . $filename)) {
910 $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
911 $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
912 }
913 if (@file_exists($imagepath_original . $this->getThumbPrefix() . $filename)) {
914 if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename)) {
915 $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
916 $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
917 }
918 }
919 }
920 }
921 }
922
926 protected function syncImages()
927 {
928 global $DIC;
929 $ilLog = $DIC['ilLog'];
930
931 $imagepath = $this->getImagePath();
932
933 $question_id = $this->getOriginalId();
934 $originalObjId = parent::lookupParentObjId($this->getOriginalId());
935 $imagepath_original = $this->getImagePath($question_id, $originalObjId);
936
937 ilUtil::delDir($imagepath_original);
938 foreach ($this->answers as $answer) {
939 $filename = $answer->getImage();
940 if (strlen($filename)) {
941 if (@file_exists($imagepath . $filename)) {
942 if (!file_exists($imagepath)) {
943 ilUtil::makeDirParents($imagepath);
944 }
945 if (!file_exists($imagepath_original)) {
946 ilUtil::makeDirParents($imagepath_original);
947 }
948 if (!@copy($imagepath . $filename, $imagepath_original . $filename)) {
949 $ilLog->write("image could not be duplicated!!!!", $ilLog->ERROR);
950 $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
951 }
952 }
953 if (@file_exists($imagepath . $this->getThumbPrefix() . $filename)) {
954 if (!@copy($imagepath . $this->getThumbPrefix() . $filename, $imagepath_original . $this->getThumbPrefix() . $filename)) {
955 $ilLog->write("image thumbnail could not be duplicated!!!!", $ilLog->ERROR);
956 $ilLog->write("object: " . print_r($this, true), $ilLog->ERROR);
957 }
958 }
959 }
960 }
961 }
962
967 {
968 $text = parent::getRTETextWithMediaObjects();
969 foreach ($this->answers as $index => $answer) {
970 $text .= $this->feedbackOBJ->getSpecificAnswerFeedbackContent($this->getId(), 0, $index);
971 $answer_obj = $this->answers[$index];
972 $text .= $answer_obj->getAnswertext();
973 }
974 return $text;
975 }
976
980 public function &getAnswers()
981 {
982 return $this->answers;
983 }
984
988 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
989 {
990 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
991
992 $solution = $this->getSolutionValues($active_id, $pass);
993
994 $i = 1;
995 foreach ($this->getAnswers() as $id => $answer) {
996 $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
997 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
998 $checked = false;
999 foreach ($solution as $solutionvalue) {
1000 if ($id == $solutionvalue["value1"]) {
1001 $checked = true;
1002 }
1003 }
1004 if ($checked) {
1005 $worksheet->setCell($startrow + $i, 1, 1);
1006 } else {
1007 $worksheet->setCell($startrow + $i, 1, 0);
1008 }
1009 $i++;
1010 }
1011
1012 return $startrow + $i + 1;
1013 }
1014
1015 public function getThumbSize()
1016 {
1017 return $this->thumb_size;
1018 }
1019
1020 public function setThumbSize($a_size)
1021 {
1022 $this->thumb_size = $a_size;
1023 }
1024
1029 {
1030 foreach ($this->getAnswers() as $answer) {
1031 /* @var ASS_AnswerBinaryStateImage $answer */
1032 $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
1033 }
1034 }
1035
1039 public function toJSON()
1040 {
1041 require_once './Services/RTE/classes/class.ilRTE.php';
1042 $result = array();
1043 $result['id'] = (int) $this->getId();
1044 $result['type'] = (string) $this->getQuestionType();
1045 $result['title'] = (string) $this->getTitle();
1046 $result['question'] = $this->formatSAQuestion($this->getQuestion());
1047 $result['nr_of_tries'] = (int) $this->getNrOfTries();
1048 $result['shuffle'] = (bool) $this->getShuffle();
1049 $result['selection_limit'] = (int) $this->getSelectionLimit();
1050 $result['feedback'] = array(
1051 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1052 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1053 );
1054
1055 $answers = array();
1056 $has_image = false;
1057 foreach ($this->getAnswers() as $key => $answer_obj) {
1058 if ((string) $answer_obj->getImage()) {
1059 $has_image = true;
1060 }
1061 array_push($answers, array(
1062 "answertext" => (string) $this->formatSAQuestion($answer_obj->getAnswertext()),
1063 "points_checked" => (float) $answer_obj->getPointsChecked(),
1064 "points_unchecked" => (float) $answer_obj->getPointsUnchecked(),
1065 "order" => (int) $answer_obj->getOrder(),
1066 "image" => (string) $answer_obj->getImage(),
1067 "feedback" => $this->formatSAQuestion(
1068 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
1069 )
1070 ));
1071 }
1072 $result['answers'] = $answers;
1073
1074 if ($has_image) {
1075 $result['path'] = $this->getImagePathWeb();
1076 $result['thumb'] = $this->getThumbSize();
1077 }
1078
1079 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1080 $result['mobs'] = $mobs;
1081
1082 return json_encode($result);
1083 }
1084
1085 public function removeAnswerImage($index)
1086 {
1087 $answer = $this->answers[$index];
1088 if (is_object($answer)) {
1089 $this->deleteImage($answer->getImage());
1090 $answer->setImage('');
1091 }
1092 }
1093
1095 {
1096 global $DIC;
1097 $ilUser = $DIC['ilUser'];
1098
1099 $multilineAnswerSetting = $ilUser->getPref("tst_multiline_answers");
1100 if ($multilineAnswerSetting != 1) {
1101 $multilineAnswerSetting = 0;
1102 }
1103 return $multilineAnswerSetting;
1104 }
1105
1106 public function setMultilineAnswerSetting($a_setting = 0)
1107 {
1108 global $DIC;
1109 $ilUser = $DIC['ilUser'];
1110 $ilUser->writePref("tst_multiline_answers", $a_setting);
1111 }
1112
1122 public function setSpecificFeedbackSetting($a_feedback_setting)
1123 {
1124 $this->feedback_setting = $a_feedback_setting;
1125 }
1126
1137 {
1138 if ($this->feedback_setting) {
1140 } else {
1141 return 1;
1142 }
1143 }
1144
1146 {
1147 return 'feedback_correct_sc_mc';
1148 }
1149
1161 public function isAnswered($active_id, $pass = null)
1162 {
1163 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1164
1165 return $numExistingSolutionRecords > 0;
1166 }
1167
1179 public static function isObligationPossible($questionId)
1180 {
1182 global $DIC;
1183 $ilDB = $DIC['ilDB'];
1184
1185 $query = "
1186 SELECT SUM(points) points_for_checked_answers
1187 FROM qpl_a_mc
1188 WHERE question_fi = %s AND points > 0
1189 ";
1190
1191 $res = $ilDB->queryF($query, array('integer'), array($questionId));
1192
1193 $row = $ilDB->fetchAssoc($res);
1194
1195 return $row['points_for_checked_answers'] > 0;
1196 }
1197
1206 public function ensureNoInvalidObligation($questionId)
1207 {
1209 global $DIC;
1210 $ilDB = $DIC['ilDB'];
1211
1212 $query = "
1213 SELECT SUM(qpl_a_mc.points) points_for_checked_answers,
1214 test_question_id
1215
1216 FROM tst_test_question
1217
1218 INNER JOIN qpl_a_mc
1219 ON qpl_a_mc.question_fi = tst_test_question.question_fi
1220
1221 WHERE tst_test_question.question_fi = %s
1222 AND tst_test_question.obligatory = 1
1223
1224 GROUP BY test_question_id
1225 ";
1226
1227 $res = $ilDB->queryF($query, array('integer'), array($questionId));
1228
1229 $updateTestQuestionIds = array();
1230
1231 while ($row = $ilDB->fetchAssoc($res)) {
1232 if ($row['points_for_checked_answers'] <= 0) {
1233 $updateTestQuestionIds[] = $row['test_question_id'];
1234 }
1235 }
1236
1237 if (count($updateTestQuestionIds)) {
1238 $test_question_id__IN__updateTestQuestionIds = $ilDB->in(
1239 'test_question_id',
1240 $updateTestQuestionIds,
1241 false,
1242 'integer'
1243 );
1244
1245 $query = "
1246 UPDATE tst_test_question
1247 SET obligatory = 0
1248 WHERE $test_question_id__IN__updateTestQuestionIds
1249 ";
1250
1251 $ilDB->manipulate($query);
1252 }
1253 }
1254
1258 protected function getSolutionSubmit()
1259 {
1260 $solutionSubmit = array();
1261 foreach ($_POST as $key => $value) {
1262 if (preg_match("/^multiple_choice_result_(\d+)/", $key)) {
1263 if (strlen($value)) {
1264 $solutionSubmit[] = $value;
1265 }
1266 }
1267 }
1268 return $solutionSubmit;
1269 }
1270
1276 protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
1277 {
1278 $points = 0;
1279 foreach ($this->answers as $key => $answer) {
1280 if (in_array($key, $found_values)) {
1281 $points += $answer->getPoints();
1282 } else {
1283 $points += $answer->getPointsUnchecked();
1284 }
1285 }
1286 if ($active_id) {
1287 include_once "./Modules/Test/classes/class.ilObjTest.php";
1288 $mc_scoring = ilObjTest::_getMCScoring($active_id);
1289 if (($mc_scoring == 0) && (count($found_values) == 0)) {
1290 $points = 0;
1291 }
1292 }
1293 return $points;
1294 }
1295
1304 public function getOperators($expression)
1305 {
1306 require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1308 }
1309
1314 public function getExpressionTypes()
1315 {
1316 return array(
1321 );
1322 }
1323
1332 public function getUserQuestionResult($active_id, $pass)
1333 {
1335 global $DIC;
1336 $ilDB = $DIC['ilDB'];
1337 $result = new ilUserQuestionResult($this, $active_id, $pass);
1338
1339 $maxStep = $this->lookupMaxStep($active_id, $pass);
1340
1341 if ($maxStep !== null) {
1342 $data = $ilDB->queryF(
1343 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
1344 array("integer", "integer", "integer","integer"),
1345 array($active_id, $pass, $this->getId(), $maxStep)
1346 );
1347 } else {
1348 $data = $ilDB->queryF(
1349 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
1350 array("integer", "integer", "integer"),
1351 array($active_id, $pass, $this->getId())
1352 );
1353 }
1354
1355 while ($row = $ilDB->fetchAssoc($data)) {
1356 $result->addKeyValue($row["value1"], $row["value1"]);
1357 }
1358
1359 $points = $this->calculateReachedPoints($active_id, $pass);
1360 $max_points = $this->getMaximumPoints();
1361
1362 $result->setReachedPercentage(($points / $max_points) * 100);
1363
1364 return $result;
1365 }
1366
1375 public function getAvailableAnswerOptions($index = null)
1376 {
1377 if ($index !== null) {
1378 return $this->getAnswer($index);
1379 } else {
1380 return $this->getAnswers();
1381 }
1382 }
1383
1384 protected function buildTestPresentationConfig()
1385 {
1386 $config = parent::buildTestPresentationConfig();
1387 $config->setUseUnchangedAnswerLabel($this->lng->txt('tst_mc_label_none_above'));
1388 return $config;
1389 }
1390}
$result
$filename
Definition: buildRTE.php:89
$_POST["username"]
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked,...
An exception for terminatinating execution or to throw for unit testing.
Class for multiple choice tests.
buildTestPresentationConfig()
build basic test question configuration instance
syncImages()
Sync images of a MC question on synchronisation with the original question.
toJSON()
Returns a JSON representation of the question.
getSpecificFeedbackSetting()
Gets the current feedback settings in effect for the question.
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{Creates an Excel worksheet for the detailed cumulated results of this question.object}
copyImages($question_id, $source_questionpool)
saveToDb($original_id="")
Saves a assMultipleChoice object to a database.
loadFromDb($question_id)
Loads a assMultipleChoice object from a database.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assMultipleChoiceQuestion.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
getAnswerCount()
Returns the number of answers.
getAnswerTableName()
Returns the name of the answer table in the database.
isForcedEmptySolution($solutionSubmit)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
addAnswer( $answertext="", $points=0.0, $points_unchecked=0.0, $order=0, $answerimage="")
Adds a possible answer for a multiple choice question.
setSpecificFeedbackSetting($a_feedback_setting)
Sets the feedback settings in effect for the question.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
setSelectionLimit($selectionLimit)
calculateReachedPointsForSolution($found_values, $active_id=0)
getOutputType()
Gets the multiple choice output type which is either OUTPUT_ORDER (=0) or OUTPUT_RANDOM (=1).
copyObject($target_questionpool_id, $title="")
Copies an assMultipleChoice object.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
setMultilineAnswerSetting($a_setting=0)
isComplete()
Returns true, if a multiple choice question is complete for use.
flushAnswers()
Deletes all answers.
deleteAnswer($index=0)
Deletes an answer with a given index.
getQuestionType()
Returns the question type of the question.
deleteImage($image_filename)
Deletes an image file.
setImageFile($image_filename, $image_tempfilename="")
Sets the image file and uploads the image to the object's image directory.
__construct( $title="", $comment="", $author="", $owner=-1, $question="", $output_type=OUTPUT_ORDER)
assMultipleChoice constructor
setIsSingleline($isSingleline)
setOutputType($output_type=OUTPUT_ORDER)
Sets the output type of the assMultipleChoice object.
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
getAnswer($index=0)
Returns an answer with a given index.
generateThumbForFile($path, $file)
getExpressionTypes()
Get all available expression types for a specific question.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
rebuildThumbnails()
Rebuild the thumbnail images with a new thumbnail size.
& getAnswers()
Returns a reference to the answers array.
getOperators($expression)
Get all available operations for a specific question.
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.
setShuffle($shuffle=true)
Sets the shuffle flag.
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.
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
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.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second.
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)
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.
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
getShuffle()
Gets the shuffle flag.
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
setQuestion($question="")
Sets the question string of the question object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
static _getLogLanguage()
retrieve the log language for assessment logging
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static getMimeType($a_file, $a_external=null)
get mime type for file
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.
Class ilUserQuestionResult.
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 sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
const OUTPUT_ORDER
Class iQuestionCondition.
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
Interface ilObjAnswerScoringAdjustable.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
Interface ilObjQuestionScoringAdjustable.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
$index
Definition: metadata.php:128
if(!array_key_exists('PATH_INFO', $_SERVER)) $config
Definition: metadata.php:68
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$query
foreach($_POST as $key=> $value) $res
global $ilDB
$data
Definition: storeScorm.php:23
$mobs
$ilUser
Definition: imgupload.php:18
$DIC
Definition: xapitoken.php:46