| @@ -39,15 +39,19 @@ | ||
| 39 | 39 | * @param array $events Specific events to fetch. |
| 40 | 40 | * @param string $user_id User ID to filter logs. |
| 41 | 41 | * @param string $ip IP address to filter logs. |
| 42 | 42 | * @param int $paged Pagination page number. |
| 43 | + * @param int $per_page Number of logs per page. | |
| 43 | 44 | * |
| 44 | 45 | * @return Audit_Log[]|WP_Error Returns an array of Audit_Log objects or WP_Error on failure. |
| 45 | 46 | * @throws Exception Throws exception on failure. |
| 46 | 47 | */ |
| 47 | - public function fetch( $date_from, $date_to, $events = array(), $user_id = '', $ip = '', $paged = 1 ) { | |
| 48 | - $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged ); | |
| 48 | + public function fetch( $date_from, $date_to, $events = array(), $user_id = '', $ip = '', $paged = 1, $per_page = 10 ) { | |
| 49 | + $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged, $per_page ); | |
| 49 | 50 | $this->log( sprintf( 'Found %s from local', count( $internal ) ), self::AUDIT_LOG ); |
| 51 | + if ( ! wd_di()->get( WPMUDEV::class )->is_pro() ) { | |
| 52 | + return $internal; | |
| 53 | + } | |
| 50 | 54 | $checkpoint = get_site_option( self::CACHE_LAST_CHECKPOINT ); |
| 51 | 55 | if ( false === $checkpoint ) { |
| 52 | 56 | // This case where user install the plugin, have some local data but never reach to Logs page, then check point will be today. |
| 53 | 57 | $checkpoint = time(); |
| @@ -67,9 +71,9 @@ | ||
| 67 | 71 | if ( count( $cloud ) ) { |
| 68 | 72 | // No data from cloud too. |
| 69 | 73 | Audit_Log::mass_insert( $cloud ); |
| 70 | 74 | // Because this is roughly fetch, so we have to filter out again using the local data. |
| 71 | - $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged ); | |
| 75 | + $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged, $per_page ); | |
| 72 | 76 | } |
| 73 | 77 | // Cache the last time fetch, this will be useful in case of mixed data. |
| 74 | 78 | update_site_option( self::CACHE_LAST_CHECKPOINT, $date_from ); |
| 75 | 79 | // This case we have the data, however, maybe it can be out of the cached range, so we have to check. |
| @@ -92,9 +96,9 @@ | ||
| 92 | 96 | } |
| 93 | 97 | if ( is_array( $cloud ) ) { |
| 94 | 98 | // Silence the error here, as we actually have data. |
| 95 | 99 | Audit_Log::mass_insert( $cloud ); |
| 96 | - $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged ); | |
| 100 | + $internal = Audit_Log::query( $date_from, $date_to, $events, $user_id, $ip, $paged, $per_page ); | |
| 97 | 101 | // Cache the last time fetch, this will be useful in case of mixed data. |
| 98 | 102 | update_site_option( self::CACHE_LAST_CHECKPOINT, $date_from ); |
| 99 | 103 | } |
| 100 | 104 | } |
| @@ -148,33 +152,17 @@ | ||
| 148 | 152 | |
| 149 | 153 | return $data['data']; |
| 150 | 154 | } |
| 151 | 155 | |
| 156 | + | |
| 152 | 157 | /** |
| 153 | - * Flushes logs that need to be synced with the cloud. | |
| 158 | + * Delete all local audit log entries from database and reset checkpoint. | |
| 159 | + * | |
| 160 | + * @return void | |
| 154 | 161 | */ |
| 155 | - public function flush() { | |
| 156 | - $logs = Audit_Log::get_logs_need_flush(); | |
| 157 | - // Build the data. | |
| 158 | - $data = array(); | |
| 159 | - foreach ( $logs as $log ) { | |
| 160 | - $item = $log->export(); | |
| 161 | - unset( $item['synced'] ); | |
| 162 | - unset( $item['safe'] ); | |
| 163 | - unset( $item['id'] ); | |
| 164 | - $item['msg'] = addslashes( $item['msg'] ); | |
| 165 | - $data[] = $item; | |
| 166 | - } | |
| 167 | - if ( count( $data ) ) { | |
| 168 | - $ret = $this->curl_to_api( $data ); | |
| 169 | - if ( ! is_wp_error( $ret ) ) { | |
| 170 | - $this->log( sprintf( 'API sync successful, marking %s logs as synced', count( $data ) ), self::AUDIT_LOG ); | |
| 171 | - foreach ( $logs as $log ) { | |
| 172 | - $log->synced = 1; | |
| 173 | - $log->save(); | |
| 174 | - } | |
| 175 | - } | |
| 176 | - } | |
| 162 | + public function reset(): void { | |
| 163 | + Audit_Log::truncate(); | |
| 164 | + delete_site_option( self::CACHE_LAST_CHECKPOINT ); | |
| 177 | 165 | } |
| 178 | 166 | |
| 179 | 167 | /** |
| 180 | 168 | * Cleans up old logs based on storage settings. |
| @@ -184,9 +172,10 @@ | ||
| 184 | 172 | public function audit_clean_up_logs() { |
| 185 | 173 | $audit_settings = wd_di()->get( Audit_Logging::class ); |
| 186 | 174 | $interval = $this->calculate_date_interval( $audit_settings->storage_days ); |
| 187 | 175 | // Since v5.7.0. |
| 188 | - $interval = (string) apply_filters( 'wpdef_audit_logs_store_backward', $interval ); | |
| 176 | + $interval = apply_filters( 'wpdef_audit_logs_store_backward', $interval ); | |
| 177 | + $interval = is_string( $interval ) ? $interval : (string) $interval; | |
| 189 | 178 | |
| 190 | 179 | try { |
| 191 | 180 | $interval_obj = new DateInterval( $interval ); |
| 192 | 181 | } catch ( Exception ) { |
| @@ -205,13 +194,13 @@ | ||
| 205 | 194 | $logs_count = Audit_Log::count( $date_from->getTimestamp(), $date_to->getTimestamp() ); |
| 206 | 195 | if ( $logs_count > 0 ) { |
| 207 | 196 | $this->log( 'Cleaning up old logs from ' . $date_from->format( 'Y-m-d H:i:s' ) . ' to ' . $date_to->format( 'Y-m-d H:i:s' ), self::AUDIT_LOG ); |
| 208 | 197 | // Since v5.0.0. |
| 209 | - $delete_count = (int) apply_filters( 'wpdef_audit_limit_deleted_logs', 50 ); | |
| 198 | + $delete_count = apply_filters( 'wpdef_audit_limit_deleted_logs', 50 ); | |
| 210 | 199 | Audit_Log::delete_old_logs( |
| 211 | 200 | $date_from->getTimestamp(), |
| 212 | 201 | $date_to->getTimestamp(), |
| 213 | - $delete_count, | |
| 202 | + is_int( $delete_count ) ? $delete_count : (int) $delete_count, | |
| 214 | 203 | ); |
| 215 | 204 | } |
| 216 | 205 | } |
| 217 | 206 | } |
| @@ -240,84 +229,8 @@ | ||
| 240 | 229 | return $ret; |
| 241 | 230 | } |
| 242 | 231 | |
| 243 | 232 | /** |
| 244 | - * Sends data to the API using a socket connection. | |
| 245 | - * | |
| 246 | - * @param array $data Data to be sent to the API. | |
| 247 | - * | |
| 248 | - * @return bool Returns true on success, false on failure. | |
| 249 | - */ | |
| 250 | - public function socket_to_api( $data ) { | |
| 251 | - $sockets = Array_Cache::get( 'sockets', 'audit', array() ); | |
| 252 | - // We will need to wait a bit. | |
| 253 | - if ( 0 === ( is_array( $sockets ) || $sockets instanceof Countable ? count( $sockets ) : 0 ) ) { | |
| 254 | - // Fall back. | |
| 255 | - return false; | |
| 256 | - } | |
| 257 | - $this->log( | |
| 258 | - sprintf( 'Flush %s to cloud', is_array( $data ) || $data instanceof Countable ? count( $data ) : 0 ), | |
| 259 | - self::AUDIT_LOG | |
| 260 | - ); | |
| 261 | - $start_time = microtime( true ); | |
| 262 | - $sks = $sockets; | |
| 263 | - $r = null; | |
| 264 | - $e = null; | |
| 265 | - if ( ( false === stream_select( $r, $sks, $e, 1 ) ) ) { | |
| 266 | - // This case error happen. | |
| 267 | - return false; | |
| 268 | - } | |
| 269 | - | |
| 270 | - $fp = array_shift( $sockets ); | |
| 271 | - | |
| 272 | - $uri = '/logs/add_multiple'; | |
| 273 | - $vars = http_build_query( $data ); | |
| 274 | - $this->attach_behavior( WPMUDEV::class, WPMUDEV::class ); | |
| 275 | - // We're sending data to the server, so we're not manipulating files. Ignore the error. | |
| 276 | - // @codingStandardsIgnoreStart | |
| 277 | - fwrite( $fp, 'POST ' . $uri . " HTTP/1.1\r\n" ); | |
| 278 | - fwrite( $fp, 'Host: ' . $this->strip_protocol( $this->get_endpoint() ) . "\r\n" ); | |
| 279 | - fwrite( $fp, "Content-Type: application/x-www-form-urlencoded\r\n" ); | |
| 280 | - fwrite( $fp, 'Content-Length: ' . strlen( $vars ) . "\r\n" ); | |
| 281 | - fwrite( $fp, 'apikey: ' . $this->get_apikey() . "\r\n" ); | |
| 282 | - fwrite( $fp, "Connection: close\r\n" ); | |
| 283 | - fwrite( $fp, "\r\n" ); | |
| 284 | - fwrite( $fp, $vars ); | |
| 285 | - stream_set_timeout( $fp, 5 ); | |
| 286 | - $res = ''; | |
| 287 | - while ( ! feof( $fp ) ) { | |
| 288 | - $res .= fgets( $fp, 1024 ); | |
| 289 | - // Check if the transfer has taken too long. | |
| 290 | - $end_time = microtime( true ); | |
| 291 | - if ( $end_time - $start_time > 3 ) { | |
| 292 | - fclose( $fp ); | |
| 293 | - break; | |
| 294 | - } | |
| 295 | - } | |
| 296 | - // @codingStandardsIgnoreEnd | |
| 297 | - return true; | |
| 298 | - } | |
| 299 | - | |
| 300 | - /** | |
| 301 | - * Open a socket to API for faster transmit. | |
| 302 | - */ | |
| 303 | - public function open_socket() { | |
| 304 | - $sockets = Array_Cache::get( 'sockets', 'audit', array() ); | |
| 305 | - $endpoint = $this->strip_protocol( $this->get_endpoint() ); | |
| 306 | - if ( array() === $sockets ) { | |
| 307 | - $fp = stream_socket_client( | |
| 308 | - 'ssl://' . $endpoint . ':443', | |
| 309 | - $errno, | |
| 310 | - $errstr, | |
| 311 | - 5 | |
| 312 | - ); | |
| 313 | - if ( is_resource( $fp ) ) { | |
| 314 | - Array_Cache::set( 'sockets', array( $fp ), 'audit' ); | |
| 315 | - } | |
| 316 | - } | |
| 317 | - } | |
| 318 | - | |
| 319 | - /** | |
| 320 | 233 | * Strips the protocol from a URL. |
| 321 | 234 | * |
| 322 | 235 | * @param string $url URL to process. |
| 323 | 236 | * |
| @@ -409,9 +322,9 @@ | ||
| 409 | 322 | $model = new Audit_Log(); |
| 410 | 323 | |
| 411 | 324 | if ( ( is_array( $events ) || $events instanceof Countable ? count( $events ) : 0 ) > 1 ) { |
| 412 | 325 | if ( $model->has_method( 'mass_insert' ) ) { |
| 413 | - $model->mass_insert( $events ); | |
| 326 | + $model->mass_insert( $events, 0 ); | |
| 414 | 327 | |
| 415 | 328 | return; |
| 416 | 329 | } |
| 417 | 330 | } |
| @@ -420,6 +333,21 @@ | ||
| 420 | 333 | $model->import( $event ); |
| 421 | 334 | $model->synced = 0; |
| 422 | 335 | $model->save(); |
| 423 | 336 | } |
| 337 | + } | |
| 338 | + | |
| 339 | + /** | |
| 340 | + * Get audit events per week. | |
| 341 | + */ | |
| 342 | + public static function get_audit_events_per_week(): int { | |
| 343 | + $timezone = wp_timezone(); | |
| 344 | + $date_to = ( new DateTime( 'now', $timezone ) ) | |
| 345 | + ->setTime( 23, 59, 59 ) | |
| 346 | + ->getTimestamp(); | |
| 347 | + $date_from = ( new DateTime( '-7 days', $timezone ) ) | |
| 348 | + ->setTime( 0, 0, 0 ) | |
| 349 | + ->getTimestamp(); | |
| 350 | + | |
| 351 | + return Audit_Log::count( $date_from, $date_to ); | |
| 424 | 352 | } |
| 425 | 353 | } |