ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 $DIC;
88  $ilDB = $DIC['ilDB'];
89  $ilDB->manipulateF(
90  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
91  array( "integer" ),
92  array( $this->getId() )
93  );
94  $ilDB->manipulateF(
95  "INSERT INTO " . $this->getAdditionalTableName(
96  ) . " (question_fi, maxsize, allowedextensions, compl_by_submission) VALUES (%s, %s, %s, %s)",
97  array( "integer", "float", "text", "integer" ),
98  array(
99  $this->getId(),
100  (strlen($this->getMaxSize())) ? $this->getMaxSize() : null,
101  (strlen($this->getAllowedExtensions())) ? $this->getAllowedExtensions() : null,
102  (int) $this->isCompletionBySubmissionEnabled()
103  )
104  );
105  }
106 
112  public function loadFromDb($question_id)
113  {
114  global $DIC;
115  $ilDB = $DIC['ilDB'];
116  $result = $ilDB->queryF(
117  "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",
118  array("integer"),
119  array($question_id)
120  );
121  if ($result->numRows() == 1) {
122  $data = $ilDB->fetchAssoc($result);
123  $this->setId($question_id);
124  $this->setTitle($data["title"]);
125  $this->setComment($data["description"]);
126  $this->setNrOfTries($data['nr_of_tries']);
127  $this->setSuggestedSolution($data["solution_hint"]);
128  $this->setOriginalId($data["original_id"]);
129  $this->setObjId($data["obj_fi"]);
130  $this->setAuthor($data["author"]);
131  $this->setOwner($data["owner"]);
132  $this->setPoints($data["points"]);
133 
134  include_once("./Services/RTE/classes/class.ilRTE.php");
135  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
136  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
137  $this->setMaxSize($data["maxsize"]);
138  $this->setAllowedExtensions($data["allowedextensions"]);
139  $this->setCompletionBySubmission($data['compl_by_submission'] == 1 ? true : false);
140 
141  try {
142  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
143  } catch (ilTestQuestionPoolException $e) {
144  }
145  }
146  parent::loadFromDb($question_id);
147  }
148 
152  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
153  {
154  if ($this->id <= 0) {
155  // The question has not been saved. It cannot be duplicated
156  return;
157  }
158  // duplicate the question in database
159  $this_id = $this->getId();
160  $thisObjId = $this->getObjId();
161 
162  $clone = $this;
163  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
165  $clone->id = -1;
166 
167  if ((int) $testObjId > 0) {
168  $clone->setObjId($testObjId);
169  }
170 
171  if ($title) {
172  $clone->setTitle($title);
173  }
174 
175  if ($author) {
176  $clone->setAuthor($author);
177  }
178  if ($owner) {
179  $clone->setOwner($owner);
180  }
181 
182  if ($for_test) {
183  $clone->saveToDb($original_id);
184  } else {
185  $clone->saveToDb();
186  }
187 
188  // copy question page content
189  $clone->copyPageOfQuestion($this_id);
190  // copy XHTML media objects
191  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
192 
193  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
194 
195  return $clone->id;
196  }
197 
201  public function copyObject($target_questionpool_id, $title = "")
202  {
203  if ($this->id <= 0) {
204  // The question has not been saved. It cannot be duplicated
205  return;
206  }
207  // duplicate the question in database
208  $clone = $this;
209  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
211  $clone->id = -1;
212  $source_questionpool_id = $this->getObjId();
213  $clone->setObjId($target_questionpool_id);
214  if ($title) {
215  $clone->setTitle($title);
216  }
217  $clone->saveToDb();
218 
219  // copy question page content
220  $clone->copyPageOfQuestion($original_id);
221  // copy XHTML media objects
222  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
223 
224  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
225 
226  return $clone->id;
227  }
228 
229  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
230  {
231  if ($this->id <= 0) {
232  // The question has not been saved. It cannot be duplicated
233  return;
234  }
235 
236  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
237 
238  $sourceQuestionId = $this->id;
239  $sourceParentId = $this->getObjId();
240 
241  // duplicate the question in database
242  $clone = $this;
243  $clone->id = -1;
244 
245  $clone->setObjId($targetParentId);
246 
247  if ($targetQuestionTitle) {
248  $clone->setTitle($targetQuestionTitle);
249  }
250 
251  $clone->saveToDb();
252  // copy question page content
253  $clone->copyPageOfQuestion($sourceQuestionId);
254  // copy XHTML media objects
255  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
256 
257  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
258 
259  return $clone->id;
260  }
261 
267  public function getMaximumPoints()
268  {
269  return $this->getPoints();
270  }
271 
282  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
283  {
284  if ($returndetails) {
285  throw new ilTestException('return details not implemented for ' . __METHOD__);
286  }
287 
288  if ($this->isCompletionBySubmissionEnabled()) {
289  if (is_null($pass)) {
290  $pass = $this->getSolutionMaxPass($active_id);
291  }
292 
293  global $DIC;
294 
295  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
296 
297  while ($data = $DIC->database()->fetchAssoc($result)) {
298  if ($this->isDummySolutionRecord($data)) {
299  continue;
300  }
301 
302  return $this->getPoints();
303  }
304  }
305 
306  return 0;
307  }
308 
309  protected function calculateReachedPointsForSolution($userSolution)
310  {
311  if ($this->isCompletionBySubmissionEnabled() && count($userSolution)) {
312  return $this->getPoints();
313  }
314 
315  return 0;
316  }
317 
323  public function checkUpload()
324  {
325  $this->lng->loadLanguageModule("form");
326  // remove trailing '/'
327  $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
328 
329  $filename = $_FILES["upload"]["name"];
330  $filename_arr = pathinfo($_FILES["upload"]["name"]);
331  $suffix = $filename_arr["extension"];
332  $mimetype = $_FILES["upload"]["type"];
333  $size_bytes = $_FILES["upload"]["size"];
334  $temp_name = $_FILES["upload"]["tmp_name"];
335  $error = $_FILES["upload"]["error"];
336 
337  if ($size_bytes > $this->getMaxFilesizeInBytes()) {
338  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
339  return false;
340  }
341 
342  // error handling
343  if ($error > 0) {
344  switch ($error) {
345  case UPLOAD_ERR_INI_SIZE:
346  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
347  return false;
348  break;
349 
350  case UPLOAD_ERR_FORM_SIZE:
351  ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
352  return false;
353  break;
354 
355  case UPLOAD_ERR_PARTIAL:
356  ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
357  return false;
358  break;
359 
360  case UPLOAD_ERR_NO_FILE:
361  ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
362  return false;
363  break;
364 
365  case UPLOAD_ERR_NO_TMP_DIR:
366  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
367  return false;
368  break;
369 
370  case UPLOAD_ERR_CANT_WRITE:
371  ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
372  return false;
373  break;
374 
375  case UPLOAD_ERR_EXTENSION:
376  ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
377  return false;
378  break;
379  }
380  }
381 
382  // check suffixes
383  if (count($this->getAllowedExtensionsArray())) {
384  if (!strlen($suffix)) {
385  ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
386  return false;
387  }
388 
389  if (!in_array(strtolower($suffix), $this->getAllowedExtensionsArray())) {
390  ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
391  return false;
392  }
393  }
394 
395  // virus handling
396  if (strlen($temp_name)) {
397  $vir = ilUtil::virusHandling($temp_name, $filename);
398  if ($vir[0] == false) {
399  ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found") . "<br />" . $vir[1], true);
400  return false;
401  }
402  }
403  return true;
404  }
405 
409  public function getFileUploadPath($test_id, $active_id, $question_id = null)
410  {
411  if (is_null($question_id)) {
412  $question_id = $this->getId();
413  }
414  return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
415  }
416 
420  protected function getPreviewFileUploadPath($userId)
421  {
422  return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
423  }
424 
430  public function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
431  {
432  if (is_null($question_id)) {
433  $question_id = $this->getId();
434  }
435  include_once "./Services/Utilities/classes/class.ilUtil.php";
436  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
437  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
438  }
439 
443  protected function getPreviewFileUploadPathWeb($userId)
444  {
445  include_once "./Services/Utilities/classes/class.ilUtil.php";
446  $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
447  return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
448  }
449 
455  public function getUploadedFiles($active_id, $pass = null, $authorized = true)
456  {
457  global $DIC;
458  $ilDB = $DIC['ilDB'];
459 
460  if (is_null($pass)) {
461  $pass = $this->getSolutionMaxPass($active_id);
462  }
463  // fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
464  $result = $ilDB->queryF(
465  "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",
466  array("integer", "integer", "integer", 'integer'),
467  array($active_id, $this->getId(), $pass, (int) $authorized)
468  );
469  // fau.
470  $found = array();
471 
472  while ($data = $ilDB->fetchAssoc($result)) {
473  array_push($found, $data);
474  }
475 
476  return $found;
477  }
478 
479  public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
480  {
481  return (array) $previewSession->getParticipantsSolution();
482  }
483 
489  public function getUploadedFilesForWeb($active_id, $pass)
490  {
491  global $DIC;
492  $ilDB = $DIC['ilDB'];
493 
494  $found = $this->getUploadedFiles($active_id, $pass);
495  $result = $ilDB->queryF(
496  "SELECT test_fi FROM tst_active WHERE active_id = %s",
497  array('integer'),
498  array($active_id)
499  );
500  if ($result->numRows() == 1) {
501  $row = $ilDB->fetchAssoc($result);
502  $test_id = $row["test_fi"];
503  $path = $this->getFileUploadPathWeb($test_id, $active_id);
504  foreach ($found as $idx => $data) {
505  $found[$idx]['webpath'] = $path;
506  }
507  }
508  return $found;
509  }
510 
516  protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
517  {
518  global $DIC;
519  $ilDB = $DIC['ilDB'];
520 
521  $pass = null;
522  $active_id = null;
523  foreach ($files as $solution_id) {
524  $result = $ilDB->queryF(
525  "SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
526  array("integer", 'integer'),
527  array($solution_id, (int) $authorized)
528  );
529  if ($result->numRows() == 1) {
530  $data = $ilDB->fetchAssoc($result);
531  $pass = $data['pass'];
532  $active_id = $data['active_fi'];
533  @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
534  }
535  }
536  foreach ($files as $solution_id) {
537  $affectedRows = $ilDB->manipulateF(
538  "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
539  array("integer", 'integer'),
540  array($solution_id, $authorized)
541  );
542  }
543  }
544 
545  // fau: testNav new function deleteUnusedFiles()
552  protected function deleteUnusedFiles($test_id, $active_id, $pass)
553  {
554  // read all solutions (authorized and intermediate) from all steps
555  $step = $this->getStep();
556  $this->setStep(null);
557  $solutions = array_merge(
558  $this->getSolutionValues($active_id, $pass, true),
559  $this->getSolutionValues($active_id, $pass, false)
560  );
561  $this->setStep($step);
562 
563  // get the used files from these solutions
564  $used_files = array();
565  foreach ($solutions as $solution) {
566  $used_files[] = $solution['value1'];
567  }
568 
569  // read the existing files for user and pass
570  // delete all files that are not used in the solutions
571  $uploadPath = $this->getFileUploadPath($test_id, $active_id);
572  if (is_dir($uploadPath) && is_readable($uploadPath)) {
573  $iter = new \RegexIterator(new \DirectoryIterator($uploadPath), '/^file_' . $active_id . '_' . $pass . '_(.*)/');
574  foreach ($iter as $file) {
576  if ($file->isFile() && !in_array($file->getFilename(), $used_files)) {
577  unlink($file->getPathname());
578  }
579  }
580  }
581  }
582  // fau.
583 
584  protected function deletePreviewFileUploads($userId, $userSolution, $files)
585  {
586  foreach ($files as $name) {
587  if (isset($userSolution[$name])) {
588  unset($userSolution[$name]);
589  @unlink($this->getPreviewFileUploadPath($userId) . $name);
590  }
591  }
592 
593  return $userSolution;
594  }
595 
601  public function getMaxFilesizeAsString()
602  {
603  $size = $this->getMaxFilesizeInBytes();
604  if ($size < 1024) {
605  $max_filesize = sprintf("%d Bytes", $size);
606  } elseif ($size < 1024 * 1024) {
607  $max_filesize = sprintf("%.1f KB", $size / 1024);
608  } else {
609  $max_filesize = sprintf("%.1f MB", $size / 1024 / 1024);
610  }
611 
612  return $max_filesize;
613  }
614 
620  public function getMaxFilesizeInBytes()
621  {
622  if (strlen($this->getMaxSize())) {
623  return $this->getMaxSize();
624  } else {
625  // get the value for the maximal uploadable filesize from the php.ini (if available)
626  $umf = get_cfg_var("upload_max_filesize");
627  // get the value for the maximal post data from the php.ini (if available)
628  $pms = get_cfg_var("post_max_size");
629 
630  //convert from short-string representation to "real" bytes
631  $multiplier_a = array("K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024);
632 
633  $umf_parts = preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
634  $pms_parts = preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
635 
636  if (count($umf_parts) == 2) {
637  $umf = $umf_parts[0] * $multiplier_a[$umf_parts[1]];
638  }
639  if (count($pms_parts) == 2) {
640  $pms = $pms_parts[0] * $multiplier_a[$pms_parts[1]];
641  }
642 
643  // use the smaller one as limit
644  $max_filesize = min($umf, $pms);
645 
646  if (!$max_filesize) {
647  $max_filesize = max($umf, $pms);
648  }
649  return $max_filesize;
650  }
651  }
652 
653  // hey: prevPassSolutions - refactored method to get intermediate/authorized
654  // as well as upload, delete and previous files working
655  // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
664  public function saveWorkingData($active_id, $pass = null, $authorized = true)
665  {
666  $pass = $this->ensureCurrentTestPass($active_id, $pass);
667  $test_id = $this->lookupTestId($active_id);
668 
669  $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
670 
671  $entered_values = false;
672 
673  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
674  if ($authorized == false) {
675  $this->forceExistingIntermediateSolution($active_id, $pass, true);
676  }
677 
678  if ($this->isFileDeletionAction()) {
679  if ($this->isFileDeletionSubmitAvailable()) {
680  foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id) {
681  $this->removeSolutionRecordById($solution_id);
682  }
683  } else {
684  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
685  }
686  } else {
687  if ($this->isFileReuseHandlingRequired()) {
688  foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId) {
689  $solution = $this->getSolutionRecordById($solutionId);
690 
691  $this->saveCurrentSolution(
692  $active_id,
693  $pass,
694  $solution['value1'],
695  $solution['value2'],
696  false,
697  $solution['tstamp']
698  );
699  }
700  }
701 
702  if ($uploadHandlingRequired) {
703  if (!@file_exists($this->getFileUploadPath($test_id, $active_id))) {
705  }
706 
707  $solutionFileVersioningUploadTS = time();
708  $filename_arr = pathinfo($_FILES["upload"]["name"]);
709  $extension = $filename_arr["extension"];
710  $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
711 
712  include_once 'Services/Utilities/classes/class.ilFileUtils.php';
713  $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
714  $newfile = ilFileUtils::getValidFilename($newfile);
715 
716  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
717 
718  $this->saveCurrentSolution(
719  $active_id,
720  $pass,
721  $newfile,
722  $dispoFilename,
723  false,
724  $solutionFileVersioningUploadTS
725  );
726 
727  $entered_values = true;
728  }
729  }
730 
731  if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass)) {
732  // remove the dummy record of the intermediate solution
733  $this->deleteDummySolutionRecord($active_id, $pass);
734 
735  // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
736  $this->removeCurrentSolution($active_id, $pass, true);
737  $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
738  }
739 
740  $this->deleteUnusedFiles($test_id, $active_id, $pass);
741  });
742 
743  if ($entered_values) {
744  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
746  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
747  }
748  } else {
749  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
751  assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
752  }
753  }
754 
755  return true;
756  }
757  // hey.
758 
759  // fau: testNav - remove dummy value when intermediate solution is got for test display
766  public function getUserSolutionPreferingIntermediate($active_id, $pass = null)
767  {
768  $solution = $this->getSolutionValues($active_id, $pass, false);
769 
770  if (!count($solution)) {
771  $solution = $this->getSolutionValues($active_id, $pass, true);
772  } else {
773  $cleaned = array();
774  foreach ($solution as $row) {
775  if (!empty($row['value1'])) {
776  $cleaned[] = $row;
777  }
778  }
779  $solution = $cleaned;
780  }
781 
782  return $solution;
783  }
784  // fau.
785 
786 
787  // fau: testNav - remove unused files if an intermediate solution is removed
794  public function removeIntermediateSolution($active_id, $pass)
795  {
796  global $DIC;
797  $ilDB = $DIC['ilDB'];
798 
799  $result = parent::removeIntermediateSolution($active_id, $pass);
800 
801  // get the current test id
802  // hey: prevPassSolutions - exract until you drop :-D
803  $test_id = $this->lookupTestId($active_id);
804  // hey.
805 
806  $this->deleteUnusedFiles($test_id, $active_id, $pass);
807 
808  return $result;
809  }
810  // fau.
811 
812 
813  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
814  {
815  $userSolution = $previewSession->getParticipantsSolution();
816 
817  if (!is_array($userSolution)) {
818  $userSolution = array();
819  }
820 
821  // hey: prevPassSolutions - readability spree - get a chance to understand the code
822  if ($this->isFileDeletionAction()) {
823  // hey.
824  // hey: prevPassSolutions - readability spree - get a chance to understand the code
825  if ($this->isFileDeletionSubmitAvailable()) {
826  // hey.
827  $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
828  } else {
829  ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
830  }
831  } else {
832  // hey: prevPassSolutions - readability spree - get a chance to understand the code
833  if ($this->isFileUploadAvailable()) {
834  // hey.
835  if ($this->checkUpload()) {
836  if (!@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId()))) {
837  ilUtil::makeDirParents($this->getPreviewFileUploadPath($previewSession->getUserId()));
838  }
839 
840  $version = time();
841  $filename_arr = pathinfo($_FILES["upload"]["name"]);
842  $extension = $filename_arr["extension"];
843  $newfile = "file_" . md5($_FILES["upload"]["name"]) . "_" . $version . "." . $extension;
844  ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
845 
846  $userSolution[$newfile] = array(
847  'solution_id' => $newfile,
848  'value1' => $newfile,
849  'value2' => $_FILES['upload']['name'],
850  'tstamp' => $version,
851  'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
852  );
853  }
854  }
855  }
856 
857  $previewSession->setParticipantsSolution($userSolution);
858  }
859 
869  protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
870  {
871  if (!$authorized) {
872  return;
873  }
874 
875  if ($this->isCompletionBySubmissionEnabled()) {
876  $maxpoints = assQuestion::_getMaximumPoints($this->getId());
877 
878  if ($this->getUploadedFiles($active_id, $pass, $authorized)) {
879  $points = $maxpoints;
880  } else {
881  // fau: testNav - don't set reached points if no file is available
882  return;
883  // fau.
884  }
885 
886  assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
887 
888  // update learning progress
889  include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
890  include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
892  ilObjTest::_getObjectIDFromActiveID((int) $active_id),
893  ilObjTestAccess::_getParticipantId((int) $active_id)
894  );
895  }
896  }
897 
903  public function getQuestionType()
904  {
905  return "assFileUpload";
906  }
907 
913  public function getAdditionalTableName()
914  {
915  return "qpl_qst_fileupload";
916  }
917 
923  public function getAnswerTableName()
924  {
925  return "";
926  }
927 
933  public function deleteAnswers($question_id)
934  {
935  }
936 
941  public function getRTETextWithMediaObjects()
942  {
943  $text = parent::getRTETextWithMediaObjects();
944  return $text;
945  }
946 
950  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
951  {
952  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
953 
954  $i = 1;
955  $solutions = $this->getSolutionValues($active_id, $pass);
956  foreach ($solutions as $solution) {
957  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
958  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
959  if (strlen($solution["value1"])) {
960  $worksheet->setCell($startrow + $i, 1, $solution["value1"]);
961  $worksheet->setCell($startrow + $i, 2, $solution["value2"]);
962  }
963  $i++;
964  }
965 
966  return $startrow + $i + 1;
967  }
968 
981  public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
982  {
983  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
984  $import = new assFileUploadImport($this);
985  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
986  }
987 
994  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
995  {
996  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
997  $export = new assFileUploadExport($this);
998  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
999  }
1000 
1006  public function getBestSolution($active_id, $pass)
1007  {
1008  $user_solution = array();
1009  return $user_solution;
1010  }
1011 
1017  public function getMaxSize()
1018  {
1019  return $this->maxsize;
1020  }
1021 
1027  public function setMaxSize($a_value)
1028  {
1029  $this->maxsize = $a_value;
1030  }
1031 
1037  public function getAllowedExtensionsArray()
1038  {
1039  if (strlen($this->allowedextensions)) {
1040  return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1041  }
1042  return array();
1043  }
1044 
1050  public function getAllowedExtensions()
1051  {
1052  return $this->allowedextensions;
1053  }
1054 
1060  public function setAllowedExtensions($a_value)
1061  {
1062  $this->allowedextensions = strtolower(trim($a_value));
1063  }
1064 
1068  public function __get($value)
1069  {
1070  switch ($value) {
1071  case "maxsize":
1072  return $this->getMaxSize();
1073  break;
1074  case "allowedextensions":
1075  return $this->getAllowedExtensions();
1076  break;
1077  case 'completion_by_submission':
1078  return $this->isCompletionBySubmissionEnabled();
1079  break;
1080  default:
1081  return parent::__get($value);
1082  break;
1083  }
1084  }
1085 
1089  public function __set($key, $value)
1090  {
1091  switch ($key) {
1092  case "maxsize":
1093  $this->setMaxSize($value);
1094  break;
1095  case "allowedextensions":
1096  $this->setAllowedExtensions($value);
1097  break;
1098  case 'completion_by_submission':
1099  $this->setCompletionBySubmission($value);
1100  break;
1101  default:
1102  parent::__set($key, $value);
1103  break;
1104  }
1105  }
1106 
1114  public function hasFileUploads($test_id)
1115  {
1116  global $DIC;
1117  $ilDB = $DIC['ilDB'];
1118  $query = "
1119  SELECT tst_solutions.solution_id
1120  FROM tst_solutions, tst_active, qpl_questions
1121  WHERE tst_solutions.active_fi = tst_active.active_id
1122  AND tst_solutions.question_fi = qpl_questions.question_id
1123  AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
1124  $result = $ilDB->queryF(
1125  $query,
1126  array("integer", "integer"),
1127  array($this->getId(), $test_id)
1128  );
1129  if ($result->numRows() > 0) {
1130  return true;
1131  } else {
1132  return false;
1133  }
1134  }
1135 
1141  public function deliverFileUploadZIPFile($ref_id, $test_id, $test_title)
1142  {
1143  global $DIC;
1144  $ilDB = $DIC['ilDB'];
1145  $lng = $DIC['lng'];
1146 
1147  require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1148  $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1149 
1150  $exporter->setRefId($ref_id);
1151  $exporter->setTestId($test_id);
1152  $exporter->setTestTitle($test_title);
1153  $exporter->setQuestion($this);
1154 
1155  $exporter->build();
1156 
1158  $exporter->getFinalZipFilePath(),
1159  $exporter->getDispoZipFileName(),
1160  $exporter->getZipFileMimeType(),
1161  false,
1162  true
1163  );
1164  }
1165 
1175  {
1177  }
1178 
1188  public function setCompletionBySubmission($bool)
1189  {
1190  $this->completion_by_submission = (bool) $bool;
1191  return $this;
1192  }
1193 
1205  public function isAnswered($active_id, $pass = null)
1206  {
1207  $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1208 
1209  return $numExistingSolutionRecords > 0;
1210  }
1211 
1222  public static function isObligationPossible($questionId)
1223  {
1224  return true;
1225  }
1226 
1227  public function isAutosaveable()
1228  {
1229  return false;
1230  }
1231 
1232  // fau: testNav - new function getTestQuestionConfig()
1239  // hey: refactored identifiers
1241  // hey.
1242  {
1243  // hey: refactored identifiers
1244  return parent::buildTestPresentationConfig()
1245  // hey.
1246  ->setFormChangeDetectionEnabled(false);
1247  }
1248  // fau.
1249 
1250  // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1254  protected function isFileDeletionAction()
1255  {
1256  require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1258  }
1259 
1263  protected function isFileDeletionSubmitAvailable()
1264  {
1265  return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1266  }
1267 
1271  protected function isFileReuseSubmitAvailable()
1272  {
1273  return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1274  }
1275 
1279  protected function isFileReuseHandlingRequired()
1280  {
1281  if (!$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed()) {
1282  return false;
1283  }
1284 
1285  if (!$this->isFileReuseSubmitAvailable()) {
1286  return false;
1287  }
1288 
1289  return true;
1290  }
1291 
1295  protected function isFileUploadAvailable()
1296  {
1297  if (!isset($_FILES['upload'])) {
1298  return false;
1299  }
1300 
1301  if (!isset($_FILES['upload']['tmp_name'])) {
1302  return false;
1303  }
1304 
1305  return strlen($_FILES['upload']['tmp_name']) > 0;
1306  }
1307  // hey.
1308 }
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)
$path
Definition: aliased.php:25
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)
$files
Definition: metarefresh.php:49
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.
getBestSolution($active_id, $pass)
Returns the best solution for a given pass of a participant.
global $DIC
Definition: saml.php:7
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.
$version
Definition: build.php:27
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)
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.
isDummySolutionRecord($solutionRecord)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
$filename
Definition: buildRTE.php:89
$row
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)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
$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.
__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.
$data
Definition: bench.php:6
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
getSolutionRecordById($solutionId)