3 declare(strict_types=0);
51 ?
int $parent_obj_id = null
64 if ($parent_obj_id == null) {
66 'SELECT r2.obj_id par_obj_id FROM object_reference r1 ' .
67 'JOIN tree t ON t.child = r1.ref_id ' .
68 'JOIN object_reference r2 ON r2.ref_id = t.parent ' .
69 'WHERE r1.obj_id = ' .
$ilDB->quote($obj_id,
'integer')
72 while ($prec =
$ilDB->fetchAssoc($pset)) {
73 $nid =
$ilDB->nextId(
"write_event");
75 'INSERT INTO write_event ' .
76 '(write_id, obj_id, parent_obj_id, usr_id, action, ts) VALUES ' .
77 '(%s, %s, %s, %s, %s, ' .
$ilDB->now() .
')',
78 $ilDB->quote($nid,
'integer'),
79 $ilDB->quote($obj_id,
'integer'),
80 $ilDB->quote($prec[
"par_obj_id"],
'integer'),
81 $ilDB->quote($usr_id,
'integer'),
82 $ilDB->quote($action,
'text')
88 $nid =
$ilDB->nextId(
"write_event");
90 'INSERT INTO write_event ' .
91 '(write_id, obj_id, parent_obj_id, usr_id, action, ts) ' .
92 'VALUES (%s,%s,%s,%s,%s,' .
$ilDB->now() .
')',
93 $ilDB->quote($nid,
'integer'),
94 $ilDB->quote($obj_id,
'integer'),
95 $ilDB->quote($parent_obj_id,
'integer'),
96 $ilDB->quote($usr_id,
'integer'),
97 $ilDB->quote($action,
'text')
108 bool $isCatchupWriteEvents =
true,
114 $ilDB = $DIC[
'ilDB'];
115 $tree = $DIC[
'tree'];
120 'SELECT * FROM read_event ' .
121 'WHERE obj_id = %s ' .
123 $ilDB->quote($obj_id,
'integer'),
124 $ilDB->quote($usr_id,
'integer')
127 $row =
$ilDB->fetchObject($res);
130 if ($a_ext_rc !== null) {
131 $read_count =
'read_count = ' .
$ilDB->quote(
135 $read_count_init = max(1, (
int) $a_ext_rc);
136 $read_count_diff = max(1, (
int) $a_ext_rc) - $row->read_count;
138 $read_count =
'read_count = read_count + 1, ';
139 $read_count_init = 1;
140 $read_count_diff = 1;
144 if ($a_ext_time !== null) {
145 $time = (
int) $a_ext_time;
147 $time =
$ilDB->quote(
148 (time() - $row->last_access) <= $validTimeSpan
149 ? $row->spent_seconds + time() - $row->last_access
150 : $row->spent_seconds,
157 if ((time() - $row->last_access) <= $validTimeSpan) {
159 $read_count_init = 1;
160 $read_count_diff = 0;
163 $time_diff = $time - (
int) ($row->spent_seconds ?? 0);
167 'UPDATE read_event SET ' .
169 'spent_seconds = %s, ' .
170 'last_access = %s ' .
171 'WHERE obj_id = %s ' .
174 $ilDB->quote(time(),
'integer'),
175 $ilDB->quote($obj_id,
'integer'),
176 $ilDB->quote($usr_id,
'integer')
180 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
182 if ($a_ext_time !==
false) {
183 $time = (
int) $a_ext_time;
188 $time_diff = $time - (
int) ($row->spent_seconds ?? 0);
194 'obj_id' => array(
'integer', $obj_id),
195 'usr_id' => array(
'integer', $usr_id)
198 'read_count' => array(
'integer', $read_count_init),
199 'spent_seconds' => array(
'integer', $time),
200 'first_access' => array(
'timestamp', date(
"Y-m-d H:i:s")),
202 'last_access' => array(
'integer', time())
206 self::$has_accessed[$obj_id][$usr_id] =
true;
208 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
211 if ($isCatchupWriteEvents) {
216 if (!in_array($a_type, array(
"cat",
"root",
"crs"))) {
217 if ($tree->isInTree($a_ref_id)) {
218 $path = $tree->getPathId($a_ref_id);
220 foreach (
$path as $p) {
224 if (($p != $a_ref_id) && (in_array(
233 'SELECT * FROM read_event ' .
234 'WHERE obj_id = %s ' .
236 $ilDB->quote($obj2_id,
'integer'),
237 $ilDB->quote($usr_id,
'integer')
240 if ($row2 =
$ilDB->fetchAssoc($res2)) {
244 'UPDATE read_event SET ' .
245 'childs_read_count = childs_read_count + %s ,' .
246 'childs_spent_seconds = childs_spent_seconds + %s ' .
247 'WHERE obj_id = %s ' .
249 $ilDB->quote((
int) $read_count_diff,
'integer'),
250 $ilDB->quote((
int) $time_diff,
'integer'),
251 $ilDB->quote($obj2_id,
'integer'),
252 $ilDB->quote($usr_id,
'integer')
256 self::_recordObjStats(
261 (
int) $read_count_diff
269 'obj_id' => array(
'integer', $obj2_id),
270 'usr_id' => array(
'integer', $usr_id)
273 'read_count' => array(
'integer', 1),
274 'spent_seconds' => array(
'integer', $time),
275 'first_access' => array(
'timestamp',
278 'last_access' => array(
'integer', time()),
279 'childs_read_count' => array(
'integer',
280 (
int) $read_count_diff
282 'childs_spent_seconds' => array(
'integer',
288 self::$has_accessed[$obj2_id][$usr_id] =
true;
290 self::_recordObjStats(
295 (
int) $read_count_diff
311 ?
int $a_spent_seconds,
313 ?
int $a_childs_spent_seconds = null,
314 ?
int $a_child_read_count = null
318 $ilDB = $DIC[
'ilDB'];
328 $fields[
'log_id'] = array(
"integer",
$ilDB->nextId(
'obj_stat_log'));
329 $fields[
"obj_id"] = array(
"integer", $a_obj_id);
331 $fields[
"tstamp"] = array(
"timestamp", $now);
332 $fields[
"yyyy"] = array(
"integer", date(
"Y"));
333 $fields[
"mm"] = array(
"integer", date(
"m"));
334 $fields[
"dd"] = array(
"integer", date(
"d"));
335 $fields[
"hh"] = array(
"integer", date(
"H"));
336 if ($a_spent_seconds > 0) {
337 $fields[
"spent_seconds"] = array(
"integer", $a_spent_seconds);
339 if ($a_read_count > 0) {
340 $fields[
"read_count"] = array(
"integer", $a_read_count);
342 if ($a_childs_spent_seconds > 0) {
343 $fields[
"childs_spent_seconds"] = array(
"integer",
344 $a_childs_spent_seconds
347 if ($a_child_read_count > 0) {
348 $fields[
"childs_read_count"] = array(
"integer",
352 $ilDB->insert(
"obj_stat_log", $fields);
355 if (mt_rand(1, 100) == 1) {
356 self::_syncObjectStats($now);
362 int $a_minimum = 20000
366 $ilDB = $DIC[
'ilDB'];
375 $set =
$ilDB->query(
"SELECT COUNT(*) AS counter FROM obj_stat_log");
376 $row =
$ilDB->fetchAssoc($set);
377 if ($row[
"counter"] >= $a_minimum) {
378 $ilAtomQuery =
$ilDB->buildAtomQuery();
379 $ilAtomQuery->addTableLock(
'obj_stat_log');
380 $ilAtomQuery->addTableLock(
'obj_stat_tmp');
382 $ilAtomQuery->addQueryCallable(
388 "SELECT COUNT(*) AS counter FROM obj_stat_log" 391 if ($row[
"counter"] >= $a_minimum) {
394 "INSERT INTO obj_stat_tmp" .
395 " SELECT * FROM obj_stat_log" .
396 " WHERE tstamp < " . $ilDB->
quote(
404 "DELETE FROM obj_stat_log" .
405 " WHERE tstamp < " . $ilDB->
quote(
423 $ilAtomQuery->addTableLock(
'obj_stat_tmp');
424 $ilAtomQuery->addTableLock(
'obj_stat');
426 $ilAtomQuery->addQueryCallable(
430 $sql =
"SELECT obj_id, obj_type, yyyy, mm, dd, hh, SUM(read_count) AS read_count," .
431 " SUM(childs_read_count) AS childs_read_count, SUM(spent_seconds) AS spent_seconds," .
432 " SUM(childs_spent_seconds) AS childs_spent_seconds" .
433 " FROM obj_stat_tmp" .
434 " GROUP BY obj_id, obj_type, yyyy, mm, dd, hh";
435 $set = $ilDB->
query($sql);
438 $where = array(
"obj_id" => array(
"integer",
441 "obj_type" => array(
"text",
444 "yyyy" => array(
"integer",
447 "mm" => array(
"integer", $row[
"mm"]),
448 "dd" => array(
"integer", $row[
"dd"]),
449 "hh" => array(
"integer", $row[
"hh"])
452 $where_sql = array();
453 foreach ($where as $field => $def) {
454 $where_sql[] = $field .
" = " . $ilDB->
quote(
459 $where_sql = implode(
" AND ", $where_sql);
463 "SELECT read_count, childs_read_count, spent_seconds," .
464 "childs_spent_seconds" .
466 " WHERE " . $where_sql
472 $fields = array(
"read_count" => array(
"integer",
473 $old[
"read_count"] + $row[
"read_count"]
475 "childs_read_count" => array(
"integer",
476 $old[
"childs_read_count"] + $row[
"childs_read_count"]
478 "spent_seconds" => array(
"integer",
479 $old[
"spent_seconds"] + $row[
"spent_seconds"]
481 "childs_spent_seconds" => array(
"integer",
482 $old[
"childs_spent_seconds"] + $row[
"childs_spent_seconds"]
486 $ilDB->
update(
"obj_stat", $fields, $where);
490 $fields[
"read_count"] = array(
"integer",
493 $fields[
"childs_read_count"] = array(
"integer",
494 $row[
"childs_read_count"]
496 $fields[
"spent_seconds"] = array(
"integer",
497 $row[
"spent_seconds"]
499 $fields[
"childs_spent_seconds"] = array(
"integer",
500 $row[
"childs_spent_seconds"]
503 $ilDB->
insert(
"obj_stat", $fields);
508 $ilDB->
query(
"DELETE FROM obj_stat_tmp");
532 $ilDB = $DIC[
'ilDB'];
534 $query =
"SELECT obj_id FROM catch_write_events " .
535 "WHERE obj_id = " .
$ilDB->quote($obj_id,
'integer') .
" " .
536 "AND usr_id = " .
$ilDB->quote($usr_id,
'integer');
538 if (
$res->numRows()) {
548 "catch_write_events",
550 "obj_id" => array(
"integer", $obj_id),
551 "usr_id" => array(
"integer", $usr_id)
554 "ts" => array(
"timestamp", $ts)
572 $ilDB = $DIC[
'ilDB'];
574 "FROM catch_write_events " .
575 "WHERE obj_id=" .
$ilDB->quote($obj_id,
'integer') .
" " .
576 "AND usr_id=" .
$ilDB->quote($usr_id,
'integer');
577 $r =
$ilDB->query($q);
580 $catchup = $row[
'ts'];
583 if ($catchup == null) {
585 'SELECT * FROM write_event ' .
586 'WHERE obj_id = %s ' .
587 'AND usr_id <> %s ' .
589 $ilDB->quote($obj_id,
'integer'),
590 $ilDB->quote($usr_id,
'integer')
595 'SELECT * FROM write_event ' .
596 'WHERE obj_id = %s ' .
597 'AND usr_id <> %s ' .
600 $ilDB->quote($obj_id,
'integer'),
601 $ilDB->quote($usr_id,
'integer'),
602 $ilDB->quote($catchup,
'timestamp')
626 $ilDB = $DIC[
'ilDB'];
629 "FROM catch_write_events " .
630 "WHERE obj_id=" .
$ilDB->quote($obj_id,
'integer') .
" " .
631 "AND usr_id=" .
$ilDB->quote($usr_id,
'integer');
632 $r =
$ilDB->query($q);
635 $catchup = $row[
'ts'];
638 if ($catchup == null) {
641 'SELECT * FROM write_event ' .
642 'WHERE obj_id = %s ' .
644 $ilDB->quote($obj_id,
'integer'),
645 $ilDB->quote($usr_id,
'integer')
651 'SELECT * FROM write_event ' .
652 'WHERE obj_id = %s ' .
653 'AND usr_id <> %s ' .
655 $ilDB->quote($obj_id,
'integer'),
656 $ilDB->quote($usr_id,
'integer'),
657 $ilDB->quote($catchup,
'timestamp')
662 $numRows =
$res->numRows();
667 return ($catchup == null) ? 1 : 2;
682 $ilDB = $DIC[
'ilDB'];
684 if ($usr_id == null) {
686 'SELECT * FROM read_event ' .
687 'WHERE obj_id = %s ' .
688 'ORDER BY last_access DESC',
689 $ilDB->quote($obj_id,
'integer')
694 'SELECT * FROM read_event ' .
695 'WHERE obj_id = %s ' .
697 'ORDER BY last_access DESC',
698 $ilDB->quote($obj_id,
'integer'),
699 $ilDB->quote($usr_id,
'integer')
707 $events[$counter][
'obj_id'] = $row[
'obj_id'];
708 $events[$counter][
'usr_id'] = $row[
'usr_id'];
709 $events[$counter][
'last_access'] = $row[
'last_access'];
710 $events[$counter][
'read_count'] = $row[
'read_count'];
711 $events[$counter][
'spent_seconds'] = $row[
'spent_seconds'];
712 $events[$counter][
'first_access'] = $row[
'first_access'];
723 $ilDB = $DIC[
'ilDB'];
726 'SELECT DISTINCT(usr_id) usr FROM read_event ' .
727 'WHERE obj_id = %s ',
728 $ilDB->quote($a_obj_id,
'integer')
732 while ($row =
$ilDB->fetchObject($res)) {
733 $users[] = (
int) $row->usr;
741 public static function hasAccessed(
int $a_obj_id,
int $a_usr_id): bool
745 $ilDB = $DIC[
'ilDB'];
747 if (isset(self::$has_accessed[$a_obj_id][$a_usr_id])) {
748 return self::$has_accessed[$a_obj_id][$a_usr_id];
752 "SELECT usr_id FROM read_event WHERE " .
753 "obj_id = " .
$ilDB->quote($a_obj_id,
"integer") .
" AND " .
754 "usr_id = " .
$ilDB->quote($a_usr_id,
"integer")
756 if ($rec =
$ilDB->fetchAssoc($set)) {
757 return self::$has_accessed[$a_obj_id][$a_usr_id] =
true;
759 return self::$has_accessed[$a_obj_id][$a_usr_id] =
false;
772 $ilDB = $DIC[
'ilDB'];
782 'SELECT r1.obj_id,r2.obj_id p,d.owner,%s,d.create_date ' .
783 'FROM object_data d ' .
784 'LEFT JOIN write_event w ON d.obj_id = w.obj_id ' .
785 'JOIN object_reference r1 ON d.obj_id=r1.obj_id ' .
786 'JOIN tree t ON t.child=r1.ref_id ' .
787 'JOIN object_reference r2 on r2.ref_id=t.parent ' .
788 'WHERE w.obj_id IS NULL',
789 $ilDB->quote(
'create',
'text')
793 while ($rec =
$ilDB->fetchAssoc($set)) {
794 $nid =
$ilDB->nextId(
"write_event");
795 $query =
'INSERT INTO write_event ' .
796 '(write_id, obj_id,parent_obj_id,usr_id,action,ts) VALUES (' .
797 $ilDB->quote($nid,
"integer") .
"," .
798 $ilDB->quote($rec[
"obj_id"],
"integer") .
"," .
799 $ilDB->quote($rec[
"p"],
"integer") .
"," .
800 $ilDB->quote($rec[
"owner"],
"integer") .
"," .
801 $ilDB->quote(
"create",
"text") .
"," .
802 $ilDB->quote($rec[
"create_date"],
"timestamp") .
810 $ilSetting->set(
'enable_change_event_tracking',
'1');
812 return $res !== null;
824 $ilSetting->set(
'enable_change_event_tracking',
'0');
836 return $ilSetting->get(
'enable_change_event_tracking',
'0') ==
'1';
842 public static function _delete(
int $a_obj_id): bool
846 $ilDB = $DIC[
'ilDB'];
848 'DELETE FROM write_event WHERE obj_id = %s ',
849 $ilDB->quote($a_obj_id,
'integer')
854 'DELETE FROM read_event WHERE obj_id = %s ',
855 $ilDB->quote($a_obj_id,
'integer')
865 $ilDB = $DIC[
'ilDB'];
868 "DELETE FROM read_event" .
869 " WHERE obj_id = " .
$ilDB->quote($a_obj_id,
"integer")
879 $ilDB = $DIC[
'ilDB'];
882 "DELETE FROM read_event" .
883 " WHERE obj_id = " .
$ilDB->quote($a_obj_id,
"integer") .
884 " AND " .
$ilDB->in(
"usr_id", $a_user_ids,
"",
"integer")
892 $ilDB = $DIC[
'ilDB'];
895 "SELECT usr_id FROM read_event" .
896 " WHERE obj_id = " .
$ilDB->quote($a_obj_id,
"integer")
898 while ($row =
$ilDB->fetchAssoc($set)) {
static _delete(int $a_obj_id)
Delete object entries.
static _activate()
Activates change event tracking.
numRows(ilDBStatement $statement)
insert(string $table_name, array $values)
static _enabledObjectStatistics()
static _lookupUncaughtWriteEvents(int $obj_id, int $usr_id)
Reads all write events which occured on the object which happened after the last time the user caught...
fetchAssoc(ilDBStatement $statement)
update(string $table_name, array $values, array $where)
$where MUST contain existing columns only.
static hasAccessed(int $a_obj_id, int $a_usr_id)
Has accessed.
quote($value, string $type)
static now()
Return current timestamp in Y-m-d H:i:s format.
static _syncObjectStats(?int $a_now=null, int $a_minimum=20000)
static _lookupObjId(int $ref_id)
static lookupUsersInProgress(int $a_obj_id)
static _recordReadEvent(string $a_type, int $a_ref_id, int $obj_id, int $usr_id, bool $isCatchupWriteEvents=true, $a_ext_rc=null, $a_ext_time=null)
static _lookupChangeState(int $obj_id, int $usr_id)
Returns the change state of the object for the specified user.
static _recordWriteEvent(int $obj_id, int $usr_id, string $action, ?int $parent_obj_id=null)
Records a write event.
query(string $query)
Run a (read-only) Query on the database.
static _deleteReadEvents(int $a_obj_id)
static _lookupReadEvents($obj_id, $usr_id=null)
Reads all read events which occured on the object.
foreach($mandatory_scripts as $file) $timestamp
Class ilChangeEvent tracks change events on repository objects.
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 _isActive()
Returns true, if change event tracking is active.
static _deactivate()
Deactivates change event tracking.
static _deleteReadEventsForUsers(int $a_obj_id, array $a_user_ids)
static array $has_accessed
static _lookupType(int $id, bool $reference=false)
static _catchupWriteEvents(int $obj_id, int $usr_id, ?string $timestamp=null)
Catches up with all write events which occured before the specified timestamp.
static _getAllUserIds(int $a_obj_id)
static _getValidTimeSpan()