ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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 ) {
55 }
56
62 public function isComplete()
63 {
64 if (
65 strlen($this->title)
66 && ($this->author)
67 && ($this->question)
68 && ($this->getMaximumPoints() >= 0)
69 && is_numeric($this->getMaximumPoints())) {
70 return true;
71 }
72 return false;
73 }
74
78 public function saveToDb($original_id = "")
79 {
82 parent::saveToDb();
83 }
84
86 {
87 global $DIC;
88 $ilDB = $DIC['ilDB'];
89 $ilDB->manipulateF(
90 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
91 array( "integer" ),
92 array( $this->getId() )
93 );
94 $ilDB->manipulateF(
95 "INSERT INTO " . $this->getAdditionalTableName(
96 ) . " (question_fi, maxsize, allowedextensions, compl_by_submission) VALUES (%s, %s, %s, %s)",
97 array( "integer", "float", "text", "integer" ),
98 array(
99 $this->getId(),
100 (strlen($this->getMaxSize())) ? $this->getMaxSize() : null,
101 (strlen($this->getAllowedExtensions())) ? $this->getAllowedExtensions() : null,
103 )
104 );
105 }
106
112 public function loadFromDb($question_id)
113 {
114 global $DIC;
115 $ilDB = $DIC['ilDB'];
116 $result = $ilDB->queryF(
117 "SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
118 array("integer"),
119 array($question_id)
120 );
121 if ($result->numRows() == 1) {
122 $data = $ilDB->fetchAssoc($result);
123 $this->setId($question_id);
124 $this->setTitle($data["title"]);
125 $this->setComment($data["description"]);
126 $this->setNrOfTries($data['nr_of_tries']);
127 $this->setSuggestedSolution($data["solution_hint"]);
128 $this->setOriginalId($data["original_id"]);
129 $this->setObjId($data["obj_fi"]);
130 $this->setAuthor($data["author"]);
131 $this->setOwner($data["owner"]);
132 $this->setPoints($data["points"]);
133
134 include_once("./Services/RTE/classes/class.ilRTE.php");
135 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
136 $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
137 $this->setMaxSize($data["maxsize"]);
138 $this->setAllowedExtensions($data["allowedextensions"]);
139 $this->setCompletionBySubmission($data['compl_by_submission'] == 1 ? true : false);
140
141 try {
145 }
146
147 try {
148 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
150 }
151 }
152 parent::loadFromDb($question_id);
153 }
154
158 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
159 {
160 if ($this->id <= 0) {
161 // The question has not been saved. It cannot be duplicated
162 return;
163 }
164 // duplicate the question in database
165 $this_id = $this->getId();
166 $thisObjId = $this->getObjId();
167
168 $clone = $this;
169 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
171 $clone->id = -1;
172
173 if ((int) $testObjId > 0) {
174 $clone->setObjId($testObjId);
175 }
176
177 if ($title) {
178 $clone->setTitle($title);
179 }
180
181 if ($author) {
182 $clone->setAuthor($author);
183 }
184 if ($owner) {
185 $clone->setOwner($owner);
186 }
187
188 if ($for_test) {
189 $clone->saveToDb($original_id);
190 } else {
191 $clone->saveToDb();
192 }
193
194 // copy question page content
195 $clone->copyPageOfQuestion($this_id);
196 // copy XHTML media objects
197 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
198
199 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
200
201 return $clone->id;
202 }
203
207 public function copyObject($target_questionpool_id, $title = "")
208 {
209 if ($this->id <= 0) {
210 // The question has not been saved. It cannot be duplicated
211 return;
212 }
213 // duplicate the question in database
214 $clone = $this;
215 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
217 $clone->id = -1;
218 $source_questionpool_id = $this->getObjId();
219 $clone->setObjId($target_questionpool_id);
220 if ($title) {
221 $clone->setTitle($title);
222 }
223 $clone->saveToDb();
224
225 // copy question page content
226 $clone->copyPageOfQuestion($original_id);
227 // copy XHTML media objects
228 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
229
230 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
231
232 return $clone->id;
233 }
234
235 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
236 {
237 if ($this->id <= 0) {
238 // The question has not been saved. It cannot be duplicated
239 return;
240 }
241
242 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
243
244 $sourceQuestionId = $this->id;
245 $sourceParentId = $this->getObjId();
246
247 // duplicate the question in database
248 $clone = $this;
249 $clone->id = -1;
250
251 $clone->setObjId($targetParentId);
252
253 if ($targetQuestionTitle) {
254 $clone->setTitle($targetQuestionTitle);
255 }
256
257 $clone->saveToDb();
258 // copy question page content
259 $clone->copyPageOfQuestion($sourceQuestionId);
260 // copy XHTML media objects
261 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
262
263 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
264
265 return $clone->id;
266 }
267
273 public function getMaximumPoints()
274 {
275 return $this->getPoints();
276 }
277
288 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
289 {
290 if ($returndetails) {
291 throw new ilTestException('return details not implemented for ' . __METHOD__);
292 }
293
294 if ($this->isCompletionBySubmissionEnabled()) {
295 if (is_null($pass)) {
296 $pass = $this->getSolutionMaxPass($active_id);
297 }
298
299 global $DIC;
300
301 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
302
303 while ($data = $DIC->database()->fetchAssoc($result)) {
304 if ($this->isDummySolutionRecord($data)) {
305 continue;
306 }
307
308 return $this->getPoints();
309 }
310 }
311
312 return 0;
313 }
314
315 protected function calculateReachedPointsForSolution($userSolution)
316 {
317 if ($this->isCompletionBySubmissionEnabled() && count($userSolution)) {
318 return $this->getPoints();
319 }
320
321 return 0;
322 }
323
329 public function checkUpload()
330 {
331 $this->lng->loadLanguageModule("form");
332 // remove trailing '/'
333 $_FILES["upload"]["name"] = rtrim($_FILES["upload"]["name"], '/');
334
335 $filename = $_FILES["upload"]["name"];
336 $filename_arr = pathinfo($_FILES["upload"]["name"]);
337 $suffix = $filename_arr["extension"];
338 $mimetype = $_FILES["upload"]["type"];
339 $size_bytes = $_FILES["upload"]["size"];
340 $temp_name = $_FILES["upload"]["tmp_name"];
341 $error = $_FILES["upload"]["error"];
342
343 if ($size_bytes > $this->getMaxFilesizeInBytes()) {
344 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
345 return false;
346 }
347
348 // error handling
349 if ($error > 0) {
350 switch ($error) {
351 case UPLOAD_ERR_INI_SIZE:
352 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
353 return false;
354 break;
355
356 case UPLOAD_ERR_FORM_SIZE:
357 ilUtil::sendFailure($this->lng->txt("form_msg_file_size_exceeds"), true);
358 return false;
359 break;
360
361 case UPLOAD_ERR_PARTIAL:
362 ilUtil::sendFailure($this->lng->txt("form_msg_file_partially_uploaded"), true);
363 return false;
364 break;
365
366 case UPLOAD_ERR_NO_FILE:
367 ilUtil::sendFailure($this->lng->txt("form_msg_file_no_upload"), true);
368 return false;
369 break;
370
371 case UPLOAD_ERR_NO_TMP_DIR:
372 ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_tmp_dir"), true);
373 return false;
374 break;
375
376 case UPLOAD_ERR_CANT_WRITE:
377 ilUtil::sendFailure($this->lng->txt("form_msg_file_cannot_write_to_disk"), true);
378 return false;
379 break;
380
381 case UPLOAD_ERR_EXTENSION:
382 ilUtil::sendFailure($this->lng->txt("form_msg_file_upload_stopped_ext"), true);
383 return false;
384 break;
385 }
386 }
387
388 // check suffixes
389 if (count($this->getAllowedExtensionsArray())) {
390 if (!strlen($suffix)) {
391 ilUtil::sendFailure($this->lng->txt("form_msg_file_missing_file_ext"), true);
392 return false;
393 }
394
395 if (!in_array(strtolower($suffix), $this->getAllowedExtensionsArray())) {
396 ilUtil::sendFailure($this->lng->txt("form_msg_file_wrong_file_type"), true);
397 return false;
398 }
399 }
400
401 // virus handling
402 if (strlen($temp_name)) {
403 $vir = ilUtil::virusHandling($temp_name, $filename);
404 if ($vir[0] == false) {
405 ilUtil::sendFailure($this->lng->txt("form_msg_file_virus_found") . "<br />" . $vir[1], true);
406 return false;
407 }
408 }
409 return true;
410 }
411
415 public function getFileUploadPath($test_id, $active_id, $question_id = null)
416 {
417 if (is_null($question_id)) {
418 $question_id = $this->getId();
419 }
420 return CLIENT_WEB_DIR . "/assessment/tst_$test_id/$active_id/$question_id/files/";
421 }
422
426 protected function getPreviewFileUploadPath($userId)
427 {
428 return CLIENT_WEB_DIR . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
429 }
430
436 public function getFileUploadPathWeb($test_id, $active_id, $question_id = null)
437 {
438 if (is_null($question_id)) {
439 $question_id = $this->getId();
440 }
441 include_once "./Services/Utilities/classes/class.ilUtil.php";
442 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/tst_$test_id/$active_id/$question_id/files/";
443 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
444 }
445
449 protected function getPreviewFileUploadPathWeb($userId)
450 {
451 include_once "./Services/Utilities/classes/class.ilUtil.php";
452 $webdir = ilUtil::removeTrailingPathSeparators(CLIENT_WEB_DIR) . "/assessment/qst_preview/$userId/{$this->getId()}/fileuploads/";
453 return str_replace(ilUtil::removeTrailingPathSeparators(ILIAS_ABSOLUTE_PATH), ilUtil::removeTrailingPathSeparators(ILIAS_HTTP_PATH), $webdir);
454 }
455
461 public function getUploadedFiles($active_id, $pass = null, $authorized = true)
462 {
463 global $DIC;
464 $ilDB = $DIC['ilDB'];
465
466 if (is_null($pass)) {
467 $pass = $this->getSolutionMaxPass($active_id);
468 }
469 // fau: testNav - check existing value1 because the intermediate solution will have a dummy entry
470 $result = $ilDB->queryF(
471 "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",
472 array("integer", "integer", "integer", 'integer'),
473 array($active_id, $this->getId(), $pass, (int) $authorized)
474 );
475 // fau.
476 $found = array();
477
478 while ($data = $ilDB->fetchAssoc($result)) {
479 array_push($found, $data);
480 }
481
482 return $found;
483 }
484
485 public function getPreviewFileUploads(ilAssQuestionPreviewSession $previewSession)
486 {
487 return (array) $previewSession->getParticipantsSolution();
488 }
489
495 public function getUploadedFilesForWeb($active_id, $pass)
496 {
497 global $DIC;
498 $ilDB = $DIC['ilDB'];
499
500 $found = $this->getUploadedFiles($active_id, $pass);
501 $result = $ilDB->queryF(
502 "SELECT test_fi FROM tst_active WHERE active_id = %s",
503 array('integer'),
504 array($active_id)
505 );
506 if ($result->numRows() == 1) {
507 $row = $ilDB->fetchAssoc($result);
508 $test_id = $row["test_fi"];
509 $path = $this->getFileUploadPathWeb($test_id, $active_id);
510 foreach ($found as $idx => $data) {
511 $found[$idx]['webpath'] = $path;
512 }
513 }
514 return $found;
515 }
516
522 protected function deleteUploadedFiles($files, $test_id, $active_id, $authorized)
523 {
524 global $DIC;
525 $ilDB = $DIC['ilDB'];
526
527 $pass = null;
528 $active_id = null;
529 foreach ($files as $solution_id) {
530 $result = $ilDB->queryF(
531 "SELECT * FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
532 array("integer", 'integer'),
533 array($solution_id, (int) $authorized)
534 );
535 if ($result->numRows() == 1) {
536 $data = $ilDB->fetchAssoc($result);
537 $pass = $data['pass'];
538 $active_id = $data['active_fi'];
539 @unlink($this->getFileUploadPath($test_id, $active_id) . $data['value1']);
540 }
541 }
542 foreach ($files as $solution_id) {
543 $affectedRows = $ilDB->manipulateF(
544 "DELETE FROM tst_solutions WHERE solution_id = %s AND authorized = %s",
545 array("integer", 'integer'),
546 array($solution_id, $authorized)
547 );
548 }
549 }
550
551 // fau: testNav new function deleteUnusedFiles()
558 protected function deleteUnusedFiles($test_id, $active_id, $pass)
559 {
560 // read all solutions (authorized and intermediate) from all steps
561 $step = $this->getStep();
562 $this->setStep(null);
563 $solutions = array_merge(
564 $this->getSolutionValues($active_id, $pass, true),
565 $this->getSolutionValues($active_id, $pass, false)
566 );
567 $this->setStep($step);
568
569 // get the used files from these solutions
570 $used_files = array();
571 foreach ($solutions as $solution) {
572 $used_files[] = $solution['value1'];
573 }
574
575 // read the existing files for user and pass
576 // delete all files that are not used in the solutions
577 $uploadPath = $this->getFileUploadPath($test_id, $active_id);
578 if (is_dir($uploadPath) && is_readable($uploadPath)) {
579 $iter = new \RegexIterator(new \DirectoryIterator($uploadPath), '/^file_' . $active_id . '_' . $pass . '_(.*)/');
580 foreach ($iter as $file) {
582 if ($file->isFile() && !in_array($file->getFilename(), $used_files)) {
583 unlink($file->getPathname());
584 }
585 }
586 }
587 }
588 // fau.
589
590 protected function deletePreviewFileUploads($userId, $userSolution, $files)
591 {
592 foreach ($files as $name) {
593 if (isset($userSolution[$name])) {
594 unset($userSolution[$name]);
595 @unlink($this->getPreviewFileUploadPath($userId) . $name);
596 }
597 }
598
599 return $userSolution;
600 }
601
607 public function getMaxFilesizeAsString()
608 {
609 $size = $this->getMaxFilesizeInBytes();
610 if ($size < 1024) {
611 $max_filesize = sprintf("%d Bytes", $size);
612 } elseif ($size < 1024 * 1024) {
613 $max_filesize = sprintf("%.1f KB", $size / 1024);
614 } else {
615 $max_filesize = sprintf("%.1f MB", $size / 1024 / 1024);
616 }
617
618 return $max_filesize;
619 }
620
626 public function getMaxFilesizeInBytes()
627 {
628 if (strlen($this->getMaxSize())) {
629 return $this->getMaxSize();
630 } else {
631 // get the value for the maximal uploadable filesize from the php.ini (if available)
632 $umf = get_cfg_var("upload_max_filesize");
633 // get the value for the maximal post data from the php.ini (if available)
634 $pms = get_cfg_var("post_max_size");
635
636 //convert from short-string representation to "real" bytes
637 $multiplier_a = array("K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024);
638
639 $umf_parts = preg_split("/(\d+)([K|G|M])/", $umf, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
640 $pms_parts = preg_split("/(\d+)([K|G|M])/", $pms, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
641
642 if (count($umf_parts) == 2) {
643 $umf = $umf_parts[0] * $multiplier_a[$umf_parts[1]];
644 }
645 if (count($pms_parts) == 2) {
646 $pms = $pms_parts[0] * $multiplier_a[$pms_parts[1]];
647 }
648
649 // use the smaller one as limit
650 $max_filesize = min($umf, $pms);
651
652 if (!$max_filesize) {
653 $max_filesize = max($umf, $pms);
654 }
655 return $max_filesize;
656 }
657 }
658
659 // hey: prevPassSolutions - refactored method to get intermediate/authorized
660 // as well as upload, delete and previous files working
661 // BASED ON LAST FRED IMPLEMENTATION (@Fred: simply replace and solve unknown calls)
670 public function saveWorkingData($active_id, $pass = null, $authorized = true)
671 {
672 $pass = $this->ensureCurrentTestPass($active_id, $pass);
673 $test_id = $this->lookupTestId($active_id);
674
675 $uploadHandlingRequired = $this->isFileUploadAvailable() && $this->checkUpload();
676
677 $entered_values = false;
678
679 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $uploadHandlingRequired, $test_id, $active_id, $pass, $authorized) {
680 if ($authorized == false) {
681 $this->forceExistingIntermediateSolution($active_id, $pass, true);
682 }
683
684 if ($this->isFileDeletionAction()) {
685 if ($this->isFileDeletionSubmitAvailable()) {
686 foreach ($_POST[self::DELETE_FILES_TBL_POSTVAR] as $solution_id) {
687 $this->removeSolutionRecordById($solution_id);
688 }
689 } else {
690 ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
691 }
692 } else {
693 if ($this->isFileReuseHandlingRequired()) {
694 foreach ($_POST[self::REUSE_FILES_TBL_POSTVAR] as $solutionId) {
695 $solution = $this->getSolutionRecordById($solutionId);
696
697 $this->saveCurrentSolution(
698 $active_id,
699 $pass,
700 $solution['value1'],
701 $solution['value2'],
702 false,
703 $solution['tstamp']
704 );
705 }
706 }
707
708 if ($uploadHandlingRequired) {
709 if (!@file_exists($this->getFileUploadPath($test_id, $active_id))) {
711 }
712
713 $solutionFileVersioningUploadTS = time();
714 $filename_arr = pathinfo($_FILES["upload"]["name"]);
715 $extension = $filename_arr["extension"];
716 $newfile = "file_" . $active_id . "_" . $pass . "_" . $solutionFileVersioningUploadTS . "." . $extension;
717
718 include_once 'Services/Utilities/classes/class.ilFileUtils.php';
719 $dispoFilename = ilFileUtils::getValidFilename($_FILES['upload']['name']);
720 $newfile = ilFileUtils::getValidFilename($newfile);
721
722 ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getFileUploadPath($test_id, $active_id) . $newfile);
723
724 $this->saveCurrentSolution(
725 $active_id,
726 $pass,
727 $newfile,
728 $dispoFilename,
729 false,
730 $solutionFileVersioningUploadTS
731 );
732
733 $entered_values = true;
734 }
735 }
736
737 if ($authorized == true && $this->intermediateSolutionExists($active_id, $pass)) {
738 // remove the dummy record of the intermediate solution
739 $this->deleteDummySolutionRecord($active_id, $pass);
740
741 // delete the authorized solution and make the intermediate solution authorized (keeping timestamps)
742 $this->removeCurrentSolution($active_id, $pass, true);
743 $this->updateCurrentSolutionsAuthorization($active_id, $pass, true, true);
744 }
745
746 $this->deleteUnusedFiles($test_id, $active_id, $pass);
747 });
748
749 if ($entered_values) {
750 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
752 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
753 }
754 } else {
755 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
757 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
758 }
759 }
760
761 return true;
762 }
763 // hey.
764
765 // fau: testNav - remove dummy value when intermediate solution is got for test display
772 public function getUserSolutionPreferingIntermediate($active_id, $pass = null)
773 {
774 $solution = $this->getSolutionValues($active_id, $pass, false);
775
776 if (!count($solution)) {
777 $solution = $this->getSolutionValues($active_id, $pass, true);
778 } else {
779 $cleaned = array();
780 foreach ($solution as $row) {
781 if (!empty($row['value1'])) {
782 $cleaned[] = $row;
783 }
784 }
785 $solution = $cleaned;
786 }
787
788 return $solution;
789 }
790 // fau.
791
792
793 // fau: testNav - remove unused files if an intermediate solution is removed
800 public function removeIntermediateSolution($active_id, $pass)
801 {
802 global $DIC;
803 $ilDB = $DIC['ilDB'];
804
805 $result = parent::removeIntermediateSolution($active_id, $pass);
806
807 // get the current test id
808 // hey: prevPassSolutions - exract until you drop :-D
809 $test_id = $this->lookupTestId($active_id);
810 // hey.
811
812 $this->deleteUnusedFiles($test_id, $active_id, $pass);
813
814 return $result;
815 }
816 // fau.
817
818
819 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
820 {
821 $userSolution = $previewSession->getParticipantsSolution();
822
823 if (!is_array($userSolution)) {
824 $userSolution = array();
825 }
826
827 // hey: prevPassSolutions - readability spree - get a chance to understand the code
828 if ($this->isFileDeletionAction()) {
829 // hey.
830 // hey: prevPassSolutions - readability spree - get a chance to understand the code
831 if ($this->isFileDeletionSubmitAvailable()) {
832 // hey.
833 $userSolution = $this->deletePreviewFileUploads($previewSession->getUserId(), $userSolution, $_POST['deletefiles']);
834 } else {
835 ilUtil::sendInfo($this->lng->txt('no_checkbox'), true);
836 }
837 } else {
838 // hey: prevPassSolutions - readability spree - get a chance to understand the code
839 if ($this->isFileUploadAvailable()) {
840 // hey.
841 if ($this->checkUpload()) {
842 if (!@file_exists($this->getPreviewFileUploadPath($previewSession->getUserId()))) {
844 }
845
846 $version = time();
847 $filename_arr = pathinfo($_FILES["upload"]["name"]);
848 $extension = $filename_arr["extension"];
849 $newfile = "file_" . md5($_FILES["upload"]["name"]) . "_" . $version . "." . $extension;
850 ilUtil::moveUploadedFile($_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $this->getPreviewFileUploadPath($previewSession->getUserId()) . $newfile);
851
852 $userSolution[$newfile] = array(
853 'solution_id' => $newfile,
854 'value1' => $newfile,
855 'value2' => $_FILES['upload']['name'],
856 'tstamp' => $version,
857 'webpath' => $this->getPreviewFileUploadPathWeb($previewSession->getUserId())
858 );
859 }
860 }
861 }
862
863 $previewSession->setParticipantsSolution($userSolution);
864 }
865
875 protected function handleSubmission($active_id, $pass, $obligationsAnswered, $authorized)
876 {
877 if (!$authorized) {
878 return;
879 }
880
881 if ($this->isCompletionBySubmissionEnabled()) {
882 $maxpoints = assQuestion::_getMaximumPoints($this->getId());
883
884 if ($this->getUploadedFiles($active_id, $pass, $authorized)) {
885 $points = $maxpoints;
886 } else {
887 // fau: testNav - don't set reached points if no file is available
888 return;
889 // fau.
890 }
891
892 assQuestion::_setReachedPoints($active_id, $this->getId(), $points, $maxpoints, $pass, 1, $obligationsAnswered);
893
894 // update learning progress
895 include_once 'Modules/Test/classes/class.ilObjTestAccess.php';
896 include_once 'Services/Tracking/classes/class.ilLPStatusWrapper.php';
898 ilObjTest::_getObjectIDFromActiveID((int) $active_id),
899 ilObjTestAccess::_getParticipantId((int) $active_id)
900 );
901 }
902 }
903
909 public function getQuestionType()
910 {
911 return "assFileUpload";
912 }
913
919 public function getAdditionalTableName()
920 {
921 return "qpl_qst_fileupload";
922 }
923
929 public function getAnswerTableName()
930 {
931 return "";
932 }
933
939 public function deleteAnswers($question_id)
940 {
941 }
942
948 {
949 $text = parent::getRTETextWithMediaObjects();
950 return $text;
951 }
952
956 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
957 {
958 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
959
960 $i = 1;
961 $solutions = $this->getSolutionValues($active_id, $pass);
962 foreach ($solutions as $solution) {
963 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
964 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
965 if (strlen($solution["value1"])) {
966 $worksheet->setCell($startrow + $i, 1, $solution["value1"]);
967 $worksheet->setCell($startrow + $i, 2, $solution["value2"]);
968 }
969 $i++;
970 }
971
972 return $startrow + $i + 1;
973 }
974
987 public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
988 {
989 include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assFileUploadImport.php";
990 $import = new assFileUploadImport($this);
991 $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
992 }
993
1000 public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
1001 {
1002 include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assFileUploadExport.php";
1003 $export = new assFileUploadExport($this);
1004 return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
1005 }
1006
1012 public function getBestSolution($active_id, $pass)
1013 {
1014 $user_solution = array();
1015 return $user_solution;
1016 }
1017
1023 public function getMaxSize()
1024 {
1025 return $this->maxsize;
1026 }
1027
1033 public function setMaxSize($a_value)
1034 {
1035 $this->maxsize = $a_value;
1036 }
1037
1044 {
1045 if (strlen($this->allowedextensions)) {
1046 return array_filter(array_map('trim', explode(",", $this->allowedextensions)));
1047 }
1048 return array();
1049 }
1050
1056 public function getAllowedExtensions()
1057 {
1059 }
1060
1066 public function setAllowedExtensions($a_value)
1067 {
1068 $this->allowedextensions = strtolower(trim($a_value));
1069 }
1070
1074 public function __get($value)
1075 {
1076 switch ($value) {
1077 case "maxsize":
1078 return $this->getMaxSize();
1079 break;
1080 case "allowedextensions":
1081 return $this->getAllowedExtensions();
1082 break;
1083 case 'completion_by_submission':
1084 return $this->isCompletionBySubmissionEnabled();
1085 break;
1086 default:
1087 return parent::__get($value);
1088 break;
1089 }
1090 }
1091
1095 public function __set($key, $value)
1096 {
1097 switch ($key) {
1098 case "maxsize":
1099 $this->setMaxSize($value);
1100 break;
1101 case "allowedextensions":
1102 $this->setAllowedExtensions($value);
1103 break;
1104 case 'completion_by_submission':
1105 $this->setCompletionBySubmission($value);
1106 break;
1107 default:
1108 parent::__set($key, $value);
1109 break;
1110 }
1111 }
1112
1120 public function hasFileUploads($test_id)
1121 {
1122 global $DIC;
1123 $ilDB = $DIC['ilDB'];
1124 $query = "
1125 SELECT tst_solutions.solution_id
1126 FROM tst_solutions, tst_active, qpl_questions
1127 WHERE tst_solutions.active_fi = tst_active.active_id
1128 AND tst_solutions.question_fi = qpl_questions.question_id
1129 AND tst_solutions.question_fi = %s AND tst_active.test_fi = %s";
1130 $result = $ilDB->queryF(
1131 $query,
1132 array("integer", "integer"),
1133 array($this->getId(), $test_id)
1134 );
1135 if ($result->numRows() > 0) {
1136 return true;
1137 } else {
1138 return false;
1139 }
1140 }
1141
1147 public function deliverFileUploadZIPFile($ref_id, $test_id, $test_title)
1148 {
1149 global $DIC;
1150 $ilDB = $DIC['ilDB'];
1151 $lng = $DIC['lng'];
1152
1153 require_once 'Modules/TestQuestionPool/classes/class.ilAssFileUploadUploadsExporter.php';
1154 $exporter = new ilAssFileUploadUploadsExporter($ilDB, $lng);
1155
1156 $exporter->setRefId($ref_id);
1157 $exporter->setTestId($test_id);
1158 $exporter->setTestTitle($test_title);
1159 $exporter->setQuestion($this);
1160
1161 $exporter->build();
1162
1164 $exporter->getFinalZipFilePath(),
1165 $exporter->getDispoZipFileName(),
1166 $exporter->getZipFileMimeType(),
1167 false,
1168 true
1169 );
1170 }
1171
1181 {
1183 }
1184
1194 public function setCompletionBySubmission($bool)
1195 {
1196 $this->completion_by_submission = (bool) $bool;
1197 return $this;
1198 }
1199
1211 public function isAnswered($active_id, $pass = null)
1212 {
1213 $numExistingSolutionRecords = assQuestion::getNumExistingSolutionRecords($active_id, $pass, $this->getId());
1214
1215 return $numExistingSolutionRecords > 0;
1216 }
1217
1228 public static function isObligationPossible($questionId)
1229 {
1230 return true;
1231 }
1232
1233 public function isAutosaveable()
1234 {
1235 return false;
1236 }
1237
1238 // fau: testNav - new function getTestQuestionConfig()
1245 // hey: refactored identifiers
1247 // hey.
1248 {
1249 // hey: refactored identifiers
1250 return parent::buildTestPresentationConfig()
1251 // hey.
1252 ->setFormChangeDetectionEnabled(false);
1253 }
1254 // fau.
1255
1256 // hey: prevPassSolutions - additional extractions to get a just chance to understand saveWorkingData()
1260 protected function isFileDeletionAction()
1261 {
1262 require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssFileUploadFileTableDeleteButton.php';
1264 }
1265
1270 {
1271 return $this->isNonEmptyItemListPostSubmission(self::DELETE_FILES_TBL_POSTVAR);
1272 }
1273
1277 protected function isFileReuseSubmitAvailable()
1278 {
1279 return $this->isNonEmptyItemListPostSubmission(self::REUSE_FILES_TBL_POSTVAR);
1280 }
1281
1285 protected function isFileReuseHandlingRequired()
1286 {
1287 if (!$this->getTestPresentationConfig()->isPreviousPassSolutionReuseAllowed()) {
1288 return false;
1289 }
1290
1291 if (!$this->isFileReuseSubmitAvailable()) {
1292 return false;
1293 }
1294
1295 return true;
1296 }
1297
1301 protected function isFileUploadAvailable()
1302 {
1303 if (!isset($_FILES['upload'])) {
1304 return false;
1305 }
1306
1307 if (!isset($_FILES['upload']['tmp_name'])) {
1308 return false;
1309 }
1310
1311 return strlen($_FILES['upload']['tmp_name']) > 0;
1312 }
1313 // hey.
1314}
$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.
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}
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)
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
$DIC
Definition: xapitoken.php:46