ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilExAssignment.php
Go to the documentation of this file.
1<?php
2
28
35{
39 public const TYPE_UPLOAD = 1;
40 public const TYPE_BLOG = 2;
41 public const TYPE_PORTFOLIO = 3;
42 public const TYPE_UPLOAD_TEAM = 4;
43 public const TYPE_TEXT = 5;
44 public const TYPE_WIKI_TEAM = 6;
45
46 public const FEEDBACK_DATE_DEADLINE = 1;
47 public const FEEDBACK_DATE_SUBMISSION = 2;
48 public const FEEDBACK_DATE_CUSTOM = 3;
49
50 public const PEER_REVIEW_VALID_NONE = 1;
51 public const PEER_REVIEW_VALID_ONE = 2;
52 public const PEER_REVIEW_VALID_ALL = 3;
53
55 public const TEAMS_FORMED_BY_TUTOR = 1;
56 public const TEAMS_FORMED_BY_RANDOM = 2;
58
59 public const DEADLINE_ABSOLUTE = 0;
60 public const DEADLINE_RELATIVE = 1;
62 protected \ILIAS\Exercise\InternalGUIService $gui;
63 protected \ILIAS\Exercise\InternalDomainService $domain;
65
66 protected ilDBInterface $db;
67 protected ilLanguage $lng;
68 protected ilObjUser $user;
71
72 protected int $id = 0;
73 protected int $exc_id = 0;
74 protected int $type = 0;
75 protected ?int $start_time = null;
76 protected ?int $deadline = null;
77 protected ?int $deadline2 = null;
78 protected string $instruction = "";
79 protected string $title = "";
80 protected bool $mandatory = false;
81 protected int $order_nr = 0;
82 protected bool $peer = false; // peer review activated
83 protected int $peer_min = 0;
84 protected int $peer_unlock = 0;
85 protected int $peer_dl = 0;
86 protected int $peer_valid; // passed after submission, one or all peer feedbacks
87 protected bool $peer_file = false;
88 protected bool $peer_personal = false; // personalised peer review
89 protected ?int $peer_char = null; // minimun number of characters for peer review
90 protected bool $peer_text = false;
91 protected bool $peer_rating = false;
92 protected int $peer_crit_cat = 0;
93 protected ?string $feedback_file = null;
94 protected bool $feedback_cron = false;
95 protected int $feedback_date = 0;
96 protected int $feedback_date_custom = 0;
97 protected bool $team_tutor = false;
98 protected ?int $max_file = null;
99 protected int $portfolio_template = 0;
100 protected int $min_char_limit = 0;
101 protected int $max_char_limit = 0;
104 protected int $deadline_mode = 0;
105 protected int $relative_deadline = 0;
106 protected int $rel_deadline_last_subm = 0;
107 protected array $member_status = [];
108 protected ilLogger $log;
109 protected ?int $crit_cat = 0;
110
115 public function __construct($a_id = 0)
116 {
117 global $DIC;
118
119 $this->domain = $DIC->exercise()->internal()->domain();
120 $this->db = $DIC->database();
121 $this->lng = $DIC->language();
122 $this->user = $DIC->user();
123 $this->app_event_handler = $DIC["ilAppEventHandler"];
124 $this->types = ilExAssignmentTypes::getInstance();
125 $this->access = $DIC->access();
126 $this->domain = $DIC->exercise()->internal()->domain();
127 $this->gui = $DIC->exercise()->internal()->gui();
128
129 $this->setType(self::TYPE_UPLOAD);
130 $this->setFeedbackDate(self::FEEDBACK_DATE_DEADLINE);
131
132 $this->log = ilLoggerFactory::getLogger("exc");
133
134 if ($a_id > 0) {
135 $this->setId($a_id);
136 $this->read();
137 }
138 $this->string_transform = $DIC->refinery()
139 ->string();
140 }
141
147 public static function getInstancesByExercise(int $a_exc_id): array
148 {
149 global $DIC;
150
151 $ilDB = $DIC->database();
152
153 $set = $ilDB->query("SELECT * FROM exc_assignment " .
154 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer") .
155 " ORDER BY order_nr");
156 $data = array();
157
158 $order_val = 10;
159 while ($rec = $ilDB->fetchAssoc($set)) {
160 // ???
161 $rec["order_val"] = $order_val;
162
163 $ass = new self();
164 $ass->initFromDB($rec);
165 $data[] = $ass;
166
167 $order_val += 10;
168 }
169
170 return $data;
171 }
172
178 public static function instructionFileGetFileOrderData(
179 array $a_file_data,
180 int $a_ass_id
181 ): array {
182 global $DIC;
183
184 $db = $DIC->database();
185 $db->setLimit(1, 0);
186
187 $result_order_val = $db->query("
188 SELECT id, order_nr
189 FROM exc_ass_file_order
190 WHERE assignment_id = {$db->quote($a_ass_id, 'integer')}
191 AND filename = {$db->quote($a_file_data['entry'], 'text')}
192 ");
193
194 $order_val = 0;
195 $order_id = 0;
196 while ($row = $db->fetchAssoc($result_order_val)) {
197 $order_val = (int) $row['order_nr'];
198 $order_id = (int) $row['id'];
199 }
200 return array($order_val, $order_id);
201 }
202
203 public function hasTeam(): bool
204 {
205 return $this->ass_type->usesTeams();
206 }
207
208 public function setId(int $a_val): void
209 {
210 $this->id = $a_val;
211 }
212
213 public function getId(): int
214 {
215 return $this->id;
216 }
217
218 public function setExerciseId(int $a_val): void
219 {
220 $this->exc_id = $a_val;
221 }
222
223 public function getExerciseId(): int
224 {
225 return $this->exc_id;
226 }
227
228 public function setStartTime(?int $a_val): void
229 {
230 $this->start_time = $a_val;
231 }
232
233 public function getStartTime(): ?int
234 {
235 return $this->start_time;
236 }
237
238 public function setDeadline(?int $a_val): void
239 {
240 $this->deadline = $a_val;
241 }
242
243 public function getDeadline(): ?int
244 {
245 return $this->deadline;
246 }
247
252 public function setDeadlineMode(int $a_val): void
253 {
254 $this->deadline_mode = $a_val;
255 }
256
257 public function getDeadlineMode(): int
258 {
259 return $this->deadline_mode;
260 }
261
262 public function setRelativeDeadline(int $a_val): void
263 {
264 $this->relative_deadline = $a_val;
265 }
266
267 public function getRelativeDeadline(): int
268 {
269 return $this->relative_deadline;
270 }
271
272 public function setRelDeadlineLastSubmission(int $a_val): void
273 {
274 $this->rel_deadline_last_subm = $a_val;
275 }
276
278 {
279 return $this->rel_deadline_last_subm;
280 }
281
282
283 // Get individual deadline (max of common or idl (team) deadline = Official Deadline)
284 public function getPersonalDeadline(int $a_user_id): int
285 {
286 $ilDB = $this->db;
287
288 $is_team = false;
289 if ($this->ass_type->usesTeams()) {
290 $team_id = ilExAssignmentTeam::getTeamId($this->getId(), $a_user_id);
291 if (!$team_id) {
292 // #0021043
293 return $this->getDeadline();
294 }
295 $a_user_id = $team_id;
296 $is_team = true;
297 }
298
299 $set = $ilDB->query("SELECT tstamp FROM exc_idl" .
300 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer") .
301 " AND member_id = " . $ilDB->quote($a_user_id, "integer") .
302 " AND is_team = " . $ilDB->quote($is_team, "integer"));
303 $row = $ilDB->fetchAssoc($set);
304
305 // use assignment deadline if no direct personal
306 return max(($row["tstamp"] ?? 0), $this->getDeadline());
307 }
308
309 // Get last/final personal deadline (of assignment)
310 public function getLastPersonalDeadline(): int
311 {
312 $ilDB = $this->db;
313
314 $set = $ilDB->query("SELECT MAX(tstamp) FROM exc_idl" .
315 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer"));
316 $row = $ilDB->fetchAssoc($set);
317 return $row["tstamp"] ?? 0;
318 }
319
320 // Set extended deadline (timestamp)
321 public function setExtendedDeadline(?int $a_val): void
322 {
323 $this->deadline2 = $a_val;
324 }
325
326 public function getExtendedDeadline(): ?int
327 {
328 return $this->deadline2;
329 }
330
331 public function setInstruction(string $a_val): void
332 {
333 $this->instruction = $a_val;
334 }
335
336 public function getInstruction(): string
337 {
338 return $this->instruction;
339 }
340
341 public function getInstructionPresentation(): string
342 {
343 $inst = $this->getInstruction();
344 if (trim($inst)) {
345 $is_html = (strlen($inst) != strlen(strip_tags($inst)));
346 if (!$is_html) {
347 $inst = nl2br(
348 $this->string_transform->makeClickable()->transform($inst)
349 );
350 }
351 }
352 $inst = $this->gui->html()->escapeCurly($inst);
353 return $inst;
354 }
355
356 public function setTitle(string $a_val): void
357 {
358 $this->title = $a_val;
359 }
360
361 public function getTitle(): string
362 {
363 return $this->title;
364 }
365
366 public function setMandatory(bool $a_val): void
367 {
368 $this->mandatory = $a_val;
369 }
370
371 public function getMandatory(): bool
372 {
373 return $this->mandatory;
374 }
375
376 public function setOrderNr(int $a_val): void
377 {
378 $this->order_nr = $a_val;
379 }
380
381 public function getOrderNr(): int
382 {
383 return $this->order_nr;
384 }
385
391 public function setType(int $a_value): void
392 {
393 if ($this->isValidType($a_value)) {
394 $this->type = $a_value;
395
396 $this->ass_type = $this->types->getById($a_value);
397
398 if ($this->ass_type->usesTeams()) {
399 $this->setPeerReview(false);
400 }
401 }
402 }
403
405 {
406 return $this->ass_type;
407 }
408
409
414 public function getType(): int
415 {
416 return $this->type;
417 }
418
419 public function isValidType(int $a_value): bool
420 {
421 return $this->types->isValidId($a_value);
422 }
423
424 public function setPeerReview(bool $a_value): void
425 {
426 $this->peer = $a_value;
427 }
428
429 public function getPeerReview(): bool
430 {
431 return $this->peer;
432 }
433
434 public function setPeerReviewMin(int $a_value): void
435 {
436 $this->peer_min = $a_value;
437 }
438
439 public function getPeerReviewMin(): int
440 {
441 return $this->peer_min;
442 }
443
444 public function setPeerReviewSimpleUnlock(int $a_value): void
445 {
446 $this->peer_unlock = $a_value;
447 }
448
450 {
451 return $this->peer_unlock;
452 }
453
457 public function setPeerReviewDeadline(int $a_val): void
458 {
459 $this->peer_dl = $a_val;
460 }
461
462 public function getPeerReviewDeadline(): int
463 {
464 return $this->peer_dl;
465 }
466
472 public function setPeerReviewValid(int $a_value): void
473 {
474 $this->peer_valid = $a_value;
475 }
476
477 public function getPeerReviewValid(): int
478 {
479 return $this->peer_valid;
480 }
481
482 public function setPeerReviewRating(bool $a_val): void
483 {
484 $this->peer_rating = $a_val;
485 }
486
487 public function hasPeerReviewRating(): bool
488 {
489 return $this->peer_rating;
490 }
491
492 public function setPeerReviewText(bool $a_val): void
493 {
494 $this->peer_text = $a_val;
495 }
496
497 public function hasPeerReviewText(): bool
498 {
499 return $this->peer_text;
500 }
501
502 public function setPeerReviewFileUpload(bool $a_val): void
503 {
504 $this->peer_file = $a_val;
505 }
506
507 public function hasPeerReviewFileUpload(): bool
508 {
509 return $this->peer_file;
510 }
511
512 public function setPeerReviewPersonalized(bool $a_val): void
513 {
514 $this->peer_personal = $a_val;
515 }
516
517 public function hasPeerReviewPersonalized(): bool
518 {
519 return $this->peer_personal;
520 }
521
522 public function setPeerReviewChars(?int $a_value): void
523 {
524 $a_value = (is_numeric($a_value) && (int) $a_value > 0)
525 ? (int) $a_value
526 : null;
527 $this->peer_char = $a_value;
528 }
529
530 public function getPeerReviewChars(): ?int
531 {
532 return $this->peer_char;
533 }
534
535 public function setPeerReviewCriteriaCatalogue(?int $a_value): void
536 {
537 $this->crit_cat = $a_value;
538 }
539
541 {
542 return $this->crit_cat;
543 }
544
545 public function getPeerReviewCriteriaCatalogueItems(): array
546 {
547 if ($this->crit_cat) {
548 return ilExcCriteria::getInstancesByParentId($this->crit_cat);
549 } else {
550 $res = array();
551
552 if ($this->peer_rating) {
554 }
555
556 if ($this->peer_text) {
558 $crit = ilExcCriteria::getInstanceByType("text");
559 if ($this->peer_char) {
560 $crit->setMinChars($this->peer_char);
561 }
562 $res[] = $crit;
563 }
564
565 if ($this->peer_file) {
567 }
568
569 return $res;
570 }
571 }
572
573 public function setFeedbackFile(?string $a_value): void
574 {
575 $this->feedback_file = $a_value;
576 }
577
578 public function getFeedbackFile(): ?string
579 {
580 return $this->feedback_file;
581 }
582
586 public function setFeedbackCron(bool $a_value): void
587 {
588 $this->feedback_cron = $a_value;
589 }
590
591 public function hasFeedbackCron(): bool
592 {
593 return $this->feedback_cron;
594 }
595
596 // Set (global) feedback file availability date
597 public function setFeedbackDate(int $a_value): void
598 {
599 $this->feedback_date = $a_value;
600 }
601
602 public function getFeedbackDate(): int
603 {
604 return $this->feedback_date;
605 }
606
611 public function setFeedbackDateCustom(int $a_value): void
612 {
613 $this->feedback_date_custom = $a_value;
614 }
615
616 public function getFeedbackDateCustom(): int
617 {
618 return $this->feedback_date_custom;
619 }
620
621 // Set team management by tutor
622 public function setTeamTutor(bool $a_value): void
623 {
624 $this->team_tutor = $a_value;
625 }
626
627 public function getTeamTutor(): bool
628 {
629 return $this->team_tutor;
630 }
631
632 // Set max number of uploads
633 public function setMaxFile(?int $a_value): void
634 {
635 $this->max_file = $a_value;
636 }
637
638 public function getMaxFile(): ?int
639 {
640 return $this->max_file;
641 }
642
643 // Set portfolio template id
644 public function setPortfolioTemplateId(int $a_val): void
645 {
646 $this->portfolio_template = $a_val;
647 }
648
649 public function getPortfolioTemplateId(): int
650 {
651 return $this->portfolio_template;
652 }
653
657 public function read(): void
658 {
659 $ilDB = $this->db;
660
661 $set = $ilDB->query(
662 "SELECT * FROM exc_assignment " .
663 " WHERE id = " . $ilDB->quote($this->getId(), "integer")
664 );
665 $rec = $ilDB->fetchAssoc($set);
666
667 // #16172 - might be deleted
668 if (is_array($rec)) {
669 $this->initFromDB($rec);
670 }
671 }
672
678 protected function initFromDB(array $a_set): void
679 {
680 $this->setId((int) $a_set["id"]);
681 $this->setExerciseId((int) $a_set["exc_id"]);
682 $this->setDeadline((int) $a_set["time_stamp"]);
683 $this->setExtendedDeadline((int) $a_set["deadline2"]);
684 $this->setInstruction((string) $a_set["instruction"]);
685 $this->setTitle((string) $a_set["title"]);
686 $this->setStartTime((int) $a_set["start_time"]);
687 $this->setOrderNr((int) $a_set["order_nr"]);
688 $this->setMandatory((bool) $a_set["mandatory"]);
689 $this->setType((int) $a_set["type"]);
690 $this->setPeerReview((bool) $a_set["peer"]);
691 $this->setPeerReviewMin((int) $a_set["peer_min"]);
692 $this->setPeerReviewSimpleUnlock((int) $a_set["peer_unlock"]);
693 $this->setPeerReviewDeadline((int) $a_set["peer_dl"]);
694 $this->setPeerReviewValid((int) $a_set["peer_valid"]);
695 $this->setPeerReviewFileUpload((bool) $a_set["peer_file"]);
696 $this->setPeerReviewPersonalized((bool) $a_set["peer_prsl"]);
697 $this->setPeerReviewChars((int) $a_set["peer_char"]);
698 $this->setPeerReviewText((bool) $a_set["peer_text"]);
699 $this->setPeerReviewRating((bool) $a_set["peer_rating"]);
700 $this->setPeerReviewCriteriaCatalogue((int) $a_set["peer_crit_cat"]);
701 $this->setFeedbackFile((string) $a_set["fb_file"]);
702 $this->setFeedbackDate((int) $a_set["fb_date"]);
703 $this->setFeedbackDateCustom((int) $a_set["fb_date_custom"]);
704 $this->setFeedbackCron((bool) $a_set["fb_cron"]);
705 $this->setTeamTutor((bool) $a_set["team_tutor"]);
706 $this->setMaxFile((int) $a_set["max_file"]);
707 $this->setPortfolioTemplateId((int) $a_set["portfolio_template"]);
708 $this->setMinCharLimit((int) $a_set["min_char_limit"]);
709 $this->setMaxCharLimit((int) $a_set["max_char_limit"]);
710 $this->setDeadlineMode((int) $a_set["deadline_mode"]);
711 $this->setRelativeDeadline((int) $a_set["relative_deadline"]);
712 $this->setRelDeadlineLastSubmission((int) $a_set["rel_deadline_last_subm"]);
713 }
714
718 public function save(): void
719 {
720 $ilDB = $this->db;
721
722 if ($this->getOrderNr() == 0) {
723 $this->setOrderNr(
724 self::lookupMaxOrderNrForEx($this->getExerciseId())
725 + 10
726 );
727 }
728
729 $next_id = $ilDB->nextId("exc_assignment");
730 $ilDB->insert("exc_assignment", array(
731 "id" => array("integer", $next_id),
732 "exc_id" => array("integer", $this->getExerciseId()),
733 "time_stamp" => array("integer", $this->getDeadline()),
734 "deadline2" => array("integer", $this->getExtendedDeadline()),
735 "instruction" => array("clob", $this->getInstruction()),
736 "title" => array("text", $this->getTitle()),
737 "start_time" => array("integer", $this->getStartTime()),
738 "order_nr" => array("integer", $this->getOrderNr()),
739 "mandatory" => array("integer", $this->getMandatory()),
740 "type" => array("integer", $this->getType()),
741 "peer" => array("integer", $this->getPeerReview()),
742 "peer_min" => array("integer", $this->getPeerReviewMin()),
743 "peer_unlock" => array("integer", $this->getPeerReviewSimpleUnlock()),
744 "peer_dl" => array("integer", $this->getPeerReviewDeadline()),
745 "peer_valid" => array("integer", $this->getPeerReviewValid()),
746 "peer_file" => array("integer", $this->hasPeerReviewFileUpload()),
747 "peer_prsl" => array("integer", $this->hasPeerReviewPersonalized()),
748 "peer_char" => array("integer", $this->getPeerReviewChars()),
749 "peer_text" => array("integer", (int) $this->hasPeerReviewText()),
750 "peer_rating" => array("integer", (int) $this->hasPeerReviewRating()),
751 "peer_crit_cat" => array("integer", $this->getPeerReviewCriteriaCatalogue()),
752 "fb_file" => array("text", $this->getFeedbackFile()),
753 "fb_date" => array("integer", $this->getFeedbackDate()),
754 "fb_date_custom" => array("integer", $this->getFeedbackDateCustom()),
755 "fb_cron" => array("integer", $this->hasFeedbackCron()),
756 "team_tutor" => array("integer", $this->getTeamTutor()),
757 "max_file" => array("integer", $this->getMaxFile()),
758 "portfolio_template" => array("integer", $this->getPortfolioTemplateId()),
759 "min_char_limit" => array("integer", $this->getMinCharLimit()),
760 "max_char_limit" => array("integer", $this->getMaxCharLimit()),
761 "relative_deadline" => array("integer", $this->getRelativeDeadline()),
762 "rel_deadline_last_subm" => array("integer", $this->getRelDeadlineLastSubmission()),
763 "deadline_mode" => array("integer", $this->getDeadlineMode()),
764 "solution_rid" => array("text", ''),
765 ));
766 $this->setId($next_id);
767 $exc = new ilObjExercise($this->getExerciseId(), false);
768 $exc->updateAllUsersStatus();
769
770 $this->domain->assignment()->instructionFiles($next_id)->createCollection();
771
772 self::createNewAssignmentRecords($next_id, $exc);
773 ilObjectSearch::raiseContentChanged($this->getExerciseId());
774 $this->handleCalendarEntries("create", $exc);
775 }
776
780 public function update(): void
781 {
782 $ilDB = $this->db;
783
784 $ilDB->update(
785 "exc_assignment",
786 array(
787 "exc_id" => array("integer", $this->getExerciseId()),
788 "time_stamp" => array("integer", $this->getDeadline()),
789 "deadline2" => array("integer", $this->getExtendedDeadline()),
790 "instruction" => array("clob", $this->getInstruction()),
791 "title" => array("text", $this->getTitle()),
792 "start_time" => array("integer", $this->getStartTime()),
793 "order_nr" => array("integer", $this->getOrderNr()),
794 "mandatory" => array("integer", $this->getMandatory()),
795 "type" => array("integer", $this->getType()),
796 "peer" => array("integer", $this->getPeerReview()),
797 "peer_min" => array("integer", $this->getPeerReviewMin()),
798 "peer_unlock" => array("integer", $this->getPeerReviewSimpleUnlock()),
799 "peer_dl" => array("integer", $this->getPeerReviewDeadline()),
800 "peer_valid" => array("integer", $this->getPeerReviewValid()),
801 "peer_file" => array("integer", $this->hasPeerReviewFileUpload()),
802 "peer_prsl" => array("integer", $this->hasPeerReviewPersonalized()),
803 "peer_char" => array("integer", $this->getPeerReviewChars()),
804 "peer_text" => array("integer", (int) $this->hasPeerReviewText()),
805 "peer_rating" => array("integer", (int) $this->hasPeerReviewRating()),
806 "peer_crit_cat" => array("integer", $this->getPeerReviewCriteriaCatalogue()),
807 "fb_file" => array("text", $this->getFeedbackFile()),
808 "fb_date" => array("integer", $this->getFeedbackDate()),
809 "fb_date_custom" => array("integer", $this->getFeedbackDateCustom()),
810 "fb_cron" => array("integer", $this->hasFeedbackCron()),
811 "team_tutor" => array("integer", $this->getTeamTutor()),
812 "max_file" => array("integer", $this->getMaxFile()),
813 "portfolio_template" => array("integer", $this->getPortfolioTemplateId()),
814 "min_char_limit" => array("integer", $this->getMinCharLimit()),
815 "max_char_limit" => array("integer", $this->getMaxCharLimit()),
816 "deadline_mode" => array("integer", $this->getDeadlineMode()),
817 "relative_deadline" => array("integer", $this->getRelativeDeadline()),
818 "rel_deadline_last_subm" => array("integer", $this->getRelDeadlineLastSubmission())
819 ),
820 array(
821 "id" => array("integer", $this->getId()),
822 )
823 );
824 $exc = new ilObjExercise($this->getExerciseId(), false);
825 $exc->updateAllUsersStatus();
826 ilObjectSearch::raiseContentChanged($this->getExerciseId());
827 $this->handleCalendarEntries("update", $exc);
828 }
829
833 public function delete(
834 ilObjExercise $exc,
835 bool $update_user_status = true
836 ): void {
837 $ilDB = $this->db;
838
839 // delete submissions
840 $exc_members = new ilExerciseMembers($exc);
841 foreach ($exc_members->getMembers() as $mem) {
842 $submission = new ilExSubmission($this, $mem);
843 $submission->deleteAllFiles();
844 }
845
846 $ilDB->manipulateF(
847 "DELETE FROM exc_usr_tutor " .
848 "WHERE ass_id = %s",
849 array("integer"),
850 array($this->getId())
851 );
852
853 // remove peer review data
854 if ($this->getPeerReview()) {
855 $peer_review = new ilExPeerReview($this);
856 $peer_review->resetPeerReviews();
857 }
858
859 $ilDB->manipulate(
860 "DELETE FROM exc_ass_file_order" .
861 " WHERE assignment_id = " . $ilDB->quote($this->getId(), 'integer')
862 );
863
864 $ilDB->manipulate(
865 "DELETE FROM exc_mem_ass_status" .
866 " WHERE ass_id = " . $ilDB->quote($this->getId(), 'integer')
867 );
868
869 $ilDB->manipulate(
870 "DELETE FROM exc_assignment WHERE " .
871 " id = " . $ilDB->quote($this->getId(), "integer")
872 );
873
874 if ($update_user_status) {
875 $exc->updateAllUsersStatus();
876 }
877
878 $this->handleCalendarEntries("delete", $exc);
879
881
882 $reminder = new ilExAssignmentReminder();
883 $reminder->deleteReminders($this->getId());
884
885 // delete teams
886 $this->domain->team()->deleteTeamsOfAssignment($this->getId());
887
888 // delete resource collections and resources
889 $this->domain->assignment()->instructionFiles($this->getId())
890 ->deleteCollection();
891 }
892
893
894 // Get assignments data of an exercise in an array
895 public static function getAssignmentDataOfExercise(int $a_exc_id): array
896 {
897 global $DIC;
898
899 $ilDB = $DIC->database();
900
901 // should be changed to self::getInstancesByExerciseId()
902
903 $set = $ilDB->query("SELECT * FROM exc_assignment " .
904 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer") .
905 " ORDER BY order_nr");
906 $data = array();
907
908 $order_val = 10;
909 while ($rec = $ilDB->fetchAssoc($set)) {
910 $data[] = array(
911 "id" => (int) $rec["id"],
912 "exc_id" => (int) $rec["exc_id"],
913 "deadline" => (int) $rec["time_stamp"],
914 "deadline2" => (int) $rec["deadline2"],
915 "instruction" => (string) $rec["instruction"],
916 "title" => (string) $rec["title"],
917 "start_time" => (int) $rec["start_time"],
918 "order_val" => $order_val,
919 "mandatory" => (bool) $rec["mandatory"],
920 "type" => (int) $rec["type"],
921 "peer" => (bool) $rec["peer"],
922 "peer_min" => (int) $rec["peer_min"],
923 "peer_dl" => (int) $rec["peer_dl"],
924 "peer_file" => (bool) $rec["peer_file"],
925 "peer_prsl" => (bool) $rec["peer_prsl"],
926 "fb_file" => (string) $rec["fb_file"],
927 "fb_date" => (int) $rec["fb_date"],
928 "fb_cron" => (bool) $rec["fb_cron"],
929 "deadline_mode" => (int) $rec["deadline_mode"],
930 "relative_deadline" => (int) $rec["relative_deadline"],
931 "rel_deadline_last_subm" => (int) $rec["rel_deadline_last_subm"]
932 );
933 $order_val += 10;
934 }
935 return $data;
936 }
937
945 public static function cloneAssignmentsOfExercise(
946 int $a_old_exc_id,
947 int $a_new_exc_id,
948 array $a_crit_cat_map
949 ): void {
950 global $DIC;
951
952 $ass_domain = $DIC->exercise()->internal()->domain()->assignment();
953 $ass_data = self::getInstancesByExercise($a_old_exc_id);
954 foreach ($ass_data as $d) {
955 // clone assignment
956 $new_ass = new ilExAssignment();
957 $new_ass->setExerciseId($a_new_exc_id);
958 $new_ass->setTitle($d->getTitle());
959 $new_ass->setDeadline($d->getDeadline());
960 $new_ass->setExtendedDeadline($d->getExtendedDeadline());
961 $new_ass->setInstruction($d->getInstruction());
962 $new_ass->setMandatory($d->getMandatory());
963 $new_ass->setOrderNr($d->getOrderNr());
964 $new_ass->setStartTime($d->getStartTime());
965 $new_ass->setType($d->getType());
966 $new_ass->setPeerReview($d->getPeerReview());
967 $new_ass->setPeerReviewMin($d->getPeerReviewMin());
968 $new_ass->setPeerReviewDeadline($d->getPeerReviewDeadline());
969 $new_ass->setPeerReviewFileUpload($d->hasPeerReviewFileUpload());
970 $new_ass->setPeerReviewPersonalized($d->hasPeerReviewPersonalized());
971 $new_ass->setPeerReviewValid($d->getPeerReviewValid());
972 $new_ass->setPeerReviewChars($d->getPeerReviewChars());
973 $new_ass->setPeerReviewText($d->hasPeerReviewText());
974 $new_ass->setPeerReviewRating($d->hasPeerReviewRating());
975 $new_ass->setPeerReviewCriteriaCatalogue($d->getPeerReviewCriteriaCatalogue());
976 $new_ass->setPeerReviewSimpleUnlock($d->getPeerReviewSimpleUnlock());
977 $new_ass->setFeedbackFile($d->getFeedbackFile());
978 $new_ass->setFeedbackDate($d->getFeedbackDate());
979 $new_ass->setFeedbackDateCustom($d->getFeedbackDateCustom());
980 $new_ass->setFeedbackCron($d->hasFeedbackCron()); // #16295
981 $new_ass->setTeamTutor($d->getTeamTutor());
982 $new_ass->setMaxFile($d->getMaxFile());
983 $new_ass->setMinCharLimit($d->getMinCharLimit());
984 $new_ass->setMaxCharLimit($d->getMaxCharLimit());
985 $new_ass->setPortfolioTemplateId($d->getPortfolioTemplateId());
986 $new_ass->setDeadlineMode($d->getDeadlineMode());
987 $new_ass->setRelativeDeadline($d->getRelativeDeadline());
988 $new_ass->setRelDeadlineLastSubmission($d->getRelDeadlineLastSubmission());
989
990 // criteria catalogue(s)
991 if ($d->getPeerReviewCriteriaCatalogue() &&
992 array_key_exists($d->getPeerReviewCriteriaCatalogue(), $a_crit_cat_map)) {
993 $new_ass->setPeerReviewCriteriaCatalogue($a_crit_cat_map[$d->getPeerReviewCriteriaCatalogue()]);
994 }
995
996 $new_ass->save();
997
998 // clone assignment files
999 $ass_domain->instructionFiles($d->getId())->cloneTo($new_ass->getId());
1000
1001 // clone global feedback file
1002 $ass_domain->sampleSolution($d->getId())->cloneTo($new_ass->getId());
1003
1004 // clone reminders
1008 $rmd_sub = new ilExAssignmentReminder($a_old_exc_id, $d->getId(), $rem_type);
1009 if ($rmd_sub->getReminderStatus()) {
1010 $new_rmd_sub = new ilExAssignmentReminder($a_new_exc_id, $new_ass->getId(), $rem_type);
1011 $new_rmd_sub->setReminderStatus($rmd_sub->getReminderStatus());
1012 $new_rmd_sub->setReminderStart($rmd_sub->getReminderStart());
1013 $new_rmd_sub->setReminderEnd($rmd_sub->getReminderEnd());
1014 $new_rmd_sub->setReminderFrequency($rmd_sub->getReminderFrequency());
1015 $new_rmd_sub->setReminderMailTemplate($rmd_sub->getReminderMailTemplate());
1016 $new_rmd_sub->save();
1017 }
1018 }
1019
1020
1021 // type specific properties
1022 $ass_type = $d->getAssignmentType();
1023 $ass_type->cloneSpecificProperties($d, $new_ass);
1024 }
1025 }
1026
1027 public function getFiles(): array
1028 {
1029 return $this->domain->assignment()->instructionFiles($this->getId())->getFiles();
1030 }
1031
1032 public function getInstructionFilesOrder(): array
1033 {
1034 // TODO IRSS: currently the ilResourceCollectionGUI does not support the order of files done by the component,
1035 // this should be implemented as well there and will follow.
1036 $ilDB = $this->db;
1037
1038 $set = $ilDB->query(
1039 "SELECT filename, order_nr, id FROM exc_ass_file_order " .
1040 " WHERE assignment_id = " . $ilDB->quote($this->getId(), "integer")
1041 );
1042
1043 $data = array();
1044 while ($rec = $ilDB->fetchAssoc($set)) {
1045 $data[$rec['filename']] = $rec;
1046 }
1047
1048 return $data;
1049 }
1050
1051 // Select the maximum order nr for an exercise
1052 public static function lookupMaxOrderNrForEx(int $a_exc_id): int
1053 {
1054 // TODO IRSS: currently the ilResourceCollectionGUI does not support the order of files done by the component,
1055 // this should be implemented as well there and will follow.
1056 global $DIC;
1057
1058 $ilDB = $DIC->database();
1059
1060 $set = $ilDB->query(
1061 "SELECT MAX(order_nr) mnr FROM exc_assignment " .
1062 " WHERE exc_id = " . $ilDB->quote($a_exc_id, "integer")
1063 );
1064 if ($rec = $ilDB->fetchAssoc($set)) {
1065 return (int) $rec["mnr"];
1066 }
1067 return 0;
1068 }
1069
1070 public static function lookupAssignmentOnline(int $a_ass_id): bool
1071 {
1072 global $DIC;
1073
1074 $ilDB = $DIC->database();
1075
1076 $query = "SELECT id FROM exc_assignment " .
1077 "WHERE start_time <= " . $ilDB->quote(time(), 'integer') . ' ' .
1078 "AND time_stamp >= " . $ilDB->quote(time(), 'integer') . ' ' .
1079 "AND id = " . $ilDB->quote($a_ass_id, 'integer');
1080 $res = $ilDB->query($query);
1081
1082 return (bool) $res->numRows();
1083 }
1084
1085 public static function lookupExerciseId(int $a_ass_id): int
1086 {
1087 global $DIC;
1088
1089 $ilDB = $DIC->database();
1090 $query = "SELECT exc_id FROM exc_assignment " .
1091 "WHERE id = " . $ilDB->quote($a_ass_id, 'integer');
1092 $res = $ilDB->fetchAssoc($ilDB->query($query));
1093
1094 return (int) ($res["exc_id"] ?? 0);
1095 }
1096
1097 private static function lookup(int $a_id, string $a_field): string
1098 {
1099 global $DIC;
1100
1101 $ilDB = $DIC->database();
1102
1103 $set = $ilDB->query(
1104 "SELECT " . $a_field . " FROM exc_assignment " .
1105 " WHERE id = " . $ilDB->quote($a_id, "integer")
1106 );
1107
1108 $rec = $ilDB->fetchAssoc($set);
1109
1110 return $rec[$a_field] ?? "";
1111 }
1112
1113 public static function lookupTitle(int $a_id): string
1114 {
1115 return self::lookup($a_id, "title");
1116 }
1117
1118 public static function lookupType(int $a_id): int
1119 {
1120 return (int) self::lookup($a_id, "type");
1121 }
1122
1123 // Save ordering of all assignments of an exercise
1124 public static function saveAssOrderOfExercise(int $a_ex_id, array $a_order): void
1125 {
1126 global $DIC;
1127
1128 $ilDB = $DIC->database();
1129
1130 asort($a_order);
1131 $nr = 10;
1132 foreach ($a_order as $k => $v) {
1133 // the check for exc_id is for security reasons. ass ids are unique.
1134 $ilDB->manipulate(
1135 "UPDATE exc_assignment SET " .
1136 " order_nr = " . $ilDB->quote($nr, "integer") .
1137 " WHERE id = " . $ilDB->quote((int) $k, "integer") .
1138 " AND exc_id = " . $ilDB->quote($a_ex_id, "integer")
1139 );
1140 $nr += 10;
1141 }
1142 }
1143
1144 // Order assignments by deadline date
1145 public static function orderAssByDeadline(int $a_ex_id): void
1146 {
1147 global $DIC;
1148 $ilDB = $DIC->database();
1149
1150 $set = $ilDB->query(
1151 "SELECT id FROM exc_assignment " .
1152 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") .
1153 " ORDER BY time_stamp"
1154 );
1155 $nr = 10;
1156 while ($rec = $ilDB->fetchAssoc($set)) {
1157 $ilDB->manipulate(
1158 "UPDATE exc_assignment SET " .
1159 " order_nr = " . $ilDB->quote($nr, "integer") .
1160 " WHERE id = " . $ilDB->quote($rec["id"], "integer")
1161 );
1162 $nr += 10;
1163 }
1164 }
1165
1166 // Count the number of mandatory assignments
1167 public static function countMandatory(int $a_ex_id): int
1168 {
1169 global $DIC;
1170
1171 $ilDB = $DIC->database();
1172
1173 $set = $ilDB->query(
1174 "SELECT count(*) cntm FROM exc_assignment " .
1175 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") .
1176 " AND mandatory = " . $ilDB->quote(1, "integer")
1177 );
1178 $rec = $ilDB->fetchAssoc($set);
1179 return (int) $rec["cntm"];
1180 }
1181
1182 // Count assignments
1183 public static function count(int $a_ex_id): int
1184 {
1185 global $DIC;
1186
1187 $ilDB = $DIC->database();
1188
1189 $set = $ilDB->query(
1190 "SELECT count(*) cntm FROM exc_assignment " .
1191 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer")
1192 );
1193 $rec = $ilDB->fetchAssoc($set);
1194 return $rec["cntm"];
1195 }
1196
1197 // Is assignment in exercise?
1198 public static function isInExercise(int $a_ass_id, int $a_ex_id): bool
1199 {
1200 global $DIC;
1201
1202 $ilDB = $DIC->database();
1203
1204 $set = $ilDB->query(
1205 "SELECT * FROM exc_assignment " .
1206 " WHERE exc_id = " . $ilDB->quote($a_ex_id, "integer") .
1207 " AND id = " . $ilDB->quote($a_ass_id, "integer")
1208 );
1209 if ($ilDB->fetchAssoc($set)) {
1210 return true;
1211 }
1212 return false;
1213 }
1214
1215 public function getMemberListData(): array
1216 {
1217 $ilDB = $this->db;
1218
1219 $mem = array();
1220
1221 // first get list of members from member table
1222 $set = $ilDB->query("SELECT ud.usr_id, ud.lastname, ud.firstname, ud.login" .
1223 " FROM exc_members excm" .
1224 " JOIN usr_data ud ON (ud.usr_id = excm.usr_id)" .
1225 " WHERE excm.obj_id = " . $ilDB->quote($this->getExerciseId(), "integer"));
1226 while ($rec = $ilDB->fetchAssoc($set)) {
1227 $mem[$rec["usr_id"]] =
1228 array(
1229 "name" => $rec["lastname"] . ", " . $rec["firstname"],
1230 "login" => $rec["login"],
1231 "usr_id" => $rec["usr_id"],
1232 "lastname" => $rec["lastname"],
1233 "firstname" => $rec["firstname"]
1234 );
1235 }
1236
1237 // users in idl may already exist before occuring in the members db table
1238 // if user starts an assignment with relative deadline
1239 $idl = $this->getIndividualDeadlines();
1240 if (!$this->ass_type->usesTeams()) {
1241 foreach ($idl as $user_id => $v) {
1242 if (!isset($mem[$user_id])) {
1243 if (ilObjUser::_exists((int) $user_id)) {
1245 $mem[$user_id] =
1246 array(
1247 "name" => $name["lastname"] . ", " . $name["firstname"],
1248 "login" => $name["login"],
1249 "usr_id" => $user_id,
1250 "lastname" => $name["lastname"],
1251 "firstname" => $name["firstname"]
1252 );
1253 }
1254 }
1255 }
1256 }
1257
1258 $q = "SELECT * FROM exc_mem_ass_status " .
1259 "WHERE ass_id = " . $ilDB->quote($this->getId(), "integer");
1260 $set = $ilDB->query($q);
1261 while ($rec = $ilDB->fetchAssoc($set)) {
1262 if (isset($mem[$rec["usr_id"]])) {
1263 $sub = new ilExSubmission($this, $rec["usr_id"]);
1264
1265 $mem[$rec["usr_id"]]["sent_time"] = $rec["sent_time"];
1266 $mem[$rec["usr_id"]]["submission"] = $sub->getLastSubmission();
1267 $mem[$rec["usr_id"]]["status_time"] = $rec["status_time"];
1268 $mem[$rec["usr_id"]]["feedback_time"] = $rec["feedback_time"];
1269 $mem[$rec["usr_id"]]["notice"] = $rec["notice"];
1270 $mem[$rec["usr_id"]]["status"] = $rec["status"];
1271 $mem[$rec["usr_id"]]["mark"] = $rec["mark"];
1272 $mem[$rec["usr_id"]]["comment"] = $rec["u_comment"];
1273 }
1274 }
1275 return $mem;
1276 }
1277
1283 int $a_user_id,
1284 string $a_grade = ""
1285 ): ?array {
1286 global $DIC;
1287 $ilDB = $DIC->database();
1288
1289 $and_grade = "";
1290 if (in_array($a_grade, array("notgraded", "passed", "failed"))) {
1291 $and_grade = " AND status = " . $ilDB->quote($a_grade, "text");
1292 }
1293
1294 $q = "SELECT * FROM exc_mem_ass_status " .
1295 "WHERE ass_id = " . $ilDB->quote($this->getId(), "integer") .
1296 " AND usr_id = " . $ilDB->quote($a_user_id, "integer") .
1297 $and_grade;
1298
1299 $set = $ilDB->query($q);
1300
1301 $data = null;
1302 while ($rec = $ilDB->fetchAssoc($set)) {
1303 $sub = new ilExSubmission($this, $a_user_id);
1304
1305 $data["sent_time"] = $rec["sent_time"];
1306 $data["submission"] = $sub->getLastSubmission();
1307 $data["status_time"] = $rec["status_time"];
1308 $data["feedback_time"] = $rec["feedback_time"];
1309 $data["notice"] = $rec["notice"];
1310 $data["status"] = $rec["status"];
1311 $data["mark"] = $rec["mark"];
1312 $data["comment"] = $rec["u_comment"];
1313 }
1314 return $data;
1315 }
1316
1317 // Create member status record for a new participant for all assignments
1318 public static function createNewUserRecords(
1319 int $a_user_id,
1320 int $a_exc_id
1321 ): void {
1322 global $DIC;
1323
1324 $ilDB = $DIC->database();
1325 $ass_domain = $DIC->exercise()->internal()->domain()->assignment();
1326
1327 $ass_data = self::getAssignmentDataOfExercise($a_exc_id);
1328 foreach ($ass_data as $ass) {
1329 //echo "-".$ass["id"]."-".$a_user_id."-";
1330 $ilDB->replace("exc_mem_ass_status", array(
1331 "ass_id" => array("integer", $ass["id"]),
1332 "usr_id" => array("integer", $a_user_id)
1333 ), array(
1334 "status" => array("text", "notgraded")
1335 ));
1336 if (!$ass_domain->tutorFeedbackFile((int) $ass["id"])->hasCollection($a_user_id)) {
1337 $ass_domain->tutorFeedbackFile((int) $ass["id"])->createCollection($a_user_id);
1338 }
1339 }
1340 }
1341
1342 // Create member status record for a new assignment for all participants
1343 public static function createNewAssignmentRecords(
1344 int $a_ass_id,
1345 ilObjExercise $a_exc
1346 ): void {
1347 global $DIC;
1348
1349 $ilDB = $DIC->database();
1350 $ass_domain = $DIC->exercise()->internal()->domain()->assignment();
1351
1352 $exmem = new ilExerciseMembers($a_exc);
1353 $mems = $exmem->getMembers();
1354
1355 foreach ($mems as $mem) {
1356 $ilDB->replace("exc_mem_ass_status", array(
1357 "ass_id" => array("integer", $a_ass_id),
1358 "usr_id" => array("integer", $mem)
1359 ), array(
1360 "status" => array("text", "notgraded")
1361 ));
1362 if (!$ass_domain->tutorFeedbackFile($a_ass_id)->hasCollection($mem)) {
1363 $ass_domain->tutorFeedbackFile($a_ass_id)->createCollection($mem);
1364 }
1365 }
1366 }
1367
1368
1373 protected function handleCalendarEntries(
1374 string $a_event,
1375 ilObjExercise $exc
1376 ): void {
1377 $ilAppEventHandler = $this->app_event_handler;
1378
1379 $dl_id = $this->getId() . "0";
1380 $fbdl_id = $this->getId() . "1";
1381
1382 $context_ids = array($dl_id, $fbdl_id);
1383 $apps = array();
1384
1385 if ($a_event != "delete") {
1386 // deadline or relative deadline given
1387 if ($this->getDeadline() || $this->getDeadlineMode() == ilExAssignment::DEADLINE_RELATIVE) {
1388 $app = new ilCalendarAppointmentTemplate($dl_id);
1389 $app->setTranslationType(ilCalendarEntry::TRANSLATION_SYSTEM);
1390 $app->setSubtitle("cal_exc_deadline");
1391 $app->setTitle($this->getTitle());
1392 $app->setFullday(false);
1393 // note: in the case of a relative deadline this will be set to 0 / 1970...)
1394 // see ilCalendarScheduleFilterExercise for appointment modification
1395 $app->setStart(new ilDateTime($this->getDeadline(), IL_CAL_UNIX));
1396
1397 $apps[] = $app;
1398 }
1399
1400 if ($this->getPeerReview() &&
1401 $this->getPeerReviewDeadline()) {
1402 $app = new ilCalendarAppointmentTemplate($fbdl_id);
1403 $app->setTranslationType(ilCalendarEntry::TRANSLATION_SYSTEM);
1404 $app->setSubtitle("cal_exc_peer_review_deadline");
1405 $app->setTitle($this->getTitle());
1406 $app->setFullday(false);
1407 $app->setStart(new ilDateTime($this->getPeerReviewDeadline(), IL_CAL_UNIX));
1408
1409 $apps[] = $app;
1410 }
1411 }
1412
1413 $ilAppEventHandler->raise(
1414 'components/ILIAS/Exercise',
1415 $a_event . 'Assignment',
1416 array(
1417 'object' => $exc,
1418 'obj_id' => $exc->getId(),
1419 'context_ids' => $context_ids,
1420 'appointments' => $apps)
1421 );
1422 }
1423
1424 public static function getPendingFeedbackNotifications(): array
1425 {
1426 global $DIC;
1427
1429 $log->debug("Get feedback notifications.");
1430
1431 $ilDB = $DIC->database();
1432
1433 $res = array();
1434
1435 $set = $ilDB->query("SELECT id,fb_file,time_stamp,deadline2,fb_date FROM exc_assignment" .
1436 " WHERE fb_cron = " . $ilDB->quote(1, "integer") .
1437 " AND (fb_date = " . $ilDB->quote(self::FEEDBACK_DATE_DEADLINE, "integer") .
1438 " AND time_stamp IS NOT NULL" .
1439 " AND time_stamp > " . $ilDB->quote(0, "integer") .
1440 " AND time_stamp < " . $ilDB->quote(time(), "integer") .
1441 " AND fb_cron_done = " . $ilDB->quote(0, "integer") .
1442 ") OR (fb_date = " . $ilDB->quote(self::FEEDBACK_DATE_CUSTOM, "integer") .
1443 " AND fb_date_custom IS NOT NULL" .
1444 " AND fb_date_custom > " . $ilDB->quote(0, "integer") .
1445 " AND fb_date_custom < " . $ilDB->quote(time(), "integer") .
1446 " AND fb_cron_done = " . $ilDB->quote(0, "integer") . ")");
1447
1448
1449
1450 while ($row = $ilDB->fetchAssoc($set)) {
1451 $log->debug("check assignment " . $row['id'] . ", fb_file " . $row["fb_file"]);
1452 if ($row['fb_date'] == self::FEEDBACK_DATE_DEADLINE) {
1453 $max = max($row['time_stamp'], $row['deadline2']);
1454 if (trim((string) $row["fb_file"]) && $max <= time()) {
1455 $log->debug("...adding(1)");
1456 $res[] = $row["id"];
1457 }
1458 } elseif ($row['fb_date'] == self::FEEDBACK_DATE_CUSTOM) {
1459 if (trim($row["fb_file"] ?? "") && ($row['fb_date_custom'] ?? 0) <= time()) {
1460 $log->debug("...adding(2)");
1461 $res[] = $row["id"];
1462 }
1463 }
1464 }
1465
1466 return $res;
1467 }
1468
1472 public static function sendFeedbackNotifications(
1473 int $a_ass_id,
1474 ?int $a_user_id = null
1475 ): bool {
1476 global $DIC;
1477
1478 $ilDB = $DIC->database();
1479 $gui = $DIC->exercise()->internal()->gui();
1480 $domain = $DIC->exercise()->internal()->domain();
1482
1483 $ass = new self($a_ass_id);
1484
1485 // valid assignment?
1486 if (!$ass->hasFeedbackCron() || !$ass->getFeedbackFile()) {
1487 $log->debug("return(1)");
1488 return false;
1489 }
1490
1491 if (!$a_user_id) {
1492 // already done?
1493 $set = $ilDB->query("SELECT fb_cron_done" .
1494 " FROM exc_assignment" .
1495 " WHERE id = " . $ilDB->quote($a_ass_id, "integer"));
1496 $row = $ilDB->fetchAssoc($set);
1497 if ($row["fb_cron_done"]) {
1498 $log->debug("return(2)");
1499 return false;
1500 }
1501 }
1502
1503 $ntf = new ilSystemNotification();
1504 $ntf->setLangModules(array("exc"));
1505 $ntf->setObjId($ass->getExerciseId());
1506 $ntf->setSubjectLangId("exc_feedback_notification_subject");
1507 $ntf->setIntroductionLangId("exc_feedback_notification_body");
1508 $ntf->addAdditionalInfo("exc_assignment", $ass->getTitle());
1509 $ref_id = 0;
1510 if ($a_user_id) { // link to assignment
1511 $ref_id = $domain->permission()->getFirstRefIdWithPermission(
1512 "read",
1513 $ass->getExerciseId(),
1514 $a_user_id
1515 );
1516 } else {
1517 $ref_ids = ilObject::_getAllReferences($ass->getExerciseId());
1518 if (count($ref_ids) === 1) {
1519 $ref_id = current($ref_ids);
1520 }
1521 }
1522 if ($ref_id > 0) {
1523 $perm_link = $gui->permanentLink()->getPermanentLink($ref_id, $ass->getId());
1524 $ntf->addAdditionalInfo("exc_link_to_assignment", $perm_link);
1525 }
1526
1527 $ntf->setGotoLangId("exc_feedback_notification_link");
1528 $ntf->setReasonLangId("exc_feedback_notification_reason");
1529
1530 if (!$a_user_id) {
1531 $log->debug("send to members, cnt: " . count(ilExerciseMembers::_getMembers($ass->getExerciseId())));
1532 $ntf->sendMailAndReturnRecipients(ilExerciseMembers::_getMembers($ass->getExerciseId()));
1533
1534 $ilDB->manipulate("UPDATE exc_assignment" .
1535 " SET fb_cron_done = " . $ilDB->quote(1, "integer") .
1536 " WHERE id = " . $ilDB->quote($a_ass_id, "integer"));
1537 } else {
1538 $log->debug("send to user: " . $a_user_id);
1539 $ntf->sendMailAndReturnRecipients(array($a_user_id));
1540 }
1541
1542 return true;
1543 }
1544
1545
1546 // status
1547
1548 // like: after effective deadline (for single user), no deadline: true
1549 public function afterDeadline(): bool
1550 {
1551 $ilUser = $this->user;
1552
1553 // :TODO: always current user?
1554 $idl = $this->getPersonalDeadline($ilUser->getId()); // official deadline
1555
1556 // no deadline === true
1557 $deadline = max($this->deadline, $this->deadline2, $idl); // includes grace period
1558 return ($deadline - time() <= 0);
1559 }
1560
1561 public function afterDeadlineStrict(bool $a_include_personal = true): bool
1562 {
1563 // :TODO: this means that peer feedback, global feedback is available
1564 // after LAST personal deadline
1565 // team management is currently ignoring personal deadlines
1566 $idl = $a_include_personal
1567 ? $this->getLastPersonalDeadline()
1568 : null;
1569
1570 // no deadline === false
1571 $deadline = max($this->deadline, $this->deadline2, $idl);
1572
1573 // #18271 - afterDeadline() does not handle last personal deadline
1574 // after effective deadline of all users
1575 if ($idl && $deadline == $idl) {
1576 return ($deadline - time() <= 0);
1577 }
1578
1579 // like: after effective deadline (for single user), except: no deadline false
1580 return ($deadline > 0 &&
1581 $this->afterDeadline());
1582 }
1583
1587 public function afterCustomDate(): bool
1588 {
1589 $date_custom = $this->getFeedbackDateCustom();
1590 //if the solution will be displayed only after reach all the deadlines.
1591 //$final_deadline = $this->afterDeadlineStrict();
1592 //$dl = max($final_deadline, time());
1593 //return ($date_custom - $dl <= 0);
1594 return ($date_custom - time() <= 0);
1595 }
1596
1597 // like: before effective deadline (for all users), no deadline: true
1598 public function beforeDeadline(): bool
1599 {
1600 // no deadline === true
1601 return !$this->afterDeadlineStrict();
1602 }
1603
1604 public function notStartedYet(): bool
1605 {
1606 return (time() - $this->start_time <= 0);
1607 }
1608
1609
1610 //
1611 // FEEDBACK FILES
1612 //
1613
1617 public function handleGlobalFeedbackFileUpload(int $ass_id, array $a_file): bool
1618 {
1619 $rcid = $this->domain->assignment()->sampleSolution($ass_id)->importFromLegacyUpload($a_file);
1620 $this->setFeedbackFile($a_file["name"]);
1621 return ($rcid !== "");
1622 }
1623
1624
1625 public function getMemberStatus(?int $a_user_id = null): ilExAssignmentMemberStatus
1626 {
1627 $ilUser = $this->user;
1628
1629 if (!$a_user_id) {
1630 $a_user_id = $ilUser->getId();
1631 }
1632 if (!array_key_exists($a_user_id, $this->member_status)) {
1633 $this->member_status[$a_user_id] = new ilExAssignmentMemberStatus($this->getId(), $a_user_id);
1634 }
1635 return $this->member_status[$a_user_id];
1636 }
1637
1638 //
1639 // individual deadlines
1640 //
1641
1642 public function setIndividualDeadline(
1643 string $id,
1644 ilDateTime $date
1645 ): void {
1646 $is_team = false;
1647 if (!is_numeric($id)) {
1648 $id = substr($id, 1);
1649 $is_team = true;
1650 }
1651
1652 $idl = ilExcIndividualDeadline::getInstance($this->getId(), (int) $id, $is_team);
1653 $idl->setIndividualDeadline($date->get(IL_CAL_UNIX));
1654 $idl->save();
1655 }
1656
1657 public function getIndividualDeadlines(): array
1658 {
1659 $ilDB = $this->db;
1660
1661 $res = array();
1662
1663 $set = $ilDB->query("SELECT * FROM exc_idl" .
1664 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer"));
1665 while ($row = $ilDB->fetchAssoc($set)) {
1666 if ($row["is_team"]) {
1667 $row["member_id"] = "t" . $row["member_id"];
1668 }
1669
1670 $res[$row["member_id"]] = $row["tstamp"];
1671 }
1672
1673 return $res;
1674 }
1675
1676 public function getRequestedDeadlines(): array
1677 {
1678 $ilDB = $this->db;
1679
1680 $res = array();
1681
1682 $set = $ilDB->query("SELECT * FROM exc_idl" .
1683 " WHERE ass_id = " . $ilDB->quote($this->getId(), "integer") .
1684 " AND requested = " . $ilDB->quote(1, "integer"));
1685 while ($row = $ilDB->fetchAssoc($set)) {
1686 if ($row["is_team"]) {
1687 $row["member_id"] = "t" . $row["member_id"];
1688 }
1689
1690 $res[$row["member_id"]] = $row["requested"];
1691 }
1692
1693 return $res;
1694 }
1695
1696 public function hasActiveIDl(): bool
1697 {
1698 return (bool) ($this->getDeadline() || $this->getDeadlineMode() === self::DEADLINE_ABSOLUTE_INDIVIDUAL);
1699 }
1700
1701 public function hasReadOnlyIDl(): bool
1702 {
1703 if (!$this->ass_type->usesTeams() &&
1704 $this->getPeerReview()) {
1705 // all deadlines are read-only if we have peer feedback
1706 $peer_review = new ilExPeerReview($this);
1707 if ($peer_review->hasPeerReviewGroups()) {
1708 return true;
1709 }
1710 }
1711
1712 return false;
1713 }
1714
1716 int $a_ass_id,
1717 array $a_order
1718 ): void {
1719 global $DIC;
1720
1721 $db = $DIC->database();
1722
1723 asort($a_order, SORT_NUMERIC);
1724
1725 $nr = 10;
1726 foreach (array_keys($a_order) as $k) {
1727 // the check for exc_id is for security reasons. ass ids are unique.
1728 $db->manipulate(
1729 "UPDATE exc_ass_file_order SET " .
1730 " order_nr = " . $db->quote($nr, "integer") .
1731 " WHERE id = " . $db->quote((int) $k, "integer") .
1732 " AND assignment_id = " . $db->quote($a_ass_id, "integer")
1733 );
1734 $nr += 10;
1735 }
1736 }
1737
1738 public static function insertFileOrderNr(
1739 int $a_ass_id,
1740 string $a_filename,
1741 int $a_order_nr
1742 ): void {
1743 global $DIC;
1744 $db = $DIC->database();
1745 $id = $db->nextId("exc_ass_file_order");
1746 $db->insert(
1747 "exc_ass_file_order",
1748 [
1749 "id" => ["integer", $id],
1750 "order_nr" => ["integer", $a_order_nr],
1751 "assignment_id" => ["integer", $a_ass_id],
1752 "filename" => ["text", $a_filename]
1753 ]
1754 );
1755 }
1756
1757 // Store the order nr of a file in the database
1758 public static function instructionFileInsertOrder(
1759 string $a_filename,
1760 int $a_ass_id,
1761 int $a_order_nr = 0
1762 ): void {
1763 global $DIC;
1764
1765 $db = $DIC->database();
1766
1767 if ($a_ass_id) {
1768 //first of all check the suffix and change if necessary
1770
1771 if (self::instructionFileExistsInDb($filename, $a_ass_id) == 0) {
1772 if ($a_order_nr == 0) {
1773 $order_val = self::instructionFileOrderGetMax($a_ass_id);
1774 $order = $order_val + 10;
1775 } else {
1776 $order = $a_order_nr;
1777 }
1778
1779 $id = $db->nextID('exc_ass_file_order');
1780 $db->manipulate("INSERT INTO exc_ass_file_order " .
1781 "(id, assignment_id, filename, order_nr) VALUES (" .
1782 $db->quote($id, "integer") . "," .
1783 $db->quote($a_ass_id, "integer") . "," .
1784 $db->quote($filename, "text") . "," .
1785 $db->quote($order, "integer") .
1786 ")");
1787 }
1788 }
1789 }
1790
1794 public static function instructionFileDeleteOrder(
1795 int $a_ass_id,
1796 array $a_file
1797 ): void {
1798 global $DIC;
1799
1800 $db = $DIC->database();
1801
1802 //now its done by filename. We need to figure how to get the order id in the confirmdelete method
1803 foreach ($a_file as $v) {
1804 $db->manipulate(
1805 "DELETE FROM exc_ass_file_order " .
1806 "WHERE filename = " . $db->quote($v, 'text') .
1807 " AND assignment_id = " . $db->quote($a_ass_id, 'integer')
1808 );
1809 }
1810 }
1811
1812 public static function renameInstructionFile(
1813 string $a_old_name,
1814 string $a_new_name,
1815 int $a_ass_id
1816 ): void {
1817 global $DIC;
1818
1819 $db = $DIC->database();
1820
1821 if ($a_ass_id) {
1822 $db->manipulate(
1823 "DELETE FROM exc_ass_file_order" .
1824 " WHERE assignment_id = " . $db->quote($a_ass_id, 'integer') .
1825 " AND filename = " . $db->quote($a_new_name, 'text')
1826 );
1827
1828 $db->manipulate(
1829 "UPDATE exc_ass_file_order SET" .
1830 " filename = " . $db->quote($a_new_name, 'text') .
1831 " WHERE assignment_id = " . $db->quote($a_ass_id, 'integer') .
1832 " AND filename = " . $db->quote($a_old_name, 'text')
1833 );
1834 }
1835 }
1836
1837 public static function instructionFileExistsInDb(
1838 string $a_filename,
1839 int $a_ass_id
1840 ): int {
1841 global $DIC;
1842
1843 $db = $DIC->database();
1844
1845 if ($a_ass_id) {
1846 $result = $db->query(
1847 "SELECT id FROM exc_ass_file_order" .
1848 " WHERE assignment_id = " . $db->quote($a_ass_id, 'integer') .
1849 " AND filename = " . $db->quote($a_filename, 'text')
1850 );
1851
1852 return $db->numRows($result);
1853 }
1854
1855 return 0;
1856 }
1857
1858 public function fixInstructionFileOrdering(): void
1859 {
1860 $db = $this->db;
1861
1862 $files = array_map(function ($v) {
1863 return $v["name"];
1864 }, $this->getFiles());
1865
1866 $set = $db->query("SELECT * FROM exc_ass_file_order " .
1867 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") .
1868 " ORDER BY order_nr");
1869 $order_nr = 10;
1870 $numbered_files = array();
1871 while ($rec = $db->fetchAssoc($set)) {
1872 // file exists, set correct order nr
1873 if (in_array($rec["filename"], $files)) {
1874 $db->manipulate(
1875 "UPDATE exc_ass_file_order SET " .
1876 " order_nr = " . $db->quote($order_nr, "integer") .
1877 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") .
1878 " AND id = " . $db->quote($rec["id"], "integer")
1879 );
1880 $order_nr += 10;
1881 $numbered_files[] = $rec["filename"];
1882 } else { // file does not exist, delete entry
1883 $db->manipulate(
1884 "DELETE FROM exc_ass_file_order " .
1885 " WHERE assignment_id = " . $db->quote($this->getId(), "integer") .
1886 " AND id = " . $db->quote($rec["id"], "integer")
1887 );
1888 }
1889 }
1890 foreach ($files as $f) {
1891 if (!in_array($f, $numbered_files)) {
1892 self::instructionFileInsertOrder($f, $this->getId());
1893 }
1894 }
1895 }
1896
1897 public function fileAddOrder(
1898 array $a_entries = array()
1899 ): array {
1900 $this->fixInstructionFileOrdering();
1901
1902 $order = $this->getInstructionFilesOrder();
1903 foreach ($a_entries as $k => $e) {
1904 $a_entries[$k]["order_val"] = $order[$e["file"]]["order_nr"] ?? 0;
1905 $a_entries[$k]["order_id"] = $order[$e["file"]]["id"] ?? "";
1906 }
1907
1908 return $a_entries;
1909 }
1910
1911 public static function instructionFileOrderGetMax(int $a_ass_id): int
1912 {
1913 global $DIC;
1914
1915 $db = $DIC->database();
1916
1917 //get max order number
1918 $result = $db->queryF(
1919 "SELECT max(order_nr) as max_order FROM exc_ass_file_order WHERE assignment_id = %s",
1920 array('integer'),
1921 array($db->quote($a_ass_id, 'integer'))
1922 );
1923
1924 $order_val = 0;
1925 while ($row = $db->fetchAssoc($result)) {
1926 $order_val = (int) $row['max_order'];
1927 }
1928 return $order_val;
1929 }
1930
1931
1932 // Set limit minimum characters
1933 public function setMinCharLimit(int $a_val): void
1934 {
1935 $this->min_char_limit = $a_val;
1936 }
1937
1938 public function getMinCharLimit(): int
1939 {
1940 return $this->min_char_limit;
1941 }
1942
1943 // Set limit maximum characters
1944 public function setMaxCharLimit(int $a_val): void
1945 {
1946 $this->max_char_limit = $a_val;
1947 }
1948
1949 public function getMaxCharLimit(): int
1950 {
1951 return $this->max_char_limit;
1952 }
1953
1962 public function getCalculatedDeadlines(): array
1963 {
1964 $calculated_deadlines = array(
1965 "user" => array(),
1966 "team" => array()
1967 );
1968
1969 if ($this->getRelativeDeadline() && $this->getDeadlineMode() == self::DEADLINE_RELATIVE) {
1970 foreach (ilExcIndividualDeadline::getStartingTimestamps($this->getId()) as $ts) {
1971 $type = $ts["is_team"]
1972 ? "team"
1973 : "user";
1974
1975 $calculated_deadlines[$type][$ts["member_id"]] = array(
1976 "calculated_deadline" => $ts["starting_ts"] + ($this->getRelativeDeadline() * 24 * 60 * 60)
1977 );
1978 }
1979 }
1980 return $calculated_deadlines;
1981 }
1982
1983 // see bug #36253
1984 public function canParticipantReceiveFeedback(int $part_id): bool
1985 {
1986 if ($this->hasTeam()) {
1987 if (!ilExAssignmentTeam::getTeamId($this->getId(), $part_id)) {
1988 return false;
1989 }
1990 }
1991 return true;
1992 }
1993}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$filename
Definition: buildRTE.php:78
Indicates that the directory is missing or not found.
Indicates that a file is missing or not found.
Indicates general problems with the input or output operations.
Definition: IOException.php:28
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
This exception indicates that an UI component was accepted by the JF but is not backed by a real impl...
const IL_CAL_UNIX
return true
Global event handler.
Apointment templates are used for automatic generated apointments.
@classDescription Date and time handling
get(int $a_format, string $a_format_str='', string $a_tz='')
get formatted date
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getTeamId(int $a_assignment_id, int $a_user_id, bool $a_create_on_demand=false)
Exercise assignment.
ILIAS Refinery String Group $string_transform
static getAssignmentDataOfExercise(int $a_exc_id)
static lookupExerciseId(int $a_ass_id)
ilAppEventHandler $app_event_handler
setTeamTutor(bool $a_value)
static lookupMaxOrderNrForEx(int $a_exc_id)
ilExAssignmentTypes $types
static saveInstructionFilesOrderOfAssignment(int $a_ass_id, array $a_order)
static lookup(int $a_id, string $a_field)
static saveAssOrderOfExercise(int $a_ex_id, array $a_order)
setPeerReviewSimpleUnlock(int $a_value)
setRelativeDeadline(int $a_val)
setPeerReviewMin(int $a_value)
ilExAssignmentTypeInterface $ass_type
static instructionFileOrderGetMax(int $a_ass_id)
static instructionFileGetFileOrderData(array $a_file_data, int $a_ass_id)
static count(int $a_ex_id)
static lookupType(int $a_id)
setDeadline(?int $a_val)
setExtendedDeadline(?int $a_val)
static cloneAssignmentsOfExercise(int $a_old_exc_id, int $a_new_exc_id, array $a_crit_cat_map)
Clone assignments of exercise.
setPeerReviewText(bool $a_val)
setPeerReviewCriteriaCatalogue(?int $a_value)
static countMandatory(int $a_ex_id)
setPeerReview(bool $a_value)
isValidType(int $a_value)
static instructionFileDeleteOrder(int $a_ass_id, array $a_file)
getType()
Get type this will most probably become an non public function in the future (or become obsolete)
static isInExercise(int $a_ass_id, int $a_ex_id)
handleCalendarEntries(string $a_event, ilObjExercise $exc)
Handle calendar entries for deadline(s)
static insertFileOrderNr(int $a_ass_id, string $a_filename, int $a_order_nr)
setStartTime(?int $a_val)
setDeadlineMode(int $a_val)
Set deadline mode.
setFeedbackCron(bool $a_value)
Toggle (global) feedback file cron.
getCalculatedDeadlines()
Get calculated deadlines for user/team members.
static renameInstructionFile(string $a_old_name, string $a_new_name, int $a_ass_id)
setPeerReviewValid(int $a_value)
Set peer review validation.
static instructionFileInsertOrder(string $a_filename, int $a_ass_id, int $a_order_nr=0)
static getInstancesByExercise(int $a_exc_id)
setPeerReviewPersonalized(bool $a_val)
static createNewUserRecords(int $a_user_id, int $a_exc_id)
getExerciseMemberAssignmentData(int $a_user_id, string $a_grade="")
Get submission data for an specific user,exercise and assignment.
static getPendingFeedbackNotifications()
initFromDB(array $a_set)
Import DB record.
__construct($a_id=0)
Constructor.
setInstruction(string $a_val)
setPortfolioTemplateId(int $a_val)
static sendFeedbackNotifications(int $a_ass_id, ?int $a_user_id=null)
setTitle(string $a_val)
const TYPE_UPLOAD
direct checks against const should be avoided, use type objects instead
static createNewAssignmentRecords(int $a_ass_id, ilObjExercise $a_exc)
getPersonalDeadline(int $a_user_id)
handleGlobalFeedbackFileUpload(int $ass_id, array $a_file)
setRelDeadlineLastSubmission(int $a_val)
ILIAS Exercise InternalDomainService $domain
getMemberStatus(?int $a_user_id=null)
setFeedbackFile(?string $a_value)
ilAccessHandler $access
ILIAS Exercise InternalGUIService $gui
setPeerReviewChars(?int $a_value)
setPeerReviewRating(bool $a_val)
setType(int $a_value)
Set type this will most probably become an non public function in the future (or become obsolete)
static orderAssByDeadline(int $a_ex_id)
setPeerReviewDeadline(int $a_val)
fileAddOrder(array $a_entries=array())
setFeedbackDateCustom(int $a_value)
Set (global) feedback file availability using a custom date.
setFeedbackDate(int $a_value)
setIndividualDeadline(string $id, ilDateTime $date)
canParticipantReceiveFeedback(int $part_id)
setMaxFile(?int $a_value)
static lookupAssignmentOnline(int $a_ass_id)
setMandatory(bool $a_val)
setPeerReviewFileUpload(bool $a_val)
static instructionFileExistsInDb(string $a_filename, int $a_ass_id)
static lookupTitle(int $a_id)
afterDeadlineStrict(bool $a_include_personal=true)
Exercise peer review.
Exercise submission //TODO: This class has many static methods related to delivered "files".
static getInstancesByParentId(int $a_parent_id)
static getInstanceByType(string $a_type)
static getStartingTimestamps(int $a_ass_id)
Get starting timestamp data for an assignment.
static getInstance(int $a_ass_id, int $a_participant_id, bool $a_is_team=false)
Class ilExerciseMembers.
static _getMembers(int $a_obj_id)
static getSafeFilename(string $a_initial_filename)
language handling
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
Class ilObjExercise.
updateAllUsersStatus()
Update status of all users.
User class.
static _lookupName(int $a_user_id)
static raiseContentChanged(int $obj_id)
static _getAllReferences(int $id)
get all reference ids for object ID
static _exists(int $id, bool $reference=false, ?string $type=null)
checks if an object exists in object_data
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface ilAccessHandler This interface combines all available interfaces which can be called via gl...
Interface ilDBInterface.
insert(string $table_name, array $values)
nextId(string $table_name)
setLimit(int $limit, int $offset=0)
numRows(ilDBStatement $statement)
quote($value, string $type)
manipulate(string $query)
Run a (write) Query on the database.
query(string $query)
Run a (read-only) Query on the database.
fetchAssoc(ilDBStatement $statement)
queryF(string $query, array $types, array $values)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
cloneSpecificProperties(ilExAssignment $source, ilExAssignment $target)
$ref_id
Definition: ltiauth.php:66
$log
Definition: ltiresult.php:34
$res
Definition: ltiservices.php:69
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:25