1<?php 2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */ 3 4/** 5* Exercise assignment 6* 7* @author Alex Killing <alex.killing@gmx.de> 8* @ingroup ModulesExercise 9*/ 10class ilExAssignment 11{ 12 /** 13 * @var ilDB 14 */ 15 protected $db; 16 17 /** 18 * @var ilLanguage 19 */ 20 protected $lng; 21 22 /** 23 * @var ilObjUser 24 */ 25 protected $user; 26 27 /** 28 * @var ilAppEventHandler 29 */ 30 protected $app_event_handler; 31 32 /** 33 * @deprecated direct checks against const should be avoided, use type objects instead 34 */ 35 const TYPE_UPLOAD = 1; 36 /** 37 * @deprecated 38 */ 39 const TYPE_BLOG = 2; 40 /** 41 * @deprecated 42 */ 43 const TYPE_PORTFOLIO = 3; 44 /** 45 * @deprecated 46 */ 47 const TYPE_UPLOAD_TEAM = 4; 48 /** 49 * @deprecated 50 */ 51 const TYPE_TEXT = 5; 52 /** 53 * @deprecated 54 */ 55 const TYPE_WIKI_TEAM = 6; 56 57 const FEEDBACK_DATE_DEADLINE = 1; 58 const FEEDBACK_DATE_SUBMISSION = 2; 59 const FEEDBACK_DATE_CUSTOM = 3; 60 61 const PEER_REVIEW_VALID_NONE = 1; 62 const PEER_REVIEW_VALID_ONE = 2; 63 const PEER_REVIEW_VALID_ALL = 3; 64 65 const TEAMS_FORMED_BY_PARTICIPANTS = 0; 66 const TEAMS_FORMED_BY_TUTOR = 1; 67 const TEAMS_FORMED_BY_RANDOM = 2; 68 const TEAMS_FORMED_BY_ASSIGNMENT = 3; 69 70 const DEADLINE_ABSOLUTE = 0; 71 const DEADLINE_RELATIVE = 1; 72 73 74 protected $id; 75 protected $exc_id; 76 protected $type; 77 protected $start_time; 78 protected $deadline; 79 protected $deadline2; 80 protected $instruction; 81 protected $title; 82 protected $mandatory; 83 protected $order_nr; 84 protected $peer; 85 protected $peer_min; 86 protected $peer_unlock; 87 protected $peer_dl; 88 protected $peer_valid; 89 protected $peer_file; 90 protected $peer_personal; 91 protected $peer_char; 92 protected $peer_text; 93 protected $peer_rating; 94 protected $peer_crit_cat; 95 protected $feedback_file; 96 protected $feedback_cron; 97 protected $feedback_date; 98 protected $feedback_date_custom; 99 protected $team_tutor = false; 100 protected $max_file; 101 102 protected $portfolio_template; 103 protected $min_char_limit; 104 protected $max_char_limit; 105 106 protected $types = null; 107 108 /** 109 * @var ilExAssignmentTypeInterface 110 */ 111 protected $ass_type; 112 113 protected $deadline_mode = 0; 114 protected $relative_deadline = 0; 115 protected $starting_timestamp = null; 116 117 /** 118 * @var 119 */ 120 protected $rel_deadline_last_subm; 121 122 protected $member_status = array(); // [array] 123 124 protected $log; 125 126 /** 127 * Constructor 128 */ 129 public function __construct($a_id = 0) 130 { 131 global $DIC; 132 133 $this->db = $DIC->database(); 134 $this->lng = $DIC->language(); 135 $this->user = $DIC->user(); 136 $this->app_event_handler = $DIC["ilAppEventHandler"]; 137 $this->types = ilExAssignmentTypes::getInstance(); 138 139 $this->setType(self::TYPE_UPLOAD); 140 $this->setFeedbackDate(self::FEEDBACK_DATE_DEADLINE); 141 142 $this->log = ilLoggerFactory::getLogger("exc"); 143 144 if ($a_id > 0) { 145 $this->setId($a_id); 146 $this->read(); 147 } 148 } 149 150 /** 151 * @param $a_exc_id 152 * @return ilExAssignment[] 153 */ 154 public static function getInstancesByExercise($a_exc_id) 155 { 156 global $DIC; 157 158 $ilDB = $DIC->database(); 159 160 $set = $ilDB->query("SELECT * FROM exc_assignment " . 161 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer") . 162 " ORDER BY order_nr ASC"); 163 $data = array(); 164 165 $order_val = 10; 166 while ($rec = $ilDB->fetchAssoc($set)) { 167 // ??? 168 $rec["order_val"] = $order_val; 169 170 $ass = new self(); 171 $ass->initFromDB($rec); 172 $data[] = $ass; 173 174 $order_val += 10; 175 } 176 177 return $data; 178 } 179 180 /** 181 * @param array $a_file_data 182 * @param integer $a_ass_id assignment id. 183 * @return array 184 */ 185 public static function instructionFileGetFileOrderData($a_file_data, $a_ass_id) 186 { 187 global $DIC; 188 189 $db = $DIC->database(); 190 $db->setLimit(1, 0); 191 192 $result_order_val = $db->query(" 193 SELECT id, order_nr 194 FROM exc_ass_file_order 195 WHERE assignment_id = {$db->quote($a_ass_id, 'integer')} 196 AND filename = {$db->quote($a_file_data['entry'], 'string')} 197 "); 198 199 $order_val = 0; 200 $order_id = 0; 201 while ($row = $db->fetchAssoc($result_order_val)) { 202 $order_val = (int) $row['order_nr']; 203 $order_id = (int) $row['id']; 204 } 205 return array($order_val, $order_id); 206 } 207 208 public function hasTeam() 209 { 210 return $this->ass_type->usesTeams(); 211 } 212 213 /** 214 * Set assignment id 215 * 216 * @param int assignment id 217 */ 218 public function setId($a_val) 219 { 220 $this->id = $a_val; 221 } 222 223 /** 224 * Get assignment id 225 * 226 * @return int assignment id 227 */ 228 public function getId() 229 { 230 return $this->id; 231 } 232 233 /** 234 * Set exercise id 235 * 236 * @param int exercise id 237 */ 238 public function setExerciseId($a_val) 239 { 240 $this->exc_id = $a_val; 241 } 242 243 /** 244 * Get exercise id 245 * 246 * @return int exercise id 247 */ 248 public function getExerciseId() 249 { 250 return $this->exc_id; 251 } 252 253 /** 254 * Set start time (timestamp) 255 * 256 * @param int start time (timestamp) 257 */ 258 public function setStartTime($a_val) 259 { 260 $this->start_time = $a_val; 261 } 262 263 /** 264 * Get start time (timestamp) 265 * 266 * @return int start time (timestamp) 267 */ 268 public function getStartTime() 269 { 270 return $this->start_time; 271 } 272 273 /** 274 * Set deadline (timestamp) 275 * 276 * @param int deadline (timestamp) 277 */ 278 public function setDeadline($a_val) 279 { 280 $this->deadline = $a_val; 281 } 282 283 /** 284 * Get deadline (timestamp) 285 * 286 * @return int deadline (timestamp) 287 */ 288 public function getDeadline() 289 { 290 return $this->deadline; 291 } 292 293 /** 294 * Set deadline mode 295 * 296 * @param int $a_val deadline mode 297 */ 298 public function setDeadlineMode($a_val) 299 { 300 $this->deadline_mode = $a_val; 301 } 302 303 /** 304 * Get deadline mode 305 * 306 * @return int deadline mode 307 */ 308 public function getDeadlineMode() 309 { 310 return $this->deadline_mode; 311 } 312 313 /** 314 * Set relative deadline 315 * 316 * @param int $a_val relative deadline 317 */ 318 public function setRelativeDeadline($a_val) 319 { 320 $this->relative_deadline = $a_val; 321 } 322 323 /** 324 * Get relative deadline 325 * 326 * @return int relative deadline 327 */ 328 public function getRelativeDeadline() 329 { 330 return $this->relative_deadline; 331 } 332 333 /** 334 * Set relative deadline last submission 335 * 336 * @param int $a_val 337 */ 338 public function setRelDeadlineLastSubmission($a_val) 339 { 340 $this->rel_deadline_last_subm = $a_val; 341 } 342 343 /** 344 * Get relative deadline last submission 345 * 346 * @return int 347 */ 348 public function getRelDeadlineLastSubmission() 349 { 350 return $this->rel_deadline_last_subm; 351 } 352 353 354 /** 355 * Get individual deadline (max of common or idl (team) deadline = Official Deadline) 356 * @param int $a_user_id 357 * @return int 358 */ 359 public function getPersonalDeadline($a_user_id) 360 { 361 $ilDB = $this->db; 362 363 $is_team = false; 364 if ($this->ass_type->usesTeams()) { 365 $team_id = ilExAssignmentTeam::getTeamId($this->getId(), $a_user_id); 366 if (!$team_id) { 367 // #0021043 368 return $this->getDeadline(); 369 } 370 $a_user_id = $team_id; 371 $is_team = true; 372 } 373 374 $set = $ilDB->query("SELECT tstamp FROM exc_idl" . 375 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer") . 376 " AND member_id = " . $ilDB->quote($a_user_id, "integer") . 377 " AND is_team = " . $ilDB->quote($is_team, "integer")); 378 $row = $ilDB->fetchAssoc($set); 379 380 // use assignment deadline if no direct personal 381 return max($row["tstamp"], $this->getDeadline()); 382 } 383 384 /** 385 * Get last/final personal deadline (of assignment) 386 * 387 * @return int 388 */ 389 public function getLastPersonalDeadline() 390 { 391 $ilDB = $this->db; 392 393 $set = $ilDB->query("SELECT MAX(tstamp) FROM exc_idl" . 394 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer")); 395 $row = $ilDB->fetchAssoc($set); 396 return $row["tstamp"]; 397 } 398 399 /** 400 * Set extended deadline (timestamp) 401 * 402 * @param int 403 */ 404 public function setExtendedDeadline($a_val) 405 { 406 if ($a_val !== null) { 407 $a_val = (int) $a_val; 408 } 409 $this->deadline2 = $a_val; 410 } 411 412 /** 413 * Get extended deadline (timestamp) 414 * 415 * @return int 416 */ 417 public function getExtendedDeadline() 418 { 419 return $this->deadline2; 420 } 421 422 /** 423 * Set instruction 424 * 425 * @param string instruction 426 */ 427 public function setInstruction($a_val) 428 { 429 $this->instruction = $a_val; 430 } 431 432 /** 433 * Get instruction 434 * 435 * @return string instruction 436 */ 437 public function getInstruction() 438 { 439 return $this->instruction; 440 } 441 442 /** 443 * Get instruction presentation 444 * 445 * @return string 446 */ 447 public function getInstructionPresentation() 448 { 449 $inst = $this->getInstruction(); 450 if (trim($inst)) { 451 $is_html = (strlen($inst) != strlen(strip_tags($inst))); 452 if (!$is_html) { 453 $inst = nl2br(ilUtil::makeClickable($inst, true)); 454 } 455 } 456 return $inst; 457 } 458 459 460 /** 461 * Set title 462 * 463 * @param string title 464 */ 465 public function setTitle($a_val) 466 { 467 $this->title = $a_val; 468 } 469 470 /** 471 * Get title 472 * 473 * @return string title 474 */ 475 public function getTitle() 476 { 477 return $this->title; 478 } 479 480 /** 481 * Set mandatory 482 * 483 * @param int mandatory 484 */ 485 public function setMandatory($a_val) 486 { 487 $this->mandatory = $a_val; 488 } 489 490 /** 491 * Get mandatory 492 * 493 * @return int mandatory 494 */ 495 public function getMandatory() 496 { 497 return $this->mandatory; 498 } 499 500 /** 501 * Set order nr 502 * 503 * @param int order nr 504 */ 505 public function setOrderNr($a_val) 506 { 507 $this->order_nr = $a_val; 508 } 509 510 /** 511 * Get order nr 512 * 513 * @return int order nr 514 */ 515 public function getOrderNr() 516 { 517 return $this->order_nr; 518 } 519 520 /** 521 * Set type 522 * 523 * @param int $a_value 524 * @deprecated this will most probably become an non public function in the future (or become obsolete) 525 */ 526 public function setType($a_value) 527 { 528 if ($this->isValidType($a_value)) { 529 $this->type = (int) $a_value; 530 531 $this->ass_type = $this->types->getById($a_value); 532 533 if ($this->ass_type->usesTeams()) { 534 $this->setPeerReview(false); 535 } 536 } 537 } 538 539 /** 540 * Get assignment type 541 * 542 * @param 543 * @return null|ilExAssignmentTypeInterface 544 */ 545 public function getAssignmentType() 546 { 547 return $this->ass_type; 548 } 549 550 551 /** 552 * Get type 553 * 554 * @return int 555 * @deprecated this will most probably become an non public function in the future (or become obsolete) 556 */ 557 public function getType() 558 { 559 return $this->type; 560 } 561 562 /** 563 * Is given type valid? 564 * 565 * @param int $a_value 566 * @return bool 567 */ 568 public function isValidType($a_value) 569 { 570 return $this->types->isValidId($a_value); 571 } 572 573 /** 574 * Toggle peer review 575 * 576 * @param bool $a_value 577 */ 578 public function setPeerReview($a_value) 579 { 580 $this->peer = (bool) $a_value; 581 } 582 583 /** 584 * Get peer review status 585 * 586 * @return bool 587 */ 588 public function getPeerReview() 589 { 590 return (bool) $this->peer; 591 } 592 593 /** 594 * Set peer review minimum 595 * 596 * @param int $a_value 597 */ 598 public function setPeerReviewMin($a_value) 599 { 600 $this->peer_min = (int) $a_value; 601 } 602 603 /** 604 * Get peer review minimum 605 * 606 * @return int 607 */ 608 public function getPeerReviewMin() 609 { 610 return (int) $this->peer_min; 611 } 612 613 /** 614 * Set peer review simple unlock 615 * 616 * @param bool $a_value 617 */ 618 public function setPeerReviewSimpleUnlock($a_value) 619 { 620 $this->peer_unlock = (bool) $a_value; 621 } 622 623 /** 624 * Get peer review simple unlock 625 * 626 * @return bool 627 */ 628 public function getPeerReviewSimpleUnlock() 629 { 630 return (bool) $this->peer_unlock; 631 } 632 633 /** 634 * Set peer review deadline (timestamp) 635 * 636 * @param int deadline (timestamp) 637 */ 638 public function setPeerReviewDeadline($a_val) 639 { 640 $this->peer_dl = $a_val; 641 } 642 643 /** 644 * Get peer review deadline (timestamp) 645 * 646 * @return int deadline (timestamp) 647 */ 648 public function getPeerReviewDeadline() 649 { 650 return $this->peer_dl; 651 } 652 653 /** 654 * Set peer review validation 655 * 656 * @param int $a_value 657 */ 658 public function setPeerReviewValid($a_value) 659 { 660 $this->peer_valid = (int) $a_value; 661 } 662 663 /** 664 * Get peer review validatiob 665 * 666 * @return int 667 */ 668 public function getPeerReviewValid() 669 { 670 return (int) $this->peer_valid; 671 } 672 673 /** 674 * Set peer review rating 675 * 676 * @param bool 677 */ 678 public function setPeerReviewRating($a_val) 679 { 680 $this->peer_rating = (bool) $a_val; 681 } 682 683 /** 684 * Get peer review rating status 685 * 686 * @return bool 687 */ 688 public function hasPeerReviewRating() 689 { 690 return $this->peer_rating; 691 } 692 693 /** 694 * Set peer review text 695 * 696 * @param bool 697 */ 698 public function setPeerReviewText($a_val) 699 { 700 $this->peer_text = (bool) $a_val; 701 } 702 703 /** 704 * Get peer review text status 705 * 706 * @return bool 707 */ 708 public function hasPeerReviewText() 709 { 710 return $this->peer_text; 711 } 712 713 /** 714 * Set peer review file upload 715 * 716 * @param bool 717 */ 718 public function setPeerReviewFileUpload($a_val) 719 { 720 $this->peer_file = (bool) $a_val; 721 } 722 723 /** 724 * Get peer review file upload status 725 * 726 * @return bool 727 */ 728 public function hasPeerReviewFileUpload() 729 { 730 return $this->peer_file; 731 } 732 733 /** 734 * Set peer review personalized 735 * 736 * @param bool 737 */ 738 public function setPeerReviewPersonalized($a_val) 739 { 740 $this->peer_personal = (bool) $a_val; 741 } 742 743 /** 744 * Get peer review personalized status 745 * 746 * @return bool 747 */ 748 public function hasPeerReviewPersonalized() 749 { 750 return $this->peer_personal; 751 } 752 753 /** 754 * Set peer review minimum characters 755 * 756 * @param int $a_value 757 */ 758 public function setPeerReviewChars($a_value) 759 { 760 $a_value = (is_numeric($a_value) && (int) $a_value > 0) 761 ? (int) $a_value 762 : null; 763 $this->peer_char = $a_value; 764 } 765 766 /** 767 * Get peer review minimum characters 768 * 769 * @return int 770 */ 771 public function getPeerReviewChars() 772 { 773 return $this->peer_char; 774 } 775 776 /** 777 * Set peer review criteria catalogue id 778 * 779 * @param int $a_value 780 */ 781 public function setPeerReviewCriteriaCatalogue($a_value) 782 { 783 $a_value = is_numeric($a_value) 784 ? (int) $a_value 785 : null; 786 $this->crit_cat = $a_value; 787 } 788 789 /** 790 * Get peer review criteria catalogue id 791 * 792 * @return int 793 */ 794 public function getPeerReviewCriteriaCatalogue() 795 { 796 return $this->crit_cat; 797 } 798 799 public function getPeerReviewCriteriaCatalogueItems() 800 { 801 if ($this->crit_cat) { 802 return ilExcCriteria::getInstancesByParentId($this->crit_cat); 803 } else { 804 $res = array(); 805 806 if ($this->peer_rating) { 807 $res[] = ilExcCriteria::getInstanceByType("rating"); 808 } 809 810 if ($this->peer_text) { 811 $crit = ilExcCriteria::getInstanceByType("text"); 812 if ($this->peer_char) { 813 $crit->setMinChars($this->peer_char); 814 } 815 $res[] = $crit; 816 } 817 818 if ($this->peer_file) { 819 $res[] = ilExcCriteria::getInstanceByType("file"); 820 } 821 822 return $res; 823 } 824 } 825 826 /** 827 * Set (global) feedback file 828 * 829 * @param string $a_value 830 */ 831 public function setFeedbackFile($a_value) 832 { 833 $this->feedback_file = (string) $a_value; 834 } 835 836 /** 837 * Get (global) feedback file 838 * 839 * @return int 840 */ 841 public function getFeedbackFile() 842 { 843 return (string) $this->feedback_file; 844 } 845 846 /** 847 * Toggle (global) feedback file cron 848 * 849 * @param bool $a_value 850 */ 851 public function setFeedbackCron($a_value) 852 { 853 $this->feedback_cron = (string) $a_value; 854 } 855 856 /** 857 * Get (global) feedback file cron status 858 * 859 * @return int 860 */ 861 public function hasFeedbackCron() 862 { 863 return (bool) $this->feedback_cron; 864 } 865 866 /** 867 * Set (global) feedback file availability date 868 * 869 * @param int $a_value 870 */ 871 public function setFeedbackDate($a_value) 872 { 873 $this->feedback_date = (int) $a_value; 874 } 875 876 /** 877 * Get (global) feedback file availability date 878 * 879 * @return int 880 */ 881 public function getFeedbackDate() 882 { 883 return (int) $this->feedback_date; 884 } 885 886 /** 887 * Set (global) feedback file availability using a custom date. 888 * @param int $a_value timestamp 889 */ 890 public function setFeedbackDateCustom($a_value) 891 { 892 $this->feedback_date_custom = $a_value; 893 } 894 895 /** 896 * Get feedback file availability using custom date. 897 * @return string timestamp 898 */ 899 public function getFeedbackDateCustom() 900 { 901 return $this->feedback_date_custom; 902 } 903 904 /** 905 * Set team management by tutor 906 * 907 * @param bool $a_value 908 */ 909 public function setTeamTutor($a_value) 910 { 911 $this->team_tutor = (bool) $a_value; 912 } 913 914 /** 915 * Get team management by tutor 916 * 917 * @return bool 918 */ 919 public function getTeamTutor() 920 { 921 return $this->team_tutor; 922 } 923 924 /** 925 * Set max number of uploads 926 * 927 * @param int $a_value 928 */ 929 public function setMaxFile($a_value) 930 { 931 if ($a_value !== null) { 932 $a_value = (int) $a_value; 933 } 934 $this->max_file = $a_value; 935 } 936 937 /** 938 * Get max number of uploads 939 * 940 * @return bool 941 */ 942 public function getMaxFile() 943 { 944 return $this->max_file; 945 } 946 947 /** 948 * Set portfolio template id 949 * 950 * @param int $a_val 951 */ 952 public function setPortfolioTemplateId($a_val) 953 { 954 $this->portfolio_template = $a_val; 955 } 956 957 /** 958 * Get portfolio template id 959 * 960 * @return int portfolio template id 961 */ 962 public function getPortfolioTemplateId() 963 { 964 return $this->portfolio_template; 965 } 966 967 968 /** 969 * Read from db 970 */ 971 public function read() 972 { 973 $ilDB = $this->db; 974 975 $set = $ilDB->query( 976 "SELECT * FROM exc_assignment " . 977 " WHERE id = " . $ilDB->quote($this->getId(), "integer") 978 ); 979 $rec = $ilDB->fetchAssoc($set); 980 981 // #16172 - might be deleted 982 if (is_array($rec)) { 983 $this->initFromDB($rec); 984 } 985 } 986 987 /** 988 * Import DB record 989 * 990 * @see getInstancesByExercise() 991 * @param array $a_set 992 */ 993 protected function initFromDB(array $a_set) 994 { 995 $this->setId($a_set["id"]); 996 $this->setExerciseId($a_set["exc_id"]); 997 $this->setDeadline($a_set["time_stamp"]); 998 $this->setExtendedDeadline($a_set["deadline2"]); 999 $this->setInstruction($a_set["instruction"]); 1000 $this->setTitle($a_set["title"]); 1001 $this->setStartTime($a_set["start_time"]); 1002 $this->setOrderNr($a_set["order_nr"]); 1003 $this->setMandatory($a_set["mandatory"]); 1004 $this->setType($a_set["type"]); 1005 $this->setPeerReview($a_set["peer"]); 1006 $this->setPeerReviewMin($a_set["peer_min"]); 1007 $this->setPeerReviewSimpleUnlock($a_set["peer_unlock"]); 1008 $this->setPeerReviewDeadline($a_set["peer_dl"]); 1009 $this->setPeerReviewValid($a_set["peer_valid"]); 1010 $this->setPeerReviewFileUpload($a_set["peer_file"]); 1011 $this->setPeerReviewPersonalized($a_set["peer_prsl"]); 1012 $this->setPeerReviewChars($a_set["peer_char"]); 1013 $this->setPeerReviewText($a_set["peer_text"]); 1014 $this->setPeerReviewRating($a_set["peer_rating"]); 1015 $this->setPeerReviewCriteriaCatalogue($a_set["peer_crit_cat"]); 1016 $this->setFeedbackFile($a_set["fb_file"]); 1017 $this->setFeedbackDate($a_set["fb_date"]); 1018 $this->setFeedbackDateCustom($a_set["fb_date_custom"]); 1019 $this->setFeedbackCron($a_set["fb_cron"]); 1020 $this->setTeamTutor($a_set["team_tutor"]); 1021 $this->setMaxFile($a_set["max_file"]); 1022 $this->setPortfolioTemplateId($a_set["portfolio_template"]); 1023 $this->setMinCharLimit($a_set["min_char_limit"]); 1024 $this->setMaxCharLimit($a_set["max_char_limit"]); 1025 $this->setDeadlineMode($a_set["deadline_mode"]); 1026 $this->setRelativeDeadline($a_set["relative_deadline"]); 1027 $this->setRelDeadlineLastSubmission($a_set["rel_deadline_last_subm"]); 1028 } 1029 1030 /** 1031 * Save assignment 1032 */ 1033 public function save() 1034 { 1035 $ilDB = $this->db; 1036 1037 if ($this->getOrderNr() == 0) { 1038 $this->setOrderNr( 1039 self::lookupMaxOrderNrForEx($this->getExerciseId()) 1040 + 10 1041 ); 1042 } 1043 1044 $next_id = $ilDB->nextId("exc_assignment"); 1045 $ilDB->insert("exc_assignment", array( 1046 "id" => array("integer", $next_id), 1047 "exc_id" => array("integer", $this->getExerciseId()), 1048 "time_stamp" => array("integer", $this->getDeadline()), 1049 "deadline2" => array("integer", $this->getExtendedDeadline()), 1050 "instruction" => array("clob", $this->getInstruction()), 1051 "title" => array("text", $this->getTitle()), 1052 "start_time" => array("integer", $this->getStartTime()), 1053 "order_nr" => array("integer", $this->getOrderNr()), 1054 "mandatory" => array("integer", $this->getMandatory()), 1055 "type" => array("integer", $this->getType()), 1056 "peer" => array("integer", $this->getPeerReview()), 1057 "peer_min" => array("integer", $this->getPeerReviewMin()), 1058 "peer_unlock" => array("integer", $this->getPeerReviewSimpleUnlock()), 1059 "peer_dl" => array("integer", $this->getPeerReviewDeadline()), 1060 "peer_valid" => array("integer", $this->getPeerReviewValid()), 1061 "peer_file" => array("integer", $this->hasPeerReviewFileUpload()), 1062 "peer_prsl" => array("integer", $this->hasPeerReviewPersonalized()), 1063 "peer_char" => array("integer", $this->getPeerReviewChars()), 1064 "peer_text" => array("integer", (int) $this->hasPeerReviewText()), 1065 "peer_rating" => array("integer", (int) $this->hasPeerReviewRating()), 1066 "peer_crit_cat" => array("integer", $this->getPeerReviewCriteriaCatalogue()), 1067 "fb_file" => array("text", $this->getFeedbackFile()), 1068 "fb_date" => array("integer", $this->getFeedbackDate()), 1069 "fb_date_custom" => array("integer", $this->getFeedbackDateCustom()), 1070 "fb_cron" => array("integer", $this->hasFeedbackCron()), 1071 "team_tutor" => array("integer", $this->getTeamTutor()), 1072 "max_file" => array("integer", $this->getMaxFile()), 1073 "portfolio_template" => array("integer", $this->getPortFolioTemplateId()), 1074 "min_char_limit" => array("integer", $this->getMinCharLimit()), 1075 "max_char_limit" => array("integer", $this->getMaxCharLimit()), 1076 "relative_deadline" => array("integer", $this->getRelativeDeadline()), 1077 "rel_deadline_last_subm" => array("integer", (int) $this->getRelDeadlineLastSubmission()), 1078 "deadline_mode" => array("integer", $this->getDeadlineMode()) 1079 )); 1080 $this->setId($next_id); 1081 $exc = new ilObjExercise($this->getExerciseId(), false); 1082 $exc->updateAllUsersStatus(); 1083 self::createNewAssignmentRecords($next_id, $exc); 1084 1085 $this->handleCalendarEntries("create"); 1086 } 1087 1088 /** 1089 * Update 1090 */ 1091 public function update() 1092 { 1093 $ilDB = $this->db; 1094 1095 $ilDB->update( 1096 "exc_assignment", 1097 array( 1098 "exc_id" => array("integer", $this->getExerciseId()), 1099 "time_stamp" => array("integer", $this->getDeadline()), 1100 "deadline2" => array("integer", $this->getExtendedDeadline()), 1101 "instruction" => array("clob", $this->getInstruction()), 1102 "title" => array("text", $this->getTitle()), 1103 "start_time" => array("integer", $this->getStartTime()), 1104 "order_nr" => array("integer", $this->getOrderNr()), 1105 "mandatory" => array("integer", $this->getMandatory()), 1106 "type" => array("integer", $this->getType()), 1107 "peer" => array("integer", $this->getPeerReview()), 1108 "peer_min" => array("integer", $this->getPeerReviewMin()), 1109 "peer_unlock" => array("integer", $this->getPeerReviewSimpleUnlock()), 1110 "peer_dl" => array("integer", $this->getPeerReviewDeadline()), 1111 "peer_valid" => array("integer", $this->getPeerReviewValid()), 1112 "peer_file" => array("integer", $this->hasPeerReviewFileUpload()), 1113 "peer_prsl" => array("integer", $this->hasPeerReviewPersonalized()), 1114 "peer_char" => array("integer", $this->getPeerReviewChars()), 1115 "peer_text" => array("integer", (int) $this->hasPeerReviewText()), 1116 "peer_rating" => array("integer", (int) $this->hasPeerReviewRating()), 1117 "peer_crit_cat" => array("integer", $this->getPeerReviewCriteriaCatalogue()), 1118 "fb_file" => array("text", $this->getFeedbackFile()), 1119 "fb_date" => array("integer", $this->getFeedbackDate()), 1120 "fb_date_custom" => array("integer", $this->getFeedbackDateCustom()), 1121 "fb_cron" => array("integer", $this->hasFeedbackCron()), 1122 "team_tutor" => array("integer", $this->getTeamTutor()), 1123 "max_file" => array("integer", $this->getMaxFile()), 1124 "portfolio_template" => array("integer", $this->getPortFolioTemplateId()), 1125 "min_char_limit" => array("integer", $this->getMinCharLimit()), 1126 "max_char_limit" => array("integer", $this->getMaxCharLimit()), 1127 "deadline_mode" => array("integer", $this->getDeadlineMode()), 1128 "relative_deadline" => array("integer", $this->getRelativeDeadline()), 1129 "rel_deadline_last_subm" => array("integer", (int) $this->getRelDeadlineLastSubmission()) 1130 ), 1131 array( 1132 "id" => array("integer", $this->getId()), 1133 ) 1134 ); 1135 $exc = new ilObjExercise($this->getExerciseId(), false); 1136 $exc->updateAllUsersStatus(); 1137 1138 $this->handleCalendarEntries("update"); 1139 } 1140 1141 /** 1142 * Delete assignment 1143 */ 1144 public function delete() 1145 { 1146 $ilDB = $this->db; 1147 1148 $this->deleteGlobalFeedbackFile(); 1149 1150 $ilDB->manipulate( 1151 "DELETE FROM exc_assignment WHERE " . 1152 " id = " . $ilDB->quote($this->getId(), "integer") 1153 ); 1154 $exc = new ilObjExercise($this->getExerciseId(), false); 1155 $exc->updateAllUsersStatus(); 1156 1157 $this->handleCalendarEntries("delete"); 1158 1159 $reminder = new ilExAssignmentReminder(); 1160 $reminder->deleteReminders($this->getId()); 1161 } 1162 1163 1164 /** 1165 * Get assignments data of an exercise in an array 1166 */ 1167 public static function getAssignmentDataOfExercise($a_exc_id) 1168 { 1169 global $DIC; 1170 1171 $ilDB = $DIC->database(); 1172 1173 // should be changed to self::getInstancesByExerciseId() 1174 1175 $set = $ilDB->query("SELECT * FROM exc_assignment " . 1176 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer") . 1177 " ORDER BY order_nr ASC"); 1178 $data = array(); 1179 1180 $order_val = 10; 1181 while ($rec = $ilDB->fetchAssoc($set)) { 1182 $data[] = array( 1183 "id" => $rec["id"], 1184 "exc_id" => $rec["exc_id"], 1185 "deadline" => $rec["time_stamp"], 1186 "deadline2" => $rec["deadline2"], 1187 "instruction" => $rec["instruction"], 1188 "title" => $rec["title"], 1189 "start_time" => $rec["start_time"], 1190 "order_val" => $order_val, 1191 "mandatory" => $rec["mandatory"], 1192 "type" => $rec["type"], 1193 "peer" => $rec["peer"], 1194 "peer_min" => $rec["peer_min"], 1195 "peer_dl" => $rec["peer_dl"], 1196 "peer_file" => $rec["peer_file"], 1197 "peer_prsl" => $rec["peer_prsl"], 1198 "fb_file" => $rec["fb_file"], 1199 "fb_date" => $rec["fb_date"], 1200 "fb_cron" => $rec["fb_cron"], 1201 "deadline_mode" => $rec["deadline_mode"], 1202 "relative_deadline" => $rec["relative_deadline"], 1203 "rel_deadline_last_subm" => $rec["rel_deadline_last_subm"] 1204 ); 1205 $order_val += 10; 1206 } 1207 return $data; 1208 } 1209 1210 /** 1211 * Clone assignments of exercise 1212 * @param 1213 * @return 1214 */ 1215 public static function cloneAssignmentsOfExercise($a_old_exc_id, $a_new_exc_id, array $a_crit_cat_map) 1216 { 1217 $ass_data = self::getInstancesByExercise($a_old_exc_id); 1218 foreach ($ass_data as $d) { 1219 // clone assignment 1220 $new_ass = new ilExAssignment(); 1221 $new_ass->setExerciseId($a_new_exc_id); 1222 $new_ass->setTitle($d->getTitle()); 1223 $new_ass->setDeadline($d->getDeadline()); 1224 $new_ass->setExtendedDeadline($d->getExtendedDeadline()); 1225 $new_ass->setInstruction($d->getInstruction()); 1226 $new_ass->setMandatory($d->getMandatory()); 1227 $new_ass->setOrderNr($d->getOrderNr()); 1228 $new_ass->setStartTime($d->getStartTime()); 1229 $new_ass->setType($d->getType()); 1230 $new_ass->setPeerReview($d->getPeerReview()); 1231 $new_ass->setPeerReviewMin($d->getPeerReviewMin()); 1232 $new_ass->setPeerReviewDeadline($d->getPeerReviewDeadline()); 1233 $new_ass->setPeerReviewFileUpload($d->hasPeerReviewFileUpload()); 1234 $new_ass->setPeerReviewPersonalized($d->hasPeerReviewPersonalized()); 1235 $new_ass->setPeerReviewValid($d->getPeerReviewValid()); 1236 $new_ass->setPeerReviewChars($d->getPeerReviewChars()); 1237 $new_ass->setPeerReviewText($d->hasPeerReviewText()); 1238 $new_ass->setPeerReviewRating($d->hasPeerReviewRating()); 1239 $new_ass->setPeerReviewCriteriaCatalogue($d->getPeerReviewCriteriaCatalogue()); 1240 $new_ass->setPeerReviewSimpleUnlock($d->getPeerReviewSimpleUnlock()); 1241 $new_ass->setFeedbackFile($d->getFeedbackFile()); 1242 $new_ass->setFeedbackDate($d->getFeedbackDate()); 1243 $new_ass->setFeedbackDateCustom($d->getFeedbackDateCustom()); 1244 $new_ass->setFeedbackCron($d->hasFeedbackCron()); // #16295 1245 $new_ass->setTeamTutor($d->getTeamTutor()); 1246 $new_ass->setMaxFile($d->getMaxFile()); 1247 $new_ass->setMinCharLimit($d->getMinCharLimit()); 1248 $new_ass->setMaxCharLimit($d->getMaxCharLimit()); 1249 $new_ass->setPortfolioTemplateId($d->getPortfolioTemplateId()); 1250 $new_ass->setDeadlineMode($d->getDeadlineMode()); 1251 $new_ass->setRelativeDeadline($d->getRelativeDeadline()); 1252 $new_ass->setRelDeadlineLastSubmission($d->getRelDeadlineLastSubmission()); 1253 1254 // criteria catalogue(s) 1255 if ($d->getPeerReviewCriteriaCatalogue() && 1256 array_key_exists($d->getPeerReviewCriteriaCatalogue(), $a_crit_cat_map)) { 1257 $new_ass->setPeerReviewCriteriaCatalogue($a_crit_cat_map[$d->getPeerReviewCriteriaCatalogue()]); 1258 } 1259 1260 $new_ass->save(); 1261 1262 1263 // clone assignment files 1264 $old_web_storage = new ilFSWebStorageExercise($a_old_exc_id, (int) $d->getId()); 1265 $new_web_storage = new ilFSWebStorageExercise($a_new_exc_id, (int) $new_ass->getId()); 1266 $new_web_storage->create(); 1267 if (is_dir($old_web_storage->getPath())) { 1268 ilUtil::rCopy($old_web_storage->getPath(), $new_web_storage->getPath()); 1269 } 1270 $order = $d->getInstructionFilesOrder(); 1271 foreach ($order as $file) { 1272 ilExAssignment::insertFileOrderNr($new_ass->getId(), $file["filename"], $file["order_nr"]); 1273 } 1274 1275 // clone global feedback file 1276 $old_storage = new ilFSStorageExercise($a_old_exc_id, (int) $d->getId()); 1277 $new_storage = new ilFSStorageExercise($a_new_exc_id, (int) $new_ass->getId()); 1278 $new_storage->create(); 1279 if (is_dir($old_storage->getGlobalFeedbackPath())) { 1280 ilUtil::rCopy($old_storage->getGlobalFeedbackPath(), $new_storage->getGlobalFeedbackPath()); 1281 } 1282 1283 // clone reminders 1284 foreach ([ilExAssignmentReminder::SUBMIT_REMINDER, 1285 ilExAssignmentReminder::GRADE_REMINDER, 1286 ilExAssignmentReminder::FEEDBACK_REMINDER] as $rem_type) { 1287 $rmd_sub = new ilExAssignmentReminder($a_old_exc_id, $d->getId(), $rem_type); 1288 if ($rmd_sub->getReminderStatus()) { 1289 $new_rmd_sub = new ilExAssignmentReminder($a_new_exc_id, $new_ass->getId(), $rem_type); 1290 $new_rmd_sub->setReminderStatus($rmd_sub->getReminderStatus()); 1291 $new_rmd_sub->setReminderStart($rmd_sub->getReminderStart()); 1292 $new_rmd_sub->setReminderEnd($rmd_sub->getReminderEnd()); 1293 $new_rmd_sub->setReminderFrequency($rmd_sub->getReminderFrequency()); 1294 $new_rmd_sub->setReminderMailTemplate($rmd_sub->getReminderMailTemplate()); 1295 $new_rmd_sub->save(); 1296 } 1297 } 1298 1299 1300 // type specific properties 1301 $ass_type = $d->getAssignmentType(); 1302 $ass_type->cloneSpecificProperties($d, $new_ass); 1303 } 1304 } 1305 1306 /** 1307 * Get files 1308 */ 1309 public function getFiles() 1310 { 1311 $this->log->debug("getting files from class.ilExAssignment using ilFSWebStorageExercise"); 1312 $storage = new ilFSWebStorageExercise($this->getExerciseId(), $this->getId()); 1313 return $storage->getFiles(); 1314 } 1315 1316 /** 1317 * @param $a_ass_id 1318 * @return array 1319 */ 1320 public function getInstructionFilesOrder() 1321 { 1322 $ilDB = $this->db; 1323 1324 $set = $ilDB->query( 1325 "SELECT filename, order_nr, id FROM exc_ass_file_order " . 1326 " WHERE assignment_id = " . $ilDB->quote($this->getId(), "integer") 1327 ); 1328 1329 $data = array(); 1330 while ($rec = $ilDB->fetchAssoc($set)) { 1331 $data[$rec['filename']] = $rec; 1332 } 1333 1334 return $data; 1335 } 1336 1337 /** 1338 * Select the maximum order nr for an exercise 1339 */ 1340 public static function lookupMaxOrderNrForEx($a_exc_id) 1341 { 1342 global $DIC; 1343 1344 $ilDB = $DIC->database(); 1345 1346 $set = $ilDB->query( 1347 "SELECT MAX(order_nr) mnr FROM exc_assignment " . 1348 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer") 1349 ); 1350 while ($rec = $ilDB->fetchAssoc($set)) { 1351 return (int) $rec["mnr"]; 1352 } 1353 return 0; 1354 } 1355 1356 /** 1357 * Check if assignment is online 1358 * @param int $a_ass_id 1359 * @return bool 1360 */ 1361 public static function lookupAssignmentOnline($a_ass_id) 1362 { 1363 global $DIC; 1364 1365 $ilDB = $DIC->database(); 1366 1367 $query = "SELECT id FROM exc_assignment " . 1368 "WHERE start_time <= " . $ilDB->quote(time(), 'integer') . ' ' . 1369 "AND time_stamp >= " . $ilDB->quote(time(), 'integer') . ' ' . 1370 "AND id = " . $ilDB->quote($a_ass_id, 'integer'); 1371 $res = $ilDB->query($query); 1372 1373 return $res->numRows() ? true : false; 1374 } 1375 1376 /** 1377 * Lookup excercise id for assignment id 1378 * 1379 * @param int $a_ass_id 1380 * @return int 1381 */ 1382 public static function lookupExerciseId($a_ass_id) 1383 { 1384 global $DIC; 1385 1386 $ilDB = $DIC->database(); 1387 1388 $query = "SELECT exc_id FROM exc_assignment " . 1389 "WHERE id = " . $ilDB->quote($a_ass_id, 'integer'); 1390 $res = $ilDB->fetchAssoc($ilDB->query($query)); 1391 1392 return (int) $res["exc_id"]; 1393 } 1394 1395 /** 1396 * Private lookup 1397 */ 1398 private static function lookup($a_id, $a_field) 1399 { 1400 global $DIC; 1401 1402 $ilDB = $DIC->database(); 1403 1404 $set = $ilDB->query( 1405 "SELECT " . $a_field . " FROM exc_assignment " . 1406 " WHERE id = " . $ilDB->quote($a_id, "integer") 1407 ); 1408 1409 $rec = $ilDB->fetchAssoc($set); 1410 1411 return $rec[$a_field]; 1412 } 1413 1414 /** 1415 * Lookup title 1416 */ 1417 public static function lookupTitle($a_id) 1418 { 1419 return self::lookup($a_id, "title"); 1420 } 1421 1422 /** 1423 * Lookup type 1424 */ 1425 public static function lookupType($a_id) 1426 { 1427 return self::lookup($a_id, "type"); 1428 } 1429 1430 /** 1431 * Save ordering of all assignments of an exercise 1432 */ 1433 public static function saveAssOrderOfExercise($a_ex_id, $a_order) 1434 { 1435 global $DIC; 1436 1437 $ilDB = $DIC->database(); 1438 1439 $result_order = array(); 1440 asort($a_order); 1441 $nr = 10; 1442 foreach ($a_order as $k => $v) { 1443 // the check for exc_id is for security reasons. ass ids are unique. 1444 $ilDB->manipulate( 1445 $t = "UPDATE exc_assignment SET " . 1446 " order_nr = " . $ilDB->quote($nr, "integer") . 1447 " WHERE id = " . $ilDB->quote((int) $k, "integer") . 1448 " AND exc_id = " . $ilDB->quote((int) $a_ex_id, "integer") 1449 ); 1450 $nr += 10; 1451 } 1452 } 1453 1454 /** 1455 * Order assignments by deadline date 1456 */ 1457 public static function orderAssByDeadline($a_ex_id) 1458 { 1459 global $DIC; 1460 $ilDB = $DIC->database(); 1461 1462 $set = $ilDB->query( 1463 "SELECT id FROM exc_assignment " . 1464 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") . 1465 " ORDER BY time_stamp ASC" 1466 ); 1467 $nr = 10; 1468 while ($rec = $ilDB->fetchAssoc($set)) { 1469 $ilDB->manipulate( 1470 "UPDATE exc_assignment SET " . 1471 " order_nr = " . $ilDB->quote($nr, "integer") . 1472 " WHERE id = " . $ilDB->quote($rec["id"], "integer") 1473 ); 1474 $nr += 10; 1475 } 1476 } 1477 1478 /** 1479 * Count the number of mandatory assignments 1480 */ 1481 public static function countMandatory($a_ex_id) 1482 { 1483 global $DIC; 1484 1485 $ilDB = $DIC->database(); 1486 1487 $set = $ilDB->query( 1488 "SELECT count(*) cntm FROM exc_assignment " . 1489 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") . 1490 " AND mandatory = " . $ilDB->quote(1, "integer") 1491 ); 1492 $rec = $ilDB->fetchAssoc($set); 1493 return $rec["cntm"]; 1494 } 1495 1496 /** 1497 * Order assignments by deadline date 1498 */ 1499 public static function count($a_ex_id) 1500 { 1501 global $DIC; 1502 1503 $ilDB = $DIC->database(); 1504 1505 $set = $ilDB->query( 1506 "SELECT count(*) cntm FROM exc_assignment " . 1507 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") 1508 ); 1509 $rec = $ilDB->fetchAssoc($set); 1510 return $rec["cntm"]; 1511 } 1512 1513 /** 1514 * Is assignment in exercise? 1515 */ 1516 public static function isInExercise($a_ass_id, $a_ex_id) 1517 { 1518 global $DIC; 1519 1520 $ilDB = $DIC->database(); 1521 1522 $set = $ilDB->query( 1523 "SELECT * FROM exc_assignment " . 1524 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") . 1525 " AND id = " . $ilDB->quote($a_ass_id, "integer") 1526 ); 1527 if ($rec = $ilDB->fetchAssoc($set)) { 1528 return true; 1529 } 1530 return false; 1531 } 1532 1533 /// 1534 /** 1535 * Check whether student has upload new files after tutor has 1536 * set the exercise to another than notgraded. 1537 */ 1538 public static function lookupUpdatedSubmission($ass_id, $member_id) 1539 { 1540 global $DIC; 1541 1542 $ilDB = $DIC->database(); 1543 $lng = $DIC->language(); 1544 1545 // team upload? 1546 $user_ids = self::getTeamMembersByAssignmentId($ass_id, $member_id); 1547 if (!$user_ids) { 1548 $user_ids = array($member_id); 1549 } 1550 1551 $q = "SELECT exc_mem_ass_status.status_time, exc_returned.ts " . 1552 "FROM exc_mem_ass_status, exc_returned " . 1553 "WHERE exc_mem_ass_status.status_time < exc_returned.ts " . 1554 "AND NOT exc_mem_ass_status.status_time IS NULL " . 1555 "AND exc_returned.ass_id = exc_mem_ass_status.ass_id " . 1556 "AND exc_returned.user_id = exc_mem_ass_status.usr_id " . 1557 "AND exc_returned.ass_id=" . $ilDB->quote($ass_id, "integer") . 1558 " AND " . $ilDB->in("exc_returned.user_id", $user_ids, "", "integer"); 1559 1560 $usr_set = $ilDB->query($q); 1561 1562 $array = $ilDB->fetchAssoc($usr_set); 1563 1564 if (count($array) == 0) { 1565 return 0; 1566 } else { 1567 return 1; 1568 } 1569 } 1570 1571 /** 1572 * get member list data 1573 */ 1574 public function getMemberListData() 1575 { 1576 $ilDB = $this->db; 1577 1578 $mem = array(); 1579 1580 // first get list of members from member table 1581 $set = $ilDB->query("SELECT ud.usr_id, ud.lastname, ud.firstname, ud.login" . 1582 " FROM exc_members excm" . 1583 " JOIN usr_data ud ON (ud.usr_id = excm.usr_id)" . 1584 " WHERE excm.obj_id = " . $ilDB->quote($this->getExerciseId(), "integer")); 1585 while ($rec = $ilDB->fetchAssoc($set)) { 1586 $mem[$rec["usr_id"]] = 1587 array( 1588 "name" => $rec["lastname"] . ", " . $rec["firstname"], 1589 "login" => $rec["login"], 1590 "usr_id" => $rec["usr_id"], 1591 "lastname" => $rec["lastname"], 1592 "firstname" => $rec["firstname"] 1593 ); 1594 } 1595 1596 $q = "SELECT * FROM exc_mem_ass_status " . 1597 "WHERE ass_id = " . $ilDB->quote($this->getId(), "integer"); 1598 $set = $ilDB->query($q); 1599 while ($rec = $ilDB->fetchAssoc($set)) { 1600 if (isset($mem[$rec["usr_id"]])) { 1601 $sub = new ilExSubmission($this, $rec["usr_id"]); 1602 1603 $mem[$rec["usr_id"]]["sent_time"] = $rec["sent_time"]; 1604 $mem[$rec["usr_id"]]["submission"] = $sub->getLastSubmission(); 1605 $mem[$rec["usr_id"]]["status_time"] = $rec["status_time"]; 1606 $mem[$rec["usr_id"]]["feedback_time"] = $rec["feedback_time"]; 1607 $mem[$rec["usr_id"]]["notice"] = $rec["notice"]; 1608 $mem[$rec["usr_id"]]["status"] = $rec["status"]; 1609 $mem[$rec["usr_id"]]["mark"] = $rec["mark"]; 1610 $mem[$rec["usr_id"]]["comment"] = $rec["u_comment"]; 1611 } 1612 } 1613 return $mem; 1614 } 1615 1616 /** 1617 * Get submission data for an specific user,exercise and assignment. 1618 * todo we can refactor a bit the method getMemberListData to use this and remove duplicate code. 1619 * @param $a_user_id 1620 * @param $a_grade 1621 * @return array 1622 */ 1623 public function getExerciseMemberAssignmentData($a_user_id, $a_grade = "") 1624 { 1625 global $DIC; 1626 $ilDB = $DIC->database(); 1627 1628 if (in_array($a_grade, array("notgraded", "passed", "failed"))) { 1629 $and_grade = " AND status = " . $ilDB->quote($a_grade, "text"); 1630 } 1631 1632 $q = "SELECT * FROM exc_mem_ass_status " . 1633 "WHERE ass_id = " . $ilDB->quote($this->getId(), "integer") . 1634 " AND usr_id = " . $ilDB->quote($a_user_id, "integer") . 1635 $and_grade; 1636 1637 $set = $ilDB->query($q); 1638 1639 while ($rec = $ilDB->fetchAssoc($set)) { 1640 $sub = new ilExSubmission($this, $a_user_id); 1641 1642 $data["sent_time"] = $rec["sent_time"]; 1643 $data["submission"] = $sub->getLastSubmission(); 1644 $data["status_time"] = $rec["status_time"]; 1645 $data["feedback_time"] = $rec["feedback_time"]; 1646 $data["notice"] = $rec["notice"]; 1647 $data["status"] = $rec["status"]; 1648 $data["mark"] = $rec["mark"]; 1649 $data["comment"] = $rec["u_comment"]; 1650 } 1651 1652 return $data; 1653 } 1654 1655 /** 1656 * Create member status record for a new participant for all assignments 1657 */ 1658 public static function createNewUserRecords($a_user_id, $a_exc_id) 1659 { 1660 global $DIC; 1661 1662 $ilDB = $DIC->database(); 1663 1664 $ass_data = self::getAssignmentDataOfExercise($a_exc_id); 1665 foreach ($ass_data as $ass) { 1666 //echo "-".$ass["id"]."-".$a_user_id."-"; 1667 $ilDB->replace("exc_mem_ass_status", array( 1668 "ass_id" => array("integer", $ass["id"]), 1669 "usr_id" => array("integer", $a_user_id) 1670 ), array( 1671 "status" => array("text", "notgraded") 1672 )); 1673 } 1674 } 1675 1676 /** 1677 * Create member status record for a new assignment for all participants 1678 */ 1679 public static function createNewAssignmentRecords($a_ass_id, $a_exc) 1680 { 1681 global $DIC; 1682 1683 $ilDB = $DIC->database(); 1684 1685 $exmem = new ilExerciseMembers($a_exc); 1686 $mems = $exmem->getMembers(); 1687 1688 foreach ($mems as $mem) { 1689 $ilDB->replace("exc_mem_ass_status", array( 1690 "ass_id" => array("integer", $a_ass_id), 1691 "usr_id" => array("integer", $mem) 1692 ), array( 1693 "status" => array("text", "notgraded") 1694 )); 1695 } 1696 } 1697 1698 /** 1699 * Upload assignment files 1700 * (from creation form) 1701 */ 1702 public function uploadAssignmentFiles($a_files) 1703 { 1704 ilLoggerFactory::getLogger("exc")->debug("upload assignment files files = ", $a_files); 1705 $storage = new ilFSWebStorageExercise($this->getExerciseId(), $this->getId()); 1706 $storage->create(); 1707 $storage->uploadAssignmentFiles($a_files); 1708 } 1709 1710 1711 //// 1712 //// Multi-Feedback 1713 //// 1714 1715 /** 1716 * Create member status record for a new assignment for all participants 1717 */ 1718 public function sendMultiFeedbackStructureFile(ilObjExercise $exercise) 1719 { 1720 global $DIC; 1721 1722 1723 // send and delete the zip file 1724 $deliverFilename = trim(str_replace(" ", "_", $this->getTitle() . "_" . $this->getId())); 1725 $deliverFilename = ilUtil::getASCIIFilename($deliverFilename); 1726 $deliverFilename = "multi_feedback_" . $deliverFilename; 1727 1728 $exc = new ilObjExercise($this->getExerciseId(), false); 1729 1730 $cdir = getcwd(); 1731 1732 // create temporary directoy 1733 $tmpdir = ilUtil::ilTempnam(); 1734 ilUtil::makeDir($tmpdir); 1735 $mfdir = $tmpdir . "/" . $deliverFilename; 1736 ilUtil::makeDir($mfdir); 1737 1738 // create subfolders <lastname>_<firstname>_<id> for each participant 1739 $exmem = new ilExerciseMembers($exc); 1740 $mems = $exmem->getMembers(); 1741 1742 $mems = $DIC->access()->filterUserIdsByRbacOrPositionOfCurrentUser( 1743 'edit_submissions_grades', 1744 'edit_submissions_grades', 1745 $exercise->getRefId(), 1746 $mems 1747 ); 1748 foreach ($mems as $mem) { 1749 $name = ilObjUser::_lookupName($mem); 1750 $subdir = $name["lastname"] . "_" . $name["firstname"] . "_" . $name["login"] . "_" . $name["user_id"]; 1751 $subdir = ilUtil::getASCIIFilename($subdir); 1752 ilUtil::makeDir($mfdir . "/" . $subdir); 1753 } 1754 1755 // create the zip file 1756 chdir($tmpdir); 1757 $tmpzipfile = $tmpdir . "/multi_feedback.zip"; 1758 ilUtil::zip($tmpdir, $tmpzipfile, true); 1759 chdir($cdir); 1760 1761 1762 ilUtil::deliverFile($tmpzipfile, $deliverFilename . ".zip", "", false, true); 1763 } 1764 1765 /** 1766 * Upload multi feedback file 1767 * 1768 * @param array 1769 * @return 1770 */ 1771 public function uploadMultiFeedbackFile($a_file) 1772 { 1773 $lng = $this->lng; 1774 $ilUser = $this->user; 1775 1776 if (!is_file($a_file["tmp_name"])) { 1777 throw new ilExerciseException($lng->txt("exc_feedback_file_could_not_be_uploaded")); 1778 } 1779 1780 $storage = new ilFSStorageExercise($this->getExerciseId(), $this->getId()); 1781 $mfu = $storage->getMultiFeedbackUploadPath($ilUser->getId()); 1782 ilUtil::delDir($mfu, true); 1783 ilUtil::moveUploadedFile($a_file["tmp_name"], "multi_feedback.zip", $mfu . "/" . "multi_feedback.zip"); 1784 ilUtil::unzip($mfu . "/multi_feedback.zip", true); 1785 $subdirs = ilUtil::getDir($mfu); 1786 $subdir = "notfound"; 1787 foreach ($subdirs as $s => $j) { 1788 if ($j["type"] == "dir" && substr($s, 0, 14) == "multi_feedback") { 1789 $subdir = $s; 1790 } 1791 } 1792 1793 if (!is_dir($mfu . "/" . $subdir)) { 1794 throw new ilExerciseException($lng->txt("exc_no_feedback_dir_found_in_zip")); 1795 } 1796 1797 return true; 1798 } 1799 1800 /** 1801 * Get multi feedback files (of uploader) 1802 * 1803 * @param int $a_user_id user id of uploader 1804 * @return array array of user files (keys: lastname, firstname, user_id, login, file) 1805 */ 1806 public function getMultiFeedbackFiles($a_user_id = 0) 1807 { 1808 $ilUser = $this->user; 1809 1810 if ($a_user_id == 0) { 1811 $a_user_id = $ilUser->getId(); 1812 } 1813 1814 $mf_files = array(); 1815 1816 // get members 1817 $exc = new ilObjExercise($this->getExerciseId(), false); 1818 $exmem = new ilExerciseMembers($exc); 1819 $mems = $exmem->getMembers(); 1820 1821 // read mf directory 1822 $storage = new ilFSStorageExercise($this->getExerciseId(), $this->getId()); 1823 $mfu = $storage->getMultiFeedbackUploadPath($ilUser->getId()); 1824 1825 // get subdir that starts with multi_feedback 1826 $subdirs = ilUtil::getDir($mfu); 1827 $subdir = "notfound"; 1828 foreach ($subdirs as $s => $j) { 1829 if ($j["type"] == "dir" && substr($s, 0, 14) == "multi_feedback") { 1830 $subdir = $s; 1831 } 1832 } 1833 1834 $items = ilUtil::getDir($mfu . "/" . $subdir); 1835 foreach ($items as $k => $i) { 1836 // check directory 1837 if ($i["type"] == "dir" && !in_array($k, array(".", ".."))) { 1838 // check if valid member id is given 1839 $parts = explode("_", $i["entry"]); 1840 $user_id = (int) $parts[count($parts) - 1]; 1841 if (in_array($user_id, $mems)) { 1842 // read dir of user 1843 $name = ilObjUser::_lookupName($user_id); 1844 $files = ilUtil::getDir($mfu . "/" . $subdir . "/" . $k); 1845 foreach ($files as $k2 => $f) { 1846 // append files to array 1847 if ($f["type"] == "file" && substr($k2, 0, 1) != ".") { 1848 $mf_files[] = array( 1849 "lastname" => $name["lastname"], 1850 "firstname" => $name["firstname"], 1851 "login" => $name["login"], 1852 "user_id" => $name["user_id"], 1853 "full_path" => $mfu . "/" . $subdir . "/" . $k . "/" . $k2, 1854 "file" => $k2); 1855 } 1856 } 1857 } 1858 } 1859 } 1860 return $mf_files; 1861 } 1862 1863 /** 1864 * Clear multi feedback directory 1865 * 1866 * @param array 1867 * @return 1868 */ 1869 public function clearMultiFeedbackDirectory() 1870 { 1871 $lng = $this->lng; 1872 $ilUser = $this->user; 1873 1874 $storage = new ilFSStorageExercise($this->getExerciseId(), $this->getId()); 1875 $mfu = $storage->getMultiFeedbackUploadPath($ilUser->getId()); 1876 ilUtil::delDir($mfu); 1877 } 1878 1879 /** 1880 * Save multi feedback files 1881 * 1882 * @param 1883 * @return 1884 */ 1885 public function saveMultiFeedbackFiles($a_files, ilObjExercise $a_exc) 1886 { 1887 if ($this->getExerciseId() != $a_exc->getId()) { 1888 return; 1889 } 1890 1891 $fstorage = new ilFSStorageExercise($this->getExerciseId(), $this->getId()); 1892 $fstorage->create(); 1893 1894 $team_map = array(); 1895 1896 $mf_files = $this->getMultiFeedbackFiles(); 1897 foreach ($mf_files as $f) { 1898 $user_id = $f["user_id"]; 1899 $file_path = $f["full_path"]; 1900 $file_name = $f["file"]; 1901 1902 // if checked in confirmation gui 1903 if ($a_files[$user_id][md5($file_name)] != "") { 1904 $submission = new ilExSubmission($this, $user_id); 1905 $feedback_id = $submission->getFeedbackId(); 1906 $noti_rec_ids = $submission->getUserIds(); 1907 1908 if ($feedback_id) { 1909 $fb_path = $fstorage->getFeedbackPath($feedback_id); 1910 $target = $fb_path . "/" . $file_name; 1911 if (is_file($target)) { 1912 unlink($target); 1913 } 1914 // rename file 1915 rename($file_path, $target); 1916 1917 if ($noti_rec_ids) { 1918 foreach ($noti_rec_ids as $user_id) { 1919 $member_status = $this->getMemberStatus($user_id); 1920 $member_status->setFeedback(true); 1921 $member_status->update(); 1922 } 1923 1924 $a_exc->sendFeedbackFileNotification( 1925 $file_name, 1926 $noti_rec_ids, 1927 (int) $this->getId() 1928 ); 1929 } 1930 } 1931 } 1932 } 1933 1934 $this->clearMultiFeedbackDirectory(); 1935 } 1936 1937 1938 1939 1940 /** 1941 * Handle calendar entries for deadline(s) 1942 * 1943 * @param string $a_event 1944 */ 1945 protected function handleCalendarEntries($a_event) 1946 { 1947 $ilAppEventHandler = $this->app_event_handler; 1948 1949 $dl_id = $this->getId() . "0"; 1950 $fbdl_id = $this->getId() . "1"; 1951 1952 $context_ids = array($dl_id, $fbdl_id); 1953 $apps = array(); 1954 1955 if ($a_event != "delete") { 1956 // deadline or relative deadline given 1957 if ($this->getDeadline() || $this->getDeadlineMode() == ilExAssignment::DEADLINE_RELATIVE) { 1958 $app = new ilCalendarAppointmentTemplate($dl_id); 1959 $app->setTranslationType(IL_CAL_TRANSLATION_SYSTEM); 1960 $app->setSubtitle("cal_exc_deadline"); 1961 $app->setTitle($this->getTitle()); 1962 $app->setFullday(false); 1963 // note: in the case of a relative deadline this will be set to 0 / 1970...) 1964 // see ilCalendarScheduleFilterExercise for appointment modification 1965 $app->setStart(new ilDateTime($this->getDeadline(), IL_CAL_UNIX)); 1966 1967 $apps[] = $app; 1968 } 1969 1970 if ($this->getPeerReview() && 1971 $this->getPeerReviewDeadline()) { 1972 $app = new ilCalendarAppointmentTemplate($fbdl_id); 1973 $app->setTranslationType(IL_CAL_TRANSLATION_SYSTEM); 1974 $app->setSubtitle("cal_exc_peer_review_deadline"); 1975 $app->setTitle($this->getTitle()); 1976 $app->setFullday(false); 1977 $app->setStart(new ilDateTime($this->getPeerReviewDeadline(), IL_CAL_UNIX)); 1978 1979 $apps[] = $app; 1980 } 1981 } 1982 1983 $exc = new ilObjExercise($this->getExerciseId(), false); 1984 1985 $ilAppEventHandler->raise( 1986 'Modules/Exercise', 1987 $a_event . 'Assignment', 1988 array( 1989 'object' => $exc, 1990 'obj_id' => $exc->getId(), 1991 'context_ids' => $context_ids, 1992 'appointments' => $apps) 1993 ); 1994 } 1995 1996 1997 public static function getPendingFeedbackNotifications() 1998 { 1999 global $DIC; 2000 2001 $ilDB = $DIC->database(); 2002 2003 $res = array(); 2004 2005 $set = $ilDB->query("SELECT id,fb_file,time_stamp,deadline2,fb_date FROM exc_assignment" . 2006 " WHERE fb_cron = " . $ilDB->quote(1, "integer") . 2007 " AND (fb_date = " . $ilDB->quote(self::FEEDBACK_DATE_DEADLINE, "integer") . 2008 " AND time_stamp IS NOT NULL" . 2009 " AND time_stamp > " . $ilDB->quote(0, "integer") . 2010 " AND time_stamp < " . $ilDB->quote(time(), "integer") . 2011 " AND fb_cron_done = " . $ilDB->quote(0, "integer") . 2012 ") OR (fb_date = " . $ilDB->quote(self::FEEDBACK_DATE_CUSTOM, "integer") . 2013 " AND fb_date_custom IS NOT NULL" . 2014 " AND fb_date_custom > " . $ilDB->quote(0, "integer") . 2015 " AND fb_date_custom < " . $ilDB->quote(time(), "integer") . 2016 " AND fb_cron_done = " . $ilDB->quote(0, "integer") . ")"); 2017 2018 2019 2020 while ($row = $ilDB->fetchAssoc($set)) { 2021 if ($row['fb_date'] == self::FEEDBACK_DATE_DEADLINE) { 2022 $max = max($row['time_stamp'], $row['deadline2']); 2023 if (trim($row["fb_file"]) && $max <= time()) { 2024 $res[] = $row["id"]; 2025 } 2026 } elseif ($row['fb_date'] == self::FEEDBACK_DATE_CUSTOM) { 2027 if (trim($row["fb_file"]) && $row['fb_date_custom'] <= time()) { 2028 $res[] = $row["id"]; 2029 } 2030 } 2031 } 2032 2033 return $res; 2034 } 2035 2036 public static function sendFeedbackNotifications($a_ass_id, $a_user_id = null) 2037 { 2038 global $DIC; 2039 2040 $ilDB = $DIC->database(); 2041 2042 $ass = new self($a_ass_id); 2043 2044 // valid assignment? 2045 if (!$ass->hasFeedbackCron() || !$ass->getFeedbackFile()) { 2046 return false; 2047 } 2048 2049 if (!$a_user_id) { 2050 // already done? 2051 $set = $ilDB->query("SELECT fb_cron_done" . 2052 " FROM exc_assignment" . 2053 " WHERE id = " . $ilDB->quote($a_ass_id, "integer")); 2054 $row = $ilDB->fetchAssoc($set); 2055 if ($row["fb_cron_done"]) { 2056 return false; 2057 } 2058 } 2059 2060 $ntf = new ilSystemNotification(); 2061 $ntf->setLangModules(array("exc")); 2062 $ntf->setObjId($ass->getExerciseId()); 2063 $ntf->setSubjectLangId("exc_feedback_notification_subject"); 2064 $ntf->setIntroductionLangId("exc_feedback_notification_body"); 2065 $ntf->addAdditionalInfo("exc_assignment", $ass->getTitle()); 2066 $ntf->setGotoLangId("exc_feedback_notification_link"); 2067 $ntf->setReasonLangId("exc_feedback_notification_reason"); 2068 2069 if (!$a_user_id) { 2070 $ntf->sendMail(ilExerciseMembers::_getMembers($ass->getExerciseId())); 2071 2072 $ilDB->manipulate("UPDATE exc_assignment" . 2073 " SET fb_cron_done = " . $ilDB->quote(1, "integer") . 2074 " WHERE id = " . $ilDB->quote($a_ass_id, "integer")); 2075 } else { 2076 $ntf->sendMail(array($a_user_id)); 2077 } 2078 2079 return true; 2080 } 2081 2082 2083 // status 2084 2085 public function afterDeadline() // like: after effective deadline (for single user), no deadline: true 2086 { 2087 $ilUser = $this->user; 2088 2089 // :TODO: always current user? 2090 $idl = $this->getPersonalDeadline($ilUser->getId()); // official deadline 2091 2092 // no deadline === true 2093 $deadline = max($this->deadline, $this->deadline2, $idl); // includes grace period 2094 return ($deadline - time() <= 0); 2095 } 2096 2097 public function afterDeadlineStrict($a_include_personal = true) 2098 { 2099 // :TODO: this means that peer feedback, global feedback is available 2100 // after LAST personal deadline 2101 // team management is currently ignoring personal deadlines 2102 $idl = (bool) $a_include_personal 2103 ? $this->getLastPersonalDeadline() 2104 : null; 2105 2106 // no deadline === false 2107 $deadline = max($this->deadline, $this->deadline2, $idl); 2108 2109 // #18271 - afterDeadline() does not handle last personal deadline 2110 if ($idl && $deadline == $idl) { // after effective deadline of all users 2111 return ($deadline - time() <= 0); 2112 } 2113 2114 return ($deadline > 0 && // like: after effective deadline (for single user), except: no deadline false 2115 $this->afterDeadline()); 2116 } 2117 2118 /** 2119 * @return bool return if sample solution is available using a custom date. 2120 */ 2121 public function afterCustomDate() 2122 { 2123 $date_custom = $this->getFeedbackDateCustom(); 2124 //if the solution will be displayed only after reach all the deadlines. 2125 //$final_deadline = $this->afterDeadlineStrict(); 2126 //$dl = max($final_deadline, time()); 2127 //return ($date_custom - $dl <= 0); 2128 return ($date_custom - time() <= 0); 2129 } 2130 2131 public function beforeDeadline() // like: before effective deadline (for all users), no deadline: true 2132 { 2133 // no deadline === true 2134 return !$this->afterDeadlineStrict(); 2135 } 2136 2137 public function notStartedYet() 2138 { 2139 return (time() - $this->start_time <= 0); 2140 } 2141 2142 2143 // 2144 // FEEDBACK FILES 2145 // 2146 2147 public function getGlobalFeedbackFileStoragePath() 2148 { 2149 $storage = new ilFSStorageExercise($this->getExerciseId(), $this->getId()); 2150 return $storage->getGlobalFeedbackPath(); 2151 } 2152 2153 public function deleteGlobalFeedbackFile() 2154 { 2155 ilUtil::delDir($this->getGlobalFeedbackFileStoragePath()); 2156 } 2157 2158 public function handleGlobalFeedbackFileUpload(array $a_file) 2159 { 2160 $path = $this->getGlobalFeedbackFileStoragePath(); 2161 ilUtil::delDir($path, true); 2162 if (ilUtil::moveUploadedFile($a_file["tmp_name"], $a_file["name"], $path . "/" . $a_file["name"])) { 2163 $this->setFeedbackFile($a_file["name"]); 2164 return true; 2165 } 2166 return false; 2167 } 2168 2169 public function getGlobalFeedbackFilePath() 2170 { 2171 $file = $this->getFeedbackFile(); 2172 if ($file) { 2173 $path = $this->getGlobalFeedbackFileStoragePath(); 2174 return $path . "/" . $file; 2175 } 2176 } 2177 2178 /** 2179 * @param int|null $a_user_id 2180 * @return \ilExAssignmentMemberStatus 2181 */ 2182 public function getMemberStatus($a_user_id = null) 2183 { 2184 $ilUser = $this->user; 2185 2186 if (!$a_user_id) { 2187 $a_user_id = $ilUser->getId(); 2188 } 2189 if (!array_key_exists($a_user_id, $this->member_status)) { 2190 $this->member_status[$a_user_id] = new ilExAssignmentMemberStatus($this->getId(), $a_user_id); 2191 } 2192 return $this->member_status[$a_user_id]; 2193 } 2194 2195 public function recalculateLateSubmissions() 2196 { 2197 $ilDB = $this->db; 2198 2199 // see JF, 2015-05-11 2200 2201 $ext_deadline = $this->getExtendedDeadline(); 2202 2203 foreach (ilExSubmission::getAllAssignmentFiles($this->exc_id, $this->getId()) as $file) { 2204 $id = $file["returned_id"]; 2205 $uploaded = new ilDateTime($file["ts"], IL_CAL_DATETIME); 2206 $uploaded = $uploaded->get(IL_CAL_UNIX); 2207 2208 $deadline = $this->getPersonalDeadline($file["user_id"]); 2209 $last_deadline = max($deadline, $this->getExtendedDeadline()); 2210 2211 $late = null; 2212 2213 // upload is not late anymore 2214 if ($file["late"] && 2215 (!$last_deadline || 2216 !$ext_deadline || 2217 $uploaded < $deadline)) { 2218 $late = false; 2219 } 2220 // upload is now late 2221 elseif (!$file["late"] && 2222 $ext_deadline && 2223 $deadline && 2224 $uploaded > $deadline) { 2225 $late = true; 2226 } elseif ($last_deadline && $uploaded > $last_deadline) { 2227 // do nothing, we do not remove submissions? 2228 } 2229 2230 if ($late !== null) { 2231 $ilDB->manipulate("UPDATE exc_returned" . 2232 " SET late = " . $ilDB->quote($late, "integer") . 2233 " WHERE returned_id = " . $ilDB->quote($id, "integer")); 2234 } 2235 } 2236 } 2237 2238 2239 // 2240 // individual deadlines 2241 // 2242 2243 public function setIndividualDeadline($id, ilDateTime $date) 2244 { 2245 $ilDB = $this->db; 2246 2247 $is_team = false; 2248 if (!is_numeric($id)) { 2249 $id = substr($id, 1); 2250 $is_team = true; 2251 } 2252 2253 $idl = ilExcIndividualDeadline::getInstance($this->getId(), $id, $is_team); 2254 $idl->setIndividualDeadline($date->get(IL_CAL_UNIX)); 2255 $idl->save(); 2256 2257 /* 2258 $ilDB->replace("exc_idl", 2259 array( 2260 "ass_id" => array("integer", $this->getId()), 2261 "member_id" => array("integer", $id), 2262 "is_team" => array("integer", $is_team) 2263 ), 2264 array( 2265 "tstamp" => array("integer", $date->get(IL_CAL_UNIX)) 2266 ) 2267 );*/ 2268 } 2269 2270 public function getIndividualDeadlines() 2271 { 2272 $ilDB = $this->db; 2273 2274 $res = array(); 2275 2276 $set = $ilDB->query("SELECT * FROM exc_idl" . 2277 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer")); 2278 while ($row = $ilDB->fetchAssoc($set)) { 2279 if ($row["is_team"]) { 2280 $row["member_id"] = "t" . $row["member_id"]; 2281 } 2282 2283 $res[$row["member_id"]] = $row["tstamp"]; 2284 } 2285 2286 return $res; 2287 } 2288 2289 public function hasActiveIDl() 2290 { 2291 return (bool) $this->getDeadline(); 2292 } 2293 2294 public function hasReadOnlyIDl() 2295 { 2296 if (!$this->ass_type->usesTeams() && 2297 $this->getPeerReview()) { 2298 // all deadlines are read-only if we have peer feedback 2299 $peer_review = new ilExPeerReview($this); 2300 if ($peer_review->hasPeerReviewGroups()) { 2301 return true; 2302 } 2303 } 2304 2305 return false; 2306 } 2307 2308 /** 2309 * Save ordering of instruction files for an assignment 2310 * @param int $a_ass_id assignment id 2311 * @param int $a_order order 2312 */ 2313 public static function saveInstructionFilesOrderOfAssignment($a_ass_id, $a_order) 2314 { 2315 global $DIC; 2316 2317 $db = $DIC->database(); 2318 2319 asort($a_order, SORT_NUMERIC); 2320 2321 $nr = 10; 2322 foreach ($a_order as $k => $v) { 2323 // the check for exc_id is for security reasons. ass ids are unique. 2324 $db->manipulate( 2325 $t = "UPDATE exc_ass_file_order SET " . 2326 " order_nr = " . $db->quote($nr, "integer") . 2327 " WHERE id = " . $db->quote((int) $k, "integer") . 2328 " AND assignment_id = " . $db->quote((int) $a_ass_id, "integer") 2329 ); 2330 $nr += 10; 2331 } 2332 } 2333 2334 public static function insertFileOrderNr(int $a_ass_id, string $a_filename, int $a_order_nr) 2335 { 2336 global $DIC; 2337 $db = $DIC->database(); 2338 $id = $db->nextId("exc_ass_file_order"); 2339 $db->insert("exc_ass_file_order", [ 2340 "id" => ["integer", $id], 2341 "order_nr" => ["integer", $a_order_nr], 2342 "assignment_id" => ["integer", $a_ass_id], 2343 "filename" => ["text", $a_filename] 2344 ] 2345 ); 2346 } 2347 2348 /** 2349 * Store the file order in the database 2350 * @param string $a_filename previously sanitized. 2351 * @param int $a_ass_id assignment id. 2352 */ 2353 public static function instructionFileInsertOrder($a_filename, $a_ass_id, $a_order_nr = 0) 2354 { 2355 global $DIC; 2356 2357 $db = $DIC->database(); 2358 2359 $order = 0; 2360 $order_val = 0; 2361 2362 if ($a_ass_id) { 2363 //first of all check the suffix and change if necessary 2364 $filename = ilUtil::getSafeFilename($a_filename); 2365 2366 if (!self::instructionFileExistsInDb($filename, $a_ass_id)) { 2367 if ($a_order_nr == 0) { 2368 $order_val = self::instructionFileOrderGetMax($a_ass_id); 2369 $order = $order_val + 10; 2370 } else { 2371 $order = $a_order_nr; 2372 } 2373 2374 $id = $db->nextID('exc_ass_file_order'); 2375 $db->manipulate("INSERT INTO exc_ass_file_order " . 2376 "(id, assignment_id, filename, order_nr) VALUES (" . 2377 $db->quote($id, "integer") . "," . 2378 $db->quote($a_ass_id, "integer") . "," . 2379 $db->quote($filename, "text") . "," . 2380 $db->quote($order, "integer") . 2381 ")"); 2382 } 2383 } 2384 } 2385 2386 public static function instructionFileDeleteOrder($a_ass_id, $a_file) 2387 { 2388 global $DIC; 2389 2390 $db = $DIC->database(); 2391 2392 //now its done by filename. We need to figure how to get the order id in the confirmdelete method 2393 foreach ($a_file as $k => $v) { 2394 $db->manipulate( 2395 "DELETE FROM exc_ass_file_order " . 2396 //"WHERE id = " . $ilDB->quote((int)$k, "integer") . 2397 "WHERE filename = " . $db->quote($v, "string") . 2398 " AND assignment_id = " . $db->quote($a_ass_id, 'integer') 2399 ); 2400 } 2401 } 2402 2403 /** 2404 * @param string $a_old_name 2405 * @param string $a_new_name 2406 * @param int $a_ass_id assignment id 2407 */ 2408 public static function renameInstructionFile($a_old_name, $a_new_name, $a_ass_id) 2409 { 2410 global $DIC; 2411 2412 $db = $DIC->database(); 2413 2414 if ($a_ass_id) { 2415 $db->manipulate( 2416 "DELETE FROM exc_ass_file_order" . 2417 " WHERE assignment_id = " . $db->quote((int) $a_ass_id, 'integer') . 2418 " AND filename = " . $db->quote($a_new_name, 'string') 2419 ); 2420 2421 $db->manipulate( 2422 "UPDATE exc_ass_file_order SET" . 2423 " filename = " . $db->quote($a_new_name, 'string') . 2424 " WHERE assignment_id = " . $db->quote((int) $a_ass_id, 'integer') . 2425 " AND filename = " . $db->quote($a_old_name, 'string') 2426 ); 2427 } 2428 } 2429 2430 /** 2431 * @param $a_filename 2432 * @param $a_ass_id assignment id 2433 * @return int if the file exists or not in the DB 2434 */ 2435 public static function instructionFileExistsInDb($a_filename, $a_ass_id) 2436 { 2437 global $DIC; 2438 2439 $db = $DIC->database(); 2440 2441 if ($a_ass_id) { 2442 $result = $db->query( 2443 "SELECT id FROM exc_ass_file_order" . 2444 " WHERE assignment_id = " . $db->quote((int) $a_ass_id, 'integer') . 2445 " AND filename = " . $db->quote($a_filename, 'string') 2446 ); 2447 2448 return $db->numRows($result); 2449 } 2450 } 2451 2452 public function fixInstructionFileOrdering() 2453 { 2454 global $DIC; 2455 2456 $db = $DIC->database(); 2457 2458 $files = array_map(function ($v) { 2459 return $v["name"]; 2460 }, $this->getFiles()); 2461 2462 $set = $db->query("SELECT * FROM exc_ass_file_order " . 2463 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") . 2464 " ORDER BY order_nr"); 2465 $order_nr = 10; 2466 $numbered_files = array(); 2467 while ($rec = $db->fetchAssoc($set)) { 2468 // file exists, set correct order nr 2469 if (in_array($rec["filename"], $files)) { 2470 $db->manipulate( 2471 "UPDATE exc_ass_file_order SET " . 2472 " order_nr = " . $db->quote($order_nr, "integer") . 2473 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") . 2474 " AND id = " . $db->quote($rec["id"], "integer") 2475 ); 2476 $order_nr += 10; 2477 $numbered_files[] = $rec["filename"]; 2478 } else { // file does not exist, delete entry 2479 $db->manipulate( 2480 "DELETE FROM exc_ass_file_order " . 2481 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") . 2482 " AND id = " . $db->quote($rec["id"], "integer") 2483 ); 2484 } 2485 } 2486 foreach ($files as $f) { 2487 if (!in_array($f, $numbered_files)) { 2488 self::instructionFileInsertOrder($f, $this->getId()); 2489 } 2490 } 2491 } 2492 2493 /** 2494 * @param array $a_entries 2495 * @param integer $a_ass_id assignment id 2496 * @return array data items 2497 */ 2498 public function fileAddOrder($a_entries = array()) 2499 { 2500 $this->fixInstructionFileOrdering(); 2501 2502 $order = $this->getInstructionFilesOrder(); 2503 foreach ($a_entries as $k => $e) { 2504 $a_entries[$k]["order_val"] = $order[$e["file"]]["order_nr"]; 2505 $a_entries[$k]["order_id"] = $order[$e["file"]]["id"]; 2506 } 2507 2508 return $a_entries; 2509 } 2510 2511 /** 2512 * @param int $a_ass_id assignment id 2513 * @return int 2514 */ 2515 public static function instructionFileOrderGetMax($a_ass_id) 2516 { 2517 global $DIC; 2518 2519 $db = $DIC->database(); 2520 2521 //get max order number 2522 $result = $db->queryF( 2523 "SELECT max(order_nr) as max_order FROM exc_ass_file_order WHERE assignment_id = %s", 2524 array('integer'), 2525 array($db->quote($a_ass_id, 'integer')) 2526 ); 2527 2528 while ($row = $db->fetchAssoc($result)) { 2529 $order_val = (int) $row['max_order']; 2530 } 2531 return $order_val; 2532 } 2533 2534 2535 /** 2536 * Set limit minimum characters 2537 * 2538 * @param int minim limit 2539 */ 2540 public function setMinCharLimit($a_val) 2541 { 2542 $this->min_char_limit = $a_val; 2543 } 2544 2545 /** 2546 * Get limit minimum characters 2547 * 2548 * @return int minimum limit 2549 */ 2550 public function getMinCharLimit() 2551 { 2552 return $this->min_char_limit; 2553 } 2554 2555 /** 2556 * Set limit maximum characters 2557 * @param int max limit 2558 */ 2559 public function setMaxCharLimit($a_val) 2560 { 2561 $this->max_char_limit = $a_val; 2562 } 2563 2564 /** 2565 * get limit maximum characters 2566 * return int max limit 2567 */ 2568 public function getMaxCharLimit() 2569 { 2570 return $this->max_char_limit; 2571 } 2572 2573 /** 2574 * Get calculated deadlines for user/team members. These arrays will contain no entries, if team or user 2575 * has not started the assignment yet. 2576 * 2577 * @return array[array] contains two arrays one with key "user", second with key "team", each one has 2578 * member id as keys and calculated deadline as value 2579 */ 2580 public function getCalculatedDeadlines() 2581 { 2582 $calculated_deadlines = array( 2583 "user" => array(), 2584 "team" => array() 2585 ); 2586 2587 if ($this->getRelativeDeadline() && $this->getDeadlineMode() == self::DEADLINE_RELATIVE) { 2588 foreach (ilExcIndividualDeadline::getStartingTimestamps($this->getId()) as $ts) { 2589 $type = $ts["is_team"] 2590 ? "team" 2591 : "user"; 2592 2593 $calculated_deadlines[$type][$ts["member_id"]] = array( 2594 "calculated_deadline" => $ts["starting_ts"] + ($this->getRelativeDeadline() * 24 * 60 * 60) 2595 ); 2596 } 2597 } 2598 return $calculated_deadlines; 2599 } 2600} 2601