ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 function __construct(
48 $title = "",
49 $comment = "",
50 $author = "",
51 $owner = -1,
52 $question = ""
53 )
54 {
55 parent::__construct($title, $comment, $author, $owner, $question);
56 }
57
63 public function isComplete()
64 {
65 if (
66 strlen($this->title)
67 && ($this->author)
68 && ($this->question)
69 && ($this->getMaximumPoints() >= 0)
70 && is_numeric($this->getMaximumPoints()))
71 {
72 return true;
73 }
74 return false;
75 }
76
80 public function saveToDb($original_id = "")
81 {
84 parent::saveToDb();
85 }
86
88 {
89 global $ilDB;
90 $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
91 array( "integer" ),
92 array( $this->getId() )
93 );
94 $ilDB->manipulateF( "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("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",
115 array("integer"),
116 array($question_id)
117 );
118 if ($result->numRows() == 1)
119 {
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 {
141 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
142 }
144 {
145 }
146 }
147 parent::loadFromDb($question_id);
148 }
149
153 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
154 {
155 if ($this->id <= 0)
156 {
157 // The question has not been saved. It cannot be duplicated
158 return;
159 }
160 // duplicate the question in database
161 $this_id = $this->getId();
162 $thisObjId = $this->getObjId();
163
164 $clone = $this;
165 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
167 $clone->id = -1;
168
169 if( (int)$testObjId > 0 )
170 {
171 $clone->setObjId($testObjId);
172 }
173
174 if ($title)
175 {
176 $clone->setTitle($title);
177 }
178
179 if ($author)
180 {
181 $clone->setAuthor($author);
182 }
183 if ($owner)
184 {
185 $clone->setOwner($owner);
186 }
187
188 if ($for_test)
189 {
190 $clone->saveToDb($original_id);
191 }
192 else
193 {
194 $clone->saveToDb();
195 }
196
197 // copy question page content
198 $clone->copyPageOfQuestion($this_id);
199 // copy XHTML media objects
200 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
201
202 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
203
204 return $clone->id;
205 }
206
210 public function copyObject($target_questionpool_id, $title = "")
211 {
212 if ($this->id <= 0)
213 {
214 // The question has not been saved. It cannot be duplicated
215 return;
216 }
217 // duplicate the question in database
218 $clone = $this;
219 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
221 $clone->id = -1;
222 $source_questionpool_id = $this->getObjId();
223 $clone->setObjId($target_questionpool_id);
224 if ($title)
225 {
226 $clone->setTitle($title);
227 }
228 $clone->saveToDb();
229
230 // copy question page content
231 $clone->copyPageOfQuestion($original_id);
232 // copy XHTML media objects
233 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
234
235 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
236
237 return $clone->id;
238 }
239
240 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
241 {
242 if ($this->id <= 0)
243 {
244 // The question has not been saved. It cannot be duplicated
245 return;
246 }
247
248 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
249
250 $sourceQuestionId = $this->id;
251 $sourceParentId = $this->getObjId();
252
253 // duplicate the question in database
254 $clone = $this;
255 $clone->id = -1;
256
257 $clone->setObjId($targetParentId);
258
259 if ($targetQuestionTitle)
260 {
261 $clone->setTitle($targetQuestionTitle);
262 }
263
264 $clone->saveToDb();
265 // copy question page content
266 $clone->copyPageOfQuestion($sourceQuestionId);
267 // copy XHTML media objects
268 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
269
270 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
271
272 return $clone->id;
273 }
274
280 public function getMaximumPoints()
281 {
282 return $this->getPoints();
283 }
284
295 public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE)
296 {
297 if( $returndetails )
298 {
299 throw new ilTestException('return details not implemented for '.__METHOD__);
300 }
301
302 global $ilDB;
303
304 if (is_null($pass))
305 {
306 $pass = $this->getSolutionMaxPass($active_id);
307 }
308 $points = 0;
309 return $points;
310 }
311
312 protected function calculateReachedPointsForSolution($userSolution)
313 {
314 if( $this->isCompletionBySubmissionEnabled() && count($userSolution) )
315 {
316 return $this->getPoints();
317 }
318
319 return 0;
320 }
321
327 function checkUpload()
328 {
329 $this->lng->loadLanguageModule("form");
330 // remove trailing '/'
331 $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
332
333 $filename = $_FILES["upload"]["name"];
334 $filename_arr = pathinfo($_FILES["upload"]["name"]);
335 $suffix = $filename_arr["extension"];
336 $mimetype = $_FILES["upload"]["type"];
337 $size_bytes = $_FILES["upload"]["size"];
338 $temp_name = $_FILES["upload"]["tmp_name"];
339 $error = $_FILES["upload"]["error"];
340
341 if ($size_bytes > $this->getMaxFilesizeInBytes())
342 {
343 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
344 return false;
345 }
346
347 // error handling
348 if ($error > 0)
349 {
350 switch ($error)
351 {
352 case UPLOAD_ERR_INI_SIZE:
353 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
354 return false;
355 break;
356
357 case UPLOAD_ERR_FORM_SIZE:
358 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
359 return false;
360 break;
361
362 case UPLOAD_ERR_PARTIAL:
363 ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
364 return false;
365 break;
366
367 case UPLOAD_ERR_NO_FILE:
368 ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
369 return false;
370 break;
371
372 case UPLOAD_ERR_NO_TMP_DIR:
373 ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
374 return false;
375 break;
376
377 case UPLOAD_ERR_CANT_WRITE:
378 ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
379 return false;
380 break;
381
382 case UPLOAD_ERR_EXTENSION:
383 ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
384 return false;
385 break;
386 }
387 }
388
389 // check suffixes
390 if( count($this->getAllowedExtensionsArray()) )
391 {
392 if( !strlen($suffix) )
393 {
394 ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
395 return false;
396 }
397
398 if( !in_array(strtolower($suffix), $this->getAllowedExtensionsArray()) )
399 {
400 ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
401 return false;
402 }
403 }
404
405 // virus handling
406 if (strlen($temp_name))
407 {
408 $vir = ilUtil::virusHandling($temp_name, $filename);
409 if ($vir[0] == false)
410 {
411 ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found")."<br />".$vir[1], true);
412 return false;
413 }
414 }
415 return true;
416 }
417
421 public function getFileUploadPath($test_id, $active_id, $question_id = null)
422 {
423 if (is_null($question_id)) $question_id = $this->getId();
424 return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
425 }
426
430 protected function getPreviewFileUploadPath($userId)
431 {
432 return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
433 }
434
440 function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
441 {
442 if (is_null($question_id)) $question_id = $this->getId();
443 include_once "./Services/Utilities/classes/class.ilUtil.php";
444 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
445 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
446 }
447
451 protected function getPreviewFileUploadPathWeb($userId)
452 {
453 include_once "./Services/Utilities/classes/class.ilUtil.php";
454 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
455 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
456 }
457
463 public function getUploadedFiles($active_id, $pass = null, $authorized = true)
464 {
465 global $ilDB;
466
467 if (is_null($pass))
468 {
469 $pass = $this->getSolutionMaxPass($active_id);
470 }
471// fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
472 $result = $ilDB->queryF("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",
473 array("integer", "integer", "integer", 'integer'),
474 array($active_id, $this->getId(), $pass, (int)$authorized)
475 );
476// fau.
477 $found = array();
478
479 while ($data = $ilDB->fetchAssoc($result))
480 {
481 array_push($found, $data);
482 }
483
484 return $found;
485 }
486
487 public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
488 {
489 return (array)$previewSession->getParticipantsSolution();
490 }
491
497 public function getUploadedFilesForWeb($active_id, $pass)
498 {
499 global $ilDB;
500
501 $found = $this->getUploadedFiles($active_id, $pass);
502 $result = $ilDB->queryF("SELECT test_fi FROM tst_active WHERE active_id = %s",
503 array('integer'),
504 array($active_id)
505 );
506 if ($result->numRows() == 1)
507 {
508 $row = $ilDB->fetchAssoc($result);
509 $test_id = $row["test_fi"];
510 $path = $this->getFileUploadPathWeb($test_id, $active_id);
511 foreach ($found as $idx => $data)
512 {
513 $found[$idx]['webpath'] = $path;
514 }
515 }
516 return $found;
517 }
518
524 protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
525 {
526 global $ilDB;
527
528 $pass = null;
529 $active_id = null;
530 foreach ($files as $solution_id)
531 {
532 $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
533 array("integer", 'integer'),
534 array($solution_id, (int)$authorized)
535 );
536 if ($result->numRows() == 1)
537 {
538 $data = $ilDB->fetchAssoc($result);
539 $pass = $data['pass'];
540 $active_id = $data['active_fi'];
541 @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
542 }
543 }
544 foreach ($files as $solution_id)
545 {
546 $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
547 array("integer", 'integer'),
548 array($solution_id, $authorized)
549 );
550 }
551 }
552
553// fau: testNav new function deleteUnusedFiles()
560 protected function deleteUnusedFiles($test_id, $active_id, $pass)
561 {
562 // read all solutions (authorized and intermediate) from all steps
563 $step = $this->getStep();
564 $this->setStep(null);
565 $solutions = array_merge(
566 $this->getSolutionValues($active_id, $pass, true),
567 $this->getSolutionValues($active_id, $pass, false)
568 );
569 $this->setStep($step);
570
571 // get the used files from these solutions
572 $used_files = array();
573 foreach ($solutions as $solution)
574 {
575 $used_files[] = $solution['value1'];
576 }
577
578 // read the existing files for user and pass
579 // delete all files that are not used in the solutions
580 $curdir = getcwd();
581 chdir($this->getFileUploadPath($test_id, $active_id));
582 $existing_files = glob("file_" . $active_id . "_" . $pass . "_*");
583 foreach($existing_files as $file)
584 {
585 if (!in_array($file, $used_files))
586 {
587 @unlink($file);
588 }
589 }
590 chdir($curdir);
591 }
592// fau.
593
594 protected function deletePreviewFileUploads($userId, $userSolution, $files)
595 {
596 foreach($files as $name)
597 {
598 if( isset($userSolution[$name]) )
599 {
600 unset($userSolution[$name]);
601 @unlink($this->getPreviewFileUploadPath($userId) . $name);
602 }
603 }
604
605 return $userSolution;
606 }
607
613 public function getMaxFilesizeAsString()
614 {
615 $size = $this->getMaxFilesizeInBytes();
616 if ($size < 1024)
617 {
618 $max_filesize = sprintf("%d Bytes",$size);
619 }
620 else if ($size < 1024*1024)
621 {
622 $max_filesize = sprintf("%.1f KB",$size/1024);
623 }
624 else
625 {
626 $max_filesize = sprintf("%.1f MB",$size/1024/1024);
627 }
628
629 return $max_filesize;
630 }
631
637 public function getMaxFilesizeInBytes()
638 {
639 if (strlen($this->getMaxSize()))
640 {
641 return $this->getMaxSize();
642 }
643 else
644 {
645 // get the value for the maximal uploadable filesize from the php.ini (if available)
646 $umf = get_cfg_var("upload_max_filesize");
647 // get the value for the maximal post data from the php.ini (if available)
648 $pms = get_cfg_var("post_max_size");
649
650 //convert from short-string representation to "real" bytes
651 $multiplier_a=array("K"=>1024, "M"=>1024*1024, "G"=>1024*1024*1024);
652
653 $umf_parts=preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
654 $pms_parts=preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
655
656 if (count($umf_parts) == 2) { $umf = $umf_parts[0]*$multiplier_a[$umf_parts[1]]; }
657 if (count($pms_parts) == 2) { $pms = $pms_parts[0]*$multiplier_a[$pms_parts[1]]; }
658
659 // use the smaller one as limit
660 $max_filesize = min($umf, $pms);
661
662 if (!$max_filesize) $max_filesize=max($umf, $pms);
663 return $max_filesize;
664 }
665 }
666
667 // hey: prevPassSolutions - refactored method to get intermediate/authorized
668 // as well as upload, delete and previous files working
669 // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
678 public function saveWorkingData($active_id, $pass = NULL, $authorized = true)
679 {
680 $pass = $this->ensureCurrentTestPass($active_id, $pass);
681 $test_id = $this->lookupTestId($active_id);
682
683 $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
684
685 $entered_values = false;
686
687 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function() use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
688
689 if ($authorized == false)
690 {
691 $this->forceExistingIntermediateSolution($active_id, $pass, true);
692 }
693
694 if( $this->isFileDeletionAction() )
695 {
696 if( $this->isFileDeletionSubmitAvailable() )
697 {
698 foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id)
699 {
700 $this->removeSolutionRecordById($solution_id);
701 }
702 }
703 else
704 {
705 ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
706 }
707 }
708 else
709 {
710 if( $this->isFileReuseHandlingRequired() )
711 {
712 foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId)
713 {
714 $solution = $this->getSolutionRecordById($solutionId);
715
716 $this->saveCurrentSolution($active_id, $pass,
717 $solution['value1'], $solution['value2'],
718 false, $solution['tstamp']
719 );
720 }
721 }
722
723 if( $uploadHandlingRequired )
724 {
725 if(!@file_exists($this->getFileUploadPath($test_id, $active_id)))
726 {
728 }
729
730 $solutionFileVersioningUploadTS = time();
731 $filename_arr = pathinfo($_FILES["upload"]["name"]);
732 $extension = $filename_arr["extension"];
733 $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
734
735 include_once 'Services/Utilities/classes/class.ilFileUtils.php';
736 $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
737 $newfile = ilFileUtils::getValidFilename($newfile);
738
739 ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
740
741 $this->saveCurrentSolution($active_id, $pass, $newfile, $dispoFilename, false,
742 $solutionFileVersioningUploadTS
743 );
744
745 $entered_values = true;
746 }
747 }
748
749 if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass))
750 {
751 // remove the dummy record of the intermediate solution
752 $this->deleteDummySolutionRecord($active_id, $pass);
753
754 // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
755 $this->removeCurrentSolution($active_id, $pass, true);
756 $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
757 }
758
759 $this->deleteUnusedFiles($test_id, $active_id, $pass);
760 });
761
762 if ($entered_values)
763 {
764 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
766 {
767 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
768 }
769 }
770 else
771 {
772 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
774 {
775 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
776 }
777 }
778
779 return true;
780 }
781 // hey.
782
783// fau: testNav - remove dummy value when intermediate solution is got for test display
790 public function getUserSolutionPreferingIntermediate($active_id, $pass = NULL)
791 {
792 $solution = $this->getSolutionValues($active_id, $pass, false);
793
794 if( !count($solution) )
795 {
796 $solution = $this->getSolutionValues($active_id, $pass, true);
797 }
798 else
799 {
800 $cleaned = array();
801 foreach ($solution as $row)
802 {
803 if (!empty($row['value1']))
804 {
805 $cleaned[] = $row;
806 }
807 }
808 $solution = $cleaned;
809 }
810
811 return $solution;
812 }
813// fau.
814
815
816// fau: testNav - remove unused files if an intermediate solution is removed
823 public function removeIntermediateSolution($active_id, $pass)
824 {
825 global $ilDB;
826
827 $result = parent::removeIntermediateSolution($active_id, $pass);
828
829 // get the current test id
830 // hey: prevPassSolutions - exract until you drop :-D
831 $test_id = $this->lookupTestId($active_id);
832 // hey.
833
834 $this->deleteUnusedFiles($test_id, $active_id, $pass);
835
836 return $result;
837 }
838// fau.
839
840
841 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
842 {
843 $userSolution = $previewSession->getParticipantsSolution();
844
845 if( !is_array($userSolution) )
846 {
847 $userSolution = array();
848 }
849
850 // hey: prevPassSolutions - readability spree - get a chance to understand the code
851 if( $this->isFileDeletionAction() )
852 // hey.
853 {
854 // hey: prevPassSolutions - readability spree - get a chance to understand the code
855 if( $this->isFileDeletionSubmitAvailable() )
856 // hey.
857 {
858 $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
859 }
860 else
861 {
862 ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
863 }
864 }
865 else
866 {
867 // hey: prevPassSolutions - readability spree - get a chance to understand the code
868 if ( $this->isFileUploadAvailable() )
869 // hey.
870 {
871 if ($this->checkUpload())
872 {
873 if( !@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId())) )
874 {
876 }
877
878 $version = time();
879 $filename_arr = pathinfo($_FILES["upload"]["name"]);
880 $extension = $filename_arr["extension"];
881 $newfile = "file_".md5($_FILES["upload"]["name"])."_" . $version . "." . $extension;
882 ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
883
884 $userSolution[$newfile] = array(
885 'solution_id' => $newfile,
886 'value1' => $newfile,
887 'value2' => $_FILES['upload']['name'],
888 'tstamp' => $version,
889 'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
890 );
891 }
892 }
893 }
894
895 $previewSession->setParticipantsSolution($userSolution);
896 }
897
901 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
902 {
903 $this->handleSubmission($active_id, $pass, $obligationsAnswered, $authorized);
904 }
905
915 protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
916 {
917 if(!$authorized)
918 {
919 return;
920 }
921
923 {
924 $maxpoints = assQuestion::_getMaximumPoints($this->getId());
925
926 if($this->getUploadedFiles($active_id, $pass, $authorized))
927 {
928 $points = $maxpoints;
929 }
930 else
931 {
932// fau: testNav - don't set reached points if no file is available
933 return;
934// fau.
935 }
936
937 assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
938
939 // update learning progress
940 include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
941 include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
944 ilObjTestAccess::_getParticipantId((int) $active_id)
945 );
946 }
947 }
948
954 public function getQuestionType()
955 {
956 return "assFileUpload";
957 }
958
964 public function getAdditionalTableName()
965 {
966 return "qpl_qst_fileupload";
967 }
968
974 public function getAnswerTableName()
975 {
976 return "";
977 }
978
984 public function deleteAnswers($question_id)
985 {
986 }
987
993 {
994 $text = parent::getRTETextWithMediaObjects();
995 return $text;
996 }
997
1001 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
1002 {
1003 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
1004
1005 $i = 1;
1006 $solutions = $this->getSolutionValues($active_id, $pass);
1007 foreach ($solutions as $solution)
1008 {
1009 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
1010 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
1011 if (strlen($solution["value1"]))
1012 {
1013 $worksheet->setCell($startrow + $i, 1, $solution["value1"]);
1014 $worksheet->setCell($startrow + $i, 2, $solution["value2"]);
1015 }
1016 $i++;
1017 }
1018
1019 return $startrow + $i + 1;
1020 }
1021
1034 public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
1035 {
1036 include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
1037 $import = new assFileUploadImport($this);
1038 $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
1039 }
1040
1047 public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
1048 {
1049 include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
1050 $export = new assFileUploadExport($this);
1051 return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
1052 }
1053
1059 public function getBestSolution($active_id, $pass)
1060 {
1061 $user_solution = array();
1062 return $user_solution;
1063 }
1064
1070 public function getMaxSize()
1071 {
1072 return $this->maxsize;
1073 }
1074
1080 public function setMaxSize($a_value)
1081 {
1082 $this->maxsize = $a_value;
1083 }
1084
1091 {
1092 if (strlen($this->allowedextensions))
1093 {
1094 return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1095 }
1096 return array();
1097 }
1098
1104 public function getAllowedExtensions()
1105 {
1107 }
1108
1114 public function setAllowedExtensions($a_value)
1115 {
1116 $this->allowedextensions = strtolower(trim($a_value));
1117 }
1118
1122 public function __get($value)
1123 {
1124 switch ($value)
1125 {
1126 case "maxsize":
1127 return $this->getMaxSize();
1128 break;
1129 case "allowedextensions":
1130 return $this->getAllowedExtensions();
1131 break;
1132 case 'completion_by_submission':
1133 return $this->isCompletionBySubmissionEnabled();
1134 break;
1135 default:
1136 return parent::__get($value);
1137 break;
1138 }
1139 }
1140
1144 public function __set($key, $value)
1145 {
1146 switch ($key)
1147 {
1148 case "maxsize":
1149 $this->setMaxSize($value);
1150 break;
1151 case "allowedextensions":
1152 $this->setAllowedExtensions($value);
1153 break;
1154 case 'completion_by_submission':
1155 $this->setCompletionBySubmission($value);
1156 break;
1157 default:
1158 parent::__set($key, $value);
1159 break;
1160 }
1161 }
1162
1170 public function hasFileUploads($test_id)
1171 {
1172 global $ilDB;
1173 $query = "
1174 SELECT tst_solutions.solution_id
1175 FROM tst_solutions, tst_active, qpl_questions
1176 WHERE tst_solutions.active_fi = tst_active.active_id
1177 AND tst_solutions.question_fi = qpl_questions.question_id
1178 AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
1179 $result = $ilDB->queryF( $query,
1180 array("integer", "integer"),
1181 array($this->getId(), $test_id)
1182 );
1183 if ($result->numRows() > 0)
1184 {
1185 return true;
1186 }
1187 else
1188 {
1189 return false;
1190 }
1191 }
1192
1198 public function deliverFileUploadZIPFile($test_id, $test_title)
1199 {
1200 global $ilDB, $lng;
1201
1202 require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1203 $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1204
1205 $exporter->setTestId($test_id);
1206 $exporter->setTestTitle($test_title);
1207 $exporter->setQuestion($this);
1208
1209 $exporter->build();
1210
1212 $exporter->getFinalZipFilePath(), $exporter->getDispoZipFileName(),
1213 $exporter->getZipFileMimeType(), false, true
1214 );
1215 }
1216
1226 {
1228 }
1229
1239 public function setCompletionBySubmission($bool)
1240 {
1241 $this->completion_by_submission = (bool)$bool;
1242 return $this;
1243 }
1244
1256 public function isAnswered($active_id, $pass = null)
1257 {
1258 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1259
1260 return $numExistingSolutionRecords > 0;
1261 }
1262
1273 public static function isObligationPossible($questionId)
1274 {
1275 return true;
1276 }
1277
1278 public function isAutosaveable()
1279 {
1280 return FALSE;
1281 }
1282
1283// fau: testNav - new function getTestQuestionConfig()
1290 // hey: refactored identifiers
1292 // hey.
1293 {
1294 // hey: refactored identifiers
1295 return parent::buildTestPresentationConfig()
1296 // hey.
1297 ->setFormChangeDetectionEnabled(false);
1298 }
1299// fau.
1300
1301 // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1305 protected function isFileDeletionAction()
1306 {
1307 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1309 }
1310
1315 {
1316 return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1317 }
1318
1322 protected function isFileReuseSubmitAvailable()
1323 {
1324 return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1325 }
1326
1330 protected function isFileReuseHandlingRequired()
1331 {
1332 if( !$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed() )
1333 {
1334 return false;
1335 }
1336
1337 if( !$this->isFileReuseSubmitAvailable() )
1338 {
1339 return false;
1340 }
1341
1342 return true;
1343 }
1344
1348 protected function isFileUploadAvailable()
1349 {
1350 if( !isset($_FILES['upload']) )
1351 {
1352 return false;
1353 }
1354
1355 if( !isset($_FILES['upload']['tmp_name']) )
1356 {
1357 return false;
1358 }
1359
1360 return strlen($_FILES['upload']['tmp_name']) > 0;
1361 }
1362 // hey.
1363}
sprintf('%.4f', $callTime)
$worksheet
$result
$error
Definition: Error.php:17
$size
Definition: RandomTest.php:79
$files
Definition: add-vimline.php:18
$path
Definition: aliased.php:25
$_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.
deleteUnusedFiles($test_id, $active_id, $pass)
Delete all files that are neither used in an authorized or intermediate solution.
hasFileUploads($test_id)
Checks if file uploads exist for a given test and the original id of the question.
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
deleteUploadedFiles($files, $test_id, $active_id, $authorized)
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.
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
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.
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.
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
getUserSolutionPreferingIntermediate($active_id, $pass=NULL)
Get the user solution preferring the intermediate solution.
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.
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)
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)
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
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 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 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)
static deliverFile($a_file, $a_filename, $a_mime='', $isInline=false, $removeAfterDelivery=false, $a_exit_after=true)
deliver file for download via browser.
$text
Interface ilObjFileHandlingQuestionType.
Interface ilObjQuestionScoringAdjustable.
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