19declare(strict_types=0);
66 ?
int $parent_obj_id =
null
79 if ($parent_obj_id ==
null) {
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')
87 while ($prec =
$ilDB->fetchAssoc($pset)) {
88 $nid =
$ilDB->nextId(
"write_event");
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')
100 $aff =
$ilDB->manipulate($query);
103 $nid =
$ilDB->nextId(
"write_event");
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')
114 $aff =
$ilDB->manipulate($query);
129 $tree =
$DIC[
'tree'];
134 'SELECT * FROM read_event ' .
135 'WHERE obj_id = %s ' .
137 $ilDB->quote($obj_id,
'integer'),
138 $ilDB->quote($usr_id,
'integer')
144 if ($a_ext_rc !==
null) {
145 $read_count =
'read_count = ' .
$ilDB->quote(
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);
152 $read_count =
'read_count = read_count + 1, ';
153 $read_count_init = 1;
154 $read_count_diff = 1;
158 if ($a_ext_time !==
null) {
159 $time = (
int) $a_ext_time;
161 $time =
$ilDB->quote(
162 (time() - $row->last_access) <= $validTimeSpan
163 ? $row->spent_seconds + time() - $row->last_access
164 : $row->spent_seconds,
171 if ((time() - $row->last_access) <= $validTimeSpan) {
173 $read_count_init = 1;
174 $read_count_diff = 0;
177 $time_diff = $time - (
int) ($row->spent_seconds ?? 0);
181 'UPDATE read_event SET ' .
183 'spent_seconds = %s, ' .
184 'last_access = %s ' .
185 'WHERE obj_id = %s ' .
188 $ilDB->quote(time(),
'integer'),
189 $ilDB->quote($obj_id,
'integer'),
190 $ilDB->quote($usr_id,
'integer')
192 $aff =
$ilDB->manipulate($query);
194 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
196 if ($a_ext_time !==
false) {
197 $time = (
int) $a_ext_time;
202 $time_diff = $time - (
int) ($row->spent_seconds ?? 0);
208 'obj_id' => array(
'integer', $obj_id),
209 'usr_id' => array(
'integer', $usr_id)
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")),
216 'last_access' => array(
'integer', time())
220 self::$has_accessed[$obj_id][$usr_id] =
true;
222 self::_recordObjStats($obj_id, $time_diff, $read_count_diff);
226 if (!in_array($a_type, array(
"cat",
"root",
"crs"))) {
227 if ($tree->isInTree($a_ref_id)) {
228 $path = $tree->getPathId($a_ref_id);
230 foreach (
$path as $p) {
234 if (($p != $a_ref_id) && (in_array(
243 'SELECT * FROM read_event ' .
244 'WHERE obj_id = %s ' .
246 $ilDB->quote($obj2_id,
'integer'),
247 $ilDB->quote($usr_id,
'integer')
249 $res2 =
$ilDB->query($query);
250 if ($row2 =
$ilDB->fetchAssoc($res2)) {
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 ' .
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')
264 $aff =
$ilDB->manipulate($query);
266 self::_recordObjStats(
271 (
int) $read_count_diff
278 'obj_id' => array(
'integer', $obj2_id),
279 'usr_id' => array(
'integer', $usr_id)
282 'read_count' => array(
'integer', 1),
283 'spent_seconds' => array(
'integer', $time),
284 'first_access' => array(
'timestamp',
287 'last_access' => array(
'integer', time()),
288 'childs_read_count' => array(
'integer',
289 (
int) $read_count_diff
291 'childs_spent_seconds' => array(
'integer',
297 self::$has_accessed[$obj2_id][$usr_id] =
true;
299 self::_recordObjStats(
304 (
int) $read_count_diff
320 ?
int $a_spent_seconds,
322 ?
int $a_childs_spent_seconds =
null,
323 ?
int $a_child_read_count =
null
337 $fields[
'log_id'] = array(
"integer",
$ilDB->nextId(
'obj_stat_log'));
338 $fields[
"obj_id"] = array(
"integer", $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);
348 if ($a_read_count > 0) {
349 $fields[
"read_count"] = array(
"integer", $a_read_count);
351 if ($a_childs_spent_seconds > 0) {
352 $fields[
"childs_spent_seconds"] = array(
"integer",
353 $a_childs_spent_seconds
356 if ($a_child_read_count > 0) {
357 $fields[
"childs_read_count"] = array(
"integer",
361 $ilDB->insert(
"obj_stat_log", $fields);
364 if (mt_rand(1, 100) == 1) {
365 self::_syncObjectStats($now);
371 int $a_minimum = 20000
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');
391 $ilAtomQuery->addQueryCallable(
396 "SELECT COUNT(*) AS counter FROM obj_stat_log"
398 $row =
$ilDB->fetchAssoc($set);
399 if ($row[
"counter"] >= $a_minimum) {
402 "INSERT INTO obj_stat_tmp" .
403 " SELECT * FROM obj_stat_log" .
404 " WHERE tstamp < " .
$ilDB->quote(
412 "DELETE FROM obj_stat_log" .
413 " WHERE tstamp < " .
$ilDB->quote(
430 $ilAtomQuery =
$ilDB->buildAtomQuery();
431 $ilAtomQuery->addTableLock(
'obj_stat_tmp');
432 $ilAtomQuery->addTableLock(
'obj_stat');
434 $ilAtomQuery->addQueryCallable(
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)) {
445 $where = array(
"obj_id" => array(
"integer",
448 "obj_type" => array(
"text",
451 "yyyy" => array(
"integer",
454 "mm" => array(
"integer", $row[
"mm"]),
455 "dd" => array(
"integer", $row[
"dd"]),
456 "hh" => array(
"integer", $row[
"hh"])
459 $where_sql = array();
460 foreach ($where as $field => $def) {
461 $where_sql[] = $field .
" = " . $ilDB->quote(
466 $where_sql = implode(
" AND ", $where_sql);
470 "SELECT read_count, childs_read_count, spent_seconds," .
471 "childs_spent_seconds" .
473 " WHERE " . $where_sql
476 $old = $ilDB->fetchAssoc($check);
479 $fields = array(
"read_count" => array(
"integer",
480 $old[
"read_count"] + $row[
"read_count"]
482 "childs_read_count" => array(
"integer",
483 $old[
"childs_read_count"] + $row[
"childs_read_count"]
485 "spent_seconds" => array(
"integer",
486 $old[
"spent_seconds"] + $row[
"spent_seconds"]
488 "childs_spent_seconds" => array(
"integer",
489 $old[
"childs_spent_seconds"] + $row[
"childs_spent_seconds"]
493 $ilDB->update(
"obj_stat", $fields, $where);
497 $fields[
"read_count"] = array(
"integer",
500 $fields[
"childs_read_count"] = array(
"integer",
501 $row[
"childs_read_count"]
503 $fields[
"spent_seconds"] = array(
"integer",
504 $row[
"spent_seconds"]
506 $fields[
"childs_spent_seconds"] = array(
"integer",
507 $row[
"childs_spent_seconds"]
510 $ilDB->insert(
"obj_stat", $fields);
515 $ilDB->query(
"DELETE FROM obj_stat_tmp");
535 if ($usr_id ==
null) {
537 'SELECT * FROM read_event ' .
538 'WHERE obj_id = %s ' .
539 'ORDER BY last_access DESC',
540 $ilDB->quote($obj_id,
'integer')
545 'SELECT * FROM read_event ' .
546 'WHERE obj_id = %s ' .
548 'ORDER BY last_access DESC',
549 $ilDB->quote($obj_id,
'integer'),
550 $ilDB->quote($usr_id,
'integer')
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'];
577 'SELECT DISTINCT(usr_id) usr FROM read_event ' .
578 'WHERE obj_id = %s ',
579 $ilDB->quote($a_obj_id,
'integer')
583 while ($row =
$ilDB->fetchObject(
$res)) {
584 $users[] = (
int) $row->usr;
592 public static function hasAccessed(
int $a_obj_id,
int $a_usr_id): bool
598 if (isset(self::$has_accessed[$a_obj_id][$a_usr_id])) {
599 return self::$has_accessed[$a_obj_id][$a_usr_id];
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")
607 if ($rec =
$ilDB->fetchAssoc($set)) {
608 return self::$has_accessed[$a_obj_id][$a_usr_id] =
true;
610 return self::$has_accessed[$a_obj_id][$a_usr_id] =
false;
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')
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") .
661 $ilSetting->set(
'enable_change_event_tracking',
'1');
663 return $res !==
null;
675 $ilSetting->set(
'enable_change_event_tracking',
'0');
687 return $ilSetting->get(
'enable_change_event_tracking',
'0') ==
'1';
693 public static function _delete(
int $a_obj_id): bool
699 'DELETE FROM write_event WHERE obj_id = %s ',
700 $ilDB->quote($a_obj_id,
'integer')
702 $aff =
$ilDB->manipulate($query);
705 'DELETE FROM read_event WHERE obj_id = %s ',
706 $ilDB->quote($a_obj_id,
'integer')
708 $aff =
$ilDB->manipulate($query);
719 "DELETE FROM read_event" .
720 " WHERE obj_id = " .
$ilDB->quote($a_obj_id,
"integer")
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")
746 "SELECT usr_id FROM read_event" .
747 " WHERE obj_id = " .
$ilDB->quote($a_obj_id,
"integer")
749 while ($row =
$ilDB->fetchAssoc($set)) {
764 string $t_first_access
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)
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 _enabledObjectStatistics()
static _getValidTimeSpan()
static _lookupType(int $id, bool $reference=false)
static _lookupObjId(int $ref_id)