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