ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assOrderingQuestion.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/Test/classes/inc.AssessmentConstants.php';
6 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
7 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
8 require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
9 require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
10 
25 {
33  var $answers;
34 
44 
50  var $thumb_geometry = 100;
51 
58 
59  public $old_ordering_depth = array();
60  public $leveled_ordering = array();
61 
74  public function __construct(
75  $title = "",
76  $comment = "",
77  $author = "",
78  $owner = -1,
79  $question = "",
81  )
82  {
84  $this->answers = array();
85  $this->ordering_type = $ordering_type;
86  }
87 
93  public function isComplete()
94  {
95  if (
96  strlen($this->title) &&
97  $this->author &&
98  $this->question &&
99  count($this->answers) &&
100  $this->getMaximumPoints() > 0
101  )
102  {
103  return true;
104  }
105  return false;
106  }
107 
115  public function saveToDb($original_id = "")
116  {
117  global $ilDB;
118 
123  }
124 
132  function loadFromDb($question_id)
133  {
134  global $ilDB;
135 
136  $result = $ilDB->queryF("SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
137  array("integer"),
138  array($question_id)
139  );
140  if ($result->numRows() == 1)
141  {
142  $data = $ilDB->fetchAssoc($result);
143  $this->setId($question_id);
144  $this->setObjId($data["obj_fi"]);
145  $this->setTitle($data["title"]);
146  $this->setComment($data["description"]);
147  $this->setOriginalId($data["original_id"]);
148  $this->setAuthor($data["author"]);
149  $this->setNrOfTries($data['nr_of_tries']);
150  $this->setPoints($data["points"]);
151  $this->setOwner($data["owner"]);
152  include_once("./Services/RTE/classes/class.ilRTE.php");
153  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
154  $this->ordering_type = strlen($data["ordering_type"]) ? $data["ordering_type"] : OQ_TERMS;
155  $this->thumb_geometry = $data["thumb_geometry"];
156  $this->element_height = $data["element_height"];
157  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
158 
159  try
160  {
161  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
162  }
164  {
165  }
166  }
167 
168  $result = $ilDB->queryF("SELECT * FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC",
169  array('integer'),
170  array($question_id)
171  );
172 
173  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
174  if ($result->numRows() > 0)
175  {
176  while ($data = $ilDB->fetchAssoc($result))
177  {
178  include_once("./Services/RTE/classes/class.ilRTE.php");
179  $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
180  array_push($this->answers, new ASS_AnswerOrdering($data["answertext"], $data["random_id"], $data['depth'] ? $data['depth'] : 0));
181  }
182  }
183  parent::loadFromDb($question_id);
184  }
185 
191  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
192  {
193  if ($this->id <= 0)
194  {
195  // The question has not been saved. It cannot be duplicated
196  return;
197  }
198  // duplicate the question in database
199  $this_id = $this->getId();
200  $thisObjId = $this->getObjId();
201 
202  $clone = $this;
203  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
205  $clone->id = -1;
206 
207  if( (int)$testObjId > 0 )
208  {
209  $clone->setObjId($testObjId);
210  }
211 
212  if ($title)
213  {
214  $clone->setTitle($title);
215  }
216  if ($author)
217  {
218  $clone->setAuthor($author);
219  }
220  if ($owner)
221  {
222  $clone->setOwner($owner);
223  }
224  if ($for_test)
225  {
226  $clone->saveToDb($original_id);
227  }
228  else
229  {
230  $clone->saveToDb();
231  }
232 
233  // copy question page content
234  $clone->copyPageOfQuestion($this_id);
235  // copy XHTML media objects
236  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
237  // duplicate the image
238  $clone->duplicateImages($this_id, $thisObjId, $clone->getId(), $testObjId);
239 
240  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
241 
242  return $clone->id;
243  }
244 
250  function copyObject($target_questionpool_id, $title = "")
251  {
252  if ($this->id <= 0)
253  {
254  // The question has not been saved. It cannot be duplicated
255  return;
256  }
257  // duplicate the question in database
258  $clone = $this;
259  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
261  $clone->id = -1;
262  $source_questionpool_id = $this->getObjId();
263  $clone->setObjId($target_questionpool_id);
264  if ($title)
265  {
266  $clone->setTitle($title);
267  }
268 
269  $clone->saveToDb();
270 
271  // copy question page content
272  $clone->copyPageOfQuestion($original_id);
273  // copy XHTML media objects
274  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
275  // duplicate the image
276  $clone->copyImages($original_id, $source_questionpool_id);
277 
278  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
279 
280  return $clone->id;
281  }
282 
283  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
284  {
285  if ($this->id <= 0)
286  {
287  // The question has not been saved. It cannot be duplicated
288  return;
289  }
290 
291  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
292 
293  $sourceQuestionId = $this->id;
294  $sourceParentId = $this->getObjId();
295 
296  // duplicate the question in database
297  $clone = $this;
298  $clone->id = -1;
299 
300  $clone->setObjId($targetParentId);
301 
302  if ($targetQuestionTitle)
303  {
304  $clone->setTitle($targetQuestionTitle);
305  }
306 
307  $clone->saveToDb();
308  // copy question page content
309  $clone->copyPageOfQuestion($sourceQuestionId);
310  // copy XHTML media objects
311  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
312  // duplicate the image
313  $clone->copyImages($sourceQuestionId, $sourceParentId);
314 
315  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
316 
317  return $clone->id;
318  }
319 
320  function duplicateImages($src_question_id, $src_object_id, $dest_question_id, $dest_object_id)
321  {
322  global $ilLog;
323  if ($this->getOrderingType() == OQ_PICTURES)
324  {
325  $imagepath_original = $this->getImagePath($src_question_id, $src_object_id);
326  $imagepath = $this->getImagePath($dest_question_id, $dest_object_id);
327 
328  if (!file_exists($imagepath)) {
329  ilUtil::makeDirParents($imagepath);
330  }
331  foreach ($this->answers as $answer)
332  {
333  $filename = $answer->getAnswertext();
334  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
335  {
336  $ilLog->write("image could not be duplicated!!!!");
337  }
338  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
339  {
340  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
341  {
342  $ilLog->write("image thumbnail could not be duplicated!!!!");
343  }
344  }
345  }
346  }
347  }
348 
349  function copyImages($question_id, $source_questionpool)
350  {
351  global $ilLog;
352  if ($this->getOrderingType() == OQ_PICTURES)
353  {
354  $imagepath = $this->getImagePath();
355  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
356  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
357  if (!file_exists($imagepath)) {
358  ilUtil::makeDirParents($imagepath);
359  }
360  foreach ($this->answers as $answer)
361  {
362  $filename = $answer->getAnswertext();
363  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
364  {
365  $ilLog->write("Ordering Question image could not be copied: $imagepath_original$filename");
366  }
367  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
368  {
369  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
370  {
371  $ilLog->write("Ordering Question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
372  }
373  }
374  }
375  }
376  }
377 
386  {
387  $this->ordering_type = $ordering_type;
388  }
389 
397  function getOrderingType()
398  {
399  return $this->ordering_type;
400  }
401 
416  function addAnswer($answertext = "", $solution_order = -1 ,$depth = 0)
417  {
418  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
419  $answer = new ASS_AnswerOrdering($answertext, $this->getRandomID(), $depth);
420  if (($solution_order >= 0) && ($solution_order < count($this->answers)))
421  {
422  $part1 = array_slice($this->answers, 0, $solution_order);
423  $part2 = array_slice($this->answers, $solution_order);
424  $this->answers = array_merge($part1, array($answer), $part2);
425  }
426  else
427  {
428  array_push($this->answers, $answer);
429  }
430  }
431 
432  public function moveAnswerUp($position)
433  {
434  if ($position > 0)
435  {
436  $temp = $this->answers[$position-1];
437  $this->answers[$position-1] = $this->answers[$position];
438  $this->answers[$position] = $temp;
439  }
440  }
441 
442  public function moveAnswerDown($position)
443  {
444  if ($position < count($this->answers)-1)
445  {
446  $temp = $this->answers[$position+1];
447  $this->answers[$position+1] = $this->answers[$position];
448  $this->answers[$position] = $temp;
449  }
450  }
451 
452  protected function getRandomID()
453  {
454  $random_number = mt_rand(1, 100000);
455  $found = true;
456  while ($found)
457  {
458  $found = false;
459  foreach ($this->getAnswers() as $answer)
460  {
461  if ($answer->getRandomID() == $random_number)
462  {
463  $found = true;
464  $random_number++;
465  }
466  }
467  }
468  return $random_number;
469  }
470 
480  function getAnswer($index = 0)
481  {
482  if ($index < 0) return NULL;
483  if (count($this->answers) < 1) return NULL;
484  if ($index >= count($this->answers)) return NULL;
485  return $this->answers[$index];
486  }
487 
496  function deleteAnswer($index = 0)
497  {
498  if ($index < 0)
499  {
500  return;
501  }
502  if (count($this->answers) < 1)
503  {
504  return;
505  }
506  if ($index >= count($this->answers))
507  {
508  return;
509  }
510  unset($this->answers[$index]);
511  $this->answers = array_values($this->answers);
512  for ($i = 0; $i < count($this->answers); $i++)
513  {
514  if ($this->answers[$i]->getOrder() > $index)
515  {
516  $this->answers[$i]->setOrder($i);
517  }
518  }
519  }
520 
527  function flushAnswers()
528  {
529  $this->answers = array();
530  }
531 
539  function getAnswerCount()
540  {
541  return count($this->answers);
542  }
543 
551  {
552  if (count($this->answers) == 0)
553  {
554  $max = 0;
555  }
556  else
557  {
558  $max = $this->answers[0]->getSolutionOrder();
559  }
560  foreach ($this->answers as $key => $value)
561  {
562  if ($value->getSolutionOrder() > $max)
563  {
564  $max = $value->getSolutionOrder();
565  }
566  }
567  return $max;
568  }
569 
580  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
581  {
582  if( $returndetails )
583  {
584  throw new ilTestException('return details not implemented for '.__METHOD__);
585  }
586 
587  global $ilDB;
588 
589  $found_value1 = array();
590  $found_value2 = array();
591  if (is_null($pass))
592  {
593  $pass = $this->getSolutionMaxPass($active_id);
594  }
595  $result = $this->getCurrentSolutionResultSet($active_id, $pass);
596  $user_order = array();
597  $nested_solution = false;
598  while ($data = $ilDB->fetchAssoc($result))
599  {
600  if ((strcmp($data["value1"], "") != 0) && (strcmp($data["value2"], "") != 0))
601  {
602  if(strchr( $data['value2'],':') == true)
603  {
604 // //i.e. "61463:0" = "random_id:depth"
605  $current_solution = explode(':', $data['value2']);
606 
607  $user_order[$current_solution[0]]['index'] = $data["value1"];
608  $user_order[$current_solution[0]]['depth'] = $current_solution[1];
609  $user_order[$current_solution[0]]['random_id'] = $current_solution[0];
610 
611  $nested_solution = true;
612  }
613  else
614  {
615  $user_order[$data["value2"]] = $data["value1"];
616  $nested_solution = false;
617  }
618  }
619  }
620 
621  $points = $this->calculateReachedPointsForSolution($user_order, $nested_solution);
622 
623  return $points;
624  }
625 
627  {
628  $user_order = array();
629  $nested_solution = false;
630  foreach($previewSession->getParticipantsSolution() as $val1 => $val2)
631  {
632  if ((strcmp($val1, "") != 0) && (strcmp($val2, "") != 0))
633  {
634  if(strchr( $val2,':') == true)
635  {
636  $current_solution = explode(':', $val2);
637 
638  $user_order[$current_solution[0]]['index'] = $val1;
639  $user_order[$current_solution[0]]['depth'] = $current_solution[1];
640  $user_order[$current_solution[0]]['random_id'] = $current_solution[0];
641 
642  $nested_solution = true;
643  }
644  else
645  {
646  $user_order[$val2] = $val1;
647  $nested_solution = false;
648  }
649  }
650  }
651 
652  return $this->calculateReachedPointsForSolution($user_order, $nested_solution);
653  }
654 
661  public function getMaximumPoints()
662  {
663  return $this->getPoints();
664  }
665 
666  /*
667  * Returns the encrypted save filename of a matching picture
668  * Images are saved with an encrypted filename to prevent users from
669  * cheating by guessing the solution from the image filename
670  *
671  * @param string $filename Original filename
672  * @return string Encrypted filename
673  */
675  {
676  $extension = "";
677  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches))
678  {
679  $extension = $matches[1];
680  }
681  return md5($filename) . "." . $extension;
682  }
683 
684  protected function cleanImagefiles()
685  {
686  if ($this->getOrderingType() == OQ_PICTURES)
687  {
688  if (@file_exists($this->getImagePath()))
689  {
690  $contents = ilUtil::getDir($this->getImagePath());
691  foreach ($contents as $f)
692  {
693  if (strcmp($f['type'], 'file') == 0)
694  {
695  $found = false;
696  foreach ($this->getAnswers() as $answer)
697  {
698  if (strcmp($f['entry'], $answer->getAnswertext()) == 0) $found = true;
699  if (strcmp($f['entry'], $this->getThumbPrefix() . $answer->getAnswertext()) == 0) $found = true;
700  }
701  if (!$found)
702  {
703  if (@file_exists($this->getImagePath() . $f['entry'])) @unlink($this->getImagePath() . $f['entry']);
704  }
705  }
706  }
707  }
708  }
709  else
710  {
711  if (@file_exists($this->getImagePath()))
712  {
713  ilUtil::delDir($this->getImagePath());
714  }
715  }
716  }
717 
718  /*
719  * Deletes an imagefile from the system if the file is deleted manually
720  *
721  * @param string $filename Image file filename
722  * @return boolean Success
723  */
724  public function deleteImagefile($filename)
725  {
726  $deletename = $$filename;
727  $result = @unlink($this->getImagePath().$deletename);
728  $result = $result & @unlink($this->getImagePath().$this->getThumbPrefix() . $deletename);
729  return $result;
730  }
731 
740  function setImageFile($image_tempfilename, $image_filename, $previous_filename)
741  {
742  $result = TRUE;
743  if (strlen($image_tempfilename))
744  {
745  $image_filename = str_replace(" ", "_", $image_filename);
746  $imagepath = $this->getImagePath();
747  if (!file_exists($imagepath))
748  {
749  ilUtil::makeDirParents($imagepath);
750  }
751  $savename = $image_filename;
752  if (!ilUtil::moveUploadedFile($image_tempfilename, $savename, $imagepath.$savename))
753  {
754  $result = FALSE;
755  }
756  else
757  {
758  // create thumbnail file
759  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
760  ilUtil::convertImage($imagepath.$savename, $thumbpath, "JPEG", $this->getThumbGeometry());
761  }
762  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename)))
763  {
764  $this->deleteImagefile($previous_filename);
765  }
766  }
767  return $result;
768  }
769 
777  function checkSaveData()
778  {
779  $result = true;
780  if ($this->getOutputType() == OUTPUT_JAVASCRIPT)
781  {
782  if (strlen($_POST["orderresult"]))
783  {
784  return $result;
785  }
786  else if(strlen($_POST['answers_ordering']))
787  {
788  $answers_ordering = $_POST['answers_ordering'];
789  $new_hierarchy = json_decode($answers_ordering);
790  $with_random_id = true;
791  $this->setLeveledOrdering($new_hierarchy, $with_random_id);
792 
793  //return value as "random_id:depth"
794  return serialize($this->leveled_ordering);
795  }
796  }
797  $order_values = array();
798  foreach ($_POST as $key => $value)
799  {
800  if (preg_match("/^order_(\d+)/", $key, $matches))
801  {
802  if (strcmp($value, "") != 0)
803  {
804  array_push($order_values, $value);
805  }
806  }
807  }
808  $check_order = array_flip($order_values);
809  if (count($check_order) != count($order_values))
810  {
811  // duplicate order values!!!
812  $result = false;
813  ilUtil::sendInfo($this->lng->txt("duplicate_order_values_entered"), TRUE);
814  }
815  return $result;
816  }
817 
826  public function saveWorkingData($active_id, $pass = NULL)
827  {
828  $saveWorkingDataResult = $this->checkSaveData();
829  if ($saveWorkingDataResult)
830  {
831  if (is_null($pass))
832  {
833  include_once "./Modules/Test/classes/class.ilObjTest.php";
834  $pass = ilObjTest::_getPass($active_id);
835  }
836 
837  $this->getProcessLocker()->requestUserSolutionUpdateLock();
838 
839  $affectedRows = $this->removeCurrentSolution($active_id, $pass);
840 
841  $entered_values = 0;
842  foreach($this->getSolutionSubmit() as $val1 => $val2)
843  {
844  $this->saveCurrentSolution($active_id, $pass, $val1, trim($val2));
845  $entered_values++;
846  }
847 
848  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
849  }
850  if ($entered_values)
851  {
852  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
854  {
855  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
856  }
857  }
858  else
859  {
860  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
862  {
863  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
864  }
865  }
866 
867  return $saveWorkingDataResult;
868  }
869 
870  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
871  {
872  if( $this->checkSaveData() )
873  {
874  $previewSession->setParticipantsSolution($this->getSolutionSubmit());
875  }
876  }
877 
878  public function saveAdditionalQuestionDataToDb()
879  {
881  global $ilDB;
882 
883  // save additional data
884  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
885  array( "integer" ),
886  array( $this->getId() )
887  );
888 
889  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, ordering_type, thumb_geometry, element_height)
890  VALUES (%s, %s, %s, %s)",
891  array( "integer", "text", "integer", "integer" ),
892  array(
893  $this->getId(),
894  $this->ordering_type,
895  $this->getThumbGeometry(),
896  ($this->getElementHeight() > 20) ? $this->getElementHeight() : NULL
897  )
898  );
899  }
900 
901  public function saveAnswerSpecificDataToDb()
902  {
904  global $ilDB;
905 
906  $ilDB->manipulateF( "DELETE FROM qpl_a_ordering WHERE question_fi = %s",
907  array( 'integer' ),
908  array( $this->getId() )
909  );
910 
911  foreach ($this->answers as $key => $value)
912  {
913  $answer_obj = $this->answers[$key];
914  $next_id = $ilDB->nextId( 'qpl_a_ordering' );
915  $ilDB->insert( 'qpl_a_ordering',
916  array(
917  'answer_id' => array( 'integer', $next_id ),
918  'question_fi' => array( 'integer', $this->getId() ),
919 // 'answertext' => array( 'text', ilRTE::_replaceMediaObjectImageSrc( $answer_obj->getAnswertext(), 0 ) ),
920  'answertext' => array( 'text', $answer_obj->getAnswertext()),
921  'solution_order' => array( 'integer', $key ),
922  'random_id' => array( 'integer', $answer_obj->getRandomID() ),
923  'tstamp' => array( 'integer', time() ),
924  'depth' => array( 'integer', $answer_obj->getOrderingDepth() )
925  )
926  );
927  }
928 
929  if ($this->getOrderingType() == OQ_PICTURES)
930  {
931  $this->rebuildThumbnails();
932  $this->cleanImagefiles();
933  }
934  }
935 
944  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
945  {
946  // nothing to rework!
947  }
948 
955  function getQuestionType()
956  {
957  return "assOrderingQuestion";
958  }
959 
967  {
968  return "qpl_qst_ordering";
969  }
970 
978  {
979  return "qpl_a_ordering";
980  }
981 
987  {
989  foreach ($this->answers as $index => $answer)
990  {
991  $answer_obj = $this->answers[$index];
992  $text .= $answer_obj->getAnswertext();
993  }
994  return $text;
995  }
996 
1000  function &getAnswers()
1001  {
1002  return $this->answers;
1003  }
1004 
1011  public function supportsJavascriptOutput()
1012  {
1013  return TRUE;
1014  }
1015 
1016  public function supportsNonJsOutput()
1017  {
1018  return false;
1019  }
1020 
1033  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1034  {
1035  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1036  $solutions = $this->getSolutionValues($active_id, $pass);
1037  $sol = array();
1038  foreach ($solutions as $solution)
1039  {
1040  $sol[$solution["value1"]] = $solution["value2"];
1041  }
1042  asort($sol);
1043  $sol = array_keys($sol);
1044  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1045  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1046  $i = 1;
1047  $answers = $this->getAnswers();
1048  foreach ($sol as $idx)
1049  {
1050  foreach ($solutions as $solution)
1051  {
1052  if ($solution["value1"] == $idx) $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($solution["value2"]));
1053  }
1054  $worksheet->writeString($startrow + $i, 1, ilExcelUtils::_convert_text($answers[$idx]->getAnswertext()));
1055  $i++;
1056  }
1057  return $startrow + $i + 1;
1058  }
1059 
1060  /*
1061  * Get the thumbnail geometry
1062  *
1063  * @return integer Geometry
1064  */
1065  public function getThumbGeometry()
1066  {
1067  return $this->thumb_geometry;
1068  }
1069 
1070  public function getThumbSize()
1071  {
1072  return $this->getThumbGeometry();
1073  }
1074 
1075  /*
1076  * Set the thumbnail geometry
1077  *
1078  * @param integer $a_geometry Geometry
1079  */
1080  public function setThumbGeometry($a_geometry)
1081  {
1082  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1083  }
1084 
1085  /*
1086  * Get the minimum element height
1087  *
1088  * @return integer Height
1089  */
1090  public function getElementHeight()
1091  {
1092  return $this->element_height;
1093  }
1094 
1095  /*
1096  * Set the minimum element height
1097  *
1098  * @param integer $a_height Height
1099  */
1100  public function setElementHeight($a_height)
1101  {
1102  $this->element_height = ($a_height < 20) ? "" : $a_height;
1103  }
1104 
1105  /*
1106  * Rebuild the thumbnail images with a new thumbnail size
1107  */
1108  public function rebuildThumbnails()
1109  {
1110  if ($this->getOrderingType() == OQ_PICTURES || $this->getOrderingType() == OQ_NESTED_PICTURES)
1111  {
1112  foreach ($this->getAnswers() as $answer)
1113  {
1114  $this->generateThumbForFile($this->getImagePath(), $answer->getAnswertext());
1115  }
1116  }
1117  }
1118 
1119  public function getThumbPrefix()
1120  {
1121  return "thumb.";
1122  }
1123 
1124  protected function generateThumbForFile($path, $file)
1125  {
1126  $filename = $path . $file;
1127  if (@file_exists($filename))
1128  {
1129  $thumbpath = $path . $this->getThumbPrefix() . $file;
1130  $path_info = @pathinfo($filename);
1131  $ext = "";
1132  switch (strtoupper($path_info['extension']))
1133  {
1134  case 'PNG':
1135  $ext = 'PNG';
1136  break;
1137  case 'GIF':
1138  $ext = 'GIF';
1139  break;
1140  default:
1141  $ext = 'JPEG';
1142  break;
1143  }
1144  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbGeometry());
1145  }
1146  }
1147 
1151  public function toJSON()
1152  {
1153  include_once("./Services/RTE/classes/class.ilRTE.php");
1154  $result = array();
1155  $result['id'] = (int) $this->getId();
1156  $result['type'] = (string) $this->getQuestionType();
1157  $result['title'] = (string) $this->getTitle();
1158  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1159  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1160  $result['shuffle'] = (bool) true;
1161  $result['points'] = $this->getPoints();
1162  $result['feedback'] = array(
1163  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1164  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1165  );
1166  if ($this->getOrderingType() == OQ_PICTURES)
1167  {
1168  $result['path'] = $this->getImagePathWeb();
1169  }
1170 
1171  $counter = 1;
1172  $answers = array();
1173  foreach ($this->getAnswers() as $answer_obj)
1174  {
1175  $answers[$counter] = $answer_obj->getAnswertext();
1176  $counter++;
1177  }
1178  $answers = $this->pcArrayShuffle($answers);
1179  $arr = array();
1180  foreach ($answers as $order => $answer)
1181  {
1182  array_push($arr, array(
1183  "answertext" => (string) $answer,
1184  "order" => (int) $order
1185  ));
1186  }
1187  $result['answers'] = $arr;
1188 
1189  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1190  $result['mobs'] = $mobs;
1191 
1192  return json_encode($result);
1193  }
1194 
1195  public function removeAnswerImage($index)
1196  {
1197  $answer = $this->answers[$index];
1198  if (is_object($answer))
1199  {
1200  $this->deleteImagefile($answer->getAnswertext());
1201  $answer->setAnswertext('');
1202  }
1203  }
1207  protected function getSolutionSubmit()
1208  {
1209  $solutionSubmit = array();
1210 
1211  if(array_key_exists("orderresult", $_POST))
1212  {
1213  $orderresult = $_POST["orderresult"];
1214  if(strlen($orderresult))
1215  {
1216  $orderarray = explode(":", $orderresult);
1217  $ordervalue = 1;
1218  foreach($orderarray as $index)
1219  {
1220  $idmatch = null;
1221  if(preg_match("/id_(\\d+)/", $index, $idmatch))
1222  {
1223  $randomid = $idmatch[1];
1224  foreach($this->getAnswers() as $answeridx => $answer)
1225  {
1226  if($answer->getRandomID() == $randomid)
1227  {
1228  $solutionSubmit[$answeridx] = $ordervalue;
1229  $ordervalue++;
1230  }
1231  }
1232  }
1233  }
1234  }
1235  }
1236  else if($this->getOrderingType() == OQ_NESTED_TERMS || $this->getOrderingType() == OQ_NESTED_PICTURES)
1237  {
1238  $answers_ordering = $_POST['answers_ordering__participant'];
1239  $user_solution_hierarchy = json_decode($answers_ordering);
1240  $with_random_id = true;
1241  $this->setLeveledOrdering($user_solution_hierarchy, $with_random_id);
1242 
1243  $index = 0;
1244  foreach($this->leveled_ordering as $random_id => $depth)
1245  {
1246  $value_2 = implode(':', array($random_id, $depth));
1247  $solutionSubmit[$index] = $value_2;
1248  $index++;
1249  }
1250  }
1251  else
1252  {
1253  foreach($_POST as $key => $value)
1254  {
1255  $matches = null;
1256  if(preg_match("/^order_(\d+)/", $key, $matches))
1257  {
1258  if(!(preg_match("/initial_value_\d+/", $value)))
1259  {
1260  if(strlen($value))
1261  {
1262  foreach($this->getAnswers() as $answeridx => $answer)
1263  {
1264  if($answer->getRandomID() == $matches[1])
1265  {
1266  $solutionSubmit[$answeridx] = $value;
1267  }
1268  }
1269  }
1270  }
1271  }
1272  }
1273  }
1274 
1275  return $solutionSubmit;
1276  }
1277 
1283  protected function calculateReachedPointsForSolution($user_order, $nested_solution)
1284  {
1285  if($this->getOrderingType() != OQ_NESTED_PICTURES && $this->getOrderingType() != OQ_NESTED_TERMS)
1286  {
1287  ksort($user_order);
1288  $user_order = array_values($user_order);
1289  }
1290 
1291  $points = 0;
1292  $correctcount = 0;
1293 
1294  foreach($this->answers as $index => $answer)
1295  {
1296  if($nested_solution == true)
1297  {
1298  $random_id = $answer->getRandomID();
1299 
1300  if($random_id == $user_order[$random_id]['random_id'] && $answer->getOrderingDepth() == $user_order[$random_id]['depth'] && $index == $user_order[$random_id]['index'])
1301  {
1302  $correctcount++;
1303  }
1304  } else
1305  {
1306  if($index == $user_order[$index])
1307  {
1308  $correctcount++;
1309  }
1310  }
1311  }
1312 
1313  if($correctcount == count($this->answers))
1314  {
1315  $points = $this->getPoints();
1316  return $points;
1317  }
1318  return $points;
1319  }
1320 
1321  /***
1322  * @param object $child
1323  * @param integer $ordering_depth
1324  * @param bool $with_random_id
1325  */
1326  private function getDepthRecursive($child, $ordering_depth, $with_random_id = false)
1327  {
1328  if($with_random_id == true)
1329  {
1330  // for test ouput
1331  if(is_array($child->children))
1332  {
1333  foreach($child->children as $grand_child)
1334  {
1335  $ordering_depth++;
1336  $this->leveled_ordering[$child->id] = $ordering_depth;
1337  $this->getDepthRecursive($grand_child, $ordering_depth, true);
1338  }
1339  }
1340  else
1341  {
1342  $ordering_depth++;
1343  $this->leveled_ordering[$child->id] = $ordering_depth;
1344  }
1345  }
1346  else
1347  {
1348  if(is_array($child->children))
1349  {
1350  foreach($child->children as $grand_child)
1351  {
1352  $ordering_depth++;
1353  $this->leveled_ordering[] = $ordering_depth;
1354  $this->getDepthRecursive($grand_child, $ordering_depth);
1355  }
1356  }
1357  else
1358  {
1359  $ordering_depth++;
1360  $this->leveled_ordering[] = $ordering_depth;
1361  }
1362  }
1363  }
1364 
1365  /***
1366  * @param array $new_hierarchy
1367  * @param bool $with_random_id
1368  */
1369  public function setLeveledOrdering($new_hierarchy, $with_random_id = false)
1370  {
1371  if($with_random_id == true)
1372  {
1373  //for test output
1374  if(is_array($new_hierarchy))
1375  {
1376  foreach($new_hierarchy as $id)
1377  {
1378  $ordering_depth = 0;
1379  $this->leveled_ordering[$id->id] = $ordering_depth;
1380 
1381  if(is_array($id->children))
1382  {
1383  foreach($id->children as $child)
1384  {
1385  $this->getDepthRecursive($child, $ordering_depth, true);
1386  }
1387  }
1388  }
1389  }
1390  }
1391  else
1392  {
1393  if(is_array($new_hierarchy))
1394  {
1395  foreach($new_hierarchy as $id)
1396  {
1397  $ordering_depth = 0;
1398  $this->leveled_ordering[] = $ordering_depth;
1399 
1400  if(is_array($id->children))
1401  {
1402  foreach($id->children as $child)
1403  {
1404  $this->getDepthRecursive($child, $ordering_depth, $with_random_id);
1405  }
1406  }
1407  }
1408  }
1409  }
1410  }
1411  public function getLeveledOrdering()
1412  {
1413  return $this->leveled_ordering;
1414  }
1415 
1416  public function getOldLeveledOrdering()
1417  {
1418  global $ilDB;
1419 
1420  $res = $ilDB->queryF('SELECT depth FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC',
1421  array('integer'), array($this->getId()));
1422  while($row = $ilDB->fetchAssoc($res))
1423  {
1424  $this->old_ordering_depth[] = $row['depth'];
1425  }
1427  }
1428 
1429  /***
1430  * @param integer $a_random_id
1431  * @return integer
1432  */
1433  public function lookupSolutionOrderByRandomid($a_random_id)
1434  {
1435  global $ilDB;
1436 
1437  $res = $ilDB->queryF('SELECT solution_order FROM qpl_a_ordering WHERE random_id = %s',
1438  array('integer'), array($a_random_id));
1439  $row = $ilDB->fetchAssoc($res);
1440 
1441  return $row['solution_order'];
1442  }
1443 
1444  /***
1445  * @param integer $a_random_id
1446  * @return string
1447  */
1448  public function lookupAnswerTextByRandomId($a_random_id)
1449  {
1450  global $ilDB;
1451 
1452  $res = $ilDB->queryF('SELECT answertext FROM qpl_a_ordering WHERE random_id = %s',
1453  array('integer'), array($a_random_id));
1454  $row = $ilDB->fetchAssoc($res);
1455 
1456  return $row['answertext'];
1457  }
1458 
1459  public function updateLeveledOrdering($a_index, $a_answer_text, $a_depth)
1460  {
1461  global $ilDB;
1462 
1463  $ilDB->update('qpl_a_ordering',
1464  array('solution_order'=> array('integer', $a_index),
1465  'depth' => array('integer', $a_depth)),
1466  array('answertext' => array('text', $a_answer_text)));
1467 
1468  return true;
1469  }
1470 
1479  public function getOperators($expression)
1480  {
1481  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1483  }
1484 
1489  public function getExpressionTypes()
1490  {
1491  return array(
1496  );
1497  }
1498 
1507  public function getUserQuestionResult($active_id, $pass)
1508  {
1510  global $ilDB;
1511  $result = new ilUserQuestionResult($this, $active_id, $pass);
1512 
1513  $data = $ilDB->queryF(
1514  "SELECT value1, value2 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = (
1515  SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s
1516  ) ORDER BY value1 ASC ",
1517  array("integer", "integer", "integer","integer", "integer", "integer"),
1518  array($active_id, $pass, $this->getId(), $active_id, $pass, $this->getId())
1519  );
1520 
1521 
1522  $elements = array();
1523  while($row = $ilDB->fetchAssoc($data))
1524  {
1525 
1526  $newKey = explode(":", $row["value2"]);
1527 
1528  foreach($this->getAnswers() as $key => $answer)
1529  {
1530  if($this->getOrderingType() == OQ_TERMS)
1531  {
1532  if($key == $row["value1"])
1533  {
1534  $elements[$key] = $row["value2"];
1535  break;
1536  }
1537  }
1538  else
1539  {
1540  if($answer->getRandomId() == $newKey[0])
1541  {
1542  $elements[$key] = $row["value1"];
1543  break;
1544  }
1545  }
1546  }
1547  }
1548 
1549  ksort($elements);
1550 
1551  foreach(array_values($elements) as $element)
1552  {
1553  $result->addKeyValue($element, $element);
1554  }
1555 
1556  $points = $this->calculateReachedPoints($active_id, $pass);
1557  $max_points = $this->getMaximumPoints();
1558 
1559  $result->setReachedPercentage(($points/$max_points) * 100);
1560 
1561  return $result;
1562  }
1563 
1572  public function getAvailableAnswerOptions($index = null)
1573  {
1574  if($index !== null)
1575  {
1576  return $this->getAnswer($index);
1577  }
1578  else
1579  {
1580  return $this->getAnswers();
1581  }
1582  }
1583 }