ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.assFileUpload.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.ilObjFileHandlingQuestionType.php';
8 
21 {
22  // hey: prevPassSolutions - support reusing selected files
23  const REUSE_FILES_TBL_POSTVAR = 'reusefiles';
24  const DELETE_FILES_TBL_POSTVAR = 'deletefiles';
25  // hey.
26 
27  protected $maxsize;
28 
29  protected $allowedextensions;
30 
32  protected $completion_by_submission = false;
33 
47  function __construct(
48  $title = "",
49  $comment = "",
50  $author = "",
51  $owner = -1,
52  $question = ""
53  )
54  {
55  parent::__construct($title, $comment, $author, $owner, $question);
56  }
57 
63  public function isComplete()
64  {
65  if (
66  strlen($this->title)
67  && ($this->author)
68  && ($this->question)
69  && ($this->getMaximumPoints() >= 0)
70  && is_numeric($this->getMaximumPoints()))
71  {
72  return true;
73  }
74  return false;
75  }
76 
80  public function saveToDb($original_id = "")
81  {
84  parent::saveToDb();
85  }
86 
88  {
89  global $ilDB;
90  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
91  array( "integer" ),
92  array( $this->getId() )
93  );
94  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
95  ) . " (question_fi, maxsize, allowedextensions, compl_by_submission) VALUES (%s, %s, %s, %s)",
96  array( "integer", "float", "text", "integer" ),
97  array(
98  $this->getId(),
99  (strlen( $this->getMaxSize() )) ? $this->getMaxSize() : NULL,
100  (strlen( $this->getAllowedExtensions() )) ? $this->getAllowedExtensions() : NULL,
101  (int)$this->isCompletionBySubmissionEnabled()
102  )
103  );
104  }
105 
111  public function loadFromDb($question_id)
112  {
113  global $ilDB;
114  $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",
115  array("integer"),
116  array($question_id)
117  );
118  if ($result->numRows() == 1)
119  {
120  $data = $ilDB->fetchAssoc($result);
121  $this->setId($question_id);
122  $this->setTitle($data["title"]);
123  $this->setComment($data["description"]);
124  $this->setNrOfTries($data['nr_of_tries']);
125  $this->setSuggestedSolution($data["solution_hint"]);
126  $this->setOriginalId($data["original_id"]);
127  $this->setObjId($data["obj_fi"]);
128  $this->setAuthor($data["author"]);
129  $this->setOwner($data["owner"]);
130  $this->setPoints($data["points"]);
131 
132  include_once("./Services/RTE/classes/class.ilRTE.php");
133  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
134  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
135  $this->setMaxSize($data["maxsize"]);
136  $this->setAllowedExtensions($data["allowedextensions"]);
137  $this->setCompletionBySubmission($data['compl_by_submission'] == 1 ? true : false);
138 
139  try
140  {
141  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
142  }
144  {
145  }
146  }
147  parent::loadFromDb($question_id);
148  }
149 
153  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
154  {
155  if ($this->id <= 0)
156  {
157  // The question has not been saved. It cannot be duplicated
158  return;
159  }
160  // duplicate the question in database
161  $this_id = $this->getId();
162  $thisObjId = $this->getObjId();
163 
164  $clone = $this;
165  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
167  $clone->id = -1;
168 
169  if( (int)$testObjId > 0 )
170  {
171  $clone->setObjId($testObjId);
172  }
173 
174  if ($title)
175  {
176  $clone->setTitle($title);
177  }
178 
179  if ($author)
180  {
181  $clone->setAuthor($author);
182  }
183  if ($owner)
184  {
185  $clone->setOwner($owner);
186  }
187 
188  if ($for_test)
189  {
190  $clone->saveToDb($original_id);
191  }
192  else
193  {
194  $clone->saveToDb();
195  }
196 
197  // copy question page content
198  $clone->copyPageOfQuestion($this_id);
199  // copy XHTML media objects
200  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
201 
202  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
203 
204  return $clone->id;
205  }
206 
210  public function copyObject($target_questionpool_id, $title = "")
211  {
212  if ($this->id <= 0)
213  {
214  // The question has not been saved. It cannot be duplicated
215  return;
216  }
217  // duplicate the question in database
218  $clone = $this;
219  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
221  $clone->id = -1;
222  $source_questionpool_id = $this->getObjId();
223  $clone->setObjId($target_questionpool_id);
224  if ($title)
225  {
226  $clone->setTitle($title);
227  }
228  $clone->saveToDb();
229 
230  // copy question page content
231  $clone->copyPageOfQuestion($original_id);
232  // copy XHTML media objects
233  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
234 
235  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
236 
237  return $clone->id;
238  }
239 
240  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
241  {
242  if ($this->id <= 0)
243  {
244  // The question has not been saved. It cannot be duplicated
245  return;
246  }
247 
248  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
249 
250  $sourceQuestionId = $this->id;
251  $sourceParentId = $this->getObjId();
252 
253  // duplicate the question in database
254  $clone = $this;
255  $clone->id = -1;
256 
257  $clone->setObjId($targetParentId);
258 
259  if ($targetQuestionTitle)
260  {
261  $clone->setTitle($targetQuestionTitle);
262  }
263 
264  $clone->saveToDb();
265  // copy question page content
266  $clone->copyPageOfQuestion($sourceQuestionId);
267  // copy XHTML media objects
268  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
269 
270  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
271 
272  return $clone->id;
273  }
274 
280  public function getMaximumPoints()
281  {
282  return $this->getPoints();
283  }
284 
295  public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE)
296  {
297  if( $returndetails )
298  {
299  throw new ilTestException('return details not implemented for '.__METHOD__);
300  }
301 
302  global $ilDB;
303 
304  if (is_null($pass))
305  {
306  $pass = $this->getSolutionMaxPass($active_id);
307  }
308  $points = 0;
309  return $points;
310  }
311 
312  protected function calculateReachedPointsForSolution($userSolution)
313  {
314  if( $this->isCompletionBySubmissionEnabled() && count($userSolution) )
315  {
316  return $this->getPoints();
317  }
318 
319  return 0;
320  }
321 
327  function checkUpload()
328  {
329  $this->lng->loadLanguageModule("form");
330  // remove trailing '/'
331  $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
332 
333  $filename = $_FILES["upload"]["name"];
334  $filename_arr = pathinfo($_FILES["upload"]["name"]);
335  $suffix = $filename_arr["extension"];
336  $mimetype = $_FILES["upload"]["type"];
337  $size_bytes = $_FILES["upload"]["size"];
338  $temp_name = $_FILES["upload"]["tmp_name"];
339  $error = $_FILES["upload"]["error"];
340 
341  if ($size_bytes > $this->getMaxFilesizeInBytes())
342  {
343  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
344  return false;
345  }
346 
347  // error handling
348  if ($error > 0)
349  {
350  switch ($error)
351  {
352  case UPLOAD_ERR_INI_SIZE:
353  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
354  return false;
355  break;
356 
357  case UPLOAD_ERR_FORM_SIZE:
358  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
359  return false;
360  break;
361 
362  case UPLOAD_ERR_PARTIAL:
363  ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
364  return false;
365  break;
366 
367  case UPLOAD_ERR_NO_FILE:
368  ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
369  return false;
370  break;
371 
372  case UPLOAD_ERR_NO_TMP_DIR:
373  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
374  return false;
375  break;
376 
377  case UPLOAD_ERR_CANT_WRITE:
378  ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
379  return false;
380  break;
381 
382  case UPLOAD_ERR_EXTENSION:
383  ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
384  return false;
385  break;
386  }
387  }
388 
389  // check suffixes
390  if( count($this->getAllowedExtensionsArray()) )
391  {
392  if( !strlen($suffix) )
393  {
394  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
395  return false;
396  }
397 
398  if( !in_array(strtolower($suffix), $this->getAllowedExtensionsArray()) )
399  {
400  ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
401  return false;
402  }
403  }
404 
405  // virus handling
406  if (strlen($temp_name))
407  {
408  $vir = ilUtil::virusHandling($temp_name, $filename);
409  if ($vir[0] == false)
410  {
411  ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found")."<br />".$vir[1], true);
412  return false;
413  }
414  }
415  return true;
416  }
417 
421  public function getFileUploadPath($test_id, $active_id, $question_id = null)
422  {
423  if (is_null($question_id)) $question_id = $this->getId();
424  return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
425  }
426 
430  protected function getPreviewFileUploadPath($userId)
431  {
432  return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
433  }
434 
440  function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
441  {
442  if (is_null($question_id)) $question_id = $this->getId();
443  include_once "./Services/Utilities/classes/class.ilUtil.php";
444  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
445  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
446  }
447 
451  protected function getPreviewFileUploadPathWeb($userId)
452  {
453  include_once "./Services/Utilities/classes/class.ilUtil.php";
454  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
455  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
456  }
457 
463  public function getUploadedFiles($active_id, $pass = null, $authorized = true)
464  {
465  global $ilDB;
466 
467  if (is_null($pass))
468  {
469  $pass = $this->getSolutionMaxPass($active_id);
470  }
471 // fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
472  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s AND authorized = %s AND value1 IS NOT NULL ORDER BY tstamp",
473  array("integer", "integer", "integer", 'integer'),
474  array($active_id, $this->getId(), $pass, (int)$authorized)
475  );
476 // fau.
477  $found = array();
478 
479  while ($data = $ilDB->fetchAssoc($result))
480  {
481  array_push($found, $data);
482  }
483 
484  return $found;
485  }
486 
487  public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
488  {
489  return (array)$previewSession->getParticipantsSolution();
490  }
491 
497  public function getUploadedFilesForWeb($active_id, $pass)
498  {
499  global $ilDB;
500 
501  $found = $this->getUploadedFiles($active_id, $pass);
502  $result = $ilDB->queryF("SELECT test_fi FROM tst_active WHERE active_id = %s",
503  array('integer'),
504  array($active_id)
505  );
506  if ($result->numRows() == 1)
507  {
508  $row = $ilDB->fetchAssoc($result);
509  $test_id = $row["test_fi"];
510  $path = $this->getFileUploadPathWeb($test_id, $active_id);
511  foreach ($found as $idx => $data)
512  {
513  $found[$idx]['webpath'] = $path;
514  }
515  }
516  return $found;
517  }
518 
524  protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
525  {
526  global $ilDB;
527 
528  $pass = null;
529  $active_id = null;
530  foreach ($files as $solution_id)
531  {
532  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
533  array("integer", 'integer'),
534  array($solution_id, (int)$authorized)
535  );
536  if ($result->numRows() == 1)
537  {
538  $data = $ilDB->fetchAssoc($result);
539  $pass = $data['pass'];
540  $active_id = $data['active_fi'];
541  @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
542  }
543  }
544  foreach ($files as $solution_id)
545  {
546  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
547  array("integer", 'integer'),
548  array($solution_id, $authorized)
549  );
550  }
551  }
552 
553 // fau: testNav new function deleteUnusedFiles()
560  protected function deleteUnusedFiles($test_id, $active_id, $pass)
561  {
562  // read all solutions (authorized and intermediate) from all steps
563  $step = $this->getStep();
564  $this->setStep(null);
565  $solutions = array_merge(
566  $this->getSolutionValues($active_id, $pass, true),
567  $this->getSolutionValues($active_id, $pass, false)
568  );
569  $this->setStep($step);
570 
571  // get the used files from these solutions
572  $used_files = array();
573  foreach ($solutions as $solution)
574  {
575  $used_files[] = $solution['value1'];
576  }
577 
578  // read the existing files for user and pass
579  // delete all files that are not used in the solutions
580  $curdir = getcwd();
581  chdir($this->getFileUploadPath($test_id, $active_id));
582  $existing_files = glob("file_" . $active_id . "_" . $pass . "_*");
583  foreach($existing_files as $file)
584  {
585  if (!in_array($file, $used_files))
586  {
587  @unlink($file);
588  }
589  }
590  chdir($curdir);
591  }
592 // fau.
593 
594  protected function deletePreviewFileUploads($userId, $userSolution, $files)
595  {
596  foreach($files as $name)
597  {
598  if( isset($userSolution[$name]) )
599  {
600  unset($userSolution[$name]);
601  @unlink($this->getPreviewFileUploadPath($userId) . $name);
602  }
603  }
604 
605  return $userSolution;
606  }
607 
613  public function getMaxFilesizeAsString()
614  {
615  $size = $this->getMaxFilesizeInBytes();
616  if ($size < 1024)
617  {
618  $max_filesize = sprintf("%d Bytes",$size);
619  }
620  else if ($size < 1024*1024)
621  {
622  $max_filesize = sprintf("%.1f KB",$size/1024);
623  }
624  else
625  {
626  $max_filesize = sprintf("%.1f MB",$size/1024/1024);
627  }
628 
629  return $max_filesize;
630  }
631 
637  public function getMaxFilesizeInBytes()
638  {
639  if (strlen($this->getMaxSize()))
640  {
641  return $this->getMaxSize();
642  }
643  else
644  {
645  // get the value for the maximal uploadable filesize from the php.ini (if available)
646  $umf = get_cfg_var("upload_max_filesize");
647  // get the value for the maximal post data from the php.ini (if available)
648  $pms = get_cfg_var("post_max_size");
649 
650  //convert from short-string representation to "real" bytes
651  $multiplier_a=array("K"=>1024, "M"=>1024*1024, "G"=>1024*1024*1024);
652 
653  $umf_parts=preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
654  $pms_parts=preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
655 
656  if (count($umf_parts) == 2) { $umf = $umf_parts[0]*$multiplier_a[$umf_parts[1]]; }
657  if (count($pms_parts) == 2) { $pms = $pms_parts[0]*$multiplier_a[$pms_parts[1]]; }
658 
659  // use the smaller one as limit
660  $max_filesize = min($umf, $pms);
661 
662  if (!$max_filesize) $max_filesize=max($umf, $pms);
663  return $max_filesize;
664  }
665  }
666 
667  // hey: prevPassSolutions - refactored method to get intermediate/authorized
668  // as well as upload, delete and previous files working
669  // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
678  public function saveWorkingData($active_id, $pass = NULL, $authorized = true)
679  {
680  $pass = $this->ensureCurrentTestPass($active_id, $pass);
681  $test_id = $this->lookupTestId($active_id);
682 
683  $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
684 
685  $entered_values = false;
686 
687  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function() use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
688 
689  if ($authorized == false)
690  {
691  $this->forceExistingIntermediateSolution($active_id, $pass, true);
692  }
693 
694  if( $this->isFileDeletionAction() )
695  {
696  if( $this->isFileDeletionSubmitAvailable() )
697  {
698  foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id)
699  {
700  $this->removeSolutionRecordById($solution_id);
701  }
702  }
703  else
704  {
705  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
706  }
707  }
708  else
709  {
710  if( $this->isFileReuseHandlingRequired() )
711  {
712  foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId)
713  {
714  $solution = $this->getSolutionRecordById($solutionId);
715 
716  $this->saveCurrentSolution($active_id, $pass,
717  $solution['value1'], $solution['value2'],
718  false, $solution['tstamp']
719  );
720  }
721  }
722 
723  if( $uploadHandlingRequired )
724  {
725  if(!@file_exists($this->getFileUploadPath($test_id, $active_id)))
726  {
728  }
729 
730  $solutionFileVersioningUploadTS = time();
731  $filename_arr = pathinfo($_FILES["upload"]["name"]);
732  $extension = $filename_arr["extension"];
733  $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
734 
735  include_once 'Services/Utilities/classes/class.ilFileUtils.php';
736  $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
737  $newfile = ilFileUtils::getValidFilename($newfile);
738 
739  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
740 
741  $this->saveCurrentSolution($active_id, $pass, $newfile, $dispoFilename, false,
742  $solutionFileVersioningUploadTS
743  );
744 
745  $entered_values = true;
746  }
747  }
748 
749  if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass))
750  {
751  // remove the dummy record of the intermediate solution
752  $this->deleteDummySolutionRecord($active_id, $pass);
753 
754  // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
755  $this->removeCurrentSolution($active_id, $pass, true);
756  $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
757  }
758 
759  $this->deleteUnusedFiles($test_id, $active_id, $pass);
760  });
761 
762  if ($entered_values)
763  {
764  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
766  {
767  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
768  }
769  }
770  else
771  {
772  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
774  {
775  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
776  }
777  }
778 
779  return true;
780  }
781  // hey.
782 
783 // fau: testNav - remove dummy value when intermediate solution is got for test display
790  public function getUserSolutionPreferingIntermediate($active_id, $pass = NULL)
791  {
792  $solution = $this->getSolutionValues($active_id, $pass, false);
793 
794  if( !count($solution) )
795  {
796  $solution = $this->getSolutionValues($active_id, $pass, true);
797  }
798  else
799  {
800  $cleaned = array();
801  foreach ($solution as $row)
802  {
803  if (!empty($row['value1']))
804  {
805  $cleaned[] = $row;
806  }
807  }
808  $solution = $cleaned;
809  }
810 
811  return $solution;
812  }
813 // fau.
814 
815 
816 // fau: testNav - remove unused files if an intermediate solution is removed
823  public function removeIntermediateSolution($active_id, $pass)
824  {
825  global $ilDB;
826 
827  $result = parent::removeIntermediateSolution($active_id, $pass);
828 
829  // get the current test id
830  // hey: prevPassSolutions - exract until you drop :-D
831  $test_id = $this->lookupTestId($active_id);
832  // hey.
833 
834  $this->deleteUnusedFiles($test_id, $active_id, $pass);
835 
836  return $result;
837  }
838 // fau.
839 
840 
841  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
842  {
843  $userSolution = $previewSession->getParticipantsSolution();
844 
845  if( !is_array($userSolution) )
846  {
847  $userSolution = array();
848  }
849 
850  // hey: prevPassSolutions - readability spree - get a chance to understand the code
851  if( $this->isFileDeletionAction() )
852  // hey.
853  {
854  // hey: prevPassSolutions - readability spree - get a chance to understand the code
855  if( $this->isFileDeletionSubmitAvailable() )
856  // hey.
857  {
858  $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
859  }
860  else
861  {
862  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
863  }
864  }
865  else
866  {
867  // hey: prevPassSolutions - readability spree - get a chance to understand the code
868  if ( $this->isFileUploadAvailable() )
869  // hey.
870  {
871  if ($this->checkUpload())
872  {
873  if( !@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId())) )
874  {
875  ilUtil::makeDirParents($this->getPreviewFileUploadPath($previewSession->getUserId()));
876  }
877 
878  $version = time();
879  $filename_arr = pathinfo($_FILES["upload"]["name"]);
880  $extension = $filename_arr["extension"];
881  $newfile = "file_".md5($_FILES["upload"]["name"])."_" . $version . "." . $extension;
882  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
883 
884  $userSolution[$newfile] = array(
885  'solution_id' => $newfile,
886  'value1' => $newfile,
887  'value2' => $_FILES['upload']['name'],
888  'tstamp' => $version,
889  'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
890  );
891  }
892  }
893  }
894 
895  $previewSession->setParticipantsSolution($userSolution);
896  }
897 
901  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
902  {
903  $this->handleSubmission($active_id, $pass, $obligationsAnswered, $authorized);
904  }
905 
915  protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
916  {
917  if(!$authorized)
918  {
919  return;
920  }
921 
923  {
924  $maxpoints = assQuestion::_getMaximumPoints($this->getId());
925 
926  if($this->getUploadedFiles($active_id, $pass, $authorized))
927  {
928  $points = $maxpoints;
929  }
930  else
931  {
932 // fau: testNav - don't set reached points if no file is available
933  return;
934 // fau.
935  }
936 
937  assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
938 
939  // update learning progress
940  include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
941  include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
943  ilObjTest::_getObjectIDFromActiveID((int)$active_id),
944  ilObjTestAccess::_getParticipantId((int) $active_id)
945  );
946  }
947  }
948 
954  public function getQuestionType()
955  {
956  return "assFileUpload";
957  }
958 
964  public function getAdditionalTableName()
965  {
966  return "qpl_qst_fileupload";
967  }
968 
974  public function getAnswerTableName()
975  {
976  return "";
977  }
978 
984  public function deleteAnswers($question_id)
985  {
986  }
987 
992  public function getRTETextWithMediaObjects()
993  {
994  $text = parent::getRTETextWithMediaObjects();
995  return $text;
996  }
997 
1001  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
1002  {
1003  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
1004 
1005  $i = 1;
1006  $solutions = $this->getSolutionValues($active_id, $pass);
1007  foreach ($solutions as $solution)
1008  {
1009  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
1010  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
1011  if (strlen($solution["value1"]))
1012  {
1013  $worksheet->setCell($startrow + $i, 1, $solution["value1"]);
1014  $worksheet->setCell($startrow + $i, 2, $solution["value2"]);
1015  }
1016  $i++;
1017  }
1018 
1019  return $startrow + $i + 1;
1020  }
1021 
1034  public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
1035  {
1036  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
1037  $import = new assFileUploadImport($this);
1038  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
1039  }
1040 
1047  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
1048  {
1049  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
1050  $export = new assFileUploadExport($this);
1051  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
1052  }
1053 
1059  public function getBestSolution($active_id, $pass)
1060  {
1061  $user_solution = array();
1062  return $user_solution;
1063  }
1064 
1070  public function getMaxSize()
1071  {
1072  return $this->maxsize;
1073  }
1074 
1080  public function setMaxSize($a_value)
1081  {
1082  $this->maxsize = $a_value;
1083  }
1084 
1090  public function getAllowedExtensionsArray()
1091  {
1092  if (strlen($this->allowedextensions))
1093  {
1094  return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1095  }
1096  return array();
1097  }
1098 
1104  public function getAllowedExtensions()
1105  {
1106  return $this->allowedextensions;
1107  }
1108 
1114  public function setAllowedExtensions($a_value)
1115  {
1116  $this->allowedextensions = strtolower(trim($a_value));
1117  }
1118 
1122  public function __get($value)
1123  {
1124  switch ($value)
1125  {
1126  case "maxsize":
1127  return $this->getMaxSize();
1128  break;
1129  case "allowedextensions":
1130  return $this->getAllowedExtensions();
1131  break;
1132  case 'completion_by_submission':
1133  return $this->isCompletionBySubmissionEnabled();
1134  break;
1135  default:
1136  return parent::__get($value);
1137  break;
1138  }
1139  }
1140 
1144  public function __set($key, $value)
1145  {
1146  switch ($key)
1147  {
1148  case "maxsize":
1149  $this->setMaxSize($value);
1150  break;
1151  case "allowedextensions":
1152  $this->setAllowedExtensions($value);
1153  break;
1154  case 'completion_by_submission':
1155  $this->setCompletionBySubmission($value);
1156  break;
1157  default:
1158  parent::__set($key, $value);
1159  break;
1160  }
1161  }
1162 
1170  public function hasFileUploads($test_id)
1171  {
1172  global $ilDB;
1173  $query = "
1174  SELECT tst_solutions.solution_id
1175  FROM tst_solutions, tst_active, qpl_questions
1176  WHERE tst_solutions.active_fi = tst_active.active_id
1177  AND tst_solutions.question_fi = qpl_questions.question_id
1178  AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
1179  $result = $ilDB->queryF( $query,
1180  array("integer", "integer"),
1181  array($this->getId(), $test_id)
1182  );
1183  if ($result->numRows() > 0)
1184  {
1185  return true;
1186  }
1187  else
1188  {
1189  return false;
1190  }
1191  }
1192 
1198  public function deliverFileUploadZIPFile($test_id, $test_title)
1199  {
1200  global $ilDB, $lng;
1201 
1202  require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1203  $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1204 
1205  $exporter->setTestId($test_id);
1206  $exporter->setTestTitle($test_title);
1207  $exporter->setQuestion($this);
1208 
1209  $exporter->build();
1210 
1212  $exporter->getFinalZipFilePath(), $exporter->getDispoZipFileName(),
1213  $exporter->getZipFileMimeType(), false, true
1214  );
1215  }
1216 
1226  {
1228  }
1229 
1239  public function setCompletionBySubmission($bool)
1240  {
1241  $this->completion_by_submission = (bool)$bool;
1242  return $this;
1243  }
1244 
1256  public function isAnswered($active_id, $pass = null)
1257  {
1258  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1259 
1260  return $numExistingSolutionRecords > 0;
1261  }
1262 
1273  public static function isObligationPossible($questionId)
1274  {
1275  return true;
1276  }
1277 
1278  public function isAutosaveable()
1279  {
1280  return FALSE;
1281  }
1282 
1283 // fau: testNav - new function getTestQuestionConfig()
1290  // hey: refactored identifiers
1292  // hey.
1293  {
1294  // hey: refactored identifiers
1295  return parent::buildTestPresentationConfig()
1296  // hey.
1297  ->setFormChangeDetectionEnabled(false);
1298  }
1299 // fau.
1300 
1301  // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1305  protected function isFileDeletionAction()
1306  {
1307  require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1309  }
1310 
1314  protected function isFileDeletionSubmitAvailable()
1315  {
1316  return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1317  }
1318 
1322  protected function isFileReuseSubmitAvailable()
1323  {
1324  return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1325  }
1326 
1330  protected function isFileReuseHandlingRequired()
1331  {
1332  if( !$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed() )
1333  {
1334  return false;
1335  }
1336 
1337  if( !$this->isFileReuseSubmitAvailable() )
1338  {
1339  return false;
1340  }
1341 
1342  return true;
1343  }
1344 
1348  protected function isFileUploadAvailable()
1349  {
1350  if( !isset($_FILES['upload']) )
1351  {
1352  return false;
1353  }
1354 
1355  if( !isset($_FILES['upload']['tmp_name']) )
1356  {
1357  return false;
1358  }
1359 
1360  return strlen($_FILES['upload']['tmp_name']) > 0;
1361  }
1362  // hey.
1363 }
$files
Definition: add-vimline.php:18
getPreviewFileUploadPathWeb($userId)
Returns the filesystem path for file uploads.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
getId()
Gets the id of the assQuestion object.
$error
Definition: Error.php:17
getAllowedExtensions()
Get allowed file extensions.
savePreviewData(ilAssQuestionPreviewSession $previewSession)
$path
Definition: aliased.php:25
static _getOriginalId($question_id)
Returns the original id of a question.
$worksheet
setSuggestedSolution($solution_id="", $subquestion_index=0, $is_import=false)
Sets a suggested solution for the question.
$size
Definition: RandomTest.php:79
Class for file upload question exports.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
setAllowedExtensions($a_value)
Set allowed file extensions.
forceExistingIntermediateSolution($activeId, $passIndex, $considerDummyRecordCreation)
deleteUploadedFiles($files, $test_id, $active_id, $authorized)
Delete uploaded files.
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
__set($key, $value)
Object setter.
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
static _getParticipantId($active_id)
Get user id for active id.
$result
hasFileUploads($test_id)
Checks if file uploads exist for a given test and the original id of the question.
getPoints()
Returns the maximum available points for the question.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{}
getBestSolution($active_id, $pass)
Returns the best solution for a given pass of a participant.
Abstract basic class which is to be extended by the concrete assessment question type classes...
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{}
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
intermediateSolutionExists($active_id, $pass)
static _updateStatus($a_obj_id, $a_usr_id, $a_obj=null, $a_percentage=false, $a_force_raise=false)
Update status.
deleteDummySolutionRecord($activeId, $passIndex)
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
setId($id=-1)
Sets the id of the assQuestion object.
getUserSolutionPreferingIntermediate($active_id, $pass=NULL)
Get the user solution preferring the intermediate solution.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
Interface ilObjFileHandlingQuestionType.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second. ...
isComplete()
Returns true, if the question is complete for use.
deletePreviewFileUploads($userId, $userSolution, $files)
fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
Creates a question from a QTI file.
setNrOfTries($a_nr_of_tries)
copyObject($target_questionpool_id, $title="")
Copies an assFileUpload object.
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
getFileUploadPathWeb($test_id, $active_id, $question_id=null)
Returns the file upload path for web accessible files of a question.
static _getObjectIDFromActiveID($active_id)
Returns the ILIAS test object id for a given active id.
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...
Class for file upload questions.
getMaxSize()
Get max file size.
handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
This method is called after an user submitted one or more files.
getObjId()
Get the object id of the container object.
getAllowedExtensionsArray()
Get allowed file extensions.
static _getMaximumPoints($question_id)
Returns the maximum points, a learner can reach answering the question.
Class for file upload question imports.
Base Exception for all Exceptions relating to Modules/Test.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assFileUpload.
saveToDb($original_id="")
Saves a assFileUpload object to a database.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
static sendInfo($a_info="", $a_keep=false)
Send Info Message to Screen.
removeSolutionRecordById($solutionId)
static _getLogLanguage()
retrieve the log language for assessment logging
setAuthor($author="")
Sets the authors name of the assQuestion object.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
updateCurrentSolutionsAuthorization($activeId, $pass, $authorized, $keepTime=false)
deliverFileUploadZIPFile($test_id, $test_title)
Generates a ZIP file containing all file uploads for a given test and the original id of the question...
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
getMaxFilesizeAsString()
Return the maximum allowed file size as string.
checkUpload()
Check file upload.
calculateReachedPointsForSolution($userSolution)
getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
getQuestionType()
Returns the question type of the question.
setMaxSize($a_value)
Set max file size.
removeIntermediateSolution($active_id, $pass)
Remove an intermediate soluton (overridden to remove unused fies)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
static deliverFile($a_file, $a_filename, $a_mime='', $isInline=false, $removeAfterDelivery=false, $a_exit_after=true)
deliver file for download via browser.
static removeTrailingPathSeparators($path)
getUploadedFilesForWeb($active_id, $pass)
Returns the web accessible uploaded files for an active user in a given pass.
Create styles array
The data for the language used.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
buildTestPresentationConfig()
Get the test question configuration Overridden from parent to disable the form change detection Other...
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
Returns a QTI xml representation of the question and sets the internal domxml variable with the DOM X...
getPreviewFileUploadPath($userId)
Returns the filesystem path for file uploads.
getFileUploadPath($test_id, $active_id, $question_id=null)
Returns the filesystem path for file uploads.
lookupTestId($active_id)
getAnswerTableName()
Returns the name of the answer table in the database.
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
removeCurrentSolution($active_id, $pass, $authorized=true)
ensureCurrentTestPass($active_id, $pass)
getTestPresentationConfig()
Get the test question configuration (initialised once)
global $ilDB
setOriginalId($original_id)
$text
getAdditionalTableName()
Returns the name of the additional question data table in the database.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
loadFromDb($question_id)
Loads a assFileUpload object from a database.
getUploadedFiles($active_id, $pass=null, $authorized=true)
Returns the uploaded files for an active user in a given pass.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assFileUpload constructor
getMaxFilesizeInBytes()
Return the maximum allowed file size in bytes.
setCompletionBySubmission($bool)
Enabled/Disable completion by submission.
setTitle($title="")
Sets the title string of the assQuestion object.
setObjId($obj_id=0)
Set the object id of the container object.
deleteUnusedFiles($test_id, $active_id, $pass)
Delete all files that are neither used in an authorized or intermediate solution. ...
setComment($comment="")
Sets the comment string of the assQuestion object.
__get($value)
Object getter.
$_POST["username"]
static _setReachedPoints($active_id, $question_id, $points, $maxpoints, $pass, $manualscoring, $obligationsEnabled)
Sets the points, a learner has reached answering the question Additionally objective results are upda...
static getValidFilename($a_filename)
Get valid filename.
deleteAnswers($question_id)
Deletes datasets from answers tables.
isNonEmptyItemListPostSubmission($postSubmissionFieldname)
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
isCompletionBySubmissionEnabled()
Checks whether completion by submission is enabled or not.
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
getSolutionRecordById($solutionId)