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('
',`activity_log`.`value_diff`,'')) 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('
',`resource_log`.`diff`,'')) 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)); }