PluginProbe
Stream – Activity Log & Audit Trail / 3.2.1
Stream – Activity Log & Audit Trail v3.2.1
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
stream / classes / class-log.php

class-log.php in Stream – Activity Log & Audit Trail 3.2.1, at classes/class-log.php

336 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WP_Stream;
3
4 class Log {
5 /**
6 * Hold Plugin class
7 * @var Plugin
8 */
9 public $plugin;
10
11 /**
12 * Previous Stream record ID, used for chaining same-session records
13 *
14 * @var int
15 */
16 private $prev_record;
17
18 /**
19 * Class constructor.
20 *
21 * @param Plugin $plugin The main Plugin class.
22 */
23 public function __construct( $plugin ) {
24 $this->plugin = $plugin;
25
26 // Ensure function used in various methods is pre-loaded
27 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
28 require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
29 }
30 }
31
32 /**
33 * Log handler
34 *
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
42 *
43 * @return mixed True if updated, otherwise false|WP_Error
44 */
45 public function log( $connector, $message, $args, $object_id, $context, $action, $user_id = null ) {
46 global $wp_roles;
47
48 if ( is_null( $user_id ) ) {
49 $user_id = get_current_user_id();
50 }
51
52 if ( is_null( $object_id ) ) {
53 $object_id = 0;
54 }
55
56 $wp_cron_tracking = isset( $this->plugin->settings->options['advanced_wp_cron_tracking'] ) ? $this->plugin->settings->options['advanced_wp_cron_tracking'] : false;
57 $author = new Author( $user_id );
58 $agent = $author->get_current_agent();
59
60 // WP Cron tracking requires opt-in and WP Cron to be enabled
61 if ( ! $wp_cron_tracking && 'wp_cron' === $agent ) {
62 return false;
63 }
64
65 $user = new \WP_User( $user_id );
66
67 if ( $this->is_record_excluded( $connector, $context, $action, $user ) ) {
68 return false;
69 }
70
71 $user_meta = array(
72 'user_email' => (string) ! empty( $user->user_email ) ? $user->user_email : '',
73 'display_name' => (string) $author->get_display_name(),
74 'user_login' => (string) ! empty( $user->user_login ) ? $user->user_login : '',
75 'user_role_label' => (string) $author->get_role(),
76 'agent' => (string) $agent,
77 );
78
79 if ( 'wp_cli' === $agent && function_exists( 'posix_getuid' ) ) {
80 $uid = posix_getuid();
81 $user_info = posix_getpwuid( $uid );
82
83 $user_meta['system_user_id'] = (int) $uid;
84 $user_meta['system_user_name'] = (string) $user_info['name'];
85 }
86
87 // Prevent any meta with null values from being logged
88 $stream_meta = array_filter(
89 $args,
90 function( $var ) {
91 return ! is_null( $var );
92 }
93 );
94
95 // Add user meta to Stream meta
96 $stream_meta['user_meta'] = $user_meta;
97
98 // Get the current time in milliseconds
99 $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
100
101 if ( ! empty( $user->roles ) ) {
102 $roles = array_values( $user->roles );
103 $role = $roles[0];
104 } elseif ( is_multisite() && is_super_admin() && $wp_roles->is_role( 'administrator' ) ) {
105 $role = 'administrator';
106 } else {
107 $role = '';
108 }
109
110 $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,
123 );
124
125 if ( 0 === $recordarr['object_id'] ) {
126 unset( $recordarr['object_id'] );
127 }
128
129 $result = $this->plugin->db->insert( $recordarr );
130
131 $this->debug_backtrace( $recordarr );
132
133 return $result;
134 }
135
136 /**
137 * This function is use to check whether or not a record should be excluded from the log
138 *
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
144 *
145 * @return bool
146 */
147 public function is_record_excluded( $connector, $context, $action, $user = null, $ip = null ) {
148 if ( is_null( $user ) ) {
149 $user = wp_get_current_user();
150 }
151
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 if ( ! empty( $user->roles ) ) {
159 $roles = array_values( $user->roles );
160 $role = $roles[0];
161 } else {
162 $role = '';
163 }
164 $record = array(
165 'connector' => $connector,
166 'context' => $context,
167 'action' => $action,
168 'author' => $user->ID,
169 'role' => $role,
170 'ip_address' => $ip,
171 );
172
173 $exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();
174
175 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
176 $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 }
189 }
190
191 $exclude_record = false;
192
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 ] : '';
201
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 }
234 }
235 }
236 /**
237 * Filters whether or not a record should be excluded from the log
238 *
239 * If true, the record is not logged.
240 *
241 * @param array $exclude_record Whether the record should excluded
242 * @param array $recordarr The record to log
243 *
244 * @return bool
245 */
246 return apply_filters( 'wp_stream_is_record_excluded', $exclude_record, $record );
247 }
248
249 /**
250 * Send a full backtrace of calls to the PHP error log for debugging
251 *
252 * @param array $recordarr
253 *
254 * @return void
255 */
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 );
270
271 if ( ! $enabled ) {
272 return;
273 }
274
275 if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
276 error_log( 'WP Stream debug backtrace requires at least PHP 5.3.6' );
277 return;
278 }
279
280 // Record details
281 $summary = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
282 $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
283 $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
284 $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
285 $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null;
286
287 // Stream meta
288 $stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;
289
290 unset( $stream_meta['user_meta'] );
291
292 if ( $stream_meta ) {
293 array_walk( $stream_meta, function( &$value, $key ) {
294 $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
295 });
296
297 $stream_meta = implode( ', ', $stream_meta );
298 }
299
300 // User meta
301 $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;
302
303 if ( $user_meta ) {
304 array_walk( $user_meta, function( &$value, $key ) {
305 $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
306 });
307
308 $user_meta = implode( ', ', $user_meta );
309 }
310
311 // Debug backtrace
312 ob_start();
313
314 // @codingStandardsIgnoreStart
315 debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // Option to ignore args requires PHP 5.3.6
316 // @codingStandardsIgnoreEnd
317
318 $backtrace = ob_get_clean();
319 $backtrace = array_values( array_filter( explode( "\n", $backtrace ) ) );
320
321 $output = sprintf(
322 "WP Stream Debug Backtrace\n\n Summary | %s\n Author | %s\n Connector | %s\n Context | %s\n Action | %s\nStream Meta | %s\nAuthor Meta | %s\n\n%s\n",
323 $summary,
324 $author,
325 $connector,
326 $context,
327 $action,
328 $stream_meta,
329 $user_meta,
330 implode( "\n", $backtrace )
331 );
332
333 error_log( $output );
334 }
335 }
336