* @version $Id: class.ilChangeEvent.php,v 1.02 2007/05/07 19:25:34 wrandels Exp $ * */ class ilChangeEvent { private static $has_accessed = array(); /** * Records a write event. * * The parent object should be specified for the 'delete', 'undelete' and * 'add' and 'remove' events. * * @param $obj_id int The object which was written to. * @param $usr_id int The user who performed a write action. * @param $action string The name of the write action. * 'create', 'update', 'delete', 'add', 'remove', 'undelete'. * @param $parent_obj_id int The object id of the parent object. * If this is null, then the event is recorded for all parents * of the object. If this is not null, then the event is only * recorded for the specified parent. */ public static function _recordWriteEvent($obj_id, $usr_id, $action, $parent_obj_id = null) { global $DIC; $ilDB = $DIC['ilDB']; /* see _recordReadEvent if (!ilChangeEvent::_isActive()) { return; } */ if ($parent_obj_id == null) { $pset = $ilDB->query('SELECT r2.obj_id par_obj_id FROM object_reference r1 ' . 'JOIN tree t ON t.child = r1.ref_id ' . 'JOIN object_reference r2 ON r2.ref_id = t.parent ' . 'WHERE r1.obj_id = ' . $ilDB->quote($obj_id, 'integer')); while ($prec = $ilDB->fetchAssoc($pset)) { $nid = $ilDB->nextId("write_event"); $query = sprintf( 'INSERT INTO write_event ' . '(write_id, obj_id, parent_obj_id, usr_id, action, ts) VALUES ' . '(%s, %s, %s, %s, %s, ' . $ilDB->now() . ')', $ilDB->quote($nid, 'integer'), $ilDB->quote($obj_id, 'integer'), $ilDB->quote($prec["par_obj_id"], 'integer'), $ilDB->quote($usr_id, 'integer'), $ilDB->quote($action, 'text') ); $aff = $ilDB->manipulate($query); } } else { $nid = $ilDB->nextId("write_event"); $query = sprintf( 'INSERT INTO write_event ' . '(write_id, obj_id, parent_obj_id, usr_id, action, ts) ' . 'VALUES (%s,%s,%s,%s,%s,' . $ilDB->now() . ')', $ilDB->quote($nid, 'integer'), $ilDB->quote($obj_id, 'integer'), $ilDB->quote($parent_obj_id, 'integer'), $ilDB->quote($usr_id, 'integer'), $ilDB->quote($action, 'text') ); $aff = $ilDB->manipulate($query); } } /** * Records a read event and catches up with write events. * * @param $obj_id int The object which was read. * @param $usr_id int The user who performed a read action. * @param $catchupWriteEvents boolean If true, this function catches up with * write events. */ public static function _recordReadEvent( $a_type, $a_ref_id, $obj_id, $usr_id, $isCatchupWriteEvents = true, $a_ext_rc = false, $a_ext_time = false ) { global $DIC; $ilDB = $DIC['ilDB']; $tree = $DIC['tree']; /* read_event data is now used for several features, so we are always keeping track if (!ilChangeEvent::_isActive()) { return; } */ include_once('Services/Tracking/classes/class.ilObjUserTracking.php'); $validTimeSpan = ilObjUserTracking::_getValidTimeSpan(); $query = sprintf( 'SELECT * FROM read_event ' . 'WHERE obj_id = %s ' . 'AND usr_id = %s ', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $res = $ilDB->query($query); $row = $ilDB->fetchObject($res); // read counter if ($a_ext_rc !== false) { $read_count = 'read_count = ' . $ilDB->quote($a_ext_rc, "integer") . ", "; $read_count_init = max(1, (int) $a_ext_rc); $read_count_diff = max(1, (int) $a_ext_rc) - $row->read_count; } else { $read_count = 'read_count = read_count + 1, '; $read_count_init = 1; $read_count_diff = 1; } if ($row) { if ($a_ext_time !== false) { $time = (int) $a_ext_time; } else { $time = $ilDB->quote((time() - $row->last_access) <= $validTimeSpan ? $row->spent_seconds + time() - $row->last_access : $row->spent_seconds, 'integer'); // if we are in the valid interval, we do not // add anything to the read_count, since this is the // same access for us if ((time() - $row->last_access) <= $validTimeSpan) { $read_count = ''; $read_count_init = 1; $read_count_diff = 0; } } $time_diff = $time - (int) $row->spent_seconds; // Update $query = sprintf( 'UPDATE read_event SET ' . $read_count . 'spent_seconds = %s, ' . 'last_access = %s ' . 'WHERE obj_id = %s ' . 'AND usr_id = %s ', $time, $ilDB->quote(time(), 'integer'), $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $aff = $ilDB->manipulate($query); self::_recordObjStats($obj_id, $time_diff, $read_count_diff); } else { if ($a_ext_time !== false) { $time = (int) $a_ext_time; } else { $time = 0; } $time_diff = $time - (int) $row->spent_seconds; /* $query = sprintf('INSERT INTO read_event (obj_id,usr_id,last_access,read_count,spent_seconds,first_access) '. 'VALUES (%s,%s,%s,%s,%s,'.$ilDB->now().') ', $ilDB->quote($obj_id,'integer'), $ilDB->quote($usr_id,'integer'), $ilDB->quote(time(),'integer'), $ilDB->quote($read_count_init,'integer'), $ilDB->quote($time,'integer')); $ilDB->manipulate($query); */ // #10407 $ilDB->replace( 'read_event', array( 'obj_id' => array('integer', $obj_id), 'usr_id' => array('integer', $usr_id) ), array( 'read_count' => array('integer', $read_count_init), 'spent_seconds' => array('integer', $time), 'first_access' => array('timestamp', date("Y-m-d H:i:s")), // was $ilDB->now() 'last_access' => array('integer', time()) ) ); self::$has_accessed[$obj_id][$usr_id] = true; self::_recordObjStats($obj_id, $time_diff, $read_count_diff); } if ($isCatchupWriteEvents) { ilChangeEvent::_catchupWriteEvents($obj_id, $usr_id); } // update parents (no categories or root) if (!in_array($a_type, array("cat", "root", "crs"))) { if ($tree->isInTree($a_ref_id)) { $path = $tree->getPathId($a_ref_id); foreach ($path as $p) { $obj2_id = ilObject::_lookupObjId($p); $obj2_type = ilObject::_lookupType($obj2_id); //echo "
1-$obj2_type-$p-$obj2_id-"; if (($p != $a_ref_id) && (in_array($obj2_type, array("crs", "fold", "grp", "lso")))) { $query = sprintf( 'SELECT * FROM read_event ' . 'WHERE obj_id = %s ' . 'AND usr_id = %s ', $ilDB->quote($obj2_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $res2 = $ilDB->query($query); if ($row2 = $ilDB->fetchAssoc($res2)) { //echo "
2"; // update read count and spent seconds $query = sprintf( 'UPDATE read_event SET ' . 'childs_read_count = childs_read_count + %s ,' . 'childs_spent_seconds = childs_spent_seconds + %s ' . 'WHERE obj_id = %s ' . 'AND usr_id = %s ', $ilDB->quote((int) $read_count_diff, 'integer'), $ilDB->quote((int) $time_diff, 'integer'), $ilDB->quote($obj2_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $aff = $ilDB->manipulate($query); self::_recordObjStats($obj2_id, null, null, (int) $time_diff, (int) $read_count_diff); } else { //echo "
3"; //$ilLog->write("insert read event for obj_id -".$obj2_id."-".$usr_id."-"); /* $query = sprintf('INSERT INTO read_event (obj_id,usr_id,last_access,read_count,spent_seconds,first_access,'. 'childs_read_count, childs_spent_seconds) '. 'VALUES (%s,%s,%s,%s,%s,'.$ilDB->now().', %s, %s) ', $ilDB->quote($obj2_id,'integer'), $ilDB->quote($usr_id,'integer'), $ilDB->quote(time(),'integer'), $ilDB->quote(1,'integer'), $ilDB->quote($time,'integer'), $ilDB->quote((int) $read_count_diff,'integer'), $ilDB->quote((int) $time_diff,'integer') ); $aff = $ilDB->manipulate($query); */ // #10407 $ilDB->replace( 'read_event', array( 'obj_id' => array('integer', $obj2_id), 'usr_id' => array('integer', $usr_id) ), array( 'read_count' => array('integer', 1), 'spent_seconds' => array('integer', $time), 'first_access' => array('timestamp', date("Y-m-d H:i:s")), // was $ilDB->now() 'last_access' => array('integer', time()), 'childs_read_count' => array('integer', (int) $read_count_diff), 'childs_spent_seconds' => array('integer', (int) $time_diff) ) ); self::$has_accessed[$obj2_id][$usr_id] = true; self::_recordObjStats($obj2_id, $time, 1, (int) $time_diff, (int) $read_count_diff); } } } } } // @todo: // - calculate diff of spent_seconds and read_count // - use ref id to get parents of types grp, crs, fold // - add diffs to childs_spent_seconds and childs_read_count } public static function _recordObjStats($a_obj_id, $a_spent_seconds, $a_read_count, $a_childs_spent_seconds = null, $a_child_read_count = null) { global $DIC; $ilDB = $DIC['ilDB']; if (!ilObjUserTracking::_enabledObjectStatistics() || (int) $a_obj_id <= 0) { // #12706 return; } $now = time(); $fields = array(); $fields['log_id'] = array("integer", $ilDB->nextId('obj_stat_log')); $fields["obj_id"] = array("integer", $a_obj_id); $fields["obj_type"] = array("text", ilObject::_lookupType($a_obj_id)); $fields["tstamp"] = array("timestamp", $now); $fields["yyyy"] = array("integer", date("Y")); $fields["mm"] = array("integer", date("m")); $fields["dd"] = array("integer", date("d")); $fields["hh"] = array("integer", date("H")); if ($a_spent_seconds > 0) { $fields["spent_seconds"] = array("integer", $a_spent_seconds); } if ($a_read_count > 0) { $fields["read_count"] = array("integer", $a_read_count); } if ($a_childs_spent_seconds > 0) { $fields["childs_spent_seconds"] = array("integer", $a_childs_spent_seconds); } if ($a_child_read_count > 0) { $fields["childs_read_count"] = array("integer", $a_child_read_count); } $ilDB->insert("obj_stat_log", $fields); // 0.01% probability if (mt_rand(1, 100) == 1) { self::_syncObjectStats($now); } } /** * Process object statistics log data * * @param integer $a_now * @param integer $a_minimum */ public static function _syncObjectStats($a_now = null, $a_minimum = 20000) { global $DIC; $ilDB = $DIC['ilDB']; if (!$a_now) { $a_now = time(); } set_time_limit(0); // has source table enough entries? $set = $ilDB->query("SELECT COUNT(*) AS counter FROM obj_stat_log"); $row = $ilDB->fetchAssoc($set); if ($row["counter"] >= $a_minimum) { $ilAtomQuery = $ilDB->buildAtomQuery(); $ilAtomQuery->addTableLock('obj_stat_log'); $ilAtomQuery->addTableLock('obj_stat_tmp'); $ilAtomQuery->addQueryCallable(function (ilDBInterface $ilDB) use ($a_now, $a_minimum, &$ret) { // if other process was transferring, we had to wait for the lock and // the source table should now have less than minimum/needed entries $set = $ilDB->query("SELECT COUNT(*) AS counter FROM obj_stat_log"); $row = $ilDB->fetchAssoc($set); if ($row["counter"] >= $a_minimum) { // use only "full" seconds to have a clear cut $ilDB->query("INSERT INTO obj_stat_tmp" . " SELECT * FROM obj_stat_log" . " WHERE tstamp < " . $ilDB->quote($a_now, "timestamp")); // remove transferred entries from source table $ilDB->query("DELETE FROM obj_stat_log" . " WHERE tstamp < " . $ilDB->quote($a_now, "timestamp")); $ret = true; } else { $ret = false; } }); $ilAtomQuery->run(); //continue only if obj_stat_log counter >= $a_minimum if ($ret) { $ilAtomQuery = $ilDB->buildAtomQuery(); $ilAtomQuery->addTableLock('obj_stat_tmp'); $ilAtomQuery->addTableLock('obj_stat'); $ilAtomQuery->addQueryCallable(function (ilDBInterface $ilDB) use ($a_now, $a_minimum) { // process log data (timestamp is not needed anymore) $sql = "SELECT obj_id, obj_type, yyyy, mm, dd, hh, SUM(read_count) AS read_count," . " SUM(childs_read_count) AS childs_read_count, SUM(spent_seconds) AS spent_seconds," . " SUM(childs_spent_seconds) AS childs_spent_seconds" . " FROM obj_stat_tmp" . " GROUP BY obj_id, obj_type, yyyy, mm, dd, hh"; $set = $ilDB->query($sql); while ($row = $ilDB->fetchAssoc($set)) { // "primary key" $where = array("obj_id" => array("integer", $row["obj_id"]), "obj_type" => array("text", $row["obj_type"]), "yyyy" => array("integer", $row["yyyy"]), "mm" => array("integer", $row["mm"]), "dd" => array("integer", $row["dd"]), "hh" => array("integer", $row["hh"])); $where_sql = array(); foreach ($where as $field => $def) { $where_sql[] = $field . " = " . $ilDB->quote($def[1], $def[0]); } $where_sql = implode(" AND ", $where_sql); // existing entry? $check = $ilDB->query("SELECT read_count, childs_read_count, spent_seconds," . "childs_spent_seconds" . " FROM obj_stat" . " WHERE " . $where_sql); if ($ilDB->numRows($check)) { $old = $ilDB->fetchAssoc($check); // add existing values $fields = array("read_count" => array("integer", $old["read_count"] + $row["read_count"]), "childs_read_count" => array("integer", $old["childs_read_count"] + $row["childs_read_count"]), "spent_seconds" => array("integer", $old["spent_seconds"] + $row["spent_seconds"]), "childs_spent_seconds" => array("integer", $old["childs_spent_seconds"] + $row["childs_spent_seconds"])); $ilDB->update("obj_stat", $fields, $where); } else { // new entry $fields = $where; $fields["read_count"] = array("integer", $row["read_count"]); $fields["childs_read_count"] = array("integer", $row["childs_read_count"]); $fields["spent_seconds"] = array("integer", $row["spent_seconds"]); $fields["childs_spent_seconds"] = array("integer", $row["childs_spent_seconds"]); $ilDB->insert("obj_stat", $fields); } } // clean up transfer table $ilDB->query("DELETE FROM obj_stat_tmp"); }); $ilAtomQuery->run(); } } } /** * Catches up with all write events which occured before the specified * timestamp. * * @param $obj_id int The object. * @param $usr_id int The user. * @param $timestamp SQL timestamp. */ public static function _catchupWriteEvents($obj_id, $usr_id, $timestamp = null) { global $DIC; $ilDB = $DIC['ilDB']; $query = "SELECT obj_id FROM catch_write_events " . "WHERE obj_id = " . $ilDB->quote($obj_id, 'integer') . " " . "AND usr_id = " . $ilDB->quote($usr_id, 'integer'); $res = $ilDB->query($query); if ($res->numRows()) { $ts = ($timestamp == null) ? ilUtil::now() : $timestamp; /* $query = "UPDATE catch_write_events ". "SET ts = ".($timestamp == null ? $ilDB->now() : $ilDB->quote($timestamp, 'timestamp'))." ". "WHERE usr_id = ".$ilDB->quote($usr_id ,'integer')." ". "AND obj_id = ".$ilDB->quote($obj_id ,'integer'); $res = $ilDB->manipulate($query);*/ } else { $ts = ilUtil::now(); /* $query = "INSERT INTO catch_write_events (ts,obj_id,usr_id) ". "VALUES( ". $ilDB->now().", ". $ilDB->quote($obj_id,'integer').", ". $ilDB->quote($usr_id,'integer')." ". ")"; $res = $ilDB->manipulate($query);*/ } // alex, use replace due to bug #10406 $ilDB->replace( "catch_write_events", array( "obj_id" => array("integer", $obj_id), "usr_id" => array("integer", $usr_id) ), array( "ts" => array("timestamp", $ts)) ); } /** * Catches up with all write events which occured before the specified * timestamp. * * THIS FUNCTION IS CURRENTLY NOT IN USE. BEFORE IT CAN BE USED, THE TABLE * catch_read_events MUST BE CREATED. * * * * @param $obj_id int The object. * @param $usr_id int The user. * @param $timestamp SQL timestamp. * / function _catchupReadEvents($obj_id, $usr_id, $timestamp = null) { global $DIC; $ilDB = $DIC['ilDB']; $q = "INSERT INTO catch_read_events ". "(obj_id, usr_id, action, ts) ". "VALUES (". $ilDB->quote($obj_id).",". $ilDB->quote($usr_id).",". $ilDB->quote('read').","; if ($timestamp == null) { $q .= "NOW()". ") ON DUPLICATE KEY UPDATE ts=NOW()"; } else { $q .= $ilDB->quote($timestamp). ") ON DUPLICATE KEY UPDATE ts=".$ilDB->quote($timestamp); } $r = $ilDB->query($q); } */ /** * Reads all write events which occured on the object * which happened after the last time the user caught up with them. * * @param $obj_id int The object * @param $usr_id int The user who is interested into these events. * @return array with rows from table write_event */ public static function _lookupUncaughtWriteEvents($obj_id, $usr_id) { global $DIC; $ilDB = $DIC['ilDB']; $q = "SELECT ts " . "FROM catch_write_events " . "WHERE obj_id=" . $ilDB->quote($obj_id, 'integer') . " " . "AND usr_id=" . $ilDB->quote($usr_id, 'integer'); $r = $ilDB->query($q); $catchup = null; while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) { $catchup = $row['ts']; } if ($catchup == null) { $query = sprintf( 'SELECT * FROM write_event ' . 'WHERE obj_id = %s ' . 'AND usr_id <> %s ' . 'ORDER BY ts DESC', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $res = $ilDB->query($query); } else { $query = sprintf( 'SELECT * FROM write_event ' . 'WHERE obj_id = %s ' . 'AND usr_id <> %s ' . 'AND ts >= %s ' . 'ORDER BY ts DESC', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer'), $ilDB->quote($catchup, 'timestamp') ); $res = $ilDB->query($query); } $events = array(); while ($row = $ilDB->fetchAssoc($res)) { $events[] = $row; } return $events; } /** * Returns the change state of the object for the specified user. * which happened after the last time the user caught up with them. * * @param $obj_id int The object * @param $usr_id int The user who is interested into these events. * @return 0 = object is unchanged, * 1 = object is new, * 2 = object has changed */ public static function _lookupChangeState($obj_id, $usr_id) { global $DIC; $ilDB = $DIC['ilDB']; $q = "SELECT ts " . "FROM catch_write_events " . "WHERE obj_id=" . $ilDB->quote($obj_id, 'integer') . " " . "AND usr_id=" . $ilDB->quote($usr_id, 'integer'); $r = $ilDB->query($q); $catchup = null; while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) { $catchup = $row['ts']; } if ($catchup == null) { $ilDB->setLimit(1); $query = sprintf( 'SELECT * FROM write_event ' . 'WHERE obj_id = %s ' . 'AND usr_id <> %s ', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $res = $ilDB->query($query); } else { $ilDB->setLimit(1); $query = sprintf( 'SELECT * FROM write_event ' . 'WHERE obj_id = %s ' . 'AND usr_id <> %s ' . 'AND ts > %s ', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer'), $ilDB->quote($catchup, 'timestamp') ); $res = $ilDB->query($query); } $numRows = $res->numRows(); if ($numRows > 0) { $row = $ilDB->fetchAssoc($res); // if we have write events, and user never catched one, report as new (1) // if we have write events, and user catched an old write event, report as changed (2) return ($catchup == null) ? 1 : 2; } else { return 0; // user catched all write events, report as unchanged (0) } } /** * Reads all read events which occured on the object * which happened after the last time the user caught up with them. * * NOTE: THIS FUNCTION NEEDS TO BE REWRITTEN. READ EVENTS ARE OF INTEREST * AT REF_ID's OF OBJECTS. * * @param $obj_id int The object * @param $usr_id int The user who is interested into these events. * / public static function _lookupUncaughtReadEvents($obj_id, $usr_id) { global $DIC; $ilDB = $DIC['ilDB']; $q = "SELECT ts ". "FROM catch_read_events ". "WHERE obj_id=".$ilDB->quote($obj_id)." ". "AND usr_id=".$ilDB->quote($usr_id); $r = $ilDB->query($q); $catchup = null; while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) { $catchup = $row['ts']; } $q = "SELECT * ". "FROM read_event ". "WHERE obj_id=".$ilDB->quote($obj_id)." ". ($catchup == null ? "" : "AND last_access > ".$ilDB->quote($catchup))." ". ($catchup == null ? "" : "AND last_access > ".$ilDB->quote($catchup))." ". "ORDER BY last_access DESC"; $r = $ilDB->query($q); $events = array(); while ($row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) { $events[] = $row; } return $events; }*/ /** * Reads all read events which occured on the object. * * @param $obj_id int The object * @param $usr_id int Optional, the user who performed these events. */ public static function _lookupReadEvents($obj_id, $usr_id = null) { global $DIC; $ilDB = $DIC['ilDB']; if ($usr_id == null) { $query = sprintf( 'SELECT * FROM read_event ' . 'WHERE obj_id = %s ' . 'ORDER BY last_access DESC', $ilDB->quote($obj_id, 'integer') ); $res = $ilDB->query($query); } else { $query = sprintf( 'SELECT * FROM read_event ' . 'WHERE obj_id = %s ' . 'AND usr_id = %s ' . 'ORDER BY last_access DESC', $ilDB->quote($obj_id, 'integer'), $ilDB->quote($usr_id, 'integer') ); $res = $ilDB->query($query); } $counter = 0; while ($row = $ilDB->fetchAssoc($res)) { $events[$counter]['obj_id'] = $row['obj_id']; $events[$counter]['usr_id'] = $row['usr_id']; $events[$counter]['last_access'] = $row['last_access']; $events[$counter]['read_count'] = $row['read_count']; $events[$counter]['spent_seconds'] = $row['spent_seconds']; $events[$counter]['first_access'] = $row['first_access']; $counter++; } return $events ? $events : array(); } /** * Lookup users in progress * * @return * @static */ public static function lookupUsersInProgress($a_obj_id) { global $DIC; $ilDB = $DIC['ilDB']; $query = sprintf( 'SELECT DISTINCT(usr_id) usr FROM read_event ' . 'WHERE obj_id = %s ', $ilDB->quote($a_obj_id, 'integer') ); $res = $ilDB->query($query); while ($row = $ilDB->fetchObject($res)) { $users[] = $row->usr; } return $users ? $users : array(); } /** * Has accessed */ public static function hasAccessed($a_obj_id, $a_usr_id) { global $DIC; $ilDB = $DIC['ilDB']; if (isset(self::$has_accessed[$a_obj_id][$a_usr_id])) { return self::$has_accessed[$a_obj_id][$a_usr_id]; } $set = $ilDB->query( "SELECT usr_id FROM read_event WHERE " . "obj_id = " . $ilDB->quote($a_obj_id, "integer") . " AND " . "usr_id = " . $ilDB->quote($a_usr_id, "integer") ); if ($rec = $ilDB->fetchAssoc($set)) { return self::$has_accessed[$a_obj_id][$a_usr_id] = true; } return self::$has_accessed[$a_obj_id][$a_usr_id] = false; } /** * Activates change event tracking. * * @return mixed true on success, a string with an error message on failure. */ public static function _activate() { if (ilChangeEvent::_isActive()) { return 'change event tracking is already active'; } else { global $DIC; $ilDB = $DIC['ilDB']; // Insert initial data into table write_event // We need to do this here, because we need // to catch up write events that occured while the change event tracking was // deactivated. // IGNORE isn't supported in oracle $set = $ilDB->query(sprintf( 'SELECT r1.obj_id,r2.obj_id p,d.owner,%s,d.create_date ' . 'FROM object_data d ' . 'LEFT JOIN write_event w ON d.obj_id = w.obj_id ' . 'JOIN object_reference r1 ON d.obj_id=r1.obj_id ' . 'JOIN tree t ON t.child=r1.ref_id ' . 'JOIN object_reference r2 on r2.ref_id=t.parent ' . 'WHERE w.obj_id IS NULL', $ilDB->quote('create', 'text') )); while ($rec = $ilDB->fetchAssoc($set)) { $nid = $ilDB->nextId("write_event"); $query = 'INSERT INTO write_event ' . '(write_id, obj_id,parent_obj_id,usr_id,action,ts) VALUES (' . $ilDB->quote($nid, "integer") . "," . $ilDB->quote($rec["obj_id"], "integer") . "," . $ilDB->quote($rec["p"], "integer") . "," . $ilDB->quote($rec["owner"], "integer") . "," . $ilDB->quote("create", "text") . "," . $ilDB->quote($rec["create_date"], "timestamp") . ')'; $res = $ilDB->query($query); } global $DIC; $ilSetting = $DIC['ilSetting']; $ilSetting->set('enable_change_event_tracking', '1'); return $res; } } /** * Deactivates change event tracking. * * @return mixed true on success, a string with an error message on failure. */ public static function _deactivate() { global $DIC; $ilSetting = $DIC['ilSetting']; $ilSetting->set('enable_change_event_tracking', '0'); } /** * Returns true, if change event tracking is active. * * @return mixed true on success, a string with an error message on failure. */ public static function _isActive() { global $DIC; $ilSetting = $DIC['ilSetting']; return $ilSetting->get('enable_change_event_tracking', '0') == '1'; } /** * Delete object entries * * @return * @static */ public static function _delete($a_obj_id) { global $DIC; $ilDB = $DIC['ilDB']; $query = sprintf( 'DELETE FROM write_event WHERE obj_id = %s ', $ilDB->quote($a_obj_id, 'integer') ); $aff = $ilDB->manipulate($query); $query = sprintf( 'DELETE FROM read_event WHERE obj_id = %s ', $ilDB->quote($a_obj_id, 'integer') ); $aff = $ilDB->manipulate($query); return true; } public static function _deleteReadEvents($a_obj_id) { global $DIC; $ilDB = $DIC['ilDB']; $ilDB->manipulate("DELETE FROM read_event" . " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer")); } public static function _deleteReadEventsForUsers($a_obj_id, array $a_user_ids) { global $DIC; $ilDB = $DIC['ilDB']; $ilDB->manipulate("DELETE FROM read_event" . " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer") . " AND " . $ilDB->in("usr_id", $a_user_ids, "", "integer")); } public static function _getAllUserIds($a_obj_id) { global $DIC; $ilDB = $DIC['ilDB']; $res = array(); $set = $ilDB->query("SELECT usr_id FROM read_event" . " WHERE obj_id = " . $ilDB->quote($a_obj_id, "integer")); while ($row = $ilDB->fetchAssoc($set)) { $res[] = $row["usr_id"]; } return $res; } /** * _updateAccessForScormOfflinePlayer * needed to synchronize last_access and first_access when learning modul is used offline * called in ./Modules/ScormAicc/classes/class.ilSCORMOfflineMode.php * @return true */ public static function _updateAccessForScormOfflinePlayer($obj_id, $usr_id, $i_last_access, $t_first_access) { global $DIC; $ilDB = $DIC['ilDB']; $res = $ilDB->queryF( 'UPDATE read_event SET first_access=%s, last_access = %s WHERE obj_id=%s AND usr_id=%s', array('timestamp','integer','integer','integer'), array($t_first_access,$i_last_access,$obj_id,$usr_id) ); return $res; } }