PluginProbe
Stream – Activity Log & Audit Trail / 3.9.2
Stream – Activity Log & Audit Trail v3.9.2
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
← All changes | classes/class-log.php +171 -129 3.2.23.9.2 View file →
@@ -1,15 +1,34 @@
1 1 <?php
2 +/**
3 + * Handles top-level record keeping functionality.
4 + *
5 + * @package WP_Stream
6 + */
7 +
2 8 namespace WP_Stream;
3 9
10 +/**
11 + * Class - Log
12 + */
4 13 class Log {
14 +
5 15 /**
6 - * Hold Plugin class
16 + * Holds Instance of plugin object
17 + *
7 18 * @var Plugin
8 19 */
9 20 public $plugin;
10 21
11 22 /**
23 + * Hold Current visitors IP Address.
24 + *
25 + * @var string
26 + */
27 + private $ip_address;
28 +
29 +
30 + /**
12 31 * Previous Stream record ID, used for chaining same-session records
13 32 *
14 33 * @var int
15 34 */
@@ -17,16 +36,22 @@
17 36
18 37 /**
19 38 * Class constructor.
20 39 *
21 - * @param Plugin $plugin The main Plugin class.
40 + * @param Plugin $plugin Instance of plugin object.
22 41 */
23 42 public function __construct( $plugin ) {
24 43 $this->plugin = $plugin;
25 44
26 - // Ensure function used in various methods is pre-loaded
45 + // Support proxy mode by checking the `X-Forwarded-For` header first.
46 + $ip_address = wp_stream_filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_VALIDATE_IP );
47 + $ip_address = $ip_address ? $ip_address : wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
48 +
49 + $this->ip_address = $ip_address;
50 +
51 + // Ensure function used in various methods is pre-loaded.
27 52 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
28 - require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
53 + require_once ABSPATH . '/wp-admin/includes/plugin.php';
29 54 }
30 55 }
31 56
32 57 /**
@@ -31,15 +56,15 @@
31 56
32 57 /**
33 58 * Log handler
34 59 *
35 - * @param Connector $connector Connector responsible for logging the event
36 - * @param string $message sprintf-ready error message string
37 - * @param array $args sprintf (and extra) arguments to use
38 - * @param int $object_id Target object id
39 - * @param string $context Context of the event
40 - * @param string $action Action of the event
41 - * @param int $user_id User responsible for the event
60 + * @param Connector $connector Connector responsible for logging the event.
61 + * @param string $message sprintf-ready error message string.
62 + * @param array $args sprintf (and extra) arguments to use.
63 + * @param int $object_id Target object id.
64 + * @param string $context Context of the event.
65 + * @param string $action Action of the event.
66 + * @param int $user_id User responsible for the event.
42 67 *
43 68 * @return mixed True if updated, otherwise false|WP_Error
44 69 */
45 70 public function log( $connector, $message, $args, $object_id, $context, $action, $user_id = null ) {
@@ -56,9 +81,9 @@
56 81 $wp_cron_tracking = isset( $this->plugin->settings->options['advanced_wp_cron_tracking'] ) ? $this->plugin->settings->options['advanced_wp_cron_tracking'] : false;
57 82 $author = new Author( $user_id );
58 83 $agent = $author->get_current_agent();
59 84
60 - // WP Cron tracking requires opt-in and WP Cron to be enabled
85 + // WP Cron tracking requires opt-in and WP Cron to be enabled.
61 86 if ( ! $wp_cron_tracking && 'wp_cron' === $agent ) {
62 87 return false;
63 88 }
64 89
@@ -83,22 +108,19 @@
83 108 $user_meta['system_user_id'] = (int) $uid;
84 109 $user_meta['system_user_name'] = (string) $user_info['name'];
85 110 }
86 111
87 - // Prevent any meta with null values from being logged
112 + // Prevent any meta with null values from being logged.
88 113 $stream_meta = array_filter(
89 114 $args,
90 - function( $var ) {
115 + function ( $var ) {
91 116 return ! is_null( $var );
92 117 }
93 118 );
94 119
95 - // Add user meta to Stream meta
120 + // Add user meta to Stream meta.
96 121 $stream_meta['user_meta'] = $user_meta;
97 122
98 - // Get the current time in milliseconds
99 - $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
100 -
101 123 if ( ! empty( $user->roles ) ) {
102 124 $roles = array_values( $user->roles );
103 125 $role = $roles[0];
104 126 } elseif ( is_multisite() && is_super_admin() && $wp_roles->is_role( 'administrator' ) ) {
@@ -107,20 +129,20 @@
107 129 $role = '';
108 130 }
109 131
110 132 $recordarr = array(
111 - 'object_id' => (int) $object_id,
112 - 'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
113 - 'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
114 - 'user_id' => (int) $user_id,
115 - 'user_role' => (string) $role,
116 - 'created' => (string) $iso_8601_extended_date,
117 - 'summary' => (string) vsprintf( $message, $args ),
118 - 'connector' => (string) $connector,
119 - 'context' => (string) $context,
120 - 'action' => (string) $action,
121 - 'ip' => (string) wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ),
122 - 'meta' => (array) $stream_meta,
133 + 'object_id' => (int) $object_id,
134 + 'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
135 + 'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
136 + 'user_id' => (int) $user_id,
137 + 'user_role' => (string) $role,
138 + 'created' => (string) current_time( 'mysql', true ),
139 + 'summary' => (string) vsprintf( $message, $args ),
140 + 'connector' => (string) $connector,
141 + 'context' => (string) $context,
142 + 'action' => (string) $action,
143 + 'ip' => (string) $this->ip_address,
144 + 'meta' => (array) $stream_meta,
123 145 );
124 146
125 147 if ( 0 === $recordarr['object_id'] ) {
126 148 unset( $recordarr['object_id'] );
@@ -127,31 +149,34 @@
127 149 }
128 150
129 151 $result = $this->plugin->db->insert( $recordarr );
130 152
131 - $this->debug_backtrace( $recordarr );
153 + // This is helpful in development environments:
154 + // error_log( $this->debug_backtrace( $recordarr ) );.
132 155
133 156 return $result;
134 157 }
135 158
136 159 /**
137 - * This function is use to check whether or not a record should be excluded from the log
160 + * This function is use to check whether or not a record should be excluded from the log.
138 161 *
139 - * @param string $connector Name of the connector being logged
140 - * @param string $context Name of the context being logged
141 - * @param string $action Name of the action being logged
142 - * @param \WP_User $user The user being logged
143 - * @param string $ip IP address being logged
162 + * @param string $connector Name of the connector being logged.
163 + * @param string $context Name of the context being logged.
164 + * @param string $action Name of the action being logged.
165 + * @param \WP_User $user The user being logged.
166 + * @param string $ip IP address being logged.
144 167 *
145 168 * @return bool
146 169 */
147 170 public function is_record_excluded( $connector, $context, $action, $user = null, $ip = null ) {
171 + $exclude_record = false;
172 +
148 173 if ( is_null( $user ) ) {
149 174 $user = wp_get_current_user();
150 175 }
151 176
152 177 if ( is_null( $ip ) ) {
153 - $ip = wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
178 + $ip = $this->ip_address;
154 179 } else {
155 180 $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
156 181 }
157 182
@@ -156,9 +181,9 @@
156 181 }
157 182
158 183 if ( ! empty( $user->roles ) ) {
159 184 $roles = array_values( $user->roles );
160 - $role = $roles[0];
185 + $role = $roles[0];
161 186 } else {
162 187 $role = '';
163 188 }
164 189 $record = array(
@@ -171,76 +196,38 @@
171 196 );
172 197
173 198 $exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();
174 199
175 - if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
200 + if ( is_multisite() && $this->plugin->is_network_activated() && ! is_network_admin() ) {
176 201 $multisite_options = (array) get_site_option( 'wp_stream_network', array() );
177 - $multisite_exclude_settings = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
178 -
179 - if ( ! empty( $multisite_exclude_settings ) ) {
180 - foreach ( $multisite_exclude_settings['exclude_row'] as $key => $rule ) {
181 - $exclude_settings['exclude_row'][] = $multisite_exclude_settings['exclude_row'][ $key ];
182 - $exclude_settings['author_or_role'][] = $multisite_exclude_settings['author_or_role'][ $key ];
183 - $exclude_settings['connector'][] = $multisite_exclude_settings['connector'][ $key ];
184 - $exclude_settings['context'][] = $multisite_exclude_settings['context'][ $key ];
185 - $exclude_settings['action'][] = $multisite_exclude_settings['action'][ $key ];
186 - $exclude_settings['ip_address'][] = $multisite_exclude_settings['ip_address'][ $key ];
187 - }
188 - }
202 + $exclude_settings = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
189 203 }
190 204
191 - $exclude_record = false;
205 + foreach ( $this->exclude_rules_by_rows( $exclude_settings ) as $exclude_rule ) {
206 + $exclude = array(
207 + 'connector' => ! empty( $exclude_rule['connector'] ) ? $exclude_rule['connector'] : null,
208 + 'context' => ! empty( $exclude_rule['context'] ) ? $exclude_rule['context'] : null,
209 + 'action' => ! empty( $exclude_rule['action'] ) ? $exclude_rule['action'] : null,
210 + 'ip_address' => ! empty( $exclude_rule['ip_address'] ) ? $exclude_rule['ip_address'] : null,
211 + 'author' => is_numeric( $exclude_rule['author_or_role'] ) ? absint( $exclude_rule['author_or_role'] ) : null,
212 + 'role' => ( ! empty( $exclude_rule['author_or_role'] ) && ! is_numeric( $exclude_rule['author_or_role'] ) ) ? $exclude_rule['author_or_role'] : null,
213 + );
192 214
193 - if ( isset( $exclude_settings['exclude_row'] ) && ! empty( $exclude_settings['exclude_row'] ) ) {
194 - foreach ( $exclude_settings['exclude_row'] as $key => $value ) {
195 - // Prepare values
196 - $author_or_role = isset( $exclude_settings['author_or_role'][ $key ] ) ? $exclude_settings['author_or_role'][ $key ] : '';
197 - $connector = isset( $exclude_settings['connector'][ $key ] ) ? $exclude_settings['connector'][ $key ] : '';
198 - $context = isset( $exclude_settings['context'][ $key ] ) ? $exclude_settings['context'][ $key ] : '';
199 - $action = isset( $exclude_settings['action'][ $key ] ) ? $exclude_settings['action'][ $key ] : '';
200 - $ip_address = isset( $exclude_settings['ip_address'][ $key ] ) ? $exclude_settings['ip_address'][ $key ] : '';
215 + $exclude_rules = array_filter( $exclude, 'strlen' );
201 216
202 - $exclude = array(
203 - 'connector' => ! empty( $connector ) ? $connector : null,
204 - 'context' => ! empty( $context ) ? $context : null,
205 - 'action' => ! empty( $action ) ? $action : null,
206 - 'ip_address' => ! empty( $ip_address ) ? $ip_address : null,
207 - 'author' => is_numeric( $author_or_role ) ? absint( $author_or_role ) : null,
208 - 'role' => ( ! empty( $author_or_role ) && ! is_numeric( $author_or_role ) ) ? $author_or_role : null,
209 - );
210 -
211 - $exclude_rules = array_filter( $exclude, 'strlen' );
212 -
213 - if ( ! empty( $exclude_rules ) ) {
214 - $matches_exclusion_rule = true;
215 -
216 - foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
217 - if ( 'ip_address' === $exclude_key ) {
218 - $ip_addresses = explode( ',', $exclude_value );
219 - if ( ! in_array( $record['ip_address'], $ip_addresses, true ) ) {
220 - $matches_exclusion_rule = false;
221 - break;
222 - }
223 - } elseif ( $record[ $exclude_key ] !== $exclude_value ) {
224 - $matches_exclusion_rule = false;
225 - break;
226 - }
227 - }
228 -
229 - if ( $matches_exclusion_rule ) {
230 - $exclude_record = true;
231 - break;
232 - }
233 - }
217 + if ( $this->record_matches_rules( $record, $exclude_rules ) ) {
218 + $exclude_record = true;
219 + break;
234 220 }
235 221 }
222 +
236 223 /**
237 - * Filters whether or not a record should be excluded from the log
224 + * Filters whether or not a record should be excluded from the log.
238 225 *
239 226 * If true, the record is not logged.
240 227 *
241 - * @param array $exclude_record Whether the record should excluded
242 - * @param array $recordarr The record to log
228 + * @param array $exclude_record Whether the record should excluded.
229 + * @param array $recordarr The record to log.
243 230 *
244 231 * @return bool
245 232 */
246 233 return apply_filters( 'wp_stream_is_record_excluded', $exclude_record, $record );
@@ -246,39 +233,89 @@
246 233 return apply_filters( 'wp_stream_is_record_excluded', $exclude_record, $record );
247 234 }
248 235
249 236 /**
250 - * Send a full backtrace of calls to the PHP error log for debugging
237 + * Check if a record to stored matches certain rules.
251 238 *
252 - * @param array $recordarr
239 + * @param array $record List of record parameters.
240 + * @param array $exclude_rules List of record exclude rules.
253 241 *
254 - * @return void
242 + * @return boolean
255 243 */
256 - public function debug_backtrace( $recordarr ) {
257 - /**
258 - * Enable debug backtrace on records.
259 - *
260 - * This filter is for developer use only. When enabled, Stream will send
261 - * a full debug backtrace of PHP calls for each record. Optionally, you may
262 - * use the available $recordarr parameter to specify what types of records to
263 - * create backtrace logs for.
264 - *
265 - * @param array $recordarr
266 - *
267 - * @return bool Set to FALSE by default (backtrace disabled)
268 - */
269 - $enabled = apply_filters( 'wp_stream_debug_backtrace', false, $recordarr );
244 + public function record_matches_rules( $record, $exclude_rules ) {
245 + $matches_needed = count( $exclude_rules );
246 + $matches_found = 0;
247 + foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
248 + if ( ! isset( $record[ $exclude_key ] ) || is_null( $exclude_value ) ) {
249 + continue;
250 + }
270 251
271 - if ( ! $enabled ) {
272 - return;
252 + if ( 'ip_address' === $exclude_key ) {
253 + $ip_addresses = explode( ',', $exclude_value );
254 +
255 + if ( in_array( $record['ip_address'], $ip_addresses, true ) ) {
256 + $matches_found++;
257 + }
258 + } elseif ( $record[ $exclude_key ] === $exclude_value ) {
259 + $matches_found++;
260 + }
273 261 }
274 262
263 + return $matches_found === $matches_needed;
264 + }
265 +
266 + /**
267 + * Get all exclude rules by row because we store them by rule instead.
268 + *
269 + * @param array $rules List of rules indexed by rule ID.
270 + *
271 + * @return array
272 + */
273 + public function exclude_rules_by_rows( $rules ) {
274 + $excludes = array();
275 +
276 + // TODO: Move these to where the settings are generated to ensure they're in sync.
277 + $rule_keys = array(
278 + 'exclude_row',
279 + 'author_or_role',
280 + 'connector',
281 + 'context',
282 + 'action',
283 + 'ip_address',
284 + );
285 +
286 + if ( empty( $rules['exclude_row'] ) ) {
287 + return array();
288 + }
289 +
290 + foreach ( array_keys( $rules['exclude_row'] ) as $row_id ) {
291 + $excludes[ $row_id ] = array();
292 +
293 + foreach ( $rule_keys as $rule_key ) {
294 + if ( isset( $rules[ $rule_key ][ $row_id ] ) ) {
295 + $excludes[ $row_id ][ $rule_key ] = $rules[ $rule_key ][ $row_id ];
296 + } else {
297 + $excludes[ $row_id ][ $rule_key ] = null;
298 + }
299 + }
300 + }
301 +
302 + return $excludes;
303 + }
304 +
305 + /**
306 + * Helper function to send a full backtrace of calls to the PHP error log for debugging
307 + *
308 + * @param array $recordarr Record argument array.
309 + *
310 + * @return string
311 + */
312 + public function debug_backtrace( $recordarr ) {
275 313 if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
276 - error_log( 'WP Stream debug backtrace requires at least PHP 5.3.6' );
277 - return;
314 + return __( 'Debug backtrace requires at least PHP 5.3.6', 'wp_stream' );
278 315 }
279 316
280 - // Record details
317 + // Record details.
281 318 $summary = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
282 319 $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
283 320 $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
284 321 $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
@@ -283,33 +320,38 @@
283 320 $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
284 321 $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
285 322 $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null;
286 323
287 - // Stream meta
324 + // Stream meta.
288 325 $stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;
289 326
290 327 unset( $stream_meta['user_meta'] );
291 328
292 329 if ( $stream_meta ) {
293 - array_walk( $stream_meta, function( &$value, $key ) {
294 - $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
295 - });
296 -
330 + array_walk(
331 + $stream_meta,
332 + function ( &$value, $key ) {
333 + $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
334 + }
335 + );
297 336 $stream_meta = implode( ', ', $stream_meta );
298 337 }
299 338
300 - // User meta
339 + // User meta.
301 340 $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;
302 341
303 342 if ( $user_meta ) {
304 - array_walk( $user_meta, function( &$value, $key ) {
305 - $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
306 - });
343 + array_walk(
344 + $user_meta,
345 + function ( &$value, $key ) {
346 + $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
347 + }
348 + );
307 349
308 350 $user_meta = implode( ', ', $user_meta );
309 351 }
310 352
311 - // Debug backtrace
353 + // Debug backtrace.
312 354 ob_start();
313 355
314 356 // @codingStandardsIgnoreStart
315 357 debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // Option to ignore args requires PHP 5.3.6
@@ -329,7 +371,7 @@
329 371 $user_meta,
330 372 implode( "\n", $backtrace )
331 373 );
332 374
333 - error_log( $output );
375 + return $output;
334 376 }
335 377 }