PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
mainwp / modules / logs / classes / class-log-changes-logs-helper.php

class-log-changes-logs-helper.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.2.1, at modules/logs/classes/class-log-changes-logs-helper.php

554 lines 19.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Database Site Actions
4 *
5 * This file handles all interactions with the Site Actions DB.
6 *
7 * @package MainWP/Dashboard
8 */
9
10 namespace MainWP\Dashboard\Module\Log;
11
12 use MainWP\Dashboard\MainWP_DB;
13 use MainWP\Dashboard\MainWP_Logger;
14 use MainWP\Dashboard\MainWP_Utility;
15
16 // Exit if accessed directly.
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21
22 /**
23 * Class Log_Changes_Logs_Helper
24 *
25 * @package MainWP\Dashboard
26 */
27 class Log_Changes_Logs_Helper {
28
29
30 /**
31 * Protected static variable to hold the single instance of the class.
32 *
33 * @var mixed Default null
34 */
35 protected static $instance = null;
36
37 /**
38 * Holds the last log created time.
39 *
40 * @var array
41 */
42 private static $last_log_created = array();
43
44 /**
45 * Return the single instance of the class.
46 *
47 * @return mixed $instance The single instance of the class.
48 */
49 public static function instance() {
50 if ( is_null( static::$instance ) ) {
51 static::$instance = new self();
52 }
53 return static::$instance;
54 }
55
56 /**
57 * Plugin constructor.
58 *
59 * Run each time the class is called.
60 */
61 public function __construct() {
62 }
63
64 /**
65 * Method get_sync_changes_logs_last_created().
66 *
67 * Sync site changes logs data.
68 *
69 * @param int $site_id site id.
70 *
71 * @return bool
72 */
73 public function get_sync_changes_logs_last_created( $site_id ) {
74
75 if ( ! isset( static::$last_log_created[ $site_id ] ) ) {
76 $site_opts = MainWP_DB::instance()->get_website_options_array( $site_id, array( 'changes_logs_sync_last_created' ) );
77
78 if ( ! is_array( $site_opts ) ) {
79 $site_opts = array();
80 }
81
82 static::$last_log_created[ $site_id ] = isset( $site_opts['changes_logs_sync_last_created'] ) ? $site_opts['changes_logs_sync_last_created'] : 0;
83
84 // to sure.
85 if ( empty( static::$last_log_created[ $site_id ] ) ) {
86 static::$last_log_created[ $site_id ] = time() - DAY_IN_SECONDS; // to prevent "strange" issue.
87 MainWP_DB::instance()->update_website_option( $site_id, 'changes_logs_sync_last_created', static::$last_log_created[ $site_id ] );
88 }
89 }
90
91 return static::$last_log_created[ $site_id ];
92 }
93
94
95 /**
96 * Method sync_changes_logs().
97 *
98 * Sync site changes logs data.
99 *
100 * @param int $site_id site id.
101 * @param array $sync_changes action data.
102 * @param object $website website data.
103 *
104 * @return bool
105 */
106 public function sync_changes_logs( $site_id, $sync_changes, $website ) { // phpcs:ignore -- NOSONAR - complex.
107
108 MainWP_Logger::instance()->log_events( 'sites-changes', 'Sync changes logs :: [site_id=' . $site_id . '] :: [count=' . ( is_array( $sync_changes ) ? count( $sync_changes ) : 0 ) . ']' );
109
110 if ( empty( $sync_changes ) || ! is_array( $sync_changes ) ) {
111 return false;
112 }
113
114 $sync_last_created = $this->get_sync_changes_logs_last_created( $site_id );
115 $new_last_created = 0;
116 $disabled_type_last_created = 0;
117
118 $created_success = false;
119
120 $ignored_meta_fields = apply_filters( 'mainwp_module_log_sync_ignored_meta_fields', array( 'userdata', 'user_meta' ) );
121
122 if ( ! is_array( $ignored_meta_fields ) ) {
123 $ignored_meta_fields = array();
124 }
125
126 foreach ( $sync_changes as $data ) {
127
128 if ( ! is_array( $data ) || empty( $data['created_on'] ) || empty( $data['log_type_id'] ) ) {
129 continue;
130 }
131
132 $item_created = round( (float) $data['created_on'], 4 ); // to fix float comparing issue.
133
134 if ( $item_created <= $sync_last_created ) {
135 continue;
136 }
137
138 // to fix issue of last sync value.
139 if ( $new_last_created < $item_created ) {
140 $new_last_created = $item_created;
141 }
142
143 $enable_log_type = apply_filters( 'mainwp_module_log_enable_insert_log_type', true, $data );
144 if ( ! $enable_log_type ) {
145 // to fix issue of last sync value.
146 if ( $disabled_type_last_created < $item_created ) {
147 $disabled_type_last_created = $item_created;
148 }
149 continue;
150 }
151
152 $type_id = intval( $data['log_type_id'] );
153 $user_id = ! empty( $data['user_id'] ) ? sanitize_text_field( $data['user_id'] ) : 0;
154 $user_login = ! empty( $data['username'] ) ? sanitize_text_field( $data['username'] ) : '';
155
156 if ( empty( $user_login ) ) {
157 continue;
158 }
159
160 $meta_data = isset( $data['meta_data'] ) && is_array( $data['meta_data'] ) ? $data['meta_data'] : array();
161 $user_meta = isset( $meta_data['user_meta'] ) && is_array( $meta_data['user_meta'] ) ? $meta_data['user_meta'] : array();
162
163 $meta = array();
164 $meta['user_meta_json'] = wp_json_encode( $user_meta );
165 $meta['client_ip'] = $data['client_ip'];
166
167 if ( is_array( $meta_data ) ) {
168 foreach ( $meta_data as $key => $val ) {
169 if ( in_array( $key, $ignored_meta_fields ) ) {
170 continue;
171 }
172 $meta[ $key ] = $val;
173 }
174 }
175
176 $action = isset( $data['action_name'] ) ? $this->map_changes_action( $data['action_name'] ) : '';
177
178 $context = '';
179
180 if ( isset( $data['context'] ) ) {
181 $context = $this->map_change_logs_context( $data['context'] );
182 } elseif ( isset( $data['log_type_id'] ) ) {
183 $context = $this->map_change_logs_context( '', $data['log_type_id'] );
184 }
185
186 $duration = isset( $data['duration'] ) ? sanitize_text_field( $data['duration'] ) : 0; // sanitize_text_field for seconds.
187 $created = isset( $data['created_on'] ) ? (float) ( $data['created_on'] ) : microtime( true );
188
189 $record_mapping = array(
190 'site_id' => $site_id,
191 'user_id' => $user_id,
192 'user_login' => $user_login,
193 'created' => $created,
194 'context' => $context,
195 'action' => $action,
196 'state' => 1,
197 'duration' => $duration,
198 'meta' => $meta,
199 'log_type_id' => $type_id,
200 );
201
202 $sum = 'wordpress' !== $record_mapping['context'] ? esc_html( ucfirst( rtrim( $record_mapping['context'], 's' ) ) ) : 'WordPress'; //phpcs:ignore -- wordpress text.
203 $record_mapping['item'] = $sum;
204 $inserted_id = apply_filters( 'mainwp_sync_site_log_changes_logs', false, $website, $record_mapping );
205 if ( $inserted_id ) {
206 $created_success = true;
207 }
208 }
209
210 // to fix last created sync.
211 $update_sync_last_created = 0;
212 if ( $new_last_created && $created_success ) {
213 $update_sync_last_created = $new_last_created;
214 }
215
216 if ( $disabled_type_last_created && $update_sync_last_created < $disabled_type_last_created ) {
217 $update_sync_last_created = $disabled_type_last_created;
218 }
219
220 if ( ! empty( $update_sync_last_created ) ) {
221 MainWP_DB::instance()->update_website_option( $site_id, 'changes_logs_sync_last_created', $update_sync_last_created );
222 }
223
224 return true;
225 }
226
227 /**
228 * Method map_changes_action.
229 *
230 * @param string $event Logs event.
231 *
232 * @return string Dashboard logs actions to store in db.
233 */
234 public function map_changes_action( $event ) {
235
236 $map_actions = array(
237 'installed' => 'install',
238 'deleted' => 'delete',
239 'activated' => 'activate',
240 'deactivated' => 'deactivate',
241 );
242
243 return isset( $map_actions[ $event ] ) ? $map_actions[ $event ] : $event;
244 }
245
246 /**
247 * Method map_change_logs_context()
248 *
249 * @param string $context Log context|object.
250 * @param int $type_id Logs type code.
251 *
252 * @return string Dashboard logs context to store in db.
253 */
254 public function map_change_logs_context( $context, $type_id = 0 ) {
255 if ( ! empty( $type_id ) ) {
256 $context = isset( static::get_changes_logs_types( $type_id )['context'] ) ? static::get_changes_logs_types( $type_id )['context'] : '';
257 }
258 $context = 'cron-job' === $context ? 'cron' : $context;
259 return \apply_filters( 'mainwp_module_log_changes_logs_mapping_contexts', $context, $type_id );
260 }
261
262 /**
263 * Get custom event title.
264 *
265 * @param int $log_type_id - Event type id.
266 * @param array $data - Event data.
267 * @param string $type - Title type.
268 *
269 * @return string
270 */
271 public static function get_changes_log_title( $log_type_id, $data, $type ) { // phpcs:ignore -- NOSONAR - complex.
272
273 if ( ! is_array( $data ) ) {
274 $data = array();
275 }
276
277 $title = '';
278
279 if ( 'action' === $type ) {
280 if ( 1965 === (int) $log_type_id ) {
281 $title = isset( static::get_changes_logs_types( $log_type_id )['action_name'] ) ? static::get_changes_logs_types( $log_type_id )['action_name'] : '';
282 if ( ! empty( $title ) ) {
283 $title = ucfirst( $title );
284 }
285 } else {
286 $title = isset( static::get_changes_logs_types( $log_type_id )['msg'] ) ? static::get_changes_logs_types( $log_type_id )['msg'] : '';
287 }
288 } elseif ( 'object' === $type ) {
289 $title = isset( static::get_changes_logs_types( $log_type_id )['object_msg'] ) ? static::get_changes_logs_types( $log_type_id )['object_msg'] : '';
290 }
291
292 if ( ! empty( $title ) ) {
293 if ( isset( $data['action'] ) ) {
294 $title = str_replace( '%action%', $data['action'], $title );
295 }
296 if ( isset( $data['plugin'] ) ) {
297 $title = str_replace( '%plugin%', $data['plugin'], $title );
298 }
299 if ( isset( $data['theme'] ) ) {
300 $title = str_replace( '%theme%', $data['theme'], $title );
301 }
302 if ( isset( $data['name'] ) ) {
303 $title = str_replace( '%name%', $data['name'], $title );
304 }
305 return $title;
306 }
307 return '';
308 }
309
310 /**
311 * Method get_changes_logs_types().
312 *
313 * @param int $type_id Logs type code.
314 *
315 * @return array data.
316 */
317 public static function get_changes_logs_types( $type_id = null ) { //phpcs:ignore -- NOSONAR - long function.
318
319 static $defaults;
320 if ( null === $defaults ) {
321 $defaults = include MAINWP_MODULES_DIR . 'logs/includes/default-logs.php'; //phpcs:ignore -- NOSONAR ok.
322 }
323
324 if ( ! is_array( $defaults ) ) {
325 $defaults = array();
326 }
327
328 if ( null !== $type_id ) {
329 return is_scalar( $type_id ) && isset( $defaults[ $type_id ] ) ? $defaults[ $type_id ] : array();
330 }
331 return $defaults;
332 }
333
334 /**
335 * Method get_changes_logs_object_title().
336 *
337 * @param object $item Logs type code.
338 * @param string $default_title Default title.
339 *
340 * @return strinng Object title.
341 */
342 public static function get_changes_logs_object_title( $item = null, $default_title = '' ) { //phpcs:ignore -- NOSONAR - long function.
343 $title = '';
344 if ( ! empty( $item->log_type_id ) ) {
345 $meta = isset( $item->meta ) && is_array( $item->meta )
346 ? $item->meta
347 : array();
348 if ( 'system-setting' === $item->context ) {
349 $title = isset( self::get_changes_logs_types( $item->log_type_id )['title'] ) ? self::get_changes_logs_types( $item->log_type_id )['title'] : '';
350 if ( empty( $title ) ) {
351 $title = esc_html__( 'System Setting', 'mainwp' );
352 }
353 } elseif ( 'database' === $item->context ) {
354 if ( ! empty( $meta['tablenames'] ) ) {
355 $title = is_array( $meta['tablenames'] ) ? implode( ', ', $meta['tablenames'] ) : esc_html( $meta['tablenames'] );
356 $title = esc_html( $title );
357 }
358 if ( empty( $title ) ) {
359 $title = esc_html__( 'Database Table', 'mainwp' );
360 }
361 } elseif ( ( 1970 === (int) $item->log_type_id || 1980 === (int) $item->log_type_id ) ) {
362 if ( ! empty( $meta['name'] ) && is_scalar( $meta['name'] ) ) {
363 $title = esc_html( $meta['name'] );
364 } elseif ( empty( $default_title ) ) {
365 $title = 1970 === (int) $item->log_type_id
366 ? esc_html__( 'Plugin', 'mainwp' )
367 : esc_html__( 'Theme', 'mainwp' );
368 }
369 }
370 }
371 return ! empty( $title ) ? $title : $default_title;
372 }
373
374 /**
375 * Method get_history_changes().
376 *
377 * @param array $args Arguments.
378 *
379 * @return array Results.
380 */
381 public function get_history_changes( $args ) { // phpcs:ignore -- NOSONAR - complex.
382
383 if ( ! is_array( $args ) ) {
384 $args = array();
385 }
386
387 $results = Log_DB_Helper::instance()->get_changes_logs_by( $args );
388
389 if ( ! is_array( $results ) ) {
390 $results = array();
391 }
392
393 $lists = array();
394
395 $manager = Log_Manager::instance();
396
397 $index_by_date = ! empty( $args['target_date'] ) ? 'name' : 'date';
398
399 $get_name = ! empty( $args['slug'] ) || ! empty( $args['name'] );
400
401 $name_title = '';
402
403 $tbl_list = new Log_Events_List_Table( $manager );
404
405 if ( ! empty( $results['items'] ) ) {
406 foreach ( $results['items'] as $item ) {
407 if ( empty( $item->log_id ) ) {
408 continue;
409 }
410 $created_micro = (int) $item->created;
411 $record = new Log_Record( $item );
412
413 $created_sec = (int) $record->created;
414
415 $details = array(
416 'author_name' => $tbl_list->get_log_author_name( $record ),
417 'source' => 'non-mainwp-changes' === $record->connector ? 'WP Admin' : 'Dashboard',
418 'at_hour' => MainWP_Utility::format_time( $created_sec ),
419 'connector' => $record->connector,
420 'context' => $record->context,
421 'action' => $record->action,
422 'event' => $tbl_list->get_event_title( $record, 'action' ),
423 'log_id' => $item->log_id,
424 );
425
426 $slugidx = '';
427 $slug = '';
428 $name_meta = '';
429
430 if ( ! empty( $record->meta ) && is_array( $record->meta ) ) {
431 $slug = ! empty( $record->meta['slug'] ) ? $record->meta['slug'] : '';
432 $name_meta = ! empty( $record->meta['name'] ) ? $record->meta['name'] : '';
433 }
434
435 if ( empty( $slug ) && ! empty( $record->extra_meta ) ) {
436 $extra_data = json_decode( $record->extra_meta, true );
437 if ( is_array( $extra_data ) && ! empty( $extra_data['slug'] ) ) {
438 $slug = $extra_data['slug'];
439 }
440 }
441
442 if ( ! empty( $slug ) ) {
443 if ( false !== strpos( $slug, '/' ) ) {
444 $parts = explode( '/', $slug );
445 $slugidx = $parts[0];
446 } else {
447 $slugidx = basename( $slug, '.php' );
448 }
449 $slugidx = strtolower( $slugidx );
450 } elseif ( ! empty( $name_meta ) ) {
451 $slugidx = strtolower( sanitize_title( $name_meta ) );
452 }
453
454 if ( empty( $slugidx ) && ! empty( $record->item ) ) {
455 $slugidx = strtolower( sanitize_title( $record->item ) );
456 }
457
458 if ( 'date' === $index_by_date ) {
459
460 $day = MainWP_Utility::get_local_date_by_utc_timestamp( $created_micro / 1000000 ); // float.
461
462 if ( ! isset( $lists[ $day ] ) ) {
463 $lists[ $day ] = array();
464 }
465
466 $list_item = array(
467 'date' => MainWP_Utility::format_date( $created_sec ),
468 'details' => $details,
469 );
470
471 if ( $get_name && empty( $name_title ) ) {
472 $name_title = $tbl_list->get_object_title( $record, false, false );
473 }
474
475 if ( 'updated' === $record->action || 'update' === $record->action ) {
476
477 if ( ! empty( $record->extra_meta ) ) {
478 $meta = json_decode( $record->extra_meta, true );
479 } else {
480 $meta = $record->meta;
481 }
482
483 if ( ! is_array( $meta ) ) {
484 $meta = array();
485 }
486
487 if ( ! empty( $meta['version'] ) ) {
488 $list_item['details']['version'] = $meta['version'];
489 }
490 if ( ! empty( $meta['old_version'] ) ) {
491 $list_item['details']['old_version'] = $meta['old_version'];
492 }
493 }
494
495 $lists[ $day ][] = $list_item;
496
497 } else { // group by plugin/theme name.
498
499 $group_key = $slugidx . '_' . $record->site_id;
500
501 if ( ! isset( $lists[ $group_key ] ) ) {
502 $lists[ $group_key ] = array();
503 }
504
505 $list_item = array(
506 'name' => $tbl_list->get_object_title( $record ),
507 'site_id' => $record->site_id,
508 'item_name' => $name_meta,
509 'item_slug' => $slug,
510 'details' => $details,
511 );
512
513 if ( 'updated' === $record->action || 'update' === $record->action ) {
514
515 if ( ! empty( $record->extra_meta ) ) {
516 $meta = json_decode( $record->extra_meta, true );
517 } else {
518 $meta = $record->meta;
519 }
520
521 if ( ! is_array( $meta ) ) {
522 $meta = array();
523 }
524
525 if ( ! empty( $meta['version'] ) ) {
526 $list_item['details']['version'] = $meta['version'];
527 }
528 if ( ! empty( $meta['old_version'] ) ) {
529 $list_item['details']['old_version'] = $meta['old_version'];
530 }
531 }
532
533 $lists[ $group_key ][] = $list_item;
534
535 }
536 }
537 }
538
539 $resp = array(
540 'list' => $lists,
541 );
542
543 if ( ! empty( $results['onward_time'] ) ) {
544 $resp['onward_date'] = MainWP_Utility::format_timezone( $results['onward_time'] );
545 }
546
547 if ( ! empty( $name_title ) ) {
548 $resp['name_title'] = $name_title;
549 }
550
551 return $resp;
552 }
553 }
554