ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilObjectLP.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23
30{
31 protected static ?array $type_defaults = null;
32
33 protected ilTree $tree;
34 protected ilDBInterface $db;
36
37 protected int $obj_id;
38
40 protected ?int $mode = null;
41
42 protected function __construct(int $obj_id)
43 {
44 global $DIC;
45
46 $this->tree = $DIC->repositoryTree();
47 $this->db = $DIC->database();
48 $this->objectDefinition = $DIC['objDefinition'];
49
50 $this->obj_id = $obj_id;
51 }
52
57 public static function getInstance(int $obj_id)
58 {
59 static $instances = [];
60
61 if (!isset($instances[$obj_id])) {
63 $class = self::getTypeClass($type);
64 if ($class) {
65 $instance = new $class($obj_id);
66 } else {
67 // :TODO: should we return anything?
68 $instance = new self($obj_id);
69 }
70 $instances[$obj_id] = $instance;
71 }
72 return $instances[$obj_id];
73 }
74
75 public static function getTypeClass(string $type): string
76 {
77 global $DIC;
78 $objDefinition = $DIC["objDefinition"];
79 if (self::isSupportedObjectType($type)) {
80 switch ($type) {
81 // container
82 case "crs":
83 return "ilCourseLP";
84 case 'crsr':
85 return 'ilCourseReferenceLP';
86 case "grp":
87 return "ilGroupLP";
88 case "fold":
89 return "ilFolderLP";
90 case "lso":
91 return "ilLSLP";
92
93 // learning resources
94 case "lm":
95 return "ilLearningModuleLP";
96 case "htlm":
97 return "ilHTMLLearningModuleLP";
98 case "sahs":
99 return "ilScormLP";
100
101 // misc
102 case "tst":
103 return "ilTestLP";
104 case "exc":
105 return "ilExerciseLP";
106 case 'file':
107 return 'ilFileLP';
108 case "mcst":
109 return "ilMediaCastLP";
110 case "sess":
111 return "ilSessionLP";
112 case "svy":
113 return "ilSurveyLP";
114 case "prg":
115 return "ilStudyProgrammeLP";
116 case "iass":
117 return "ilIndividualAssessmentLP";
118 case "copa":
119 return "ilContentPageLP";
120 case 'cmix':
121 return ilCmiXapiLP::class;
122 case 'lti':
123 return ilLTIConsumerLP::class;
124 case 'frm':
125 return ilForumLP::class;
126 }
127 if ($objDefinition->isPluginTypeName($type)) {
128 return "ilPluginLP";
129 }
130 }
131 return "";
132 }
133
134 public static function getSupportedObjectTypes(): array
135 {
136 global $DIC;
137 $component_repository = $DIC["component.repository"];
138
139 $valid = [
140 "crs",
141 "grp",
142 "fold",
143 "lm",
144 "htlm",
145 "sahs",
146 "tst",
147 "exc",
148 "sess",
149 "svy",
150 "file",
151 "mcst",
152 "prg",
153 "iass",
154 "copa",
155 "lso",
156 'cmix',
157 'lti',
158 'crsr',
159 'frm'
160 ];
161
162 $plugins = $component_repository->getPluginSlotById("robj")->getActivePlugins();
163 foreach ($plugins as $plugin) {
164 $type = $plugin->getId();
166 $valid[] = $type;
167 }
168 }
169
170
171 return $valid;
172 }
173
174 public static function isSupportedObjectType(string $type): bool
175 {
177
178 if (in_array($type, $valid)) {
179 return true;
180 }
181
182 return false;
183 }
184
185 public function resetCaches(): void
186 {
187 $this->mode = null;
188 $this->collection_instance = null;
189 }
190
191 public function isAnonymized(): bool
192 {
193 // see ilLPCollectionOfRepositoryObjects::validateEntry()
194 return false;
195 }
196
197 public function getDefaultMode(): int
198 {
200 }
201
205 public function getValidModes(): array
206 {
207 return [];
208 }
209
210 public function getCurrentMode(): int
211 {
212 if ($this->mode === null) {
213 // using global type default if LP is inactive
216 if ($mode === null) {
217 // fallback: inactive as type default may not be suitable
219 }
220 }
221 // use object LP setting
222 else {
223 $mode = ilLPObjSettings::_lookupDBMode($this->obj_id);
224 if ($mode === null) {
225 // fallback: object type default
226 $mode = $this->getDefaultMode();
227 }
228 }
229 $this->mode = (int) $mode;
230 }
231
232 return $this->mode;
233 }
234
235 public function isActive(): bool
236 {
237 // :TODO: check LP activation?
238
239 $mode = $this->getCurrentMode();
242 return false;
243 }
244 return true;
245 }
246
247 public function getModeText(int $mode): string
248 {
250 }
251
252 public function getModeInfoText(int $mode): string
253 {
255 }
256
257 public function getSettingsInfo(): string
258 {
259 // type-specific
260 return "";
261 }
262
263
265 {
266 if ($this->collection_instance === null) {
267 $this->collection_instance = ilLPCollection::getInstanceByMode($this->obj_id, $this->getCurrentMode());
268 }
270 }
271
272 public function getMembers(bool $search = true): array
273 {
274 if (!$search) {
275 return [];
276 }
277
278 $ref_ids = ilObject::_getAllReferences($this->obj_id);
279 $ref_id = current($ref_ids);
280
281 // walk path to find parent with specific members
282 $path = $this->tree->getPathId($ref_id);
283 array_pop($path);
284 foreach (array_reverse($path) as $path_ref_id) {
285 $olp = self::getInstance(ilObject::_lookupObjId($path_ref_id));
286 $all = $olp->getMembers(false);
287 if (is_array($all)) {
288 return $all;
289 }
290 }
291 return [];
292 }
293
294 final public function resetLPDataForCompleteObject(bool $recursive = true): void
295 {
296 $user_ids = $this->gatherLPUsers();
297 if (sizeof($user_ids)) {
298 $this->resetLPDataForUserIds(array_unique($user_ids), $recursive);
299 }
300 }
301
302 final public function resetLPDataForUserIds(array $user_ids, bool $recursive = true): void
303 {
304 if ($recursive && method_exists($this, "getPossibleCollectionItems")) { // #15203
305 $subitems = $this->getPossibleCollectionItems();
306 if (is_array($subitems)) {
307 foreach ($subitems as $sub_ref_id) {
308 $olp = self::getInstance(ilObject::_lookupObjId($sub_ref_id));
309 $olp->resetLPDataForUserIds($user_ids, false);
310 }
311 }
312 }
313
314 $this->resetCustomLPDataForUserIds($user_ids, $recursive);
315
316 ilLPMarks::_deleteForUsers($this->obj_id, $user_ids);
317
318 ilChangeEvent::_deleteReadEventsForUsers($this->obj_id, $user_ids);
319
320 // update LP status to get collections up-to-date
321 foreach ($user_ids as $user_id) {
323 }
324 }
325
326 protected function resetCustomLPDataForUserIds(array $user_ids, bool $recursive = true): void
327 {
328 // this should delete all data that is relevant for the supported LP modes
329 }
330
331 protected function gatherLPUsers(): array
332 {
333 $user_ids = ilLPMarks::_getAllUserIds($this->obj_id);
334 return array_merge($user_ids, ilChangeEvent::_getAllUserIds($this->obj_id));
335 }
336
337 final public static function handleMove(int $source_ref_id): void
338 {
339 global $DIC;
340
341 $tree = $DIC->repositoryTree();
342 $ilDB = $DIC->database();
343
344 $ref_ids = $tree->getSubTreeIds($source_ref_id);
345 $ref_ids[] = $source_ref_id;
346
347 // get "parent" path to source node (not including source node)
348 $new_path = $tree->getPathId($source_ref_id);
349 array_pop($new_path);
350 $new_path = implode("/", $new_path);
351
352 // find collections with ref_ids
353 $sql =
354 "SELECT DISTINCT(ut_lp_collections.obj_id) obj_id" . PHP_EOL
355 . "FROM object_reference" . PHP_EOL
356 . "JOIN ut_lp_collections ON" . PHP_EOL
357 . "(" . $ilDB->in("object_reference.ref_id", $ref_ids, false, "integer") . PHP_EOL
358 . "AND object_reference.ref_id = ut_lp_collections.item_id)" . PHP_EOL
359 ;
360 $result = $ilDB->query($sql);
361 while ($row = $ilDB->fetchAssoc($result)) {
362 if (in_array(ilObject::_lookupType((int) $row["obj_id"]), ["crs", "grp", "fold"])) {
363 $coll_ref_id = ilObject::_getAllReferences((int) $row["obj_id"]);
364 $coll_ref_id = array_pop($coll_ref_id);
365
366 // #13402
367 if ($coll_ref_id == $source_ref_id) {
368 continue;
369 }
370
371 // #17703 - collection has also been moved - nothing todo
372 if ($tree->isGrandChild($source_ref_id, $coll_ref_id)) {
373 continue;
374 }
375
376 // get path to collection (including collection "parent")
377 $coll_path = $tree->getPathId($coll_ref_id);
378 $coll_path = implode("/", $coll_path);
379
380 // collection path is not inside new path
381 if (!stristr($new_path, $coll_path)) {
382 // delete all items of moved (sub-)tree
383 $sql =
384 "DELETE FROM ut_lp_collections" . PHP_EOL
385 . "WHERE obj_id = " . $ilDB->quote($row["obj_id"], "integer") . PHP_EOL
386 . "AND " . $ilDB->in("item_id", $ref_ids, false, "integer") . PHP_EOL
387 ;
388 $ilDB->manipulate($sql);
389
390 ilLPStatusWrapper::_refreshStatus((int) $row["obj_id"]);
391 }
392 }
393 }
394 }
395
396 final public function handleToTrash(): void
397 {
399 }
400
401 final public function handleDelete(): void
402 {
403 ilLPMarks::deleteObject($this->obj_id);
404
405 ilChangeEvent::_delete($this->obj_id);
406
407 $collection = $this->getCollectionInstance();
408 if ($collection) {
409 $collection->delete();
410 }
411
413 }
414
415 final protected function updateParentCollections(): void
416 {
417 // update parent collections?
418 $sql =
419 "SELECT ut_lp_collections.obj_id obj_id" . PHP_EOL
420 . "FROM object_reference" . PHP_EOL
421 . "JOIN ut_lp_collections ON" . PHP_EOL
422 . "(object_reference.obj_id = " . $this->db->quote($this->obj_id, "integer") . PHP_EOL
423 . "AND object_reference.ref_id = ut_lp_collections.item_id)" . PHP_EOL
424 ;
425 $result = $this->db->query($sql);
426 while ($row = $this->db->fetchAssoc($result)) {
427 if (in_array(ilObject::_lookupType((int) $row["obj_id"]), ["crs", "grp", "fold"])) {
428 // remove from parent collection
429 $sql =
430 "DELETE FROM ut_lp_collections" . PHP_EOL
431 . "WHERE obj_id = " . $this->db->quote($row["obj_id"], "integer") . PHP_EOL
432 . "AND item_id = " . $this->db->quote($this->obj_id, "integer") . PHP_EOL
433 ;
434 $this->db->manipulate($sql);
435
436 ilLPStatusWrapper::_refreshStatus((int) $row["obj_id"]);
437 }
438 }
439 }
440
444 protected static function isLPMember(array &$res, int $usr_id, array $obj_ids): bool
445 {
446 // should be overwritten by object-type-specific class
447 return false;
448 }
449
453 protected static function findMembershipsByPath(
454 array &$res,
455 int $usr_id,
456 int $parent_ref_id,
457 array $obj_ids,
458 bool $mapped_ref_ids = false
459 ): array {
460 global $DIC;
461
462 $tree = $DIC->repositoryTree();
463
464 $found = [];
465
466 // walk path to find course or group object and check members of that object
467 $path = $tree->getPathId($parent_ref_id);
468 foreach (array_reverse($path) as $path_ref_id) {
469 $type = ilObject::_lookupType($path_ref_id, true);
470 if ($type == "crs" || $type == "grp") {
471 $class = self::getTypeClass($type);
472 $path_ob_id = ilObject::_lookupObjId($path_ref_id);
473 $chk = [];
474 $class::isLPMember($chk, $usr_id, [$path_ob_id]);
475 if (!$mapped_ref_ids) {
476 // we found a grp/crs in path of (single) parent - mark all objects
477 foreach ($obj_ids as $obj_id) {
478 $found[] = $obj_id;
479 if ($chk[$path_ob_id] ?? false) {
480 $res[$obj_id] = true;
481 }
482 }
483 } else {
484 // all children from current node are "lp-valid"
485 foreach ($obj_ids as $obj_id => $ref_ids) {
486 foreach ($ref_ids as $ref_id) {
487 if ($tree->isGrandChild($path_ref_id, $ref_id)) {
488 $found[$obj_id][] = $ref_id;
489 if ($chk[$path_ob_id] ?? false) {
490 $res[$obj_id] = true;
491 }
492 break;
493 }
494 }
495 }
496 }
497 break;
498 }
499 }
500
501 return $found;
502 }
503
507 public static function getLPMemberships(
508 int $usr_id,
509 array $obj_ids,
510 ?int $parent_ref_id = null,
511 bool $mapped_ref_ids = false
512 ): array {
513 global $DIC;
514
515 $ilDB = $DIC->database();
516 $tree = $DIC->repositoryTree();
517
518 // see ilTrQuery::getParticipantsForObject() [single object only]
519 // this is optimized for larger number of objects, e.g. list GUIs
520
521 $ref_map = [];
522 if ($mapped_ref_ids) {
523 $ref_map = $obj_ids;
524 $obj_ids = array_keys($obj_ids);
525 }
526
527 $res = [];
528
529 // get object types
530 $types_map = [];
531 $sql =
532 "SELECT obj_id, type" . PHP_EOL
533 . "FROM object_data" . PHP_EOL
534 . "WHERE " . $ilDB->in("obj_id", $obj_ids, false, "integer") . PHP_EOL
535 ;
536 $result = $ilDB->query($sql);
537 while ($row = $ilDB->fetchAssoc($result)) {
538 $types_map[$row["type"]][] = (int) $row["obj_id"];
539 $res[(int) $row["obj_id"]] = false;
540 }
541
542 $find_by_parent = [];
543 foreach ($types_map as $type => $type_obj_ids) {
544 $class = self::getTypeClass($type);
545 if ($class) {
546 // lp-supported type?
547 if (!$class::isLPMember($res, $usr_id, $type_obj_ids)) {
548 $find_by_parent = array_merge($find_by_parent, $type_obj_ids);
549 }
550 }
551 }
552
553 if (sizeof($find_by_parent)) {
554 // single parent for all objects (repository/ilObjectListGUI)
555 if ($parent_ref_id) {
556 if (self::findMembershipsByPath($res, $usr_id, $parent_ref_id, $find_by_parent)) {
557 // we found a crs/grp in path, so no need to check read_events
558 $find_by_parent = null;
559 }
560 }
561 // different parents (PD > LP)
562 elseif (is_array($ref_map) && count($ref_map) > 0) {
563 foreach ($find_by_parent as $obj_id) {
564 // maybe already found by path search from other object/reference
565 if ($res[$obj_id] === false) {
566 if (isset($ref_map[$obj_id]) && is_array($ref_map[$obj_id])) {
567 // check all references
568 foreach ($ref_map[$obj_id] as $ref_id) {
569 $parent_ref_id = $tree->getParentId($ref_id);
570 if ($parent_ref_id == ROOT_FOLDER_ID) {
571 continue;
572 }
573
574 // we are checking the complete ref_map
575 // to find all relevant objects in subtree of current ref_id
576 $found = self::findMembershipsByPath($res, $usr_id, $parent_ref_id, $ref_map, true);
577 if (is_array($found) && count($found) > 0) {
578 // if any references were found in a crs/grp-subtree
579 // remove from "read-event"-last-resort-pool
580 foreach ($found as $found_obj_id => $found_ref_ids) {
581 $diff = array_diff($ref_map[$found_obj_id], $found_ref_ids);
582 if ($diff) {
583 // 1-n refs are in another subtree
584 // have to be checked separately
585 $ref_map[$found_obj_id] = $diff;
586 } else {
587 // all references found in subtree
588 // no need to check again
589 unset($ref_map[$found_obj_id]);
590 }
591 }
592 break;
593 }
594 }
595 }
596 }
597 }
598
599 $find_by_parent = array_keys($ref_map);
600 }
601
602 // last resort: use read_event?
603 if (is_array($find_by_parent) && count($find_by_parent) > 0) {
604 $sql =
605 "SELECT obj_id" . PHP_EOL
606 . "FROM read_event" . PHP_EOL
607 . "WHERE " . $ilDB->in("obj_id", $find_by_parent, false, "integer") . PHP_EOL
608 . "AND usr_id = " . $ilDB->quote($usr_id, "integer") . PHP_EOL
609 ;
610 $result = $ilDB->query($sql);
611 while ($row = $ilDB->fetchAssoc($result)) {
612 $res[(int) $row["obj_id"]] = true;
613 }
614 }
615 }
616
617 return $res;
618 }
619
620 public function getMailTemplateId(): string
621 {
622 return '';
623 }
624
625
626 //
627 // type-specific support of features (should be enhanced)
628 //
629
630 public static function supportsSpentSeconds(string $obj_type): bool
631 {
632 return !in_array($obj_type, ["exc", "file", "mcst", "mob", "htlm", "copa", 'cmix', 'lti', 'frm']);
633 }
634
635 public static function supportsMark(string $obj_type): bool
636 {
637 return !in_array($obj_type, ["lm", "dbk"]);
638 }
639
640 public static function supportsMatrixView(string $obj_type): bool
641 {
642 $types = ['svy', 'tst', 'htlm', 'exc', 'sess', 'file', 'frm', 'prg', 'copa', 'cmix', 'lti','crsr', 'iass'];
643 return !in_array($obj_type, $types);
644 }
645
650 public static function getDefaultModes(bool $lp_active): array
651 {
653 }
654
655 protected static function getTypeDefaultFromDB(string $type): ?int
656 {
657 global $DIC;
658 $ilDB = $DIC->database();
659
660 if (is_null(self::$type_defaults)) {
661 self::$type_defaults = [];
662 $result = $ilDB->query("SELECT type_id, lp_mode FROM ut_lp_defaults");
663 while ($row = $ilDB->fetchAssoc($result)) {
664 self::$type_defaults[(string) $row["type_id"]] = (int) $row["lp_mode"];
665 }
666 }
667 return self::$type_defaults[$type] ?? null;
668 }
669
670 public static function saveTypeDefaults(array $data): void
671 {
672 global $DIC;
673 $ilDB = $DIC->database();
674
675 $ilDB->manipulate("DELETE FROM ut_lp_defaults");
676 foreach ($data as $type => $mode) {
677 $ilDB->insert("ut_lp_defaults", [
678 "type_id" => ["text", $type],
679 "lp_mode" => ["integer", $mode]
680 ]);
681 }
682 }
683
684 public static function getTypeDefault(string $type): int
685 {
686 $db = self::getTypeDefaultFromDB($type);
687 if ($db !== null) {
688 return $db;
689 }
690
691 $class = self::getTypeClass($type);
692 $olp = new $class(0);
693 return $olp->getDefaultMode();
694 }
695
696 public function hasIndividualModeOptions(): bool
697 {
698 return false;
699 }
700
705 public function initInvidualModeOptions(): array
706 {
707 return [];
708 }
709
711 {
712 return false;
713 }
714
720 string $selected_group,
721 array $group_data
722 ): int {
723 return 0;
724 }
725
729 public function appendModeConfiguration(int $mode): array
730 {
731 return [];
732 }
733
738 public function saveModeConfiguration(
739 string $selected_group,
740 array $group_data,
741 bool &$modeChanged
742 ): void {
743 }
744}
static _deleteReadEventsForUsers(int $a_obj_id, array $a_user_ids)
static _delete(int $a_obj_id)
Delete object entries.
static _getAllUserIds(int $a_obj_id)
LP collection base class.
static getInstanceByMode(int $a_obj_id, int $a_mode)
static deleteObject(int $a_obj_id)
static _getAllUserIds(int $a_obj_id)
static _deleteForUsers(int $a_obj_id, array $a_user_ids)
static _lookupDBMode(int $a_obj_id)
static _mode2InfoText(int $a_mode)
static _mode2Text(int $a_mode)
static _refreshStatus(int $a_obj_id, ?array $a_users=null)
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)
parses the objects.xml it handles the xml-description of all ilias objects
Base class for object lp connectors.
static supportsSpentSeconds(string $obj_type)
saveModeConfiguration(string $selected_group, array $group_data, bool &$modeChanged)
static getDefaultModes(bool $lp_active)
Get available type-specific default modes (no administration needed)
static saveTypeDefaults(array $data)
static supportsMark(string $obj_type)
getMembers(bool $search=true)
initInvidualModeOptions()
Post vars for input groups is taken from array keys.
resetCustomLPDataForUserIds(array $user_ids, bool $recursive=true)
static array $type_defaults
getModeText(int $mode)
ilDBInterface $db
__construct(int $obj_id)
static getTypeDefaultFromDB(string $type)
static supportsMatrixView(string $obj_type)
static getLPMemberships(int $usr_id, array $obj_ids, ?int $parent_ref_id=null, bool $mapped_ref_ids=false)
Get all objects where given user is member (from LP POV)
static getSupportedObjectTypes()
getModeInfoText(int $mode)
shouldFetchIndividualModeFromFormSubmission()
static getTypeDefault(string $type)
resetLPDataForUserIds(array $user_ids, bool $recursive=true)
static isSupportedObjectType(string $type)
appendModeConfiguration(int $mode)
static handleMove(int $source_ref_id)
static findMembershipsByPath(array &$res, int $usr_id, int $parent_ref_id, array $obj_ids, bool $mapped_ref_ids=false)
Find (lp-relevant) memberships by path.
ilLPCollection $collection_instance
static isLPMember(array &$res, int $usr_id, array $obj_ids)
Find (lp-relevant) members for given object ids.
fetchIndividualModeFromFormSubmission(string $selected_group, array $group_data)
ilObjectDefinition $objectDefinition
resetLPDataForCompleteObject(bool $recursive=true)
static getTypeClass(string $type)
static getInstance(int $obj_id)
static _lookupType(int $id, bool $reference=false)
static _getAllReferences(int $id)
get all reference ids for object ID
static _lookupObjId(int $ref_id)
static isTypePluginWithLP(string $a_type, bool $a_active_status=true)
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
isGrandChild(int $a_startnode_id, int $a_querynode_id)
checks if a node is in the path of an other node
getSubTreeIds(int $a_ref_id)
Get all ids of subnodes.
getPathId(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
getParentId(int $a_node_id)
get parent id of given node
const ROOT_FOLDER_ID
Definition: constants.php:32
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$valid
This describes commonalities between all inputs.
Definition: Input.php:47
Interface ilDBInterface.
$ref_id
Definition: ltiauth.php:66
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
global $DIC
Definition: shib_login.php:26