ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilExSubmission.php
Go to the documentation of this file.
1 <?php
2 
27 {
28  public const TYPE_FILE = "File";
29  public const TYPE_OBJECT = "Object"; // Blogs in WSP/Portfolio
30  public const TYPE_TEXT = "Text";
31  public const TYPE_REPO_OBJECT = "RepoObject"; // Wikis
32  protected \ILIAS\Exercise\InternalDomainService $domain;
33 
34  protected ilObjUser $user;
35  protected ilDBInterface $db;
36  protected ilLanguage $lng;
37  protected ilCtrl $ctrl;
39  protected int $user_id;
40  protected ?ilExAssignmentTeam $team = null;
41  protected ?ilExPeerReview $peer_review = null;
42  protected bool $is_tutor;
43  protected bool $public_submissions;
47  private \ilGlobalTemplateInterface $main_tpl;
48 
49  public function __construct(
50  ilExAssignment $a_ass,
51  int $a_user_id,
52  ilExAssignmentTeam $a_team = null,
53  bool $a_is_tutor = false,
54  bool $a_public_submissions = false
55  ) {
56  global $DIC;
57  $this->main_tpl = $DIC->ui()->mainTemplate();
58 
59  $this->user = $DIC->user();
60  $this->db = $DIC->database();
61  $this->lng = $DIC->language();
62  $this->ctrl = $DIC->ctrl();
63 
64  $this->assignment = $a_ass;
65  $this->ass_type = $this->assignment->getAssignmentType();
66  $this->ass_types = ilExAssignmentTypes::getInstance();
67 
68  $this->user_id = $a_user_id;
69  $this->is_tutor = $a_is_tutor;
70  $this->public_submissions = $a_public_submissions;
71 
72  $this->state = ilExcAssMemberState::getInstanceByIds($a_ass->getId(), $a_user_id);
73 
74  if ($a_ass->hasTeam()) {
75  if (!$a_team) {
76  $this->team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $this->user_id);
77  } else {
78  $this->team = $a_team;
79  }
80  }
81 
82  if ($this->assignment->getPeerReview()) {
83  $this->peer_review = new ilExPeerReview($this->assignment);
84  }
85  $this->domain = $DIC->exercise()->internal()->domain();
86  }
87 
88  public function getSubmissionType(): string
89  {
90  return $this->assignment->getAssignmentType()->getSubmissionType();
91  }
92 
93  public function getAssignment(): ilExAssignment
94  {
95  return $this->assignment;
96  }
97 
98  public function getTeam(): ?ilExAssignmentTeam
99  {
100  return $this->team;
101  }
102 
103  public function getPeerReview(): ?ilExPeerReview
104  {
105  return $this->peer_review;
106  }
107 
108  public function validatePeerReviews(): array
109  {
110  $res = array();
111  foreach ($this->getUserIds() as $user_id) {
112  $valid = true;
113 
114  // no peer review == valid
115  if ($this->peer_review) {
116  $valid = $this->peer_review->isFeedbackValidForPassed($user_id);
117  }
118 
119  $res[$user_id] = $valid;
120  }
121  return $res;
122  }
123 
124  public function getUserId(): int
125  {
126  return $this->user_id;
127  }
128 
129  public function getUserIds(): array
130  {
131  if ($this->team &&
132  !$this->hasNoTeamYet()) {
133  return $this->team->getMembers();
134  }
135 
136  // if has no team currently there still might be uploads attached
137  return array($this->user_id);
138  }
139 
143  public function getFeedbackId(): string
144  {
145  if ($this->team) {
146  return "t" . $this->team->getId();
147  } else {
148  return (string) $this->getUserId();
149  }
150  }
151 
152  public function hasSubmitted(): bool
153  {
154  return (bool) count($this->getFiles(null, true));
155  }
156 
157  public function hasSubmittedPrintVersion(): bool
158  {
159  return $this->getSubmittedPrintFile() !== "";
160  }
161 
162  public function getSubmittedPrintFile(): string
163  {
164  $submitted = $this->getFiles(
165  null,
166  false,
167  null,
168  true
169  );
170 
171  if (count($submitted) > 0) {
172  $submitted = array_pop($submitted);
173 
174  if (is_file($submitted['filename'])) {
175  return $submitted['filename'];
176  }
177  }
178 
179  return "";
180  }
181 
182  public function getSelectedObject(): ?array
183  {
184  $files = $this->getFiles();
185  if ($files !== []) {
186  return array_pop($files);
187  }
188  return null;
189  }
190 
191  public function canSubmit(): bool
192  {
193  return ($this->isOwner() &&
194  $this->state->isSubmissionAllowed());
195  }
196 
197  public function canView(): bool
198  {
199  $ilUser = $this->user;
200 
201  if ($this->canSubmit() ||
202  $this->isTutor() ||
203  $this->isInTeam() ||
204  $this->public_submissions) {
205  return true;
206  }
207 
208  // #16115
209  if ($this->peer_review) {
210  // peer review givers may view peer submissions
211  foreach ($this->peer_review->getPeerReviewsByPeerId($this->getUserId()) as $giver) {
212  if ($giver["giver_id"] == $ilUser->getId()) {
213  return true;
214  }
215  }
216  }
217 
218  return false;
219  }
220 
221  public function isTutor(): bool
222  {
223  return $this->is_tutor;
224  }
225 
226  public function hasNoTeamYet(): bool
227  {
228  if ($this->assignment->hasTeam() &&
229  !$this->team->getId()) {
230  return true;
231  }
232  return false;
233  }
234 
235  public function isInTeam(int $a_user_id = null): bool
236  {
237  $ilUser = $this->user;
238 
239  if (!$a_user_id) {
240  $a_user_id = $ilUser->getId();
241  }
242  return in_array($a_user_id, $this->getUserIds());
243  }
244 
245  public function isOwner(): bool
246  {
247  $ilUser = $this->user;
248 
249  return ($ilUser->getId() == $this->getUserId());
250  }
251 
252  public function hasPeerReviewAccess(): bool
253  {
254  return ($this->peer_review &&
255  $this->peer_review->hasPeerReviewAccess($this->user_id));
256  }
257 
258  public function canAddFile(): bool
259  {
260  if (!$this->canSubmit()) {
261  return false;
262  }
263 
264  $max = $this->getAssignment()->getMaxFile();
265  if ($max &&
266  $max <= sizeof($this->getFiles())) {
267  return false;
268  }
269 
270  return true;
271  }
272 
273 
274  //
275  // FILES
276  //
277 
278  protected function isLate(): bool
279  {
280  $dl = $this->state->getOfficialDeadline();
281  //$dl = $this->assignment->getPersonalDeadline($this->getUserId());
282  return ($dl && $dl < time());
283  }
284 
285  protected function initStorage(): ilFSStorageExercise
286  {
287  return new ilFSStorageExercise($this->assignment->getExerciseId(), $this->assignment->getId());
288  }
289 
290  protected function getStorageId(): int
291  {
292  if ($this->ass_type->isSubmissionAssignedToTeam()) {
293  $storage_id = $this->getTeam()->getId();
294  } else {
295  $storage_id = $this->getUserId();
296  }
297  return $storage_id;
298  }
299 
300 
306  public function uploadFile(
307  array $a_http_post_files,
308  bool $unzip = false
309  ): bool {
310  $ilDB = $this->db;
311  if (!$this->canAddFile()) {
312  return false;
313  }
314 
315  if ($this->ass_type->isSubmissionAssignedToTeam()) {
316  $team_id = $this->getTeam()->getId();
317  $user_id = 0;
318  if ($team_id == 0) {
319  return false;
320  }
321  } else {
322  $team_id = 0;
323  $user_id = $this->getUserId();
324  }
325  $storage_id = $this->getStorageId();
326 
327  $deliver_result = $this->initStorage()->uploadFile($a_http_post_files, $storage_id, $unzip);
328 
329  if ($deliver_result) {
330  $next_id = $ilDB->nextId("exc_returned");
331  $query = sprintf(
332  "INSERT INTO exc_returned " .
333  "(returned_id, obj_id, user_id, filename, filetitle, mimetype, ts, ass_id, late, team_id) " .
334  "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
335  $ilDB->quote($next_id, "integer"),
336  $ilDB->quote($this->assignment->getExerciseId(), "integer"),
337  $ilDB->quote($user_id, "integer"),
338  $ilDB->quote($deliver_result["fullname"], "text"),
339  $ilDB->quote(ilFileUtils::getValidFilename($a_http_post_files["name"]), "text"),
340  $ilDB->quote($deliver_result["mimetype"], "text"),
341  $ilDB->quote(ilUtil::now(), "timestamp"),
342  $ilDB->quote($this->assignment->getId(), "integer"),
343  $ilDB->quote($this->isLate(), "integer"),
344  $ilDB->quote($team_id, "integer")
345  );
346  $ilDB->manipulate($query);
347 
348  if ($this->team) {
349  $this->team->writeLog(
351  $a_http_post_files["name"]
352  );
353  }
354 
355  return true;
356  }
357  return false;
358  }
359 
360  public function addFileUpload(
361  \ILIAS\FileUpload\DTO\UploadResult $result
362  ): bool {
363  $ilDB = $this->db;
364 
365  if (!$this->canAddFile()) {
366  return false;
367  }
368  if ($this->ass_type->isSubmissionAssignedToTeam()) {
369  $team_id = $this->getTeam()->getId();
370  $user_id = 0;
371  if ($team_id == 0) {
372  return false;
373  }
374  } else {
375  $team_id = 0;
376  $user_id = $this->getUserId();
377  }
378  $storage_id = $this->getStorageId();
379 
380  $deliver_result = $this->initStorage()->addFileUpload($result, $storage_id);
381 
382  if ($deliver_result) {
383  $next_id = $ilDB->nextId("exc_returned");
384  $query = sprintf(
385  "INSERT INTO exc_returned " .
386  "(returned_id, obj_id, user_id, filename, filetitle, mimetype, ts, ass_id, late, team_id) " .
387  "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
388  $ilDB->quote($next_id, "integer"),
389  $ilDB->quote($this->assignment->getExerciseId(), "integer"),
390  $ilDB->quote($user_id, "integer"),
391  $ilDB->quote($deliver_result["fullname"], "text"),
392  $ilDB->quote(ilFileUtils::getValidFilename($result->getName()), "text"),
393  $ilDB->quote($deliver_result["mimetype"], "text"),
394  $ilDB->quote(ilUtil::now(), "timestamp"),
395  $ilDB->quote($this->assignment->getId(), "integer"),
396  $ilDB->quote($this->isLate(), "integer"),
397  $ilDB->quote($team_id, "integer")
398  );
399  $ilDB->manipulate($query);
400 
401  if ($this->team) {
402  $this->team->writeLog(
404  $result->getName()
405  );
406  }
407 
408  return true;
409  }
410  return false;
411  }
412 
417  public function processUploadedZipFile(
418  string $fileTmp
419  ): bool {
420  $lng = $this->lng;
421 
422  // Create unzip-directory
423  $newDir = ilFileUtils::ilTempnam();
424  ilFileUtils::makeDir($newDir);
425 
426  $success = true;
427 
428  //try {
429  $filearray = [];
430  self::processZipFile($newDir, $fileTmp, false);
431  ilFileUtils::recursive_dirscan($newDir, $filearray);
432 
433  // #18441 - check number of files in zip
434  $max_num = $this->assignment->getMaxFile();
435  if ($max_num) {
436  $current_num = count($this->getFiles());
437  $zip_num = count($filearray["file"]);
438  if ($current_num + $zip_num > $max_num) {
439  $success = false;
440  $this->main_tpl->setOnScreenMessage('failure', $lng->txt("exc_upload_error") . " [Zip1]", true);
441  }
442  }
443 
444  if ($success) {
445  foreach ($filearray["file"] as $key => $filename) {
446  $a_http_post_files["name"] = ilFileUtils::utf8_encode($filename);
447  $a_http_post_files["type"] = "other";
448  $a_http_post_files["tmp_name"] = $filearray["path"][$key] . "/" . $filename;
449  $a_http_post_files["error"] = 0;
450  $a_http_post_files["size"] = filesize($filearray["path"][$key] . "/" . $filename);
451 
452  if (!$this->uploadFile($a_http_post_files, true)) {
453  $success = false;
454  $this->main_tpl->setOnScreenMessage('failure', $lng->txt("exc_upload_error") . " [Zip2]", true);
455  }
456  }
457  }
458  /*} catch (ilException $e) {
459  $success = false;
460  $this->main_tpl->setOnScreenMessage('failure', $e->getMessage());
461  }*/
462 
463  ilFileUtils::delDir($newDir);
464  return $success;
465  }
466 
470  public static function getAllAssignmentFiles(
471  int $a_exc_id,
472  int $a_ass_id
473  ): array {
474  global $DIC;
475 
476  $ilDB = $DIC->database();
477 
478  $storage = new ilFSStorageExercise($a_exc_id, $a_ass_id);
479  $path = $storage->getAbsoluteSubmissionPath();
480 
481  $ass_type = ilExAssignmentTypes::getInstance()->getById(ilExAssignment::lookupType($a_ass_id));
482 
483  $query = "SELECT * FROM exc_returned WHERE ass_id = " .
484  $ilDB->quote($a_ass_id, "integer");
485 
486  $res = $ilDB->query($query);
487  $delivered = [];
488  while ($row = $ilDB->fetchAssoc($res)) {
489  if ($ass_type->isSubmissionAssignedToTeam()) {
490  $storage_id = $row["team_id"];
491  } else {
492  $storage_id = $row["user_id"];
493  }
494 
495  $row["timestamp"] = $row["ts"];
496  $row["filename"] = $path . "/" . $storage_id . "/" . basename($row["filename"] ?? "");
497  $delivered[] = $row;
498  }
499 
500  return $delivered;
501  }
502 
506  public static function getAssignmentFilesByUsers(
507  int $a_exc_id,
508  int $a_ass_id,
509  array $a_users
510  ): array {
511  global $DIC;
512 
513  $ilDB = $DIC->database();
514 
515  $storage = new ilFSStorageExercise($a_exc_id, $a_ass_id);
516  $path = $storage->getAbsoluteSubmissionPath();
517 
518  $ass_type = ilExAssignmentTypes::getInstance()->getById(ilExAssignment::lookupType($a_ass_id));
519 
520  $query = "SELECT * FROM exc_returned WHERE ass_id = " .
521  $ilDB->quote($a_ass_id, "integer") .
522  " AND user_id IN (" . implode(',', $a_users) . ")";
523 
524  $res = $ilDB->query($query);
525  $delivered = [];
526  while ($row = $ilDB->fetchAssoc($res)) {
527  if ($ass_type->isSubmissionAssignedToTeam()) {
528  $storage_id = $row["team_id"];
529  } else {
530  $storage_id = $row["user_id"];
531  }
532 
533  $row["timestamp"] = $row["ts"];
534  $row["filename"] = $path . "/" . $storage_id . "/" . basename($row["filename"] ?? "");
535  $delivered[] = $row;
536  }
537 
538  return $delivered;
539  }
540 
545  public function getFiles(
546  array $a_file_ids = null,
547  bool $a_only_valid = false,
548  string $a_min_timestamp = null,
549  bool $print_versions = false
550  ): array {
551  $ilDB = $this->db;
552 
553  $sql = "SELECT * FROM exc_returned" .
554  " WHERE ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer");
555 
556  $sql .= " AND " . $this->getTableUserWhere(true);
557 
558 
559  if ($a_file_ids) {
560  $sql .= " AND " . $ilDB->in("returned_id", $a_file_ids, false, "integer");
561  }
562 
563  if ($a_min_timestamp) {
564  $sql .= " AND ts > " . $ilDB->quote($a_min_timestamp, "timestamp");
565  }
566 
567  $result = $ilDB->query($sql);
568 
569  $delivered_files = array();
570  if ($ilDB->numRows($result)) {
571  $path = $this->initStorage()->getAbsoluteSubmissionPath();
572 
573  while ($row = $ilDB->fetchAssoc($result)) {
574  // blog/portfolio/text submissions
575  if ($a_only_valid &&
576  !$row["filename"] &&
577  !(trim((string) $row["atext"]))) {
578  continue;
579  }
580 
581  $row["owner_id"] = $row["user_id"];
582  $row["timestamp"] = $row["ts"];
583  $row["timestamp14"] = substr($row["ts"], 0, 4) .
584  substr($row["ts"], 5, 2) . substr($row["ts"], 8, 2) .
585  substr($row["ts"], 11, 2) . substr($row["ts"], 14, 2) .
586  substr($row["ts"], 17, 2);
587 
588  if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) {
589  $storage_id = $row["team_id"];
590  } else {
591  $storage_id = $row["user_id"];
592  }
593 
594 
595  $row["filename"] = $path .
596  "/" . $storage_id . "/" . (($row["filename"]) ? basename($row["filename"]) : '');
597 
598  // see 22301, 22719
599  if (is_file($row["filename"]) || (!$this->assignment->getAssignmentType()->usesFileUpload())) {
600  $delivered_files[] = $row;
601  }
602  }
603  }
604 
605  // filter print versions
606  if (in_array($this->assignment->getType(), [
610  ])) {
611  $delivered_files = array_filter($delivered_files, function ($i) use ($print_versions) {
612  $is_print_version = false;
613  if (substr($i["filetitle"], strlen($i["filetitle"]) - 5) == "print") {
614  $is_print_version = true;
615  }
616  if (substr($i["filetitle"], strlen($i["filetitle"]) - 9) == "print.zip") {
617  $is_print_version = true;
618  }
619  return ($is_print_version == $print_versions);
620  });
621  }
622 
623  return $delivered_files;
624  }
625 
630  public function lookupNewFiles(
631  int $a_tutor = null
632  ): array {
633  $ilDB = $this->db;
634  $ilUser = $this->user;
635 
636  $tutor = ($a_tutor)
637  ?: $ilUser->getId();
638 
639  $where = " AND " . $this->getTableUserWhere(true);
640 
641  $q = "SELECT exc_returned.returned_id AS id " .
642  "FROM exc_usr_tutor, exc_returned " .
643  "WHERE exc_returned.ass_id = exc_usr_tutor.ass_id " .
644  " AND exc_returned.user_id = exc_usr_tutor.usr_id " .
645  " AND exc_returned.ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") .
646  $where .
647  " AND exc_usr_tutor.tutor_id = " . $ilDB->quote($tutor, "integer") .
648  " AND exc_usr_tutor.download_time < exc_returned.ts ";
649 
650  $new_up_set = $ilDB->query($q);
651 
652  $new_up = array();
653  while ($new_up_rec = $ilDB->fetchAssoc($new_up_set)) {
654  $new_up[] = $new_up_rec["id"];
655  }
656 
657  return $new_up;
658  }
659 
663  public static function lookupExerciseIdForReturnedId(
664  int $a_returned_id
665  ): int {
666  global $DIC;
667 
668  $ilDB = $DIC->database();
669 
670  $set = $ilDB->query("SELECT obj_id" .
671  " FROM exc_returned" .
672  " WHERE returned_id = " . $ilDB->quote($a_returned_id, "integer"));
673  $row = $ilDB->fetchAssoc($set);
674  return (int) $row["obj_id"];
675  }
676 
681  public static function findUserFiles(
682  int $a_user_id,
683  string $a_filetitle
684  ): array {
685  global $DIC;
686 
687  $ilDB = $DIC->database();
688 
689  $set = $ilDB->query("SELECT obj_id, ass_id" .
690  " FROM exc_returned" .
691  " WHERE user_id = " . $ilDB->quote($a_user_id, "integer") .
692  " AND filetitle = " . $ilDB->quote($a_filetitle, "text"));
693  $res = array();
694  while ($row = $ilDB->fetchAssoc($set)) {
695  $res[$row["ass_id"]] = $row;
696  }
697  return $res;
698  }
699 
700  public function deleteAllFiles(): void
701  {
702  $files = array();
703  // normal files
704  foreach ($this->getFiles() as $item) {
705  $files[] = $item["returned_id"];
706  }
707  // print versions
708  foreach ($this->getFiles(null, false, null, true) as $item) {
709  $files[] = $item["returned_id"];
710  }
711  if ($files !== []) {
712  $this->deleteSelectedFiles($files);
713  }
714  }
715 
720  public function deleteSelectedFiles(
721  array $file_id_array
722  ): void {
723  $ilDB = $this->db;
724 
725 
726  $where = " AND " . $this->getTableUserWhere(true);
727 
728 
729  if ($file_id_array === []) {
730  return;
731  }
732 
733  if ($file_id_array !== []) {
734  $result = $ilDB->query("SELECT * FROM exc_returned" .
735  " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") .
736  $where);
737 
738  if ($ilDB->numRows($result)) {
739  $result_array = array();
740  while ($row = $ilDB->fetchAssoc($result)) {
741  $row["timestamp"] = $row["ts"];
742  $result_array[] = $row;
743  }
744 
745  // delete the entries in the database
746  $ilDB->manipulate("DELETE FROM exc_returned" .
747  " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") .
748  $where);
749 
750  // delete the files
751  $path = $this->initStorage()->getAbsoluteSubmissionPath();
752  foreach ($result_array as $value) {
753  if ($value["filename"]) {
754  if ($this->team) {
755  $this->team->writeLog(
757  $value["filetitle"]
758  );
759  }
760 
761  if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) {
762  $storage_id = $value["team_id"];
763  } else {
764  $storage_id = $value["user_id"];
765  }
766 
767  $filename = $path . "/" . $storage_id . "/" . basename($value["filename"]);
768  if (file_exists($filename)) {
769  unlink($filename);
770  }
771  }
772  }
773  }
774  }
775  }
776 
781  public static function deleteUser(
782  int $a_exc_id,
783  int $a_user_id
784  ): void {
785  global $DIC;
786 
787  $db = $DIC->database();
788 
789  foreach (ilExAssignment::getInstancesByExercise($a_exc_id) as $ass) {
790  $submission = new self($ass, $a_user_id);
791  $submission->deleteAllFiles();
792 
793  // remove from any team
794  $team = $submission->getTeam();
795  if ($team) {
796  $team->removeTeamMember($a_user_id);
797  }
798 
799  // #14900
800  $member_status = $ass->getMemberStatus($a_user_id);
801  $member_status->setStatus("notgraded");
802  $member_status->update();
803 
804  $db->manipulateF(
805  "DELETE FROM exc_usr_tutor " .
806  "WHERE ass_id = %s AND usr_id = %s",
807  array("integer", "integer"),
808  array($ass->getId(), $a_user_id)
809  );
810  }
811  }
812 
817  protected function getLastDownloadTime(
818  array $a_user_ids
819  ): string {
820  $ilDB = $this->db;
821  $ilUser = $this->user;
822 
823  $q = "SELECT download_time FROM exc_usr_tutor WHERE " .
824  " ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") . " AND " .
825  $ilDB->in("usr_id", $a_user_ids, "", "integer") . " AND " .
826  " tutor_id = " . $ilDB->quote($ilUser->getId(), "integer") .
827  " ORDER BY download_time DESC";
828  $lu_set = $ilDB->query($q);
829  $lu_rec = $ilDB->fetchAssoc($lu_set);
830  return $lu_rec["download_time"] ?? "";
831  }
832 
833  public function downloadFiles(
834  array $a_file_ids = null,
835  bool $a_only_new = false,
836  bool $a_peer_review_mask_filename = false
837  ): bool {
838  $ilUser = $this->user;
839  $lng = $this->lng;
840 
841  $user_ids = $this->getUserIds();
842  $is_team = $this->assignment->hasTeam();
843  // get last download time
844  $download_time = null;
845  if ($a_only_new) {
846  $download_time = $this->getLastDownloadTime($user_ids);
847  }
848 
849  if ($this->is_tutor) {
850  $this->updateTutorDownloadTime();
851  }
852 
853  if ($a_peer_review_mask_filename) {
854  // process peer review sequence id
855  $peer_id = null;
856  foreach ($this->peer_review->getPeerReviewsByGiver($ilUser->getId()) as $idx => $item) {
857  if ($item["peer_id"] == $this->getUserId()) {
858  $peer_id = $idx + 1;
859  break;
860  }
861  }
862  // this will remove personal info from zip-filename
863  $is_team = true;
864  }
865 
866  $files = $this->getFiles($a_file_ids, false, $download_time);
867 
868  if ($files !== []) {
869  if (count($files) == 1) {
870  $file = array_pop($files);
871 
872  switch ($this->assignment->getType()) {
875  $file["filetitle"] = ilObjUser::_lookupName($file["user_id"]);
876  $file["filetitle"] = ilObject::_lookupTitle($this->assignment->getExerciseId()) . " - " .
877  $this->assignment->getTitle() . " - " .
878  $file["filetitle"]["firstname"] . " " .
879  $file["filetitle"]["lastname"] . " (" .
880  $file["filetitle"]["login"] . ").zip";
881  break;
882 
883  // @todo: generalize
885  $file["filetitle"] = ilObject::_lookupTitle($this->assignment->getExerciseId()) . " - " .
886  $this->assignment->getTitle() . " (Team " . $this->getTeam()->getId() . ").zip";
887  break;
888 
889  default:
890  break;
891  }
892 
893  if ($a_peer_review_mask_filename) {
894  $title_a = explode(".", $file["filetitle"]);
895  $suffix = array_pop($title_a);
896  $file["filetitle"] = $this->assignment->getTitle() . "_peer" . $peer_id . "." . $suffix;
897  } elseif ($file["late"]) {
898  $file["filetitle"] = $lng->txt("exc_late_submission") . " - " .
899  $file["filetitle"];
900  }
901 
902  $this->downloadSingleFile($file["user_id"], $file["filename"], $file["filetitle"], $file["team_id"]);
903  } else {
904  $array_files = array();
905  foreach ($files as $seq => $file) {
906  if ($this->assignment->getAssignmentType()->isSubmissionAssignedToTeam()) {
907  $storage_id = $file["team_id"];
908  } else {
909  $storage_id = $file["user_id"];
910  }
911 
912  $src = basename($file["filename"]);
913  if ($a_peer_review_mask_filename) {
914  $src_a = explode(".", $src);
915  $suffix = array_pop($src_a);
916  $tgt = $this->assignment->getTitle() . "_peer" . $peer_id .
917  "_" . (++$seq) . "." . $suffix;
918 
919  $array_files[$storage_id][] = array(
920  "src" => $src,
921  "tgt" => $tgt
922  );
923  } else {
924  $array_files[$storage_id][] = array(
925  "src" => $src,
926  "late" => $file["late"]
927  );
928  }
929  }
930  $this->downloadMultipleFiles(
931  $array_files,
932  ($is_team ? null : $this->getUserId()),
933  $is_team
934  );
935  }
936  } else {
937  return false;
938  }
939 
940  return true;
941  }
942 
943  // Update the timestamp of the last download of current user (=tutor)
944  public function updateTutorDownloadTime(): void
945  {
946  $ilUser = $this->user;
947  $ilDB = $this->db;
948 
949  $exc_id = $this->assignment->getExerciseId();
950  $ass_id = $this->assignment->getId();
951 
952  foreach ($this->getUserIds() as $user_id) {
953  $ilDB->manipulateF(
954  "DELETE FROM exc_usr_tutor " .
955  "WHERE ass_id = %s AND usr_id = %s AND tutor_id = %s",
956  array("integer", "integer", "integer"),
957  array($ass_id, $user_id, $ilUser->getId())
958  );
959 
960  $ilDB->manipulateF(
961  "INSERT INTO exc_usr_tutor (ass_id, obj_id, usr_id, tutor_id, download_time) VALUES " .
962  "(%s, %s, %s, %s, %s)",
963  array("integer", "integer", "integer", "integer", "timestamp"),
964  array($ass_id, $exc_id, $user_id, $ilUser->getId(), ilUtil::now())
965  );
966  }
967  }
968 
969  protected function downloadSingleFile(
970  int $a_user_id,
971  string $filename,
972  string $filetitle,
973  int $a_team_id = 0
974  ): void {
975  if ($this->ass_type->isSubmissionAssignedToTeam()) {
976  $storage_id = $a_team_id;
977  } else {
978  $storage_id = $a_user_id;
979  }
980 
981  $filename = $this->initStorage()->getAbsoluteSubmissionPath() .
982  "/" . $storage_id . "/" . basename($filename);
983 
984  ilFileDelivery::deliverFileLegacy($filename, $filetitle);
985  }
986 
987  protected function downloadMultipleFiles(
988  array $a_filenames,
989  ?int $a_user_id,
990  bool $a_multi_user = false
991  ): void {
992  $lng = $this->lng;
993  $a_user_id = (int) $a_user_id;
994 
995  $path = $this->initStorage()->getAbsoluteSubmissionPath();
996 
997  $cdir = getcwd();
998 
999  $zip = PATH_TO_ZIP;
1000  $tmpdir = ilFileUtils::ilTempnam();
1001  $tmpfile = ilFileUtils::ilTempnam();
1002  $tmpzipfile = $tmpfile . ".zip";
1003 
1004  ilFileUtils::makeDir($tmpdir);
1005  chdir($tmpdir);
1006 
1007  $assTitle = ilExAssignment::lookupTitle($this->assignment->getId());
1008  $deliverFilename = str_replace(" ", "_", $assTitle);
1009  if ($a_user_id > 0 && !$a_multi_user) {
1010  $userName = ilObjUser::_lookupName($a_user_id);
1011  $deliverFilename .= "_" . $userName["lastname"] . "_" . $userName["firstname"];
1012  } else {
1013  $deliverFilename .= "_files";
1014  }
1015  $orgDeliverFilename = trim($deliverFilename);
1016  $deliverFilename = ilFileUtils::getASCIIFilename($orgDeliverFilename);
1017  ilFileUtils::makeDir($tmpdir . "/" . $deliverFilename);
1018  chdir($tmpdir . "/" . $deliverFilename);
1019 
1020  //copy all files to a temporary directory and remove them afterwards
1021  $parsed_files = $duplicates = array();
1022  foreach ($a_filenames as $storage_id => $files) {
1023  $pathname = $path . "/" . $storage_id;
1024 
1025  foreach ($files as $filename) {
1026  // peer review masked filenames, see deliverReturnedFiles()
1027  if (isset($filename["tgt"])) {
1028  $newFilename = $filename["tgt"];
1029  $filename = $filename["src"];
1030  } else {
1031  $late = $filename["late"];
1032  $filename = $filename["src"];
1033 
1034  // remove timestamp
1035  $newFilename = trim($filename);
1036  $pos = strpos($newFilename, "_");
1037  if ($pos !== false) {
1038  $newFilename = substr($newFilename, $pos + 1);
1039  }
1040  // #11070
1041  $chkName = strtolower($newFilename);
1042  if (array_key_exists($chkName, $duplicates)) {
1043  $suffix = strrpos($newFilename, ".");
1044  $newFilename = substr($newFilename, 0, $suffix) .
1045  " (" . (++$duplicates[$chkName]) . ")" .
1046  substr($newFilename, $suffix);
1047  } else {
1048  $duplicates[$chkName] = 1;
1049  }
1050 
1051  if ($late) {
1052  $newFilename = $lng->txt("exc_late_submission") . " - " .
1053  $newFilename;
1054  }
1055  }
1056 
1057  $newFilename = ilFileUtils::getASCIIFilename($newFilename);
1058  $newFilename = $tmpdir . DIRECTORY_SEPARATOR . $deliverFilename . DIRECTORY_SEPARATOR . $newFilename;
1059  // copy to temporal directory
1060  $oldFilename = $pathname . DIRECTORY_SEPARATOR . $filename;
1061  if (!copy($oldFilename, $newFilename)) {
1062  echo 'Could not copy ' . $oldFilename . ' to ' . $newFilename;
1063  }
1064  touch($newFilename, filectime($oldFilename));
1065  $parsed_files[] = ilShellUtil::escapeShellArg(
1066  $deliverFilename . DIRECTORY_SEPARATOR . basename($newFilename)
1067  );
1068  }
1069  }
1070 
1071  chdir($tmpdir);
1072  $zipcmd = $zip . " " . ilShellUtil::escapeShellArg($tmpzipfile) . " " . implode(" ", $parsed_files);
1073 
1074  exec($zipcmd);
1075  ilFileUtils::delDir($tmpdir);
1076 
1077  chdir($cdir);
1078  ilFileDelivery::deliverFileLegacy($tmpzipfile, $orgDeliverFilename . ".zip", "", false, true);
1079  exit;
1080  }
1081 
1086  public static function downloadAllAssignmentFiles(
1087  ilExAssignment $a_ass,
1088  array $members,
1089  string $to_path
1090  ): void {
1091  global $DIC;
1092 
1093  $lng = $DIC->language();
1095  $domain = $DIC->exercise()->internal()->domain();
1096 
1097  $storage = new ilFSStorageExercise($a_ass->getExerciseId(), $a_ass->getId());
1098  $storage->create();
1099 
1100  ksort($members);
1101  //$savepath = $this->getExercisePath() . "/" . $this->obj_id . "/";
1102  $savepath = $storage->getAbsoluteSubmissionPath();
1103  $cdir = getcwd();
1104 
1105 
1106  // important check: if the directory does not exist
1107  // ILIAS stays in the current directory (echoing only a warning)
1108  // and the zip command below archives the whole ILIAS directory
1109  // (including the data directory) and sends a mega file to the user :-o
1110  if (!is_dir($savepath)) {
1111  return;
1112  }
1113  // Safe mode fix
1114  // chdir($this->getExercisePath());
1115 
1116  $tmpdir = $storage->getTempPath();
1117  chdir($tmpdir);
1118  $zip = PATH_TO_ZIP;
1119 
1120  // check free diskspace
1121  $dirsize = 0;
1122  foreach (array_keys($members) as $id) {
1123  $directory = $savepath . DIRECTORY_SEPARATOR . $id;
1124  $dirsize += ilFileUtils::dirsize($directory);
1125  }
1126  if ($dirsize > disk_free_space($tmpdir)) {
1127  return;
1128  }
1129 
1130  $ass_type = $a_ass->getType();
1131 
1132  // copy all member directories to the temporary folder
1133  // switch from id to member name and append the login if the member name is double
1134  // ensure that no illegal filenames will be created
1135  // remove timestamp from filename
1136  $team_map = null;
1137  $team_dirs = null;
1138  if ($a_ass->hasTeam()) {
1139  $team_dirs = array();
1140  $team_map = ilExAssignmentTeam::getAssignmentTeamMap($a_ass->getId());
1141  }
1142  foreach ($members as $id => $item) {
1143  $user_files = $item["files"] ?? null;
1144  $sourcedir = $savepath . DIRECTORY_SEPARATOR . $id;
1145  if (!is_dir($sourcedir)) {
1146  continue;
1147  }
1148 
1149  // group by teams
1150  $team_dir = "";
1151  if (is_array($team_map) &&
1152  array_key_exists($id, $team_map)) {
1153  $team_id = $team_map[$id];
1154  if (!array_key_exists($team_id, $team_dirs)) {
1155  $team_dir = $lng->txt("exc_team") . " " . $team_id;
1156  ilFileUtils::makeDir($team_dir);
1157  $team_dirs[$team_id] = $team_dir;
1158  }
1159  $team_dir = $team_dirs[$team_id] . DIRECTORY_SEPARATOR;
1160  }
1161 
1162  if ($a_ass->getAssignmentType()->isSubmissionAssignedToTeam()) {
1163  $targetdir = $team_dir . ilFileUtils::getASCIIFilename(
1164  $item["name"]
1165  );
1166  if ($targetdir == "") {
1167  continue;
1168  }
1169  } else {
1170  $targetdir = self::getDirectoryNameFromUserData($id);
1171  if ($a_ass->getAssignmentType()->usesTeams()) {
1172  $targetdir = $team_dir . $targetdir;
1173  }
1174  }
1175 
1176  $log->debug("Creation target directory: " . $targetdir);
1177  ilFileUtils::makeDir($targetdir);
1178 
1179  $log->debug("Scanning source directory: " . $sourcedir);
1180  $sourcefiles = scandir($sourcedir);
1181  $duplicates = array();
1182  foreach ($sourcefiles as $sourcefile) {
1183  if ($sourcefile == "." || $sourcefile == "..") {
1184  continue;
1185  }
1186 
1187  $targetfile = trim(basename($sourcefile));
1188  $pos = strpos($targetfile, "_");
1189  if ($pos !== false) {
1190  $targetfile = substr($targetfile, $pos + 1);
1191  }
1192 
1193  if ($a_ass->getAssignmentType()->getSubmissionType() == self::TYPE_REPO_OBJECT) {
1194  $obj_id = ilObject::_lookupObjId($targetfile);
1195  $obj_type = ilObject::_lookupType($obj_id);
1196  $targetfile = $obj_type . "_" . $obj_id . ".zip";
1197  }
1198 
1199 
1200  // #14536
1201  if (array_key_exists($targetfile, $duplicates)) {
1202  $suffix = strrpos($targetfile, ".");
1203  $targetfile = substr($targetfile, 0, $suffix) .
1204  " (" . (++$duplicates[$targetfile]) . ")" .
1205  substr($targetfile, $suffix);
1206  } else {
1207  $duplicates[$targetfile] = 1;
1208  }
1209 
1210  // late submission?
1211  if (isset($user_files)) { // see #23900
1212  foreach ($user_files as $file) {
1213  if (basename($file["filename"]) == $sourcefile) {
1214  if ($file["late"]) {
1215  $targetfile = $lng->txt("exc_late_submission") . " - " .
1216  $targetfile;
1217  }
1218  break;
1219  }
1220  }
1221  }
1222 
1223  $targetfile = ilFileUtils::getASCIIFilename($targetfile);
1224  $targetfile = $targetdir . DIRECTORY_SEPARATOR . $targetfile;
1225  $sourcefile = $sourcedir . DIRECTORY_SEPARATOR . $sourcefile;
1226 
1227  $log->debug("Copying: " . $sourcefile . " -> " . $targetfile);
1228 
1229  if (!copy($sourcefile, $targetfile)) {
1230  throw new ilExerciseException("Could not copy " . basename($sourcefile) . " to '" . $targetfile . "'.");
1231  } else {
1232  // preserve time stamp
1233  touch($targetfile, filectime($sourcefile));
1234 
1235  // blogs and portfolios are stored as zip and have to be unzipped
1236  if ($ass_type == ilExAssignment::TYPE_PORTFOLIO ||
1237  $ass_type == ilExAssignment::TYPE_BLOG) {
1238  $log->debug("Unzipping: " . $targetfile);
1239  $log->debug("Current directory is: " . getcwd());
1240 
1241  $domain->resources()->zip()->unzipFile($targetfile);
1242  unlink($targetfile);
1243  }
1244  }
1245  }
1246  }
1247  $tmpzipfile = ilFileUtils::getASCIIFilename($lng->txt("exc_ass_submission_zip")) . ".zip";
1248  // Safe mode fix
1249  $zipcmd = $zip . " -r " . ilShellUtil::escapeShellArg($tmpzipfile) . " .";
1250  exec($zipcmd);
1251  //$path_final_zip_file = $to_path.DIRECTORY_SEPARATOR."Submissions/".$tmpzipfile;
1252  $path_final_zip_file = $to_path . DIRECTORY_SEPARATOR . $tmpzipfile;
1253 
1254  if (file_exists($tmpdir . DIRECTORY_SEPARATOR . $tmpzipfile)) {
1255  copy($tmpzipfile, $path_final_zip_file);
1256  ilFileUtils::delDir($tmpdir);
1257 
1258  //unzip the submissions zip file.(decided to unzip to allow the excel link the files more obvious when blog/portfolio)
1259  chdir($to_path);
1260  $domain->resources()->zip()->unzipFile($path_final_zip_file);
1261  unlink($path_final_zip_file);
1262  }
1263 
1264  chdir($cdir);
1265  }
1266 
1267 
1268  // Get user/team where clause
1269  public function getTableUserWhere(
1270  bool $a_team_mode = false
1271  ): string {
1272  $ilDB = $this->db;
1273 
1274  if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) {
1275  $team_id = $this->getTeam()->getId();
1276  $where = " team_id = " . $ilDB->quote($team_id, "integer") . " ";
1277  } else {
1278  if ($a_team_mode) {
1279  $where = " " . $ilDB->in("user_id", $this->getUserIds(), "", "integer") . " ";
1280  } else {
1281  $where = " user_id = " . $ilDB->quote($this->getUserId(), "integer");
1282  }
1283  }
1284  return $where;
1285  }
1286 
1287 
1292  public function getLastSubmission(): ?string
1293  {
1294  $ilDB = $this->db;
1295 
1296  $ilDB->setLimit(1, 0);
1297 
1298  $q = "SELECT obj_id,user_id,ts FROM exc_returned" .
1299  " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer") .
1300  " AND " . $this->getTableUserWhere(true) .
1301  " AND (filename IS NOT NULL OR atext IS NOT NULL)" .
1302  " AND ts IS NOT NULL" .
1303  " ORDER BY ts DESC";
1304  $usr_set = $ilDB->query($q);
1305  $array = $ilDB->fetchAssoc($usr_set);
1306  return ($array["ts"] ?? null);
1307  }
1308 
1313  public function getLastOpeningHTMLView(): ?string
1314  {
1315  $this->db->setLimit(1, 0);
1316 
1317  $q = "SELECT web_dir_access_time FROM exc_returned" .
1318  " WHERE ass_id = " . $this->db->quote($this->assignment->getId(), "integer") .
1319  " AND (filename IS NOT NULL OR atext IS NOT NULL)" .
1320  " AND web_dir_access_time IS NOT NULL" .
1321  " AND " . $this->getTableUserWhere(true) .
1322  " ORDER BY web_dir_access_time DESC";
1323 
1324  $res = $this->db->query($q);
1325 
1326  $data = $this->db->fetchAssoc($res);
1327 
1328  return $data["web_dir_access_time"] ?? null;
1329  }
1330 
1331 
1332  //
1333  // OBJECTS
1334  //
1335 
1341  public function addResourceObject(
1342  string $a_wsp_id, // note: text assignments currently call this with "TEXT"
1343  string $a_text = null
1344  ): int {
1345  $ilDB = $this->db;
1346 
1347  if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) {
1348  $user_id = 0;
1349  $team_id = $this->getTeam()->getId();
1350  } else {
1351  $user_id = $this->getUserId();
1352  $team_id = 0;
1353  }
1354 
1355  // repository objects must be unique in submissions
1356  // the same repo object cannot be used in different submissions or even different assignment/exercises
1357  // why? -> the access handling would fail, since the access depends e.g. on teams or even phase of the
1358  // assignment
1359  if ($this->getAssignment()->getAssignmentType()->getSubmissionType() == ilExSubmission::TYPE_REPO_OBJECT) {
1360  $repos_ass_type_ids = $this->ass_types->getIdsForSubmissionType(ilExSubmission::TYPE_REPO_OBJECT);
1361  $subs = $this->getSubmissionsForFilename($a_wsp_id, $repos_ass_type_ids);
1362  if ($subs !== []) {
1363  throw new ilExerciseException("Repository object $a_wsp_id is already assigned to another assignment.");
1364  }
1365  }
1366 
1367  $next_id = $ilDB->nextId("exc_returned");
1368  $query = sprintf(
1369  "INSERT INTO exc_returned " .
1370  "(returned_id, obj_id, user_id, filetitle, ass_id, ts, atext, late, team_id) " .
1371  "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
1372  $ilDB->quote($next_id, "integer"),
1373  $ilDB->quote($this->assignment->getExerciseId(), "integer"),
1374  $ilDB->quote($user_id, "integer"),
1375  $ilDB->quote($a_wsp_id, "text"),
1376  $ilDB->quote($this->assignment->getId(), "integer"),
1377  $ilDB->quote(ilUtil::now(), "timestamp"),
1378  $ilDB->quote($a_text, "text"),
1379  $ilDB->quote($this->isLate(), "integer"),
1380  $ilDB->quote($team_id, "integer")
1381  );
1382  $ilDB->manipulate($query);
1383 
1384  return $next_id;
1385  }
1386 
1387  /*
1388  * Remove ressource from assignement (and delete
1389  * its submission): Note: The object itself will not be deleted.
1390  */
1391  public function deleteResourceObject(): void
1392  {
1393  $this->deleteAllFiles();
1394  }
1395 
1401  public function updateTextSubmission(string $a_text): ?int
1402  {
1403  $ilDB = $this->db;
1404 
1405  $files = $this->getFiles();
1406 
1407  // no text = remove submission
1408  if (!trim($a_text)) {
1409  $this->deleteAllFiles();
1410  return null;
1411  }
1412 
1413  if (!$files) {
1414  return $this->addResourceObject("TEXT", $a_text);
1415  } else {
1416  $files = array_shift($files);
1417  $id = $files["returned_id"];
1418  if ($id) {
1419  $ilDB->manipulate("UPDATE exc_returned" .
1420  " SET atext = " . $ilDB->quote($a_text, "text") .
1421  ", ts = " . $ilDB->quote(ilUtil::now(), "timestamp") .
1422  ", late = " . $ilDB->quote($this->isLate(), "integer") .
1423  " WHERE returned_id = " . $ilDB->quote($id, "integer"));
1424  return $id;
1425  }
1426  }
1427  return null;
1428  }
1429 
1430  //
1431  // GUI helper
1432  //
1433 
1437  public function getDownloadedFilesInfoForTableGUIS(): array
1438  {
1439  $lng = $this->lng;
1440  $ilCtrl = $this->ctrl;
1441 
1442  $result = array();
1443  $result["files"]["count"] = "---";
1444 
1445  // submission:
1446  // see if files have been resubmmited after solved
1447  $last_sub = $this->getLastSubmission();
1448  if ($last_sub) {
1449  $last_sub = ilDatePresentation::formatDate(new ilDateTime($last_sub, IL_CAL_DATETIME));
1450  } else {
1451  $last_sub = "---";
1452  }
1453  $result["last_submission"]["txt"] = $lng->txt("exc_last_submission");
1454  $result["last_submission"]["value"] = $last_sub;
1455 
1456  // #16994
1457  $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", $this->getUserId());
1458 
1459  // assignment type specific
1460  switch ($this->assignment->getType()) {
1462  // data is merged by team - see above
1463  // fallthrough
1464 
1466  $all_files = $this->getFiles();
1467  $late_files = 0;
1468  foreach ($all_files as $file) {
1469  if ($file["late"]) {
1470  $late_files++;
1471  }
1472  }
1473 
1474  // nr of submitted files
1475  $result["files"]["txt"] = $lng->txt("exc_files_returned");
1476  if ($late_files !== 0) {
1477  $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . " (" . $late_files . ")</span>";
1478  }
1479  $sub_cnt = count($all_files);
1480  $new = $this->lookupNewFiles();
1481  if ($new !== []) {
1482  $sub_cnt .= " " . sprintf($lng->txt("cnt_new"), count($new));
1483  }
1484 
1485  $result["files"]["count"] = $sub_cnt;
1486 
1487  // download command
1488  if ($sub_cnt > 0) {
1489  $result["files"]["download_url"] =
1490  $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1491 
1492  if (count($new) <= 0) {
1493  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1494  } else {
1495  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_all_files");
1496  }
1497 
1498  // download new files only
1499  if ($new !== []) {
1500  $result["files"]["download_new_url"] =
1501  $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadNewReturned");
1502 
1503  $result["files"]["download_new_txt"] = $lng->txt("exc_tbl_action_download_new_files");
1504  }
1505  }
1506  break;
1507 
1509  $result["files"]["txt"] = $lng->txt("exc_blog_returned");
1510  $blogs = $this->getFiles();
1511  if ($blogs !== []) {
1512  $blogs = array_pop($blogs);
1513  if ($blogs && substr($blogs["filename"], -1) != "/") {
1514  if ($blogs["late"]) {
1515  $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1516  }
1517 
1518  $result["files"]["count"] = 1;
1519 
1520  $result["files"]["download_url"] =
1521  $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1522 
1523  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1524  }
1525  }
1526  break;
1527 
1529  $result["files"]["txt"] = $lng->txt("exc_portfolio_returned");
1530  $portfolios = $this->getFiles();
1531  if ($portfolios !== []) {
1532  $portfolios = array_pop($portfolios);
1533  if ($portfolios && substr($portfolios["filename"], -1) != "/") {
1534  if ($portfolios["late"]) {
1535  $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1536  }
1537 
1538  $result["files"]["count"] = 1;
1539 
1540  $result["files"]["download_url"] =
1541  $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1542 
1543  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1544  }
1545  }
1546  break;
1547 
1549  $result["files"]["txt"] = $lng->txt("exc_files_returned_text");
1550  $files = $this->getFiles();
1551  if ($files !== []) {
1552  $result["files"]["count"] = 1;
1553 
1554  $files = array_shift($files);
1555  if (trim($files["atext"]) !== '' && trim($files["atext"]) !== '0') {
1556  if ($files["late"]) {
1557  $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1558  }
1559 
1560  $result["files"]["download_url"] =
1561  $ilCtrl->getLinkTargetByClass("ilexsubmissiontextgui", "showAssignmentText");
1562 
1563  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_text_assignment_show");
1564  }
1565  }
1566  break;
1567 
1569  $result["files"]["txt"] = $lng->txt("exc_wiki_returned");
1570  $objs = $this->getFiles();
1571  if ($objs !== []) {
1572  $objs = array_pop($objs);
1573  if ($objs && substr($objs["filename"], -1) != "/") {
1574  if ($objs["late"]) {
1575  $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>";
1576  }
1577 
1578  $result["files"]["count"] = 1;
1579 
1580  $result["files"]["download_url"] =
1581  $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned");
1582 
1583  $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files");
1584  }
1585  }
1586  break;
1587  }
1588 
1589  $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", "");
1590 
1591  return $result;
1592  }
1593 
1597  public static function getSubmissionsForFilename(
1598  string $a_filename,
1599  array $a_assignment_types = array()
1600  ): array {
1601  global $DIC;
1602 
1603  $db = $DIC->database();
1604 
1605  $query = "SELECT * FROM exc_returned r LEFT JOIN exc_assignment a" .
1606  " ON (r.ass_id = a.id) " .
1607  " WHERE r.filetitle = " . $db->quote($a_filename, "string");
1608 
1609  if (is_array($a_assignment_types) && $a_assignment_types !== []) {
1610  $query .= " AND " . $db->in("a.type", $a_assignment_types, false, "integer");
1611  }
1612 
1613  $set = $db->query($query);
1614  $rets = array();
1615  while ($rec = $db->fetchAssoc($set)) {
1616  $rets[] = $rec;
1617  }
1618 
1619 
1620  return $rets;
1621  }
1622 
1623  public static function getDirectoryNameFromUserData(int $a_user_id): string
1624  {
1625  $userName = ilObjUser::_lookupName($a_user_id);
1627  trim($userName["lastname"]) . "_" .
1628  trim($userName["firstname"]) . "_" .
1629  trim($userName["login"]) . "_" .
1630  $userName["user_id"]
1631  );
1632  }
1633 
1634  public static function getAssignmentParticipants(
1635  int $a_exercise_id,
1636  int $a_ass_id
1637  ): array {
1638  global $DIC;
1639 
1640  $ilDB = $DIC->database();
1641 
1642  $participants = array();
1643  $query = "SELECT user_id FROM exc_returned WHERE ass_id = " .
1644  $ilDB->quote($a_ass_id, "integer") .
1645  " AND obj_id = " .
1646  $ilDB->quote($a_exercise_id, "integer");
1647 
1648  $res = $ilDB->query($query);
1649 
1650  while ($row = $ilDB->fetchAssoc($res)) {
1651  $participants[] = $row['user_id'];
1652  }
1653 
1654  return $participants;
1655  }
1656 
1657  public static function processZipFile(
1658  string $a_directory,
1659  string $a_file,
1660  bool $structure
1661  ): void {
1662  global $DIC;
1663 
1664  $lng = $DIC->language();
1665 
1666  $pathinfo = pathinfo($a_file);
1667  $file = $pathinfo["basename"];
1668 
1669  // see 22727
1670  if (($pathinfo["extension"] ?? '') === '') {
1671  $file .= ".zip";
1672  }
1673 
1674  // Copy zip-file to new directory, unzip and remove it
1675  // TODO: check archive for broken file
1676  //copy ($a_file, $a_directory . "/" . $file);
1677  ilFileUtils::moveUploadedFile($a_file, $file, $a_directory . "/" . $file);
1678  $DIC->legacyArchives()->unzip(
1679  $a_directory . "/" . $file,
1680  null,
1681  false,
1682  true,
1683  false
1684  );
1685  unlink($a_directory . "/" . $file);
1686  //echo "-".$a_directory . "/" . $file."-";
1687  // Stores filename and paths into $filearray to check for viruses
1688  // Checks if filenames can be read, else -> throw exception and leave
1689  $filearray = [];
1690  ilFileUtils::recursive_dirscan($a_directory, $filearray);
1691 
1692  // if there are no files unziped (->broken file!)
1693  if (empty($filearray)) {
1694  throw new ilFileUtilsException(
1695  $lng->txt("archive_broken"),
1697  );
1698  }
1699 
1700  // virus handling
1701  foreach ($filearray["file"] as $key => $value) {
1702  // remove "invisible" files
1703  if (substr($value, 0, 1) == "." || stristr(
1704  $filearray["path"][$key],
1705  "/__MACOSX/"
1706  )) {
1707  unlink($filearray["path"][$key] . $value);
1708  unset($filearray["path"][$key]);
1709  unset($filearray["file"][$key]);
1710  continue;
1711  }
1712 
1713  $vir = ilVirusScanner::virusHandling($filearray["path"][$key], $value);
1714  if (!$vir[0]) {
1715  // Unlink file and throw exception
1716  unlink($filearray['path'][$key]);
1717  throw new ilFileUtilsException(
1718  $lng->txt("file_is_infected") . "<br />" . $vir[1],
1720  );
1721  } elseif ($vir[1] != "") {
1722  throw new ilFileUtilsException(
1723  $vir[1],
1725  );
1726  }
1727  }
1728 
1729  // If archive is to be used "flat"
1730  $doublettes = '';
1731  if (!$structure) {
1732  foreach (array_count_values($filearray["file"]) as $key => $value) {
1733  // Archive contains same filenames in different directories
1734  if ($value != "1") {
1735  $doublettes .= " '" . ilFileUtils::utf8_encode($key) . "'";
1736  }
1737  }
1738  if (strlen($doublettes) > 0) {
1739  throw new ilFileUtilsException(
1740  $lng->txt("exc_upload_error") . "<br />" . $lng->txt(
1741  "zip_structure_error"
1742  ) . $doublettes,
1744  );
1745  }
1746  } else {
1747  $mac_dir = $a_directory . "/__MACOSX";
1748  if (file_exists($mac_dir)) {
1749  ilFileUtils::delDir($mac_dir);
1750  }
1751  }
1752  }
1753 }
downloadFiles(array $a_file_ids=null, bool $a_only_new=false, bool $a_peer_review_mask_filename=false)
$res
Definition: ltiservices.php:69
updateTextSubmission(string $a_text)
Handle text assignment submissions.
getFiles(array $a_file_ids=null, bool $a_only_valid=false, string $a_min_timestamp=null, bool $print_versions=false)
Get submission items (not only files)
removeTeamMember(int $a_user_id, ?int $a_exc_ref_id=null)
exit
Definition: login.php:29
getType()
Get type this will most probably become an non public function in the future (or become obsolete) ...
Exercise assignment.
getFeedbackId()
used for the legacy storage path of feedbacks only
const IL_CAL_DATETIME
manipulateF(string $query, array $types, array $values)
static getLogger(string $a_component_id)
Get component logger.
static downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, string $to_path)
Download all submitted files of an assignment (all user)
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
fetchAssoc(ilDBStatement $statement)
static utf8_encode(string $string)
Class ChatMainBarProvider .
static virusHandling(string $a_file, string $a_orig_name='', bool $a_clean=true)
$valid
static lookupTitle(int $a_id)
ilGlobalTemplateInterface $main_tpl
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
ilExcAssMemberState $state
static lookupType(int $a_id)
static _lookupName(int $a_user_id)
lookup user name
ilExPeerReview $peer_review
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getValidFilename(string $a_filename)
quote($value, string $type)
static escapeShellArg(string $a_arg)
static getInstanceByUserId(int $a_assignment_id, int $a_user_id, bool $a_create_on_demand=false)
static processZipFile(string $a_directory, string $a_file, bool $structure)
processUploadedZipFile(string $fileTmp)
processes error handling etc for uploaded archive
const TYPE_UPLOAD
direct checks against const should be avoided, use type objects instead
static now()
Return current timestamp in Y-m-d H:i:s format.
getLastDownloadTime(array $a_user_ids)
static getAssignmentParticipants(int $a_exercise_id, int $a_ass_id)
$path
Definition: ltiservices.php:32
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _lookupObjId(int $ref_id)
static deliverFileLegacy(string $a_file, ?string $a_filename=null, ?string $a_mime=null, ?bool $isInline=false, ?bool $removeAfterDelivery=false, ?bool $a_exit_after=true)
static getASCIIFilename(string $a_filename)
global $DIC
Definition: feed.php:28
downloadMultipleFiles(array $a_filenames, ?int $a_user_id, bool $a_multi_user=false)
ILIAS Exercise InternalDomainService $domain
Exercise peer review.
__construct(ilExAssignment $a_ass, int $a_user_id, ilExAssignmentTeam $a_team=null, bool $a_is_tutor=false, bool $a_public_submissions=false)
getTableUserWhere(bool $a_team_mode=false)
addResourceObject(string $a_wsp_id, string $a_text=null)
Add personal resource or repository object (ref_id) to assigment.
static _lookupTitle(int $obj_id)
static recursive_dirscan(string $dir, array &$arr)
Recursively scans a given directory and writes path and filename into referenced array.
static dirsize(string $directory)
get size of a directory or a file.
$log
Definition: result.php:33
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
ilExAssignment $assignment
static getInstanceByIds(int $a_ass_id, int $a_user_id=0)
string $key
Consumer key/client ID value.
Definition: System.php:193
getLastSubmission()
TODO -> get rid of getTableUserWhere and move to repository class Get the date of the last submission...
$structure
TOTAL STRUCTURE.
static lookupExerciseIdForReturnedId(int $a_returned_id)
Get exercise from submission id (used in ilObjMediaObject)
query(string $query)
Run a (read-only) Query on the database.
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
ilExAssignmentTeam $team
static getAssignmentFilesByUsers(int $a_exc_id, int $a_ass_id, array $a_users)
static getAssignmentTeamMap(int $a_ass_id)
getLastOpeningHTMLView()
TODO -> get rid of getTableUserWhere and move to repository class Get a mysql timestamp from the last...
$filename
Definition: buildRTE.php:78
static getDirectoryNameFromUserData(int $a_user_id)
in(string $field, array $values, bool $negate=false, string $type="")
static getInstancesByExercise(int $a_exc_id)
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
uploadFile(array $a_http_post_files, bool $unzip=false)
Save submitted file of user.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$q
Definition: shib_logout.php:21
ilExAssignmentTypes $ass_types
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilExAssignmentTypeInterface $ass_type
static deleteUser(int $a_exc_id, int $a_user_id)
Delete all delivered files of user.
lookupNewFiles(int $a_tutor=null)
Check how much files have been uploaded by the learner after the last download of the tutor...
static _lookupType(int $id, bool $reference=false)
addFileUpload(\ILIAS\FileUpload\DTO\UploadResult $result)
isInTeam(int $a_user_id=null)
static findUserFiles(int $a_user_id, string $a_filetitle)
Check if given file was assigned Used in Blog/Portfolio.
downloadSingleFile(int $a_user_id, string $filename, string $filetitle, int $a_team_id=0)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getAllAssignmentFiles(int $a_exc_id, int $a_ass_id)
static makeDir(string $a_dir)
creates a new directory and inherits all filesystem permissions of the parent directory You may pass ...
deleteSelectedFiles(array $file_id_array)
Deletes already delivered files.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getSubmissionsForFilename(string $a_filename, array $a_assignment_types=array())
Get assignment return entries for a filename.