ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilExSubmission.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
11{
15 protected $user;
16
20 protected $db;
21
25 protected $lng;
26
30 protected $ctrl;
31
32 protected $assignment; // [ilExAssignment]
33 protected $user_id; // [int]
34 protected $team; // [ilExAssignmentTeam]
35 protected $peer_review; // [ilExPeerReview]
36 protected $is_tutor; // [bool]
37 protected $public_submissions; // [bool]
38
39 public function __construct(ilExAssignment $a_ass, $a_user_id, ilExAssignmentTeam $a_team = null, $a_is_tutor = false, $a_public_submissions = false)
40 {
41 global $DIC;
42
43 $this->user = $DIC->user();
44 $this->db = $DIC->database();
45 $this->lng = $DIC->language();
46 $this->ctrl = $DIC->ctrl();
47 $ilUser = $DIC->user();
48
49 $this->assignment = $a_ass;
50 $this->user_id = $a_user_id;
51 $this->is_tutor = (bool) $a_is_tutor;
52 $this->public_submissions = (bool) $a_public_submissions;
53
54 if ($a_ass->hasTeam()) {
55 if (!$a_team) {
56 include_once "Modules/Exercise/classes/class.ilExAssignmentTeam.php";
57 $this->team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $this->user_id);
58 } else {
59 $this->team = $a_team;
60 }
61 }
62
63 if ($this->assignment->getPeerReview()) {
64 include_once "Modules/Exercise/classes/class.ilExPeerReview.php";
65 $this->peer_review = new ilExPeerReview($this->assignment);
66 }
67 }
68
69 public function getSubmissionType()
70 {
71 switch ($this->assignment->getType()) {
74 return "File";
75
78 return "Object";
79
81 return "Text";
82 };
83 }
84
85
89 public function getAssignment()
90 {
91 return $this->assignment;
92 }
93
97 public function getTeam()
98 {
99 return $this->team;
100 }
101
105 public function getPeerReview()
106 {
107 return $this->peer_review;
108 }
109
110 public function validatePeerReviews()
111 {
112 $res = array();
113 foreach ($this->getUserIds() as $user_id) {
114 $valid = true;
115
116 // no peer review == valid
117 if ($this->peer_review) {
118 $valid = $this->peer_review->isFeedbackValidForPassed($user_id);
119 }
120
122 }
123 return $res;
124 }
125
126 public function getUserId()
127 {
128 return $this->user_id;
129 }
130
131 public function getUserIds()
132 {
133 if ($this->team &&
134 !$this->hasNoTeamYet()) {
135 return $this->team->getMembers();
136 }
137
138 // if has no team currently there still might be uploads attached
139 return array($this->user_id);
140 }
141
142 public function getFeedbackId()
143 {
144 if ($this->team) {
145 return "t" . $this->team->getId();
146 } else {
147 return $this->getUserId();
148 }
149 }
150
151 public function hasSubmitted()
152 {
153 return (bool) sizeof($this->getFiles(null, true));
154 }
155
156 public function getSelectedObject()
157 {
158 $files = $this->getFiles();
159 if (sizeof($files)) {
160 return array_pop($files);
161 }
162 }
163
164 public function canSubmit()
165 {
166 return ($this->isOwner() &&
167 !$this->assignment->notStartedYet() &&
168 $this->assignment->beforeDeadline());
169 }
170
171 public function canView()
172 {
174
175 if ($this->canSubmit() ||
176 $this->isTutor() ||
177 $this->isInTeam() ||
178 $this->public_submissions) {
179 return true;
180 }
181
182 // #16115
183 if ($this->peer_review) {
184 // peer review givers may view peer submissions
185 foreach ($this->peer_review->getPeerReviewsByPeerId($this->getUserId()) as $giver) {
186 if ($giver["giver_id"] == $ilUser->getId()) {
187 return true;
188 }
189 }
190 }
191
192 return false;
193 }
194
195 public function isTutor()
196 {
197 return $this->is_tutor;
198 }
199
200 public function hasNoTeamYet()
201 {
202 if ($this->assignment->hasTeam() &&
203 !$this->team->getId()) {
204 return true;
205 }
206 return false;
207 }
208
209 public function isInTeam($a_user_id = null)
210 {
212
213 if (!$a_user_id) {
214 $a_user_id = $ilUser->getId();
215 }
216 return in_array($a_user_id, $this->getUserIds());
217 }
218
219 public function isOwner()
220 {
222
223 return ($ilUser->getId() == $this->getUserId());
224 }
225
226 public function hasPeerReviewAccess()
227 {
228 return ($this->peer_review &&
229 $this->peer_review->hasPeerReviewAccess($this->user_id));
230 }
231
232 public function canAddFile()
233 {
234 if (!$this->canSubmit()) {
235 return false;
236 }
237
238 $max = $this->getAssignment()->getMaxFile();
239 if ($max &&
240 $max <= sizeof($this->getFiles())) {
241 return false;
242 }
243
244 return true;
245 }
246
247
248 //
249 // FILES
250 //
251
252 protected function isLate()
253 {
254 $dl = $this->assignment->getPersonalDeadline($this->getUserId());
255 return ($dl && $dl < time());
256 }
257
258 protected function initStorage()
259 {
260 include_once("./Modules/Exercise/classes/class.ilFSStorageExercise.php");
261 return new ilFSStorageExercise($this->assignment->getExerciseId(), $this->assignment->getId());
262 }
263
267 public function uploadFile($a_http_post_files, $unzip = false)
268 {
270
271 if (!$this->canAddFile()) {
272 return false;
273 }
274
275 $deliver_result = $this->initStorage()->uploadFile($a_http_post_files, $this->getUserId(), $unzip);
276
277 if ($deliver_result) {
278 $next_id = $ilDB->nextId("exc_returned");
279 $query = sprintf(
280 "INSERT INTO exc_returned " .
281 "(returned_id, obj_id, user_id, filename, filetitle, mimetype, ts, ass_id, late) " .
282 "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
283 $ilDB->quote($next_id, "integer"),
284 $ilDB->quote($this->assignment->getExerciseId(), "integer"),
285 $ilDB->quote($this->getUserId(), "integer"),
286 $ilDB->quote($deliver_result["fullname"], "text"),
287 $ilDB->quote(ilFileUtils::getValidFilename($a_http_post_files["name"]), "text"),
288 $ilDB->quote($deliver_result["mimetype"], "text"),
289 $ilDB->quote(ilUtil::now(), "timestamp"),
290 $ilDB->quote($this->assignment->getId(), "integer"),
291 $ilDB->quote($this->isLate(), "integer")
292 );
293 $ilDB->manipulate($query);
294
295 if ($this->team) {
296 $this->team->writeLog(
298 $a_http_post_files["name"]
299 );
300 }
301
302 return true;
303 }
304 }
305
310 public function processUploadedZipFile($fileTmp)
311 {
313
314 // Create unzip-directory
315 $newDir = ilUtil::ilTempnam();
316 ilUtil::makeDir($newDir);
317
318 include_once("Services/Utilities/classes/class.ilFileUtils.php");
319
320 $success = true;
321
322 try {
323 ilFileUtils::processZipFile($newDir, $fileTmp, false);
324 ilFileUtils::recursive_dirscan($newDir, $filearray);
325
326 // #18441 - check number of files in zip
327 $max_num = $this->assignment->getMaxFile();
328 if ($max_num) {
329 $current_num = sizeof($this->getFiles());
330 $zip_num = sizeof($filearray["file"]);
331 if ($current_num + $zip_num > $max_num) {
332 $success = false;
333 ilUtil::sendFailure($lng->txt("exc_upload_error") . " [Zip1]", true);
334 }
335 }
336
337 if ($success) {
338 foreach ($filearray["file"] as $key => $filename) {
339 $a_http_post_files["name"] = ilFileUtils::utf8_encode($filename);
340 $a_http_post_files["type"] = "other";
341 $a_http_post_files["tmp_name"] = $filearray["path"][$key] . "/" . $filename;
342 $a_http_post_files["error"] = 0;
343 $a_http_post_files["size"] = filesize($filearray["path"][$key] . "/" . $filename);
344
345 if (!$this->uploadFile($a_http_post_files, true)) {
346 $success = false;
347 ilUtil::sendFailure($lng->txt("exc_upload_error") . " [Zip2]", true);
348 }
349 }
350 }
351 } catch (ilFileUtilsException $e) {
352 $success = false;
353 ilUtil::sendFailure($e->getMessage());
354 }
355
356 ilUtil::delDir($newDir);
357 return $success;
358 }
359
360 public static function hasAnySubmissions($a_ass_id)
361 {
362 global $DIC;
363
364 $ilDB = $DIC->database();
365
366 $query = "SELECT * FROM exc_returned" .
367 " WHERE ass_id = " . $ilDB->quote($a_ass_id, "integer") .
368 " AND (filename IS NOT NULL OR atext IS NOT NULL)" .
369 " AND ts IS NOT NULL";
370 $res = $ilDB->query($query);
371 return $res->numRows($res);
372 }
373
374 public static function getAllAssignmentFiles($a_exc_id, $a_ass_id)
375 {
376 global $DIC;
377
378 $ilDB = $DIC->database();
379
380 include_once("./Modules/Exercise/classes/class.ilFSStorageExercise.php");
381 $storage = new ilFSStorageExercise($a_exc_id, $a_ass_id);
382 $path = $storage->getAbsoluteSubmissionPath();
383
384 $query = "SELECT * FROM exc_returned WHERE ass_id = " .
385 $ilDB->quote($a_ass_id, "integer");
386
387 $res = $ilDB->query($query);
388 while ($row = $ilDB->fetchAssoc($res)) {
389 $row["timestamp"] = $row["ts"];
390 $row["filename"] = $path . "/" . $row["user_id"] . "/" . basename($row["filename"]);
391 $delivered[] = $row;
392 }
393
394 return $delivered ? $delivered : array();
395 }
396
397 public function getFiles(array $a_file_ids = null, $a_only_valid = false, $a_min_timestamp = null)
398 {
400
401 $sql = "SELECT * FROM exc_returned" .
402 " WHERE ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") .
403 " AND " . $ilDB->in("user_id", $this->getUserIds(), "", "integer");
404
405 if ($a_file_ids) {
406 $sql .= " AND " . $ilDB->in("returned_id", $a_file_ids, false, "integer");
407 }
408
409 if ($a_min_timestamp) {
410 $sql .= " AND ts > " . $ilDB->quote($a_min_timestamp, "timestamp");
411 }
412
413 $result = $ilDB->query($sql);
414
415 $delivered_files = array();
416 if ($ilDB->numRows($result)) {
417 $path = $this->initStorage()->getAbsoluteSubmissionPath();
418
419 while ($row = $ilDB->fetchAssoc($result)) {
420 // blog/portfolio/text submissions
421 if ($a_only_valid &&
422 !$row["filename"] &&
423 !(trim($row["atext"]))) {
424 continue;
425 }
426
427 $row["owner_id"] = $row["user_id"];
428 $row["timestamp"] = $row["ts"];
429 $row["timestamp14"] = substr($row["ts"], 0, 4) .
430 substr($row["ts"], 5, 2) . substr($row["ts"], 8, 2) .
431 substr($row["ts"], 11, 2) . substr($row["ts"], 14, 2) .
432 substr($row["ts"], 17, 2);
433 $row["filename"] = $path .
434 "/" . $row["user_id"] . "/" . basename($row["filename"]);
435
436 // see 22301, 22719
437 if (is_file($row["filename"]) || (!in_array(
438 $this->assignment->getType(),
440 ))) {
441 array_push($delivered_files, $row);
442 }
443 }
444 }
445
446 return $delivered_files;
447 }
448
455 public function lookupNewFiles($a_tutor = null)
456 {
459
460 $tutor = ($a_tutor)
461 ? $a_tutor
462 : $ilUser->getId();
463
464 $q = "SELECT exc_returned.returned_id AS id " .
465 "FROM exc_usr_tutor, exc_returned " .
466 "WHERE exc_returned.ass_id = exc_usr_tutor.ass_id " .
467 " AND exc_returned.user_id = exc_usr_tutor.usr_id " .
468 " AND exc_returned.ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") .
469 " AND " . $ilDB->in("exc_returned.user_id", $this->getUserIds(), "", "integer") .
470 " AND exc_usr_tutor.tutor_id = " . $ilDB->quote($tutor, "integer") .
471 " AND exc_usr_tutor.download_time < exc_returned.ts ";
472
473 $new_up_set = $ilDB->query($q);
474
475 $new_up = array();
476 while ($new_up_rec = $ilDB->fetchAssoc($new_up_set)) {
477 $new_up[] = $new_up_rec["id"];
478 }
479
480 return $new_up;
481 }
482
489 public static function lookupExerciseIdForReturnedId($a_returned_id)
490 {
491 global $DIC;
492
493 $ilDB = $DIC->database();
494
495 $set = $ilDB->query("SELECT obj_id" .
496 " FROM exc_returned" .
497 " WHERE returned_id = " . $ilDB->quote($a_returned_id, "integer"));
498 $row = $ilDB->fetchAssoc($set);
499 return (int) $row["obj_id"];
500 }
501
510 public static function findUserFiles($a_user_id, $a_filetitle)
511 {
512 global $DIC;
513
514 $ilDB = $DIC->database();
515
516 $set = $ilDB->query("SELECT obj_id, ass_id" .
517 " FROM exc_returned" .
518 " WHERE user_id = " . $ilDB->quote($a_user_id, "integer") .
519 " AND filetitle = " . $ilDB->quote($a_filetitle, "text"));
520 $res = array();
521 while ($row = $ilDB->fetchAssoc($set)) {
522 $res[$row["ass_id"]] = $row;
523 }
524 return $res;
525 }
526
527 public function deleteAllFiles()
528 {
529 $files = array();
530 foreach ($this->getFiles() as $item) {
531 $files[] = $item["returned_id"];
532 }
533 if (sizeof($files)) {
535 }
536 }
537
544 public function deleteSelectedFiles(array $file_id_array)
545 {
547
548 $user_ids = $this->getUserIds();
549 if (!$user_ids ||
550 !sizeof($file_id_array)) {
551 return;
552 }
553
554 if (count($file_id_array)) {
555 $result = $ilDB->query("SELECT * FROM exc_returned" .
556 " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") .
557 " AND " . $ilDB->in("user_id", $user_ids, "", "integer"));
558
559 if ($ilDB->numRows($result)) {
560 $result_array = array();
561 while ($row = $ilDB->fetchAssoc($result)) {
562 $row["timestamp"] = $row["ts"];
563 array_push($result_array, $row);
564 }
565
566 // delete the entries in the database
567 $ilDB->manipulate("DELETE FROM exc_returned" .
568 " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") .
569 " AND " . $ilDB->in("user_id", $user_ids, "", "integer"));
570
571 // delete the files
572 $path = $this->initStorage()->getAbsoluteSubmissionPath();
573 foreach ($result_array as $key => $value) {
574 if ($value["filename"]) {
575 if ($this->team) {
576 $this->team->writeLog(
578 $value["filetitle"]
579 );
580 }
581
582 $filename = $path . "/" . $value["user_id"] . "/" . basename($value["filename"]);
583 unlink($filename);
584 }
585 }
586 }
587 }
588 }
589
596 public static function deleteUser($a_exc_id, $a_user_id)
597 {
598 include_once("./Modules/Exercise/classes/class.ilExAssignment.php");
599
600 foreach (ilExAssignment::getInstancesByExercise($a_exc_id) as $ass) {
601 $submission = new self($ass, $a_user_id);
602 $submission->deleteAllFiles();
603
604 // remove from any team
605 $team = $submission->getTeam();
606 if ($team) {
607 $team->removeTeamMember($a_user_id);
608 }
609
610 // #14900
611 $member_status = $ass->getMemberStatus($a_user_id);
612 $member_status->setStatus("notgraded");
613 $member_status->update();
614 }
615 }
616
617 protected function getLastDownloadTime(array $a_user_ids)
618 {
621
622 $q = "SELECT download_time FROM exc_usr_tutor WHERE " .
623 " ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") . " AND " .
624 $ilDB->in("usr_id", $a_user_ids, "", "integer") . " AND " .
625 " tutor_id = " . $ilDB->quote($ilUser->getId(), "integer");
626 $lu_set = $ilDB->query($q);
627 $lu_rec = $ilDB->fetchAssoc($lu_set);
628 return $lu_rec["download_time"];
629 }
630
631 public function downloadFiles(array $a_file_ids = null, $a_only_new = false, $a_peer_review_mask_filename = false)
632 {
635
636 $user_ids = $this->getUserIds();
637 $is_team = $this->assignment->hasTeam();
638
639 // get last download time
640 $download_time = null;
641 if ($a_only_new) {
642 $download_time = $this->getLastDownloadTime($user_ids);
643 }
644
645 if ($this->is_tutor) {
647 }
648
649 if ($a_peer_review_mask_filename) {
650 // process peer review sequence id
651 $peer_id = null;
652 foreach ($this->peer_review->getPeerReviewsByGiver($ilUser->getId()) as $idx => $item) {
653 if ($item["peer_id"] == $this->getUserId()) {
654 $peer_id = $idx+1;
655 break;
656 }
657 }
658
659 // this will remove personal info from zip-filename
660 $is_team = true;
661 }
662
663 $files = $this->getFiles($a_file_ids, false, $download_time);
664 if ($files) {
665 if (sizeof($files) == 1) {
666 $file = array_pop($files);
667
668 switch ($this->assignment->getType()) {
671 $file["filetitle"] = ilObjUser::_lookupName($file["user_id"]);
672 $file["filetitle"] = ilObject::_lookupTitle($this->assignment->getExerciseId()) . " - " .
673 $this->assignment->getTitle() . " - " .
674 $file["filetitle"]["firstname"] . " " .
675 $file["filetitle"]["lastname"] . " (" .
676 $file["filetitle"]["login"] . ").zip";
677 break;
678
679 default:
680 break;
681 }
682
683 if ($a_peer_review_mask_filename) {
684 $suffix = array_pop(explode(".", $file["filetitle"]));
685 $file["filetitle"] = $this->assignment->getTitle() . "_peer" . $peer_id . "." . $suffix;
686 } elseif ($file["late"]) {
687 $file["filetitle"] = $lng->txt("exc_late_submission") . " - " .
688 $file["filetitle"];
689 }
690
691 $this->downloadSingleFile($file["user_id"], $file["filename"], $file["filetitle"]);
692 } else {
693 $array_files = array();
694 foreach ($files as $seq => $file) {
695 $src = basename($file["filename"]);
696 if ($a_peer_review_mask_filename) {
697 $suffix = array_pop(explode(".", $src));
698 $tgt = $this->assignment->getTitle() . "_peer" . $peer_id .
699 "_" . (++$seq) . "." . $suffix;
700
701 $array_files[$file["user_id"]][] = array(
702 "src" => $src,
703 "tgt" => $tgt
704 );
705 } else {
706 $array_files[$file["user_id"]][] = array(
707 "src" => $src,
708 "late" => $file["late"]
709 );
710 }
711 }
712
714 $array_files,
715 ($is_team ? null : $this->getUserId()),
716 $is_team
717 );
718 }
719 } else {
720 return false;
721 }
722
723 return true;
724 }
725
726 // Update the timestamp of the last download of current user (=tutor)
727 public function updateTutorDownloadTime()
728 {
731
732 $exc_id = $this->assignment->getExerciseId();
733 $ass_id = $this->assignment->getId();
734
735 foreach ($this->getUserIds() as $user_id) {
736 $ilDB->manipulateF(
737 "DELETE FROM exc_usr_tutor " .
738 "WHERE ass_id = %s AND usr_id = %s AND tutor_id = %s",
739 array("integer", "integer", "integer"),
740 array($ass_id, $user_id, $ilUser->getId())
741 );
742
743 $ilDB->manipulateF(
744 "INSERT INTO exc_usr_tutor (ass_id, obj_id, usr_id, tutor_id, download_time) VALUES " .
745 "(%s, %s, %s, %s, %s)",
746 array("integer", "integer", "integer", "integer", "timestamp"),
747 array($ass_id, $exc_id, $user_id, $ilUser->getId(), ilUtil::now())
748 );
749 }
750 }
751
752 protected function downloadSingleFile($a_user_id, $filename, $filetitle)
753 {
754 $filename = $this->initStorage()->getAbsoluteSubmissionPath() .
755 "/" . $a_user_id . "/" . basename($filename);
756
757 ilUtil::deliverFile($filename, $filetitle);
758 }
759
760 protected function downloadMultipleFiles($a_filenames, $a_user_id, $a_multi_user = false)
761 {
763
764 $path = $this->initStorage()->getAbsoluteSubmissionPath();
765
766 $cdir = getcwd();
767
768 $zip = PATH_TO_ZIP;
769 $tmpdir = ilUtil::ilTempnam();
770 $tmpfile = ilUtil::ilTempnam();
771 $tmpzipfile = $tmpfile . ".zip";
772
773 ilUtil::makeDir($tmpdir);
774 chdir($tmpdir);
775
776 $assTitle = ilExAssignment::lookupTitle($this->assignment->getId());
777 $deliverFilename = str_replace(" ", "_", $assTitle);
778 if ($a_user_id > 0 && !$a_multi_user) {
779 $userName = ilObjUser::_lookupName($a_user_id);
780 $deliverFilename .= "_" . $userName["lastname"] . "_" . $userName["firstname"];
781 } else {
782 $deliverFilename .= "_files";
783 }
784 $orgDeliverFilename = trim($deliverFilename);
785 $deliverFilename = ilUtil::getASCIIFilename($orgDeliverFilename);
786 ilUtil::makeDir($tmpdir . "/" . $deliverFilename);
787 chdir($tmpdir . "/" . $deliverFilename);
788
789 //copy all files to a temporary directory and remove them afterwards
790 $parsed_files = $duplicates = array();
791 foreach ($a_filenames as $user_id => $files) {
792 $pathname = $path . "/" . $user_id;
793
794 foreach ($files as $filename) {
795 // peer review masked filenames, see deliverReturnedFiles()
796 if (isset($filename["tgt"])) {
797 $newFilename = $filename["tgt"];
798 $filename = $filename["src"];
799 } else {
800 $late = $filename["late"];
801 $filename = $filename["src"];
802
803 // remove timestamp
804 $newFilename = trim($filename);
805 $pos = strpos($newFilename, "_");
806 if ($pos !== false) {
807 $newFilename = substr($newFilename, $pos + 1);
808 }
809 // #11070
810 $chkName = strtolower($newFilename);
811 if (array_key_exists($chkName, $duplicates)) {
812 $suffix = strrpos($newFilename, ".");
813 $newFilename = substr($newFilename, 0, $suffix) .
814 " (" . (++$duplicates[$chkName]) . ")" .
815 substr($newFilename, $suffix);
816 } else {
817 $duplicates[$chkName] = 1;
818 }
819
820 if ($late) {
821 $newFilename = $lng->txt("exc_late_submission") . " - " .
822 $newFilename;
823 }
824 }
825
826 $newFilename = ilUtil::getASCIIFilename($newFilename);
827 $newFilename = $tmpdir . DIRECTORY_SEPARATOR . $deliverFilename . DIRECTORY_SEPARATOR . $newFilename;
828 // copy to temporal directory
829 $oldFilename = $pathname . DIRECTORY_SEPARATOR . $filename;
830 if (!copy($oldFilename, $newFilename)) {
831 echo 'Could not copy ' . $oldFilename . ' to ' . $newFilename;
832 }
833 touch($newFilename, filectime($oldFilename));
834 $parsed_files[] = ilUtil::escapeShellArg($deliverFilename . DIRECTORY_SEPARATOR . basename($newFilename));
835 }
836 }
837
838 chdir($tmpdir);
839 $zipcmd = $zip . " " . ilUtil::escapeShellArg($tmpzipfile) . " " . join($parsed_files, " ");
840
841 exec($zipcmd);
842 ilUtil::delDir($tmpdir);
843
844 chdir($cdir);
845 ilUtil::deliverFile($tmpzipfile, $orgDeliverFilename . ".zip", "", false, true);
846 exit;
847 }
848
855 public static function downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members)
856 {
857 global $DIC;
858
859 $lng = $DIC->language();
860
861 include_once("./Modules/Exercise/classes/class.ilFSStorageExercise.php");
862
863 $storage = new ilFSStorageExercise($a_ass->getExerciseId(), $a_ass->getId());
864 $storage->create();
865
866 ksort($members);
867 //$savepath = $this->getExercisePath() . "/" . $this->obj_id . "/";
868 $savepath = $storage->getAbsoluteSubmissionPath();
869 $cdir = getcwd();
870
871
872 // important check: if the directory does not exist
873 // ILIAS stays in the current directory (echoing only a warning)
874 // and the zip command below archives the whole ILIAS directory
875 // (including the data directory) and sends a mega file to the user :-o
876 if (!is_dir($savepath)) {
877 return;
878 }
879 // Safe mode fix
880 // chdir($this->getExercisePath());
881 chdir($storage->getTempPath());
882 $zip = PATH_TO_ZIP;
883
884 // check first, if we have enough free disk space to copy all files to temporary directory
885 $tmpdir = ilUtil::ilTempnam();
886 ilUtil::makeDir($tmpdir);
887 chdir($tmpdir);
888
889 // check free diskspace
890 $dirsize = 0;
891 foreach (array_keys($members) as $id) {
892 $directory = $savepath . DIRECTORY_SEPARATOR . $id;
893 $dirsize += ilUtil::dirsize($directory);
894 }
895 if ($dirsize > disk_free_space($tmpdir)) {
896 return -1;
897 }
898
899 $ass_type = $a_ass->getType();
900
901 // copy all member directories to the temporary folder
902 // switch from id to member name and append the login if the member name is double
903 // ensure that no illegal filenames will be created
904 // remove timestamp from filename
905 if ($a_ass->hasTeam()) {
906 $team_dirs = array();
907 $team_map = ilExAssignmentTeam::getAssignmentTeamMap($a_ass->getId());
908 }
909 foreach ($members as $id => $item) {
910 $user = $item["name"];
911 $user_files = $item["files"];
912
913 $sourcedir = $savepath . DIRECTORY_SEPARATOR . $id;
914 if (!is_dir($sourcedir)) {
915 continue;
916 }
917
918 $userName = ilObjUser::_lookupName($id);
919
920 // group by teams
921 $team_dir= "";
922 if (is_array($team_map) &&
923 array_key_exists($id, $team_map)) {
924 $team_id = $team_map[$id];
925 if (!array_key_exists($team_id, $team_dirs)) {
926 $team_dir = $lng->txt("exc_team") . " " . $team_id;
927 ilUtil::makeDir($team_dir);
928 $team_dirs[$team_id] = $team_dir;
929 }
930 $team_dir = $team_dirs[$team_id] . DIRECTORY_SEPARATOR;
931 }
932
933 $targetdir = $team_dir . ilUtil::getASCIIFilename(
934 trim($userName["lastname"]) . "_" .
935 trim($userName["firstname"]) . "_" .
936 trim($userName["login"]) . "_" .
937 $userName["user_id"]
938 );
939 ilUtil::makeDir($targetdir);
940
941 $sourcefiles = scandir($sourcedir);
942 $duplicates = array();
943 foreach ($sourcefiles as $sourcefile) {
944 if ($sourcefile == "." || $sourcefile == "..") {
945 continue;
946 }
947
948 $targetfile = trim(basename($sourcefile));
949 $pos = strpos($targetfile, "_");
950 if ($pos !== false) {
951 $targetfile= substr($targetfile, $pos + 1);
952 }
953
954 // #14536
955 if (array_key_exists($targetfile, $duplicates)) {
956 $suffix = strrpos($targetfile, ".");
957 $targetfile = substr($targetfile, 0, $suffix) .
958 " (" . (++$duplicates[$targetfile]) . ")" .
959 substr($targetfile, $suffix);
960 } else {
961 $duplicates[$targetfile] = 1;
962 }
963
964 // late submission?
965 if (is_array($user_files)) { // see #23900
966 foreach ($user_files as $file) {
967 if (basename($file["filename"]) == $sourcefile) {
968 if ($file["late"]) {
969 $targetfile = $lng->txt("exc_late_submission") . " - " .
970 $targetfile;
971 }
972 break;
973 }
974 }
975 }
976
977 $targetfile = ilUtil::getASCIIFilename($targetfile);
978 $targetfile = $targetdir . DIRECTORY_SEPARATOR . $targetfile;
979 $sourcefile = $sourcedir . DIRECTORY_SEPARATOR . $sourcefile;
980
981 if (!copy($sourcefile, $targetfile)) {
982 include_once "Modules/Exercise/exceptions/class.ilExerciseException.php";
983 throw new ilExerciseException("Could not copy " . basename($sourcefile) . " to '" . $targetfile . "'.");
984 } else {
985 // preserve time stamp
986 touch($targetfile, filectime($sourcefile));
987
988 // blogs and portfolios are stored as zip and have to be unzipped
989 if ($ass_type == ilExAssignment::TYPE_PORTFOLIO ||
990 $ass_type == ilExAssignment::TYPE_BLOG) {
991 ilUtil::unzip($targetfile);
992 unlink($targetfile);
993 }
994 }
995 }
996 }
997
998 $tmpfile = ilUtil::ilTempnam();
999 $tmpzipfile = $tmpfile . ".zip";
1000 // Safe mode fix
1001 $zipcmd = $zip . " -r " . ilUtil::escapeShellArg($tmpzipfile) . " .";
1002 exec($zipcmd);
1003 ilUtil::delDir($tmpdir);
1004
1005 $assTitle = $a_ass->getTitle() . "_" . $a_ass->getId();
1006 chdir($cdir);
1007 ilUtil::deliverFile($tmpzipfile, (strlen($assTitle) == 0
1008 ? strtolower($lng->txt("exc_assignment"))
1009 : $assTitle) . ".zip", "", false, true);
1010 }
1011
1017 public function getLastSubmission()
1018 {
1019 $ilDB = $this->db;
1020
1021 $ilDB->setLimit(1);
1022
1023 $q = "SELECT obj_id,user_id,ts FROM exc_returned" .
1024 " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer") .
1025 " AND " . $ilDB->in("user_id", $this->getUserIds(), "", "integer") .
1026 " AND (filename IS NOT NULL OR atext IS NOT NULL)" .
1027 " AND ts IS NOT NULL" .
1028 " ORDER BY ts DESC";
1029 $usr_set = $ilDB->query($q);
1030 $array = $ilDB->fetchAssoc($usr_set);
1031 return ilUtil::getMySQLTimestamp($array["ts"]);
1032 }
1033
1034
1035 //
1036 // OBJECTS
1037 //
1038
1045 public function addResourceObject($a_wsp_id, $a_text = null)
1046 {
1047 $ilDB = $this->db;
1048
1049 $next_id = $ilDB->nextId("exc_returned");
1050 $query = sprintf(
1051 "INSERT INTO exc_returned " .
1052 "(returned_id, obj_id, user_id, filetitle, ass_id, ts, atext, late) " .
1053 "VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
1054 $ilDB->quote($next_id, "integer"),
1055 $ilDB->quote($this->assignment->getExerciseId(), "integer"),
1056 $ilDB->quote($this->getUserId(), "integer"),
1057 $ilDB->quote($a_wsp_id, "text"),
1058 $ilDB->quote($this->assignment->getId(), "integer"),
1059 $ilDB->quote(ilUtil::now(), "timestamp"),
1060 $ilDB->quote($a_text, "text"),
1061 $ilDB->quote($this->isLate(), "integer")
1062 );
1063 $ilDB->manipulate($query);
1064
1065 return $next_id;
1066 }
1067
1073 public function deleteResourceObject($a_returned_id)
1074 {
1075 $ilDB = $this->db;
1076
1077 $ilDB->manipulate("DELETE FROM exc_returned" .
1078 " WHERE obj_id = " . $ilDB->quote($this->assignment->getExerciseId(), "integer") .
1079 " AND user_id = " . $ilDB->quote($this->getUserId(), "integer") .
1080 " AND ass_id = " . $ilDB->quote($this->assignment->getId(), "integer") .
1081 " AND returned_id = " . $ilDB->quote($a_returned_id, "integer"));
1082 }
1083
1090 public function updateTextSubmission($a_text)
1091 {
1092 $ilDB = $this->db;
1093
1094 $files = $this->getFiles();
1095
1096 // no text = remove submission
1097 if (!trim($a_text)) {
1098 $this->deleteAllFiles();
1099 return;
1100 }
1101
1102 if (!$files) {
1103 return $this->addResourceObject("TEXT", $a_text);
1104 } else {
1105 $files = array_shift($files);
1106 $id = $files["returned_id"];
1107 if ($id) {
1108 $ilDB->manipulate("UPDATE exc_returned" .
1109 " SET atext = " . $ilDB->quote($a_text, "text") .
1110 ", ts = " . $ilDB->quote(ilUtil::now(), "timestamp") .
1111 ", late = " . $ilDB->quote($this->isLate(), "integer") .
1112 " WHERE returned_id = " . $ilDB->quote($id, "integer"));
1113 return $id;
1114 }
1115 }
1116 }
1117
1118
1119 //
1120 // GUI helper
1121 //
1122
1123 // :TODO:
1124
1125 public function getDownloadedFilesInfoForTableGUIS($a_parent_obj, $a_parent_cmd = null)
1126 {
1127 $lng = $this->lng;
1129
1130 $result = array();
1131 $result["files"]["count"] = "---";
1132
1133 // submission:
1134 // see if files have been resubmmited after solved
1135 $last_sub = $this->getLastSubmission();
1136 if ($last_sub) {
1137 $last_sub = ilDatePresentation::formatDate(new ilDateTime($last_sub, IL_CAL_DATETIME));
1138 } else {
1139 $last_sub = "---";
1140 }
1141 /* #13741 - status_time has been reduced to grading (mark/status)
1142 if (self::lookupUpdatedSubmission($a_ass_id, $a_user_id) == 1)
1143 {
1144 $last_sub = "<b>".$last_sub."</b>";
1145 }
1146 */
1147 $result["last_submission"]["txt"] = $lng->txt("exc_last_submission");
1148 $result["last_submission"]["value"] = $last_sub;
1149
1150 // #16994
1151 $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", $this->getUserId());
1152
1153 // assignment type specific
1154 switch ($this->assignment->getType()) {
1156 // data is merged by team - see above
1157 // fallthrough
1158
1160 $all_files = $this->getFiles();
1161 $late_files = 0;
1162 foreach ($all_files as $file) {
1163 if ($file["late"]) {
1164 $late_files++;
1165 }
1166 }
1167
1168 // nr of submitted files
1169 $result["files"]["txt"] = $lng->txt("exc_files_returned");
1170 if ($late_files) {
1171 $result["files"]["txt"].= ' - <span class="warning">' . $lng->txt("exc_late_submission") . " (" . $late_files . ")</span>";
1172 }
1173 $sub_cnt = count($all_files);
1174 $new = $this->lookupNewFiles();
1175 if (count($new) > 0) {
1176 $sub_cnt.= " " . sprintf($lng->txt("cnt_new"), count($new));
1177 }
1178
1179 $result["files"]["count"] = $sub_cnt;
1180
1181 // download command
1182 if ($sub_cnt > 0) {
1183 $result["files"]["download_url"] =
1184 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1185
1186 if (count($new) <= 0) {
1187 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1188 } else {
1189 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_all_files");
1190 }
1191
1192 // download new files only
1193 if (count($new) > 0) {
1194 $result["files"]["download_new_url"] =
1195 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadNewReturned");
1196
1197 $result["files"]["download_new_txt"] = $lng->txt("exc_tbl_action_download_new_files");
1198 }
1199 }
1200 break;
1201
1203 $result["files"]["txt"] =$lng->txt("exc_blog_returned");
1204 $blogs = $this->getFiles();
1205 if ($blogs) {
1206 $blogs = array_pop($blogs);
1207 if ($blogs && substr($blogs["filename"], -1) != "/") {
1208 if ($blogs["late"]) {
1209 $result["files"]["txt"].= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1210 }
1211
1212 $result["files"]["count"] = 1;
1213
1214 $result["files"]["download_url"] =
1215 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1216
1217 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1218 }
1219 }
1220 break;
1221
1223 $result["files"]["txt"] = $lng->txt("exc_portfolio_returned");
1224 $portfolios = $this->getFiles();
1225 if ($portfolios) {
1226 $portfolios = array_pop($portfolios);
1227 if ($portfolios && substr($portfolios["filename"], -1) != "/") {
1228 if ($portfolios["late"]) {
1229 $result["files"]["txt"].= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1230 }
1231
1232 $result["files"]["count"] = 1;
1233
1234 $result["files"]["download_url"] =
1235 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1236
1237 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1238 }
1239 }
1240 break;
1241
1243 $result["files"]["txt"] = $lng->txt("exc_files_returned_text");
1244 $files = $this->getFiles();
1245 if ($files) {
1246 $result["files"]["count"] = 1;
1247
1248 $files = array_shift($files);
1249 if (trim($files["atext"])) {
1250 if ($files["late"]) {
1251 $result["files"]["txt"].= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1252 }
1253
1254 $result["files"]["download_url"] =
1255 $ilCtrl->getLinkTargetByClass("ilexsubmissiontextgui", "showAssignmentText");
1256
1257 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_text_assignment_show");
1258 }
1259 }
1260 break;
1261 }
1262
1263 $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", "");
1264
1265 return $result;
1266 }
1267}
sprintf('%.4f', $callTime)
$result
user()
Definition: user.php:4
$success
Definition: Utf8Test.php:86
$files
Definition: add-vimline.php:18
An exception for terminatinating execution or to throw for unit testing.
const IL_CAL_DATETIME
static formatDate(ilDateTime $date, $a_skip_day=false, $a_include_wd=false)
Format a date @access public.
@classDescription Date and time handling
Exercise assignment team.
static getAssignmentTeamMap($a_ass_id)
Get team structure for assignment.
static getInstanceByUserId($a_assignment_id, $a_user_id, $a_create_on_demand=false)
Exercise assignment.
getExerciseId()
Get exercise id.
getId()
Get assignment id.
static getInstancesByExercise($a_exc_id)
static lookupTitle($a_id)
Lookup title.
Exercise peer review.
Exercise submission.
static deleteUser($a_exc_id, $a_user_id)
Delete all delivered files of user.
static getAllAssignmentFiles($a_exc_id, $a_ass_id)
downloadSingleFile($a_user_id, $filename, $filetitle)
processUploadedZipFile($fileTmp)
processes errorhandling etc for uploaded archive
addResourceObject($a_wsp_id, $a_text=null)
Add personal resource to assigment.
downloadMultipleFiles($a_filenames, $a_user_id, $a_multi_user=false)
isInTeam($a_user_id=null)
static lookupExerciseIdForReturnedId($a_returned_id)
Get exercise from submission id (used in ilObjMediaObject)
static hasAnySubmissions($a_ass_id)
static downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members)
Download all submitted files of an assignment (all user)
deleteSelectedFiles(array $file_id_array)
Deletes already delivered files.
lookupNewFiles($a_tutor=null)
Check how much files have been uploaded by the learner after the last download of the tutor.
__construct(ilExAssignment $a_ass, $a_user_id, ilExAssignmentTeam $a_team=null, $a_is_tutor=false, $a_public_submissions=false)
downloadFiles(array $a_file_ids=null, $a_only_new=false, $a_peer_review_mask_filename=false)
deleteResourceObject($a_returned_id)
Remove personal resource to assigment.
updateTextSubmission($a_text)
Handle text assignment submissions.
uploadFile($a_http_post_files, $unzip=false)
Save submitted file of user.
static findUserFiles($a_user_id, $a_filetitle)
Check if given file was assigned.
getDownloadedFilesInfoForTableGUIS($a_parent_obj, $a_parent_cmd=null)
getFiles(array $a_file_ids=null, $a_only_valid=false, $a_min_timestamp=null)
getLastSubmission()
Get the date of the last submission of a user for the assignment.
getLastDownloadTime(array $a_user_ids)
Exercise exceptions class.
Class to report exception.
static getValidFilename($a_filename)
Get valid filename.
static processZipFile($a_directory, $a_file, $structure, $ref_id=null, $containerType=null, $tree=null, $access_handler=null)
unzips in given directory and processes uploaded zip for use as single files
static utf8_encode($string)
utf8-encodes string if it is not a valid utf8-string.
static recursive_dirscan($dir, &$arr)
Recursively scans a given directory and writes path and filename into referenced array.
static _lookupName($a_user_id)
lookup user name
static _lookupTitle($a_id)
lookup object title
static delDir($a_dir, $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static getMySQLTimestamp($a_ts)
Get MySQL timestamp in 4.1.x or higher format (yyyy-mm-dd hh:mm:ss) This function converts a timestam...
static escapeShellArg($a_arg)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static now()
Return current timestamp in Y-m-d H:i:s format.
static ilTempnam($a_temp_path=null)
Create a temporary file in an ILIAS writable directory.
static deliverFile( $a_file, $a_filename, $a_mime='', $isInline=false, $removeAfterDelivery=false, $a_exit_after=true)
deliver file for download via browser.
static getASCIIFilename($a_filename)
convert utf8 to ascii filename
static unzip($a_file, $overwrite=false, $a_flat=false)
unzip file
static makeDir($a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
static dirsize($directory)
get size of a directory or a file.
$key
Definition: croninfo.php:18
$valid
if(!array_key_exists('StateId', $_REQUEST)) $id
global $ilCtrl
Definition: ilias.php:18
$query
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18