ILIAS  release_7 Revision v7.30-3-g800a261c036
class.assFileUpload.php
Go to the documentation of this file.
1<?php
2
19require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
20require_once './Modules/Test/classes/inc.AssessmentConstants.php';
21require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
22require_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
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,
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']);
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()))) {
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
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
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 {
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
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}
$result
$size
Definition: RandomTest.php:84
$filename
Definition: buildRTE.php:89
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
Class for file upload question exports.
Class for file upload question imports.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping, array $solutionhints=[])
Creates a question from a QTI file.
__set($key, $value)
Object setter.
isCompletionBySubmissionEnabled()
Checks whether completion by submission is enabled or not.
deleteAnswers($question_id)
Deletes datasets from answers tables.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setMaxSize($a_value)
Set max file size.
getUserSolutionPreferingIntermediate($active_id, $pass=null)
Get the user solution preferring the intermediate solution.
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
hasFileUploads($test_id)
Checks if file uploads exist for a given test and the original id of the question.
deleteUploadedFiles($files, $test_id, $active_id, $authorized)
Delete uploaded files.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
getPreviewFileUploadPathWeb($userId)
Returns the filesystem path for file uploads.
getFileUploadPathWeb($test_id, $active_id, $question_id=null)
Returns the file upload path for web accessible files of a question.
deletePreviewFileUploads($userId, $userSolution, $files)
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assFileUpload.
saveToDb($original_id="")
Saves a assFileUpload object to a database.
getUploadedFiles($active_id, $pass=null, $authorized=true)
Returns the uploaded files for an active user in a given pass.
copyObject($target_questionpool_id, $title="")
Copies an assFileUpload object.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
removeIntermediateSolution($active_id, $pass)
Remove an intermediate soluton (overridden to remove unused fies)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
calculateReachedPointsForSolution($userSolution)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{Creates an Excel worksheet for the detailed cumulated results of this question.object}
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...
handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
This method is called after an user submitted one or more files.
getBestSolution($active_id, $pass)
Returns the best solution for a given pass of a participant.
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
getPreviewFileUploadPath($userId)
Returns the filesystem path for file uploads.
isComplete()
Returns true, if the question is complete for use.
getAllowedExtensions()
Get allowed file extensions.
getMaxSize()
Get max file size.
getMaxFilesizeAsString()
Return the maximum allowed file size as string.
setAllowedExtensions($a_value)
Set allowed file extensions.
getAnswerTableName()
Returns the name of the answer table in the database.
isAnswered($active_id, $pass=null)
returns boolean wether the question is answered during test pass or not
setCompletionBySubmission($bool)
Enabled/Disable completion by submission.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assFileUpload constructor
getFileUploadPath($test_id, $active_id, $question_id=null)
Returns the filesystem path for file uploads.
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...
savePreviewData(ilAssQuestionPreviewSession $previewSession)
loadFromDb($question_id)
Loads a assFileUpload object from a database.
getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
__get($value)
Object getter.
getAllowedExtensionsArray()
Get allowed file extensions.
getQuestionType()
Returns the question type of the question.
getUploadedFilesForWeb($active_id, $pass)
Returns the web accessible uploaded files for an active user in a given pass.
getMaxFilesizeInBytes()
Return the maximum allowed file size in bytes.
checkUpload()
Check file upload.
static isObligationPossible($questionId)
returns boolean wether it is possible to set this question type as obligatory or not considering the ...
buildTestPresentationConfig()
Get the test question configuration Overridden from parent to disable the form change detection Other...
getAdditionalTableName()
Returns the name of the additional question data table in the database.
Abstract basic class which is to be extended by the concrete assessment question type classes.
isNonEmptyItemListPostSubmission($postSubmissionFieldname)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
static _getOriginalId($question_id)
Returns the original id of a question.
setId($id=-1)
Sets the id of the assQuestion object.
setOriginalId($original_id)
setObjId($obj_id=0)
Set the object id of the container object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
setSuggestedSolution($solution_id="", $subquestion_index=0, $is_import=false)
Sets a suggested solution for the question.
static _getMaximumPoints($question_id)
Returns the maximum points, a learner can reach answering the question.
saveQuestionDataToDb($original_id="")
deleteDummySolutionRecord($activeId, $passIndex)
getId()
Gets the id of the assQuestion object.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
getObjId()
Get the object id of the container object.
isDummySolutionRecord($solutionRecord)
setTitle($title="")
Sets the title string of the assQuestion object.
lookupTestId($active_id)
getSolutionRecordById($solutionId)
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second.
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
removeCurrentSolution($active_id, $pass, $authorized=true)
removeSolutionRecordById($solutionId)
static getNumExistingSolutionRecords($activeId, $pass, $questionId)
returns the number of existing solution records for the given test active / pass and given question i...
setAuthor($author="")
Sets the authors name of the assQuestion object.
getPoints()
Returns the maximum available points for the question.
ensureCurrentTestPass($active_id, $pass)
intermediateSolutionExists($active_id, $pass)
getTestPresentationConfig()
Get the test question configuration (initialised once)
forceExistingIntermediateSolution($activeId, $passIndex, $considerDummyRecordCreation)
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...
setLifecycle(ilAssQuestionLifecycle $lifecycle)
updateCurrentSolutionsAuthorization($activeId, $pass, $authorized, $keepTime=false)
setPoints($a_points)
Sets the maximum available points for the question.
setComment($comment="")
Sets the comment string of the assQuestion object.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
setQuestion($question="")
Sets the question string of the question object.
static getValidFilename($a_filename)
Get valid filename.
static _updateStatus($a_obj_id, $a_usr_id, $a_obj=null, $a_percentage=false, $a_force_raise=false)
Update status.
static _getLogLanguage()
retrieve the log language for assessment logging
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static _getParticipantId($active_id)
Get user id for active id.
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...
Base Exception for all Exceptions relating to Modules/Test.
static moveUploadedFile($a_file, $a_name, $a_target, $a_raise_errors=true, $a_mode="move_uploaded")
move uploaded file
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static virusHandling($a_file, $a_orig_name="", $a_clean=true)
scan file for viruses and clean files if possible
static deliverFile( $a_file, $a_filename, $a_mime='', $isInline=false, $removeAfterDelivery=false, $a_exit_after=true)
deliver file for download via browser.
static makeDirParents($a_dir)
Create a new directory and all parent directories.
static sendInfo($a_info="", $a_keep=false)
Send Info Message to Screen.
static removeTrailingPathSeparators($path)
const CLIENT_WEB_DIR
Definition: constants.php:45
global $DIC
Definition: goto.php:24
Interface ilObjFileHandlingQuestionType.
Interface ilObjQuestionScoringAdjustable.
if($format !==null) $name
Definition: metadata.php:230
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$query
global $ilDB
$data
Definition: storeScorm.php:23