481 lines
22 KiB
PHP
481 lines
22 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Log activity in the system (e.g user deleted a user)
|
|
*
|
|
* @param string $note Notes/comments regarding this activity
|
|
* @param string $log_code
|
|
* @param string $value_new
|
|
* @param string $remote_table
|
|
* @param string $remote_column
|
|
* @param string $remote_ref
|
|
* @param string $ref_column_override
|
|
* @param string $value_old
|
|
* @param string $user User that ran the activity
|
|
* @param boolean $generate_diff
|
|
*
|
|
* @return void
|
|
*/
|
|
function log_activity($note = null, $log_code = LOG_CODE_UNSPECIFIED, $value_new = null, $remote_table = null, $remote_column = null, $remote_ref = null, $ref_column_override = null, $value_old = null, $user = null, $generate_diff = false)
|
|
{
|
|
|
|
if (is_null($log_code)) {
|
|
$log_code = LOG_CODE_UNSPECIFIED;
|
|
}
|
|
|
|
if (!function_exists('log_diff')) {
|
|
include_once __DIR__ . '/resource_functions.php';
|
|
}
|
|
|
|
if (is_null($user)) {
|
|
global $userref;
|
|
$user = isset($userref) && !is_null($userref) ? (int) $userref : 0;
|
|
}
|
|
|
|
if (is_null($value_old) && !is_null($remote_table) && !is_null($remote_column) && !is_null($remote_ref)) { // only try and get the old value if not explicitly set and we have table details
|
|
$row = ps_query("SELECT " . columns_in($remote_table) . " FROM `{$remote_table}` WHERE `" . (is_null($ref_column_override) ? 'ref' : $ref_column_override) . "` = ?", array("i",$remote_ref));
|
|
if (isset($row[0][$remote_column])) {
|
|
$value_old = $row[0][$remote_column];
|
|
$log_code = $log_code == LOG_CODE_UNSPECIFIED ? LOG_CODE_EDITED : $log_code;
|
|
} else {
|
|
$log_code = $log_code == LOG_CODE_UNSPECIFIED ? LOG_CODE_CREATED : $log_code;
|
|
}
|
|
}
|
|
|
|
if ($value_old == $value_new && ($log_code == LOG_CODE_EDITED || $log_code == LOG_CODE_COPIED)) { // return if the value has not changed
|
|
return;
|
|
}
|
|
|
|
$parameters = array("i",$user,"s",(!LOG_CODE_validate($log_code) ? LOG_CODE_UNSPECIFIED : $log_code));
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($note) ? null : $note);
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($value_old) ? null : $value_old);
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($value_new) ? null : $value_new);
|
|
$parameters[] = "s";
|
|
$parameters[] = (!is_null($value_old) && !is_null($value_new) && $generate_diff ? log_diff($value_old, $value_new) : '');
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($remote_table) ? null : $remote_table);
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($remote_column) ? null : $remote_column);
|
|
$parameters[] = "s";
|
|
$parameters[] = (is_null($remote_ref) ? null : mb_strcut($remote_ref, 0, 100));
|
|
|
|
ps_query("INSERT INTO `activity_log` (`logged`,`user`,`log_code`,`note`,`value_old`,`value_new`,`value_diff`,`remote_table`,`remote_column`,`remote_ref`)
|
|
VALUES (NOW()," . ps_param_insert(count($parameters) / 2) . ")", $parameters);
|
|
}
|
|
|
|
/**
|
|
* Log script messages on screen and optionally in a file. If debug_log is enabled, it will also write the message in the
|
|
* debug log file.
|
|
*
|
|
* @uses debug()
|
|
*
|
|
* @param string $message
|
|
* @param resource $file
|
|
*
|
|
* @return void
|
|
*/
|
|
function logScript($message, $file = null)
|
|
{
|
|
$date_time = date('Y-m-d H:i:s');
|
|
|
|
if (PHP_SAPI == 'cli') {
|
|
echo "{$date_time} {$message}" . PHP_EOL;
|
|
}
|
|
|
|
// Log in debug as well, with extended information to show the backtrace
|
|
global $debug_extended_info;
|
|
$orig_debug_extended_info = $debug_extended_info;
|
|
$debug_extended_info = true;
|
|
debug($message);
|
|
$debug_extended_info = $orig_debug_extended_info;
|
|
|
|
// If a file resource has been passed, then write to that file as well
|
|
if (!is_null($file) && (is_resource($file) && 'file' == get_resource_type($file) || 'stream' == get_resource_type($file))) {
|
|
fwrite($file, "{$date_time} {$message}" . PHP_EOL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve entries from resource log based on date or references
|
|
*
|
|
* @param integer $minref (Optional) Minimum ref of resource log entry to return (default 0)
|
|
* @param integer $days (Optional) Number of days to return. e.g 3 = all results for today, yesterday and the day before. Default = 7 (ignored if minref supplied)
|
|
* @param integer $maxrecords (Optional) Maximum number of records to return. Default = all rows (0)
|
|
* @param array $fields (Optional) Limit results to particular metadata field(s)
|
|
* @param array $log_codes (Optional) Limit results to particular log code(s)
|
|
*
|
|
* @return array
|
|
*/
|
|
function resource_log_last_rows($minref = 0, $days = 7, $maxrecords = 0, array $fields = [], array $log_codes = [])
|
|
{
|
|
if (!checkperm('v')) {
|
|
return array();
|
|
}
|
|
|
|
$parameters = array();
|
|
$sql = "SELECT date, ref, resource, type, resource_type_field AS field, user, notes, diff, usageoption FROM resource_log WHERE type != 'l'";
|
|
if ($minref > 0) {
|
|
$sql .= " AND ref >= ?";
|
|
$parameters[] = "i";
|
|
$parameters[] = (int)$minref;
|
|
} else {
|
|
$sql .= " AND datediff(now(),date) < ?";
|
|
$parameters[] = "i";
|
|
$parameters[] = (int)$days;
|
|
}
|
|
|
|
$fields = array_filter($fields, 'is_positive_int_loose');
|
|
if ($fields !== []) {
|
|
$sql .= sprintf(' AND resource_type_field IN (%s)', ps_param_insert(count($fields)));
|
|
$parameters = array_merge($parameters, ps_param_fill($fields, 'i'));
|
|
}
|
|
|
|
$log_codes = array_filter($log_codes, 'LOG_CODE_validate');
|
|
if ($log_codes !== []) {
|
|
$sql .= sprintf(' AND BINARY `type` IN (%s)', ps_param_insert(count($log_codes)));
|
|
$parameters = array_merge($parameters, ps_param_fill($log_codes, 's'));
|
|
}
|
|
|
|
if ($maxrecords > 0) {
|
|
$sql .= " LIMIT " . (int)$maxrecords;
|
|
}
|
|
|
|
return ps_query($sql, $parameters);
|
|
}
|
|
|
|
/**
|
|
* Get activity log entries from log tables (e.g activity_log, resource_log and collection_log)
|
|
*
|
|
* @uses ps_query()
|
|
*
|
|
* @param string $search Search text to filter down results using fuzzy searching
|
|
* @param integer $offset Specifies the offset of the first row to return
|
|
* @param integer $rows Specifies the maximum number of rows to return
|
|
* @param array $where_statements Where statements for log tables
|
|
* Example of where statements:
|
|
* $where_statements = array(
|
|
* 'activity_log' => "`activity_log`.`user`='{$actasuser}' AND ",
|
|
* 'resource_log' => "`resource_log`.`user`='{$actasuser}' AND ",
|
|
* 'collection_log' => "`collection_log`.`user`='{$actasuser}' AND ",
|
|
* );
|
|
* @param string $table Table name (e.g resource_type_field, user, resource)
|
|
* @param integer $table_reference ID of the record in the referred table
|
|
* @param boolean $count Switch for if the result should be a single count or the result set
|
|
*
|
|
* @return array
|
|
*/
|
|
function get_activity_log($search, $offset, $rows, array $where_statements, $table, $table_reference, $count = false)
|
|
{
|
|
$where_activity_log_statement = $where_statements['activity_log'];
|
|
$where_resource_log_statement = $where_statements['resource_log'];
|
|
$where_collection_log_statement = $where_statements['collection_log'];
|
|
|
|
$log_codes = array_values(LOG_CODE_get_all());
|
|
$col_when_statements = $res_when_statements = "";
|
|
$col_when_parameters = $res_when_parameters = [];
|
|
$res_log_codes_processed = [];
|
|
|
|
foreach ($log_codes as $log_code) {
|
|
$log_code_description = "";
|
|
|
|
if (!isset($GLOBALS['lang']["log_code_{$log_code}"]) || in_array($log_code, $res_log_codes_processed)) {
|
|
if (!isset($GLOBALS['lang']["collectionlog-{$log_code}"])) {
|
|
continue;
|
|
}
|
|
|
|
$log_code_description = $GLOBALS['lang']["collectionlog-{$log_code}"];
|
|
$col_when_statements .= " WHEN BINARY(?) THEN ?";
|
|
$col_when_parameters = array_merge($col_when_parameters, ['s', $log_code, 's', $log_code_description]);
|
|
|
|
continue;
|
|
}
|
|
|
|
$log_code_description = $GLOBALS['lang']["log_code_{$log_code}"];
|
|
|
|
$res_when_statements .= " WHEN BINARY(?) THEN ?";
|
|
$res_when_parameters = array_merge($res_when_parameters, ['s', $log_code, 's', $log_code_description]);
|
|
|
|
$res_log_codes_processed[] = $log_code;
|
|
}
|
|
|
|
$count_statement_start = "";
|
|
$count_statement_end = "";
|
|
|
|
$sql_query = new PreparedStatementQuery();
|
|
|
|
$sql_query->sql = "
|
|
SELECT
|
|
`activity_log`.`logged` AS 'datetime',
|
|
`user`.`username` AS 'user',
|
|
CASE BINARY(`activity_log`.`log_code`) " . $res_when_statements . $col_when_statements . " ELSE `activity_log`.`log_code` END AS 'operation',
|
|
`activity_log`.`note` AS 'notes',
|
|
NULL AS 'resource_field',
|
|
`activity_log`.`value_old` AS 'old_value',
|
|
`activity_log`.`value_new` AS 'new_value',
|
|
if(`activity_log`.`value_diff`='','',concat('<pre>',`activity_log`.`value_diff`,'</pre>')) AS 'difference',
|
|
'' AS 'access_key',
|
|
`activity_log`.`remote_table`AS 'table',
|
|
`activity_log`.`remote_column` AS 'column',
|
|
`activity_log`.`remote_ref` AS 'table_reference'
|
|
FROM `activity_log`
|
|
LEFT OUTER JOIN `user` ON `activity_log`.`user`=`user`.`ref`
|
|
WHERE
|
|
{$where_activity_log_statement}";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, array_merge($res_when_parameters, $col_when_parameters));
|
|
|
|
$search_block =
|
|
"`activity_log`.`ref` LIKE ?
|
|
OR `activity_log`.`logged` LIKE ?
|
|
OR `user`.`username` LIKE ?
|
|
OR `activity_log`.`note` LIKE ?
|
|
OR `activity_log`.`value_old` LIKE ?
|
|
OR `activity_log`.`value_new` LIKE ?
|
|
OR `activity_log`.`value_diff` LIKE ?
|
|
OR `activity_log`.`remote_table` LIKE ?
|
|
OR `activity_log`.`remote_column` LIKE ?
|
|
OR `activity_log`.`remote_ref` LIKE ?
|
|
OR ? LIKE (CASE BINARY(`activity_log`.`log_code`)";
|
|
|
|
|
|
$sql_query->sql .= "(" . $search_block . " " . $res_when_statements . $col_when_statements . " ELSE `activity_log`.`log_code` END) )";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, ps_fill_param_array($search_block, "%{$search}%", 's'), array_merge($res_when_parameters, $col_when_parameters));
|
|
|
|
$sql_query->sql .=
|
|
"UNION
|
|
|
|
SELECT
|
|
`resource_log`.`date` AS 'datetime',
|
|
`user`.`username` AS 'user',
|
|
CASE BINARY(`resource_log`.`type`) {$res_when_statements} ELSE `resource_log`.`type` END AS 'operation',
|
|
`resource_log`.`notes` AS 'notes',
|
|
`resource_type_field`.`title` AS 'resource_field',
|
|
`resource_log`.`previous_value` AS 'old_value',
|
|
'' AS 'new_value',
|
|
if(`resource_log`.`diff`='','',concat('<pre>',`resource_log`.`diff`,'</pre>')) AS 'difference',
|
|
`resource_log`.`access_key` AS 'access_key',
|
|
'resource' AS 'table',
|
|
'ref' AS 'column',
|
|
`resource_log`.`resource` AS 'table_reference'
|
|
FROM `resource_log`
|
|
LEFT OUTER JOIN `user` ON `resource_log`.`user`=`user`.`ref`
|
|
LEFT OUTER JOIN `resource_type_field` ON `resource_log`.`resource_type_field`=`resource_type_field`.`ref`
|
|
WHERE
|
|
{$where_resource_log_statement}";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, $res_when_parameters);
|
|
|
|
$search_block =
|
|
"`resource_log`.`ref` LIKE ?
|
|
OR `resource_log`.`date` LIKE ?
|
|
OR `user`.`username` LIKE ?
|
|
OR `resource_log`.`notes` LIKE ?
|
|
OR `resource_log`.`previous_value` LIKE ?
|
|
OR 'resource' LIKE ?
|
|
OR 'ref' LIKE ?
|
|
OR `resource_log`.`resource` LIKE ?
|
|
OR ? LIKE (CASE BINARY(`resource_log`.`type`)";
|
|
|
|
$sql_query->sql .= "(" . $search_block . " {$res_when_statements} ELSE `resource_log`.`type` END) )";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, ps_fill_param_array($search_block, "%{$search}%", 's'), $res_when_parameters);
|
|
|
|
|
|
$sql_query->sql .=
|
|
"UNION
|
|
|
|
SELECT
|
|
`collection_log`.`date` AS 'datetime',
|
|
`user`.`username` AS 'user',
|
|
CASE BINARY(`collection_log`.`type`) {$col_when_statements} ELSE `collection_log`.`type` END AS 'operation',
|
|
`collection_log`.`notes` AS 'notes',
|
|
NULL AS 'resource_field',
|
|
'' AS 'old_value',
|
|
'' AS 'new_value',
|
|
'' AS 'difference',
|
|
'' AS 'access_key',
|
|
if(`collection_log`.`resource` IS NULL,'collection','resource') AS 'table',
|
|
'ref' AS 'column',
|
|
if(`collection_log`.`resource` IS NULL,`collection_log`.`collection`,`collection_log`.`resource`) AS 'table_reference'
|
|
FROM `collection_log`
|
|
LEFT OUTER JOIN `user` ON `collection_log`.`user`=`user`.`ref`
|
|
LEFT OUTER JOIN `collection` ON `collection_log`.`collection`=`collection`.`ref`
|
|
WHERE
|
|
{$where_collection_log_statement}";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, $col_when_parameters);
|
|
|
|
$search_block =
|
|
"`collection_log`.`collection` LIKE ?
|
|
OR `collection_log`.`date` LIKE ?
|
|
OR `collection_log`.`notes` LIKE ?
|
|
OR `collection_log`.`resource` LIKE ?
|
|
OR `collection`.`name` LIKE ?
|
|
OR `user`.`username` LIKE ?
|
|
OR ? LIKE (CASE BINARY(`collection_log`.`type`)";
|
|
|
|
$sql_query->sql .= "(" . $search_block . " {$col_when_statements} ELSE `collection_log`.`type` END) )";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, ps_fill_param_array($search_block, "%{$search}%", 's'), $col_when_parameters);
|
|
|
|
$sql_query->sql .= "ORDER BY `datetime` DESC";
|
|
|
|
# Wrap the query as a subquery within a table selection if necessary
|
|
if (trim($table) !== '') {
|
|
$sql_query->sql = "SELECT * FROM ({$sql_query->sql}) AS `logs` WHERE `logs`.`table` = ? ";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, ['s', $table]);
|
|
|
|
if (is_numeric($table_reference) && $table_reference > 0) {
|
|
$sql_query->sql .= "AND `logs`.`table_reference` = ?";
|
|
$sql_query->parameters = array_merge($sql_query->parameters, ['i', $table_reference]);
|
|
}
|
|
}
|
|
|
|
$limit = sql_limit($offset, $rows);
|
|
|
|
if ($count) {
|
|
$count_statement_start = "SELECT COUNT(*) AS value FROM (";
|
|
$count_statement_end = ") AS count_select";
|
|
$sql_query->sql = $count_statement_start . $sql_query->sql . $count_statement_end;
|
|
return ps_value($sql_query->sql, $sql_query->parameters, 0);
|
|
} else {
|
|
$sql_query->sql .= " " . $limit;
|
|
return ps_query($sql_query->sql, $sql_query->parameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Use resource log to obtain a count of resources downloaded by the specified user in the last X days
|
|
*
|
|
* @param integer $userref User reference
|
|
* @param integer $user_dl_days The number of days to check the resource log for
|
|
*
|
|
* @return integer download count
|
|
*/
|
|
function get_user_downloads($userref, $user_dl_days)
|
|
{
|
|
$daylimit = (int)$user_dl_days != 0 ? (int)$user_dl_days : 99999;
|
|
$parameters = array("i",(int)$userref, "i",$daylimit * 60 * 60 * 24);
|
|
|
|
return ps_value("SELECT COUNT(DISTINCT resource) value
|
|
FROM resource_log rl
|
|
WHERE rl.type = 'd'
|
|
AND rl.user = ?
|
|
AND TIMESTAMPDIFF(SECOND, date, now()) <= ?", $parameters, 0);
|
|
}
|
|
|
|
/**
|
|
* Add detail of node changes to resource log
|
|
*
|
|
* Note that this function originally required only the added and removed nodes to be passed.
|
|
* This was prior to node reversion changes, which requires the logging of all the existing resource nodes
|
|
*
|
|
* @param integer $resource Resource ID
|
|
* @param array $nodes_new Array of new node IDs
|
|
* @param array $nodes_current Array of old node IDs
|
|
* @param string $lognote Optional note to add to log entry
|
|
* @param array $nodes_renamed Optional array of old node names with node id as key e.g. [345 => 'oldname',678 => "pastname"]
|
|
*
|
|
* @return boolean Success/failure
|
|
*/
|
|
function log_node_changes($resource, $nodes_new, $nodes_current, $lognote = "", $nodes_renamed = [])
|
|
{
|
|
if ((string)(int)$resource !== (string)$resource) {
|
|
return false;
|
|
}
|
|
// Find treefields - required so that old value will be logged with full path
|
|
$treefields = array_column(get_resource_type_fields("", "ref", "asc", "", [FIELD_TYPE_CATEGORY_TREE]), "ref");
|
|
$nodefieldchanges = array();
|
|
if (count($nodes_current) != count($nodes_new) || array_diff($nodes_new, $nodes_current) != array_diff($nodes_current, $nodes_new)) {
|
|
foreach ($nodes_current as $node) {
|
|
$nodedata = array();
|
|
if (get_node($node, $nodedata, false)) {
|
|
if (in_array($nodedata["resource_type_field"], $treefields) && $nodedata["parent"] > 0) {
|
|
$parents = get_node_strings(get_parent_nodes($nodedata["ref"], true, true), false, false);
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][0][] = reset($parents);
|
|
} else {
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][0][] = $nodedata["name"];
|
|
}
|
|
}
|
|
}
|
|
foreach ($nodes_new as $node) {
|
|
$nodedata = array();
|
|
if (get_node($node, $nodedata, false)) {
|
|
if (in_array($nodedata["resource_type_field"], $treefields) && $nodedata["parent"] > 0) {
|
|
$parentnodes = get_parent_nodes($nodedata["ref"], true, true);
|
|
$parents = get_node_strings($parentnodes, false, false);
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][1][] = reset($parents);
|
|
} else {
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][1][] = $nodedata["name"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach ($nodes_renamed as $nodeid => $oldname) {
|
|
if (!in_array($nodeid, $nodes_new)) {
|
|
// $nodes_renamed contains a node that's not being used.
|
|
// This could be after changing from unique node to one used elsewhere.
|
|
continue;
|
|
}
|
|
$nodedata = array();
|
|
if (get_node($nodeid, $nodedata, false)) { // Don't use cache - always get the latest node name when writing to the log
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][0][] = $oldname;
|
|
$nodefieldchanges[$nodedata["resource_type_field"]][1][] = $nodedata["name"];
|
|
}
|
|
}
|
|
|
|
foreach ($nodefieldchanges as $key => $value) {
|
|
if (isset($value[0]) && isset($value[1]) && array_diff($value[0], $value[1]) == array_diff($value[1], $value[0])) {
|
|
// No difference
|
|
continue;
|
|
}
|
|
// Log changes to each field separately
|
|
$fromvalue = isset($value[0]) ? implode(NODE_NAME_STRING_SEPARATOR, $value[0]) : "";
|
|
$tovalue = isset($value[1]) ? implode(NODE_NAME_STRING_SEPARATOR, $value[1]) : "";
|
|
resource_log($resource, LOG_CODE_EDITED, $key, $lognote, $fromvalue, $tovalue);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Log search events
|
|
*
|
|
* @param string $search Actual search string {@see do_search()}
|
|
* @param array $resource_types Resource types filter
|
|
* @param array $archive_states Archive states filter
|
|
* @param integer $result_count Search result count
|
|
*
|
|
* @return void
|
|
*/
|
|
function log_search_event(string $search, array $resource_types, array $archive_states, int $result_count)
|
|
{
|
|
global $userref;
|
|
|
|
$resource_types = array_filter($resource_types, 'is_int_loose');
|
|
$archive_states = array_filter($archive_states, 'is_int_loose');
|
|
|
|
$parameters = array();
|
|
$parameters[] = "s";
|
|
$parameters[] = ($search === '' || !is_string_loose($search) ? null : $search);
|
|
$parameters[] = "s";
|
|
$parameters[] = (empty($resource_types) ? null : implode(', ', $resource_types));
|
|
$parameters[] = "s";
|
|
$parameters[] = (empty($archive_states) ? null : implode(', ', $archive_states));
|
|
$parameters[] = "i";
|
|
$parameters[] = (is_null($userref) ? null : (int)$userref);
|
|
$parameters[] = "i";
|
|
$parameters[] = (is_int_loose($result_count) ? (int)$result_count : 0);
|
|
|
|
$q = "INSERT INTO search_log (search_string, resource_types, archive_states, `user`, result_count) VALUES (?,?,?,?,?)";
|
|
return ps_query($q, $parameters);
|
|
}
|
|
|
|
/**
|
|
* Generate a fingerprint which could then be used as a trace ID for event correlation purposes.
|
|
*
|
|
* @param array $components Data making up our fingerprint.
|
|
*/
|
|
function generate_trace_id(array $components): string
|
|
{
|
|
return md5(implode('--', $components));
|
|
}
|