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