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