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