Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

mod/system_log/lib/system_log.php (1 issue)

1
<?php
2
/**
3
 * Elgg system log.
4
 * Listens to events and writes crud events into the system log database.
5
 *
6
 * @package    Elgg.Core
7
 * @subpackage Logging
8
 */
9
10
/**
11
 * Retrieve the system log based on a number of parameters.
12
 *
13
 * @param array $options Options
14
 *
15
 * @option int       $limit             Maximum number of responses to return. (default from settings)
16
 * @option int       $offset            Offset of where to start.
17
 * @option bool      $count             Return count or not
18
 * @option int|array $performed_by_guid The guid(s) of the user(s) who initiated the event.
19
 * @option string    $event             The event you are searching on.
20
 * @option string    $object_class      The class of object it effects.
21
 * @option string    $object_type       The type
22
 * @option string    $object_subtype    The subtype.
23
 * @option int       $object_id         GUID of an object
24
 * @option int       $created_before    Lower time limit
25
 * @option int       $created_after     Upper time limit
26
 * @option string    $ip_address        The IP address.
27
 *
28
 * @return int|stdClass[]
29
 */
30
function system_log_get_log($options = null) {
31
32 2
	if (!is_array($options)) {
33 2
		elgg_deprecated_notice(__FUNCTION__ . ' accepts a single argument as an array of options', '3.0');
34
35 2
		$options = [];
36
37 2
		$arguments = func_get_args();
38 2
		$arguments = array_pad($arguments, 12, null);
39
40 2
		$options['performed_by_guid'] = $arguments[0];
41 2
		$options['event'] = $arguments[1];
42 2
		$options['object_class'] = $arguments[2];
43 2
		$options['object_type'] = $arguments[3];
44 2
		$options['object_subtype'] = $arguments[4];
45 2
		$options['limit'] = $arguments[5];
46 2
		$options['offset'] = $arguments[6];
47 2
		$options['count'] = $arguments[7];
48 2
		$options['created_before'] = $arguments[8];
49 2
		$options['created_after'] = $arguments[9];
50 2
		$options['object_id'] = $arguments[10];
51 2
		$options['ip_address'] = $arguments[11];
52
	}
53
54 2
	$query = new \Elgg\SystemLog\SystemLogQuery();
55 2
	foreach ($options as $key => $value) {
56 2
		$query->$key = $value;
57
	}
58
59 2
	return $query->execute();
60
}
61
62
/**
63
 * Return a specific log entry.
64
 *
65
 * @param int $entry_id The log entry
66
 *
67
 * @return stdClass|false
68
 */
69
function system_log_get_log_entry($entry_id) {
70 1
	$entry_id = (int) $entry_id;
71
72 1
	$qb = \Elgg\Database\Select::fromTable('system_log');
73 1
	$qb->select('*');
74 1
	$qb->where($qb->compare('id', '=', $entry_id, ELGG_VALUE_INTEGER));
75
76 1
	return _elgg_services()->db->getDataRow($qb);
77
}
78
79
/**
80
 * Return the object referred to by a given log entry
81
 *
82
 * @param \stdClass|int $entry The log entry row or its ID
83
 *
84
 * @return mixed
85
 */
86
function system_log_get_object_from_log_entry($entry) {
87 1
	if (is_numeric($entry)) {
88 1
		$entry = system_log_get_log_entry($entry);
89 1
		if (!$entry) {
90
			return false;
91
		}
92
	}
93
94 1
	$class = $entry->object_class;
95 1
	$id = $entry->object_id;
96
97 1
	if (!class_exists($class)) {
98
		// failed autoload
99
		return false;
100
	}
101
102
	$getters = [
103 1
		ElggAnnotation::class => 'elgg_get_annotation_from_id',
104
		ElggMetadata::class => 'elgg_get_metadata_from_id',
105
		ElggRelationship::class => 'get_relationship',
106
	];
107
108 1
	if (isset($getters[$class]) && is_callable($getters[$class])) {
109
		$object = call_user_func($getters[$class], $id);
110 1
	} else if (preg_match('~^Elgg[A-Z]~', $class)) {
111 1
		$object = get_entity($id);
112
	} else {
113
		// surround with try/catch because object could be disabled
114
		try {
115
			$object = new $class($entry->object_id);
116
117
			return $object;
118
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
119
		}
120
	}
121
122 1
	if (!is_object($object) || get_class($object) !== $class) {
123
		return false;
124
	}
125
126 1
	return $object;
127
}
128
129
/**
130
 * Log a system event related to a specific object.
131
 *
132
 * This is called by the event system and should not be called directly.
133
 *
134
 * @param object $object The object you're talking about.
135
 * @param string $event  The event being logged
136
 *
137
 * @return void
138
 */
139
function system_log($object, $event) {
140 429
	$insert = new \Elgg\SystemLog\SystemLogInsert();
141 429
	return $insert->insert($object, $event);
142
}
143
144
/**
145
 * This function creates an archive copy of the system log.
146
 *
147
 * @param int $offset An offset in seconds from now to archive (useful for log rotation)
148
 *
149
 * @return bool
150
 */
151
function system_log_archive_log($offset = 0) {
152 1
	$offset = (int) $offset;
153 1
	$now = time(); // Take a snapshot of now
154 1
	$prefix = _elgg_config()->dbprefix;
155
156 1
	$ts = $now - $offset;
157
158
	// create table
159
	$query = "
160 1
		CREATE TABLE {$prefix}system_log_$now as
161 1
			SELECT * FROM {$prefix}system_log 
162 1
			WHERE time_created < $ts
163
	";
164
165 1
	if (!update_data($query)) {
166
		return false;
167
	}
168
169
	// delete
170
	// Don't delete on time since we are running in a concurrent environment
171 1
	if (delete_data("DELETE from {$prefix}system_log WHERE time_created < $ts") === false) {
172
		return false;
173
	}
174
175
	// alter table to engine
176 1
	if (!update_data("ALTER TABLE {$prefix}system_log_$now engine=archive")) {
177
		return false;
178
	}
179
180 1
	return true;
181
}
182
183
/**
184
 * Convert an interval to seconds
185
 *
186
 * @param string $period interval
187
 *
188
 * @return int
189
 */
190
function system_log_get_seconds_in_period($period) {
191
	$seconds_in_day = 86400;
192
	switch ($period) {
193
		case 'weekly':
194
			$offset = $seconds_in_day * 7;
195
			break;
196
		case 'yearly':
197
			$offset = $seconds_in_day * 365;
198
			break;
199
		case 'monthly':
200
		default:
201
			// assume 28 days even if a month is longer. Won't cause data loss.
202
			$offset = $seconds_in_day * 28;
203
	}
204
205
	return $offset;
206
}
207
208
/**
209
 * This function deletes archived copies of the system logs that are older than specified
210
 *
211
 * @param int $time_of_delete An offset in seconds from now to delete log tables
212
 *
213
 * @return bool
214
 */
215
function system_log_browser_delete_log($time_of_delete) {
216 1
	$dbprefix = elgg_get_config('dbprefix');
217 1
	$cutoff = time() - (int) $time_of_delete;
218
219 1
	$deleted_tables = false;
220 1
	$results = get_data("SHOW TABLES like '{$dbprefix}system_log_%'");
221 1
	if ($results) {
222 1
		foreach ($results as $result) {
223 1
			$data = (array) $result;
224 1
			$table_name = array_shift($data);
225
			// extract log table rotation time
226 1
			$log_time = str_replace("{$dbprefix}system_log_", '', $table_name);
227 1
			if ($log_time < $cutoff) {
228
				if (delete_data("DROP TABLE $table_name") !== false) {
229
					// delete_data returns 0 when dropping a table (false for failure)
230
					$deleted_tables = true;
231
				} else {
232 1
					elgg_log("Failed to delete the log table $table_name", 'ERROR');
233
				}
234
			}
235
		}
236
	}
237
238
	return $deleted_tables;
239
}