ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 
4 require_once 'Modules/TestQuestionPool/classes/class.assQuestion.php';
5 require_once 'Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
6 require_once 'Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
7 require_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 
126  public function setOptionLabel($optionLabel)
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 
151  public function getCustomFalseOptionLabel()
152  {
154  }
155 
157  {
158  $this->specificFeedbackSetting = $specificFeedbackSetting;
159  }
160 
161  public function getSpecificFeedbackSetting()
162  {
164  }
165 
166  public function setAnswers($answers)
167  {
168  $this->answers = $answers;
169  }
170 
171  public function getAnswers()
172  {
173  return $this->answers;
174  }
175 
176  public function getAnswer($position)
177  {
178  foreach ($this->getAnswers() as $answer) {
179  if ($answer->getPosition() == $position) {
180  return $answer;
181  }
182  }
183 
184  return null;
185  }
186 
187  public function addAnswer(ilAssKprimChoiceAnswer $answer)
188  {
189  $this->answers[] = $answer;
190  }
191 
192  public function loadFromDb($questionId)
193  {
194  $res = $this->db->queryF($this->buildQuestionDataQuery(), array('integer'), array($questionId));
195 
196  while ($data = $this->db->fetchAssoc($res)) {
197  $this->setId($questionId);
198 
199  $this->setOriginalId($data['original_id']);
200 
201  $this->setObjId($data['obj_fi']);
202 
203  $this->setTitle($data['title']);
204  $this->setNrOfTries($data['nr_of_tries']);
205  $this->setComment($data['description']);
206  $this->setAuthor($data['author']);
207  $this->setPoints($data['points']);
208  $this->setOwner($data['owner']);
209  $this->setEstimatedWorkingTimeFromDurationString($data['working_time']);
210  $this->setLastChange($data['tstamp']);
211  require_once 'Services/RTE/classes/class.ilRTE.php';
212  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'], 1));
213 
214  $this->setShuffleAnswersEnabled((bool) $data['shuffle_answers']);
215 
216  if ($this->isValidAnswerType($data['answer_type'])) {
217  $this->setAnswerType($data['answer_type']);
218  }
219 
220  if (is_numeric($data['thumb_size'])) {
221  $this->setThumbSize((int) $data['thumb_size']);
222  }
223 
224  if ($this->isValidOptionLabel($data['opt_label'])) {
225  $this->setOptionLabel($data['opt_label']);
226  }
227 
228  $this->setCustomTrueOptionLabel($data['custom_true']);
229  $this->setCustomFalseOptionLabel($data['custom_false']);
230 
231  if ($data['score_partsol'] !== null) {
232  $this->setScorePartialSolutionEnabled((bool) $data['score_partsol']);
233  }
234 
235  if (isset($data['feedback_setting'])) {
236  $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
237  }
238 
239  try {
240  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
241  } catch (ilTestQuestionPoolException $e) {
242  }
243  }
244 
245  $this->loadAnswerData($questionId);
246 
247  parent::loadFromDb($questionId);
248  }
249 
250  private function loadAnswerData($questionId)
251  {
252  global $DIC;
253  $ilDB = $DIC['ilDB'];
254 
255  $res = $this->db->queryF(
256  "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
257  array('integer'),
258  array($questionId)
259  );
260 
261  require_once 'Modules/TestQuestionPool/classes/class.ilAssKprimChoiceAnswer.php';
262  require_once 'Services/RTE/classes/class.ilRTE.php';
263 
264  while ($data = $ilDB->fetchAssoc($res)) {
265  $answer = new ilAssKprimChoiceAnswer();
266 
267  $answer->setPosition($data['position']);
268 
269  $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'], 1));
270 
271  $answer->setImageFile($data['imagefile']);
272  $answer->setThumbPrefix($this->getThumbPrefix());
273  $answer->setImageFsDir($this->getImagePath());
274  $answer->setImageWebDir($this->getImagePathWeb());
275 
276  $answer->setCorrectness($data['correctness']);
277 
278  $this->answers[$answer->getPosition()] = $answer;
279  }
280 
281  for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
282  $answer = new ilAssKprimChoiceAnswer();
283 
284  $answer->setPosition($i);
285 
286  $this->answers[$answer->getPosition()] = $answer;
287  }
288  }
289 
290  public function saveToDb($originalId = '')
291  {
292  $this->saveQuestionDataToDb($originalId);
293 
296 
297  parent::saveToDb($originalId);
298  }
299 
301  {
302  $this->db->replace(
303  $this->getAdditionalTableName(),
304  array(
305  'question_fi' => array('integer', $this->getId())
306  ),
307  array(
308  'shuffle_answers' => array('integer', (int) $this->isShuffleAnswersEnabled()),
309  'answer_type' => array('text', $this->getAnswerType()),
310  'thumb_size' => array('integer', (int) $this->getThumbSize()),
311  'opt_label' => array('text', $this->getOptionLabel()),
312  'custom_true' => array('text', $this->getCustomTrueOptionLabel()),
313  'custom_false' => array('text', $this->getCustomFalseOptionLabel()),
314  'score_partsol' => array('integer', (int) $this->isScorePartialSolutionEnabled()),
315  'feedback_setting' => array('integer', (int) $this->getSpecificFeedbackSetting())
316  )
317  );
318  }
319 
320  public function saveAnswerSpecificDataToDb()
321  {
322  foreach ($this->getAnswers() as $answer) {
323  $this->db->replace(
324  $this->getAnswerTableName(),
325  array(
326  'question_fi' => array('integer', (int) $this->getId()),
327  'position' => array('integer', (int) $answer->getPosition())
328  ),
329  array(
330  'answertext' => array('text', $answer->getAnswertext()),
331  'imagefile' => array('text', $answer->getImageFile()),
332  'correctness' => array('integer', (int) $answer->getCorrectness())
333  )
334  );
335  }
336 
337  $this->rebuildThumbnails();
338  }
339 
340  public function isComplete()
341  {
342  foreach (array($this->title, $this->author, $this->question) as $text) {
343  if (!strlen($text)) {
344  return false;
345  }
346  }
347 
348  if ($this->getMaximumPoints() <= 0) {
349  return false;
350  }
351 
352  foreach ($this->getAnswers() as $answer) {
353  /* @var ilAssKprimChoiceAnswer $answer */
354 
355  if (is_null($answer->getCorrectness())) {
356  return false;
357  }
358 
359  if (!strlen($answer->getAnswertext()) && !strlen($answer->getImageFile())) {
360  return false;
361  }
362  }
363 
364  return true;
365  }
366 
375  public function saveWorkingData($active_id, $pass = null, $authorized = true)
376  {
378  $ilDB = $GLOBALS['DIC']['ilDB'];
379 
380  if (is_null($pass)) {
381  include_once "./Modules/Test/classes/class.ilObjTest.php";
382  $pass = ilObjTest::_getPass($active_id);
383  }
384 
385  $entered_values = 0;
386 
387  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
388  $this->removeCurrentSolution($active_id, $pass, $authorized);
389 
390  $solutionSubmit = $this->getSolutionSubmit();
391 
392  foreach ($solutionSubmit as $answerIndex => $answerValue) {
393  $this->saveCurrentSolution($active_id, $pass, (int) $answerIndex, (int) $answerValue, $authorized);
394  $entered_values++;
395  }
396  });
397 
398  if ($entered_values) {
399  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
401  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
402  }
403  } else {
404  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
406  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
407  }
408  }
409 
410  return true;
411  }
412 
423  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
424  {
425  if ($returndetails) {
426  throw new ilTestException('return details not implemented for ' . __METHOD__);
427  }
428 
429  global $DIC;
430  $ilDB = $DIC['ilDB'];
431 
432  $found_values = array();
433  if (is_null($pass)) {
434  $pass = $this->getSolutionMaxPass($active_id);
435  }
436 
437  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
438 
439  while ($data = $ilDB->fetchAssoc($result)) {
440  $found_values[(int) $data['value1']] = (int) $data['value2'];
441  }
442 
443  $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
444 
445  return $points;
446  }
447 
448  public function getValidAnswerTypes()
449  {
450  return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
451  }
452 
454  {
455  $validTypes = $this->getValidAnswerTypes();
456  return in_array($answerType, $validTypes);
457  }
458 
460  {
462  }
463 
469  {
470  return array(
471  self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
472  self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
473  );
474  }
475 
476  public function getValidOptionLabels()
477  {
478  return array(
479  self::OPTION_LABEL_RIGHT_WRONG,
480  self::OPTION_LABEL_PLUS_MINUS,
481  self::OPTION_LABEL_APPLICABLE_OR_NOT,
482  self::OPTION_LABEL_ADEQUATE_OR_NOT,
483  self::OPTION_LABEL_CUSTOM
484  );
485  }
486 
488  {
489  return array(
490  self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
491  self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
492  self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
493  self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
494  self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
495  );
496  }
497 
499  {
500  $validLabels = $this->getValidOptionLabels();
501  return in_array($optionLabel, $validLabels);
502  }
503 
505  {
506  switch ($optionLabel) {
507  case self::OPTION_LABEL_RIGHT_WRONG:
508  return $lng->txt('option_label_right');
509 
510  case self::OPTION_LABEL_PLUS_MINUS:
511  return $lng->txt('option_label_plus');
512 
513  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
514  return $lng->txt('option_label_applicable');
515 
516  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
517  return $lng->txt('option_label_adequate');
518 
519  case self::OPTION_LABEL_CUSTOM:
520  return $this->getCustomTrueOptionLabel();
521  }
522  }
523 
525  {
526  switch ($optionLabel) {
527  case self::OPTION_LABEL_RIGHT_WRONG:
528  return $lng->txt('option_label_wrong');
529 
530  case self::OPTION_LABEL_PLUS_MINUS:
531  return $lng->txt('option_label_minus');
532 
533  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
534  return $lng->txt('option_label_not_applicable');
535 
536  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
537  return $lng->txt('option_label_not_adequate');
538 
539  case self::OPTION_LABEL_CUSTOM:
540  return $this->getCustomFalseOptionLabel();
541  }
542  }
543 
545  {
546  return sprintf(
547  $lng->txt('kprim_instruction_text'),
550  );
551  }
552 
553  public function isCustomOptionLabel($labelValue)
554  {
555  return $labelValue == self::OPTION_LABEL_CUSTOM;
556  }
557 
558  public function getThumbPrefix()
559  {
560  return self::THUMB_PREFIX;
561  }
562 
563  public function rebuildThumbnails()
564  {
565  if ($this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize()) {
566  foreach ($this->getAnswers() as $answer) {
567  if (strlen($answer->getImageFile())) {
568  $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
569  }
570  }
571  }
572  }
573 
574  protected function generateThumbForFile($path, $file)
575  {
576  $filename = $path . $file;
577  if (@file_exists($filename)) {
578  $thumbpath = $path . $this->getThumbPrefix() . $file;
579  $path_info = @pathinfo($filename);
580  $ext = "";
581  switch (strtoupper($path_info['extension'])) {
582  case 'PNG':
583  $ext = 'PNG';
584  break;
585  case 'GIF':
586  $ext = 'GIF';
587  break;
588  default:
589  $ext = 'JPEG';
590  break;
591  }
592  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
593  }
594  }
595 
597  {
598  foreach ($answers as $answer) {
599  /* @var ilAssKprimChoiceAnswer $answer */
600 
601  if (!isset($files[$answer->getPosition()])) {
602  continue;
603  }
604 
605  $this->handleFileUpload($answer, $files[$answer->getPosition()]);
606  }
607  }
608 
609  private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
610  {
611  $imagePath = $this->getImagePath();
612 
613  if (!file_exists($imagePath)) {
614  ilUtil::makeDirParents($imagePath);
615  }
616 
617  $filename = $this->buildHashedImageFilename($fileData['name'], true);
618 
619  $answer->setImageFsDir($imagePath);
620  $answer->setImageFile($filename);
621 
622  if (!ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath())) {
623  return 2;
624  }
625 
626  return 0;
627  }
628 
629  public function removeAnswerImage($position)
630  {
631  $answer = $this->getAnswer($position);
632 
633  if (file_exists($answer->getImageFsPath())) {
634  ilUtil::delDir($answer->getImageFsPath());
635  }
636 
637  if (file_exists($answer->getThumbFsPath())) {
638  ilUtil::delDir($answer->getThumbFsPath());
639  }
640 
641  $answer->setImageFile(null);
642  }
643 
644  protected function getSolutionSubmit()
645  {
646  $solutionSubmit = array();
647  foreach ($_POST as $key => $value) {
648  $matches = null;
649 
650  if (preg_match("/^kprim_choice_result_(\d+)/", $key, $matches)) {
651  if (strlen($value)) {
652  $solutionSubmit[$matches[1]] = $value;
653  }
654  }
655  }
656  return $solutionSubmit;
657  }
658 
659  protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
660  {
661  $numCorrect = 0;
662 
663  foreach ($this->getAnswers() as $key => $answer) {
664  if (!isset($found_values[$answer->getPosition()])) {
665  continue;
666  }
667 
668  if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
669  $numCorrect++;
670  }
671  }
672 
673  if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
674  $points = $this->getPoints();
675  } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
676  $points = $this->getPoints() / 2;
677  } else {
678  $points = 0;
679  }
680 
681  if ($active_id) {
682  include_once "./Modules/Test/classes/class.ilObjTest.php";
683  $mc_scoring = ilObjTest::_getMCScoring($active_id);
684  if (($mc_scoring == 0) && (count($found_values) == 0)) {
685  $points = 0;
686  }
687  }
688  return $points;
689  }
690 
691  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
692  {
693  if ($this->id <= 0) {
694  // The question has not been saved. It cannot be duplicated
695  return;
696  }
697  // duplicate the question in database
698  $this_id = $this->getId();
699  $thisObjId = $this->getObjId();
700 
701  $clone = $this;
702  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
704  $clone->id = -1;
705 
706  if ((int) $testObjId > 0) {
707  $clone->setObjId($testObjId);
708  }
709 
710  if ($title) {
711  $clone->setTitle($title);
712  }
713 
714  if ($author) {
715  $clone->setAuthor($author);
716  }
717  if ($owner) {
718  $clone->setOwner($owner);
719  }
720 
721  if ($for_test) {
722  $clone->saveToDb($original_id);
723  } else {
724  $clone->saveToDb();
725  }
726 
727  // copy question page content
728  $clone->copyPageOfQuestion($this_id);
729  // copy XHTML media objects
730  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
731  // duplicate the images
732  $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
733 
734  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
735 
736  return $clone->id;
737  }
738 
739  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
740  {
741  if ($this->id <= 0) {
742  // The question has not been saved. It cannot be duplicated
743  return;
744  }
745 
746  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
747 
748  $sourceQuestionId = $this->id;
749  $sourceParentId = $this->getObjId();
750 
751  // duplicate the question in database
752  $clone = $this;
753  $clone->id = -1;
754 
755  $clone->setObjId($targetParentId);
756 
757  if ($targetQuestionTitle) {
758  $clone->setTitle($targetQuestionTitle);
759  }
760 
761  $clone->saveToDb();
762  // copy question page content
763  $clone->copyPageOfQuestion($sourceQuestionId);
764  // copy XHTML media objects
765  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
766  // duplicate the image
767  $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
768 
769  $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
770 
771  return $clone->id;
772  }
773 
777  public function copyObject($target_questionpool_id, $title = "")
778  {
779  if ($this->id <= 0) {
780  // The question has not been saved. It cannot be duplicated
781  return;
782  }
783  // duplicate the question in database
784  $clone = $this;
785  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
787  $clone->id = -1;
788  $source_questionpool_id = $this->getObjId();
789  $clone->setObjId($target_questionpool_id);
790  if ($title) {
791  $clone->setTitle($title);
792  }
793  $clone->saveToDb();
794  // copy question page content
795  $clone->copyPageOfQuestion($original_id);
796  // copy XHTML media objects
797  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
798  // duplicate the image
799  $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
800 
801  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
802 
803  return $clone->id;
804  }
805 
806  protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
807  {
808  parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
809 
810  $question = self::_instanciateQuestion($origQuestionId);
811 
812  foreach ($question->getAnswers() as $answer) {
813  $question->removeAnswerImage($answer->getPosition());
814  }
815  }
816 
817  protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
818  {
819  parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
820 
821  $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
822  }
823 
824  protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
825  {
827  global $DIC;
828  $ilLog = $DIC['ilLog'];
829 
830  $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
831  $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
832 
833  foreach ($this->getAnswers() as $answer) {
834  $filename = $answer->getImageFile();
835 
836  if (strlen($filename)) {
837  if (!file_exists($targetPath)) {
838  ilUtil::makeDirParents($targetPath);
839  }
840 
841  if (file_exists($sourcePath . $filename)) {
842  if (!copy($sourcePath . $filename, $targetPath . $filename)) {
843  $ilLog->warning(sprintf(
844  "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
845  $sourcePath . $filename,
846  $targetPath . $filename,
847  $sourceQuestionId,
848  $targetQuestionId,
849  $sourceParentId,
850  $targetParentId
851  ));
852  }
853  }
854 
855  if (file_exists($sourcePath . $this->getThumbPrefix() . $filename)) {
856  if (!copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename)) {
857  $ilLog->warning(sprintf(
858  "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
859  $sourcePath . $this->getThumbPrefix() . $filename,
860  $targetPath . $this->getThumbPrefix() . $filename,
861  $sourceQuestionId,
862  $targetQuestionId,
863  $sourceParentId,
864  $targetParentId
865  ));
866  }
867  }
868  }
869  }
870  }
871 
872  protected function getRTETextWithMediaObjects()
873  {
874  $combinedText = parent::getRTETextWithMediaObjects();
875 
876  foreach ($this->getAnswers() as $answer) {
877  $combinedText .= $answer->getAnswertext();
878  }
879 
880  return $combinedText;
881  }
882 
887  {
888  foreach ($this->getAnswers() as $answer) {
889  /* @var ilAssKprimChoiceAnswer $answer */
890  $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
891  }
892  }
893 
897  public function toJSON()
898  {
899  $this->lng->loadLanguageModule('assessment');
900 
901  require_once './Services/RTE/classes/class.ilRTE.php';
902  $result = array();
903  $result['id'] = (int) $this->getId();
904  $result['type'] = (string) $this->getQuestionType();
905  $result['title'] = (string) $this->getTitle();
906  $result['question'] = $this->formatSAQuestion($this->getQuestion());
907  $result['instruction'] = $this->getInstructionTextTranslation(
908  $this->lng,
909  $this->getOptionLabel()
910  );
911  $result['nr_of_tries'] = (int) $this->getNrOfTries();
912  $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
913  $result['feedback'] = array(
914  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
915  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
916  );
917 
918  $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
919  $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
920 
921  $result['num_allowed_failures'] = $this->getNumAllowedFailures();
922 
923  $answers = array();
924  $has_image = false;
925 
926  foreach ($this->getAnswers() as $key => $answer) {
927  if (strlen((string) $answer->getImageFile())) {
928  $has_image = true;
929  }
930 
931  $answers[] = array(
932  'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
933  'correctness' => (bool) $answer->getCorrectness(),
934  'order' => (int) $answer->getPosition(),
935  'image' => (string) $answer->getImageFile(),
936  'feedback' => $this->formatSAQuestion(
937  $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
938  )
939  );
940  }
941 
942  $result['answers'] = $answers;
943 
944  if ($has_image) {
945  $result['path'] = $this->getImagePathWeb();
946  $result['thumb'] = $this->getThumbSize();
947  }
948 
949  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
950  $result['mobs'] = $mobs;
951 
952  return json_encode($result);
953  }
954 
955  private function getNumAllowedFailures()
956  {
957  if ($this->isScorePartialSolutionEnabled()) {
958  return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
959  }
960 
961  return 0;
962  }
963 
965  {
966  return 'feedback_correct_kprim';
967  }
968 
969  public static function isObligationPossible($questionId)
970  {
971  return true;
972  }
973 
974  public function isAnswered($active_id, $pass = null)
975  {
976  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
977 
978  return $numExistingSolutionRecords >= 4;
979  }
980 
984  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
985  {
986  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
987 
988  $solution = $this->getSolutionValues($active_id, $pass);
989 
990  $i = 1;
991  foreach ($this->getAnswers() as $id => $answer) {
992  $worksheet->setCell($startrow + $i, 0, $answer->getAnswertext());
993  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
994  $correctness = false;
995  foreach ($solution as $solutionvalue) {
996  if ($id == $solutionvalue['value1']) {
997  $correctness = $solutionvalue['value2'];
998  break;
999  }
1000  }
1001  $worksheet->setCell($startrow + $i, 1, $correctness);
1002  $i++;
1003  }
1004 
1005  return $startrow + $i + 1;
1006  }
1007 
1008  public function moveAnswerDown($position)
1009  {
1010  if ($position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1)) {
1011  return false;
1012  }
1013 
1014  for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1015  if ($i == $position) {
1016  $movingAnswer = $this->answers[$i];
1017  $targetAnswer = $this->answers[ $i + 1 ];
1018 
1019  $movingAnswer->setPosition($position + 1);
1020  $targetAnswer->setPosition($position);
1021 
1022  $this->answers[ $i + 1 ] = $movingAnswer;
1023  $this->answers[$i] = $targetAnswer;
1024  }
1025  }
1026  }
1027 
1028  public function moveAnswerUp($position)
1029  {
1030  if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
1031  return false;
1032  }
1033 
1034  for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
1035  if ($i == $position) {
1036  $movingAnswer = $this->answers[$i];
1037  $targetAnswer = $this->answers[ $i - 1 ];
1038 
1039  $movingAnswer->setPosition($position - 1);
1040  $targetAnswer->setPosition($position);
1041 
1042  $this->answers[ $i - 1 ] = $movingAnswer;
1043  $this->answers[$i] = $targetAnswer;
1044  }
1045  }
1046 
1047  return true;
1048  }
1049 }
getValidOptionLabelsTranslated(ilLanguage $lng)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getId()
Gets the id of the assQuestion object.
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
$path
Definition: aliased.php:25
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
isValidAnswerType($answerType)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
$files
Definition: metarefresh.php:49
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
buildHashedImageFilename($plain_image_filename, $unique=false)
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
$result
setScorePartialSolutionEnabled($scorePartialSolutionEnabled)
getPoints()
Returns the maximum available points for the question.
static isObligationPossible($questionId)
isCustomOptionLabel($labelValue)
global $DIC
Definition: saml.php:7
Abstract basic class which is to be extended by the concrete assessment question type classes...
__construct($title='', $comment='', $author='', $owner=-1, $question='')
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
setId($id=-1)
Sets the id of the assQuestion object.
getImagePathWeb()
Returns the web image path for web accessable images of a question.
const PARTIAL_SCORING_NUM_CORRECT_ANSWERS
removeAnswerImage($position)
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
copyObject($target_questionpool_id, $title="")
Copies an assMultipleChoice object.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setEstimatedWorkingTimeFromDurationString($durationString)
Sets the estimated working time of a question from a given datetime string.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
setCustomTrueOptionLabel($customTrueOptionLabel)
isValidOptionLabel($optionLabel)
loadAnswerData($questionId)
getTrueOptionLabelTranslation(ilLanguage $lng, $optionLabel)
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...
getObjId()
Get the object id of the container object.
beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
addAnswer(ilAssKprimChoiceAnswer $answer)
Base Exception for all Exceptions relating to Modules/Test.
setOptionLabel($optionLabel)
static _getLogLanguage()
retrieve the log language for assessment logging
setAuthor($author="")
Sets the authors name of the assQuestion object.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
foreach($_POST as $key=> $value) $res
isSingleLineAnswerType($answerType)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
$mobs
getAnswerTypeSelectOptions(ilLanguage $lng)
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
$text
Definition: errorreport.php:18
setShuffleAnswersEnabled($shuffleAnswersEnabled)
Interface ilObjAnswerScoringAdjustable.
const OPTION_LABEL_APPLICABLE_OR_NOT
handleFileUploads($answers, $files)
getQuestion()
Gets the question string of the question object.
static convertImage( $a_from, $a_to, $a_target_format="", $a_geometry="", $a_background_color="")
convert image
isAnswered($active_id, $pass=null)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{}
$filename
Definition: buildRTE.php:89
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
setCustomFalseOptionLabel($customFalseOptionLabel)
getFalseOptionLabelTranslation(ilLanguage $lng, $optionLabel)
calculateReachedPointsForSolution($found_values, $active_id=0)
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
removeCurrentSolution($active_id, $pass, $authorized=true)
setSpecificFeedbackSetting($specificFeedbackSetting)
static _getMCScoring($active_id)
Gets the scoring type for multiple choice questions.
buildImagePath($questionId, $parentObjectId)
global $ilDB
setOriginalId($original_id)
generateThumbForFile($path, $file)
setLastChange($lastChange)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
$i
Definition: disco.tpl.php:19
getTitle()
Gets the title string of the assQuestion object.
handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
language handling
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
txt($a_topic, $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
setTitle($title="")
Sets the title string of the assQuestion object.
setObjId($obj_id=0)
Set the object id of the container object.
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
$key
Definition: croninfo.php:18
setComment($comment="")
Sets the comment string of the assQuestion object.
setAnswerType($answerType)
$_POST["username"]
getInstructionTextTranslation(ilLanguage $lng, $optionLabel)
saveToDb($originalId='')
$GLOBALS['JPEG_Segment_Names']
Global Variable: XMP_tag_captions.
toJSON()
Returns a JSON representation of the question.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
$data
Definition: bench.php:6
afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)