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