ILIAS  Release_4_4_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 
23 {
31  var $answers;
32 
42 
48  var $thumb_geometry = 100;
49 
56 
57  public $old_ordering_depth = array();
58  public $leveled_ordering = array();
59 
72  public function __construct(
73  $title = "",
74  $comment = "",
75  $author = "",
76  $owner = -1,
77  $question = "",
79  )
80  {
82  $this->answers = array();
83  $this->ordering_type = $ordering_type;
84  }
85 
91  public function isComplete()
92  {
93  if (
94  strlen($this->title) &&
95  $this->author &&
96  $this->question &&
97  count($this->answers) &&
98  $this->getMaximumPoints() > 0
99  )
100  {
101  return true;
102  }
103  return false;
104  }
105 
113  public function saveToDb($original_id = "")
114  {
115  global $ilDB;
116 
121  }
122 
130  function loadFromDb($question_id)
131  {
132  global $ilDB;
133 
134  $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",
135  array("integer"),
136  array($question_id)
137  );
138  if ($result->numRows() == 1)
139  {
140  $data = $ilDB->fetchAssoc($result);
141  $this->setId($question_id);
142  $this->setObjId($data["obj_fi"]);
143  $this->setTitle($data["title"]);
144  $this->setComment($data["description"]);
145  $this->setOriginalId($data["original_id"]);
146  $this->setAuthor($data["author"]);
147  $this->setNrOfTries($data['nr_of_tries']);
148  $this->setPoints($data["points"]);
149  $this->setOwner($data["owner"]);
150  include_once("./Services/RTE/classes/class.ilRTE.php");
151  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
152  $this->ordering_type = strlen($data["ordering_type"]) ? $data["ordering_type"] : OQ_TERMS;
153  $this->thumb_geometry = $data["thumb_geometry"];
154  $this->element_height = $data["element_height"];
155  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
156 
157  try
158  {
159  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
160  }
162  {
163  }
164  }
165 
166  $result = $ilDB->queryF("SELECT * FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC",
167  array('integer'),
168  array($question_id)
169  );
170 
171  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
172  if ($result->numRows() > 0)
173  {
174  while ($data = $ilDB->fetchAssoc($result))
175  {
176  include_once("./Services/RTE/classes/class.ilRTE.php");
177  $data["answertext"] = ilRTE::_replaceMediaObjectImageSrc($data["answertext"], 1);
178  array_push($this->answers, new ASS_AnswerOrdering($data["answertext"], $data["random_id"], $data['depth'] ? $data['depth'] : 0));
179  }
180  }
181  parent::loadFromDb($question_id);
182  }
183 
189  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
190  {
191  if ($this->id <= 0)
192  {
193  // The question has not been saved. It cannot be duplicated
194  return;
195  }
196  // duplicate the question in database
197  $this_id = $this->getId();
198  $thisObjId = $this->getObjId();
199 
200  $clone = $this;
201  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
203  $clone->id = -1;
204 
205  if( (int)$testObjId > 0 )
206  {
207  $clone->setObjId($testObjId);
208  }
209 
210  if ($title)
211  {
212  $clone->setTitle($title);
213  }
214  if ($author)
215  {
216  $clone->setAuthor($author);
217  }
218  if ($owner)
219  {
220  $clone->setOwner($owner);
221  }
222  if ($for_test)
223  {
224  $clone->saveToDb($original_id);
225  }
226  else
227  {
228  $clone->saveToDb();
229  }
230 
231  // copy question page content
232  $clone->copyPageOfQuestion($this_id);
233  // copy XHTML media objects
234  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
235  // duplicate the image
236  $clone->duplicateImages($this_id, $thisObjId, $clone->getId(), $testObjId);
237 
238  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
239 
240  return $clone->id;
241  }
242 
248  function copyObject($target_questionpool_id, $title = "")
249  {
250  if ($this->id <= 0)
251  {
252  // The question has not been saved. It cannot be duplicated
253  return;
254  }
255  // duplicate the question in database
256  $clone = $this;
257  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
259  $clone->id = -1;
260  $source_questionpool_id = $this->getObjId();
261  $clone->setObjId($target_questionpool_id);
262  if ($title)
263  {
264  $clone->setTitle($title);
265  }
266 
267  $clone->saveToDb();
268 
269  // copy question page content
270  $clone->copyPageOfQuestion($original_id);
271  // copy XHTML media objects
272  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
273  // duplicate the image
274  $clone->copyImages($original_id, $source_questionpool_id);
275 
276  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
277 
278  return $clone->id;
279  }
280 
281  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
282  {
283  if ($this->id <= 0)
284  {
285  // The question has not been saved. It cannot be duplicated
286  return;
287  }
288 
289  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
290 
291  $sourceQuestionId = $this->id;
292  $sourceParentId = $this->getObjId();
293 
294  // duplicate the question in database
295  $clone = $this;
296  $clone->id = -1;
297 
298  $clone->setObjId($targetParentId);
299 
300  if ($targetQuestionTitle)
301  {
302  $clone->setTitle($targetQuestionTitle);
303  }
304 
305  $clone->saveToDb();
306  // copy question page content
307  $clone->copyPageOfQuestion($sourceQuestionId);
308  // copy XHTML media objects
309  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
310  // duplicate the image
311  $clone->copyImages($sourceQuestionId, $sourceParentId);
312 
313  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
314 
315  return $clone->id;
316  }
317 
318  function duplicateImages($src_question_id, $src_object_id, $dest_question_id, $dest_object_id)
319  {
320  global $ilLog;
321  if ($this->getOrderingType() == OQ_PICTURES)
322  {
323  $imagepath_original = $this->getImagePath($src_question_id, $src_object_id);
324  $imagepath = $this->getImagePath($dest_question_id, $dest_object_id);
325 
326  if (!file_exists($imagepath)) {
327  ilUtil::makeDirParents($imagepath);
328  }
329  foreach ($this->answers as $answer)
330  {
331  $filename = $answer->getAnswertext();
332  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
333  {
334  $ilLog->write("image could not be duplicated!!!!");
335  }
336  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
337  {
338  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
339  {
340  $ilLog->write("image thumbnail could not be duplicated!!!!");
341  }
342  }
343  }
344  }
345  }
346 
347  function copyImages($question_id, $source_questionpool)
348  {
349  global $ilLog;
350  if ($this->getOrderingType() == OQ_PICTURES)
351  {
352  $imagepath = $this->getImagePath();
353  $imagepath_original = str_replace("/$this->id/images", "/$question_id/images", $imagepath);
354  $imagepath_original = str_replace("/$this->obj_id/", "/$source_questionpool/", $imagepath_original);
355  if (!file_exists($imagepath)) {
356  ilUtil::makeDirParents($imagepath);
357  }
358  foreach ($this->answers as $answer)
359  {
360  $filename = $answer->getAnswertext();
361  if (!@copy($imagepath_original . $filename, $imagepath . $filename))
362  {
363  $ilLog->write("Ordering Question image could not be copied: $imagepath_original$filename");
364  }
365  if (@file_exists($imagepath_original. $this->getThumbPrefix(). $filename))
366  {
367  if (!@copy($imagepath_original . $this->getThumbPrefix() . $filename, $imagepath . $this->getThumbPrefix() . $filename))
368  {
369  $ilLog->write("Ordering Question image thumbnail could not be copied: $imagepath_original" . $this->getThumbPrefix() . $filename);
370  }
371  }
372  }
373  }
374  }
375 
384  {
385  $this->ordering_type = $ordering_type;
386  }
387 
395  function getOrderingType()
396  {
397  return $this->ordering_type;
398  }
399 
414  function addAnswer($answertext = "", $solution_order = -1 ,$depth = 0)
415  {
416  include_once "./Modules/TestQuestionPool/classes/class.assAnswerOrdering.php";
417  $answer = new ASS_AnswerOrdering($answertext, $this->getRandomID(), $depth);
418  if (($solution_order >= 0) && ($solution_order < count($this->answers)))
419  {
420  $part1 = array_slice($this->answers, 0, $solution_order);
421  $part2 = array_slice($this->answers, $solution_order);
422  $this->answers = array_merge($part1, array($answer), $part2);
423  }
424  else
425  {
426  array_push($this->answers, $answer);
427  }
428  }
429 
430  public function moveAnswerUp($position)
431  {
432  if ($position > 0)
433  {
434  $temp = $this->answers[$position-1];
435  $this->answers[$position-1] = $this->answers[$position];
436  $this->answers[$position] = $temp;
437  }
438  }
439 
440  public function moveAnswerDown($position)
441  {
442  if ($position < count($this->answers)-1)
443  {
444  $temp = $this->answers[$position+1];
445  $this->answers[$position+1] = $this->answers[$position];
446  $this->answers[$position] = $temp;
447  }
448  }
449 
450  protected function getRandomID()
451  {
452  $random_number = mt_rand(1, 100000);
453  $found = true;
454  while ($found)
455  {
456  $found = false;
457  foreach ($this->getAnswers() as $answer)
458  {
459  if ($answer->getRandomID() == $random_number)
460  {
461  $found = true;
462  $random_number++;
463  }
464  }
465  }
466  return $random_number;
467  }
468 
478  function getAnswer($index = 0)
479  {
480  if ($index < 0) return NULL;
481  if (count($this->answers) < 1) return NULL;
482  if ($index >= count($this->answers)) return NULL;
483  return $this->answers[$index];
484  }
485 
494  function deleteAnswer($index = 0)
495  {
496  if ($index < 0)
497  {
498  return;
499  }
500  if (count($this->answers) < 1)
501  {
502  return;
503  }
504  if ($index >= count($this->answers))
505  {
506  return;
507  }
508  unset($this->answers[$index]);
509  $this->answers = array_values($this->answers);
510  for ($i = 0; $i < count($this->answers); $i++)
511  {
512  if ($this->answers[$i]->getOrder() > $index)
513  {
514  $this->answers[$i]->setOrder($i);
515  }
516  }
517  }
518 
525  function flushAnswers()
526  {
527  $this->answers = array();
528  }
529 
537  function getAnswerCount()
538  {
539  return count($this->answers);
540  }
541 
549  {
550  if (count($this->answers) == 0)
551  {
552  $max = 0;
553  }
554  else
555  {
556  $max = $this->answers[0]->getSolutionOrder();
557  }
558  foreach ($this->answers as $key => $value)
559  {
560  if ($value->getSolutionOrder() > $max)
561  {
562  $max = $value->getSolutionOrder();
563  }
564  }
565  return $max;
566  }
567 
578  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
579  {
580  if( $returndetails )
581  {
582  throw new ilTestException('return details not implemented for '.__METHOD__);
583  }
584 
585  global $ilDB;
586 
587  $found_value1 = array();
588  $found_value2 = array();
589  if (is_null($pass))
590  {
591  $pass = $this->getSolutionMaxPass($active_id);
592  }
593  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
594  array('integer','integer','integer'),
595  array($active_id, $this->getId(), $pass)
596  );
597  $user_order = array();
598  $nested_solution = false;
599  while ($data = $ilDB->fetchAssoc($result))
600  {
601  if ((strcmp($data["value1"], "") != 0) && (strcmp($data["value2"], "") != 0))
602  {
603  if(strchr( $data['value2'],':') == true)
604  {
605 // //i.e. "61463:0" = "random_id:depth"
606  $current_solution = explode(':', $data['value2']);
607 
608  $user_order[$current_solution[0]]['index'] = $data["value1"];
609  $user_order[$current_solution[0]]['depth'] = $current_solution[1];
610  $user_order[$current_solution[0]]['random_id'] = $current_solution[0];
611 
612  $nested_solution = true;
613  }
614  else
615  {
616  $user_order[$data["value2"]] = $data["value1"];
617  $nested_solution = false;
618  }
619  }
620  }
621 
622  if($this->getOrderingType() != OQ_NESTED_PICTURES
623  && $this->getOrderingType() != OQ_NESTED_TERMS)
624  {
625  ksort($user_order);
626  $user_order = array_values($user_order);
627  }
628 
629  $points = 0;
630  $correctcount = 0;
631 
632  foreach ($this->answers as $index => $answer)
633  {
634  if($nested_solution == true)
635  {
636  $random_id = $answer->getRandomID();
637 
638  if($random_id == $user_order[$random_id]['random_id']
639  && $answer->getOrderingDepth() == $user_order[$random_id]['depth']
640  && $index == $user_order[$random_id]['index'])
641  {
642  $correctcount++;
643  }
644  }
645  else
646  {
647  if ($index == $user_order[$index])
648  {
649  $correctcount++;
650  }
651  }
652  }
653 
654  if ($correctcount == count($this->answers))
655  {
656  $points = $this->getPoints();
657  }
658 
659  return $points;
660  }
661 
668  public function getMaximumPoints()
669  {
670  return $this->getPoints();
671  }
672 
673  /*
674  * Returns the encrypted save filename of a matching picture
675  * Images are saved with an encrypted filename to prevent users from
676  * cheating by guessing the solution from the image filename
677  *
678  * @param string $filename Original filename
679  * @return string Encrypted filename
680  */
682  {
683  $extension = "";
684  if (preg_match("/.*\\.(\\w+)$/", $filename, $matches))
685  {
686  $extension = $matches[1];
687  }
688  return md5($filename) . "." . $extension;
689  }
690 
691  protected function cleanImagefiles()
692  {
693  if ($this->getOrderingType() == OQ_PICTURES)
694  {
695  if (@file_exists($this->getImagePath()))
696  {
697  $contents = ilUtil::getDir($this->getImagePath());
698  foreach ($contents as $f)
699  {
700  if (strcmp($f['type'], 'file') == 0)
701  {
702  $found = false;
703  foreach ($this->getAnswers() as $answer)
704  {
705  if (strcmp($f['entry'], $answer->getAnswertext()) == 0) $found = true;
706  if (strcmp($f['entry'], $this->getThumbPrefix() . $answer->getAnswertext()) == 0) $found = true;
707  }
708  if (!$found)
709  {
710  if (@file_exists($this->getImagePath() . $f['entry'])) @unlink($this->getImagePath() . $f['entry']);
711  }
712  }
713  }
714  }
715  }
716  else
717  {
718  if (@file_exists($this->getImagePath()))
719  {
720  ilUtil::delDir($this->getImagePath());
721  }
722  }
723  }
724 
725  /*
726  * Deletes an imagefile from the system if the file is deleted manually
727  *
728  * @param string $filename Image file filename
729  * @return boolean Success
730  */
731  public function deleteImagefile($filename)
732  {
733  $deletename = $$filename;
734  $result = @unlink($this->getImagePath().$deletename);
735  $result = $result & @unlink($this->getImagePath().$this->getThumbPrefix() . $deletename);
736  return $result;
737  }
738 
747  function setImageFile($image_tempfilename, $image_filename, $previous_filename)
748  {
749  $result = TRUE;
750  if (strlen($image_tempfilename))
751  {
752  $image_filename = str_replace(" ", "_", $image_filename);
753  $imagepath = $this->getImagePath();
754  if (!file_exists($imagepath))
755  {
756  ilUtil::makeDirParents($imagepath);
757  }
758  $savename = $image_filename;
759  if (!ilUtil::moveUploadedFile($image_tempfilename, $savename, $imagepath.$savename))
760  {
761  $result = FALSE;
762  }
763  else
764  {
765  // create thumbnail file
766  $thumbpath = $imagepath . $this->getThumbPrefix() . $savename;
767  ilUtil::convertImage($imagepath.$savename, $thumbpath, "JPEG", $this->getThumbGeometry());
768  }
769  if ($result && (strcmp($image_filename, $previous_filename) != 0) && (strlen($previous_filename)))
770  {
771  $this->deleteImagefile($previous_filename);
772  }
773  }
774  return $result;
775  }
776 
784  function checkSaveData()
785  {
786  $result = true;
787  if ($this->getOutputType() == OUTPUT_JAVASCRIPT)
788  {
789  if (strlen($_POST["orderresult"]))
790  {
791  return $result;
792  }
793  else if(strlen($_POST['answers_ordering']))
794  {
795  $answers_ordering = $_POST['answers_ordering'];
796  $new_hierarchy = json_decode($answers_ordering);
797  $with_random_id = true;
798  $this->setLeveledOrdering($new_hierarchy, $with_random_id);
799 
800  //return value as "random_id:depth"
801  return serialize($this->leveled_ordering);
802  }
803  }
804  $order_values = array();
805  foreach ($_POST as $key => $value)
806  {
807  if (preg_match("/^order_(\d+)/", $key, $matches))
808  {
809  if (strcmp($value, "") != 0)
810  {
811  array_push($order_values, $value);
812  }
813  }
814  }
815  $check_order = array_flip($order_values);
816  if (count($check_order) != count($order_values))
817  {
818  // duplicate order values!!!
819  $result = false;
820  ilUtil::sendInfo($this->lng->txt("duplicate_order_values_entered"), TRUE);
821  }
822  return $result;
823  }
824 
833  public function saveWorkingData($active_id, $pass = NULL)
834  {
835  global $ilDB;
836  global $ilUser;
837 
838  $saveWorkingDataResult = $this->checkSaveData();
839  $entered_values = 0;
840  if ($saveWorkingDataResult)
841  {
842  if (is_null($pass))
843  {
844  include_once "./Modules/Test/classes/class.ilObjTest.php";
845  $pass = ilObjTest::_getPass($active_id);
846  }
847 
848  $this->getProcessLocker()->requestUserSolutionUpdateLock();
849 
850  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
851  array('integer','integer','integer'),
852  array($active_id, $this->getId(), $pass)
853  );
854  if (array_key_exists("orderresult", $_POST))
855  {
856  $orderresult = $_POST["orderresult"];
857  if (strlen($orderresult))
858  {
859  $orderarray = explode(":", $orderresult);
860  $ordervalue = 1;
861  foreach ($orderarray as $index)
862  {
863  if (preg_match("/id_(\\d+)/", $index, $idmatch))
864  {
865  $randomid = $idmatch[1];
866  foreach ($this->getAnswers() as $answeridx => $answer)
867  {
868  if ($answer->getRandomID() == $randomid)
869  {
870  $next_id = $ilDB->nextId('tst_solutions');
871  $affectedRows = $ilDB->insert("tst_solutions", array(
872  "solution_id" => array("integer", $next_id),
873  "active_fi" => array("integer", $active_id),
874  "question_fi" => array("integer", $this->getId()),
875  "value1" => array("clob", $answeridx),
876  "value2" => array("clob", trim($ordervalue)),
877  "pass" => array("integer", $pass),
878  "tstamp" => array("integer", time())
879  ));
880  $ordervalue++;
881  $entered_values++;
882  }
883  }
884  }
885  }
886  }
887  }
888  else if($this->getOrderingType() == OQ_NESTED_TERMS
889  ||$this->getOrderingType() == OQ_NESTED_PICTURES)
890  {
891  $answers_ordering = $_POST['answers_ordering__participant'];
892  $user_solution_hierarchy = json_decode($answers_ordering);
893  $with_random_id = true;
894  $this->setLeveledOrdering($user_solution_hierarchy, $with_random_id);
895 
896  $index = 0;
897  foreach($this->leveled_ordering as $random_id=>$depth)
898  {
899  $value_2 = implode(':',array($random_id,$depth));
900  $next_id = $ilDB->nextId('tst_solutions');
901 
902  $affectedRows = $ilDB->insert("tst_solutions", array(
903  "solution_id" => array("integer", $next_id),
904  "active_fi" => array("integer", $active_id),
905  "question_fi" => array("integer", $this->getId()),
906  "value1" => array("clob", $index),
907  "value2" => array("clob", $value_2),
908  "pass" => array("integer", $pass),
909  "tstamp" => array("integer", time())
910  ));
911  $index++;
912  $entered_values++;
913  }
914  }
915  else
916  {
917  foreach ($_POST as $key => $value)
918  {
919  if (preg_match("/^order_(\d+)/", $key, $matches))
920  {
921  if (!(preg_match("/initial_value_\d+/", $value)))
922  {
923  if (strlen($value))
924  {
925  foreach ($this->getAnswers() as $answeridx => $answer)
926  {
927  if ($answer->getRandomID() == $matches[1])
928  {
929  $next_id = $ilDB->nextId('tst_solutions');
930  $affectedRows = $ilDB->insert("tst_solutions", array(
931  "solution_id" => array("integer", $next_id),
932  "active_fi" => array("integer", $active_id),
933  "question_fi" => array("integer", $this->getId()),
934  "value1" => array("clob", $answeridx),
935  "value2" => array("clob", $value),
936  "pass" => array("integer", $pass),
937  "tstamp" => array("integer", time())
938  ));
939  $entered_values++;
940  }
941  }
942  }
943  }
944  }
945  }
946  }
947 
948  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
949  }
950  if ($entered_values)
951  {
952  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
954  {
955  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
956  }
957  }
958  else
959  {
960  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
962  {
963  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
964  }
965  }
966 
967  return $saveWorkingDataResult;
968  }
969 
970  public function saveAdditionalQuestionDataToDb()
971  {
973  global $ilDB;
974 
975  // save additional data
976  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
977  array( "integer" ),
978  array( $this->getId() )
979  );
980 
981  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, ordering_type, thumb_geometry, element_height)
982  VALUES (%s, %s, %s, %s)",
983  array( "integer", "text", "integer", "integer" ),
984  array(
985  $this->getId(),
986  $this->ordering_type,
987  $this->getThumbGeometry(),
988  ($this->getElementHeight() > 20) ? $this->getElementHeight() : NULL
989  )
990  );
991  }
992 
993  public function saveAnswerSpecificDataToDb()
994  {
996  global $ilDB;
997 
998  $ilDB->manipulateF( "DELETE FROM qpl_a_ordering WHERE question_fi = %s",
999  array( 'integer' ),
1000  array( $this->getId() )
1001  );
1002 
1003  foreach ($this->answers as $key => $value)
1004  {
1005  $answer_obj = $this->answers[$key];
1006  $next_id = $ilDB->nextId( 'qpl_a_ordering' );
1007  $ilDB->insert( 'qpl_a_ordering',
1008  array(
1009  'answer_id' => array( 'integer', $next_id ),
1010  'question_fi' => array( 'integer', $this->getId() ),
1011 // 'answertext' => array( 'text', ilRTE::_replaceMediaObjectImageSrc( $answer_obj->getAnswertext(), 0 ) ),
1012  'answertext' => array( 'text', $answer_obj->getAnswertext()),
1013  'solution_order' => array( 'integer', $key ),
1014  'random_id' => array( 'integer', $answer_obj->getRandomID() ),
1015  'tstamp' => array( 'integer', time() ),
1016  'depth' => array( 'integer', $answer_obj->getOrderingDepth() )
1017  )
1018  );
1019  }
1020 
1021  if ($this->getOrderingType() == OQ_PICTURES)
1022  {
1023  $this->rebuildThumbnails();
1024  $this->cleanImagefiles();
1025  }
1026  }
1027 
1036  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
1037  {
1038  // nothing to rework!
1039  }
1040 
1047  function getQuestionType()
1048  {
1049  return "assOrderingQuestion";
1050  }
1051 
1059  {
1060  return "qpl_qst_ordering";
1061  }
1062 
1070  {
1071  return "qpl_a_ordering";
1072  }
1073 
1079  {
1081  foreach ($this->answers as $index => $answer)
1082  {
1083  $answer_obj = $this->answers[$index];
1084  $text .= $answer_obj->getAnswertext();
1085  }
1086  return $text;
1087  }
1088 
1092  function &getAnswers()
1093  {
1094  return $this->answers;
1095  }
1096 
1103  public function supportsJavascriptOutput()
1104  {
1105  return TRUE;
1106  }
1107 
1108  public function supportsNonJsOutput()
1109  {
1110  return false;
1111  }
1112 
1125  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1126  {
1127  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1128  $solutions = $this->getSolutionValues($active_id, $pass);
1129  $sol = array();
1130  foreach ($solutions as $solution)
1131  {
1132  $sol[$solution["value1"]] = $solution["value2"];
1133  }
1134  asort($sol);
1135  $sol = array_keys($sol);
1136  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1137  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1138  $i = 1;
1139  $answers = $this->getAnswers();
1140  foreach ($sol as $idx)
1141  {
1142  foreach ($solutions as $solution)
1143  {
1144  if ($solution["value1"] == $idx) $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($solution["value2"]));
1145  }
1146  $worksheet->writeString($startrow + $i, 1, ilExcelUtils::_convert_text($answers[$idx]->getAnswertext()));
1147  $i++;
1148  }
1149  return $startrow + $i + 1;
1150  }
1151 
1152  /*
1153  * Get the thumbnail geometry
1154  *
1155  * @return integer Geometry
1156  */
1157  public function getThumbGeometry()
1158  {
1159  return $this->thumb_geometry;
1160  }
1161 
1162  public function getThumbSize()
1163  {
1164  return $this->getThumbGeometry();
1165  }
1166 
1167  /*
1168  * Set the thumbnail geometry
1169  *
1170  * @param integer $a_geometry Geometry
1171  */
1172  public function setThumbGeometry($a_geometry)
1173  {
1174  $this->thumb_geometry = ($a_geometry < 1) ? 100 : $a_geometry;
1175  }
1176 
1177  /*
1178  * Get the minimum element height
1179  *
1180  * @return integer Height
1181  */
1182  public function getElementHeight()
1183  {
1184  return $this->element_height;
1185  }
1186 
1187  /*
1188  * Set the minimum element height
1189  *
1190  * @param integer $a_height Height
1191  */
1192  public function setElementHeight($a_height)
1193  {
1194  $this->element_height = ($a_height < 20) ? "" : $a_height;
1195  }
1196 
1197  /*
1198  * Rebuild the thumbnail images with a new thumbnail size
1199  */
1200  public function rebuildThumbnails()
1201  {
1202  if ($this->getOrderingType() == OQ_PICTURES || $this->getOrderingType() == OQ_NESTED_PICTURES)
1203  {
1204  foreach ($this->getAnswers() as $answer)
1205  {
1206  $this->generateThumbForFile($this->getImagePath(), $answer->getAnswertext());
1207  }
1208  }
1209  }
1210 
1211  public function getThumbPrefix()
1212  {
1213  return "thumb.";
1214  }
1215 
1216  protected function generateThumbForFile($path, $file)
1217  {
1218  $filename = $path . $file;
1219  if (@file_exists($filename))
1220  {
1221  $thumbpath = $path . $this->getThumbPrefix() . $file;
1222  $path_info = @pathinfo($filename);
1223  $ext = "";
1224  switch (strtoupper($path_info['extension']))
1225  {
1226  case 'PNG':
1227  $ext = 'PNG';
1228  break;
1229  case 'GIF':
1230  $ext = 'GIF';
1231  break;
1232  default:
1233  $ext = 'JPEG';
1234  break;
1235  }
1236  ilUtil::convertImage($filename, $thumbpath, $ext, $this->getThumbGeometry());
1237  }
1238  }
1239 
1243  public function toJSON()
1244  {
1245  include_once("./Services/RTE/classes/class.ilRTE.php");
1246  $result = array();
1247  $result['id'] = (int) $this->getId();
1248  $result['type'] = (string) $this->getQuestionType();
1249  $result['title'] = (string) $this->getTitle();
1250  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1251  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1252  $result['shuffle'] = (bool) true;
1253  $result['points'] = (bool) $this->getPoints();
1254  $result['feedback'] = array(
1255  "onenotcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false),
1256  "allcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true)
1257  );
1258  if ($this->getOrderingType() == OQ_PICTURES)
1259  {
1260  $result['path'] = $this->getImagePathWeb();
1261  }
1262 
1263  $counter = 1;
1264  $answers = array();
1265  foreach ($this->getAnswers() as $answer_obj)
1266  {
1267  $answers[$counter] = $answer_obj->getAnswertext();
1268  $counter++;
1269  }
1270  $answers = $this->pcArrayShuffle($answers);
1271  $arr = array();
1272  foreach ($answers as $order => $answer)
1273  {
1274  array_push($arr, array(
1275  "answertext" => (string) $answer,
1276  "order" => (int) $order
1277  ));
1278  }
1279  $result['answers'] = $arr;
1280 
1281  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1282  $result['mobs'] = $mobs;
1283 
1284  return json_encode($result);
1285  }
1286 
1287  public function removeAnswerImage($index)
1288  {
1289  $answer = $this->answers[$index];
1290  if (is_object($answer))
1291  {
1292  $this->deleteImagefile($answer->getAnswertext());
1293  $answer->setAnswertext('');
1294  }
1295  }
1296 
1297  /***
1298  * @param object $child
1299  * @param integer $ordering_depth
1300  * @param bool $with_random_id
1301  */
1302  private function getDepthRecursive($child, $ordering_depth, $with_random_id = false)
1303  {
1304  if($with_random_id == true)
1305  {
1306  // for test ouput
1307  if(is_array($child->children))
1308  {
1309  foreach($child->children as $grand_child)
1310  {
1311  $ordering_depth++;
1312  $this->leveled_ordering[$child->id] = $ordering_depth;
1313  $this->getDepthRecursive($grand_child, $ordering_depth, true);
1314  }
1315  }
1316  else
1317  {
1318  $ordering_depth++;
1319  $this->leveled_ordering[$child->id] = $ordering_depth;
1320  }
1321  }
1322  else
1323  {
1324  if(is_array($child->children))
1325  {
1326  foreach($child->children as $grand_child)
1327  {
1328  $ordering_depth++;
1329  $this->leveled_ordering[] = $ordering_depth;
1330  $this->getDepthRecursive($grand_child, $ordering_depth);
1331  }
1332  }
1333  else
1334  {
1335  $ordering_depth++;
1336  $this->leveled_ordering[] = $ordering_depth;
1337  }
1338  }
1339  }
1340 
1341  /***
1342  * @param array $new_hierarchy
1343  * @param bool $with_random_id
1344  */
1345  public function setLeveledOrdering($new_hierarchy, $with_random_id = false)
1346  {
1347  if($with_random_id == true)
1348  {
1349  //for test output
1350  foreach($new_hierarchy as $id)
1351  {
1352  $ordering_depth = 0;
1353  $this->leveled_ordering[$id->id] = $ordering_depth;
1354 
1355  if(is_array($id->children))
1356  {
1357  foreach($id->children as $child)
1358  {
1359  $this->getDepthRecursive($child, $ordering_depth, true);
1360 }
1361  }
1362  }
1363  }
1364  else
1365  {
1366  foreach($new_hierarchy as $id)
1367  {
1368  $ordering_depth = 0;
1369  $this->leveled_ordering[] = $ordering_depth;
1370 
1371  if(is_array($id->children))
1372  {
1373  foreach($id->children as $child)
1374  {
1375  $this->getDepthRecursive($child, $ordering_depth, $with_random_id);
1376  }
1377  }
1378  }
1379  }
1380  }
1381  public function getLeveledOrdering()
1382  {
1383  return $this->leveled_ordering;
1384  }
1385 
1386  public function getOldLeveledOrdering()
1387  {
1388  global $ilDB;
1389 
1390  $res = $ilDB->queryF('SELECT depth FROM qpl_a_ordering WHERE question_fi = %s ORDER BY solution_order ASC',
1391  array('integer'), array($this->getId()));
1392  while($row = $ilDB->fetchAssoc($res))
1393  {
1394  $this->old_ordering_depth[] = $row['depth'];
1395  }
1397  }
1398 
1399  /***
1400  * @param integer $a_random_id
1401  * @return integer
1402  */
1403  public function lookupSolutionOrderByRandomid($a_random_id)
1404  {
1405  global $ilDB;
1406 
1407  $res = $ilDB->queryF('SELECT solution_order FROM qpl_a_ordering WHERE random_id = %s',
1408  array('integer'), array($a_random_id));
1409  $row = $ilDB->fetchAssoc($res);
1410 
1411  return $row['solution_order'];
1412  }
1413 
1414  /***
1415  * @param integer $a_random_id
1416  * @return string
1417  */
1418  public function lookupAnswerTextByRandomId($a_random_id)
1419  {
1420  global $ilDB;
1421 
1422  $res = $ilDB->queryF('SELECT answertext FROM qpl_a_ordering WHERE random_id = %s',
1423  array('integer'), array($a_random_id));
1424  $row = $ilDB->fetchAssoc($res);
1425 
1426  return $row['answertext'];
1427  }
1428 
1429  public function updateLeveledOrdering($a_index, $a_answer_text, $a_depth)
1430  {
1431  global $ilDB;
1432 
1433  $ilDB->update('qpl_a_ordering',
1434  array('solution_order'=> array('integer', $a_index),
1435  'depth' => array('integer', $a_depth)),
1436  array('answertext' => array('text', $a_answer_text)));
1437 
1438  return true;
1439  }
1440 }
1441 ?>