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