ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilObjStudyProgramme.php
Go to the documentation of this file.
1<?php declare(strict_types = 1);
2
3/* Copyright (c) 2015-2019 Richard Klees <richard.klees@concepts-and-training.de>, Stefan Hecken <stefan.hecken@concepts-and-training.de> Extended GPL, see docs/LICENSE */
4
6{
10 protected $parent;
11
15 protected $children;
16
20 protected $lp_children;
21
26
31
36
41
46
51
55 protected $events;
56
57 // GLOBALS from ILIAS
58
62 public $webdir;
63
67 public $tree;
68
72 public $ilUser;
73
77 protected $db;
78
82 protected $plugin_admin;
83
88
94
99
103 public static $study_programme_cache = null;
104
108 protected $members_cache;
109
114
119 public function __construct($a_id = 0, bool $a_call_by_reference = true)
120 {
122 $this->type = "prg";
123
124 $this->type_repository = $dic['model.Type.ilStudyProgrammeTypeRepository'];
125 $this->auto_categories_repository = $dic['model.AutoCategories.ilStudyProgrammeAutoCategoriesRepository'];
126 $this->auto_memberships_repository = $dic['model.AutoMemberships.ilStudyProgrammeAutoMembershipsRepository'];
127 $this->membersourcereader_factory = $dic['model.AutoMemberships.ilStudyProgrammeMembershipSourceReaderFactory'];
128
129 $this->settings_repository = $dic['model.Settings.ilStudyProgrammeSettingsRepository'];
130 $this->assignment_repository = $dic['model.Assignment.ilStudyProgrammeAssignmentRepository'];
131 $this->progress_repository = $dic['model.Progress.ilStudyProgrammeProgressRepository'];
132
133 $this->events = $dic['ilStudyProgrammeEvents'];
134
135 parent::__construct($a_id, $a_call_by_reference);
136
137 $this->clearParentCache();
138 $this->clearChildrenCache();
139 $this->clearLPChildrenCache();
140
141 global $DIC;
142 $tree = $DIC['tree'];
143 $ilUser = $DIC['ilUser'];
144 $this->webdir = $DIC->filesystem()->web();
145 $this->tree = $tree;
146 $this->ilUser = $ilUser;
147 $this->db = $DIC['ilDB'];
148 $this->plugin_admin = $DIC['ilPluginAdmin'];
149 $this->lng = $DIC['lng'];
150 $this->logger = ilLoggerFactory::getLogger($this->type);
151
152 $this->object_factory = ilObjectFactoryWrapper::singleton();
153
154 $this->custom_icon_factory = $DIC['object.customicons.factory'];
155
157 }
158
159 public static function initStudyProgrammeCache() : void
160 {
161 if (self::$study_programme_cache === null) {
162 self::$study_programme_cache = ilObjStudyProgrammeCache::singleton();
163 }
164 }
165
169 protected function clearParentCache() : void
170 {
171 // This is not initialized, but we need null if there is no parent.
172 $this->parent = false;
173 }
174
178 protected function clearChildrenCache() : void
179 {
180 $this->children = null;
181 }
182
186 protected function clearLPChildrenCache() : void
187 {
188 $this->lp_children = null;
189 }
190
191 public static function getRefIdFor(int $obj_id) : int
192 {
193 $refs = ilObject::_getAllReferences($obj_id);
194 if (count($refs) < 1) {
195 throw new ilException("Could not find ref_id for programme with obj_id $obj_id");
196 }
197 return (int) array_shift($refs);
198 }
199
200 protected function getPrgInstanceByObjId(int $obj_id) : ilObjStudyProgramme
201 {
202 return self::getInstanceByRefId(self::getRefIdFor($obj_id));
203 }
204
205 public static function getInstanceByObjId($obj_id) : ilObjStudyProgramme
206 {
207 return self::getInstanceByRefId(self::getRefIdFor($obj_id));
208 }
209
210 public static function getInstanceByRefId($a_ref_id) : ilObjStudyProgramme
211 {
212 if (self::$study_programme_cache === null) {
214 }
215 return self::$study_programme_cache->getInstanceByRefId($a_ref_id);
216 }
217
219 {
221 }
223 {
225 }
227 {
229 }
230 protected function getTree() : ilTree
231 {
232 return $this->tree;
233 }
234 protected function getLogger() : ilComponentLogger
235 {
236 return $this->logger;
237 }
238
244 public static function createInstance() : ilObjStudyProgramme
245 {
246 $obj = new ilObjStudyProgramme();
247 $obj->create();
248 $obj->createReference();
249 self::$study_programme_cache->addInstance($obj);
250 return $obj;
251 }
252
254 // CRUD
256
258 {
259 return $this->getSettingsRepository()->get($this->getId());
260 }
261
262 public function updateSettings(ilStudyProgrammeSettings $settings) : void
263 {
264 if ($settings->getObjId() !== (int) $this->getId()) {
265 throw new Exception("The given settings-object does not belong to this programme", 1);
266 }
267 $this->getSettingsRepository()->update($settings);
268 }
269
270 protected function deleteSettings() : void
271 {
272 $this->getSettingsRepository()->delete($this->getSettings());
273 }
274
280 protected function deleteAssignmentsAndProgresses() : void
281 {
283 $assignment_repository->deleteAllAssignmentsForProgrammeId($this->getId());
284 $orphan_condition_field = $assignment_repository->getTableAndFieldOfAssignmentIds();
286 $progress_repository->deleteProgressesFor($this->getId());
287 $progress_repository->deleteAllOrphanedProgresses(...$orphan_condition_field);
288 }
289
293 public function create() : int
294 {
295 $id = (int) parent::create();
296 $this->getSettingsRepository()->createFor($id);
297 return $id;
298 }
299
303 public function update() : void
304 {
305 parent::update();
306
307 $type_settings = $this->getSettings()->getTypeSettings();
308 // Update selection for advanced meta data of the type
309 if ($type_settings->getTypeId()) {
311 $this->getId(),
312 'prg_type',
313 $this->type_repository->getAssignedAMDRecordIdsByType($type_settings->getTypeId())
314 );
315 } else {
316 // If no type is assigned, delete relations by passing an empty array
317 ilAdvancedMDRecord::saveObjRecSelection($this->getId(), 'prg_type', array());
318 }
319 }
320
326 public function delete() : bool
327 {
328 // always call parent delete function first!!
329 if (!parent::delete()) {
330 return false;
331 }
332
333 $this->deleteSettings();
335 try {
336 $this->auto_categories_repository->deleteFor((int) $this->getId());
338 // This would be the case when SP is in trash (#17797)
339 }
340
343
344 $this->events->raise('delete', ['object' => $this, 'obj_id' => $this->getId()]);
345 return true;
346 }
347
348 public function hasAdvancedMetadata() : bool
349 {
350 $sub_type_id = $this->getSettings()->getTypeSettings()->getTypeId();
351 if ($sub_type_id) {
352 $type = $this->type_repository->getType($sub_type_id);
353 }
354
355 return !is_null($type) && count($this->type_repository->getAssignedAMDRecordIdsByType($type->getId(), true)) > 0;
356 }
357
359 // GETTERS AND SETTERS
361
365 public function getLastChange() : DateTime
366 {
367 return $this->getSettings()->getLastChange();
368 }
369
373 public function getPoints() : int
374 {
375 return $this->getSettings()->getAssessmentSettings()->getPoints();
376 }
377
383 public function setPoints(int $a_points) : ilObjStudyProgramme
384 {
385 $settings = $this->getSettings()->getAssessmentSettings()
386 ->withPoints($a_points);
387 $this->setAssessmentSettings($settings);
388 $this->updateLastChange();
389 return $this;
390 }
391
392 public function getLPMode() : int
393 {
394 return $this->getSettings()->getLPMode();
395 }
396
405 public function adjustLPMode() : void
406 {
407 // Clear caches here, there have been some changes, because this method
408 // would not have been called otherwise, and the changer just does not
409 // know if we have filled the caches already...
410 $this->clearLPChildrenCache();
411 $this->clearChildrenCache();
412
413 if ($this->getAmountOfLPChildren() > 0) {
414 $this->settings_repository->update(
416 );
417 } else {
418 if ($this->getAmountOfChildren(true) > 0) {
419 $this->settings_repository->update(
421 );
422 } else {
423 $this->settings_repository->update(
425 );
426 }
427 }
428 }
429
430 public function getStatus() : int
431 {
432 return $this->getSettings()->getAssessmentSettings()->getStatus();
433 }
434
440 public function setStatus(int $a_status) : ilObjStudyProgramme
441 {
442 $settings = $this->getSettings()->getAssessmentSettings()
443 ->withStatus($a_status);
444 $this->setAssessmentSettings($settings);
445 $this->updateLastChange();
446 return $this;
447 }
448
449 public function isActive() : bool
450 {
452 }
453
459 public function getSubType()
460 {
461 $type_settings = $this->getSettings()->getTypeSettings();
462 if (!in_array($type_settings->getTypeId(), array("-", "0"))) {
463 $subtype_id = $type_settings->getTypeId();
464 return $this->type_repository->getType($subtype_id);
465 }
466
467 return null;
468 }
469
470
472 // TREE NAVIGATION
474
483 public static function getAllChildren(int $a_ref_id, bool $include_references = false)
484 {
485 $ret = array();
486 $root = self::getInstanceByRefId($a_ref_id);
487 $root_id = $root->getId();
488 $root->applyToSubTreeNodes(function (ilObjStudyProgramme $prg) use (&$ret, $root_id) {
489 // exclude root node of subtree.
490 if ($prg->getId() == $root_id) {
491 return;
492 }
493 $ret[] = $prg;
494 }, $include_references);
495 return $ret;
496 }
497
498 public function getAllPrgChildren() : array
499 {
500 $ret = [];
501 $this->applyToSubTreeNodes(
502 function (ilObjStudyProgramme $prg) use (&$ret) {
503 if ($prg->getId() == $this->getId()) {
504 return;
505 }
506 $ret[] = $prg;
507 },
508 false
509 );
510 return $ret;
511 }
512
520 public function getChildren(bool $include_references = false) : array
521 {
522 $this->throwIfNotInTree();
523
524 if ($this->children === null) {
525 $ref_ids = $this->tree->getChildsByType($this->getRefId(), "prg");
526
527 // apply container sorting to tree
528 $sorting = ilContainerSorting::_getInstance($this->getId());
529 $ref_ids = $sorting->sortItems(array('prg' => $ref_ids));
530 $ref_ids = $ref_ids['prg'];
531
532 $this->children = array_map(function ($node_data) {
533 return ilObjStudyProgramme::getInstanceByRefId($node_data["child"]);
534 }, $ref_ids);
535 }
536
537 if ($include_references && $this->reference_children === null) {
538 $this->reference_children = [];
539 $ref_child_ref_ids = $this->tree->getChildsByType($this->getRefId(), "prgr");
540 foreach ($this->children as $prg) {
541 $ref_child_ref_ids =
542 array_merge(
543 $this->tree->getChildsByType($prg->getRefId(), "prgr"),
544 $ref_child_ref_ids
545 );
546 }
547 foreach (
548 array_unique(
549 array_map(
550 function ($data) {
551 return $data['child'];
552 },
553 array_filter($ref_child_ref_ids, function ($data) {
554 return $data["deleted"] === null;
555 })
556 )
557 ) as $prg_ref_id
558 ) {
559 $this->reference_children[] =
560 (new ilObjStudyProgrammeReference($prg_ref_id))->getReferencedObject();
561 }
562 }
563 return $include_references ?
564 array_merge($this->children, $this->reference_children) :
566 }
567
574 public function getParent() : ?ilObjStudyProgramme
575 {
576 if ($this->parent === false) {
577 $this->throwIfNotInTree();
578 $parent_data = $this->tree->getParentNodeData($this->getRefId());
579 if ($parent_data["type"] != "prg") {
580 $this->parent = null;
581 } else {
582 $this->parent = ilObjStudyProgramme::getInstanceByRefId($parent_data["ref_id"]);
583 }
584 }
585 return $this->parent;
586 }
587
588 protected function getReferencesTo(ilObjStudyProgramme $prg) : array
589 {
591 return array_filter(
592 array_map(
593 function ($id) {
595 array_shift(
597 )
598 );
599 },
601 ),
602 function ($prg_ref) use ($tree) {
603 return !$tree->isDeleted((int) $prg_ref->getRefId());
604 }
605 );
606 }
607
608 public function getReferencesToSelf() : array
609 {
610 return $this->getReferencesTo($this);
611 }
612
618 public function getParents(bool $include_references = false) : array
619 {
620 $current = $this;
621 $parents = [];
622 $queque = [$current];
623 while ($element = array_shift($queque)) {
624 $parent = $element->getParent();
625 if ($parent === null || $include_references) {
626 foreach ($this->getReferencesTo($element) as $reference) {
627 if ($this->tree->isDeleted($reference->getRefId())) {
628 continue;
629 }
630 $r_parent = $reference->getParent();
631 if (is_null($r_parent)) {
632 continue;
633 }
634 array_push($queque, $r_parent);
635 $parents[] = $r_parent;
636 }
637 continue;
638 }
639 array_push($queque, $parent);
640 $parents[] = $parent;
641 }
642 return array_reverse($parents);
643 }
644
650 public function hasChildren(bool $include_references = false) : bool
651 {
652 return $this->getAmountOfChildren($include_references) > 0;
653 }
654
661 public function getAmountOfChildren($include_references = false) : int
662 {
663 return count($this->getChildren($include_references));
664 }
665
672 public function getDepth() : int
673 {
674 $cur = $this;
675 $count = 0;
676 while ($cur = $cur->getParent()) {
677 $count++;
678 }
679 return $count;
680 }
681
688 public function getRoot()
689 {
690 $parents = $this->getParents();
691 if (count($parents) < 1) {
692 return $this;
693 }
694 return $parents[0];
695 }
696
703 public function getLPChildren()
704 {
705 $this->throwIfNotInTree();
706
707 if ($this->lp_children === null) {
708 $this->lp_children = array();
709
710 $ref_ids = $this->tree->getChildsByType($this->getRefId(), "crsr");
711
712 // apply container sorting to tree
713 $sorting = ilContainerSorting::_getInstance($this->getId());
714 $ref_ids = $sorting->sortItems(array('crs_ref' => $ref_ids));
715 $ref_ids = $ref_ids['crs_ref'];
716
717 $lp_children = array_map(function ($node_data) {
718 $lp_obj = $this->object_factory->getInstanceByRefId($node_data["child"]);
719
720 // filter out all StudyProgramme instances
721 return ($lp_obj instanceof $this) ? null : $lp_obj;
722 }, $ref_ids);
723
724 $this->lp_children = array_filter($lp_children);
725 }
726 return $this->lp_children;
727 }
728
735 public function getLPChildrenIds()
736 {
737 return array_map(function ($child) {
738 return $child->getId();
739 }, $this->getLPChildren());
740 }
741
747 public function getAmountOfLPChildren()
748 {
749 return count($this->getLPChildren());
750 }
751
752 public function hasLPChildren() : bool
753 {
754 return ($this->getAmountOfLPChildren() > 0);
755 }
756
760 protected function throwIfNotInTree() : void
761 {
762 if (!$this->tree->isInTree($this->getRefId())) {
763 throw new ilStudyProgrammeTreeException("This program is not in tree.");
764 }
765 }
766
768 // QUERIES ON SUBTREE
770
778 public function applyToSubTreeNodes(Closure $fun, bool $include_references = false) : void
779 {
780 $this->throwIfNotInTree();
781
782 if ($fun($this) !== false) {
783 foreach ($this->getChildren($include_references) as $child) {
784 $child->applyToSubTreeNodes($fun, $include_references);
785 }
786 }
787 }
788
794 public function getCompletedCourses(int $a_user_id) : array
795 {
796 $node_data = $this->tree->getNodeData($this->getRefId());
797 $crsrs = $this->tree->getSubTree($node_data, true, "crsr");
798
799 $completed_crss = array();
800 foreach ($crsrs as $ref) {
801 if (ilObject::_exists($ref['ref_id'], true) &&
802 is_null(ilObject::_lookupDeletedDate($ref['ref_id']))
803 ) {
804 $crs_id = ilContainerReference::_lookupTargetId($ref["obj_id"]);
805 if (ilLPStatus::_hasUserCompleted($crs_id, $a_user_id)) {
806 $containing_prg = self::getInstanceByRefId($ref["parent"]);
807 if ($containing_prg->isActive()) {
808 $completed_crss[] = [
809 "crs_id" => $crs_id
810 , "prg_ref_id" => $ref["parent"]
811 , "crsr_ref_id" => $ref["child"]
812 , "crsr_id" => $ref["obj_id"]
813 , "title" => ilContainerReference::_lookupTitle($ref["obj_id"])
814 ];
815 }
816 }
817 }
818 }
819 return $completed_crss;
820 }
821
823 // TREE MANIPULATION
825
837 {
838 $this->throwIfNotInTree();
839
841 throw new ilStudyProgrammeTreeException("Program already contains leafs.");
842 }
843
844 if ($this->tree->isInTree($a_prg->getRefId())) {
845 throw new ilStudyProgrammeTreeException("Other program already is in tree.");
846 }
847
848 if ($a_prg->getRefId() === null) {
849 $a_prg->createReference();
850 }
851 $a_prg->putInTree($this->getRefId());
852 return $this;
853 }
854
862 public function nodeInserted(ilObjStudyProgramme $a_prg)
863 {
865 throw new ilStudyProgrammeTreeException("Program already contains leafs.");
866 }
867
869 $this->settings_repository->update(
871 );
872 }
873
874 $this->clearChildrenCache();
875 $this->addMissingProgresses();
876 }
877
887 public function putInTree($a_parent_ref)
888 {
889 $res = parent::putInTree($a_parent_ref);
890
891 if (ilObject::_lookupType($a_parent_ref, true) == "prg") {
892 $par = ilObjStudyProgramme::getInstanceByRefId($a_parent_ref);
893 $par->nodeInserted($this);
894 }
895
896 return $res;
897 }
898
909 {
910 if ($a_prg->getParent()->getId() !== $this->getId()) {
911 throw new ilStudyProgrammeTreeException("This is no parent of the given programm.");
912 }
913
914 if (!$a_prg->canBeRemoved()) {
915 throw new ilStudyProgrammeTreeException("The node has relevant assignments.");
916 }
917
918 // *sigh*...
919 $node_data = $this->tree->getNodeData($a_prg->getRefId());
920 $this->tree->deleteTree($node_data);
921 $a_prg->clearParentCache();
922 $this->clearChildrenCache();
923
924 return $this;
925 }
926
931 public function canBeRemoved() : bool
932 {
933 foreach ($this->getProgresses() as $progress) {
934 if ($progress->getStatus() != ilStudyProgrammeProgress::STATUS_NOT_RELEVANT) {
935 return false;
936 }
937 if ($progress->getLastChangeBy() !== null) {
938 return false;
939 }
940 }
941 return true;
942 }
943
954 {
955 $this->throwIfNotInTree();
956
957 if ($this->hasChildren()) {
958 throw new ilStudyProgrammeTreeException("Program already contains other programm nodes.");
959 }
960
961 if ($a_leaf->getRefId() === null) {
962 $a_leaf->createReference();
963 }
964 $a_leaf->putInTree($this->getRefId());
965 $this->clearLPChildrenCache();
966 $this->settings_repository->update(
968 );
969
970 return $this;
971 }
972
983 {
984 if (self::getParentId($a_leaf) !== $this->getId()) {
985 throw new ilStudyProgrammeTreeException("This is no parent of the given leaf node.");
986 }
987
988 $node_data = $this->tree->getNodeData($a_leaf->getRefId());
989 $this->tree->deleteTree($node_data);
990 $this->clearLPChildrenCache();
991
992 return $this;
993 }
994
1004 public function moveTo(ilObjStudyProgramme $a_new_parent) : ilObjStudyProgramme
1005 {
1006 global $DIC;
1007 $rbacadmin = $DIC['rbacadmin'];
1008
1009 if ($parent = $this->getParent()) {
1010
1011 // TODO: check if there some leafs in the new parent
1012
1013 $this->tree->moveTree($this->getRefId(), $a_new_parent->getRefId());
1014 // necessary to clean up permissions
1015 $rbacadmin->adjustMovedObjectPermissions($this->getRefId(), $parent->getRefId());
1016
1017 // TODO: lp-progress needs to be updated
1018
1019 // clear caches on different nodes
1020 $this->clearParentCache();
1021
1022 $parent->clearChildrenCache();
1023 $parent->clearLPChildrenCache();
1024
1025 $a_new_parent->clearChildrenCache();
1026 $a_new_parent->clearLPChildrenCache();
1027 }
1028
1029 return $this;
1030 }
1031
1033 // USER ASSIGNMENTS
1035
1046 public function assignUser(int $usr_id, int $acting_usr_id = null) : ilStudyProgrammeAssignment
1047 {
1048 $this->members_cache = null;
1049
1051 throw new ilException(
1052 "ilObjStudyProgramme::assignUser: Can't assign user to program '"
1053 . $this->getId() . "', since it's not in active status."
1054 );
1055 }
1056
1057 if (is_null($acting_usr_id)) {
1058 $acting_usr_id = $this->getLoggedInUserId();
1059 }
1060
1061 $ass = $this->assignment_repository->createFor($this->getId(), $usr_id, $acting_usr_id);
1062
1063 $this->applyToSubTreeNodes(
1064 function (ilObjStudyProgramme $node) use ($ass, $acting_usr_id) {
1065 $progress = $node->createProgressForAssignment($ass, $acting_usr_id);
1066 $progress = $node->resetProgressToSettings($progress, $acting_usr_id);
1067 $this->getProgressRepository()->update($progress);
1068 },
1069 true
1070 );
1071
1072 $this->events->userAssigned($ass);
1073 return $ass;
1074 }
1075
1084 {
1085 $this->members_cache = null;
1086 if ($assignment->getRootId() != $this->getId()) {
1087 throw new ilException(
1088 "ilObjStudyProgramme::removeAssignment: Assignment '"
1089 . $assignment->getId() . "' does not belong to study "
1090 . "program '" . $this->getId() . "'."
1091 );
1092 }
1093
1094 $this->assignment_repository->delete($assignment);
1095
1096 $affected_node_ids = $this->progress_repository->deleteForAssignmentId($assignment->getId());
1097 foreach ($affected_node_ids as $node_obj_id) {
1098 $this->refreshLPStatus($assignment->getUserId(), $node_obj_id);
1099 }
1100
1101 $this->events->userDeassigned($assignment);
1102 return $this;
1103 }
1104
1108 public function hasAssignmentOf(int $a_user_id) : bool
1109 {
1110 return $this->getAmountOfAssignmentsOf($a_user_id) > 0;
1111 }
1112
1117 public function getAmountOfAssignmentsOf(int $a_user_id) : int
1118 {
1119 return count($this->getAssignmentsOf($a_user_id));
1120 }
1121
1129 public function getAssignmentsOf(int $a_user_id) : array
1130 {
1131 $prg_ids = $this->getIdsFromNodesOnPathFromRootToHere();
1132 $assignments = [];
1133 foreach ($prg_ids as $prg_id) {
1134 $assignments = array_merge(
1135 $assignments,
1136 $this->assignment_repository->getByUsrIdAndPrgId($a_user_id, $prg_id)
1137 );
1138 }
1139 usort($assignments, function ($a_one, $a_other) {
1140 return strcmp(
1141 $a_one->getLastChange()->format('Y-m-d'),
1142 $a_other->getLastChange()->format('Y-m-d')
1143 );
1144 });
1146 return array_map(function ($ass) use ($assignment_repository) {
1147 return $assignment_repository->getInstanceByModel($ass);
1148 }, array_values($assignments)); // use array values since we want keys 0...
1149 }
1150
1156 public function getAssignments() : array
1157 {
1159 return array_map(function ($ass) use ($assignment_repository) {
1160 return $assignment_repository->getInstanceByModel($ass);
1161 }, array_values($this->getAssignmentsRaw())); // use array values since we want keys 0...
1162 }
1163
1168 public function getMembers() : array
1169 {
1170 $usr_ids = [];
1171 foreach ($this->getProgresses() as $progress) {
1172 $usr_ids[] = $progress->getUserId();
1173 }
1174 return array_unique($usr_ids);
1175 }
1179 public function getLocalMembers() : array
1180 {
1181 if (!$this->members_cache) {
1182 $this->members_cache = array_map(
1183 function ($assignment) {
1184 return $assignment->getUserId();
1185 },
1186 $this->assignment_repository->getByPrgId($this->getId())
1187 );
1188 }
1189 return $this->members_cache;
1190 }
1191
1195 public function hasAssignments() : bool
1196 {
1197 return count($this->getAssignments()) > 0;
1198 }
1199
1204 {
1205 $this->members_cache = null;
1206 $assignments = $this->getAssignmentRepository()->getByPrgId((int) $this->getId());
1207 foreach ($assignments as $ass) {
1208 $ass->updateFromProgram();
1209 }
1210 return $this;
1211 }
1212
1218 public function getAssignmentsOfSingleProgramForUser(int $usr_id) : array
1219 {
1220 return $this->assignment_repository->getByUsrIdAndPrgId($usr_id, $this->getId());
1221 }
1222
1226 public function hasAssignmentsOfSingleProgramForUser(int $usr_id) : bool
1227 {
1228 return count($this->getAssignmentsOfSingleProgramForUser($usr_id)) > 0;
1229 }
1230
1231
1233 // USER PROGRESS
1235
1240 {
1241 return $this->progress_repository->createFor($this->getSettings(), $ass, $acting_user);
1242 }
1243
1250 public function getProgressesOf(int $a_user_id) : array
1251 {
1252 return $this->progress_repository->getByPrgIdAndUserId($this->getId(), $a_user_id);
1253 }
1254
1255 public function getProgressForAssignment(int $assignment_id) : ?ilStudyProgrammeProgress
1256 {
1257 return $this->getProgressRepository()->getByPrgIdAndAssignmentId($this->getId(), $assignment_id);
1258 }
1259
1266 public function addMissingProgresses() : void
1267 {
1268 $logger = $this->getLogger();
1272
1273 $assignment_ids_here = array_unique(
1274 array_map(
1275 function ($progress) {
1276 return $progress->getAssignmentId();
1277 },
1278 $progress_repository->getByPrgId((int) $this->getId())
1279 )
1280 );
1281
1282 $nodes_below = $this->getChildren(true); //include references
1283 foreach ($assignment_ids_here as $ass_id) {
1284 foreach ($nodes_below as $node) {
1285 $node_id = $node->getId();
1286 if (!$progress_repository->getByPrgIdAndAssignmentId($node_id, $ass_id)) {
1287 $new_progress = $progress_repository->createFor(
1288 $settings_repository->get($node_id),
1289 $assignment_repository->get($ass_id),
1290 null //acting user
1291 );
1292 $progress_repository->update(
1293 $new_progress->withStatus(ilStudyProgrammeProgress::STATUS_NOT_RELEVANT)
1294 );
1295
1296 $logger->log("Added progress for assingnment $ass_id at node $node_id.");
1297 }
1298 $node->addMissingProgresses();
1299 }
1300 }
1301 }
1302
1308 public function getProgresses() : array
1309 {
1310 return $this->progress_repository->getByPrgId($this->getId());
1311 }
1312
1316 public function hasProgresses() : bool
1317 {
1318 return count($this->getProgresses()) > 0;
1319 }
1320
1324 public function hasRelevantProgresses() : bool
1325 {
1326 foreach ($this->getProgresses() as $progress) {
1327 if ($progress->isRelevant()) {
1328 return true;
1329 }
1330 }
1331 return false;
1332 }
1333
1339 public function getIdsOfUsersWithRelevantProgress() : array
1340 {
1341 $returns = array();
1342 foreach ($this->getProgresses() as $progress) {
1343 if ($progress->isRelevant()) {
1344 $returns[] = $progress->getUserId();
1345 }
1346 }
1347 return array_unique($returns);
1348 }
1349
1355 public function getIdsOfUsersWithCompletedProgress() : array
1356 {
1357 $returns = array();
1358 foreach ($this->getProgresses() as $progress) {
1359 if ($progress->isSuccessful() && !$progress->isSuccessfulExpired()) {
1360 $returns[] = $progress->getUserId();
1361 }
1362 }
1363 return array_unique($returns);
1364 }
1365
1371 public function getIdsOfUsersWithFailedProgress() : array
1372 {
1373 $returns = array();
1374 foreach ($this->getProgresses() as $progress) {
1375 if ($progress->isFailed() || $progress->isSuccessfulExpired()) {
1376 $returns[] = $progress->getUserId();
1377 }
1378 }
1379 return array_unique(array_diff($returns, $this->getIdsOfUsersWithCompletedProgress()));
1380 }
1381
1382
1390 {
1391 $returns = array();
1392 foreach ($this->getProgresses() as $progress) {
1393 if ($progress->isRelevant() && !$progress->isSuccessful()) {
1394 $returns[] = $progress->getUserId();
1395 }
1396 }
1397 return array_unique($returns);
1398 }
1399
1400
1402 // AUTOMATIC CONTENT CATEGORIES
1404
1409 public function getAutomaticContentCategories() : array
1410 {
1411 return $this->auto_categories_repository->getFor($this->getId());
1412 }
1413
1414 public function hasAutomaticContentCategories() : bool
1415 {
1416 return count($this->getAutomaticContentCategories()) > 0;
1417 }
1418
1419
1424 public function storeAutomaticContentCategory(int $category_ref_id) : void
1425 {
1426 $ac = $this->auto_categories_repository->create(
1427 $this->getId(),
1428 $category_ref_id
1429 );
1430 $this->auto_categories_repository->update($ac);
1431 }
1432
1437 public function deleteAutomaticContentCategories(array $category_ids = []) : void
1438 {
1439 $this->auto_categories_repository->delete($this->getId(), $category_ids);
1440 }
1441
1446 {
1447 $this->auto_categories_repository->deleteFor($this->getId());
1448 }
1449
1453 public static function addCrsToProgrammes(int $crs_ref_id, int $cat_ref_id) : void
1454 {
1455 foreach (self::getProgrammesMonitoringCategory($cat_ref_id) as $prg) {
1456 $course_ref = new ilObjCourseReference();
1457 $course_ref->setTitleType(ilObjCourseReference::TITLE_TYPE_REUSE);
1458 $course_ref->setTargetRefId($crs_ref_id);
1459 $course_ref->create();
1460 $course_ref->createReference();
1461 $course_ref->putInTree($prg->getRefId());
1462 $course_ref->setPermissions($crs_ref_id);
1463 $course_ref->setTargetId(ilObject::_lookupObjectId($crs_ref_id));
1464 $course_ref->update();
1465 $lp = new ilLPObjSettings($course_ref->getId());
1466 $lp->insert();
1467 $lp->setMode($lp::LP_MODE_COURSE_REFERENCE);
1468 $lp->update(false);
1469 }
1470 }
1471
1477 public static function removeCrsFromProgrammes(int $crs_ref_id, int $cat_ref_id)
1478 {
1479 foreach (self::getProgrammesMonitoringCategory($cat_ref_id) as $prg) {
1480 foreach ($prg->getLPChildren() as $child) {
1481 if ((int) $child->getTargetRefId() === $crs_ref_id) {
1482 $child->delete();
1483 }
1484 }
1485 }
1486 }
1487
1492 protected static function getProgrammesMonitoringCategory(int $cat_ref_id) : array
1493 {
1494 $db = ilStudyProgrammeDIC::dic()['model.AutoCategories.ilStudyProgrammeAutoCategoriesRepository'];
1495 $programmes = array_map(
1496 function ($rec) {
1497 $prg_obj_id = (int) array_shift(array_values($rec));
1498 $prg_ref_id = (int) array_shift(ilObject::_getAllReferences($prg_obj_id));
1499 $prg = self::getInstanceByRefId($prg_ref_id);
1500 if ($prg->isAutoContentApplicable()) {
1501 return $prg;
1502 }
1503 },
1504 $db::getProgrammesFor($cat_ref_id)
1505 );
1506 return $programmes;
1507 }
1508
1515 public function isAutoContentApplicable() : bool
1516 {
1517 $valid_status = in_array(
1518 $this->getSettings()->getAssessmentSettings()->getStatus(),
1519 [
1522 ]
1523 );
1524
1525 $crslnk_allowed = (
1526 $this->hasLPChildren()
1527 || $this->getAmountOfChildren(true) === 0
1528 );
1529
1530 return $valid_status && $crslnk_allowed;
1531 }
1532
1533
1535 // AUTOMATIC MEMBERSHIPS
1537
1542 public function getAutomaticMembershipSources() : array
1543 {
1544 return $this->auto_memberships_repository->getFor($this->getId());
1545 }
1546
1550 public function storeAutomaticMembershipSource(string $type, int $src_id) : void
1551 {
1552 $ams = $this->auto_memberships_repository->create($this->getId(), $type, $src_id, false);
1553 $this->auto_memberships_repository->update($ams);
1554 }
1555
1559 public function deleteAutomaticMembershipSource(string $type, int $src_id) : void
1560 {
1561 $this->auto_memberships_repository->delete($this->getId(), $type, $src_id);
1562 }
1563
1568 {
1569 $this->auto_memberships_repository->deleteFor($this->getId());
1570 }
1571
1575 public function disableAutomaticMembershipSource(string $type, int $src_id) : void
1576 {
1577 $ams = $this->auto_memberships_repository->create($this->getId(), $type, $src_id, false);
1578 $this->auto_memberships_repository->update($ams);
1579 }
1580
1585 public function enableAutomaticMembershipSource(string $type, int $src_id, $assign_now = false) : void
1586 {
1587 if ($assign_now) {
1589 $member_ids = $this->getMembersOfMembershipSource($type, $src_id);
1590 foreach ($member_ids as $usr_id) {
1591 if (!$this->getAssignmentsOfSingleProgramForUser($usr_id)) {
1592 $this->assignUser($usr_id, $assigned_by);
1593 }
1594 }
1595 }
1596 $ams = $this->auto_memberships_repository->create($this->getId(), $type, $src_id, true);
1597 $this->auto_memberships_repository->update($ams);
1598 }
1599
1605 protected function getMembersOfMembershipSource(string $src_type, int $src_id) : array
1606 {
1607 $source_reader = $this->membersourcereader_factory->getReaderFor($src_type, $src_id);
1608 return $source_reader->getMemberIds();
1609 }
1610
1611
1616 protected static function getProgrammesMonitoringMemberSource(string $src_type, int $src_id) : array
1617 {
1618 $db = ilStudyProgrammeDIC::dic()['model.AutoMemberships.ilStudyProgrammeAutoMembershipsRepository'];
1619 $programmes = array_map(
1620 function ($rec) {
1621 $prg_obj_id = (int) array_shift(array_values($rec));
1622 $prg_ref_id = (int) array_shift(ilObject::_getAllReferences($prg_obj_id));
1623 $prg = self::getInstanceByRefId($prg_ref_id);
1624 return $prg;
1625 },
1626 $db::getProgrammesFor($src_type, $src_id)
1627 );
1628 return $programmes;
1629 }
1630
1631 public static function addMemberToProgrammes(string $src_type, int $src_id, int $usr_id) : void
1632 {
1633 foreach (self::getProgrammesMonitoringMemberSource($src_type, $src_id) as $prg) {
1634 if ($prg->isActive() &&
1635 !$prg->hasAssignmentsOfSingleProgramForUser($usr_id)) {
1637 $prg->assignUser($usr_id, $assigned_by);
1638 }
1639 }
1640 }
1641
1642 public static function removeMemberFromProgrammes(string $src_type, int $src_id, int $usr_id) : void
1643 {
1644 $now = new DateTimeImmutable();
1645 foreach (self::getProgrammesMonitoringMemberSource($src_type, $src_id) as $prg) {
1646 foreach ($prg->getProgressesOf($usr_id) as $progress) {
1647 if ($progress->getStatus() !== ilStudyProgrammeProgress::STATUS_IN_PROGRESS) {
1648 continue;
1649 }
1650 $assignments = $prg->getAssignmentsOfSingleProgramForUser($usr_id);
1651 $next_membership_source = $prg->getApplicableMembershipSourceForUser($usr_id, $src_type);
1652
1653 foreach ($assignments as $assignment) {
1654 if (!is_null($next_membership_source) && $next_membership_source->isEnabled()) {
1655 $new_src_type = $next_membership_source->getSourceType();
1656 $assigned_by = ilStudyProgrammeAutoMembershipSource::SOURCE_MAPPING[$new_src_type];
1657 $assignment = $assignment->withLastChange($assigned_by, $now);
1658 $prg->assignment_repository->update($assignment);
1659 break;
1660 } else {
1661 $assignment_repository = ilStudyProgrammeDIC::dic()['ilStudyProgrammeUserAssignmentDB'];
1662 $user_assignment = $assignment_repository->getInstanceByModel($assignment);
1663 $prg->removeAssignment($user_assignment);
1664 }
1665 }
1666 }
1667 }
1668 }
1669
1673 public function getApplicableMembershipSourceForUser(int $usr_id, string $exclude_type)
1674 {
1675 foreach ($this->getAutomaticMembershipSources() as $ams) {
1676 $src_type = $ams->getSourceType();
1677 if ($src_type !== $exclude_type) {
1678 $source_members = $this->getMembersOfMembershipSource($src_type, $ams->getSourceId());
1679 if (in_array($usr_id, $source_members)) {
1680 return $ams;
1681 }
1682 }
1683 }
1684 return null;
1685 }
1686
1688 // HELPERS
1690
1694 protected function updateLastChange() : void
1695 {
1696 $this->getSettings()->updateLastChange();
1697 if ($parent = $this->getParent()) {
1698 $parent->updateLastChange();
1699 }
1700 $this->update();
1701 }
1702
1709 protected function getIdsFromNodesOnPathFromRootToHere(bool $include_references = false) : array
1710 {
1711 $prg_ids = array_map(function ($par) {
1712 return $par->getId();
1713 }, $this->getParents($include_references));
1714 $prg_ids[] = $this->getId();
1715 return $prg_ids;
1716 }
1717
1723 protected function getAssignmentsRaw() : array
1724 {
1725 $assignments = [];
1726 foreach ($this->getIdsFromNodesOnPathFromRootToHere(true) as $prg_id) {
1727 $assignments = array_merge($this->assignment_repository->getByPrgId($prg_id), $assignments);
1728 }
1729 usort(
1730 $assignments,
1731 function (ilStudyProgrammeAssignment $a_one, ilStudyProgrammeAssignment $a_other) {
1732 return strcmp(
1733 $a_one->getLastChange()->format('Y-m-d'),
1734 $a_other->getLastChange()->format('Y-m-d')
1735 );
1736 }
1737 );
1738 return $assignments;
1739 }
1740
1745 public static function setProgressesCompletedFor(int $a_obj_id, int $a_user_id) : void
1746 {
1747 // We only use courses via crs_refs
1748 $type = ilObject::_lookupType($a_obj_id);
1749 if ($type == "crsr") {
1750 foreach (ilObject::_getAllReferences($a_obj_id) as $ref_id) {
1752 }
1753 }
1754 }
1755
1760 int $a_ref_id,
1761 int $a_obj_id,
1762 int $a_user_id
1763 ) : void {
1764 global $DIC; // TODO: replace this by a settable static for testing purpose?
1765 $tree = $DIC['tree'];
1766 $node_data = $tree->getParentNodeData($a_ref_id);
1767 if ($node_data["type"] !== "prg") {
1768 return;
1769 }
1771 $prg = ilObjStudyProgramme::getInstanceByRefId($node_data["child"]);
1772 if ($prg->getLPMode() != ilStudyProgrammeSettings::MODE_LP_COMPLETED) {
1773 return;
1774 }
1775
1776 $now = new DateTimeImmutable();
1777 foreach ($prg->getProgressesOf($a_user_id) as $progress) {
1778 $progress_deadline = $progress->getDeadline();
1779 if (
1780 (is_null($progress_deadline) || $progress_deadline >= $now)
1781 && $progress->getStatus() === ilStudyProgrammeProgress::STATUS_IN_PROGRESS
1782 ) {
1783 $prg->succeed($progress->getId(), $a_obj_id);
1784 }
1785 }
1786 }
1787
1794 protected static function getParentId(ilObject $a_object)
1795 {
1796 global $DIC;
1797 $tree = $DIC['tree'];
1798 if (!$tree->isInTree($a_object->getRefId())) {
1799 return null;
1800 }
1801
1802 $nd = $tree->getParentNodeData($a_object->getRefId());
1803 return $nd["obj_id"];
1804 }
1805
1806
1807 public function updateCustomIcon() : void
1808 {
1809 $customIcon = $this->custom_icon_factory->getByObjId($this->getId(), $this->getType());
1810 $subtype = $this->getSubType();
1811
1812 if ($subtype
1813 && $this->webdir->has($subtype->getIconPath(true))
1814 && $subtype->getIconPath(true) != $subtype->getIconPath(false)
1815 ) {
1816 $icon = $subtype->getIconPath(true);
1817 $customIcon->saveFromSourceFile($icon);
1818 } else {
1819 $customIcon->remove();
1820 }
1821 }
1822
1824 // HOOKS
1826
1837 public static function getCreatableSubObjects(array $a_subobjects, $a_ref_id) : array
1838 {
1839 if ($a_ref_id === null) {
1840 return $a_subobjects;
1841 }
1842
1843 if (ilObject::_lookupType($a_ref_id, true) != "prg") {
1844 throw new ilException("Ref-Id '$a_ref_id' does not belong to a study programme object.");
1845 }
1846
1847 $parent = ilObjStudyProgramme::getInstanceByRefId($a_ref_id);
1848
1849 $mode = $parent->getLPMode();
1850
1851 switch ($mode) {
1853 $possible_subobjects = $a_subobjects;
1854 break;
1856 $possible_subobjects = [
1857 "prg" => $a_subobjects["prg"],
1858 "prgr" => $a_subobjects["prgr"]
1859 ];
1860 break;
1862 $possible_subobjects = ['crsr' => $a_subobjects['crsr']];
1863 break;
1864 default:
1865 throw new ilException("Undefined mode for study programme: '$mode'");
1866 }
1867
1868 if ($parent->hasAutomaticContentCategories()) {
1869 $possible_subobjects = array_filter(
1870 $possible_subobjects,
1871 function ($subtype) {
1872 return $subtype === 'crsr';
1873 },
1874 ARRAY_FILTER_USE_KEY
1875 );
1876 }
1877 return $possible_subobjects;
1878 }
1879
1880 public static function sendReAssignedMail(int $ref_id, int $usr_id) : bool
1881 {
1882 global $DIC;
1883 $lng = $DIC['lng'];
1884 $lng->loadLanguageModule("prg");
1885 $lng->loadLanguageModule("mail");
1886 $log = $DIC['ilLog'];
1887
1889 $prg_should_send_mail = $prg->getSettings()->getAutoMailSettings()->getSendReAssignedMail();
1890 if (!$prg_should_send_mail) {
1891 $log->write("Send re assign mail is deactivated in study programme settings");
1892 return false;
1893 }
1894
1895 $subject = $lng->txt("re_assigned_mail_subject");
1896 $gender = ilObjUser::_lookupGender($usr_id);
1898 $body = sprintf(
1899 $lng->txt("re_assigned_mail_body"),
1900 $lng->txt("mail_salutation_" . $gender),
1901 $name,
1902 $prg->getTitle()
1903 );
1904
1905 $send = true;
1906 $mail = new ilMail(ANONYMOUS_USER_ID);
1907 try {
1908 $mail->enqueue(
1909 ilObjUser::_lookupLogin($usr_id),
1910 '',
1911 '',
1912 $subject,
1913 $body,
1914 null
1915 );
1916 } catch (Exception $e) {
1917 $send = false;
1918 }
1919
1920 return $send;
1921 }
1922
1923 public static function sendInvalidateMail(int $ref_id, int $usr_id) : bool
1924 {
1925 global $DIC;
1926 $lng = $DIC['lng'];
1927 $lng->loadLanguageModule("prg");
1928 $lng->loadLanguageModule("mail");
1929
1931
1932 $subject = $lng->txt("invalidate_mail_subject");
1933 $gender = ilObjUser::_lookupGender($usr_id);
1935 $body = sprintf(
1936 $lng->txt("invalidate_mail_body"),
1937 $lng->txt("mail_salutation_" . $gender),
1938 $name,
1939 $prg->getTitle()
1940 );
1941
1942 $send = true;
1943 $mail = new ilMail(ANONYMOUS_USER_ID);
1944 try {
1945 $mail->enqueue(
1946 ilObjUser::_lookupLogin($usr_id),
1947 '',
1948 '',
1949 $subject,
1950 $body,
1951 null
1952 );
1953 } catch (Exception $e) {
1954 $send = false;
1955 }
1956
1957 return $send;
1958 }
1959
1960 public static function sendInformToReAssignMail(int $progress_id, int $usr_id) : void
1961 {
1962 global $DIC;
1963 $lng = $DIC['lng'];
1964 $log = $DIC['ilLog'];
1965 $lng->loadLanguageModule("prg");
1966 $lng->loadLanguageModule("mail");
1967
1968 $usr_progress_db = ilStudyProgrammeDIC::dic()['ilStudyProgrammeUserProgressDB'];
1969 $usr_progress = $usr_progress_db->get($progress_id);
1970 $prg = ilObjStudyProgramme::getInstanceByObjId($usr_progress->getNodeId());
1971 $prg_should_send_mail = $prg->getSettings()->getAutoMailSettings()
1972 ->getReminderNotRestartedByUserDays() > 0;
1973
1974 if (!$prg_should_send_mail) {
1975 $log->write("Send info to re assign mail is deactivated in study programme settings");
1976 return;
1977 }
1978
1979 $subject = $lng->txt("info_to_re_assign_mail_subject");
1980 $gender = ilObjUser::_lookupGender($usr_id);
1982 $body = sprintf(
1983 $lng->txt("info_to_re_assign_mail_body"),
1984 $lng->txt("mail_salutation_" . $gender),
1985 $name,
1986 $prg->getTitle()
1987 );
1988
1989 $send = true;
1990 $mail = new ilMail(ANONYMOUS_USER_ID);
1991 try {
1992 $mail->enqueue(
1993 ilObjUser::_lookupLogin($usr_id),
1994 '',
1995 '',
1996 $subject,
1997 $body,
1998 null
1999 );
2000 } catch (Exception $e) {
2001 $send = false;
2002 }
2003
2004 if ($send) {
2005 $usr_progress_db->sentExpiryInfoFor($usr_progress->getId());
2006 }
2007 }
2008
2012 public static function sendRiskyToFailMail(int $progress_id, int $usr_id) : void
2013 {
2014 global $DIC;
2015 $lng = $DIC['lng'];
2016 $log = $DIC['ilLog'];
2017 $lng->loadLanguageModule("prg");
2018 $lng->loadLanguageModule("mail");
2019
2020 $usr_progress_db = ilStudyProgrammeDIC::dic()['ilStudyProgrammeUserProgressDB'];
2021 $usr_progress = $usr_progress_db->get($progress_id);
2022 $prg = ilObjStudyProgramme::getInstanceByObjId($usr_progress->getNodeId());
2023 $prg_should_send_mail = $prg->getSettings()->getAutoMailSettings()
2024 ->getProcessingEndsNotSuccessfulDays() > 0;
2025
2026 if (!$prg_should_send_mail) {
2027 $log->write("Send risky to fail mail is deactivated in study programme settings");
2028 return;
2029 }
2030
2031 $subject = $lng->txt("risky_to_fail_mail_subject");
2032 $gender = ilObjUser::_lookupGender($usr_id);
2034 $body = sprintf(
2035 $lng->txt("risky_to_fail_mail_body"),
2036 $lng->txt("mail_salutation_" . $gender),
2037 $name,
2038 $prg->getTitle()
2039 );
2040
2041 $send = true;
2042 $mail = new ilMail(ANONYMOUS_USER_ID);
2043 try {
2044 $mail->enqueue(
2045 ilObjUser::_lookupLogin($usr_id),
2046 '',
2047 '',
2048 $subject,
2049 $body,
2050 null
2051 );
2052 } catch (Exception $e) {
2053 $send = false;
2054 }
2055
2056 if ($send) {
2057 $usr_progress_db->sentRiskyToFailFor($usr_progress->getId());
2058 }
2059 }
2060
2064 public function getIdsOfSuccessfulChildren(int $ass_id) : array
2065 {
2066 $children = $this->getChildren(true);
2067
2068 $ids = array();
2069
2070 foreach ($children as $child) {
2071 $prgrs = $child->getProgressForAssignment($ass_id);
2072 if (!$prgrs->isSuccessful()) {
2073 continue;
2074 }
2075 $ids[] = [$child->getId(), $child->getRefId()];
2076 }
2077 if (count($ids) > 0) {
2078 return $ids;
2079 }
2080
2081 //courses:
2082 $children = $this->getTree()->getChildsByType($this->getRefId(), "crsr");
2083 $usr_id = $this->getAssignmentRepository()->get($ass_id)->getUserId();
2084 foreach ($children as $child) {
2085 if (ilObject::_exists($child['ref_id'], true) &&
2086 is_null(ilObject::_lookupDeletedDate($child['ref_id']))
2087 ) {
2088 continue;
2089 }
2090 $crs_id = ilContainerReference::_lookupTargetId($child["obj_id"]);
2091
2092 if (ilLPStatus::_hasUserCompleted($crs_id, $usr_id)) {
2093 $ref_id = ilContainerReference::_lookupTargetRefId($child["obj_id"]);
2094 $ids[] = [$crs_id, $ref_id];
2095 }
2096 }
2097 return $ids;
2098 }
2099
2100 public function getNamesOfCompletedOrAccreditedChildren(int $ass_id) : array
2101 {
2102 return array_map(
2103 function ($entry) {
2104 list($obj_id, $ref_id) = $entry;
2105 return ilObject::_lookupTitle($obj_id);
2106 },
2107 $this->getIdsOfSuccessfulChildren($ass_id)
2108 );
2109 }
2110
2111 protected function getLoggedInUserId() : int
2112 {
2113 return (int) $this->ilUser->getId();
2114 }
2115
2116 protected function getNow() : DateTimeImmutable
2117 {
2118 return new DateTimeImmutable();
2119 }
2120
2122 {
2123 return $this->assignment_repository->get($progress->getAssignmentId());
2124 }
2125
2127 {
2128 return $this->settings_repository->get($progress->getNodeId());
2129 }
2130
2131
2132 protected function getObjIdsOfChildren(int $node_obj_id) : array
2133 {
2134 $node_ref_id = self::getRefIdFor($node_obj_id);
2135
2136 $prgs = $this->tree->getChildsByType($node_ref_id, "prg");
2137 $prg_ids = array_map(
2138 function ($nd) {
2139 return (int) $nd['obj_id'];
2140 },
2141 $prgs
2142 );
2143
2144 $prg_ref_ids = [];
2145 $prg_refs = $this->tree->getChildsByType($node_ref_id, "prgr");
2146 foreach ($prg_refs as $ref) {
2147 $ref_obj = new ilObjStudyProgrammeReference((int) $ref['ref_id']);
2148 $prg_ref_ids[] = (int) $ref_obj->getReferencedObject()->getId();
2149 }
2150
2151 return array_merge($prg_ids, $prg_ref_ids);
2152 }
2153
2157 public function getChildrenProgress($progress) : array
2158 {
2159 $children = $this->getObjIdsOfChildren($progress->getNodeId());
2160 $ass_id = $progress->getAssignmentId();
2161
2162 $ret = [];
2163 foreach ($children as $child_obj_id) {
2164 $progress = $this->getProgressRepository()->getByPrgIdAndAssignmentId($child_obj_id, $ass_id);
2165 $ret[] = $this->getProgressRepository()->getByPrgIdAndAssignmentId($child_obj_id, $ass_id);
2166 }
2167
2168 return $ret;
2169 }
2170
2172 {
2173 $assignment_id = $progress->getAssignmentId();
2174
2175 $prg_ref_id = $this->getRefIdFor($progress->getNodeId());
2176 $parent_node = $this->tree->getParentNodeData($prg_ref_id);
2177 $parent_prg_obj_id = (int) $parent_node["obj_id"];
2178
2179 $parent_progress = $this->progress_repository->getByPrgIdAndAssignmentId(
2180 $parent_prg_obj_id,
2181 $assignment_id
2182 );
2183
2184 if (!$parent_progress) {
2185 //maybe by reference?
2186 $obj_ids_referencing_node = ilContainerReference::_lookupSourceIds($progress->getNodeId());
2187 foreach (ilContainerReference::_lookupSourceIds($progress->getNodeId()) as $obj_id_referencing) {
2188 foreach (ilObject::_getAllReferences($obj_id_referencing) as $ref_id_referencing) {
2189 $parent_node = $this->tree->getParentNodeData($ref_id_referencing);
2190 $parent_prg_obj_id = (int) $parent_node["obj_id"];
2191
2192 $parent_progress = $this->progress_repository->getByPrgIdAndAssignmentId(
2193 $parent_prg_obj_id,
2194 $assignment_id
2195 );
2196 if ($parent_progress) {
2197 return $parent_progress;
2198 }
2199 }
2200 }
2201 }
2202
2203 return $parent_progress; //...which is null here
2204 }
2205
2206
2208 {
2209 $sum = 0;
2210 foreach ($this->getChildrenProgress($progress) as $child_progress) {
2211 if (!is_null($child_progress) && $child_progress->isRelevant()) {
2212 $sum += $child_progress->getAmountOfPoints();
2213 }
2214 }
2215 return $sum;
2216 }
2217
2219 {
2220 $sum = 0;
2221 $children = $this->getChildrenProgress($progress);
2222 foreach ($children as $child_progress) {
2223 if (!is_null($child_progress) && $child_progress->isSuccessful()) {
2224 $sum += $child_progress->getAmountOfPoints();
2225 }
2226 }
2227 return $sum;
2228 }
2229
2230 protected function refreshLPStatus(int $usr_id, int $node_obj_id = null) : void
2231 {
2232 if (is_null($node_obj_id)) {
2233 $node_obj_id = $this->getId();
2234 }
2235 ilLPStatusWrapper::_updateStatus($node_obj_id, $usr_id);
2236 }
2237
2239 {
2240 $parent_progress = $this->getParentProgress($progress);
2241 if (is_null($parent_progress)) {
2242 return $progress;
2243 }
2244 $parent_progress = $this->recalculateProgressStatus($parent_progress);
2245 $this->getProgressRepository()->update($parent_progress);
2246 return $this->updateParentProgress($parent_progress); //recurse
2247 }
2248
2250 {
2251 if (!$progress->isRelevant()) {
2252 return $progress;
2253 }
2254 $node_settings = $this->getSettingsRepository()->get($progress->getNodeId());
2255 $completion_mode = $node_settings->getLPMode();
2256
2257 if ($completion_mode === ilStudyProgrammeSettings::MODE_UNDEFINED) {
2258 return $progress;
2259 }
2260
2261 $required_points = $progress->getAmountOfPoints();
2262
2263 if ($completion_mode === ilStudyProgrammeSettings::MODE_LP_COMPLETED) {
2264 $achieved_points = 0;
2265
2266 $node_ref = self::getRefIdFor($progress->getNodeId());
2267 $children = $this->tree->getChildsByType($node_ref, "crsr");
2268 foreach ($children as $child) {
2269 if (ilObject::_exists($child['ref_id'], true) &&
2270 is_null(ilObject::_lookupDeletedDate($child['ref_id']))
2271 ) {
2272 $crs_id = ilContainerReference::_lookupTargetId($child["obj_id"]);
2273 if (ilLPStatus::_hasUserCompleted($crs_id, $progress->getUserId())) {
2274 $achieved_points = $progress->getAmountOfPoints();
2275 break;
2276 }
2277 }
2278 }
2279 }
2280
2281 if ($completion_mode === ilStudyProgrammeSettings::MODE_POINTS) {
2282 $achieved_points = $this->getAchievedPointsOfChildren($progress);
2283 }
2284
2285 $progress = $progress->withCurrentAmountOfPoints($achieved_points);
2286 $successful = ($achieved_points >= $required_points);
2287
2288 if ($successful && !$progress->isSuccessful()) {
2289 $progress = $progress
2291 ->withCompletion(null, new DateTimeImmutable());
2292
2293 // there was a status change, so:
2294 $this->events->userSuccessful($progress);
2295 }
2296
2297 if (!$successful && $progress->isSuccessful()) {
2298 $progress = $progress
2300 ->withCompletion(null, null)
2301 ->withValidityOfQualification(null);
2302 }
2303
2304 return $progress;
2305 }
2306
2307
2308 protected function applyProgressDeadline(ilStudyProgrammeProgress $progress, int $acting_usr_id = null) : ilStudyProgrammeProgress
2309 {
2310 $today = $this->getNow();
2312 $deadline = $progress->getDeadline();
2313
2314 if (is_null($acting_usr_id)) {
2315 $acting_usr_id = $this->getLoggedInUserId();
2316 }
2317
2318 switch ($progress->getStatus()) {
2319
2321 if (!is_null($deadline)
2322 && $deadline->format($format) < $today->format($format)
2323 ) {
2324 $progress = $progress->markFailed($this->getNow(), $acting_usr_id);
2325 }
2326 break;
2327
2329 if (is_null($deadline)
2330 || $deadline->format($format) >= $today->format($format)
2331 ) {
2332 $progress = $progress->markNotFailed($this->getNow(), $acting_usr_id);
2333 }
2334 break;
2335 }
2336
2337 return $progress;
2338 }
2339
2340 public function markAccredited(
2341 int $progress_id,
2342 int $acting_usr_id,
2343 ilPRGMessageCollection $err_collection
2344 ) : void {
2345 $progress = $this->getProgressRepository()->get($progress_id);
2346 $new_status = $progress::STATUS_ACCREDITED;
2347
2348 if (!$progress->isRelevant()) {
2349 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2350 return;
2351 }
2352 if ($progress->getStatus() === $new_status) {
2353 $err_collection->add(false, 'status_unchanged', $this->getProgressIdString($progress));
2354 return;
2355 }
2356 if (!$progress->isTransitionAllowedTo($new_status)) {
2357 $err_collection->add(false, 'status_transition_not_allowed', $this->getProgressIdString($progress));
2358 return;
2359 }
2360
2361 $progress = $progress
2362 ->markAccredited($this->getNow(), $acting_usr_id)
2363 ->withCurrentAmountOfPoints($progress->getAmountOfPoints());
2364
2365 if (!$progress->getValidityOfQualification()) {
2366 $progress = $this->updateProgressValidityFromSettings($progress);
2367 }
2368
2369 $this->events->userSuccessful($progress);
2370
2371 $this->getProgressRepository()->update($progress);
2372 $this->refreshLPStatus($progress->getUserId());
2373 $this->updateParentProgress($progress);
2374 $err_collection->add(true, 'status_changed', $this->getProgressIdString($progress));
2375 }
2376
2377 public function unmarkAccredited(
2378 int $progress_id,
2379 int $acting_usr_id,
2380 ilPRGMessageCollection $err_collection
2381 ) : void {
2382 $progress = $this->getProgressRepository()->get($progress_id);
2383 $new_status = $progress::STATUS_IN_PROGRESS;
2384
2385 if (!$progress->isRelevant()) {
2386 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2387 return;
2388 }
2389 if ($progress->getStatus() === $new_status) {
2390 $err_collection->add(false, 'status_unchanged', $this->getProgressIdString($progress));
2391 return;
2392 }
2393 if (!$progress->isTransitionAllowedTo($new_status)
2394 //special case: completion may not be revoked manually (but might be as a calculation-result of underlying progresses)
2395 || $progress->getStatus() === $progress::STATUS_COMPLETED
2396 ) {
2397 $err_collection->add(false, 'status_transition_not_allowed', $this->getProgressIdString($progress));
2398 return;
2399 }
2400
2401 $progress = $progress
2402 ->unmarkAccredited($this->getNow(), $acting_usr_id);
2403
2404 $achieved_points = $this->getAchievedPointsOfChildren($progress);
2405 $progress = $progress->withCurrentAmountOfPoints($achieved_points);
2406
2407 $progress = $this->applyProgressDeadline($progress);
2408
2409 $this->getProgressRepository()->update($progress);
2410 $this->refreshLPStatus($progress->getUserId());
2411 $this->updateParentProgress($progress);
2412 $err_collection->add(true, 'status_changed', $this->getProgressIdString($progress));
2413 }
2414
2415 public function markFailed(int $progress_id, int $acting_usr_id) : void
2416 {
2417 $progress = $this->getProgressRepository()->get($progress_id);
2418 if (!$progress->isRelevant()) {
2419 return;
2420 }
2421 $progress = $progress->markFailed($this->getNow(), $acting_usr_id);
2422
2423 $this->getProgressRepository()->update($progress);
2424 $this->refreshLPStatus($progress->getUserId());
2425 $this->updateParentProgress($progress);
2426 }
2427
2428 public function markNotFailed(int $progress_id, int $acting_usr_id) : void
2429 {
2430 if (!$progress->isRelevant()) {
2431 return;
2432 }
2433 $progress = $this->getProgressRepository()->get($progress_id)
2434 ->markNotFailed($this->getNow(), $acting_usr_id);
2435
2436 $this->getProgressRepository()->update($progress);
2437 $this->refreshLPStatus($progress->getUserId());
2438 $this->updateParentProgress($progress);
2439 }
2440
2441 public function markNotRelevant(
2442 int $progress_id,
2443 int $acting_usr_id,
2444 ilPRGMessageCollection $err_collection
2445 ) : void {
2446 $progress = $this->getProgressRepository()->get($progress_id);
2447 if (!$progress->isRelevant()) {
2448 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2449 return;
2450 }
2451 if (is_null($this->getParentProgress($progress))) {
2452 $err_collection->add(false, 'will_not_set_top_progress_to_irrelevant', $this->getProgressIdString($progress));
2453 return;
2454 }
2455 if ($progress->getStatus() === ilStudyProgrammeProgress::STATUS_COMPLETED) {
2456 $err_collection->add(false, 'will_not_set_completed_progress_to_irrelevant_', $this->getProgressIdString($progress));
2457 return;
2458 }
2459
2460 $progress = $progress
2461 ->markNotRelevant($this->getNow(), $acting_usr_id);
2462
2463 $this->getProgressRepository()->update($progress);
2464 $this->refreshLPStatus($progress->getUserId());
2465 $this->updateParentProgress($progress);
2466 $err_collection->add(true, 'set_to_irrelevant', $this->getProgressIdString($progress));
2467 }
2468
2469 public function markRelevant(
2470 int $progress_id,
2471 int $acting_usr_id,
2472 ilPRGMessageCollection $err_collection
2473 ) : void {
2474 $progress = $this->getProgressRepository()->get($progress_id);
2475 if ($progress->isRelevant()) {
2476 $err_collection->add(false, 'will_not_modify_relevant_progress', $this->getProgressIdString($progress));
2477 return;
2478 }
2479
2480 $progress = $progress
2481 ->markRelevant($this->getNow(), $acting_usr_id);
2482
2483 $progress = $this->recalculateProgressStatus($progress);
2484
2485 $this->getProgressRepository()->update($progress);
2486 $this->refreshLPStatus($progress->getUserId());
2487 $this->updateParentProgress($progress);
2488 $err_collection->add(true, 'set_to_relevant', $this->getProgressIdString($progress));
2489 }
2490
2491 public function invalidate(int $progress_id) : void
2492 {
2493 $progress = $this->getProgressRepository()->get($progress_id)
2494 ->invalidate();
2495
2496 $this->getProgressRepository()->update($progress);
2497 $this->refreshLPStatus($progress->getUserId());
2498 $this->updateParentProgress($progress);
2499 }
2500
2501 public function succeed(int $progress_id, int $triggering_obj_id) : void
2502 {
2503 $progress = $this->getProgressRepository()->get($progress_id)
2504 ->succeed($this->getNow(), $triggering_obj_id);
2505
2506 $achieved_points = $progress->getAmountOfPoints();
2507 $progress = $progress->withCurrentAmountOfPoints($achieved_points);
2508 $progress = $this->updateProgressValidityFromSettings($progress);
2509
2510 $this->getProgressRepository()->update($progress);
2511
2512 $this->refreshLPStatus($progress->getUserId());
2513 $this->updateParentProgress($progress);
2514 }
2515
2516 public function changeProgressDeadline(
2517 int $progress_id,
2518 int $acting_usr_id,
2519 ilPRGMessageCollection $err_collection,
2520 ?DateTimeImmutable $deadline
2521 ) : void {
2522 $progress = $this->getProgressRepository()->get($progress_id);
2523
2524 if (!$progress->isRelevant()) {
2525 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2526 return;
2527 }
2528 if ($progress->isSuccessful()) {
2529 $err_collection->add(false, 'will_not_modify_deadline_on_successful_progress', $this->getProgressIdString($progress));
2530 return;
2531 }
2532
2533 $progress = $progress
2534 ->withDeadline($deadline)
2535 ->withLastChange($acting_usr_id, $this->getNow())
2536 ->withIndividualModifications(true);
2537
2538 $progress = $this->applyProgressDeadline($progress, $acting_usr_id);
2539
2540 $this->getProgressRepository()->update($progress);
2541 $this->refreshLPStatus($progress->getUserId());
2542 $this->updateParentProgress($progress);
2543 $err_collection->add(true, 'deadline_updated', $this->getProgressIdString($progress));
2544 }
2545
2547 int $progress_id,
2548 int $acting_usr_id,
2549 ilPRGMessageCollection $err_collection,
2550 ?DateTimeImmutable $validity
2551 ) : void {
2552 $progress = $this->getProgressRepository()->get($progress_id);
2553
2554 if (!$progress->isRelevant()) {
2555 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2556 return;
2557 }
2558 if (!$progress->isSuccessful()) {
2559 $err_collection->add(false, 'will_not_modify_validity_on_non_successful_progress', $this->getProgressIdString($progress));
2560 return;
2561 }
2562
2563 $progress = $progress
2564 ->withValidityOfQualification($validity)
2565 ->withLastChange($acting_usr_id, $this->getNow())
2566 ->withIndividualModifications(true);
2567
2568 $this->getProgressRepository()->update($progress);
2569 $err_collection->add(true, 'validity_updated', $this->getProgressIdString($progress));
2570
2571 //update LearningProgress, but this does not affect upper progresses
2572 $this->refreshLPStatus($progress->getUserId());
2573 }
2574
2575 public function changeAmountOfPoints(
2576 int $progress_id,
2577 int $acting_usr_id,
2578 ilPRGMessageCollection $err_collection,
2579 ?int $points
2580 ) : void {
2581 $progress = $this->getProgressRepository()->get($progress_id);
2582
2583 if (!$progress->isRelevant()) {
2584 $err_collection->add(false, 'will_not_modify_irrelevant_progress', $this->getProgressIdString($progress));
2585 return;
2586 }
2587 if ($progress->isSuccessful()) {
2588 $err_collection->add(false, 'will_not_modify_successful_progress', $this->getProgressIdString($progress));
2589 return;
2590 }
2591
2592 $progress = $progress
2593 ->withAmountOfPoints($points)
2594 ->withLastChange($acting_usr_id, $this->getNow())
2595 ->withIndividualModifications(true);
2596
2597 $progress = $this->recalculateProgressStatus($progress);
2598
2599 $this->getProgressRepository()->update($progress);
2600 $err_collection->add(true, 'required_points_updated', $this->getProgressIdString($progress));
2601 $this->refreshLPStatus($progress->getUserId());
2602 $this->updateParentProgress($progress);
2603 }
2604
2606 int $progress_id,
2607 int $acting_usr_id,
2608 ilPRGMessageCollection $err_collection = null
2609 ) : void {
2610 $progress = $this->getProgressRepository()->get($progress_id);
2611 $assignment_id = $progress->getAssignmentId();
2612 $progresses = $this->getProgressRepository()->getByAssignmentId($assignment_id);
2613
2614 $leafs = [];
2615 foreach ($progresses as $progress) {
2616 // get node for progress; settings are pulled from the node, which might not be "this"
2617 $node = $this->getPrgInstanceByObjId($progress->getNodeId());
2618 $progress = $node->updateProgressRelevanceFromSettings($progress);
2619 $progress = $node->resetProgressToSettings($progress, $acting_usr_id);
2620 $this->getProgressRepository()->update($progress);
2621
2622 if (!$node->hasChildren(true)) {
2623 $leafs[] = [$node, $progress];
2624 }
2625 }
2626
2627 foreach ($leafs as $leaf) {
2628 list($node, $progress) = $leaf;
2629 $progress = $this->recalculateProgressStatus($progress);
2630 $progress = $this->applyProgressDeadline($progress);
2631 $this->getProgressRepository()->update($progress);
2632 $this->refreshLPStatus($progress->getUserId(), (int) $node->getId());
2633 $this->updateParentProgress($progress);
2634 }
2635 }
2636
2637 protected function resetProgressToSettings(
2638 ilStudyProgrammeProgress $progress,
2639 int $acting_usr_id
2641 if ($progress->isRelevant()) {
2642 $progress = $this->updateProgressValidityFromSettings($progress);
2643 $progress = $this->updateProgressDeadlineFromSettings($progress);
2644 } else {
2645 $progress = $progress
2646 ->withValidityOfQualification(null)
2647 ->withDeadline(null);
2648 }
2649
2650 $progress = $progress->withAmountOfPoints($this->getPoints());
2651 $progress = $progress
2652 ->withLastChange($acting_usr_id, $this->getNow())
2653 ->withIndividualModifications(false);
2654
2655 return $progress;
2656 }
2657
2659 {
2660 if ($this->isActive() && !$progress->isRelevant()) {
2662 }
2663 if (!$this->isActive() && $progress->isInProgress()) {
2664 $progress = $progress->withStatus(ilStudyProgrammeProgress::STATUS_NOT_RELEVANT);
2665 }
2666
2667 return $progress;
2668 }
2669
2671 {
2672 $cdate = $progress->getCompletionDate();
2673 if (!$cdate
2674 || $progress->isSuccessful() === false
2675 ) {
2676 return $progress;
2677 }
2678
2679 $settings = $this->getSettings()->getValidityOfQualificationSettings();
2680 $period = $settings->getQualificationPeriod();
2681 $date = $settings->getQualificationDate();
2682
2683 if ($date) {
2684 $date = DateTimeImmutable::createFromMutable($date);
2685 }
2686
2687 if ($period) {
2688 $date = $cdate->add(new DateInterval('P' . $period . 'D'));
2689 }
2690
2691 return $progress->withValidityOfQualification($date);
2692 }
2693
2695 {
2696 $settings = $this->getSettings()->getDeadlineSettings();
2697 $period = $settings->getDeadlinePeriod();
2698 $date = $settings->getDeadlineDate();
2699 if ($date) {
2700 $date = DateTimeImmutable::createFromMutable($date);
2701 }
2702
2703 if ($period) {
2704 $date = $progress->getAssignmentDate();
2705 $date = $date->add(new DateInterval('P' . $period . 'D'));
2706 }
2707 return $progress->withDeadline($date);
2708 }
2709
2710 public function canBeCompleted(ilStudyProgrammeProgress $progress) : bool
2711 {
2712 if ($this->getLPMode() == ilStudyProgrammeSettings::MODE_LP_COMPLETED) {
2713 return true;
2714 }
2715 $possible_points = $this->getPossiblePointsOfRelevantChildren($progress);
2716 return $possible_points >= $progress->getAmountOfPoints();
2717 }
2718
2722 public function statusToRepr($a_status)
2723 {
2724 $lng = $this->lng;
2725 $lng->loadLanguageModule("prg");
2726
2728 return $lng->txt("prg_status_in_progress");
2729 }
2731 return $lng->txt("prg_status_completed");
2732 }
2734 return $lng->txt("prg_status_accredited");
2735 }
2737 return $lng->txt("prg_status_not_relevant");
2738 }
2739 if ($a_status == ilStudyProgrammeProgress::STATUS_FAILED) {
2740 return $lng->txt("prg_status_failed");
2741 }
2742 throw new ilException("Unknown status: '$a_status'");
2743 }
2744
2745 protected function getProgressIdString(ilStudyProgrammeProgress $progress) : string
2746 {
2747 $username = ilObjUser::_lookupFullname($progress->getUserId());
2748 return sprintf(
2749 '%s, progress-id %s',
2750 $username,
2751 $progress->getId()
2752 );
2753 }
2754}
An exception for terminatinating execution or to throw for unit testing.
return true
Flag indicating whether or not HTTP headers will be sent when outputting captcha image/audio.
static saveObjRecSelection($a_obj_id, $a_sub_type="", array $a_records=null, $a_delete_before=true)
Save repository object record selection.
Component logger with individual log levels by component id.
static _lookupTargetId($a_obj_id)
lookup target id
static _lookupTargetRefId($a_obj_id)
Lookup target ref_id.
static _lookupTitle($a_obj_id)
Overwitten from base class.
static _lookupSourceIds($a_target_id)
Get ids of all container references that target the object with the given id.
static _getInstance($a_obj_id)
get instance by obj_id
Class ilContainer.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _updateStatus($a_obj_id, $a_usr_id, $a_obj=null, $a_percentage=false, $a_force_raise=false)
Update status.
static _hasUserCompleted($a_obj_id, $a_user_id)
Lookup user object completion.
static getLogger($a_component_id)
Get component logger.
static setProgressesCompletedIfParentIsProgrammeInLPCompletedMode(int $a_ref_id, int $a_obj_id, int $a_user_id)
changeProgressValidityDate(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection, ?DateTimeImmutable $validity)
getLPChildren()
Get the leafs the study programme contains.
getAssignmentsOf(int $a_user_id)
Get the assignments of user at this program or any node above.
getRoot()
Get the ilObjStudyProgramme that is the root node of the tree this programme is in.
addLeaf(ilStudyProgrammeLeaf $a_leaf)
Insert a leaf in this object.
getAmountOfAssignmentsOf(int $a_user_id)
Get the amount of assignments a user has on this program node or any node above.
getAssignmentsRaw()
Get model objects for the assignments on this programm.
markAccredited(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection)
setPoints(int $a_points)
Set the amount of points.
getAmountOfChildren($include_references=false)
Get the amount of other StudyProgrammes this StudyProgramme has as children.
getMembers()
get usr_ids with any progress on this node
static sendInvalidateMail(int $ref_id, int $usr_id)
getApplicableMembershipSourceForUser(int $usr_id, string $exclude_type)
static removeCrsFromProgrammes(int $crs_ref_id, int $cat_ref_id)
Check, if a category is under surveilllance and automatically remove the deleted course.
removeLeaf(ilStudyProgrammeLeaf $a_leaf)
Remove a leaf from this object.
hasAssignmentsOfSingleProgramForUser(int $usr_id)
Get assignments of user to this program-node only.
deleteAutomaticContentCategories(array $category_ids=[])
Delete configuration of categories with auto-content for this StudyProgramme;.
markRelevant(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection)
assignUser(int $usr_id, int $acting_usr_id=null)
Assign a user to this node at the study program.
updateSettings(ilStudyProgrammeSettings $settings)
applyToSubTreeNodes(Closure $fun, bool $include_references=false)
Apply the given Closure to every node in the subtree starting at this object.
canBeRemoved()
Check weather a node can be removed.
removeAssignment(ilStudyProgrammeAssignment $assignment)
Remove an assignment from this program.
getIdsOfUsersWithNotCompletedAndRelevantProgress()
Get the ids of all users that have not completed this programme but have a relevant progress on it.
isAutoContentApplicable()
AutoContent should only be available in active- or draft-mode, and only, if there is no sub-programme...
static getProgrammesMonitoringMemberSource(string $src_type, int $src_id)
Get all StudyProgrammes monitoring this membership-source.
storeAutomaticMembershipSource(string $type, int $src_id)
Store a source to be monitored for automatic memberships.
deleteAllAutomaticContentCategories()
Delete all configuration of categories with auto-content for this StudyProgramme;.
getReferencesTo(ilObjStudyProgramme $prg)
getCompletedCourses(int $a_user_id)
Get courses in this program that the given user already completed.
applyProgressDeadline(ilStudyProgrammeProgress $progress, int $acting_usr_id=null)
static getProgrammesMonitoringCategory(int $cat_ref_id)
Get all StudyProgrammes monitoring this category.
canBeCompleted(ilStudyProgrammeProgress $progress)
getProgressIdString(ilStudyProgrammeProgress $progress)
static getInstanceByRefId($a_ref_id)
changeProgressDeadline(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection, ?DateTimeImmutable $deadline)
deleteAllAutomaticMembershipSources()
Delete all membership sources of this StudyProgramme;.
getIdsFromNodesOnPathFromRootToHere(bool $include_references=false)
Get the ids from the nodes in the path leading from the root node of this program to this node,...
getPoints()
Get the amount of points.
static getCreatableSubObjects(array $a_subobjects, $a_ref_id)
Filter the list of possible subobjects for the objects that actually could be created on a concrete n...
refreshLPStatus(int $usr_id, int $node_obj_id=null)
storeAutomaticContentCategory(int $category_ref_id)
Store a Category with auto-content for this StudyProgramme; a category can only be referenced once (p...
moveTo(ilObjStudyProgramme $a_new_parent)
Move this tree node to a new parent.
getProgressesOf(int $a_user_id)
Get the progresses the user has on this node.
recalculateProgressStatus(ilStudyProgrammeProgress $progress)
getAssignments()
Get all assignments to this program or any node above.
clearLPChildrenCache()
Clear the cached lp children.
static getParentId(ilObject $a_object)
Get the obj id of the parent object for the given object.
static sendInformToReAssignMail(int $progress_id, int $usr_id)
markFailed(int $progress_id, int $acting_usr_id)
static sendRiskyToFailMail(int $progress_id, int $usr_id)
getLocalMembers()
get usr_ids with assignment on this node
getMembersOfMembershipSource(string $src_type, int $src_id)
Get member-ids of a certain source.
disableAutomaticMembershipSource(string $type, int $src_id)
Disable a membership source.
clearParentCache()
Clear the cached parent to query it again at the tree.
addMissingProgresses()
Add missing progress records for all assignments of this programm.
getChildren(bool $include_references=false)
Get all ilObjStudyProgrammes that are direct children of this object.
getProgresses()
Get all progresses on this node.
updateAllAssignments()
Update all assignments to this program node.
getProgressForAssignment(int $assignment_id)
static addMemberToProgrammes(string $src_type, int $src_id, int $usr_id)
statusToRepr($a_status)
Get a user readable representation of a status.
nodeInserted(ilObjStudyProgramme $a_prg)
Clears child chache and adds progress for new node.
updatePlanFromRepository(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection=null)
getAutomaticContentCategories()
Get configuration of categories with auto-content for this StudyProgramme;.
static addCrsToProgrammes(int $crs_ref_id, int $cat_ref_id)
Check, if a category is under surveilllance and automatically add the course.
getLastChange()
Get the timestamp of the last change on this program or sub program.
clearChildrenCache()
Clear the cached children.
getObjIdsOfChildren(int $node_obj_id)
__construct($a_id=0, bool $a_call_by_reference=true)
ATTENTION: After using the constructor the object won't be in the cache.
hasProgresses()
Are there any users that have a progress on this programme?
getIdsOfUsersWithRelevantProgress()
Get the ids of all users that have a relevant progress at this programme.
hasChildren(bool $include_references=false)
Does this StudyProgramme have other ilObjStudyProgrammes as children?
static sendReAssignedMail(int $ref_id, int $usr_id)
deleteAssignmentsAndProgresses()
Delete all assignments from the DB.
setStatus(int $a_status)
Set the status of the node.
changeAmountOfPoints(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection, ?int $points)
getAssignmentsOfSingleProgramForUser(int $usr_id)
Get assignments of user to this program-node only.
getAutomaticMembershipSources()
Get sources for auto-memberships.
getIdsOfUsersWithFailedProgress()
Get the ids of all users that have failed this programme.
getDepth()
Get the depth of this StudyProgramme in the tree starting at the topmost StudyProgramme (not root nod...
static getAllChildren(int $a_ref_id, bool $include_references=false)
Get a list of all ilObjStudyProgrammes in the subtree starting at $a_ref_id.
static createInstance()
Create an instance of ilObjStudyProgramme, put in cache.
getPossiblePointsOfRelevantChildren(ilStudyProgrammeProgress $progress)
getAchievedPointsOfChildren(ilStudyProgrammeProgress $progress)
enableAutomaticMembershipSource(string $type, int $src_id, $assign_now=false)
Enable a membership source.
getProgrammeSettingsForProgress(ilStudyProgrammeProgress $progress)
getSubType()
Gets the SubType Object.
getAssignmentForProgress(ilStudyProgrammeProgress $progress)
succeed(int $progress_id, int $triggering_obj_id)
removeNode(ilObjStudyProgramme $a_prg)
Remove a node from this object.
static getRefIdFor(int $obj_id)
getNamesOfCompletedOrAccreditedChildren(int $ass_id)
hasAssignmentOf(int $a_user_id)
Check whether user is assigned to this program or any node above.
deleteAutomaticMembershipSource(string $type, int $src_id)
Delete a membership source.
updateParentProgress(ilStudyProgrammeProgress $progress)
hasAssignments()
Are there any assignments on this node or any node above?
getParent()
Get the parent ilObjStudyProgramme of this object.
getParentProgress(ilStudyProgrammeProgress $progress)
hasRelevantProgresses()
Are there any users that have a relevant progress on this programme?
getLPChildrenIds()
Get the obj-ids of the leafs the program contains.
updateLastChange()
Update last change timestamp on this node and its parents.
getAmountOfLPChildren()
Get the amount of leafs, the study programme contains.
static removeMemberFromProgrammes(string $src_type, int $src_id, int $usr_id)
putInTree($a_parent_ref)
Overwritten from ilObject.
adjustLPMode()
Adjust the lp mode to match current state of tree:
markNotFailed(int $progress_id, int $acting_usr_id)
static setProgressesCompletedFor(int $a_obj_id, int $a_user_id)
Set all progresses to completed where the object with given id is a leaf and that belong to the user.
addNode(ilObjStudyProgramme $a_prg)
Inserts another ilObjStudyProgramme in this object.
getIdsOfUsersWithCompletedProgress()
Get the ids of all users that have completed this programme.
markNotRelevant(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection)
unmarkAccredited(int $progress_id, int $acting_usr_id, ilPRGMessageCollection $err_collection)
getParents(bool $include_references=false)
Get all parents of the node, where the root of the program comes first.
resetProgressToSettings(ilStudyProgrammeProgress $progress, int $acting_usr_id)
createProgressForAssignment(ilStudyProgrammeAssignment $ass, int $acting_user=null)
Create a progress on this programme for the given assignment.
static _lookupLogin($a_user_id)
lookup login
static _lookupGender($a_user_id)
Lookup gender.
static _lookupFullname($a_user_id)
Lookup Full Name.
Class ilObject Basic functions for all objects.
createReference()
creates reference for object
static _lookupTitle($a_id)
lookup object title
static _lookupObjectId($a_ref_id)
lookup object id
getRefId()
get reference id @access public
static _getAllReferences($a_id)
get all reference ids of object
static _exists($a_id, $a_reference=false, $a_type=null)
checks if an object exists in object_data@access public
getId()
get object id @access public
static _lookupDeletedDate($a_ref_id)
only called in ilObjectGUI::insertSavedNodes
static _lookupType($a_id, $a_reference=false)
lookup object type
Holds information about multi-actions, mainly in context of member-assignemnts and status changes.
add(bool $success, string $message, string $record_identitifer)
Represents one assignment of the user to a program tree.
Class ilStudyProgrammeProgress.
getId()
Get the id of the progress.
getAssignmentId()
Get the assignment this progress belongs to.
withCurrentAmountOfPoints(int $points_cur)
Set the amount of points the user currently has achieved on this node.
markNotFailed(DateTimeImmutable $date, int $acting_usr_id)
getAmountOfPoints()
Get the amount of points the user needs to achieve on the subnodes of this node.
getStatus()
Get the status the user has on this node.
getUserId()
Get the id of the user this progress is for.
withStatus(int $status)
Set the status of this node.
markFailed(DateTimeImmutable $date, int $acting_usr_id)
getNodeId()
Get the obj_id of the program node this progress belongs to.
getObjId()
Get the id of the study program.
Exception is thrown when invariants on the program tree would be violated by manipulation of tree.
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
if(!file_exists(getcwd() . '/ilias.ini.php'))
registration confirmation script for ilias
Definition: confirmReg.php:12
const ANONYMOUS_USER_ID
Definition: constants.php:25
$nd
Definition: error.php:12
global $DIC
Definition: goto.php:24
The interface a class has to fullfill if it should be used as leaf in a program.
getRefId()
Get the ILIAS reference id of the leaf.
putInTree($a_ref_id)
Put the leaf object in the repository tree under object identified by $a_ref_id.
createReference()
Create a reference id for this object.
Covers the persistence of settings belonging to a study programme (SP).
Covers the persistence of settings belonging to a study programme (SP).
if($format !==null) $name
Definition: metadata.php:230
$format
Definition: metadata.php:218
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
get(string $class_name)
$ret
Definition: parser.php:6
$log
Definition: result.php:15
$dic
Definition: result.php:13
$lng
foreach($_POST as $key=> $value) $res
$data
Definition: storeScorm.php:23