PluginProbe
Stream – Activity Log & Audit Trail / 3.1
Stream – Activity Log & Audit Trail v3.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.1, at classes/class-log.php

332 lines 10.7 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 // All meta must be strings, so we will serialize any array meta values
99 array_walk(
100 $stream_meta,
101 function( &$v ) {
102 $v = (string) maybe_serialize( $v );
103 }
104 );
105
106 // Get the current time in milliseconds
107 $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
108
109 if ( ! empty( $user->roles ) ) {
110 $roles = array_values( $user->roles );
111 $role = $roles[0];
112 } elseif ( is_multisite() && is_super_admin() && $wp_roles->is_role( 'administrator' ) ) {
113 $role = 'administrator';
114 } else {
115 $role = '';
116 }
117
118 $recordarr = array(
119 'object_id' => (int) $object_id,
120 'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
121 'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
122 'user_id' => (int) $user_id,
123 'user_role' => (string) $role,
124 'created' => (string) $iso_8601_extended_date,
125 'summary' => (string) vsprintf( $message, $args ),
126 'connector' => (string) $connector,
127 'context' => (string) $context,
128 'action' => (string) $action,
129 'ip' => (string) wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ),
130 'meta' => (array) $stream_meta,
131 );
132
133 if ( 0 === $recordarr['object_id'] ) {
134 unset( $recordarr['object_id'] );
135 }
136
137 $result = $this->plugin->db->insert( $recordarr );
138
139 $this->debug_backtrace( $recordarr );
140
141 return $result;
142 }
143
144 /**
145 * This function is use to check whether or not a record should be excluded from the log
146 *
147 * @param string $connector Name of the connector being logged
148 * @param string $context Name of the context being logged
149 * @param string $action Name of the action being logged
150 * @param \WP_User $user The user being logged
151 * @param string $ip IP address being logged
152 *
153 * @return bool
154 */
155 public function is_record_excluded( $connector, $context, $action, $user = null, $ip = null ) {
156 if ( is_null( $user ) ) {
157 $user = wp_get_current_user();
158 }
159
160 if ( is_null( $ip ) ) {
161 $ip = wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
162 } else {
163 $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
164 }
165
166 if ( ! empty( $user->roles ) ) {
167 $roles = array_values( $user->roles );
168 $role = $roles[0];
169 } else {
170 $role = '';
171 }
172 $record = array(
173 'connector' => $connector,
174 'context' => $context,
175 'action' => $action,
176 'author' => $user->ID,
177 'role' => $role,
178 'ip_address' => $ip,
179 );
180
181 $exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();
182
183 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
184 $multisite_options = (array) get_site_option( 'wp_stream_network', array() );
185 $multisite_exclude_settings = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
186
187 if ( ! empty( $multisite_exclude_settings ) ) {
188 foreach ( $multisite_exclude_settings['exclude_row'] as $key => $rule ) {
189 $exclude_settings['exclude_row'][] = $multisite_exclude_settings['exclude_row'][ $key ];
190 $exclude_settings['author_or_role'][] = $multisite_exclude_settings['author_or_role'][ $key ];
191 $exclude_settings['connector'][] = $multisite_exclude_settings['connector'][ $key ];
192 $exclude_settings['context'][] = $multisite_exclude_settings['context'][ $key ];
193 $exclude_settings['action'][] = $multisite_exclude_settings['action'][ $key ];
194 $exclude_settings['ip_address'][] = $multisite_exclude_settings['ip_address'][ $key ];
195 }
196 }
197 }
198
199 if ( isset( $exclude_settings['exclude_row'] ) && ! empty( $exclude_settings['exclude_row'] ) ) {
200 foreach ( $exclude_settings['exclude_row'] as $key => $value ) {
201 // Prepare values
202 $author_or_role = isset( $exclude_settings['author_or_role'][ $key ] ) ? $exclude_settings['author_or_role'][ $key ] : '';
203 $connector = isset( $exclude_settings['connector'][ $key ] ) ? $exclude_settings['connector'][ $key ] : '';
204 $context = isset( $exclude_settings['context'][ $key ] ) ? $exclude_settings['context'][ $key ] : '';
205 $action = isset( $exclude_settings['action'][ $key ] ) ? $exclude_settings['action'][ $key ] : '';
206 $ip_address = isset( $exclude_settings['ip_address'][ $key ] ) ? $exclude_settings['ip_address'][ $key ] : '';
207
208 $exclude = array(
209 'connector' => ! empty( $connector ) ? $connector : null,
210 'context' => ! empty( $context ) ? $context : null,
211 'action' => ! empty( $action ) ? $action : null,
212 'ip_address' => ! empty( $ip_address ) ? $ip_address : null,
213 'author' => is_numeric( $author_or_role ) ? absint( $author_or_role ) : null,
214 'role' => ( ! empty( $author_or_role ) && ! is_numeric( $author_or_role ) ) ? $author_or_role : null,
215 );
216
217 $exclude_rules = array_filter( $exclude, 'strlen' );
218
219 if ( ! empty( $exclude_rules ) ) {
220 $excluded = true;
221
222 foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
223 if ( 'ip_address' === $exclude_key ) {
224 $ip_addresses = explode( ',', $exclude_value );
225 if ( ! in_array( $record['ip_address'], $ip_addresses, true ) ) {
226 $excluded = false;
227 break;
228 }
229 } elseif ( $record[ $exclude_key ] !== $exclude_value ) {
230 $excluded = false;
231 break;
232 }
233 }
234
235 if ( $excluded ) {
236 return true;
237 }
238 }
239 }
240 }
241
242 return false;
243 }
244
245 /**
246 * Send a full backtrace of calls to the PHP error log for debugging
247 *
248 * @param array $recordarr
249 *
250 * @return void
251 */
252 public function debug_backtrace( $recordarr ) {
253 /**
254 * Enable debug backtrace on records.
255 *
256 * This filter is for developer use only. When enabled, Stream will send
257 * a full debug backtrace of PHP calls for each record. Optionally, you may
258 * use the available $recordarr parameter to specify what types of records to
259 * create backtrace logs for.
260 *
261 * @param array $recordarr
262 *
263 * @return bool Set to FALSE by default (backtrace disabled)
264 */
265 $enabled = apply_filters( 'wp_stream_debug_backtrace', false, $recordarr );
266
267 if ( ! $enabled ) {
268 return;
269 }
270
271 if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
272 error_log( 'WP Stream debug backtrace requires at least PHP 5.3.6' );
273 return;
274 }
275
276 // Record details
277 $summary = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
278 $author = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
279 $connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
280 $context = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
281 $action = isset( $recordarr['action'] ) ? $recordarr['action'] : null;
282
283 // Stream meta
284 $stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;
285
286 unset( $stream_meta['user_meta'] );
287
288 if ( $stream_meta ) {
289 array_walk( $stream_meta, function( &$value, $key ) {
290 $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
291 });
292
293 $stream_meta = implode( ', ', $stream_meta );
294 }
295
296 // User meta
297 $user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;
298
299 if ( $user_meta ) {
300 array_walk( $user_meta, function( &$value, $key ) {
301 $value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
302 });
303
304 $user_meta = implode( ', ', $user_meta );
305 }
306
307 // Debug backtrace
308 ob_start();
309
310 // @codingStandardsIgnoreStart
311 debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // Option to ignore args requires PHP 5.3.6
312 // @codingStandardsIgnoreEnd
313
314 $backtrace = ob_get_clean();
315 $backtrace = array_values( array_filter( explode( "\n", $backtrace ) ) );
316
317 $output = sprintf(
318 "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",
319 $summary,
320 $author,
321 $connector,
322 $context,
323 $action,
324 $stream_meta,
325 $user_meta,
326 implode( "\n", $backtrace )
327 );
328
329 error_log( $output );
330 }
331 }
332