ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilObjectLP.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "Services/Tracking/classes/class.ilLPObjSettings.php";
6 
15 {
19  protected $tree;
20 
24  protected $db;
25 
26  protected $obj_id; // [int]
27  protected $collection_instance; // [ilLPCollection]
28  protected $mode; // [int]
29 
30  protected static $type_defaults; // [array]
31 
32  protected function __construct($a_obj_id)
33  {
34  global $DIC;
35 
36  $this->tree = $DIC->repositoryTree();
37  $this->db = $DIC->database();
38  $this->obj_id = (int) $a_obj_id;
39  }
40 
41  public static function getInstance($a_obj_id)
42  {
43  static $instances = array();
44 
45  if (!isset($instances[$a_obj_id])) {
46  $type = ilObject::_lookupType($a_obj_id);
47  $class = self::getTypeClass($type);
48  if ($class) {
49  $instance = new $class($a_obj_id);
50  } else {
51  // :TODO: should we return anything?
52  $instance = new self($a_obj_id);
53  }
54  $instances[$a_obj_id] = $instance;
55  }
56 
57  return $instances[$a_obj_id];
58  }
59 
60  public static function getTypeClass($a_type)
61  {
62  global $DIC;
63 
64  $objDefinition = $DIC["objDefinition"];
65 
66  if (self::isSupportedObjectType($a_type)) {
67  switch ($a_type) {
68  // container
69 
70  case "crs":
71  include_once "Modules/Course/classes/class.ilCourseLP.php";
72  return "ilCourseLP";
73 
74  case "grp":
75  include_once "Modules/Group/classes/class.ilGroupLP.php";
76  return "ilGroupLP";
77 
78  case "fold":
79  include_once "Modules/Folder/classes/class.ilFolderLP.php";
80  return "ilFolderLP";
81 
82 
83  // learning resources
84 
85  case "lm":
86  include_once "Modules/LearningModule/classes/class.ilLearningModuleLP.php";
87  return "ilLearningModuleLP";
88 
89  case "htlm":
90  include_once "Modules/HTMLLearningModule/classes/class.ilHTMLLearningModuleLP.php";
91  return "ilHTMLLearningModuleLP";
92 
93  case "sahs":
94  include_once "Modules/ScormAicc/classes/class.ilScormLP.php";
95  return "ilScormLP";
96 
97 
98  // misc
99 
100  case "tst":
101  include_once "Modules/Test/classes/class.ilTestLP.php";
102  return "ilTestLP";
103 
104  case "exc":
105  include_once "Modules/Exercise/classes/class.ilExerciseLP.php";
106  return "ilExerciseLP";
107 
108  case 'file':
109  require_once 'Modules/File/classes/class.ilFileLP.php';
110  return 'ilFileLP';
111 
112  case "mcst":
113  require_once "Modules/MediaCast/classes/class.ilMediaCastLP.php";
114  return "ilMediaCastLP";
115 
116  case "sess":
117  include_once "Modules/Session/classes/class.ilSessionLP.php";
118  return "ilSessionLP";
119 
120  case "svy":
121  include_once "Modules/Survey/classes/class.ilSurveyLP.php";
122  return "ilSurveyLP";
123 
124  case "prg":
125  include_once "Modules/StudyProgramme/classes/class.ilStudyProgrammeLP.php";
126  return "ilStudyProgrammeLP";
127 
128  case "iass":
129  include_once "Modules/IndividualAssessment/classes/class.ilIndividualAssessmentLP.php";
130  return "ilIndividualAssessmentLP";
131 
132  // plugin
133  case $objDefinition->isPluginTypeName($a_type):
134  include_once "Services/Component/classes/class.ilPluginLP.php";
135  return "ilPluginLP";
136  }
137  }
138  }
139 
140  public static function isSupportedObjectType($a_type)
141  {
142  global $DIC;
143 
144  $objDefinition = $DIC["objDefinition"];
145 
146  $valid = array("crs", "grp", "fold", "lm", "htlm", "sahs", "tst", "exc", "sess", "svy", "file", "mcst", "prg", "iass");
147 
148  if (in_array($a_type, $valid)) {
149  return true;
150  }
151 
152  if ($objDefinition->isPluginTypeName($a_type)) {
153  include_once 'Services/Repository/classes/class.ilRepositoryObjectPluginSlot.php';
155  }
156 
157  return false;
158  }
159 
160  public function resetCaches()
161  {
162  $this->mode = null;
163  $this->collection_instance = null;
164  }
165 
166  public function isAnonymized()
167  {
168  // see ilLPCollectionOfRepositoryObjects::validateEntry()
169  return false;
170  }
171 
172 
173  //
174  // MODE
175  //
176 
177  public function getDefaultMode()
178  {
180  }
181 
182  public function getValidModes()
183  {
184  return array();
185  }
186 
187  public function getCurrentMode()
188  {
189  if ($this->mode === null) {
190  // using global type default if LP is inactive
191  include_once "Services/Tracking/classes/class.ilObjUserTracking.php";
193  $mode = self::getTypeDefaultFromDB(ilObject::_lookupType($this->obj_id));
194  if ($mode === null) {
195  // fallback: inactive as type default may not be suitable
197  }
198  }
199  // use object LP setting
200  else {
201  $mode = ilLPObjSettings::_lookupDBMode($this->obj_id);
202  if ($mode === null) {
203  // fallback: object type default
204  $mode = $this->getDefaultMode();
205  }
206  }
207  $this->mode = (int) $mode;
208  }
209 
210  return $this->mode;
211  }
212 
213  public function isActive()
214  {
215  // :TODO: check LP activation?
216 
217  $mode = $this->getCurrentMode();
220  return false;
221  }
222  return true;
223  }
224 
225  public function getModeText($a_mode)
226  {
227  return ilLPObjSettings::_mode2Text($a_mode);
228  }
229 
230  public function getModeInfoText($a_mode)
231  {
232  return ilLPObjSettings::_mode2InfoText($a_mode);
233  }
234 
235  public function getSettingsInfo()
236  {
237  // type-specific
238  }
239 
240 
241  //
242  // COLLECTION
243  //
244 
245  public function getCollectionInstance()
246  {
247  if ($this->collection_instance === null) {
248  include_once "Services/Tracking/classes/collection/class.ilLPCollection.php";
249  $this->collection_instance = ilLPCollection::getInstanceByMode($this->obj_id, $this->getCurrentMode());
250  }
251 
253  }
254 
255 
256  //
257  // MEMBERS
258  //
259 
260  public function getMembers($a_search = true)
261  {
262  $tree = $this->tree;
263 
264  if (!$a_search) {
265  return;
266  }
267 
268  $ref_ids = ilObject::_getAllReferences($this->obj_id);
269  $ref_id = current($ref_ids);
270 
271  // walk path to find parent with specific members
272  $path = $tree->getPathId($ref_id);
273  array_pop($path);
274  foreach (array_reverse($path) as $path_ref_id) {
275  $olp = self::getInstance(ilObject::_lookupObjId($path_ref_id));
276  $all = $olp->getMembers(false);
277  if (is_array($all)) {
278  return $all;
279  }
280  }
281  }
282 
283 
284  //
285  // RESET
286  //
287 
288  final public function resetLPDataForCompleteObject($a_recursive = true)
289  {
290  $user_ids = $this->gatherLPUsers();
291  if (sizeof($user_ids)) {
292  $this->resetLPDataForUserIds(array_unique($user_ids), $a_recursive);
293  }
294  }
295 
296  final public function resetLPDataForUserIds(array $a_user_ids, $a_recursive = true)
297  {
298  if ((bool) $a_recursive &&
299  method_exists($this, "getPossibleCollectionItems")) { // #15203
300  $subitems = $this->getPossibleCollectionItems();
301  if (is_array($subitems)) {
302  foreach ($subitems as $sub_ref_id) {
303  $olp = self::getInstance(ilObject::_lookupObjId($sub_ref_id));
304  $olp->resetLPDataForUserIds($a_user_ids, false);
305  }
306  }
307  }
308 
309  $this->resetCustomLPDataForUserIds($a_user_ids, (bool) $a_recursive);
310 
311  include_once "Services/Tracking/classes/class.ilLPMarks.php";
312  ilLPMarks::_deleteForUsers($this->obj_id, $a_user_ids);
313 
314  include_once "Services/Tracking/classes/class.ilChangeEvent.php";
315  ilChangeEvent::_deleteReadEventsForUsers($this->obj_id, $a_user_ids);
316 
317  // update LP status to get collections up-to-date
318  include_once "Services/Tracking/classes/class.ilLPStatusWrapper.php";
319  foreach ($a_user_ids as $user_id) {
320  ilLPStatusWrapper::_updateStatus($this->obj_id, $user_id);
321  }
322  }
323 
324  protected function resetCustomLPDataForUserIds(array $a_user_ids, $a_recursive = true)
325  {
326  // this should delete all data that is relevant for the supported LP modes
327  }
328 
329  protected function gatherLPUsers()
330  {
331  include_once "Services/Tracking/classes/class.ilLPMarks.php";
332  $user_ids = ilLPMarks::_getAllUserIds($this->obj_id);
333 
334  include_once "Services/Tracking/classes/class.ilChangeEvent.php";
335  $user_ids = array_merge($user_ids, ilChangeEvent::_getAllUserIds($this->obj_id));
336 
337  return $user_ids;
338  }
339 
340 
341  //
342  // EVENTS
343  //
344 
345  final public static function handleMove($a_source_ref_id)
346  {
347  global $DIC;
348 
349  $tree = $DIC->repositoryTree();
350  $ilDB = $DIC->database();
351 
352  $ref_ids = $tree->getSubTreeIds($a_source_ref_id);
353  $ref_ids[] = $a_source_ref_id;
354 
355  // get "parent" path to source node (not including source node)
356  $new_path = $tree->getPathId($a_source_ref_id);
357  array_pop($new_path);
358  $new_path = implode("/", $new_path);
359 
360  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
361 
362  // find collections with ref_ids
363  $set = $ilDB->query("SELECT DISTINCT(ut_lp_collections.obj_id) obj_id" .
364  " FROM object_reference" .
365  " JOIN ut_lp_collections ON" .
366  " (" . $ilDB->in("object_reference.ref_id", $ref_ids, "", "integer") .
367  " AND object_reference.ref_id = ut_lp_collections.item_id)");
368  while ($rec = $ilDB->fetchAssoc($set)) {
369  if (in_array(ilObject::_lookupType($rec["obj_id"]), array("crs", "grp", "fold"))) {
370  $coll_ref_id = ilObject::_getAllReferences($rec["obj_id"]);
371  $coll_ref_id = array_pop($coll_ref_id);
372 
373  // #13402
374  if ($coll_ref_id == $a_source_ref_id) {
375  continue;
376  }
377 
378  // #17703 - collection has also been moved - nothing todo
379  if ($tree->isGrandChild($a_source_ref_id, $coll_ref_id)) {
380  continue;
381  }
382 
383  // get path to collection (including collection "parent")
384  $coll_path = $tree->getPathId($coll_ref_id);
385  $coll_path = implode("/", $coll_path);
386 
387  // collection path is not inside new path
388  if (!stristr($new_path, $coll_path)) {
389  // delete all items of moved (sub-)tree
390  $query = "DELETE FROM ut_lp_collections" .
391  " WHERE obj_id = " . $ilDB->quote($rec["obj_id"], "integer") .
392  " AND " . $ilDB->in("item_id", $ref_ids, "", "integer");
393  $ilDB->manipulate($query);
394 
395  ilLPStatusWrapper::_refreshStatus($rec["obj_id"]);
396  }
397  }
398  }
399  }
400 
401  final public function handleToTrash()
402  {
403  $this->updateParentCollections();
404  }
405 
406  final public function handleDelete()
407  {
408  include_once "Services/Tracking/classes/class.ilLPMarks.php";
409  ilLPMarks::deleteObject($this->obj_id);
410 
411  include_once "Services/Tracking/classes/class.ilChangeEvent.php";
412  ilChangeEvent::_delete($this->obj_id);
413 
414  $collection = $this->getCollectionInstance();
415  if ($collection) {
416  $collection->delete();
417  }
418 
419  $this->updateParentCollections();
420  }
421 
422  final protected function updateParentCollections()
423  {
424  $ilDB = $this->db;
425 
426  include_once("./Services/Tracking/classes/class.ilLPStatusWrapper.php");
427 
428  // update parent collections?
429  $set = $ilDB->query("SELECT ut_lp_collections.obj_id obj_id FROM " .
430  "object_reference JOIN ut_lp_collections ON " .
431  "(object_reference.obj_id = " . $ilDB->quote($this->obj_id, "integer") .
432  " AND object_reference.ref_id = ut_lp_collections.item_id)");
433  while ($rec = $ilDB->fetchAssoc($set)) {
434  if (in_array(ilObject::_lookupType($rec["obj_id"]), array("crs", "grp", "fold"))) {
435  // remove from parent collection
436  $query = "DELETE FROM ut_lp_collections" .
437  " WHERE obj_id = " . $ilDB->quote($rec["obj_id"], "integer") .
438  " AND item_id = " . $ilDB->quote($this->obj_id, "integer");
439  $ilDB->manipulate($query);
440 
441  ilLPStatusWrapper::_refreshStatus($rec["obj_id"]);
442  }
443  }
444  }
445 
446 
447  //
448  // LP-relevant memberships
449  //
450 
458  protected static function isLPMember(array &$a_res, $a_usr_id, $a_obj_ids)
459  {
460  // should be overwritten by object-type-specific class
461  return false;
462  }
463 
474  protected static function findMembershipsByPath(array &$a_res, $a_usr_id, $a_parent_ref_id, array $a_obj_ids, $a_mapped_ref_ids = false)
475  {
476  global $DIC;
477 
478  $tree = $DIC->repositoryTree();
479 
480  $found = array();
481 
482  // walk path to find course or group object and check members of that object
483  $path = $tree->getPathId($a_parent_ref_id);
484  foreach (array_reverse($path) as $path_ref_id) {
485  $type = ilObject::_lookupType($path_ref_id, true);
486  if ($type == "crs" ||
487  $type == "grp") {
488  $class = self::getTypeClass($type);
489  $path_ob_id = ilObject::_lookupObjId($path_ref_id);
490  $chk = array();
491  $class::isLPMember($chk, $a_usr_id, array($path_ob_id));
492  if (!$a_mapped_ref_ids) {
493  // we found a grp/crs in path of (single) parent - mark all objects
494  foreach ($a_obj_ids as $obj_id) {
495  $found[] = $obj_id;
496  if ($chk[$path_ob_id]) {
497  $a_res[$obj_id] = true;
498  }
499  }
500  } else {
501  // all children from current node are "lp-valid"
502  foreach ($a_obj_ids as $obj_id => $ref_ids) {
503  foreach ($ref_ids as $ref_id) {
504  if ($tree->isGrandChild($path_ref_id, $ref_id)) {
505  $found[$obj_id][] = $ref_id;
506  if ($chk[$path_ob_id]) {
507  $a_res[$obj_id] = true;
508  }
509  break;
510  }
511  }
512  }
513  }
514  break;
515  }
516  }
517 
518  return $found;
519  }
520 
530  public static function getLPMemberships($a_usr_id, array $a_obj_ids, $a_parent_ref_id = null, $a_mapped_ref_ids = false)
531  {
532  global $DIC;
533 
534  $ilDB = $DIC->database();
535  $tree = $DIC->repositoryTree();
536 
537  // see ilTrQuery::getParticipantsForObject() [single object only]
538  // this is optimized for larger number of objects, e.g. list GUIs
539 
540  $ref_map = [];
541  if ((bool) $a_mapped_ref_ids) {
542  $ref_map = $a_obj_ids;
543  $a_obj_ids = array_keys($a_obj_ids);
544  }
545 
546  $res = array();
547 
548  // get object types
549  $types_map = array();
550  $query = " SELECT obj_id, type" .
551  " FROM object_data" .
552  " WHERE " . $ilDB->in("obj_id", $a_obj_ids, "", "integer");
553  $set = $ilDB->query($query);
554  while ($row = $ilDB->fetchAssoc($set)) {
555  $types_map[$row["type"]][] = $row["obj_id"];
556  $res[$row["obj_id"]] = false;
557  }
558 
559  $find_by_parent = array();
560  foreach ($types_map as $type => $type_obj_ids) {
561  $class = self::getTypeClass($type);
562  if ($class) {
563  // lp-supported type?
564  if (!$class::isLPMember($res, $a_usr_id, $type_obj_ids)) {
565  $find_by_parent = array_merge($find_by_parent, $type_obj_ids);
566  }
567  }
568  }
569 
570  if (sizeof($find_by_parent)) {
571  // single parent for all objects (repository/ilObjectListGUI)
572  if ($a_parent_ref_id) {
573  if (self::findMembershipsByPath($res, $a_usr_id, $a_parent_ref_id, $find_by_parent)) {
574  // we found a crs/grp in path, so no need to check read_events
575  $find_by_parent = null;
576  }
577  }
578  // different parents (PD > LP)
579  elseif (is_array($ref_map) && count($ref_map) > 0) {
580  foreach ($find_by_parent as $obj_id) {
581  // maybe already found by path search from other object/reference
582  if ($res[$obj_id] === false) {
583  if (isset($ref_map[$obj_id]) && is_array($ref_map[$obj_id])) {
584  // check all references
585  foreach ($ref_map[$obj_id] as $ref_id) {
586  $parent_ref_id = $tree->getParentId($ref_id);
587  if ($parent_ref_id == ROOT_FOLDER_ID) {
588  continue;
589  }
590 
591  // we are checking the complete ref_map
592  // to find all relevant objects in subtree of current ref_id
593  $found = self::findMembershipsByPath($res, $a_usr_id, $parent_ref_id, $ref_map, true);
594  if (is_array($found) && count($found) > 0) {
595  // if any references were found in a crs/grp-subtree
596  // remove from "read-event"-last-resort-pool
597  foreach ($found as $found_obj_id => $found_ref_ids) {
598  $diff = array_diff($ref_map[$found_obj_id], $found_ref_ids);
599  if ($diff) {
600  // 1-n refs are in another subtree
601  // have to be checked separately
602  $ref_map[$found_obj_id] = $diff;
603  } else {
604  // all references found in subtree
605  // no need to check again
606  unset($ref_map[$found_obj_id]);
607  }
608  }
609  break;
610  }
611  }
612  }
613  }
614  }
615 
616  $find_by_parent = array_keys($ref_map);
617  }
618 
619  // last resort: use read_event?
620  if (is_array($find_by_parent) && count($find_by_parent) > 0) {
621  $set = $ilDB->query("SELECT obj_id" .
622  " FROM read_event" .
623  " WHERE " . $ilDB->in("obj_id", $find_by_parent, "", "integer") .
624  " AND usr_id = " . $ilDB->quote($a_usr_id, "integer"));
625  while ($row = $ilDB->fetchAssoc($set)) {
626  $res[$row["obj_id"]] = true;
627  }
628  }
629  }
630 
631  return $res;
632  }
633 
634  public function getMailTemplateId()
635  {
636  // type-specific
637  }
638 
639 
640  //
641  // type-specific support of features (should be enhanced)
642  //
643 
644  public static function supportsSpentSeconds($a_obj_type)
645  {
646  return !in_array($a_obj_type, array("exc", "file", "mcst", "mob", "htlm"));
647  }
648 
649  public static function supportsMark($a_obj_type)
650  {
651  return !in_array($a_obj_type, array("lm", "dbk"));
652  }
653 
654  public static function supportsMatrixView($a_obj_type)
655  {
656  return !in_array($a_obj_type, array('svy', 'tst', 'htlm', 'exc', 'sess', 'file', 'prg'));
657  }
658 
659 
660  // type-wide default
661 
667  public static function getDefaultModes($a_lp_active)
668  {
670  }
671 
672  protected static function getTypeDefaultFromDB($a_type)
673  {
674  global $DIC;
675 
676  $ilDB = $DIC->database();
677 
678  if (!is_array(self::$type_defaults)) {
679  self::$type_defaults = array();
680  $set = $ilDB->query("SELECT * FROM ut_lp_defaults");
681  while ($row = $ilDB->fetchAssoc($set)) {
682  self::$type_defaults[$row["type_id"]] = $row["lp_mode"];
683  }
684  }
685  return self::$type_defaults[$a_type];
686  }
687 
688  public static function saveTypeDefaults(array $a_data)
689  {
690  global $DIC;
691 
692  $ilDB = $DIC->database();
693 
694  $ilDB->manipulate("DELETE FROM ut_lp_defaults");
695  foreach ($a_data as $type => $mode) {
696  $ilDB->insert("ut_lp_defaults", array(
697  "type_id" => array("text", $type),
698  "lp_mode" => array("integer", $mode)
699  ));
700  }
701  }
702 
709  public static function getTypeDefault($a_type)
710  {
711  $db = self::getTypeDefaultFromDB($a_type);
712  if ($db !== null) {
713  return $db;
714  }
715 
716  $class = self::getTypeClass($a_type);
717  $olp = new $class(0);
718  return $olp->getDefaultMode();
719  }
720 }
static isTypePluginWithLP($a_type, $a_active_status=true)
Check whether a repository type is a plugin which has active learning progress.
static getTypeDefaultFromDB($a_type)
resetLPDataForUserIds(array $a_user_ids, $a_recursive=true)
static getTypeClass($a_type)
getModeInfoText($a_mode)
$type
global $DIC
Definition: saml.php:7
resetLPDataForCompleteObject($a_recursive=true)
static _updateStatus($a_obj_id, $a_usr_id, $a_obj=null, $a_percentage=false, $a_force_raise=false)
Update status.
static isSupportedObjectType($a_type)
$valid
static getInstanceByMode($a_obj_id, $a_mode)
static _getAllUserIds($a_obj_id)
static _refreshStatus($a_obj_id, $a_users=null)
Set dirty.
static _getAllUserIds($a_obj_id)
static _getAllReferences($a_id)
get all reference ids of object
__construct($a_obj_id)
resetCustomLPDataForUserIds(array $a_user_ids, $a_recursive=true)
$a_type
Definition: workflow.php:92
static getTypeDefault($a_type)
Get current type default.
static _enabledLearningProgress()
check wether learing progress is enabled or not
static _delete($a_obj_id)
Delete object entries.
static findMembershipsByPath(array &$a_res, $a_usr_id, $a_parent_ref_id, array $a_obj_ids, $a_mapped_ref_ids=false)
Find (lp-relevant) memberships by path.
static _lookupDBMode($a_obj_id)
foreach($_POST as $key=> $value) $res
static _lookupObjId($a_id)
static saveTypeDefaults(array $a_data)
static $type_defaults
static getDefaultModes($a_lp_active)
Get available type-specific default modes (no administration needed)
static _deleteForUsers($a_obj_id, array $a_user_ids)
static supportsMatrixView($a_obj_type)
$query
Create styles array
The data for the language used.
static _lookupType($a_id, $a_reference=false)
lookup object type
getModeText($a_mode)
static supportsMark($a_obj_type)
static _mode2InfoText($a_mode)
static supportsSpentSeconds($a_obj_type)
static isLPMember(array &$a_res, $a_usr_id, $a_obj_ids)
Find (lp-relevant) members for given object ids.
static deleteObject($a_obj_id)
Delete object.
getMembers($a_search=true)
static _deleteReadEventsForUsers($a_obj_id, array $a_user_ids)
global $ilDB
static handleMove($a_source_ref_id)
static getLPMemberships($a_usr_id, array $a_obj_ids, $a_parent_ref_id=null, $a_mapped_ref_ids=false)
Get all objects where given user is member (from LP POV)
static getInstance($a_obj_id)
static _mode2Text($a_mode)