ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
class.ilChangeEvent.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=0);
20
45{
46 private static array $has_accessed = [];
47
62 public static function _recordWriteEvent(
63 int $obj_id,
64 int $usr_id,
65 string $action,
66 ?int $parent_obj_id = null
67 ): void {
68 global $DIC;
69
70 $ilDB = $DIC['ilDB'];
71
72 /* see _recordReadEvent
73 if (!ilChangeEvent::_isActive())
74 {
75 return;
76 }
77 */
78
79 if ($parent_obj_id == null) {
80 $pset = $ilDB->query(
81 'SELECT r2.obj_id par_obj_id FROM object_reference r1 ' .
82 'JOIN tree t ON t.child = r1.ref_id ' .
83 'JOIN object_reference r2 ON r2.ref_id = t.parent ' .
84 'WHERE r1.obj_id = ' . $ilDB->quote($obj_id, 'integer')
85 );
86
87 while ($prec = $ilDB->fetchAssoc($pset)) {
88 $nid = $ilDB->nextId("write_event");
89 $query = sprintf(
90 'INSERT INTO write_event ' .
91 '(write_id, obj_id, parent_obj_id, usr_id, action, ts) VALUES ' .
92 '(%s, %s, %s, %s, %s, ' . $ilDB->now() . ')',
93 $ilDB->quote($nid, 'integer'),
94 $ilDB->quote($obj_id, 'integer'),
95 $ilDB->quote($prec["par_obj_id"], 'integer'),
96 $ilDB->quote($usr_id, 'integer'),
97 $ilDB->quote($action, 'text')
98 );
99
100 $aff = $ilDB->manipulate($query);
101 }
102 } else {
103 $nid = $ilDB->nextId("write_event");
104 $query = sprintf(
105 'INSERT INTO write_event ' .
106 '(write_id, obj_id, parent_obj_id, usr_id, action, ts) ' .
107 'VALUES (%s,%s,%s,%s,%s,' . $ilDB->now() . ')',
108 $ilDB->quote($nid, 'integer'),
109 $ilDB->quote($obj_id, 'integer'),
110 $ilDB->quote($parent_obj_id, 'integer'),
111 $ilDB->quote($usr_id, 'integer'),
112 $ilDB->quote($action, 'text')
113 );
114 $aff = $ilDB->manipulate($query);
115 }
116 }
117
118 public static function _recordReadEvent(
119 string $a_type,
120 int $a_ref_id,
121 int $obj_id,
122 int $usr_id,
123 $a_ext_rc = null,
124 $a_ext_time = null
125 ): void {
126 global $DIC;
127
128 $ilDB = $DIC['ilDB'];
129 $tree = $DIC['tree'];
130
131 $validTimeSpan = ilObjUserTracking::_getValidTimeSpan();
132
133 $query = sprintf(
134 'SELECT * FROM read_event ' .
135 'WHERE obj_id = %s ' .
136 'AND usr_id = %s ',
137 $ilDB->quote($obj_id, 'integer'),
138 $ilDB->quote($usr_id, 'integer')
139 );
140 $res = $ilDB->query($query);
141 $row = $ilDB->fetchObject($res);
142
143 // read counter
144 if ($a_ext_rc !== null) {
145 $read_count = 'read_count = ' . $ilDB->quote(
146 $a_ext_rc,
147 "integer"
148 ) . ", ";
149 $read_count_init = max(1, (int) $a_ext_rc);
150 $read_count_diff = max(1, (int) $a_ext_rc) - (int) ($row?->read_count ?? 0);
151 } else {
152 $read_count = 'read_count = read_count + 1, ';
153 $read_count_init = 1;
154 $read_count_diff = 1;
155 }
156
157 if ($row) {
158 if ($a_ext_time !== null) {
159 $time = (int) $a_ext_time;
160 } else {
161 $time = $ilDB->quote(
162 (time() - $row->last_access) <= $validTimeSpan
163 ? $row->spent_seconds + time() - $row->last_access
164 : $row->spent_seconds,
165 'integer'
166 );
167
168 // if we are in the valid interval, we do not
169 // add anything to the read_count, since this is the
170 // same access for us
171 if ((time() - $row->last_access) <= $validTimeSpan) {
172 $read_count = '';
173 $read_count_init = 1;
174 $read_count_diff = 0;
175 }
176 }
177 $time_diff = $time - (int) ($row->spent_seconds ?? 0);
178
179 // Update
180 $query = sprintf(
181 'UPDATE read_event SET ' .
182 $read_count .
183 'spent_seconds = %s, ' .
184 'last_access = %s ' .
185 'WHERE obj_id = %s ' .
186 'AND usr_id = %s ',
187 $time,
188 $ilDB->quote(time(), 'integer'),
189 $ilDB->quote($obj_id, 'integer'),
190 $ilDB->quote($usr_id, 'integer')
191 );
192 $aff = $ilDB->manipulate($query);
193
194 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
195 } else {
196 if ($a_ext_time !== false) {
197 $time = (int) $a_ext_time;
198 } else {
199 $time = 0;
200 }
201
202 $time_diff = $time - (int) ($row->spent_seconds ?? 0);
203
204 // #10407
205 $ilDB->replace(
206 'read_event',
207 array(
208 'obj_id' => array('integer', $obj_id),
209 'usr_id' => array('integer', $usr_id)
210 ),
211 array(
212 'read_count' => array('integer', $read_count_init),
213 'spent_seconds' => array('integer', $time),
214 'first_access' => array('timestamp', date("Y-m-d H:i:s")),
215 // was $ilDB->now()
216 'last_access' => array('integer', time())
217 )
218 );
219
220 self::$has_accessed[$obj_id][$usr_id] = true;
221
222 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
223 }
224
225 // update parents (no categories or root)
226 if (!in_array($a_type, array("cat", "root", "crs"))) {
227 if ($tree->isInTree($a_ref_id)) {
228 $path = $tree->getPathId($a_ref_id);
229
230 foreach ($path as $p) {
231 $obj2_id = ilObject::_lookupObjId($p);
232 $obj2_type = ilObject::_lookupType($obj2_id);
233 //echo "<br>1-$obj2_type-$p-$obj2_id-";
234 if (($p != $a_ref_id) && (in_array(
235 $obj2_type,
236 array("crs",
237 "fold",
238 "grp",
239 "lso"
240 )
241 ))) {
242 $query = sprintf(
243 'SELECT * FROM read_event ' .
244 'WHERE obj_id = %s ' .
245 'AND usr_id = %s ',
246 $ilDB->quote($obj2_id, 'integer'),
247 $ilDB->quote($usr_id, 'integer')
248 );
249 $res2 = $ilDB->query($query);
250 if ($row2 = $ilDB->fetchAssoc($res2)) {
251 //echo "<br>2";
252 // update read count and spent seconds
253 $query = sprintf(
254 'UPDATE read_event SET ' .
255 'childs_read_count = childs_read_count + %s ,' .
256 'childs_spent_seconds = childs_spent_seconds + %s ' .
257 'WHERE obj_id = %s ' .
258 'AND usr_id = %s ',
259 $ilDB->quote((int) $read_count_diff, 'integer'),
260 $ilDB->quote((int) $time_diff, 'integer'),
261 $ilDB->quote($obj2_id, 'integer'),
262 $ilDB->quote($usr_id, 'integer')
263 );
264 $aff = $ilDB->manipulate($query);
265
266 self::_recordObjStats(
267 $obj2_id,
268 null,
269 null,
270 (int) $time_diff,
271 (int) $read_count_diff
272 );
273 } else {
274 // #10407
275 $ilDB->replace(
276 'read_event',
277 array(
278 'obj_id' => array('integer', $obj2_id),
279 'usr_id' => array('integer', $usr_id)
280 ),
281 array(
282 'read_count' => array('integer', 1),
283 'spent_seconds' => array('integer', $time),
284 'first_access' => array('timestamp',
285 date("Y-m-d H:i:s")
286 ), // was $ilDB->now()
287 'last_access' => array('integer', time()),
288 'childs_read_count' => array('integer',
289 (int) $read_count_diff
290 ),
291 'childs_spent_seconds' => array('integer',
292 (int) $time_diff
293 )
294 )
295 );
296
297 self::$has_accessed[$obj2_id][$usr_id] = true;
298
299 self::_recordObjStats(
300 $obj2_id,
301 $time,
302 1,
303 (int) $time_diff,
304 (int) $read_count_diff
305 );
306 }
307 }
308 }
309 }
310 }
311
312 // @todo:
313 // - calculate diff of spent_seconds and read_count
314 // - use ref id to get parents of types grp, crs, fold
315 // - add diffs to childs_spent_seconds and childs_read_count
316 }
317
318 public static function _recordObjStats(
319 int $a_obj_id,
320 ?int $a_spent_seconds,
321 ?int $a_read_count,
322 ?int $a_childs_spent_seconds = null,
323 ?int $a_child_read_count = null
324 ): void {
325 global $DIC;
326
327 $ilDB = $DIC['ilDB'];
328
330 $a_obj_id <= 0) { // #12706
331 return;
332 }
333
334 $now = time();
335
336 $fields = array();
337 $fields['log_id'] = array("integer", $ilDB->nextId('obj_stat_log'));
338 $fields["obj_id"] = array("integer", $a_obj_id);
339 $fields["obj_type"] = array("text", ilObject::_lookupType($a_obj_id));
340 $fields["tstamp"] = array("timestamp", $now);
341 $fields["yyyy"] = array("integer", date("Y"));
342 $fields["mm"] = array("integer", date("m"));
343 $fields["dd"] = array("integer", date("d"));
344 $fields["hh"] = array("integer", date("H"));
345 if ($a_spent_seconds > 0) {
346 $fields["spent_seconds"] = array("integer", $a_spent_seconds);
347 }
348 if ($a_read_count > 0) {
349 $fields["read_count"] = array("integer", $a_read_count);
350 }
351 if ($a_childs_spent_seconds > 0) {
352 $fields["childs_spent_seconds"] = array("integer",
353 $a_childs_spent_seconds
354 );
355 }
356 if ($a_child_read_count > 0) {
357 $fields["childs_read_count"] = array("integer",
358 $a_child_read_count
359 );
360 }
361 $ilDB->insert("obj_stat_log", $fields);
362
363 // 0.01% probability
364 if (mt_rand(1, 100) == 1) {
365 self::_syncObjectStats($now);
366 }
367 }
368
369 public static function _syncObjectStats(
370 ?int $a_now = null,
371 int $a_minimum = 20000
372 ) {
373 global $DIC;
374
375 $ilDB = $DIC['ilDB'];
376
377 if (!$a_now) {
378 $a_now = time();
379 }
380
381 set_time_limit(0);
382
383 // has source table enough entries?
384 $set = $ilDB->query("SELECT COUNT(*) AS counter FROM obj_stat_log");
385 $row = $ilDB->fetchAssoc($set);
386 if ($row["counter"] >= $a_minimum) {
387 $ilAtomQuery = $ilDB->buildAtomQuery();
388 $ilAtomQuery->addTableLock('obj_stat_log');
389 $ilAtomQuery->addTableLock('obj_stat_tmp');
390
391 $ilAtomQuery->addQueryCallable(
392 function (ilDBInterface $ilDB) use ($a_now, $a_minimum, &$ret) {
393 // if other process was transferring, we had to wait for the lock and
394 // the source table should now have less than minimum/needed entries
395 $set = $ilDB->query(
396 "SELECT COUNT(*) AS counter FROM obj_stat_log"
397 );
398 $row = $ilDB->fetchAssoc($set);
399 if ($row["counter"] >= $a_minimum) {
400 // use only "full" seconds to have a clear cut
401 $ilDB->query(
402 "INSERT INTO obj_stat_tmp" .
403 " SELECT * FROM obj_stat_log" .
404 " WHERE tstamp < " . $ilDB->quote(
405 $a_now,
406 "timestamp"
407 )
408 );
409
410 // remove transferred entries from source table
411 $ilDB->query(
412 "DELETE FROM obj_stat_log" .
413 " WHERE tstamp < " . $ilDB->quote(
414 $a_now,
415 "timestamp"
416 )
417 );
418
419 $ret = true;
420 } else {
421 $ret = false;
422 }
423 }
424 );
425
426 $ilAtomQuery->run();
427
428 //continue only if obj_stat_log counter >= $a_minimum
429 if ($ret) {
430 $ilAtomQuery = $ilDB->buildAtomQuery();
431 $ilAtomQuery->addTableLock('obj_stat_tmp');
432 $ilAtomQuery->addTableLock('obj_stat');
433
434 $ilAtomQuery->addQueryCallable(
435 function (ilDBInterface $ilDB) use ($a_now, $a_minimum) {
436 // process log data (timestamp is not needed anymore)
437 $sql = "SELECT obj_id, obj_type, yyyy, mm, dd, hh, SUM(read_count) AS read_count," .
438 " SUM(childs_read_count) AS childs_read_count, SUM(spent_seconds) AS spent_seconds," .
439 " SUM(childs_spent_seconds) AS childs_spent_seconds" .
440 " FROM obj_stat_tmp" .
441 " GROUP BY obj_id, obj_type, yyyy, mm, dd, hh";
442 $set = $ilDB->query($sql);
443 while ($row = $ilDB->fetchAssoc($set)) {
444 // "primary key"
445 $where = array("obj_id" => array("integer",
446 $row["obj_id"]
447 ),
448 "obj_type" => array("text",
449 $row["obj_type"]
450 ),
451 "yyyy" => array("integer",
452 $row["yyyy"]
453 ),
454 "mm" => array("integer", $row["mm"]),
455 "dd" => array("integer", $row["dd"]),
456 "hh" => array("integer", $row["hh"])
457 );
458
459 $where_sql = array();
460 foreach ($where as $field => $def) {
461 $where_sql[] = $field . " = " . $ilDB->quote(
462 $def[1],
463 $def[0]
464 );
465 }
466 $where_sql = implode(" AND ", $where_sql);
467
468 // existing entry?
469 $check = $ilDB->query(
470 "SELECT read_count, childs_read_count, spent_seconds," .
471 "childs_spent_seconds" .
472 " FROM obj_stat" .
473 " WHERE " . $where_sql
474 );
475 if ($ilDB->numRows($check)) {
476 $old = $ilDB->fetchAssoc($check);
477
478 // add existing values
479 $fields = array("read_count" => array("integer",
480 $old["read_count"] + $row["read_count"]
481 ),
482 "childs_read_count" => array("integer",
483 $old["childs_read_count"] + $row["childs_read_count"]
484 ),
485 "spent_seconds" => array("integer",
486 $old["spent_seconds"] + $row["spent_seconds"]
487 ),
488 "childs_spent_seconds" => array("integer",
489 $old["childs_spent_seconds"] + $row["childs_spent_seconds"]
490 )
491 );
492
493 $ilDB->update("obj_stat", $fields, $where);
494 } else {
495 // new entry
496 $fields = $where;
497 $fields["read_count"] = array("integer",
498 $row["read_count"]
499 );
500 $fields["childs_read_count"] = array("integer",
501 $row["childs_read_count"]
502 );
503 $fields["spent_seconds"] = array("integer",
504 $row["spent_seconds"]
505 );
506 $fields["childs_spent_seconds"] = array("integer",
507 $row["childs_spent_seconds"]
508 );
509
510 $ilDB->insert("obj_stat", $fields);
511 }
512 }
513
514 // clean up transfer table
515 $ilDB->query("DELETE FROM obj_stat_tmp");
516 }
517 );
518
519 $ilAtomQuery->run();
520 }
521 }
522 }
523
529 public static function _lookupReadEvents($obj_id, $usr_id = null)
530 {
531 global $DIC;
532
533 $ilDB = $DIC['ilDB'];
534
535 if ($usr_id == null) {
536 $query = sprintf(
537 'SELECT * FROM read_event ' .
538 'WHERE obj_id = %s ' .
539 'ORDER BY last_access DESC',
540 $ilDB->quote($obj_id, 'integer')
541 );
542 $res = $ilDB->query($query);
543 } else {
544 $query = sprintf(
545 'SELECT * FROM read_event ' .
546 'WHERE obj_id = %s ' .
547 'AND usr_id = %s ' .
548 'ORDER BY last_access DESC',
549 $ilDB->quote($obj_id, 'integer'),
550 $ilDB->quote($usr_id, 'integer')
551 );
552 $res = $ilDB->query($query);
553 }
554
555 $counter = 0;
556 $events = [];
557 while ($row = $ilDB->fetchAssoc($res)) {
558 $events[$counter]['obj_id'] = $row['obj_id'];
559 $events[$counter]['usr_id'] = $row['usr_id'];
560 $events[$counter]['last_access'] = $row['last_access'];
561 $events[$counter]['read_count'] = $row['read_count'];
562 $events[$counter]['spent_seconds'] = $row['spent_seconds'];
563 $events[$counter]['first_access'] = $row['first_access'];
564
565 $counter++;
566 }
567 return $events;
568 }
569
570 public static function lookupUsersInProgress(int $a_obj_id): array
571 {
572 global $DIC;
573
574 $ilDB = $DIC['ilDB'];
575
576 $query = sprintf(
577 'SELECT DISTINCT(usr_id) usr FROM read_event ' .
578 'WHERE obj_id = %s ',
579 $ilDB->quote($a_obj_id, 'integer')
580 );
581 $res = $ilDB->query($query);
582 $users = [];
583 while ($row = $ilDB->fetchObject($res)) {
584 $users[] = (int) $row->usr;
585 }
586 return $users;
587 }
588
592 public static function hasAccessed(int $a_obj_id, int $a_usr_id): bool
593 {
594 global $DIC;
595
596 $ilDB = $DIC['ilDB'];
597
598 if (isset(self::$has_accessed[$a_obj_id][$a_usr_id])) {
599 return self::$has_accessed[$a_obj_id][$a_usr_id];
600 }
601
602 $set = $ilDB->query(
603 "SELECT usr_id FROM read_event WHERE " .
604 "obj_id = " . $ilDB->quote($a_obj_id, "integer") . " AND " .
605 "usr_id = " . $ilDB->quote($a_usr_id, "integer")
606 );
607 if ($rec = $ilDB->fetchAssoc($set)) {
608 return self::$has_accessed[$a_obj_id][$a_usr_id] = true;
609 }
610 return self::$has_accessed[$a_obj_id][$a_usr_id] = false;
611 }
612
616 public static function _activate(): bool
617 {
619 return false;
620 } else {
621 global $DIC;
622
623 $ilDB = $DIC['ilDB'];
624
625 // Insert initial data into table write_event
626 // We need to do this here, because we need
627 // to catch up write events that occured while the change event tracking was
628 // deactivated.
629
630 // IGNORE isn't supported in oracle
631 $set = $ilDB->query(
632 sprintf(
633 'SELECT r1.obj_id,r2.obj_id p,d.owner,%s,d.create_date ' .
634 'FROM object_data d ' .
635 'LEFT JOIN write_event w ON d.obj_id = w.obj_id ' .
636 'JOIN object_reference r1 ON d.obj_id=r1.obj_id ' .
637 'JOIN tree t ON t.child=r1.ref_id ' .
638 'JOIN object_reference r2 on r2.ref_id=t.parent ' .
639 'WHERE w.obj_id IS NULL',
640 $ilDB->quote('create', 'text')
641 )
642 );
643 $res = null;
644 while ($rec = $ilDB->fetchAssoc($set)) {
645 $nid = $ilDB->nextId("write_event");
646 $query = 'INSERT INTO write_event ' .
647 '(write_id, obj_id,parent_obj_id,usr_id,action,ts) VALUES (' .
648 $ilDB->quote($nid, "integer") . "," .
649 $ilDB->quote($rec["obj_id"], "integer") . "," .
650 $ilDB->quote($rec["p"], "integer") . "," .
651 $ilDB->quote($rec["owner"], "integer") . "," .
652 $ilDB->quote("create", "text") . "," .
653 $ilDB->quote($rec["create_date"], "timestamp") .
654 ')';
655 $res = $ilDB->query($query);
656 }
657
658 global $DIC;
659
660 $ilSetting = $DIC['ilSetting'];
661 $ilSetting->set('enable_change_event_tracking', '1');
662
663 return $res !== null;
664 }
665 }
666
670 public static function _deactivate(): bool
671 {
672 global $DIC;
673
674 $ilSetting = $DIC['ilSetting'];
675 $ilSetting->set('enable_change_event_tracking', '0');
676 return true;
677 }
678
682 public static function _isActive(): bool
683 {
684 global $DIC;
685
686 $ilSetting = $DIC['ilSetting'];
687 return $ilSetting->get('enable_change_event_tracking', '0') == '1';
688 }
689
693 public static function _delete(int $a_obj_id): bool
694 {
695 global $DIC;
696
697 $ilDB = $DIC['ilDB'];
698 $query = sprintf(
699 'DELETE FROM write_event WHERE obj_id = %s ',
700 $ilDB->quote($a_obj_id, 'integer')
701 );
702 $aff = $ilDB->manipulate($query);
703
704 $query = sprintf(
705 'DELETE FROM read_event WHERE obj_id = %s ',
706 $ilDB->quote($a_obj_id, 'integer')
707 );
708 $aff = $ilDB->manipulate($query);
709 return true;
710 }
711
712 public static function _deleteReadEvents(int $a_obj_id): void
713 {
714 global $DIC;
715
716 $ilDB = $DIC['ilDB'];
717
718 $ilDB->manipulate(
719 "DELETE FROM read_event" .
720 " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer")
721 );
722 }
723
724 public static function _deleteReadEventsForUsers(
725 int $a_obj_id,
726 array $a_user_ids
727 ): void {
728 global $DIC;
729
730 $ilDB = $DIC['ilDB'];
731
732 $ilDB->manipulate(
733 "DELETE FROM read_event" .
734 " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer") .
735 " AND " . $ilDB->in("usr_id", $a_user_ids, "", "integer")
736 );
737 }
738
739 public static function _getAllUserIds(int $a_obj_id): array
740 {
741 global $DIC;
742
743 $ilDB = $DIC['ilDB'];
744 $res = array();
745 $set = $ilDB->query(
746 "SELECT usr_id FROM read_event" .
747 " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer")
748 );
749 while ($row = $ilDB->fetchAssoc($set)) {
750 $res[] = (int) $row["usr_id"];
751 }
752 return $res;
753 }
754
761 int $obj_id,
762 int $usr_id,
763 int $i_last_access,
764 string $t_first_access
765 ): bool {
766 global $DIC;
767
768 $ilDB = $DIC->database();
769 $res = $ilDB->queryF(
770 'UPDATE read_event SET first_access=%s, last_access = %s WHERE obj_id=%s AND usr_id=%s',
771 array('timestamp', 'integer', 'integer', 'integer'),
772 array($t_first_access, $i_last_access, $obj_id, $usr_id)
773 );
774 return true;
775 }
776}
$check
Definition: buildRTE.php:81
Class ilChangeEvent tracks change events on repository objects.
static _recordReadEvent(string $a_type, int $a_ref_id, int $obj_id, int $usr_id, $a_ext_rc=null, $a_ext_time=null)
static _deleteReadEvents(int $a_obj_id)
static _updateAccessForScormOfflinePlayer(int $obj_id, int $usr_id, int $i_last_access, string $t_first_access)
_updateAccessForScormOfflinePlayer needed to synchronize last_access and first_access when learning m...
static _syncObjectStats(?int $a_now=null, int $a_minimum=20000)
static _lookupReadEvents($obj_id, $usr_id=null)
Reads all read events which occured on the object.
static _isActive()
Returns true, if change event tracking is active.
static hasAccessed(int $a_obj_id, int $a_usr_id)
Has accessed.
static _deleteReadEventsForUsers(int $a_obj_id, array $a_user_ids)
static array $has_accessed
static _activate()
Activates change event tracking.
static _deactivate()
Deactivates change event tracking.
static lookupUsersInProgress(int $a_obj_id)
static _recordObjStats(int $a_obj_id, ?int $a_spent_seconds, ?int $a_read_count, ?int $a_childs_spent_seconds=null, ?int $a_child_read_count=null)
static _delete(int $a_obj_id)
Delete object entries.
static _getAllUserIds(int $a_obj_id)
static _recordWriteEvent(int $obj_id, int $usr_id, string $action, ?int $parent_obj_id=null)
Records a write event.
static _lookupType(int $id, bool $reference=false)
static _lookupObjId(int $ref_id)
Interface ilDBInterface.
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
global $ilSetting
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26
$counter