ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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  global $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()->requestUserSolutionUpdateLock();
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  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
419 
420  if ($entered_values)
421  {
422  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
424  {
425  $this->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  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
434  }
435  }
436 
437  return true;
438  }
439 
448  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
449  {
450  // nothing to do
451  }
452 
463  public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE)
464  {
465  if( $returndetails )
466  {
467  throw new ilTestException('return details not implemented for '.__METHOD__);
468  }
469 
470  global $ilDB;
471 
472  $found_values = array();
473  if (is_null($pass))
474  {
475  $pass = $this->getSolutionMaxPass($active_id);
476  }
477 
478  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
479 
480  while ($data = $ilDB->fetchAssoc($result))
481  {
482  $found_values[(int)$data['value1']] = (int)$data['value2'];
483  }
484 
485  $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
486 
487  return $points;
488  }
489 
490  public function getValidAnswerTypes()
491  {
492  return array(self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE);
493  }
494 
496  {
497  $validTypes = $this->getValidAnswerTypes();
498  return in_array($answerType, $validTypes);
499  }
500 
502  {
504  }
505 
511  {
512  return array(
513  self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
514  self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
515  );
516  }
517 
518  public function getValidOptionLabels()
519  {
520  return array(
521  self::OPTION_LABEL_RIGHT_WRONG,
522  self::OPTION_LABEL_PLUS_MINUS,
523  self::OPTION_LABEL_APPLICABLE_OR_NOT,
524  self::OPTION_LABEL_ADEQUATE_OR_NOT,
525  self::OPTION_LABEL_CUSTOM
526  );
527  }
528 
530  {
531  return array(
532  self::OPTION_LABEL_RIGHT_WRONG => $lng->txt('option_label_right_wrong'),
533  self::OPTION_LABEL_PLUS_MINUS => $lng->txt('option_label_plus_minus'),
534  self::OPTION_LABEL_APPLICABLE_OR_NOT => $lng->txt('option_label_applicable_or_not'),
535  self::OPTION_LABEL_ADEQUATE_OR_NOT => $lng->txt('option_label_adequate_or_not'),
536  self::OPTION_LABEL_CUSTOM => $lng->txt('option_label_custom')
537  );
538  }
539 
541  {
542  $validLabels = $this->getValidOptionLabels();
543  return in_array($optionLabel, $validLabels);
544  }
545 
547  {
548  switch($optionLabel)
549  {
550  case self::OPTION_LABEL_RIGHT_WRONG:
551  return $lng->txt('option_label_right');
552 
553  case self::OPTION_LABEL_PLUS_MINUS:
554  return $lng->txt('option_label_plus');
555 
556  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
557  return $lng->txt('option_label_applicable');
558 
559  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
560  return $lng->txt('option_label_adequate');
561 
562  case self::OPTION_LABEL_CUSTOM:
563  return $this->getCustomTrueOptionLabel();
564  }
565  }
566 
568  {
569  switch($optionLabel)
570  {
571  case self::OPTION_LABEL_RIGHT_WRONG:
572  return $lng->txt('option_label_wrong');
573 
574  case self::OPTION_LABEL_PLUS_MINUS:
575  return $lng->txt('option_label_minus');
576 
577  case self::OPTION_LABEL_APPLICABLE_OR_NOT:
578  return $lng->txt('option_label_not_applicable');
579 
580  case self::OPTION_LABEL_ADEQUATE_OR_NOT:
581  return $lng->txt('option_label_not_adequate');
582 
583  case self::OPTION_LABEL_CUSTOM:
584  return $this->getCustomFalseOptionLabel();
585  }
586  }
587 
589  {
590  return sprintf(
591  $lng->txt('kprim_instruction_text'),
594  );
595  }
596 
597  public function isCustomOptionLabel($labelValue)
598  {
599  return $labelValue == self::OPTION_LABEL_CUSTOM;
600  }
601 
602  public function getThumbPrefix()
603  {
604  return self::THUMB_PREFIX;
605  }
606 
607  public function rebuildThumbnails()
608  {
609  if( $this->isSingleLineAnswerType($this->getAnswerType()) && $this->getThumbSize() )
610  {
611  foreach ($this->getAnswers() as $answer)
612  {
613  if (strlen($answer->getImageFile()))
614  {
615  $this->generateThumbForFile($answer->getImageFsDir(), $answer->getImageFile());
616  }
617  }
618  }
619  }
620 
621  protected function generateThumbForFile($path, $file)
622  {
623  $filename = $path . $file;
624  if (@file_exists($filename))
625  {
626  $thumbpath = $path . $this->getThumbPrefix() . $file;
627  $path_info = @pathinfo($filename);
628  $ext = "";
629  switch (strtoupper($path_info['extension']))
630  {
631  case 'PNG':
632  $ext = 'PNG';
633  break;
634  case 'GIF':
635  $ext = 'GIF';
636  break;
637  default:
638  $ext = 'JPEG';
639  break;
640  }
641  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbSize());
642  }
643  }
644 
645  public function handleFileUploads($answers, $files)
646  {
647  foreach($answers as $answer)
648  {
649  /* @var ilAssKprimChoiceAnswer $answer */
650 
651  if( !isset($files[$answer->getPosition()]) )
652  {
653  continue;
654  }
655 
656  $this->handleFileUpload($answer, $files[$answer->getPosition()]);
657  }
658  }
659 
660  private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
661  {
662  $imagePath = $this->getImagePath();
663 
664  if( !file_exists($imagePath) )
665  {
666  ilUtil::makeDirParents($imagePath);
667  }
668 
669  $filename = $this->createNewImageFileName($fileData['name'], true);
670 
671  $answer->setImageFsDir($imagePath);
672  $answer->setImageFile($filename);
673 
674  if( !ilUtil::moveUploadedFile($fileData['tmp_name'], $fileData['name'], $answer->getImageFsPath()) )
675  {
676  return 2;
677  }
678 
679  return 0;
680  }
681 
682  public function removeAnswerImage($position)
683  {
684  $answer = $this->getAnswer($position);
685 
686  if( file_exists($answer->getImageFsPath()) )
687  {
688  ilUtil::delDir($answer->getImageFsPath());
689  }
690 
691  if( file_exists($answer->getThumbFsPath()) )
692  {
693  ilUtil::delDir($answer->getThumbFsPath());
694  }
695 
696  $answer->setImageFile(null);
697  }
698 
699  protected function getSolutionSubmit()
700  {
701  $solutionSubmit = array();
702  foreach($_POST as $key => $value)
703  {
704  $matches = null;
705 
706  if(preg_match("/^kprim_choice_result_(\d+)/", $key, $matches))
707  {
708  if(strlen($value))
709  {
710  $solutionSubmit[$matches[1]] = $value;
711  }
712  }
713  }
714  return $solutionSubmit;
715  }
716 
717  protected function calculateReachedPointsForSolution($found_values, $active_id = 0)
718  {
719  $numCorrect = 0;
720 
721  foreach($this->getAnswers() as $key => $answer)
722  {
723  if( !isset($found_values[$answer->getPosition()]) )
724  {
725  continue;
726  }
727 
728  if( $found_values[$answer->getPosition()] == $answer->getCorrectness() )
729  {
730  $numCorrect++;
731  }
732  }
733 
734  if( $numCorrect >= self::NUM_REQUIRED_ANSWERS )
735  {
736  $points = $this->getPoints();
737  }
738  elseif( $this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS )
739  {
740  $points = $this->getPoints() / 2;
741  }
742  else
743  {
744  $points = 0;
745  }
746 
747  if($active_id)
748  {
749  include_once "./Modules/Test/classes/class.ilObjTest.php";
750  $mc_scoring = ilObjTest::_getMCScoring($active_id);
751  if(($mc_scoring == 0) && (count($found_values) == 0))
752  {
753  $points = 0;
754  }
755  }
756  return $points;
757  }
758 
759  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
760  {
761  if ($this->id <= 0)
762  {
763  // The question has not been saved. It cannot be duplicated
764  return;
765  }
766  // duplicate the question in database
767  $this_id = $this->getId();
768  $thisObjId = $this->getObjId();
769 
770  $clone = $this;
771  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
773  $clone->id = -1;
774 
775  if( (int)$testObjId > 0 )
776  {
777  $clone->setObjId($testObjId);
778  }
779 
780  if ($title)
781  {
782  $clone->setTitle($title);
783  }
784 
785  if ($author)
786  {
787  $clone->setAuthor($author);
788  }
789  if ($owner)
790  {
791  $clone->setOwner($owner);
792  }
793 
794  if ($for_test)
795  {
796  $clone->saveToDb($original_id);
797  }
798  else
799  {
800  $clone->saveToDb();
801  }
802 
803  // copy question page content
804  $clone->copyPageOfQuestion($this_id);
805  // copy XHTML media objects
806  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
807  // duplicate the images
808  $clone->cloneAnswerImages($this_id, $thisObjId, $clone->getId(), $clone->getObjId());
809 
810  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
811 
812  return $clone->id;
813  }
814 
815  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
816  {
817  if ($this->id <= 0)
818  {
819  // The question has not been saved. It cannot be duplicated
820  return;
821  }
822 
823  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
824 
825  $sourceQuestionId = $this->id;
826  $sourceParentId = $this->getObjId();
827 
828  // duplicate the question in database
829  $clone = $this;
830  $clone->id = -1;
831 
832  $clone->setObjId($targetParentId);
833 
834  if ($targetQuestionTitle)
835  {
836  $clone->setTitle($targetQuestionTitle);
837  }
838 
839  $clone->saveToDb();
840  // copy question page content
841  $clone->copyPageOfQuestion($sourceQuestionId);
842  // copy XHTML media objects
843  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
844  // duplicate the image
845  $clone->cloneAnswerImages($sourceQuestionId, $sourceParentId, $clone->getId(), $clone->getObjId());
846 
847  $clone->onCopy($sourceParentId, $sourceQuestionId, $targetParentId, $clone->getId());
848 
849  return $clone->id;
850  }
851 
855  public function copyObject($target_questionpool_id, $title = "")
856  {
857  if ($this->id <= 0)
858  {
859  // The question has not been saved. It cannot be duplicated
860  return;
861  }
862  // duplicate the question in database
863  $clone = $this;
864  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
866  $clone->id = -1;
867  $source_questionpool_id = $this->getObjId();
868  $clone->setObjId($target_questionpool_id);
869  if ($title)
870  {
871  $clone->setTitle($title);
872  }
873  $clone->saveToDb();
874  // copy question page content
875  $clone->copyPageOfQuestion($original_id);
876  // copy XHTML media objects
877  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
878  // duplicate the image
879  $clone->cloneAnswerImages($original_id, $source_questionpool_id, $clone->getId(), $clone->getObjId());
880 
881  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
882 
883  return $clone->id;
884  }
885 
886  protected function beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
887  {
888  parent::beforeSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
889 
890  $question = self::_instanciateQuestion($origQuestionId);
891 
892  foreach($question->getAnswers() as $answer)
893  {
894  $question->removeAnswerImage($answer->getPosition());
895  }
896  }
897 
898  protected function afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId)
899  {
900  parent::afterSyncWithOriginal($origQuestionId, $dupQuestionId, $origParentObjId, $dupParentObjId);
901 
902  $this->cloneAnswerImages($dupQuestionId, $dupParentObjId, $origQuestionId, $origParentObjId);
903  }
904 
905  protected function cloneAnswerImages($sourceQuestionId, $sourceParentId, $targetQuestionId, $targetParentId)
906  {
908  global $ilLog;
909 
910  $sourcePath = $this->buildImagePath($sourceQuestionId, $sourceParentId);
911  $targetPath = $this->buildImagePath($targetQuestionId, $targetParentId);
912 
913  foreach($this->getAnswers() as $answer)
914  {
915  $filename = $answer->getImageFile();
916 
917  if (strlen($filename))
918  {
919  if (!file_exists($targetPath))
920  {
921  ilUtil::makeDirParents($targetPath);
922  }
923 
924  if(!@copy($sourcePath . $filename, $targetPath . $filename))
925  {
926  $ilLog->warning(sprintf(
927  "Could not clone source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
928  $sourcePath . $filename, $targetPath . $filename,
929  $sourceQuestionId, $targetQuestionId, $sourceParentId, $targetParentId
930  ));
931  }
932 
933  if(@file_exists($sourcePath . $this->getThumbPrefix() . $filename))
934  {
935  if(!@copy($sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename))
936  {
937  $ilLog->warning(sprintf(
938  "Could not clone thumbnail source image '%s' to '%s' (srcQuestionId: %s|tgtQuestionId: %s|srcParentObjId: %s|tgtParentObjId: %s)",
939  $sourcePath . $this->getThumbPrefix() . $filename, $targetPath . $this->getThumbPrefix() . $filename,
940  $sourceQuestionId, $targetQuestionId, $sourceParentId, $targetParentId
941  ));
942  }
943  }
944  }
945  }
946  }
947 
948  protected function getRTETextWithMediaObjects()
949  {
950  $combinedText = parent::getRTETextWithMediaObjects();
951 
952  foreach($this->getAnswers() as $answer)
953  {
954  $combinedText .= $answer->getAnswertext();
955  }
956 
957  return $combinedText;
958  }
959 
964  {
965  foreach($this->getAnswers() as $answer)
966  {
967  /* @var ilAssKprimChoiceAnswer $answer */
968  $answer->setAnswertext( $migrator->migrateToLmContent($answer->getAnswertext()) );
969  }
970  }
971 
975  public function toJSON()
976  {
977  $this->lng->loadLanguageModule('assessment');
978 
979  require_once './Services/RTE/classes/class.ilRTE.php';
980  $result = array();
981  $result['id'] = (int) $this->getId();
982  $result['type'] = (string) $this->getQuestionType();
983  $result['title'] = (string) $this->getTitle();
984  $result['question'] = $this->formatSAQuestion($this->getQuestion());
985  $result['instruction'] = $this->getInstructionTextTranslation(
986  $this->lng, $this->getOptionLabel()
987  );
988  $result['nr_of_tries'] = (int) $this->getNrOfTries();
989  $result['shuffle'] = (bool) $this->isShuffleAnswersEnabled();
990  $result['feedback'] = array(
991  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
992  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
993  );
994 
995  $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
996  $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
997 
998  $result['num_allowed_failures'] = $this->getNumAllowedFailures();
999 
1000  $answers = array();
1001  $has_image = false;
1002 
1003  foreach( $this->getAnswers() as $key => $answer )
1004  {
1005  if( strlen((string)$answer->getImageFile()) )
1006  {
1007  $has_image = true;
1008  }
1009 
1010  $answers[] = array(
1011  'answertext' => (string) $this->formatSAQuestion($answer->getAnswertext()),
1012  'correctness' => (bool) $answer->getCorrectness(),
1013  'order' => (int)$answer->getPosition(),
1014  'image' => (string)$answer->getImageFile(),
1015  'feedback' => $this->formatSAQuestion(
1016  $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), $key)
1017  )
1018  );
1019  }
1020 
1021  $result['answers'] = $answers;
1022 
1023  if($has_image)
1024  {
1025  $result['path'] = $this->getImagePathWeb();
1026  $result['thumb'] = $this->getThumbSize();
1027  }
1028 
1029  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1030  $result['mobs'] = $mobs;
1031 
1032  return json_encode($result);
1033  }
1034 
1035  private function getNumAllowedFailures()
1036  {
1037  if( $this->isScorePartialSolutionEnabled() )
1038  {
1039  return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
1040  }
1041 
1042  return 0;
1043  }
1044 
1046  {
1047  return 'feedback_correct_kprim';
1048  }
1049 
1050  public static function isObligationPossible($questionId)
1051  {
1052  return true;
1053  }
1054 
1055  public function isAnswered($active_id, $pass = null)
1056  {
1057  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1058 
1059  return $numExistingSolutionRecords >= 4;
1060  }
1061 
1065  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1066  {
1067  require_once 'Services/Excel/classes/class.ilExcelUtils.php';
1068 
1069  $solution = $this->getSolutionValues($active_id, $pass);
1070 
1071  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1072  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1073  $i = 1;
1074  foreach($this->getAnswers() as $id => $answer)
1075  {
1076  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($answer->getAnswertext()), $format_bold);
1077  $correctness = FALSE;
1078  foreach($solution as $solutionvalue)
1079  {
1080  if($id == $solutionvalue['value1'])
1081  {
1082  $correctness = $solutionvalue['value2'];
1083  break;
1084  }
1085  }
1086  $worksheet->write($startrow + $i, 1, $correctness);
1087  $i++;
1088  }
1089  return $startrow + $i + 1;
1090  }
1091 
1092  public function moveAnswerDown($position)
1093  {
1094  if( $position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1) )
1095  {
1096  return false;
1097  }
1098 
1099  for($i = 0, $max = count($this->answers); $i < $max; $i++)
1100  {
1101  if( $i == $position )
1102  {
1103  $movingAnswer = $this->answers[$i];
1104  $targetAnswer = $this->answers[ $i + 1 ];
1105 
1106  $movingAnswer->setPosition( $position + 1 );
1107  $targetAnswer->setPosition( $position );
1108 
1109  $this->answers[ $i + 1 ] = $movingAnswer;
1110  $this->answers[$i] = $targetAnswer;
1111  }
1112  }
1113  }
1114 
1115  public function moveAnswerUp($position)
1116  {
1117  if( $position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1) )
1118  {
1119  return false;
1120  }
1121 
1122  for($i = 0, $max = count($this->answers); $i < $max; $i++)
1123  {
1124  if( $i == $position )
1125  {
1126  $movingAnswer = $this->answers[$i];
1127  $targetAnswer = $this->answers[ $i - 1 ];
1128 
1129  $movingAnswer->setPosition( $position - 1 );
1130  $targetAnswer->setPosition( $position );
1131 
1132  $this->answers[ $i - 1 ] = $movingAnswer;
1133  $this->answers[$i] = $targetAnswer;
1134  }
1135  }
1136 
1137  return true;
1138  }
1139 }
getValidOptionLabelsTranslated(ilLanguage $lng)
static makeDirParents($a_dir)
Create a new directory and all parent directories.
getId()
Gets the id of the assQuestion object.
print $file
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
isValidAnswerType($answerType)
$_POST['username']
Definition: cron.php:12
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
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...
_getPass($active_id)
Retrieves the actual pass of a given user for a given test.
__construct($title='', $comment='', $author='', $owner=-1, $question='')
_convert_text($a_text, $a_target="has been removed")
setId($id=-1)
Sets the id of the assQuestion object.
_getMCScoring($active_id)
Gets the scoring type for multiple choice questions.
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.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true)
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)
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
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)
reworkWorkingData($active_id, $pass, $obligationsAnswered)
Reworks the allready saved working data if neccessary.
setAuthor($author="")
Sets the authors name of the assQuestion object.
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
$data
isSingleLineAnswerType($answerType)
$mobs
getAnswerTypeSelectOptions(ilLanguage $lng)
removeCurrentSolution($active_id, $pass, $authorized=true, $ignoredSolutionIds=array())
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)
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
_getLogLanguage()
retrieve the log language for assessment logging
$filename
Definition: buildRTE.php:89
_getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
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)
setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
{}
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.
setSpecificFeedbackSetting($specificFeedbackSetting)
$path
Definition: index.php:22
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.
logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
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
setComment($comment="")
Sets the comment string of the assQuestion object.
setAnswerType($answerType)
getInstructionTextTranslation(ilLanguage $lng, $optionLabel)
createNewImageFileName($image_filename, $unique=false)
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)