ILIAS  release_7 Revision v7.30-3-g800a261c036
class.assFileUpload.php
Go to the documentation of this file.
1 <?php
2 
19 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
20 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
21 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
22 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjFileHandlingQuestionType.php';
23 
36 {
37  // hey: prevPassSolutions - support reusing selected files
38  const REUSE_FILES_TBL_POSTVAR = 'reusefiles';
39  const DELETE_FILES_TBL_POSTVAR = 'deletefiles';
40  // hey.
41 
42  protected $maxsize;
43 
44  protected $allowedextensions;
45 
47  protected $completion_by_submission = false;
48 
62  public function __construct(
63  $title = "",
64  $comment = "",
65  $author = "",
66  $owner = -1,
67  $question = ""
68  ) {
70  }
71 
77  public function isComplete()
78  {
79  if (
80  strlen($this->title)
81  && ($this->author)
82  && ($this->question)
83  && ($this->getMaximumPoints() >= 0)
84  && is_numeric($this->getMaximumPoints())) {
85  return true;
86  }
87  return false;
88  }
89 
93  public function saveToDb($original_id = "")
94  {
97  parent::saveToDb();
98  }
99 
101  {
102  global $DIC;
103  $ilDB = $DIC['ilDB'];
104  $ilDB->manipulateF(
105  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
106  array( "integer" ),
107  array( $this->getId() )
108  );
109  $ilDB->manipulateF(
110  "INSERT INTO " . $this->getAdditionalTableName(
111  ) . " (question_fi, maxsize, allowedextensions, compl_by_submission) VALUES (%s, %s, %s, %s)",
112  array( "integer", "float", "text", "integer" ),
113  array(
114  $this->getId(),
115  (strlen($this->getMaxSize())) ? $this->getMaxSize() : null,
116  (strlen($this->getAllowedExtensions())) ? $this->getAllowedExtensions() : null,
117  (int) $this->isCompletionBySubmissionEnabled()
118  )
119  );
120  }
121 
127  public function loadFromDb($question_id)
128  {
129  global $DIC;
130  $ilDB = $DIC['ilDB'];
131  $result = $ilDB->queryF(
132  "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",
133  array("integer"),
134  array($question_id)
135  );
136  if ($result->numRows() == 1) {
137  $data = $ilDB->fetchAssoc($result);
138  $this->setId($question_id);
139  $this->setTitle($data["title"]);
140  $this->setComment($data["description"]);
141  $this->setNrOfTries($data['nr_of_tries']);
142  $this->setSuggestedSolution($data["solution_hint"]);
143  $this->setOriginalId($data["original_id"]);
144  $this->setObjId($data["obj_fi"]);
145  $this->setAuthor($data["author"]);
146  $this->setOwner($data["owner"]);
147  $this->setPoints($data["points"]);
148 
149  include_once("./Services/RTE/classes/class.ilRTE.php");
150  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
151  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
152  $this->setMaxSize($data["maxsize"]);
153  $this->setAllowedExtensions($data["allowedextensions"]);
154  $this->setCompletionBySubmission($data['compl_by_submission'] == 1 ? true : false);
155 
156  try {
160  }
161 
162  try {
163  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
164  } catch (ilTestQuestionPoolException $e) {
165  }
166  }
167  parent::loadFromDb($question_id);
168  }
169 
173  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
174  {
175  if ($this->id <= 0) {
176  // The question has not been saved. It cannot be duplicated
177  return;
178  }
179  // duplicate the question in database
180  $this_id = $this->getId();
181  $thisObjId = $this->getObjId();
182 
183  $clone = $this;
184  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
186  $clone->id = -1;
187 
188  if ((int) $testObjId > 0) {
189  $clone->setObjId($testObjId);
190  }
191 
192  if ($title) {
193  $clone->setTitle($title);
194  }
195 
196  if ($author) {
197  $clone->setAuthor($author);
198  }
199  if ($owner) {
200  $clone->setOwner($owner);
201  }
202 
203  if ($for_test) {
204  $clone->saveToDb($original_id);
205  } else {
206  $clone->saveToDb();
207  }
208 
209  // copy question page content
210  $clone->copyPageOfQuestion($this_id);
211  // copy XHTML media objects
212  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
213 
214  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
215 
216  return $clone->id;
217  }
218 
222  public function copyObject($target_questionpool_id, $title = "")
223  {
224  if ($this->id <= 0) {
225  // The question has not been saved. It cannot be duplicated
226  return;
227  }
228  // duplicate the question in database
229  $clone = $this;
230  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
232  $clone->id = -1;
233  $source_questionpool_id = $this->getObjId();
234  $clone->setObjId($target_questionpool_id);
235  if ($title) {
236  $clone->setTitle($title);
237  }
238  $clone->saveToDb();
239 
240  // copy question page content
241  $clone->copyPageOfQuestion($original_id);
242  // copy XHTML media objects
243  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
244 
245  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
246 
247  return $clone->id;
248  }
249 
250  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
251  {
252  if ($this->id <= 0) {
253  // The question has not been saved. It cannot be duplicated
254  return;
255  }
256 
257  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
258 
259  $sourceQuestionId = $this->id;
260  $sourceParentId = $this->getObjId();
261 
262  // duplicate the question in database
263  $clone = $this;
264  $clone->id = -1;
265 
266  $clone->setObjId($targetParentId);
267 
268  if ($targetQuestionTitle) {
269  $clone->setTitle($targetQuestionTitle);
270  }
271 
272  $clone->saveToDb();
273  // copy question page content
274  $clone->copyPageOfQuestion($sourceQuestionId);
275  // copy XHTML media objects
276  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
277 
278  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
279 
280  return $clone->id;
281  }
282 
288  public function getMaximumPoints()
289  {
290  return $this->getPoints();
291  }
292 
303  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
304  {
305  if ($returndetails) {
306  throw new ilTestException('return details not implemented for ' . __METHOD__);
307  }
308 
309  if ($this->isCompletionBySubmissionEnabled()) {
310  if (is_null($pass)) {
311  $pass = $this->getSolutionMaxPass($active_id);
312  }
313 
314  global $DIC;
315 
316  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
317 
318  while ($data = $DIC->database()->fetchAssoc($result)) {
319  if ($this->isDummySolutionRecord($data)) {
320  continue;
321  }
322 
323  return $this->getPoints();
324  }
325  }
326 
327  return 0;
328  }
329 
330  protected function calculateReachedPointsForSolution($userSolution)
331  {
332  if ($this->isCompletionBySubmissionEnabled() && count($userSolution)) {
333  return $this->getPoints();
334  }
335 
336  return 0;
337  }
338 
344  public function checkUpload()
345  {
346  $this->lng->loadLanguageModule("form");
347  // remove trailing '/'
348  $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
349 
350  $filename = $_FILES["upload"]["name"];
351  $filename_arr = pathinfo($_FILES["upload"]["name"]);
352  $suffix = $filename_arr["extension"];
353  $mimetype = $_FILES["upload"]["type"];
354  $size_bytes = $_FILES["upload"]["size"];
355  $temp_name = $_FILES["upload"]["tmp_name"];
356  $error = $_FILES["upload"]["error"];
357 
358  if ($size_bytes > $this->getMaxFilesizeInBytes()) {
359  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
360  return false;
361  }
362 
363  // error handling
364  if ($error > 0) {
365  switch ($error) {
366  case UPLOAD_ERR_INI_SIZE:
367  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
368  return false;
369  break;
370 
371  case UPLOAD_ERR_FORM_SIZE:
372  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
373  return false;
374  break;
375 
376  case UPLOAD_ERR_PARTIAL:
377  ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
378  return false;
379  break;
380 
381  case UPLOAD_ERR_NO_FILE:
382  ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
383  return false;
384  break;
385 
386  case UPLOAD_ERR_NO_TMP_DIR:
387  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
388  return false;
389  break;
390 
391  case UPLOAD_ERR_CANT_WRITE:
392  ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
393  return false;
394  break;
395 
396  case UPLOAD_ERR_EXTENSION:
397  ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
398  return false;
399  break;
400  }
401  }
402 
403  // check suffixes
404  if (count($this->getAllowedExtensionsArray())) {
405  if (!strlen($suffix)) {
406  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
407  return false;
408  }
409 
410  if (!in_array(strtolower($suffix), $this->getAllowedExtensionsArray())) {
411  ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
412  return false;
413  }
414  }
415 
416  // virus handling
417  if (strlen($temp_name)) {
418  $vir = ilUtil::virusHandling($temp_name, $filename);
419  if ($vir[0] == false) {
420  ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found") . "<br />" . $vir[1], true);
421  return false;
422  }
423  }
424  return true;
425  }
426 
430  public function getFileUploadPath($test_id, $active_id, $question_id = null)
431  {
432  if (is_null($question_id)) {
433  $question_id = $this->getId();
434  }
435  return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
436  }
437 
441  protected function getPreviewFileUploadPath($userId)
442  {
443  return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
444  }
445 
451  public function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
452  {
453  if (is_null($question_id)) {
454  $question_id = $this->getId();
455  }
456  include_once "./Services/Utilities/classes/class.ilUtil.php";
457  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
458  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
459  }
460 
464  protected function getPreviewFileUploadPathWeb($userId)
465  {
466  include_once "./Services/Utilities/classes/class.ilUtil.php";
467  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
468  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
469  }
470 
476  public function getUploadedFiles($active_id, $pass = null, $authorized = true)
477  {
478  global $DIC;
479  $ilDB = $DIC['ilDB'];
480 
481  if (is_null($pass)) {
482  $pass = $this->getSolutionMaxPass($active_id);
483  }
484  // fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
485  $result = $ilDB->queryF(
486  "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",
487  array("integer", "integer", "integer", 'integer'),
488  array($active_id, $this->getId(), $pass, (int) $authorized)
489  );
490  // fau.
491  $found = array();
492 
493  while ($data = $ilDB->fetchAssoc($result)) {
494  array_push($found, $data);
495  }
496 
497  return $found;
498  }
499 
500  public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
501  {
502  return (array) $previewSession->getParticipantsSolution();
503  }
504 
510  public function getUploadedFilesForWeb($active_id, $pass)
511  {
512  global $DIC;
513  $ilDB = $DIC['ilDB'];
514 
515  $found = $this->getUploadedFiles($active_id, $pass);
516  $result = $ilDB->queryF(
517  "SELECT test_fi FROM tst_active WHERE active_id = %s",
518  array('integer'),
519  array($active_id)
520  );
521  if ($result->numRows() == 1) {
522  $row = $ilDB->fetchAssoc($result);
523  $test_id = $row["test_fi"];
524  $path = $this->getFileUploadPathWeb($test_id, $active_id);
525  foreach ($found as $idx => $data) {
526  $found[$idx]['webpath'] = $path;
527  }
528  }
529  return $found;
530  }
531 
537  protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
538  {
539  global $DIC;
540  $ilDB = $DIC['ilDB'];
541 
542  $pass = null;
543  $active_id = null;
544  foreach ($files as $solution_id) {
545  $result = $ilDB->queryF(
546  "SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
547  array("integer", 'integer'),
548  array($solution_id, (int) $authorized)
549  );
550  if ($result->numRows() == 1) {
551  $data = $ilDB->fetchAssoc($result);
552  $pass = $data['pass'];
553  $active_id = $data['active_fi'];
554  @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
555  }
556  }
557  foreach ($files as $solution_id) {
558  $affectedRows = $ilDB->manipulateF(
559  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
560  array("integer", 'integer'),
561  array($solution_id, $authorized)
562  );
563  }
564  }
565 
566  // fau: testNav new function deleteUnusedFiles()
573  protected function deleteUnusedFiles($test_id, $active_id, $pass)
574  {
575  // read all solutions (authorized and intermediate) from all steps
576  $step = $this->getStep();
577  $this->setStep(null);
578  $solutions = array_merge(
579  $this->getSolutionValues($active_id, $pass, true),
580  $this->getSolutionValues($active_id, $pass, false)
581  );
582  $this->setStep($step);
583 
584  // get the used files from these solutions
585  $used_files = array();
586  foreach ($solutions as $solution) {
587  $used_files[] = $solution['value1'];
588  }
589 
590  // read the existing files for user and pass
591  // delete all files that are not used in the solutions
592  $uploadPath = $this->getFileUploadPath($test_id, $active_id);
593  if (is_dir($uploadPath) && is_readable($uploadPath)) {
594  $iter = new \RegexIterator(new \DirectoryIterator($uploadPath), '/^file_' . $active_id . '_' . $pass . '_(.*)/');
595  foreach ($iter as $file) {
597  if ($file->isFile() && !in_array($file->getFilename(), $used_files)) {
598  unlink($file->getPathname());
599  }
600  }
601  }
602  }
603  // fau.
604 
605  protected function deletePreviewFileUploads($userId, $userSolution, $files)
606  {
607  foreach ($files as $name) {
608  if (isset($userSolution[$name])) {
609  unset($userSolution[$name]);
610  @unlink($this->getPreviewFileUploadPath($userId) . $name);
611  }
612  }
613 
614  return $userSolution;
615  }
616 
622  public function getMaxFilesizeAsString()
623  {
624  $size = $this->getMaxFilesizeInBytes();
625  if ($size < 1024) {
626  $max_filesize = sprintf("%d Bytes", $size);
627  } elseif ($size < 1024 * 1024) {
628  $max_filesize = sprintf("%.1f KB", $size / 1024);
629  } else {
630  $max_filesize = sprintf("%.1f MB", $size / 1024 / 1024);
631  }
632 
633  return $max_filesize;
634  }
635 
641  public function getMaxFilesizeInBytes()
642  {
643  if (strlen($this->getMaxSize())) {
644  return $this->getMaxSize();
645  } else {
646  // get the value for the maximal uploadable filesize from the php.ini (if available)
647  $umf = get_cfg_var("upload_max_filesize");
648  // get the value for the maximal post data from the php.ini (if available)
649  $pms = get_cfg_var("post_max_size");
650 
651  //convert from short-string representation to "real" bytes
652  $multiplier_a = array("K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024);
653 
654  $umf_parts = preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
655  $pms_parts = preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
656 
657  if (count($umf_parts) == 2) {
658  $umf = $umf_parts[0] * $multiplier_a[$umf_parts[1]];
659  }
660  if (count($pms_parts) == 2) {
661  $pms = $pms_parts[0] * $multiplier_a[$pms_parts[1]];
662  }
663 
664  // use the smaller one as limit
665  $max_filesize = min($umf, $pms);
666 
667  if (!$max_filesize) {
668  $max_filesize = max($umf, $pms);
669  }
670  return $max_filesize;
671  }
672  }
673 
674  // hey: prevPassSolutions - refactored method to get intermediate/authorized
675  // as well as upload, delete and previous files working
676  // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
685  public function saveWorkingData($active_id, $pass = null, $authorized = true)
686  {
687  $pass = $this->ensureCurrentTestPass($active_id, $pass);
688  $test_id = $this->lookupTestId($active_id);
689 
690  $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
691 
692  $entered_values = false;
693 
694  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
695  if ($authorized == false) {
696  $this->forceExistingIntermediateSolution($active_id, $pass, true);
697  }
698 
699  if ($this->isFileDeletionAction()) {
700  if ($this->isFileDeletionSubmitAvailable()) {
701  foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id) {
702  $this->removeSolutionRecordById($solution_id);
703  }
704  } else {
705  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
706  }
707  } else {
708  if ($this->isFileReuseHandlingRequired()) {
709  foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId) {
710  $solution = $this->getSolutionRecordById($solutionId);
711 
712  $this->saveCurrentSolution(
713  $active_id,
714  $pass,
715  $solution['value1'],
716  $solution['value2'],
717  false,
718  $solution['tstamp']
719  );
720  }
721  }
722 
723  if ($uploadHandlingRequired) {
724  if (!@file_exists($this->getFileUploadPath($test_id, $active_id))) {
726  }
727 
728  $solutionFileVersioningUploadTS = time();
729  $filename_arr = pathinfo($_FILES["upload"]["name"]);
730  $extension = $filename_arr["extension"];
731  $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
732 
733  include_once 'Services/Utilities/classes/class.ilFileUtils.php';
734  $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
735  $newfile = ilFileUtils::getValidFilename($newfile);
736 
737  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
738 
739  $this->saveCurrentSolution(
740  $active_id,
741  $pass,
742  $newfile,
743  $dispoFilename,
744  false,
745  $solutionFileVersioningUploadTS
746  );
747 
748  $entered_values = true;
749  }
750  }
751 
752  if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass)) {
753  // remove the dummy record of the intermediate solution
754  $this->deleteDummySolutionRecord($active_id, $pass);
755 
756  // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
757  $this->removeCurrentSolution($active_id, $pass, true);
758  $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
759  }
760 
761  $this->deleteUnusedFiles($test_id, $active_id, $pass);
762  });
763 
764  if ($entered_values) {
765  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
767  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
768  }
769  } else {
770  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
772  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
773  }
774  }
775 
776  return true;
777  }
778  // hey.
779 
780  // fau: testNav - remove dummy value when intermediate solution is got for test display
787  public function getUserSolutionPreferingIntermediate($active_id, $pass = null)
788  {
789  $solution = $this->getSolutionValues($active_id, $pass, false);
790 
791  if (!count($solution)) {
792  $solution = $this->getSolutionValues($active_id, $pass, true);
793  } else {
794  $cleaned = array();
795  foreach ($solution as $row) {
796  if (!empty($row['value1'])) {
797  $cleaned[] = $row;
798  }
799  }
800  $solution = $cleaned;
801  }
802 
803  return $solution;
804  }
805  // fau.
806 
807 
808  // fau: testNav - remove unused files if an intermediate solution is removed
815  public function removeIntermediateSolution($active_id, $pass)
816  {
817  global $DIC;
818  $ilDB = $DIC['ilDB'];
819 
820  $result = parent::removeIntermediateSolution($active_id, $pass);
821 
822  // get the current test id
823  // hey: prevPassSolutions - exract until you drop :-D
824  $test_id = $this->lookupTestId($active_id);
825  // hey.
826 
827  $this->deleteUnusedFiles($test_id, $active_id, $pass);
828 
829  return $result;
830  }
831  // fau.
832 
833 
834  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
835  {
836  $userSolution = $previewSession->getParticipantsSolution();
837 
838  if (!is_array($userSolution)) {
839  $userSolution = array();
840  }
841 
842  // hey: prevPassSolutions - readability spree - get a chance to understand the code
843  if ($this->isFileDeletionAction()) {
844  // hey.
845  // hey: prevPassSolutions - readability spree - get a chance to understand the code
846  if ($this->isFileDeletionSubmitAvailable()) {
847  // hey.
848  $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
849  } else {
850  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
851  }
852  } else {
853  // hey: prevPassSolutions - readability spree - get a chance to understand the code
854  if ($this->isFileUploadAvailable()) {
855  // hey.
856  if ($this->checkUpload()) {
857  if (!@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId()))) {
858  ilUtil::makeDirParents($this->getPreviewFileUploadPath($previewSession->getUserId()));
859  }
860 
861  $version = time();
862  $filename_arr = pathinfo($_FILES["upload"]["name"]);
863  $extension = $filename_arr["extension"];
864  $newfile = "file_" . md5($_FILES["upload"]["name"]) . "_" . $version . "." . $extension;
865  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
866 
867  $userSolution[$newfile] = array(
868  'solution_id' => $newfile,
869  'value1' => $newfile,
870  'value2' => $_FILES['upload']['name'],
871  'tstamp' => $version,
872  'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
873  );
874  }
875  }
876  }
877 
878  $previewSession->setParticipantsSolution($userSolution);
879  }
880 
890  protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
891  {
892  if (!$authorized) {
893  return;
894  }
895 
896  if ($this->isCompletionBySubmissionEnabled()) {
897  $maxpoints = assQuestion::_getMaximumPoints($this->getId());
898 
899  if ($this->getUploadedFiles($active_id, $pass, $authorized)) {
900  $points = $maxpoints;
901  } else {
902  // fau: testNav - don't set reached points if no file is available
903  return;
904  // fau.
905  }
906 
907  assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
908 
909  // update learning progress
910  include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
911  include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
913  ilObjTest::_getObjectIDFromActiveID((int) $active_id),
914  ilObjTestAccess::_getParticipantId((int) $active_id)
915  );
916  }
917  }
918 
924  public function getQuestionType()
925  {
926  return "assFileUpload";
927  }
928 
934  public function getAdditionalTableName()
935  {
936  return "qpl_qst_fileupload";
937  }
938 
944  public function getAnswerTableName()
945  {
946  return "";
947  }
948 
954  public function deleteAnswers($question_id)
955  {
956  }
957 
962  public function getRTETextWithMediaObjects()
963  {
964  $text = parent::getRTETextWithMediaObjects();
965  return $text;
966  }
967 
971  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
972  {
973  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
974 
975  $i = 1;
976  $solutions = $this->getSolutionValues($active_id, $pass);
977  foreach ($solutions as $solution) {
978  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
979  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
980  if (strlen($solution["value1"])) {
981  $worksheet->setCell($startrow + $i, 2, $solution["value1"]);
982  $worksheet->setCell($startrow + $i, 3, $solution["value2"]);
983  }
984  $i++;
985  }
986 
987  return $startrow + $i + 1;
988  }
989 
1002  public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping, array $solutionhints = [])
1003  {
1004  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
1005  $import = new assFileUploadImport($this);
1006  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
1007  }
1008 
1015  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
1016  {
1017  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
1018  $export = new assFileUploadExport($this);
1019  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
1020  }
1021 
1027  public function getBestSolution($active_id, $pass)
1028  {
1029  $user_solution = array();
1030  return $user_solution;
1031  }
1032 
1038  public function getMaxSize()
1039  {
1040  return $this->maxsize;
1041  }
1042 
1048  public function setMaxSize($a_value)
1049  {
1050  $this->maxsize = $a_value;
1051  }
1052 
1058  public function getAllowedExtensionsArray()
1059  {
1060  if (strlen($this->allowedextensions)) {
1061  return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1062  }
1063  return array();
1064  }
1065 
1071  public function getAllowedExtensions()
1072  {
1073  return $this->allowedextensions;
1074  }
1075 
1081  public function setAllowedExtensions($a_value)
1082  {
1083  $this->allowedextensions = strtolower(trim($a_value));
1084  }
1085 
1089  public function __get($value)
1090  {
1091  switch ($value) {
1092  case "maxsize":
1093  return $this->getMaxSize();
1094  break;
1095  case "allowedextensions":
1096  return $this->getAllowedExtensions();
1097  break;
1098  case 'completion_by_submission':
1099  return $this->isCompletionBySubmissionEnabled();
1100  break;
1101  default:
1102  return parent::__get($value);
1103  break;
1104  }
1105  }
1106 
1110  public function __set($key, $value)
1111  {
1112  switch ($key) {
1113  case "maxsize":
1114  $this->setMaxSize($value);
1115  break;
1116  case "allowedextensions":
1117  $this->setAllowedExtensions($value);
1118  break;
1119  case 'completion_by_submission':
1120  $this->setCompletionBySubmission($value);
1121  break;
1122  default:
1123  parent::__set($key, $value);
1124  break;
1125  }
1126  }
1127 
1135  public function hasFileUploads($test_id)
1136  {
1137  global $DIC;
1138  $ilDB = $DIC['ilDB'];
1139  $query = "
1140  SELECT tst_solutions.solution_id
1141  FROM tst_solutions, tst_active, qpl_questions
1142  WHERE tst_solutions.active_fi = tst_active.active_id
1143  AND tst_solutions.question_fi = qpl_questions.question_id
1144  AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s
1145  AND tst_solutions.value1 is not null";
1146  $result = $ilDB->queryF(
1147  $query,
1148  array("integer", "integer"),
1149  array($this->getId(), $test_id)
1150  );
1151  if ($result->numRows() > 0) {
1152  return true;
1153  } else {
1154  return false;
1155  }
1156  }
1157 
1163  public function deliverFileUploadZIPFile($ref_id, $test_id, $test_title)
1164  {
1165  global $DIC;
1166  $ilDB = $DIC['ilDB'];
1167  $lng = $DIC['lng'];
1168 
1169  require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1170  $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1171 
1172  $exporter->setRefId($ref_id);
1173  $exporter->setTestId($test_id);
1174  $exporter->setTestTitle($test_title);
1175  $exporter->setQuestion($this);
1176 
1177  $exporter->build();
1178 
1180  $exporter->getFinalZipFilePath(),
1181  $exporter->getDispoZipFileName(),
1182  $exporter->getZipFileMimeType(),
1183  false,
1184  true
1185  );
1186  }
1187 
1197  {
1199  }
1200 
1210  public function setCompletionBySubmission($bool)
1211  {
1212  $this->completion_by_submission = (bool) $bool;
1213  return $this;
1214  }
1215 
1227  public function isAnswered($active_id, $pass = null)
1228  {
1229  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1230 
1231  return $numExistingSolutionRecords > 0;
1232  }
1233 
1244  public static function isObligationPossible($questionId)
1245  {
1246  return true;
1247  }
1248 
1249  public function isAutosaveable()
1250  {
1251  return false;
1252  }
1253 
1254  // fau: testNav - new function getTestQuestionConfig()
1261  // hey: refactored identifiers
1263  // hey.
1264  {
1265  // hey: refactored identifiers
1266  return parent::buildTestPresentationConfig()
1267  // hey.
1268  ->setFormChangeDetectionEnabled(false);
1269  }
1270  // fau.
1271 
1272  // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1276  protected function isFileDeletionAction()
1277  {
1278  require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1280  }
1281 
1285  protected function isFileDeletionSubmitAvailable()
1286  {
1287  return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1288  }
1289 
1293  protected function isFileReuseSubmitAvailable()
1294  {
1295  return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1296  }
1297 
1301  protected function isFileReuseHandlingRequired()
1302  {
1303  if (!$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed()) {
1304  return false;
1305  }
1306 
1307  if (!$this->isFileReuseSubmitAvailable()) {
1308  return false;
1309  }
1310 
1311  return true;
1312  }
1313 
1317  protected function isFileUploadAvailable()
1318  {
1319  if (!isset($_FILES['upload'])) {
1320  return false;
1321  }
1322 
1323  if (!isset($_FILES['upload']['tmp_name'])) {
1324  return false;
1325  }
1326 
1327  return strlen($_FILES['upload']['tmp_name']) > 0;
1328  }
1329  // hey.
1330 }
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.
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.
deliverFileUploadZIPFile($ref_id, $test_id, $test_title)
Generates a ZIP file containing all file uploads for a given test and the original id of the question...
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...
$data
Definition: storeScorm.php:23
__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.
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.
fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping, array $solutionhints=[])
Creates a question from a QTI file.
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)
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...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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)
if($format !==null) $name
Definition: metadata.php:230
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)
getUserSolutionPreferingIntermediate($active_id, $pass=null)
Get the user solution preferring the intermediate solution.
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)
global $DIC
Definition: goto.php:24
getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
getQuestionType()
Returns the question type of the question.
setMaxSize($a_value)
Set max file size.
$query
const CLIENT_WEB_DIR
Definition: constants.php:45
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.
isDummySolutionRecord($solutionRecord)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
$filename
Definition: buildRTE.php:89
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)
__construct(Container $dic, ilPlugin $plugin)
getTestPresentationConfig()
Get the test question configuration (initialised once)
global $ilDB
setOriginalId($original_id)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
getAdditionalTableName()
Returns the name of the additional question data table in the database.
setLifecycle(ilAssQuestionLifecycle $lifecycle)
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.
__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.
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.
$i
Definition: metadata.php:24
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)