ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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  public function __construct(
48  $title = "",
49  $comment = "",
50  $author = "",
51  $owner = -1,
52  $question = ""
53  ) {
54  parent::__construct($title, $comment, $author, $owner, $question);
55  }
56 
62  public function isComplete()
63  {
64  if (
65  strlen($this->title)
66  && ($this->author)
67  && ($this->question)
68  && ($this->getMaximumPoints() >= 0)
69  && is_numeric($this->getMaximumPoints())) {
70  return true;
71  }
72  return false;
73  }
74 
78  public function saveToDb($original_id = "")
79  {
82  parent::saveToDb();
83  }
84 
86  {
87  global $ilDB;
88  $ilDB->manipulateF(
89  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
90  array( "integer" ),
91  array( $this->getId() )
92  );
93  $ilDB->manipulateF(
94  "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(
115  "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",
116  array("integer"),
117  array($question_id)
118  );
119  if ($result->numRows() == 1) {
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  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
141  } catch (ilTestQuestionPoolException $e) {
142  }
143  }
144  parent::loadFromDb($question_id);
145  }
146 
150  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
151  {
152  if ($this->id <= 0) {
153  // The question has not been saved. It cannot be duplicated
154  return;
155  }
156  // duplicate the question in database
157  $this_id = $this->getId();
158  $thisObjId = $this->getObjId();
159 
160  $clone = $this;
161  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
163  $clone->id = -1;
164 
165  if ((int) $testObjId > 0) {
166  $clone->setObjId($testObjId);
167  }
168 
169  if ($title) {
170  $clone->setTitle($title);
171  }
172 
173  if ($author) {
174  $clone->setAuthor($author);
175  }
176  if ($owner) {
177  $clone->setOwner($owner);
178  }
179 
180  if ($for_test) {
181  $clone->saveToDb($original_id);
182  } else {
183  $clone->saveToDb();
184  }
185 
186  // copy question page content
187  $clone->copyPageOfQuestion($this_id);
188  // copy XHTML media objects
189  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
190 
191  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
192 
193  return $clone->id;
194  }
195 
199  public function copyObject($target_questionpool_id, $title = "")
200  {
201  if ($this->id <= 0) {
202  // The question has not been saved. It cannot be duplicated
203  return;
204  }
205  // duplicate the question in database
206  $clone = $this;
207  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
209  $clone->id = -1;
210  $source_questionpool_id = $this->getObjId();
211  $clone->setObjId($target_questionpool_id);
212  if ($title) {
213  $clone->setTitle($title);
214  }
215  $clone->saveToDb();
216 
217  // copy question page content
218  $clone->copyPageOfQuestion($original_id);
219  // copy XHTML media objects
220  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
221 
222  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
223 
224  return $clone->id;
225  }
226 
227  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
228  {
229  if ($this->id <= 0) {
230  // The question has not been saved. It cannot be duplicated
231  return;
232  }
233 
234  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
235 
236  $sourceQuestionId = $this->id;
237  $sourceParentId = $this->getObjId();
238 
239  // duplicate the question in database
240  $clone = $this;
241  $clone->id = -1;
242 
243  $clone->setObjId($targetParentId);
244 
245  if ($targetQuestionTitle) {
246  $clone->setTitle($targetQuestionTitle);
247  }
248 
249  $clone->saveToDb();
250  // copy question page content
251  $clone->copyPageOfQuestion($sourceQuestionId);
252  // copy XHTML media objects
253  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
254 
255  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
256 
257  return $clone->id;
258  }
259 
265  public function getMaximumPoints()
266  {
267  return $this->getPoints();
268  }
269 
280  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
281  {
282  if ($returndetails) {
283  throw new ilTestException('return details not implemented for ' . __METHOD__);
284  }
285 
286  global $ilDB;
287 
288  if (is_null($pass)) {
289  $pass = $this->getSolutionMaxPass($active_id);
290  }
291  $points = 0;
292  return $points;
293  }
294 
295  protected function calculateReachedPointsForSolution($userSolution)
296  {
297  if ($this->isCompletionBySubmissionEnabled() && count($userSolution)) {
298  return $this->getPoints();
299  }
300 
301  return 0;
302  }
303 
309  public function checkUpload()
310  {
311  $this->lng->loadLanguageModule("form");
312  // remove trailing '/'
313  $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
314 
315  $filename = $_FILES["upload"]["name"];
316  $filename_arr = pathinfo($_FILES["upload"]["name"]);
317  $suffix = $filename_arr["extension"];
318  $mimetype = $_FILES["upload"]["type"];
319  $size_bytes = $_FILES["upload"]["size"];
320  $temp_name = $_FILES["upload"]["tmp_name"];
321  $error = $_FILES["upload"]["error"];
322 
323  if ($size_bytes > $this->getMaxFilesizeInBytes()) {
324  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
325  return false;
326  }
327 
328  // error handling
329  if ($error > 0) {
330  switch ($error) {
331  case UPLOAD_ERR_INI_SIZE:
332  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
333  return false;
334  break;
335 
336  case UPLOAD_ERR_FORM_SIZE:
337  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
338  return false;
339  break;
340 
341  case UPLOAD_ERR_PARTIAL:
342  ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
343  return false;
344  break;
345 
346  case UPLOAD_ERR_NO_FILE:
347  ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
348  return false;
349  break;
350 
351  case UPLOAD_ERR_NO_TMP_DIR:
352  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
353  return false;
354  break;
355 
356  case UPLOAD_ERR_CANT_WRITE:
357  ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
358  return false;
359  break;
360 
361  case UPLOAD_ERR_EXTENSION:
362  ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
363  return false;
364  break;
365  }
366  }
367 
368  // check suffixes
369  if (count($this->getAllowedExtensionsArray())) {
370  if (!strlen($suffix)) {
371  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
372  return false;
373  }
374 
375  if (!in_array(strtolower($suffix), $this->getAllowedExtensionsArray())) {
376  ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
377  return false;
378  }
379  }
380 
381  // virus handling
382  if (strlen($temp_name)) {
383  $vir = ilUtil::virusHandling($temp_name, $filename);
384  if ($vir[0] == false) {
385  ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found") . "<br />" . $vir[1], true);
386  return false;
387  }
388  }
389  return true;
390  }
391 
395  public function getFileUploadPath($test_id, $active_id, $question_id = null)
396  {
397  if (is_null($question_id)) {
398  $question_id = $this->getId();
399  }
400  return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
401  }
402 
406  protected function getPreviewFileUploadPath($userId)
407  {
408  return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
409  }
410 
416  public function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
417  {
418  if (is_null($question_id)) {
419  $question_id = $this->getId();
420  }
421  include_once "./Services/Utilities/classes/class.ilUtil.php";
422  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
423  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
424  }
425 
429  protected function getPreviewFileUploadPathWeb($userId)
430  {
431  include_once "./Services/Utilities/classes/class.ilUtil.php";
432  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
433  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
434  }
435 
441  public function getUploadedFiles($active_id, $pass = null, $authorized = true)
442  {
443  global $ilDB;
444 
445  if (is_null($pass)) {
446  $pass = $this->getSolutionMaxPass($active_id);
447  }
448  // fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
449  $result = $ilDB->queryF(
450  "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",
451  array("integer", "integer", "integer", 'integer'),
452  array($active_id, $this->getId(), $pass, (int) $authorized)
453  );
454  // fau.
455  $found = array();
456 
457  while ($data = $ilDB->fetchAssoc($result)) {
458  array_push($found, $data);
459  }
460 
461  return $found;
462  }
463 
464  public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
465  {
466  return (array) $previewSession->getParticipantsSolution();
467  }
468 
474  public function getUploadedFilesForWeb($active_id, $pass)
475  {
476  global $ilDB;
477 
478  $found = $this->getUploadedFiles($active_id, $pass);
479  $result = $ilDB->queryF(
480  "SELECT test_fi FROM tst_active WHERE active_id = %s",
481  array('integer'),
482  array($active_id)
483  );
484  if ($result->numRows() == 1) {
485  $row = $ilDB->fetchAssoc($result);
486  $test_id = $row["test_fi"];
487  $path = $this->getFileUploadPathWeb($test_id, $active_id);
488  foreach ($found as $idx => $data) {
489  $found[$idx]['webpath'] = $path;
490  }
491  }
492  return $found;
493  }
494 
500  protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
501  {
502  global $ilDB;
503 
504  $pass = null;
505  $active_id = null;
506  foreach ($files as $solution_id) {
507  $result = $ilDB->queryF(
508  "SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
509  array("integer", 'integer'),
510  array($solution_id, (int) $authorized)
511  );
512  if ($result->numRows() == 1) {
513  $data = $ilDB->fetchAssoc($result);
514  $pass = $data['pass'];
515  $active_id = $data['active_fi'];
516  @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
517  }
518  }
519  foreach ($files as $solution_id) {
520  $affectedRows = $ilDB->manipulateF(
521  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
522  array("integer", 'integer'),
523  array($solution_id, $authorized)
524  );
525  }
526  }
527 
528  // fau: testNav new function deleteUnusedFiles()
535  protected function deleteUnusedFiles($test_id, $active_id, $pass)
536  {
537  // read all solutions (authorized and intermediate) from all steps
538  $step = $this->getStep();
539  $this->setStep(null);
540  $solutions = array_merge(
541  $this->getSolutionValues($active_id, $pass, true),
542  $this->getSolutionValues($active_id, $pass, false)
543  );
544  $this->setStep($step);
545 
546  // get the used files from these solutions
547  $used_files = array();
548  foreach ($solutions as $solution) {
549  $used_files[] = $solution['value1'];
550  }
551 
552  // read the existing files for user and pass
553  // delete all files that are not used in the solutions
554  $uploadPath = $this->getFileUploadPath($test_id, $active_id);
555  if (is_dir($uploadPath) && is_readable($uploadPath)) {
556  $iter = new \RegexIterator(new \DirectoryIterator($uploadPath), '/^file_' . $active_id . '_' . $pass . '_(.*)/');
557  foreach ($iter as $file) {
559  if ($file->isFile() && !in_array($file->getFilename(), $used_files)) {
560  unlink($file->getPathname());
561  }
562  }
563  }
564  }
565  // fau.
566 
567  protected function deletePreviewFileUploads($userId, $userSolution, $files)
568  {
569  foreach ($files as $name) {
570  if (isset($userSolution[$name])) {
571  unset($userSolution[$name]);
572  @unlink($this->getPreviewFileUploadPath($userId) . $name);
573  }
574  }
575 
576  return $userSolution;
577  }
578 
584  public function getMaxFilesizeAsString()
585  {
586  $size = $this->getMaxFilesizeInBytes();
587  if ($size < 1024) {
588  $max_filesize = sprintf("%d Bytes", $size);
589  } elseif ($size < 1024*1024) {
590  $max_filesize = sprintf("%.1f KB", $size/1024);
591  } else {
592  $max_filesize = sprintf("%.1f MB", $size/1024/1024);
593  }
594 
595  return $max_filesize;
596  }
597 
603  public function getMaxFilesizeInBytes()
604  {
605  if (strlen($this->getMaxSize())) {
606  return $this->getMaxSize();
607  } else {
608  // get the value for the maximal uploadable filesize from the php.ini (if available)
609  $umf = get_cfg_var("upload_max_filesize");
610  // get the value for the maximal post data from the php.ini (if available)
611  $pms = get_cfg_var("post_max_size");
612 
613  //convert from short-string representation to "real" bytes
614  $multiplier_a=array("K"=>1024, "M"=>1024*1024, "G"=>1024*1024*1024);
615 
616  $umf_parts=preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
617  $pms_parts=preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
618 
619  if (count($umf_parts) == 2) {
620  $umf = $umf_parts[0]*$multiplier_a[$umf_parts[1]];
621  }
622  if (count($pms_parts) == 2) {
623  $pms = $pms_parts[0]*$multiplier_a[$pms_parts[1]];
624  }
625 
626  // use the smaller one as limit
627  $max_filesize = min($umf, $pms);
628 
629  if (!$max_filesize) {
630  $max_filesize=max($umf, $pms);
631  }
632  return $max_filesize;
633  }
634  }
635 
636  // hey: prevPassSolutions - refactored method to get intermediate/authorized
637  // as well as upload, delete and previous files working
638  // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
647  public function saveWorkingData($active_id, $pass = null, $authorized = true)
648  {
649  $pass = $this->ensureCurrentTestPass($active_id, $pass);
650  $test_id = $this->lookupTestId($active_id);
651 
652  $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
653 
654  $entered_values = false;
655 
656  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
657  if ($authorized == false) {
658  $this->forceExistingIntermediateSolution($active_id, $pass, true);
659  }
660 
661  if ($this->isFileDeletionAction()) {
662  if ($this->isFileDeletionSubmitAvailable()) {
663  foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id) {
664  $this->removeSolutionRecordById($solution_id);
665  }
666  } else {
667  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
668  }
669  } else {
670  if ($this->isFileReuseHandlingRequired()) {
671  foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId) {
672  $solution = $this->getSolutionRecordById($solutionId);
673 
674  $this->saveCurrentSolution(
675  $active_id,
676  $pass,
677  $solution['value1'],
678  $solution['value2'],
679  false,
680  $solution['tstamp']
681  );
682  }
683  }
684 
685  if ($uploadHandlingRequired) {
686  if (!@file_exists($this->getFileUploadPath($test_id, $active_id))) {
688  }
689 
690  $solutionFileVersioningUploadTS = time();
691  $filename_arr = pathinfo($_FILES["upload"]["name"]);
692  $extension = $filename_arr["extension"];
693  $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
694 
695  include_once 'Services/Utilities/classes/class.ilFileUtils.php';
696  $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
697  $newfile = ilFileUtils::getValidFilename($newfile);
698 
699  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
700 
701  $this->saveCurrentSolution(
702  $active_id,
703  $pass,
704  $newfile,
705  $dispoFilename,
706  false,
707  $solutionFileVersioningUploadTS
708  );
709 
710  $entered_values = true;
711  }
712  }
713 
714  if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass)) {
715  // remove the dummy record of the intermediate solution
716  $this->deleteDummySolutionRecord($active_id, $pass);
717 
718  // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
719  $this->removeCurrentSolution($active_id, $pass, true);
720  $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
721  }
722 
723  $this->deleteUnusedFiles($test_id, $active_id, $pass);
724  });
725 
726  if ($entered_values) {
727  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
729  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
730  }
731  } else {
732  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
734  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
735  }
736  }
737 
738  return true;
739  }
740  // hey.
741 
742  // fau: testNav - remove dummy value when intermediate solution is got for test display
749  public function getUserSolutionPreferingIntermediate($active_id, $pass = null)
750  {
751  $solution = $this->getSolutionValues($active_id, $pass, false);
752 
753  if (!count($solution)) {
754  $solution = $this->getSolutionValues($active_id, $pass, true);
755  } else {
756  $cleaned = array();
757  foreach ($solution as $row) {
758  if (!empty($row['value1'])) {
759  $cleaned[] = $row;
760  }
761  }
762  $solution = $cleaned;
763  }
764 
765  return $solution;
766  }
767  // fau.
768 
769 
770  // fau: testNav - remove unused files if an intermediate solution is removed
777  public function removeIntermediateSolution($active_id, $pass)
778  {
779  global $ilDB;
780 
781  $result = parent::removeIntermediateSolution($active_id, $pass);
782 
783  // get the current test id
784  // hey: prevPassSolutions - exract until you drop :-D
785  $test_id = $this->lookupTestId($active_id);
786  // hey.
787 
788  $this->deleteUnusedFiles($test_id, $active_id, $pass);
789 
790  return $result;
791  }
792  // fau.
793 
794 
795  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
796  {
797  $userSolution = $previewSession->getParticipantsSolution();
798 
799  if (!is_array($userSolution)) {
800  $userSolution = array();
801  }
802 
803  // hey: prevPassSolutions - readability spree - get a chance to understand the code
804  if ($this->isFileDeletionAction()) {
805  // hey.
806  // hey: prevPassSolutions - readability spree - get a chance to understand the code
807  if ($this->isFileDeletionSubmitAvailable()) {
808  // hey.
809  $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
810  } else {
811  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
812  }
813  } else {
814  // hey: prevPassSolutions - readability spree - get a chance to understand the code
815  if ($this->isFileUploadAvailable()) {
816  // hey.
817  if ($this->checkUpload()) {
818  if (!@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId()))) {
819  ilUtil::makeDirParents($this->getPreviewFileUploadPath($previewSession->getUserId()));
820  }
821 
822  $version = time();
823  $filename_arr = pathinfo($_FILES["upload"]["name"]);
824  $extension = $filename_arr["extension"];
825  $newfile = "file_" . md5($_FILES["upload"]["name"]) . "_" . $version . "." . $extension;
826  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
827 
828  $userSolution[$newfile] = array(
829  'solution_id' => $newfile,
830  'value1' => $newfile,
831  'value2' => $_FILES['upload']['name'],
832  'tstamp' => $version,
833  'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
834  );
835  }
836  }
837  }
838 
839  $previewSession->setParticipantsSolution($userSolution);
840  }
841 
845  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
846  {
847  $this->handleSubmission($active_id, $pass, $obligationsAnswered, $authorized);
848  }
849 
859  protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
860  {
861  if (!$authorized) {
862  return;
863  }
864 
865  if ($this->isCompletionBySubmissionEnabled()) {
866  $maxpoints = assQuestion::_getMaximumPoints($this->getId());
867 
868  if ($this->getUploadedFiles($active_id, $pass, $authorized)) {
869  $points = $maxpoints;
870  } else {
871  // fau: testNav - don't set reached points if no file is available
872  return;
873  // fau.
874  }
875 
876  assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
877 
878  // update learning progress
879  include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
880  include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
882  ilObjTest::_getObjectIDFromActiveID((int) $active_id),
883  ilObjTestAccess::_getParticipantId((int) $active_id)
884  );
885  }
886  }
887 
893  public function getQuestionType()
894  {
895  return "assFileUpload";
896  }
897 
903  public function getAdditionalTableName()
904  {
905  return "qpl_qst_fileupload";
906  }
907 
913  public function getAnswerTableName()
914  {
915  return "";
916  }
917 
923  public function deleteAnswers($question_id)
924  {
925  }
926 
931  public function getRTETextWithMediaObjects()
932  {
933  $text = parent::getRTETextWithMediaObjects();
934  return $text;
935  }
936 
940  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
941  {
942  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
943 
944  $i = 1;
945  $solutions = $this->getSolutionValues($active_id, $pass);
946  foreach ($solutions as $solution) {
947  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
948  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
949  if (strlen($solution["value1"])) {
950  $worksheet->setCell($startrow + $i, 1, $solution["value1"]);
951  $worksheet->setCell($startrow + $i, 2, $solution["value2"]);
952  }
953  $i++;
954  }
955 
956  return $startrow + $i + 1;
957  }
958 
971  public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
972  {
973  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
974  $import = new assFileUploadImport($this);
975  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
976  }
977 
984  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
985  {
986  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
987  $export = new assFileUploadExport($this);
988  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
989  }
990 
996  public function getBestSolution($active_id, $pass)
997  {
998  $user_solution = array();
999  return $user_solution;
1000  }
1001 
1007  public function getMaxSize()
1008  {
1009  return $this->maxsize;
1010  }
1011 
1017  public function setMaxSize($a_value)
1018  {
1019  $this->maxsize = $a_value;
1020  }
1021 
1027  public function getAllowedExtensionsArray()
1028  {
1029  if (strlen($this->allowedextensions)) {
1030  return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1031  }
1032  return array();
1033  }
1034 
1040  public function getAllowedExtensions()
1041  {
1042  return $this->allowedextensions;
1043  }
1044 
1050  public function setAllowedExtensions($a_value)
1051  {
1052  $this->allowedextensions = strtolower(trim($a_value));
1053  }
1054 
1058  public function __get($value)
1059  {
1060  switch ($value) {
1061  case "maxsize":
1062  return $this->getMaxSize();
1063  break;
1064  case "allowedextensions":
1065  return $this->getAllowedExtensions();
1066  break;
1067  case 'completion_by_submission':
1068  return $this->isCompletionBySubmissionEnabled();
1069  break;
1070  default:
1071  return parent::__get($value);
1072  break;
1073  }
1074  }
1075 
1079  public function __set($key, $value)
1080  {
1081  switch ($key) {
1082  case "maxsize":
1083  $this->setMaxSize($value);
1084  break;
1085  case "allowedextensions":
1086  $this->setAllowedExtensions($value);
1087  break;
1088  case 'completion_by_submission':
1089  $this->setCompletionBySubmission($value);
1090  break;
1091  default:
1092  parent::__set($key, $value);
1093  break;
1094  }
1095  }
1096 
1104  public function hasFileUploads($test_id)
1105  {
1106  global $ilDB;
1107  $query = "
1108  SELECT tst_solutions.solution_id
1109  FROM tst_solutions, tst_active, qpl_questions
1110  WHERE tst_solutions.active_fi = tst_active.active_id
1111  AND tst_solutions.question_fi = qpl_questions.question_id
1112  AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
1113  $result = $ilDB->queryF(
1114  $query,
1115  array("integer", "integer"),
1116  array($this->getId(), $test_id)
1117  );
1118  if ($result->numRows() > 0) {
1119  return true;
1120  } else {
1121  return false;
1122  }
1123  }
1124 
1130  public function deliverFileUploadZIPFile($test_id, $test_title)
1131  {
1132  global $ilDB, $lng;
1133 
1134  require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1135  $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1136 
1137  $exporter->setTestId($test_id);
1138  $exporter->setTestTitle($test_title);
1139  $exporter->setQuestion($this);
1140 
1141  $exporter->build();
1142 
1144  $exporter->getFinalZipFilePath(),
1145  $exporter->getDispoZipFileName(),
1146  $exporter->getZipFileMimeType(),
1147  false,
1148  true
1149  );
1150  }
1151 
1161  {
1163  }
1164 
1174  public function setCompletionBySubmission($bool)
1175  {
1176  $this->completion_by_submission = (bool) $bool;
1177  return $this;
1178  }
1179 
1191  public function isAnswered($active_id, $pass = null)
1192  {
1193  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1194 
1195  return $numExistingSolutionRecords > 0;
1196  }
1197 
1208  public static function isObligationPossible($questionId)
1209  {
1210  return true;
1211  }
1212 
1213  public function isAutosaveable()
1214  {
1215  return false;
1216  }
1217 
1218  // fau: testNav - new function getTestQuestionConfig()
1225  // hey: refactored identifiers
1227  // hey.
1228  {
1229  // hey: refactored identifiers
1230  return parent::buildTestPresentationConfig()
1231  // hey.
1232  ->setFormChangeDetectionEnabled(false);
1233  }
1234  // fau.
1235 
1236  // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1240  protected function isFileDeletionAction()
1241  {
1242  require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1244  }
1245 
1249  protected function isFileDeletionSubmitAvailable()
1250  {
1251  return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1252  }
1253 
1257  protected function isFileReuseSubmitAvailable()
1258  {
1259  return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1260  }
1261 
1265  protected function isFileReuseHandlingRequired()
1266  {
1267  if (!$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed()) {
1268  return false;
1269  }
1270 
1271  if (!$this->isFileReuseSubmitAvailable()) {
1272  return false;
1273  }
1274 
1275  return true;
1276  }
1277 
1281  protected function isFileUploadAvailable()
1282  {
1283  if (!isset($_FILES['upload'])) {
1284  return false;
1285  }
1286 
1287  if (!isset($_FILES['upload']['tmp_name'])) {
1288  return false;
1289  }
1290 
1291  return strlen($_FILES['upload']['tmp_name']) > 0;
1292  }
1293  // hey.
1294 }
$files
Definition: add-vimline.php:18
getPreviewFileUploadPathWeb($userId)
Returns the filesystem path for file uploads.
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getId()
Gets the id of the assQuestion object.
getAllowedExtensions()
Get allowed file extensions.
savePreviewData(ilAssQuestionPreviewSession $previewSession)
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:84
Class for file upload question exports.
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
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 ...
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
setId($id=-1)
Sets the id of the assQuestion object.
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
$error
Definition: Error.php:17
if($format !==null) $name
Definition: metadata.php:146
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...
getUserSolutionPreferingIntermediate($active_id, $pass=null)
Get the user solution preferring the intermediate solution.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
getMaxFilesizeAsString()
Return the maximum allowed file size as string.
checkUpload()
Check file upload.
calculateReachedPointsForSolution($userSolution)
$text
Definition: errorreport.php:18
getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
getQuestionType()
Returns the question type of the question.
setMaxSize($a_value)
Set max file size.
$query
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 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)
$i
Definition: disco.tpl.php:19
getAdditionalTableName()
Returns the name of the additional question data table in the database.
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
__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.
$key
Definition: croninfo.php:18
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.
static deliverFile( $a_file, $a_filename, $a_mime='', $isInline=false, $removeAfterDelivery=false, $a_exit_after=true)
deliver file for download via browser.
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)