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