1<?php 2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4/** 5 * Exercise submission 6 * //TODO: This class has to much static methods related to delivered "files". Extract them to classes. 7 * 8 * @author Jörg Lützenkirchen <luetzenkirchen@leifos.com> 9 * @ingroup ModulesExercise 10 */ 11class ilExSubmission 12{ 13 const TYPE_FILE = "File"; 14 const TYPE_OBJECT = "Object"; // Blogs in WSP/Portfolio 15 const TYPE_TEXT = "Text"; 16 const TYPE_REPO_OBJECT = "RepoObject"; // Wikis 17 18 /** 19 * @var ilObjUser 20 */ 21 protected $user; 22 23 /** 24 * @var ilDB 25 */ 26 protected $db; 27 28 /** 29 * @var ilLanguage 30 */ 31 protected $lng; 32 33 /** 34 * @var ilCtrl 35 */ 36 protected $ctrl; 37 38 protected $assignment; // [ilExAssignment] 39 protected $user_id; // [int] 40 protected $team; // [ilExAssignmentTeam] 41 protected $peer_review; // [ilExPeerReview] 42 protected $is_tutor; // [bool] 43 protected $public_submissions; // [bool] 44 45 /** 46 * @var ilExAssignmentTypeInterface 47 */ 48 protected $ass_type; 49 50 /** 51 * @var ilExAssignmentTypes 52 */ 53 protected $ass_types; 54 55 public function __construct(ilExAssignment $a_ass, $a_user_id, ilExAssignmentTeam $a_team = null, $a_is_tutor = false, $a_public_submissions = false) 56 { 57 global $DIC; 58 59 $this->user = $DIC->user(); 60 $this->db = $DIC->database(); 61 $this->lng = $DIC->language(); 62 $this->ctrl = $DIC->ctrl(); 63 $ilUser = $DIC->user(); 64 65 $this->assignment = $a_ass; 66 $this->ass_type = $this->assignment->getAssignmentType(); 67 $this->ass_types = ilExAssignmentTypes::getInstance(); 68 69 $this->user_id = $a_user_id; 70 $this->is_tutor = (bool) $a_is_tutor; 71 $this->public_submissions = (bool) $a_public_submissions; 72 73 $this->state = ilExcAssMemberState::getInstanceByIds($a_ass->getId(), $a_user_id); 74 75 if ($a_ass->hasTeam()) { 76 if (!$a_team) { 77 $this->team = ilExAssignmentTeam::getInstanceByUserId($this->assignment->getId(), $this->user_id); 78 } else { 79 $this->team = $a_team; 80 } 81 } 82 83 if ($this->assignment->getPeerReview()) { 84 $this->peer_review = new ilExPeerReview($this->assignment); 85 } 86 } 87 88 public function getSubmissionType() 89 { 90 return $this->assignment->getAssignmentType()->getSubmissionType(); 91 /*switch($this->assignment->getType()) 92 { 93 case ilExAssignment::TYPE_UPLOAD_TEAM: 94 case ilExAssignment::TYPE_UPLOAD: 95 return "File"; 96 97 case ilExAssignment::TYPE_BLOG: 98 case ilExAssignment::TYPE_PORTFOLIO: 99 return "Object"; 100 101 case ilExAssignment::TYPE_TEXT: 102 return "Text"; 103 };*/ 104 } 105 106 107 /** 108 * @return \ilExAssignment 109 */ 110 public function getAssignment() 111 { 112 return $this->assignment; 113 } 114 115 /** 116 * @return \ilExAssignmentTeam 117 */ 118 public function getTeam() 119 { 120 return $this->team; 121 } 122 123 /** 124 * @return \ilExPeerReview 125 */ 126 public function getPeerReview() 127 { 128 return $this->peer_review; 129 } 130 131 public function validatePeerReviews() 132 { 133 $res = array(); 134 foreach ($this->getUserIds() as $user_id) { 135 $valid = true; 136 137 // no peer review == valid 138 if ($this->peer_review) { 139 $valid = $this->peer_review->isFeedbackValidForPassed($user_id); 140 } 141 142 $res[$user_id] = $valid; 143 } 144 return $res; 145 } 146 147 public function getUserId() 148 { 149 return $this->user_id; 150 } 151 152 public function getUserIds() 153 { 154 if ($this->team && 155 !$this->hasNoTeamYet()) { 156 return $this->team->getMembers(); 157 } 158 159 // if has no team currently there still might be uploads attached 160 return array($this->user_id); 161 } 162 163 public function getFeedbackId() 164 { 165 if ($this->team) { 166 return "t" . $this->team->getId(); 167 } else { 168 return $this->getUserId(); 169 } 170 } 171 172 public function hasSubmitted() 173 { 174 return (bool) sizeof($this->getFiles(null, true)); 175 } 176 177 public function getSelectedObject() 178 { 179 $files = $this->getFiles(); 180 if (sizeof($files)) { 181 return array_pop($files); 182 } 183 } 184 185 public function canSubmit() 186 { 187 return ($this->isOwner() && 188 $this->state->isSubmissionAllowed()); 189 } 190 191 public function canView() 192 { 193 $ilUser = $this->user; 194 195 if ($this->canSubmit() || 196 $this->isTutor() || 197 $this->isInTeam() || 198 $this->public_submissions) { 199 return true; 200 } 201 202 // #16115 203 if ($this->peer_review) { 204 // peer review givers may view peer submissions 205 foreach ($this->peer_review->getPeerReviewsByPeerId($this->getUserId()) as $giver) { 206 if ($giver["giver_id"] == $ilUser->getId()) { 207 return true; 208 } 209 } 210 } 211 212 return false; 213 } 214 215 public function isTutor() 216 { 217 return $this->is_tutor; 218 } 219 220 public function hasNoTeamYet() 221 { 222 if ($this->assignment->hasTeam() && 223 !$this->team->getId()) { 224 return true; 225 } 226 return false; 227 } 228 229 public function isInTeam($a_user_id = null) 230 { 231 $ilUser = $this->user; 232 233 if (!$a_user_id) { 234 $a_user_id = $ilUser->getId(); 235 } 236 return in_array($a_user_id, $this->getUserIds()); 237 } 238 239 public function isOwner() 240 { 241 $ilUser = $this->user; 242 243 return ($ilUser->getId() == $this->getUserId()); 244 } 245 246 public function hasPeerReviewAccess() 247 { 248 return ($this->peer_review && 249 $this->peer_review->hasPeerReviewAccess($this->user_id)); 250 } 251 252 public function canAddFile() 253 { 254 if (!$this->canSubmit()) { 255 return false; 256 } 257 258 $max = $this->getAssignment()->getMaxFile(); 259 if ($max && 260 $max <= sizeof($this->getFiles())) { 261 return false; 262 } 263 264 return true; 265 } 266 267 268 // 269 // FILES 270 // 271 272 protected function isLate() 273 { 274 $dl = $this->state->getOfficialDeadline(); 275 //$dl = $this->assignment->getPersonalDeadline($this->getUserId()); 276 return ($dl && $dl < time()); 277 } 278 279 protected function initStorage() 280 { 281 return new ilFSStorageExercise($this->assignment->getExerciseId(), $this->assignment->getId()); 282 } 283 284 /** 285 * Get storage id 286 * 287 * @return int 288 */ 289 protected function getStorageId() 290 { 291 if ($this->ass_type->isSubmissionAssignedToTeam()) { 292 $storage_id = $this->getTeam()->getId(); 293 } else { 294 $storage_id = $this->getUserId(); 295 } 296 return $storage_id; 297 } 298 299 300 /** 301 * Save submitted file of user 302 */ 303 public function uploadFile($a_http_post_files, $unzip = false) 304 { 305 $ilDB = $this->db; 306 307 if (!$this->canAddFile()) { 308 return false; 309 } 310 311 if ($this->ass_type->isSubmissionAssignedToTeam()) { 312 $team_id = $this->getTeam()->getId(); 313 $user_id = 0; 314 if ($team_id == 0) { 315 return false; 316 } 317 } else { 318 $team_id = 0; 319 $user_id = $this->getUserId(); 320 } 321 $storage_id = $this->getStorageId(); 322 323 $deliver_result = $this->initStorage()->uploadFile($a_http_post_files, $storage_id, $unzip); 324 325 if ($deliver_result) { 326 $next_id = $ilDB->nextId("exc_returned"); 327 $query = sprintf( 328 "INSERT INTO exc_returned " . 329 "(returned_id, obj_id, user_id, filename, filetitle, mimetype, ts, ass_id, late, team_id) " . 330 "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", 331 $ilDB->quote($next_id, "integer"), 332 $ilDB->quote($this->assignment->getExerciseId(), "integer"), 333 $ilDB->quote($user_id, "integer"), 334 $ilDB->quote($deliver_result["fullname"], "text"), 335 $ilDB->quote(ilFileUtils::getValidFilename($a_http_post_files["name"]), "text"), 336 $ilDB->quote($deliver_result["mimetype"], "text"), 337 $ilDB->quote(ilUtil::now(), "timestamp"), 338 $ilDB->quote($this->assignment->getId(), "integer"), 339 $ilDB->quote($this->isLate(), "integer"), 340 $ilDB->quote($team_id, "integer") 341 ); 342 $ilDB->manipulate($query); 343 344 if ($this->team) { 345 $this->team->writeLog( 346 ilExAssignmentTeam::TEAM_LOG_ADD_FILE, 347 $a_http_post_files["name"] 348 ); 349 } 350 351 return true; 352 } 353 return false; 354 } 355 356 /** 357 * processes errorhandling etc for uploaded archive 358 * @param string $tmpFile path and filename to uploaded file 359 */ 360 public function processUploadedZipFile($fileTmp) 361 { 362 $lng = $this->lng; 363 364 // Create unzip-directory 365 $newDir = ilUtil::ilTempnam(); 366 ilUtil::makeDir($newDir); 367 368 $success = true; 369 370 try { 371 ilFileUtils::processZipFile($newDir, $fileTmp, false); 372 ilFileUtils::recursive_dirscan($newDir, $filearray); 373 374 // #18441 - check number of files in zip 375 $max_num = $this->assignment->getMaxFile(); 376 if ($max_num) { 377 $current_num = sizeof($this->getFiles()); 378 $zip_num = sizeof($filearray["file"]); 379 if ($current_num + $zip_num > $max_num) { 380 $success = false; 381 ilUtil::sendFailure($lng->txt("exc_upload_error") . " [Zip1]", true); 382 } 383 } 384 385 if ($success) { 386 foreach ($filearray["file"] as $key => $filename) { 387 $a_http_post_files["name"] = ilFileUtils::utf8_encode($filename); 388 $a_http_post_files["type"] = "other"; 389 $a_http_post_files["tmp_name"] = $filearray["path"][$key] . "/" . $filename; 390 $a_http_post_files["error"] = 0; 391 $a_http_post_files["size"] = filesize($filearray["path"][$key] . "/" . $filename); 392 393 if (!$this->uploadFile($a_http_post_files, true)) { 394 $success = false; 395 ilUtil::sendFailure($lng->txt("exc_upload_error") . " [Zip2]", true); 396 } 397 } 398 } 399 } catch (ilFileUtilsException $e) { 400 $success = false; 401 ilUtil::sendFailure($e->getMessage()); 402 } 403 404 ilUtil::delDir($newDir); 405 return $success; 406 } 407 408 public static function getAllAssignmentFiles($a_exc_id, $a_ass_id) 409 { 410 global $DIC; 411 412 $ilDB = $DIC->database(); 413 414 $storage = new ilFSStorageExercise($a_exc_id, $a_ass_id); 415 $path = $storage->getAbsoluteSubmissionPath(); 416 417 $ass_type = ilExAssignmentTypes::getInstance()->getById(ilExAssignment::lookupType($a_ass_id)); 418 419 $query = "SELECT * FROM exc_returned WHERE ass_id = " . 420 $ilDB->quote($a_ass_id, "integer"); 421 422 $res = $ilDB->query($query); 423 while ($row = $ilDB->fetchAssoc($res)) { 424 if ($ass_type->isSubmissionAssignedToTeam()) { 425 $storage_id = $row["team_id"]; 426 } else { 427 $storage_id = $row["user_id"]; 428 } 429 430 $row["timestamp"] = $row["ts"]; 431 $row["filename"] = $path . "/" . $storage_id . "/" . basename($row["filename"]); 432 $delivered[] = $row; 433 } 434 435 return $delivered ? $delivered : array(); 436 } 437 438 public static function getAssignmentFilesByUsers(int $a_exc_id, int $a_ass_id, array $a_users) : array 439 { 440 global $DIC; 441 442 $ilDB = $DIC->database(); 443 444 $storage = new ilFSStorageExercise($a_exc_id, $a_ass_id); 445 $path = $storage->getAbsoluteSubmissionPath(); 446 447 $ass_type = ilExAssignmentTypes::getInstance()->getById(ilExAssignment::lookupType($a_ass_id)); 448 449 $query = "SELECT * FROM exc_returned WHERE ass_id = " . 450 $ilDB->quote($a_ass_id, "integer") . 451 " AND user_id IN (" . implode(',', $a_users) . ")"; 452 453 $res = $ilDB->query($query); 454 while ($row = $ilDB->fetchAssoc($res)) { 455 if ($ass_type->isSubmissionAssignedToTeam()) { 456 $storage_id = $row["team_id"]; 457 } else { 458 $storage_id = $row["user_id"]; 459 } 460 461 $row["timestamp"] = $row["ts"]; 462 $row["filename"] = $path . "/" . $storage_id . "/" . basename($row["filename"]); 463 $delivered[] = $row; 464 } 465 466 return $delivered ? $delivered : array(); 467 } 468 469 /** 470 * Get submission items (not only files) 471 * @todo this also returns non-file entries, rename this, see dev.txt.php 472 * @param array|null $a_file_ids 473 * @param bool $a_only_valid 474 * @param null $a_min_timestamp 475 * @return array 476 */ 477 public function getFiles(array $a_file_ids = null, $a_only_valid = false, $a_min_timestamp = null) 478 { 479 $ilDB = $this->db; 480 481 $sql = "SELECT * FROM exc_returned" . 482 " WHERE ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer"); 483 484 $sql .= " AND " . $this->getTableUserWhere(true); 485 486 487 if ($a_file_ids) { 488 $sql .= " AND " . $ilDB->in("returned_id", $a_file_ids, false, "integer"); 489 } 490 491 if ($a_min_timestamp) { 492 $sql .= " AND ts > " . $ilDB->quote($a_min_timestamp, "timestamp"); 493 } 494 495 $result = $ilDB->query($sql); 496 497 $delivered_files = array(); 498 if ($ilDB->numRows($result)) { 499 $path = $this->initStorage()->getAbsoluteSubmissionPath(); 500 501 while ($row = $ilDB->fetchAssoc($result)) { 502 // blog/portfolio/text submissions 503 if ($a_only_valid && 504 !$row["filename"] && 505 !(trim($row["atext"]))) { 506 continue; 507 } 508 509 $row["owner_id"] = $row["user_id"]; 510 $row["timestamp"] = $row["ts"]; 511 $row["timestamp14"] = substr($row["ts"], 0, 4) . 512 substr($row["ts"], 5, 2) . substr($row["ts"], 8, 2) . 513 substr($row["ts"], 11, 2) . substr($row["ts"], 14, 2) . 514 substr($row["ts"], 17, 2); 515 516 if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) { 517 $storage_id = $row["team_id"]; 518 } else { 519 $storage_id = $row["user_id"]; 520 } 521 522 523 $row["filename"] = $path . 524 "/" . $storage_id . "/" . basename($row["filename"]); 525 526 // see 22301, 22719 527 if (is_file($row["filename"]) || (!$this->assignment->getAssignmentType()->usesFileUpload())) { 528 array_push($delivered_files, $row); 529 } 530 } 531 } 532 533 return $delivered_files; 534 } 535 536 /** 537 * Check how much files have been uploaded by the learner 538 * after the last download of the tutor. 539 * @param tutor integer 540 * @return array 541 */ 542 public function lookupNewFiles($a_tutor = null) 543 { 544 $ilDB = $this->db; 545 $ilUser = $this->user; 546 547 $tutor = ($a_tutor) 548 ? $a_tutor 549 : $ilUser->getId(); 550 551 $where = " AND " . $this->getTableUserWhere(true); 552 553 $q = "SELECT exc_returned.returned_id AS id " . 554 "FROM exc_usr_tutor, exc_returned " . 555 "WHERE exc_returned.ass_id = exc_usr_tutor.ass_id " . 556 " AND exc_returned.user_id = exc_usr_tutor.usr_id " . 557 " AND exc_returned.ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") . 558 $where . 559 " AND exc_usr_tutor.tutor_id = " . $ilDB->quote($tutor, "integer") . 560 " AND exc_usr_tutor.download_time < exc_returned.ts "; 561 562 $new_up_set = $ilDB->query($q); 563 564 $new_up = array(); 565 while ($new_up_rec = $ilDB->fetchAssoc($new_up_set)) { 566 $new_up[] = $new_up_rec["id"]; 567 } 568 569 return $new_up; 570 } 571 572 /** 573 * Get exercise from submission id (used in ilObjMediaObject) 574 * 575 * @param int $a_returned_id 576 * @return int 577 */ 578 public static function lookupExerciseIdForReturnedId($a_returned_id) 579 { 580 global $DIC; 581 582 $ilDB = $DIC->database(); 583 584 $set = $ilDB->query("SELECT obj_id" . 585 " FROM exc_returned" . 586 " WHERE returned_id = " . $ilDB->quote($a_returned_id, "integer")); 587 $row = $ilDB->fetchAssoc($set); 588 return (int) $row["obj_id"]; 589 } 590 591 /** 592 * Check if given file was assigned 593 * 594 * Used in Blog/Portfolio 595 * 596 * @param int $a_user_id 597 * @param string $a_filetitle 598 */ 599 public static function findUserFiles($a_user_id, $a_filetitle) 600 { 601 global $DIC; 602 603 $ilDB = $DIC->database(); 604 605 $set = $ilDB->query("SELECT obj_id, ass_id" . 606 " FROM exc_returned" . 607 " WHERE user_id = " . $ilDB->quote($a_user_id, "integer") . 608 " AND filetitle = " . $ilDB->quote($a_filetitle, "text")); 609 $res = array(); 610 while ($row = $ilDB->fetchAssoc($set)) { 611 $res[$row["ass_id"]] = $row; 612 } 613 return $res; 614 } 615 616 public function deleteAllFiles() 617 { 618 $files = array(); 619 foreach ($this->getFiles() as $item) { 620 $files[] = $item["returned_id"]; 621 } 622 if (sizeof($files)) { 623 $this->deleteSelectedFiles($files); 624 } 625 } 626 627 /** 628 * Deletes already delivered files 629 * @param array $file_id_array An array containing database ids of the delivered files 630 * @param numeric $user_id The database id of the user 631 * @access public 632 */ 633 public function deleteSelectedFiles(array $file_id_array) 634 { 635 $ilDB = $this->db; 636 637 638 $where = " AND " . $this->getTableUserWhere(true); 639 640 641 if (!sizeof($file_id_array)) { 642 return; 643 } 644 645 if (count($file_id_array)) { 646 $result = $ilDB->query("SELECT * FROM exc_returned" . 647 " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") . 648 $where); 649 650 if ($ilDB->numRows($result)) { 651 $result_array = array(); 652 while ($row = $ilDB->fetchAssoc($result)) { 653 $row["timestamp"] = $row["ts"]; 654 array_push($result_array, $row); 655 } 656 657 // delete the entries in the database 658 $ilDB->manipulate("DELETE FROM exc_returned" . 659 " WHERE " . $ilDB->in("returned_id", $file_id_array, false, "integer") . 660 $where); 661 662 // delete the files 663 $path = $this->initStorage()->getAbsoluteSubmissionPath(); 664 foreach ($result_array as $key => $value) { 665 if ($value["filename"]) { 666 if ($this->team) { 667 $this->team->writeLog( 668 ilExAssignmentTeam::TEAM_LOG_REMOVE_FILE, 669 $value["filetitle"] 670 ); 671 } 672 673 if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) { 674 $storage_id = $value["team_id"]; 675 } else { 676 $storage_id = $value["user_id"]; 677 } 678 679 $filename = $path . "/" . $storage_id . "/" . basename($value["filename"]); 680 if (file_exists($filename)) { 681 unlink($filename); 682 } 683 } 684 } 685 } 686 } 687 } 688 689 /** 690 * Delete all delivered files of user 691 * 692 * @param int $a_exc_id excercise id 693 * @param int $a_user_id user id 694 */ 695 public static function deleteUser($a_exc_id, $a_user_id) 696 { 697 foreach (ilExAssignment::getInstancesByExercise($a_exc_id) as $ass) { 698 $submission = new self($ass, $a_user_id); 699 $submission->deleteAllFiles(); 700 701 // remove from any team 702 $team = $submission->getTeam(); 703 if ($team) { 704 $team->removeTeamMember($a_user_id); 705 } 706 707 // #14900 708 $member_status = $ass->getMemberStatus($a_user_id); 709 $member_status->setStatus("notgraded"); 710 $member_status->update(); 711 } 712 } 713 714 protected function getLastDownloadTime(array $a_user_ids) 715 { 716 $ilDB = $this->db; 717 $ilUser = $this->user; 718 719 $q = "SELECT download_time FROM exc_usr_tutor WHERE " . 720 " ass_id = " . $ilDB->quote($this->getAssignment()->getId(), "integer") . " AND " . 721 $ilDB->in("usr_id", $a_user_ids, "", "integer") . " AND " . 722 " tutor_id = " . $ilDB->quote($ilUser->getId(), "integer"); 723 $lu_set = $ilDB->query($q); 724 $lu_rec = $ilDB->fetchAssoc($lu_set); 725 return $lu_rec["download_time"]; 726 } 727 728 public function downloadFiles(array $a_file_ids = null, $a_only_new = false, $a_peer_review_mask_filename = false) 729 { 730 $ilUser = $this->user; 731 $lng = $this->lng; 732 733 $user_ids = $this->getUserIds(); 734 $is_team = $this->assignment->hasTeam(); 735 736 // get last download time 737 $download_time = null; 738 if ($a_only_new) { 739 $download_time = $this->getLastDownloadTime($user_ids); 740 } 741 742 if ($this->is_tutor) { 743 $this->updateTutorDownloadTime(); 744 } 745 746 if ($a_peer_review_mask_filename) { 747 // process peer review sequence id 748 $peer_id = null; 749 foreach ($this->peer_review->getPeerReviewsByGiver($ilUser->getId()) as $idx => $item) { 750 if ($item["peer_id"] == $this->getUserId()) { 751 $peer_id = $idx + 1; 752 break; 753 } 754 } 755 756 // this will remove personal info from zip-filename 757 $is_team = true; 758 } 759 760 $files = $this->getFiles($a_file_ids, false, $download_time); 761 if ($files) { 762 if (sizeof($files) == 1) { 763 $file = array_pop($files); 764 765 switch ($this->assignment->getType()) { 766 case ilExAssignment::TYPE_BLOG: 767 case ilExAssignment::TYPE_PORTFOLIO: 768 $file["filetitle"] = ilObjUser::_lookupName($file["user_id"]); 769 $file["filetitle"] = ilObject::_lookupTitle($this->assignment->getExerciseId()) . " - " . 770 $this->assignment->getTitle() . " - " . 771 $file["filetitle"]["firstname"] . " " . 772 $file["filetitle"]["lastname"] . " (" . 773 $file["filetitle"]["login"] . ").zip"; 774 break; 775 776 // @todo: generalize 777 case ilExAssignment::TYPE_WIKI_TEAM: 778 $file["filetitle"] = ilObject::_lookupTitle($this->assignment->getExerciseId()) . " - " . 779 $this->assignment->getTitle() . " (Team " . $this->getTeam()->getId() . ").zip"; 780 break; 781 782 default: 783 break; 784 } 785 786 if ($a_peer_review_mask_filename) { 787 $suffix = array_pop(explode(".", $file["filetitle"])); 788 $file["filetitle"] = $this->assignment->getTitle() . "_peer" . $peer_id . "." . $suffix; 789 } elseif ($file["late"]) { 790 $file["filetitle"] = $lng->txt("exc_late_submission") . " - " . 791 $file["filetitle"]; 792 } 793 794 $this->downloadSingleFile($file["user_id"], $file["filename"], $file["filetitle"], $file["team_id"]); 795 } else { 796 $array_files = array(); 797 foreach ($files as $seq => $file) { 798 if ($this->assignment->getAssignmentType()->isSubmissionAssignedToTeam()) { 799 $storage_id = $file["team_id"]; 800 } else { 801 $storage_id = $file["user_id"]; 802 } 803 804 $src = basename($file["filename"]); 805 if ($a_peer_review_mask_filename) { 806 $suffix = array_pop(explode(".", $src)); 807 $tgt = $this->assignment->getTitle() . "_peer" . $peer_id . 808 "_" . (++$seq) . "." . $suffix; 809 810 $array_files[$storage_id][] = array( 811 "src" => $src, 812 "tgt" => $tgt 813 ); 814 } else { 815 $array_files[$storage_id][] = array( 816 "src" => $src, 817 "late" => $file["late"] 818 ); 819 } 820 } 821 822 $this->downloadMultipleFiles( 823 $array_files, 824 ($is_team ? null : $this->getUserId()), 825 $is_team 826 ); 827 } 828 } else { 829 return false; 830 } 831 832 return true; 833 } 834 835 // Update the timestamp of the last download of current user (=tutor) 836 public function updateTutorDownloadTime() 837 { 838 $ilUser = $this->user; 839 $ilDB = $this->db; 840 841 $exc_id = $this->assignment->getExerciseId(); 842 $ass_id = $this->assignment->getId(); 843 844 foreach ($this->getUserIds() as $user_id) { 845 $ilDB->manipulateF( 846 "DELETE FROM exc_usr_tutor " . 847 "WHERE ass_id = %s AND usr_id = %s AND tutor_id = %s", 848 array("integer", "integer", "integer"), 849 array($ass_id, $user_id, $ilUser->getId()) 850 ); 851 852 $ilDB->manipulateF( 853 "INSERT INTO exc_usr_tutor (ass_id, obj_id, usr_id, tutor_id, download_time) VALUES " . 854 "(%s, %s, %s, %s, %s)", 855 array("integer", "integer", "integer", "integer", "timestamp"), 856 array($ass_id, $exc_id, $user_id, $ilUser->getId(), ilUtil::now()) 857 ); 858 } 859 } 860 861 protected function downloadSingleFile($a_user_id, $filename, $filetitle, $a_team_id) 862 { 863 if ($this->ass_type->isSubmissionAssignedToTeam()) { 864 $storage_id = $a_team_id; 865 } else { 866 $storage_id = $a_user_id; 867 } 868 869 $filename = $this->initStorage()->getAbsoluteSubmissionPath() . 870 "/" . $storage_id . "/" . basename($filename); 871 872 ilUtil::deliverFile($filename, $filetitle); 873 } 874 875 protected function downloadMultipleFiles($a_filenames, $a_user_id, $a_multi_user = false) 876 { 877 $lng = $this->lng; 878 879 $path = $this->initStorage()->getAbsoluteSubmissionPath(); 880 881 $cdir = getcwd(); 882 883 $zip = PATH_TO_ZIP; 884 $tmpdir = ilUtil::ilTempnam(); 885 $tmpfile = ilUtil::ilTempnam(); 886 $tmpzipfile = $tmpfile . ".zip"; 887 888 ilUtil::makeDir($tmpdir); 889 chdir($tmpdir); 890 891 $assTitle = ilExAssignment::lookupTitle($this->assignment->getId()); 892 $deliverFilename = str_replace(" ", "_", $assTitle); 893 if ($a_user_id > 0 && !$a_multi_user) { 894 $userName = ilObjUser::_lookupName($a_user_id); 895 $deliverFilename .= "_" . $userName["lastname"] . "_" . $userName["firstname"]; 896 } else { 897 $deliverFilename .= "_files"; 898 } 899 $orgDeliverFilename = trim($deliverFilename); 900 $deliverFilename = ilUtil::getASCIIFilename($orgDeliverFilename); 901 ilUtil::makeDir($tmpdir . "/" . $deliverFilename); 902 chdir($tmpdir . "/" . $deliverFilename); 903 904 //copy all files to a temporary directory and remove them afterwards 905 $parsed_files = $duplicates = array(); 906 foreach ($a_filenames as $storage_id => $files) { 907 $pathname = $path . "/" . $storage_id; 908 909 foreach ($files as $filename) { 910 // peer review masked filenames, see deliverReturnedFiles() 911 if (isset($filename["tgt"])) { 912 $newFilename = $filename["tgt"]; 913 $filename = $filename["src"]; 914 } else { 915 $late = $filename["late"]; 916 $filename = $filename["src"]; 917 918 // remove timestamp 919 $newFilename = trim($filename); 920 $pos = strpos($newFilename, "_"); 921 if ($pos !== false) { 922 $newFilename = substr($newFilename, $pos + 1); 923 } 924 // #11070 925 $chkName = strtolower($newFilename); 926 if (array_key_exists($chkName, $duplicates)) { 927 $suffix = strrpos($newFilename, "."); 928 $newFilename = substr($newFilename, 0, $suffix) . 929 " (" . (++$duplicates[$chkName]) . ")" . 930 substr($newFilename, $suffix); 931 } else { 932 $duplicates[$chkName] = 1; 933 } 934 935 if ($late) { 936 $newFilename = $lng->txt("exc_late_submission") . " - " . 937 $newFilename; 938 } 939 } 940 941 $newFilename = ilUtil::getASCIIFilename($newFilename); 942 $newFilename = $tmpdir . DIRECTORY_SEPARATOR . $deliverFilename . DIRECTORY_SEPARATOR . $newFilename; 943 // copy to temporal directory 944 $oldFilename = $pathname . DIRECTORY_SEPARATOR . $filename; 945 if (!copy($oldFilename, $newFilename)) { 946 echo 'Could not copy ' . $oldFilename . ' to ' . $newFilename; 947 } 948 touch($newFilename, filectime($oldFilename)); 949 $parsed_files[] = ilUtil::escapeShellArg($deliverFilename . DIRECTORY_SEPARATOR . basename($newFilename)); 950 } 951 } 952 953 chdir($tmpdir); 954 $zipcmd = $zip . " " . ilUtil::escapeShellArg($tmpzipfile) . " " . join(" ", $parsed_files); 955 956 exec($zipcmd); 957 ilUtil::delDir($tmpdir); 958 959 chdir($cdir); 960 ilUtil::deliverFile($tmpzipfile, $orgDeliverFilename . ".zip", "", false, true); 961 exit; 962 } 963 964 /** 965 * Download all submitted files of an assignment (all user) 966 * @param $a_ass ilExAssignment 967 * @param $members array of user names, key is user id 968 * @param $to_path string 969 * @throws ilExerciseException 970 * @return void 971 */ 972 public static function downloadAllAssignmentFiles(ilExAssignment $a_ass, array $members, $to_path) 973 { 974 global $DIC; 975 976 $lng = $DIC->language(); 977 978 $storage = new ilFSStorageExercise($a_ass->getExerciseId(), $a_ass->getId()); 979 $storage->create(); 980 981 ksort($members); 982 //$savepath = $this->getExercisePath() . "/" . $this->obj_id . "/"; 983 $savepath = $storage->getAbsoluteSubmissionPath(); 984 $cdir = getcwd(); 985 986 987 // important check: if the directory does not exist 988 // ILIAS stays in the current directory (echoing only a warning) 989 // and the zip command below archives the whole ILIAS directory 990 // (including the data directory) and sends a mega file to the user :-o 991 if (!is_dir($savepath)) { 992 return; 993 } 994 // Safe mode fix 995 // chdir($this->getExercisePath()); 996 997 $tmpdir = $storage->getTempPath(); 998 chdir($tmpdir); 999 $zip = PATH_TO_ZIP; 1000 1001 // check free diskspace 1002 $dirsize = 0; 1003 foreach (array_keys($members) as $id) { 1004 $directory = $savepath . DIRECTORY_SEPARATOR . $id; 1005 $dirsize += ilUtil::dirsize($directory); 1006 } 1007 if ($dirsize > disk_free_space($tmpdir)) { 1008 return -1; 1009 } 1010 1011 $ass_type = $a_ass->getType(); 1012 1013 // copy all member directories to the temporary folder 1014 // switch from id to member name and append the login if the member name is double 1015 // ensure that no illegal filenames will be created 1016 // remove timestamp from filename 1017 if ($a_ass->hasTeam()) { 1018 $team_dirs = array(); 1019 $team_map = ilExAssignmentTeam::getAssignmentTeamMap($a_ass->getId()); 1020 } 1021 foreach ($members as $id => $item) { 1022 $user = $item["name"]; 1023 $user_files = $item["files"]; 1024 $sourcedir = $savepath . DIRECTORY_SEPARATOR . $id; 1025 if (!is_dir($sourcedir)) { 1026 continue; 1027 } 1028 1029 // group by teams 1030 $team_dir = ""; 1031 if (is_array($team_map) && 1032 array_key_exists($id, $team_map)) { 1033 $team_id = $team_map[$id]; 1034 if (!array_key_exists($team_id, $team_dirs)) { 1035 $team_dir = $lng->txt("exc_team") . " " . $team_id; 1036 ilUtil::makeDir($team_dir); 1037 $team_dirs[$team_id] = $team_dir; 1038 } 1039 $team_dir = $team_dirs[$team_id] . DIRECTORY_SEPARATOR; 1040 } 1041 1042 if ($a_ass->getAssignmentType()->isSubmissionAssignedToTeam()) { 1043 $targetdir = $team_dir . ilUtil::getASCIIFilename( 1044 $item["name"] 1045 ); 1046 if ($targetdir == "") { 1047 continue; 1048 } 1049 } else { 1050 $targetdir = self::getDirectoryNameFromUserData($id); 1051 if ($a_ass->getAssignmentType()->usesTeams()) { 1052 $targetdir = $team_dir . $targetdir; 1053 } 1054 } 1055 ilUtil::makeDir($targetdir); 1056 1057 $sourcefiles = scandir($sourcedir); 1058 $duplicates = array(); 1059 foreach ($sourcefiles as $sourcefile) { 1060 if ($sourcefile == "." || $sourcefile == "..") { 1061 continue; 1062 } 1063 1064 $targetfile = trim(basename($sourcefile)); 1065 $pos = strpos($targetfile, "_"); 1066 if ($pos !== false) { 1067 $targetfile = substr($targetfile, $pos + 1); 1068 } 1069 1070 if ($a_ass->getAssignmentType()->getSubmissionType() == self::TYPE_REPO_OBJECT) { 1071 $obj_id = ilObject::_lookupObjId($targetfile); 1072 $obj_type = ilObject::_lookupType($obj_id); 1073 $targetfile = $obj_type . "_" . $obj_id . ".zip"; 1074 } 1075 1076 1077 // #14536 1078 if (array_key_exists($targetfile, $duplicates)) { 1079 $suffix = strrpos($targetfile, "."); 1080 $targetfile = substr($targetfile, 0, $suffix) . 1081 " (" . (++$duplicates[$targetfile]) . ")" . 1082 substr($targetfile, $suffix); 1083 } else { 1084 $duplicates[$targetfile] = 1; 1085 } 1086 1087 // late submission? 1088 if (is_array($user_files)) { // see #23900 1089 foreach ($user_files as $file) { 1090 if (basename($file["filename"]) == $sourcefile) { 1091 if ($file["late"]) { 1092 $targetfile = $lng->txt("exc_late_submission") . " - " . 1093 $targetfile; 1094 } 1095 break; 1096 } 1097 } 1098 } 1099 1100 $targetfile = ilUtil::getASCIIFilename($targetfile); 1101 $targetfile = $targetdir . DIRECTORY_SEPARATOR . $targetfile; 1102 $sourcefile = $sourcedir . DIRECTORY_SEPARATOR . $sourcefile; 1103 1104 if (!copy($sourcefile, $targetfile)) { 1105 throw new ilExerciseException("Could not copy " . basename($sourcefile) . " to '" . $targetfile . "'."); 1106 } else { 1107 // preserve time stamp 1108 touch($targetfile, filectime($sourcefile)); 1109 1110 // blogs and portfolios are stored as zip and have to be unzipped 1111 if ($ass_type == ilExAssignment::TYPE_PORTFOLIO || 1112 $ass_type == ilExAssignment::TYPE_BLOG) { 1113 ilUtil::unzip($targetfile); 1114 unlink($targetfile); 1115 } 1116 } 1117 } 1118 } 1119 $tmpzipfile = ilUtil::getASCIIFilename($lng->txt("exc_ass_submission_zip")) . ".zip"; 1120 // Safe mode fix 1121 $zipcmd = $zip . " -r " . ilUtil::escapeShellArg($tmpzipfile) . " ."; 1122 exec($zipcmd); 1123 //$path_final_zip_file = $to_path.DIRECTORY_SEPARATOR."Submissions/".$tmpzipfile; 1124 $path_final_zip_file = $to_path . DIRECTORY_SEPARATOR . $tmpzipfile; 1125 1126 if (file_exists($tmpdir . DIRECTORY_SEPARATOR . $tmpzipfile)) { 1127 copy($tmpzipfile, $path_final_zip_file); 1128 ilUtil::delDir($tmpdir); 1129 1130 //unzip the submissions zip file.(decided to unzip to allow the excel link the files more obvious when blog/portfolio) 1131 chdir($to_path); 1132 //TODO Bug in ilUtil -> if flat unzip fails. We can get rid of creating Submissions directory 1133 //ilUtil::unzip($path_final_zip_file,FALSE, TRUE); 1134 ilUtil::unzip($path_final_zip_file); 1135 unlink($path_final_zip_file); 1136 } 1137 1138 chdir($cdir); 1139 } 1140 1141 1142 /** 1143 * Get user/team where clause 1144 * 1145 * @param 1146 * @return 1147 */ 1148 public function getTableUserWhere($a_team_mode = false) 1149 { 1150 $ilDB = $this->db; 1151 1152 if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) { 1153 $team_id = $this->getTeam()->getId(); 1154 $where = " team_id = " . $ilDB->quote($team_id, "integer") . " "; 1155 } else { 1156 if ($a_team_mode) { 1157 $where = " " . $ilDB->in("user_id", $this->getUserIds(), "", "integer") . " "; 1158 } else { 1159 $where = " user_id = " . $ilDB->quote($this->getUserId(), "integer"); 1160 } 1161 } 1162 return $where; 1163 } 1164 1165 1166 /** 1167 * TODO -> get rid of getTableUserWhere and move to repository class 1168 * Get the date of the last submission of a user for the assignment 1169 * 1170 * @return mixed false or mysql timestamp of last submission 1171 */ 1172 public function getLastSubmission() 1173 { 1174 $ilDB = $this->db; 1175 1176 $ilDB->setLimit(1); 1177 1178 $q = "SELECT obj_id,user_id,ts FROM exc_returned" . 1179 " WHERE ass_id = " . $ilDB->quote($this->assignment->getId(), "integer") . 1180 " AND " . $this->getTableUserWhere(true) . 1181 " AND (filename IS NOT NULL OR atext IS NOT NULL)" . 1182 " AND ts IS NOT NULL" . 1183 " ORDER BY ts DESC"; 1184 $usr_set = $ilDB->query($q); 1185 $array = $ilDB->fetchAssoc($usr_set); 1186 return ilUtil::getMySQLTimestamp($array["ts"]); 1187 } 1188 1189 /** 1190 * TODO -> get rid of getTableUserWhere and move to repository class 1191 * Get a mysql timestamp from the last HTML view opening. 1192 */ 1193 public function getLastOpeningHTMLView() 1194 { 1195 $this->db->setLimit(1); 1196 1197 $q = "SELECT web_dir_access_time FROM exc_returned" . 1198 " WHERE ass_id = " . $this->db->quote($this->assignment->getId(), "integer") . 1199 " AND (filename IS NOT NULL OR atext IS NOT NULL)" . 1200 " AND web_dir_access_time IS NOT NULL" . 1201 " AND " . $this->getTableUserWhere(true) . 1202 " ORDER BY web_dir_access_time DESC"; 1203 1204 $res = $this->db->query($q); 1205 1206 $data = $this->db->fetchAssoc($res); 1207 1208 return ilUtil::getMySQLTimestamp($data["web_dir_access_time"]); 1209 } 1210 1211 1212 // 1213 // OBJECTS 1214 // 1215 1216 /** 1217 * Add personal resource or repository object (ref_id) to assigment 1218 * 1219 * @param int $a_wsp_id 1220 * @param string $a_text 1221 * @return int 1222 * @throws ilExerciseException 1223 */ 1224 public function addResourceObject($a_wsp_id, $a_text = null) 1225 { 1226 $ilDB = $this->db; 1227 1228 if ($this->getAssignment()->getAssignmentType()->isSubmissionAssignedToTeam()) { 1229 $user_id = 0; 1230 $team_id = $this->getTeam()->getId(); 1231 } else { 1232 $user_id = $this->getUserId(); 1233 $team_id = 0; 1234 } 1235 1236 // repository objects must be unique in submissions 1237 // the same repo object cannot be used in different submissions or even different assignment/exercises 1238 // why? -> the access handling would fail, since the access depends e.g. on teams or even phase of the 1239 // assignment 1240 if ($this->getAssignment()->getAssignmentType()->getSubmissionType() == ilExSubmission::TYPE_REPO_OBJECT) { 1241 $repos_ass_type_ids = $this->ass_types->getIdsForSubmissionType(ilExSubmission::TYPE_REPO_OBJECT); 1242 $subs = $this->getSubmissionsForFilename($a_wsp_id, $repos_ass_type_ids); 1243 if (count($subs) > 0) { 1244 throw new ilExerciseException("Repository object $a_wsp_id is already assigned to another assignment."); 1245 } 1246 } 1247 1248 $next_id = $ilDB->nextId("exc_returned"); 1249 $query = sprintf( 1250 "INSERT INTO exc_returned " . 1251 "(returned_id, obj_id, user_id, filetitle, ass_id, ts, atext, late, team_id) " . 1252 "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", 1253 $ilDB->quote($next_id, "integer"), 1254 $ilDB->quote($this->assignment->getExerciseId(), "integer"), 1255 $ilDB->quote($user_id, "integer"), 1256 $ilDB->quote($a_wsp_id, "text"), 1257 $ilDB->quote($this->assignment->getId(), "integer"), 1258 $ilDB->quote(ilUtil::now(), "timestamp"), 1259 $ilDB->quote($a_text, "text"), 1260 $ilDB->quote($this->isLate(), "integer"), 1261 $ilDB->quote($team_id, "integer") 1262 ); 1263 $ilDB->manipulate($query); 1264 1265 return $next_id; 1266 } 1267 1268 /** 1269 * Remove personal resource to assigment 1270 * 1271 * @param int $a_returned_id 1272 */ 1273 public function deleteResourceObject($a_returned_id) 1274 { 1275 $ilDB = $this->db; 1276 1277 $ilDB->manipulate("DELETE FROM exc_returned" . 1278 " WHERE obj_id = " . $ilDB->quote($this->assignment->getExerciseId(), "integer") . 1279 " AND " . $this->getTableUserWhere(false) . 1280 " AND ass_id = " . $ilDB->quote($this->assignment->getId(), "integer") . 1281 " AND returned_id = " . $ilDB->quote($a_returned_id, "integer")); 1282 } 1283 1284 /** 1285 * Handle text assignment submissions 1286 * 1287 * @param string $a_text 1288 * @return int 1289 */ 1290 public function updateTextSubmission($a_text) 1291 { 1292 $ilDB = $this->db; 1293 1294 $files = $this->getFiles(); 1295 1296 // no text = remove submission 1297 if (!trim($a_text)) { 1298 $this->deleteAllFiles(); 1299 return; 1300 } 1301 1302 if (!$files) { 1303 return $this->addResourceObject("TEXT", $a_text); 1304 } else { 1305 $files = array_shift($files); 1306 $id = $files["returned_id"]; 1307 if ($id) { 1308 $ilDB->manipulate("UPDATE exc_returned" . 1309 " SET atext = " . $ilDB->quote($a_text, "text") . 1310 ", ts = " . $ilDB->quote(ilUtil::now(), "timestamp") . 1311 ", late = " . $ilDB->quote($this->isLate(), "integer") . 1312 " WHERE returned_id = " . $ilDB->quote($id, "integer")); 1313 return $id; 1314 } 1315 } 1316 } 1317 1318 // 1319 // GUI helper 1320 // 1321 1322 // :TODO: 1323 1324 public function getDownloadedFilesInfoForTableGUIS($a_parent_obj, $a_parent_cmd = null) 1325 { 1326 $lng = $this->lng; 1327 $ilCtrl = $this->ctrl; 1328 1329 $result = array(); 1330 $result["files"]["count"] = "---"; 1331 1332 // submission: 1333 // see if files have been resubmmited after solved 1334 $last_sub = $this->getLastSubmission(); 1335 if ($last_sub) { 1336 $last_sub = ilDatePresentation::formatDate(new ilDateTime($last_sub, IL_CAL_DATETIME)); 1337 } else { 1338 $last_sub = "---"; 1339 } 1340 /* #13741 - status_time has been reduced to grading (mark/status) 1341 if (self::lookupUpdatedSubmission($a_ass_id, $a_user_id) == 1) 1342 { 1343 $last_sub = "<b>".$last_sub."</b>"; 1344 } 1345 */ 1346 $result["last_submission"]["txt"] = $lng->txt("exc_last_submission"); 1347 $result["last_submission"]["value"] = $last_sub; 1348 1349 // #16994 1350 $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", $this->getUserId()); 1351 1352 // assignment type specific 1353 switch ($this->assignment->getType()) { 1354 case ilExAssignment::TYPE_UPLOAD_TEAM: 1355 // data is merged by team - see above 1356 // fallthrough 1357 1358 case ilExAssignment::TYPE_UPLOAD: 1359 $all_files = $this->getFiles(); 1360 $late_files = 0; 1361 foreach ($all_files as $file) { 1362 if ($file["late"]) { 1363 $late_files++; 1364 } 1365 } 1366 1367 // nr of submitted files 1368 $result["files"]["txt"] = $lng->txt("exc_files_returned"); 1369 if ($late_files) { 1370 $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . " (" . $late_files . ")</span>"; 1371 } 1372 $sub_cnt = count($all_files); 1373 $new = $this->lookupNewFiles(); 1374 if (count($new) > 0) { 1375 $sub_cnt .= " " . sprintf($lng->txt("cnt_new"), count($new)); 1376 } 1377 1378 $result["files"]["count"] = $sub_cnt; 1379 1380 // download command 1381 if ($sub_cnt > 0) { 1382 $result["files"]["download_url"] = 1383 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned"); 1384 1385 if (count($new) <= 0) { 1386 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files"); 1387 } else { 1388 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_all_files"); 1389 } 1390 1391 // download new files only 1392 if (count($new) > 0) { 1393 $result["files"]["download_new_url"] = 1394 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadNewReturned"); 1395 1396 $result["files"]["download_new_txt"] = $lng->txt("exc_tbl_action_download_new_files"); 1397 } 1398 } 1399 break; 1400 1401 case ilExAssignment::TYPE_BLOG: 1402 $result["files"]["txt"] = $lng->txt("exc_blog_returned"); 1403 $blogs = $this->getFiles(); 1404 if ($blogs) { 1405 $blogs = array_pop($blogs); 1406 if ($blogs && substr($blogs["filename"], -1) != "/") { 1407 if ($blogs["late"]) { 1408 $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>"; 1409 } 1410 1411 $result["files"]["count"] = 1; 1412 1413 $result["files"]["download_url"] = 1414 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned"); 1415 1416 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files"); 1417 } 1418 } 1419 break; 1420 1421 case ilExAssignment::TYPE_PORTFOLIO: 1422 $result["files"]["txt"] = $lng->txt("exc_portfolio_returned"); 1423 $portfolios = $this->getFiles(); 1424 if ($portfolios) { 1425 $portfolios = array_pop($portfolios); 1426 if ($portfolios && substr($portfolios["filename"], -1) != "/") { 1427 if ($portfolios["late"]) { 1428 $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>"; 1429 } 1430 1431 $result["files"]["count"] = 1; 1432 1433 $result["files"]["download_url"] = 1434 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned"); 1435 1436 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files"); 1437 } 1438 } 1439 break; 1440 1441 case ilExAssignment::TYPE_TEXT: 1442 $result["files"]["txt"] = $lng->txt("exc_files_returned_text"); 1443 $files = $this->getFiles(); 1444 if ($files) { 1445 $result["files"]["count"] = 1; 1446 1447 $files = array_shift($files); 1448 if (trim($files["atext"])) { 1449 if ($files["late"]) { 1450 $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>"; 1451 } 1452 1453 $result["files"]["download_url"] = 1454 $ilCtrl->getLinkTargetByClass("ilexsubmissiontextgui", "showAssignmentText"); 1455 1456 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_text_assignment_show"); 1457 } 1458 } 1459 break; 1460 1461 case ilExAssignment::TYPE_WIKI_TEAM: 1462 $result["files"]["txt"] = $lng->txt("exc_wiki_returned"); 1463 $objs = $this->getFiles(); 1464 if ($objs) { 1465 $objs = array_pop($objs); 1466 if ($objs && substr($objs["filename"], -1) != "/") { 1467 if ($objs["late"]) { 1468 $result["files"]["txt"] .= ' - <span class="warning">' . $lng->txt("exc_late_submission") . "</span>"; 1469 } 1470 1471 $result["files"]["count"] = 1; 1472 1473 $result["files"]["download_url"] = 1474 $ilCtrl->getLinkTargetByClass("ilexsubmissionfilegui", "downloadReturned"); 1475 1476 $result["files"]["download_txt"] = $lng->txt("exc_tbl_action_download_files"); 1477 } 1478 } 1479 break; 1480 } 1481 1482 $ilCtrl->setParameterByClass("ilexsubmissionfilegui", "member_id", ""); 1483 1484 return $result; 1485 } 1486 1487 /** 1488 * Get assignment return entries for a filename 1489 * 1490 * @param string $a_filename 1491 * @param int[] $a_assignment_types 1492 * @return array 1493 */ 1494 public static function getSubmissionsForFilename($a_filename, $a_assignment_types = array()) 1495 { 1496 global $DIC; 1497 1498 $db = $DIC->database(); 1499 1500 $query = "SELECT * FROM exc_returned r LEFT JOIN exc_assignment a" . 1501 " ON (r.ass_id = a.id) " . 1502 " WHERE r.filetitle = " . $db->quote($a_filename, "string"); 1503 1504 if (is_array($a_assignment_types) && count($a_assignment_types) > 0) { 1505 $query .= " AND " . $db->in("a.type", $a_assignment_types, false, "integer"); 1506 } 1507 1508 $set = $db->query($query); 1509 $rets = array(); 1510 while ($rec = $db->fetchAssoc($set)) { 1511 $rets[] = $rec; 1512 } 1513 1514 1515 return $rets; 1516 } 1517 1518 /* 1519 * @param $a_user_id 1520 * @return string 1521 */ 1522 public static function getDirectoryNameFromUserData($a_user_id) 1523 { 1524 $userName = ilObjUser::_lookupName($a_user_id); 1525 $targetdir = ilUtil::getASCIIFilename( 1526 trim($userName["lastname"]) . "_" . 1527 trim($userName["firstname"]) . "_" . 1528 trim($userName["login"]) . "_" . 1529 $userName["user_id"] 1530 ); 1531 1532 return $targetdir; 1533 } 1534 1535 /** 1536 * @param $a_exercise_id 1537 * @param $a_ass_id 1538 * @return array 1539 */ 1540 public static function getAssignmentParticipants(int $a_exercise_id, int $a_ass_id) : array 1541 { 1542 global $DIC; 1543 1544 $ilDB = $DIC->database(); 1545 1546 $participants = array(); 1547 $query = "SELECT user_id FROM exc_returned WHERE ass_id = " . 1548 $ilDB->quote($a_ass_id, "integer") . 1549 " AND obj_id = " . 1550 $ilDB->quote($a_exercise_id, "integer"); 1551 1552 $res = $ilDB->query($query); 1553 1554 while ($row = $ilDB->fetchAssoc($res)) { 1555 $participants[] = $row['user_id']; 1556 } 1557 1558 return $participants; 1559 } 1560} 1561