WP Security Audit Log - Version 3.4.1

Version Description

(2019-05-16) =

Release notes: New plugin settings import/export tool & hooks for theme developers

  • New Features

    • Tool to export and import plugin configuration & settings.
    • Exclude range of IP addresses from the activity log.
    • New hooks for theme developers to display the custom login messages the plugin shows when multiple user sessions are blocked.
    • New hook to add a list of hidden meta keys the plugin should keep a log of.
  • Plugin Improvements

    • First time plugin use texts is now easier to read and much shorter.
    • Added support for more time & date formats in the activity log reports for WordPress.
    • Improved content sensor - previously reporting events not neccessarily needed (background processes)
    • Removed hardcoded paths from the WordPress file integrity scanner.
    • Removed Sites filter from audit log viewer - made redundant by the site selector drop down menu.
  • Bug Fixes

    • Multiple events reporting the same thing generated when user changes WooCommerce shipping / billing address.
    • Updated incorrect tags used in test SMS message.
    • Event 2002 (modified post) reported even when there is a specific change event ID.
    • Event 2016 (plugin modified post) reported whenever a post is updated by user.
    • External database connection cannot be deleted because it is marked in use even when not.
    • Plugin generating error when set_user_role is set to NUL in request.
    • Infinite scroll stops working on Firefox (intermittent issue).
Download this release

Release Info

Developer WPWhiteSecurity
Plugin Icon 128x128 WP Security Audit Log
Version 3.4.1
Comparing to
See all releases

Code changes from version 3.4.0.1 to 3.4.1

classes/AlertManager.php CHANGED
@@ -724,9 +724,24 @@ final class WSAL_AlertManager {
724
  $is_disabled = false;
725
  $ip = $this->plugin->settings->GetMainClientIP();
726
  $excluded_ips = $this->plugin->settings->GetExcludedMonitoringIP();
727
- if ( in_array( $ip, $excluded_ips ) ) {
728
- $is_disabled = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
  }
 
730
  return $is_disabled;
731
  }
732
 
724
  $is_disabled = false;
725
  $ip = $this->plugin->settings->GetMainClientIP();
726
  $excluded_ips = $this->plugin->settings->GetExcludedMonitoringIP();
727
+
728
+ if ( ! empty( $excluded_ips ) ) {
729
+ foreach ( $excluded_ips as $excluded_ip ) {
730
+ if ( false !== strpos( $excluded_ip, '-' ) ) {
731
+ $ip_range = $this->plugin->settings->get_ipv4_by_range( $excluded_ip );
732
+ $ip_range = $ip_range->lower . '-' . $ip_range->upper;
733
+
734
+ if ( $this->plugin->settings->check_ipv4_in_range( $ip, $ip_range ) ) {
735
+ $is_disabled = true;
736
+ break;
737
+ }
738
+ } elseif ( $ip === $excluded_ip ) {
739
+ $is_disabled = true;
740
+ break;
741
+ }
742
+ }
743
  }
744
+
745
  return $is_disabled;
746
  }
747
 
classes/Connector/ConnectorFactory.php CHANGED
@@ -43,6 +43,15 @@ abstract class WSAL_Connector_ConnectorFactory {
43
  */
44
  public static $adapter;
45
 
 
 
 
 
 
 
 
 
 
46
  /**
47
  * Returns the a default WPDB connector for saving options
48
  */
@@ -85,6 +94,18 @@ abstract class WSAL_Connector_ConnectorFactory {
85
  public static function GetConfig() {
86
  $conf = new WSAL_Settings( WpSecurityAuditLog::GetInstance() );
87
  $type = $conf->GetAdapterConfig( 'adapter-type' );
 
 
 
 
 
 
 
 
 
 
 
 
88
  if ( empty( $type ) ) {
89
  return null;
90
  } else {
43
  */
44
  public static $adapter;
45
 
46
+ /**
47
+ * Occurrence is installed.
48
+ *
49
+ * @since 3.4.1
50
+ *
51
+ * @var bool
52
+ */
53
+ private static $is_installed;
54
+
55
  /**
56
  * Returns the a default WPDB connector for saving options
57
  */
94
  public static function GetConfig() {
95
  $conf = new WSAL_Settings( WpSecurityAuditLog::GetInstance() );
96
  $type = $conf->GetAdapterConfig( 'adapter-type' );
97
+
98
+ if ( $type && wsal_freemius()->is_not_paying() ) {
99
+ $connector = new WSAL_Connector_MySQLDB();
100
+
101
+ if ( ! self::$is_installed ) {
102
+ self::$is_installed = $connector->isInstalled();
103
+ $connector->installAll();
104
+ }
105
+
106
+ $type = null;
107
+ }
108
+
109
  if ( empty( $type ) ) {
110
  return null;
111
  } else {
classes/Connector/MySQLDB.php CHANGED
@@ -266,8 +266,9 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
266
  $result['empty'] = true;
267
  return $result;
268
  }
269
- $sql = 'SELECT * FROM ' . $occurrence->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
270
- $occurrences = $wpdb->get_results( $sql, ARRAY_A );
 
271
 
272
  // Insert data to External DB.
273
  if ( ! empty( $occurrences ) ) {
@@ -308,8 +309,9 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
308
  $result['empty'] = true;
309
  return $result;
310
  }
311
- $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
312
- $occurrences = $_wpdb->get_results( $sql, ARRAY_A );
 
313
 
314
  // Insert data to WP.
315
  if ( ! empty( $occurrences ) ) {
266
  $result['empty'] = true;
267
  return $result;
268
  }
269
+ $sql = 'SELECT * FROM ' . $occurrence->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
270
+ $occurrences = $wpdb->get_results( $sql, ARRAY_A );
271
+ $result['count'] = count( $occurrences );
272
 
273
  // Insert data to External DB.
274
  if ( ! empty( $occurrences ) ) {
309
  $result['empty'] = true;
310
  return $result;
311
  }
312
+ $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
313
+ $occurrences = $_wpdb->get_results( $sql, ARRAY_A );
314
+ $result['count'] = count( $occurrences );
315
 
316
  // Insert data to WP.
317
  if ( ! empty( $occurrences ) ) {
classes/Models/Option.php CHANGED
@@ -77,6 +77,12 @@ class WSAL_Models_Option extends WSAL_Models_ActiveRecord {
77
  // Serialize if $value is array or object.
78
  $value = maybe_serialize( $value );
79
  $this->option_value = $value;
 
 
 
 
 
 
80
  return $this->Save();
81
  }
82
 
77
  // Serialize if $value is array or object.
78
  $value = maybe_serialize( $value );
79
  $this->option_value = $value;
80
+
81
+ // Replace the value present in the option cache.
82
+ if ( array_key_exists( $name, $this->option_cache ) ) {
83
+ $this->option_cache[ $name ]['option_value'] = $value;
84
+ }
85
+
86
  return $this->Save();
87
  }
88
 
classes/Sensors/Content.php CHANGED
@@ -161,7 +161,7 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
161
  */
162
  if ( ! defined( 'REST_REQUEST' ) && ! defined( 'DOING_AJAX' ) ) {
163
  // Either Gutenberg's second post request or classic editor's request.
164
- if ( ! isset( $_REQUEST['classic-editor'] ) ) {
165
  $editor_replace = get_option( 'classic-editor-replace', 'classic' );
166
  $allow_users = get_option( 'classic-editor-allow-users', 'disallow' );
167
 
@@ -960,33 +960,29 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
960
  /**
961
  * Post visibility changed.
962
  *
963
- * @param stdClass $oldpost - Old post.
964
- * @param stdClass $newpost - New post.
965
- * @param string $old_status - Old status.
966
- * @param string $new_status - New status.
967
  */
968
  protected function check_visibility_change( $oldpost, $newpost, $old_status, $new_status ) {
969
- if ( 'draft' === $old_status || 'draft' === $new_status ) {
970
- return;
971
- }
972
-
973
  $old_visibility = '';
974
  $new_visibility = '';
975
 
976
  if ( $oldpost->post_password ) {
977
  $old_visibility = __( 'Password Protected', 'wp-security-audit-log' );
978
- } elseif ( 'publish' === $old_status ) {
979
- $old_visibility = __( 'Public', 'wp-security-audit-log' );
980
- } elseif ( 'private' === $old_status ) {
981
  $old_visibility = __( 'Private', 'wp-security-audit-log' );
 
 
982
  }
983
 
984
  if ( $newpost->post_password ) {
985
  $new_visibility = __( 'Password Protected', 'wp-security-audit-log' );
986
- } elseif ( 'publish' === $new_status ) {
987
- $new_visibility = __( 'Public', 'wp-security-audit-log' );
988
- } elseif ( 'private' === $new_status ) {
989
  $new_visibility = __( 'Private', 'wp-security-audit-log' );
 
 
990
  }
991
 
992
  if ( $old_visibility && $new_visibility && ( $old_visibility !== $new_visibility ) ) {
@@ -1019,7 +1015,7 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1019
  $from = strtotime( $oldpost->post_date );
1020
  $to = strtotime( $newpost->post_date );
1021
 
1022
- if ( 'draft' === $oldpost->post_status || 'pending' === $oldpost->post_status ) {
1023
  return 0;
1024
  }
1025
 
@@ -1353,14 +1349,16 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1353
  public function must_not_contain_events( WSAL_AlertManager $manager ) {
1354
  if ( $manager->WillOrHasTriggered( 2016 ) ) {
1355
  return false;
1356
- } elseif ( $manager->WillOrHasTriggered( 2119 ) ) {
1357
- return false;
1358
- } elseif ( $manager->WillOrHasTriggered( 2120 ) ) {
1359
  return false;
1360
  } elseif ( $manager->WillOrHasTriggered( 2049 ) ) {
1361
  return false;
1362
  } elseif ( $manager->WillOrHasTriggered( 2050 ) ) {
1363
  return false;
 
 
 
 
1364
  }
1365
  return true;
1366
  }
@@ -1433,11 +1431,22 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1433
  $current_path = isset( $_SERVER['SCRIPT_NAME'] ) ? esc_url_raw( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) . '?post=' . $post->ID : false;
1434
  $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false;
1435
 
1436
- if ( ! empty( $referrer )
1437
- && strpos( $referrer, $current_path ) !== false ) {
 
 
 
 
 
 
 
 
 
 
1438
  // Ignore this if we were on the same page so we avoid double audit entries.
1439
  return $post;
1440
  }
 
1441
  if ( ! empty( $post->post_title ) ) {
1442
  $event = 2100;
1443
  if ( ! $this->was_triggered( $event ) ) {
161
  */
162
  if ( ! defined( 'REST_REQUEST' ) && ! defined( 'DOING_AJAX' ) ) {
163
  // Either Gutenberg's second post request or classic editor's request.
164
+ if ( ! isset( $_REQUEST['classic-editor'] ) ) { // phpcs:ignore
165
  $editor_replace = get_option( 'classic-editor-replace', 'classic' );
166
  $allow_users = get_option( 'classic-editor-allow-users', 'disallow' );
167
 
960
  /**
961
  * Post visibility changed.
962
  *
963
+ * @param WP_Post $oldpost - Old post.
964
+ * @param WP_Post $newpost - New post.
965
+ * @param string $old_status - Old status.
966
+ * @param string $new_status - New status.
967
  */
968
  protected function check_visibility_change( $oldpost, $newpost, $old_status, $new_status ) {
 
 
 
 
969
  $old_visibility = '';
970
  $new_visibility = '';
971
 
972
  if ( $oldpost->post_password ) {
973
  $old_visibility = __( 'Password Protected', 'wp-security-audit-log' );
974
+ } elseif ( 'private' === $oldpost->post_status ) {
 
 
975
  $old_visibility = __( 'Private', 'wp-security-audit-log' );
976
+ } else {
977
+ $old_visibility = __( 'Public', 'wp-security-audit-log' );
978
  }
979
 
980
  if ( $newpost->post_password ) {
981
  $new_visibility = __( 'Password Protected', 'wp-security-audit-log' );
982
+ } elseif ( 'private' === $newpost->post_status ) {
 
 
983
  $new_visibility = __( 'Private', 'wp-security-audit-log' );
984
+ } else {
985
+ $new_visibility = __( 'Public', 'wp-security-audit-log' );
986
  }
987
 
988
  if ( $old_visibility && $new_visibility && ( $old_visibility !== $new_visibility ) ) {
1015
  $from = strtotime( $oldpost->post_date );
1016
  $to = strtotime( $newpost->post_date );
1017
 
1018
+ if ( 'pending' === $oldpost->post_status ) {
1019
  return 0;
1020
  }
1021
 
1349
  public function must_not_contain_events( WSAL_AlertManager $manager ) {
1350
  if ( $manager->WillOrHasTriggered( 2016 ) ) {
1351
  return false;
1352
+ } elseif ( $manager->WillOrHasTriggered( 2048 ) ) {
 
 
1353
  return false;
1354
  } elseif ( $manager->WillOrHasTriggered( 2049 ) ) {
1355
  return false;
1356
  } elseif ( $manager->WillOrHasTriggered( 2050 ) ) {
1357
  return false;
1358
+ } elseif ( $manager->WillOrHasTriggered( 2119 ) ) {
1359
+ return false;
1360
+ } elseif ( $manager->WillOrHasTriggered( 2120 ) ) {
1361
+ return false;
1362
  }
1363
  return true;
1364
  }
1431
  $current_path = isset( $_SERVER['SCRIPT_NAME'] ) ? esc_url_raw( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) . '?post=' . $post->ID : false;
1432
  $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false;
1433
 
1434
+ // Check referrer URL.
1435
+ if ( ! empty( $referrer ) ) {
1436
+ // Parse the referrer.
1437
+ $parsed_url = wp_parse_url( $referrer );
1438
+
1439
+ // If the referrer is post-new then we can ignore this one.
1440
+ if ( isset( $parsed_url['path'] ) && 'post-new' === basename( $parsed_url['path'], '.php' ) ) {
1441
+ return $post;
1442
+ }
1443
+ }
1444
+
1445
+ if ( ! empty( $referrer ) && strpos( $referrer, $current_path ) !== false ) {
1446
  // Ignore this if we were on the same page so we avoid double audit entries.
1447
  return $post;
1448
  }
1449
+
1450
  if ( ! empty( $post->post_title ) ) {
1451
  $event = 2100;
1452
  if ( ! $this->was_triggered( $event ) ) {
classes/Sensors/FileChanges.php CHANGED
@@ -87,6 +87,13 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
87
  */
88
  private $scan_limit_file = false;
89
 
 
 
 
 
 
 
 
90
  /**
91
  * Class constants.
92
  */
@@ -146,11 +153,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
146
  * Method: Load file detection settings.
147
  */
148
  public function load_file_change_settings() {
149
- if ( ! is_multisite() ) {
150
- $default_scan_dirs = array( 'root', 'wp-admin', 'wp-includes', 'wp-content', 'wp-content/themes', 'wp-content/plugins', 'wp-content/uploads' );
151
- } else {
152
- $default_scan_dirs = array( 'root', 'wp-admin', 'wp-includes', 'wp-content', 'wp-content/themes', 'wp-content/plugins', 'wp-content/uploads', 'wp-content/uploads/sites' );
153
- }
154
 
155
  // Load file detection settings.
156
  $this->scan_settings = array(
@@ -261,26 +264,8 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
261
  // Set the options name for file list.
262
  $file_list = "local_files_$next_to_scan";
263
 
264
- // Prepare directories array.
265
- // @todo Store this in transient to cache the value. We don't need to load it every time.
266
- $uploads_dir = wp_upload_dir();
267
-
268
  // Server directories.
269
- $server_dirs = array(
270
- '', // Root directory.
271
- 'wp-admin', // WordPress Admin.
272
- WPINC, // wp-includes.
273
- WP_CONTENT_DIR, // wp-content.
274
- WP_CONTENT_DIR . '/themes', // Themes.
275
- WP_PLUGIN_DIR, // Plugins.
276
- $uploads_dir['basedir'], // Uploads.
277
- );
278
-
279
- // Prepare directories path.
280
- foreach ( $server_dirs as $index => $server_dir ) {
281
- $server_dir = untrailingslashit( $server_dir );
282
- $server_dirs[ $index ] = preg_replace( '/^' . preg_quote( ABSPATH, '/' ) . '/', '', $server_dir );
283
- }
284
 
285
  // Get directory path to scan.
286
  $path_to_scan = $server_dirs[ $next_to_scan ];
@@ -418,11 +403,14 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
418
  }
419
 
420
  // Created file event.
421
- $this->plugin->alerts->Trigger( 6029, array(
422
- 'FileLocation' => $file,
423
- 'FileHash' => $file_hash,
424
- 'CurrentUserID' => '0',
425
- ) );
 
 
 
426
  }
427
  }
428
 
@@ -452,11 +440,14 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
452
  }
453
 
454
  // Removed file event.
455
- $this->plugin->alerts->Trigger( 6030, array(
456
- 'FileLocation' => $file,
457
- 'FileHash' => $file_hash,
458
- 'CurrentUserID' => '0',
459
- ) );
 
 
 
460
  }
461
  }
462
 
@@ -464,19 +455,25 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
464
  if ( count( $files_changed ) > 0 ) {
465
  // Log the alert.
466
  foreach ( $files_changed as $file => $file_hash ) {
467
- $this->plugin->alerts->Trigger( 6028, array(
468
- 'FileLocation' => $file,
469
- 'FileHash' => $file_hash,
470
- 'CurrentUserID' => '0',
471
- ) );
 
 
 
472
  }
473
  }
474
 
475
  // Check for files limit alert.
476
  if ( $this->scan_limit_file ) {
477
- $this->plugin->alerts->Trigger( 6032, array(
478
- 'CurrentUserID' => '0',
479
- ) );
 
 
 
480
  }
481
 
482
  /**
@@ -509,18 +506,24 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
509
  $this->plugin->SetGlobalOption( 'last-scanned', 'root' );
510
 
511
  // Scan started alert.
512
- $this->plugin->alerts->Trigger( 6033, array(
513
- 'CurrentUserID' => '0',
514
- 'ScanStatus' => 'started',
515
- ) );
 
 
 
516
  } elseif ( 6 === $next_to_scan ) {
517
  $this->plugin->SetGlobalOption( 'last-scanned', $next_to_scan );
518
 
519
  // Scan stopped.
520
- $this->plugin->alerts->Trigger( 6033, array(
521
- 'CurrentUserID' => '0',
522
- 'ScanStatus' => 'stopped',
523
- ) );
 
 
 
524
  } else {
525
  $this->plugin->SetGlobalOption( 'last-scanned', $next_to_scan );
526
  }
@@ -557,6 +560,9 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
557
  $file_size_limit = $this->scan_settings['file_size_limit']; // Get file size limit.
558
  $file_size_limit = $file_size_limit * 1048576; // Calculate file size limit in bytes; 1MB = 1048576 bytes.
559
 
 
 
 
560
  // Scan the directory for files.
561
  while ( false !== ( $item = @readdir( $dir_handle ) ) ) {
562
  // Ignore `.` and `..` from directory.
@@ -569,19 +575,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
569
  continue;
570
  }
571
 
572
- // If we're on root then ignore `wp-admin`, `wp-content` & `wp-includes`.
573
- if (
574
- empty( $path )
575
- && (
576
- false !== strpos( $item, 'wp-admin' )
577
- || false !== strpos( $item, 'wp-content' )
578
- || false !== strpos( $item, 'wp-includes' )
579
- )
580
- ) {
581
- continue;
582
- }
583
-
584
- // Ignore `.git`, `.svn`, & `node_modules` from scan.
585
  if ( false !== strpos( $item, '.git' ) || false !== strpos( $item, '.svn' ) || false !== strpos( $item, 'node_modules' ) ) {
586
  continue;
587
  }
@@ -596,6 +590,11 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
596
  $absolute_name = $dir_path . $item; // Complete file path w.r.t. ABSPATH.
597
  }
598
 
 
 
 
 
 
599
  // Check for directory.
600
  if ( is_dir( $absolute_name ) ) {
601
  /**
@@ -621,15 +620,9 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
621
  * Check if `wp-content/uploads/sites` is present in the
622
  * relative name of the directory & it is allowed to scan.
623
  */
624
- if (
625
- false !== strpos( $relative_name, 'wp-content/uploads/sites' )
626
- && in_array( 'wp-content/uploads/sites', $directories, true )
627
- ) {
628
  $files = array_merge( $files, $this->scan_path( $relative_name ) );
629
- } elseif (
630
- false !== strpos( $relative_name, 'wp-content/uploads/sites' )
631
- && ! in_array( 'wp-content/uploads/sites', $directories, true )
632
- ) {
633
  // If `wp-content/uploads/sites` is not allowed to scan then skip the loop.
634
  continue;
635
  } else {
@@ -670,10 +663,13 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
670
  $files[ $absolute_name ] = @md5_file( $absolute_name ); // File hash.
671
  } else {
672
  // File size is more than the limit.
673
- $this->plugin->alerts->Trigger( 6031, array(
674
- 'FileLocation' => $absolute_name,
675
- 'CurrentUserID' => '0',
676
- ) );
 
 
 
677
 
678
  // File data.
679
  $files[ $absolute_name ] = '';
@@ -708,19 +704,15 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
708
  */
709
  public function filter_scan_files( $scan_files, $path_to_scan ) {
710
  // If the path to scan is of plugins.
711
- if (
712
- false !== strpos( $path_to_scan, 'wp-content/plugins' )
713
- ) {
714
  // Filter plugin files.
715
  $scan_files = $this->filter_excluded_scan_files( $scan_files, 'plugins' );
716
- } elseif (
717
- false !== strpos( $path_to_scan, 'wp-content/themes' ) // And if the path to scan is of themes then.
718
- ) {
719
  // Filter theme files.
720
  $scan_files = $this->filter_excluded_scan_files( $scan_files, 'themes' );
721
  } elseif (
722
- false !== strpos( $path_to_scan, 'wp-admin' ) // If the path is wp-admin or
723
- || false !== strpos( $path_to_scan, 'wp-includes' ) // wp-includes then check it for core updates skip.
724
  ) {
725
  // Get `site_content` option.
726
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
@@ -748,8 +740,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
748
  * @return array
749
  */
750
  public function filter_excluded_scan_files( $scan_files, $excluded_type ) {
751
- // Check if any one of the two parameters are empty.
752
- if ( empty( $scan_files ) || empty( $excluded_type ) ) {
753
  return $scan_files;
754
  }
755
 
@@ -764,16 +755,19 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
764
  // An array of files to exclude from scan files array.
765
  $files_to_exclude = array();
766
 
 
 
 
767
  if (
768
- 'plugins' === $excluded_type
769
- && isset( $excluded_contents->skip_plugins ) // Skip plugins array exists.
770
- && is_array( $excluded_contents->skip_plugins ) // Skip plugins is array.
771
- && ! empty( $excluded_contents->skip_plugins ) // And if it is not empty.
772
  ) {
773
  // Go through each plugin to be skipped.
774
- foreach ( $excluded_contents->skip_plugins as $plugin ) {
775
  // Path of plugin to search in stored files.
776
- $search_path = '/plugins/' . $plugin;
777
 
778
  // Get array of files to exclude of plugins from scan files array.
779
  foreach ( $files as $file ) {
@@ -782,25 +776,6 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
782
  }
783
  }
784
  }
785
- } elseif (
786
- 'themes' === $excluded_type
787
- && isset( $excluded_contents->skip_themes ) // Skip themes array exists.
788
- && is_array( $excluded_contents->skip_themes ) // Skip themes is array.
789
- && ! empty( $excluded_contents->skip_themes ) // And if it is not empty.
790
- ) {
791
- // Go through each theme to be skipped.
792
- foreach ( $excluded_contents->skip_themes as $theme ) {
793
- // Path of theme to search in stored files.
794
- $search_path = '/themes/' . $theme;
795
-
796
- // Get array of files to exclude of themes from scan files array.
797
- $files_to_exclude = array();
798
- foreach ( $files as $file ) {
799
- if ( false !== strpos( $file, $search_path ) ) {
800
- $files_to_exclude[] = $file;
801
- }
802
- }
803
- }
804
  }
805
 
806
  // If there are files to be excluded then.
@@ -813,6 +788,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
813
  }
814
  }
815
  }
 
816
  return $scan_files;
817
  }
818
 
@@ -829,7 +805,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
829
  }
830
 
831
  // If path to scan is of plugins then empty the skip plugins array.
832
- if ( false !== strpos( $path_to_scan, 'wp-content/plugins' ) ) {
833
  // Get contents list.
834
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
835
 
@@ -840,7 +816,7 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
840
  $this->plugin->SetGlobalOption( 'site_content', $site_content );
841
 
842
  // If path to scan is of themes then empty the skip themes array.
843
- } elseif ( false !== strpos( $path_to_scan, 'wp-content/themes' ) ) {
844
  // Get contents list.
845
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
846
 
@@ -965,15 +941,17 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
965
  * @return bool
966
  */
967
  public function dir_left_to_scan( $scan_directories ) {
968
- // Return false if $scan_directories is empty.
969
  if ( empty( $scan_directories ) ) {
970
  return false;
971
  }
972
 
973
  // If multisite then remove all the subsites uploads of multisite from scan directories.
974
  if ( is_multisite() ) {
 
 
 
975
  foreach ( $scan_directories as $index => $dir ) {
976
- if ( false !== strpos( $dir, 'wp-content/uploads/sites' ) ) {
977
  unset( $scan_directories[ $index ] );
978
  }
979
  }
@@ -1053,4 +1031,16 @@ class WSAL_Sensors_FileChanges extends WSAL_AbstractSensor {
1053
  }
1054
  }
1055
  }
 
 
 
 
 
 
 
 
 
 
 
 
1056
  }
87
  */
88
  private $scan_limit_file = false;
89
 
90
+ /**
91
+ * WP uploads directory.
92
+ *
93
+ * @var array
94
+ */
95
+ private $uploads_dir = array();
96
+
97
  /**
98
  * Class constants.
99
  */
153
  * Method: Load file detection settings.
154
  */
155
  public function load_file_change_settings() {
156
+ $default_scan_dirs = array_keys( $this->plugin->settings->get_server_directories( 'display' ) );
 
 
 
 
157
 
158
  // Load file detection settings.
159
  $this->scan_settings = array(
264
  // Set the options name for file list.
265
  $file_list = "local_files_$next_to_scan";
266
 
 
 
 
 
267
  // Server directories.
268
+ $server_dirs = $this->plugin->settings->get_server_directories();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  // Get directory path to scan.
271
  $path_to_scan = $server_dirs[ $next_to_scan ];
403
  }
404
 
405
  // Created file event.
406
+ $this->plugin->alerts->Trigger(
407
+ 6029,
408
+ array(
409
+ 'FileLocation' => $file,
410
+ 'FileHash' => $file_hash,
411
+ 'CurrentUserID' => '0',
412
+ )
413
+ );
414
  }
415
  }
416
 
440
  }
441
 
442
  // Removed file event.
443
+ $this->plugin->alerts->Trigger(
444
+ 6030,
445
+ array(
446
+ 'FileLocation' => $file,
447
+ 'FileHash' => $file_hash,
448
+ 'CurrentUserID' => '0',
449
+ )
450
+ );
451
  }
452
  }
453
 
455
  if ( count( $files_changed ) > 0 ) {
456
  // Log the alert.
457
  foreach ( $files_changed as $file => $file_hash ) {
458
+ $this->plugin->alerts->Trigger(
459
+ 6028,
460
+ array(
461
+ 'FileLocation' => $file,
462
+ 'FileHash' => $file_hash,
463
+ 'CurrentUserID' => '0',
464
+ )
465
+ );
466
  }
467
  }
468
 
469
  // Check for files limit alert.
470
  if ( $this->scan_limit_file ) {
471
+ $this->plugin->alerts->Trigger(
472
+ 6032,
473
+ array(
474
+ 'CurrentUserID' => '0',
475
+ )
476
+ );
477
  }
478
 
479
  /**
506
  $this->plugin->SetGlobalOption( 'last-scanned', 'root' );
507
 
508
  // Scan started alert.
509
+ $this->plugin->alerts->Trigger(
510
+ 6033,
511
+ array(
512
+ 'CurrentUserID' => '0',
513
+ 'ScanStatus' => 'started',
514
+ )
515
+ );
516
  } elseif ( 6 === $next_to_scan ) {
517
  $this->plugin->SetGlobalOption( 'last-scanned', $next_to_scan );
518
 
519
  // Scan stopped.
520
+ $this->plugin->alerts->Trigger(
521
+ 6033,
522
+ array(
523
+ 'CurrentUserID' => '0',
524
+ 'ScanStatus' => 'stopped',
525
+ )
526
+ );
527
  } else {
528
  $this->plugin->SetGlobalOption( 'last-scanned', $next_to_scan );
529
  }
560
  $file_size_limit = $this->scan_settings['file_size_limit']; // Get file size limit.
561
  $file_size_limit = $file_size_limit * 1048576; // Calculate file size limit in bytes; 1MB = 1048576 bytes.
562
 
563
+ $uploads_dir = $this->plugin->settings->get_server_directory( $this->get_uploads_dir_path() );
564
+ $mu_uploads_dir = $uploads_dir . '/sites'; // Multisite uploads directory.
565
+
566
  // Scan the directory for files.
567
  while ( false !== ( $item = @readdir( $dir_handle ) ) ) {
568
  // Ignore `.` and `..` from directory.
575
  continue;
576
  }
577
 
578
+ // Ignore .git, .svn, & node_modules from scan.
 
 
 
 
 
 
 
 
 
 
 
 
579
  if ( false !== strpos( $item, '.git' ) || false !== strpos( $item, '.svn' ) || false !== strpos( $item, 'node_modules' ) ) {
580
  continue;
581
  }
590
  $absolute_name = $dir_path . $item; // Complete file path w.r.t. ABSPATH.
591
  }
592
 
593
+ // If we're on root then ignore `wp-admin`, `wp-content` & `wp-includes`.
594
+ if ( empty( $path ) && ( false !== strpos( $absolute_name, 'wp-admin' ) || false !== strpos( $absolute_name, WP_CONTENT_DIR ) || false !== strpos( $absolute_name, WPINC ) ) ) {
595
+ continue;
596
+ }
597
+
598
  // Check for directory.
599
  if ( is_dir( $absolute_name ) ) {
600
  /**
620
  * Check if `wp-content/uploads/sites` is present in the
621
  * relative name of the directory & it is allowed to scan.
622
  */
623
+ if ( false !== strpos( $relative_name, $mu_uploads_dir ) && in_array( $mu_uploads_dir, $directories, true ) ) {
 
 
 
624
  $files = array_merge( $files, $this->scan_path( $relative_name ) );
625
+ } elseif ( false !== strpos( $relative_name, $mu_uploads_dir ) && ! in_array( $mu_uploads_dir, $directories, true ) ) {
 
 
 
626
  // If `wp-content/uploads/sites` is not allowed to scan then skip the loop.
627
  continue;
628
  } else {
663
  $files[ $absolute_name ] = @md5_file( $absolute_name ); // File hash.
664
  } else {
665
  // File size is more than the limit.
666
+ $this->plugin->alerts->Trigger(
667
+ 6031,
668
+ array(
669
+ 'FileLocation' => $absolute_name,
670
+ 'CurrentUserID' => '0',
671
+ )
672
+ );
673
 
674
  // File data.
675
  $files[ $absolute_name ] = '';
704
  */
705
  public function filter_scan_files( $scan_files, $path_to_scan ) {
706
  // If the path to scan is of plugins.
707
+ if ( false !== strpos( $path_to_scan, $this->plugin->settings->get_server_directory( WP_PLUGIN_DIR ) ) ) {
 
 
708
  // Filter plugin files.
709
  $scan_files = $this->filter_excluded_scan_files( $scan_files, 'plugins' );
710
+ } elseif ( false !== strpos( $path_to_scan, $this->plugin->settings->get_server_directory( get_theme_root() ) ) ) { // And if the path to scan is of themes then.
 
 
711
  // Filter theme files.
712
  $scan_files = $this->filter_excluded_scan_files( $scan_files, 'themes' );
713
  } elseif (
714
+ false !== strpos( $path_to_scan, 'wp-admin' ) // WP Admin.
715
+ || false !== strpos( $path_to_scan, WPINC ) // WP Includes.
716
  ) {
717
  // Get `site_content` option.
718
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
740
  * @return array
741
  */
742
  public function filter_excluded_scan_files( $scan_files, $excluded_type ) {
743
+ if ( empty( $scan_files ) ) {
 
744
  return $scan_files;
745
  }
746
 
755
  // An array of files to exclude from scan files array.
756
  $files_to_exclude = array();
757
 
758
+ // Type of content to skip.
759
+ $skip_type = 'skip_' . $excluded_type; // Possitble values: `plugins` or `themes`.
760
+
761
  if (
762
+ in_array( $excluded_type, array( 'plugins', 'themes' ), true ) // Only two skip types are allowed.
763
+ && isset( $excluded_contents->$skip_type ) // Skip type array exists.
764
+ && is_array( $excluded_contents->$skip_type ) // Skip type is array.
765
+ && ! empty( $excluded_contents->$skip_type ) // And is not empty.
766
  ) {
767
  // Go through each plugin to be skipped.
768
+ foreach ( $excluded_contents->$skip_type as $content ) {
769
  // Path of plugin to search in stored files.
770
+ $search_path = '/' . $excluded_type . '/' . $content;
771
 
772
  // Get array of files to exclude of plugins from scan files array.
773
  foreach ( $files as $file ) {
776
  }
777
  }
778
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  }
780
 
781
  // If there are files to be excluded then.
788
  }
789
  }
790
  }
791
+
792
  return $scan_files;
793
  }
794
 
805
  }
806
 
807
  // If path to scan is of plugins then empty the skip plugins array.
808
+ if ( false !== strpos( $path_to_scan, $this->plugin->settings->get_server_directory( WP_PLUGIN_DIR ) ) ) {
809
  // Get contents list.
810
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
811
 
816
  $this->plugin->SetGlobalOption( 'site_content', $site_content );
817
 
818
  // If path to scan is of themes then empty the skip themes array.
819
+ } elseif ( false !== strpos( $path_to_scan, $this->plugin->settings->get_server_directory( get_theme_root() ) ) ) {
820
  // Get contents list.
821
  $site_content = $this->plugin->GetGlobalOption( 'site_content', false );
822
 
941
  * @return bool
942
  */
943
  public function dir_left_to_scan( $scan_directories ) {
 
944
  if ( empty( $scan_directories ) ) {
945
  return false;
946
  }
947
 
948
  // If multisite then remove all the subsites uploads of multisite from scan directories.
949
  if ( is_multisite() ) {
950
+ $uploads_dir = $this->plugin->settings->get_server_directory( $this->get_uploads_dir_path() );
951
+ $mu_uploads_dir = $uploads_dir . '/sites'; // Multisite uploads directory.
952
+
953
  foreach ( $scan_directories as $index => $dir ) {
954
+ if ( false !== strpos( $dir, $mu_uploads_dir ) ) {
955
  unset( $scan_directories[ $index ] );
956
  }
957
  }
1031
  }
1032
  }
1033
  }
1034
+
1035
+ /**
1036
+ * Returns the path of WP uploads directory.
1037
+ *
1038
+ * @return string
1039
+ */
1040
+ private function get_uploads_dir_path() {
1041
+ if ( ! isset( $this->uploads_dir['basedir'] ) ) {
1042
+ $this->uploads_dir = wp_upload_dir(); // Get WP uploads directory.
1043
+ }
1044
+ return $this->uploads_dir['basedir'];
1045
+ }
1046
  }
classes/Sensors/MetaData.php CHANGED
@@ -80,6 +80,18 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
80
  protected function CanLogMetaKey( $object_id, $meta_key ) {
81
  // Check if excluded meta key or starts with _.
82
  if ( '_' === substr( $meta_key, 0, 1 ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
83
  return false;
84
  } elseif ( $this->IsExcludedCustomFields( $meta_key ) ) {
85
  return false;
@@ -97,9 +109,11 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
97
  */
98
  public function IsExcludedCustomFields( $custom ) {
99
  $custom_fields = $this->plugin->settings->GetExcludedMonitoringCustom();
 
100
  if ( in_array( $custom, $custom_fields ) ) {
101
  return true;
102
  }
 
103
  foreach ( $custom_fields as $field ) {
104
  if ( false !== strpos( $field, '*' ) ) {
105
  // Wildcard str[any_character] when you enter (str*).
@@ -109,6 +123,7 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
109
  return true;
110
  }
111
  }
 
112
  // Wildcard [any_character]str when you enter (*str).
113
  if ( '*' === substr( $field, 0, 1 ) ) {
114
  $field = ltrim( $field, '*' );
@@ -164,7 +179,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
164
  if ( $log_meta_event ) {
165
  $editor_link = $this->GetEditorLink( $post );
166
  $this->plugin->alerts->Trigger(
167
- 2053, array(
 
168
  'PostID' => $object_id,
169
  'PostTitle' => $post->post_title,
170
  'PostStatus' => $post->post_status,
@@ -258,7 +274,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
258
  if ( $log_meta_event && $this->old_meta[ $meta_id ]->key !== $meta_key ) {
259
  $editor_link = $this->GetEditorLink( $post );
260
  $this->plugin->alerts->Trigger(
261
- 2062, array(
 
262
  'PostID' => $object_id,
263
  'PostTitle' => $post->post_title,
264
  'PostStatus' => $post->post_status,
@@ -276,7 +293,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
276
  } elseif ( $log_meta_event && $this->old_meta[ $meta_id ]->val !== $meta_value ) { // Check change in meta value.
277
  $editor_link = $this->GetEditorLink( $post );
278
  $this->plugin->alerts->Trigger(
279
- 2054, array(
 
280
  'PostID' => $object_id,
281
  'PostTitle' => $post->post_title,
282
  'PostStatus' => $post->post_status,
@@ -348,7 +366,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
348
  }
349
 
350
  $this->plugin->alerts->Trigger(
351
- 2055, array(
 
352
  'PostID' => $object_id,
353
  'PostTitle' => $post->post_title,
354
  'PostStatus' => $post->post_status,
@@ -489,7 +508,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
489
  case 'first_name':
490
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
491
  $this->plugin->alerts->Trigger(
492
- 4017, array(
 
493
  'TargetUsername' => $user->user_login,
494
  'new_firstname' => $meta_value,
495
  'old_firstname' => $this->old_meta[ $meta_id ]->val,
@@ -501,7 +521,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
501
  case 'last_name':
502
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
503
  $this->plugin->alerts->Trigger(
504
- 4018, array(
 
505
  'TargetUsername' => $user->user_login,
506
  'new_lastname' => $meta_value,
507
  'old_lastname' => $this->old_meta[ $meta_id ]->val,
@@ -513,7 +534,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
513
  case 'nickname':
514
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
515
  $this->plugin->alerts->Trigger(
516
- 4019, array(
 
517
  'TargetUsername' => $user->user_login,
518
  'new_nickname' => $meta_value,
519
  'old_nickname' => $this->old_meta[ $meta_id ]->val,
@@ -559,20 +581,20 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
559
  * @return boolean
560
  */
561
  private function is_woocommerce_user_meta( $meta_key ) {
562
- // Check for WordPress user profile keys.
563
- if ( false === strpos( $meta_key, 'shipping_' ) || false === strpos( $meta_key, 'billing_' ) ) {
564
- return false;
565
- }
566
 
567
- // Remove the prefix to avoid redundancy in the meta keys.
568
- $address_key = str_replace( array( 'shipping_', 'billing_' ), '', $meta_key );
569
 
570
- // WC address meta keys without prefix.
571
- $meta_keys = array( 'first_name', 'last_name', 'company', 'country', 'address_1', 'address_2', 'city', 'state', 'postcode', 'phone', 'email' );
572
-
573
- if ( in_array( $address_key, $meta_keys, true ) ) {
574
- return true;
575
  }
 
 
576
  return false;
577
  }
578
  }
80
  protected function CanLogMetaKey( $object_id, $meta_key ) {
81
  // Check if excluded meta key or starts with _.
82
  if ( '_' === substr( $meta_key, 0, 1 ) ) {
83
+ /**
84
+ * List of hidden keys allowed to log.
85
+ *
86
+ * @since 3.4.1
87
+ */
88
+ $log_hidden_keys = apply_filters( 'wsal_log_hidden_meta_keys', array() );
89
+
90
+ // If the meta key is allowed to log then return true.
91
+ if ( in_array( $meta_key, $log_hidden_keys, true ) ) {
92
+ return true;
93
+ }
94
+
95
  return false;
96
  } elseif ( $this->IsExcludedCustomFields( $meta_key ) ) {
97
  return false;
109
  */
110
  public function IsExcludedCustomFields( $custom ) {
111
  $custom_fields = $this->plugin->settings->GetExcludedMonitoringCustom();
112
+
113
  if ( in_array( $custom, $custom_fields ) ) {
114
  return true;
115
  }
116
+
117
  foreach ( $custom_fields as $field ) {
118
  if ( false !== strpos( $field, '*' ) ) {
119
  // Wildcard str[any_character] when you enter (str*).
123
  return true;
124
  }
125
  }
126
+
127
  // Wildcard [any_character]str when you enter (*str).
128
  if ( '*' === substr( $field, 0, 1 ) ) {
129
  $field = ltrim( $field, '*' );
179
  if ( $log_meta_event ) {
180
  $editor_link = $this->GetEditorLink( $post );
181
  $this->plugin->alerts->Trigger(
182
+ 2053,
183
+ array(
184
  'PostID' => $object_id,
185
  'PostTitle' => $post->post_title,
186
  'PostStatus' => $post->post_status,
274
  if ( $log_meta_event && $this->old_meta[ $meta_id ]->key !== $meta_key ) {
275
  $editor_link = $this->GetEditorLink( $post );
276
  $this->plugin->alerts->Trigger(
277
+ 2062,
278
+ array(
279
  'PostID' => $object_id,
280
  'PostTitle' => $post->post_title,
281
  'PostStatus' => $post->post_status,
293
  } elseif ( $log_meta_event && $this->old_meta[ $meta_id ]->val !== $meta_value ) { // Check change in meta value.
294
  $editor_link = $this->GetEditorLink( $post );
295
  $this->plugin->alerts->Trigger(
296
+ 2054,
297
+ array(
298
  'PostID' => $object_id,
299
  'PostTitle' => $post->post_title,
300
  'PostStatus' => $post->post_status,
366
  }
367
 
368
  $this->plugin->alerts->Trigger(
369
+ 2055,
370
+ array(
371
  'PostID' => $object_id,
372
  'PostTitle' => $post->post_title,
373
  'PostStatus' => $post->post_status,
508
  case 'first_name':
509
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
510
  $this->plugin->alerts->Trigger(
511
+ 4017,
512
+ array(
513
  'TargetUsername' => $user->user_login,
514
  'new_firstname' => $meta_value,
515
  'old_firstname' => $this->old_meta[ $meta_id ]->val,
521
  case 'last_name':
522
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
523
  $this->plugin->alerts->Trigger(
524
+ 4018,
525
+ array(
526
  'TargetUsername' => $user->user_login,
527
  'new_lastname' => $meta_value,
528
  'old_lastname' => $this->old_meta[ $meta_id ]->val,
534
  case 'nickname':
535
  if ( $this->old_meta[ $meta_id ]->val !== $meta_value ) {
536
  $this->plugin->alerts->Trigger(
537
+ 4019,
538
+ array(
539
  'TargetUsername' => $user->user_login,
540
  'new_nickname' => $meta_value,
541
  'old_nickname' => $this->old_meta[ $meta_id ]->val,
581
  * @return boolean
582
  */
583
  private function is_woocommerce_user_meta( $meta_key ) {
584
+ // Check for WooCommerce user profile keys.
585
+ if ( false !== strpos( $meta_key, 'shipping_' ) || false !== strpos( $meta_key, 'billing_' ) ) {
586
+ // Remove the prefix to avoid redundancy in the meta keys.
587
+ $address_key = str_replace( array( 'shipping_', 'billing_' ), '', $meta_key );
588
 
589
+ // WC address meta keys without prefix.
590
+ $meta_keys = array( 'first_name', 'last_name', 'company', 'country', 'address_1', 'address_2', 'city', 'state', 'postcode', 'phone', 'email' );
591
 
592
+ if ( in_array( $address_key, $meta_keys, true ) ) {
593
+ return true;
594
+ }
 
 
595
  }
596
+
597
+ // Meta key does not belong to WooCommerce.
598
  return false;
599
  }
600
  }
classes/Sensors/PluginsThemes.php CHANGED
@@ -201,7 +201,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
201
 
202
  $plugin_path = plugin_dir_path( WP_PLUGIN_DIR . '/' . $plugin_path[0] );
203
  $this->plugin->alerts->Trigger(
204
- 5000, array(
 
205
  'Plugin' => (object) array(
206
  'Name' => $plugin['Name'],
207
  'PluginURI' => $plugin['PluginURI'],
@@ -237,7 +238,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
237
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
238
  $plugin_data = get_plugin_data( $plugin_file, false, true );
239
  $this->plugin->alerts->Trigger(
240
- 5001, array(
 
241
  'PluginFile' => $plugin_file,
242
  'PluginData' => (object) array(
243
  'Name' => $plugin_data['Name'],
@@ -254,7 +256,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
254
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
255
  $plugin_data = get_plugin_data( $plugin_file, false, true );
256
  $this->plugin->alerts->Trigger(
257
- 5001, array(
 
258
  'PluginFile' => $plugin_file,
259
  'PluginData' => (object) array(
260
  'Name' => $plugin_data['Name'],
@@ -292,7 +295,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
292
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
293
  $plugin_data = get_plugin_data( $plugin_file, false, true );
294
  $this->plugin->alerts->Trigger(
295
- 5002, array(
 
296
  'PluginFile' => $plugin_file,
297
  'PluginData' => (object) array(
298
  'Name' => $plugin_data['Name'],
@@ -309,7 +313,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
309
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
310
  $plugin_data = get_plugin_data( $plugin_file, false, true );
311
  $this->plugin->alerts->Trigger(
312
- 5002, array(
 
313
  'PluginFile' => $plugin_file,
314
  'PluginData' => (object) array(
315
  'Name' => $plugin_data['Name'],
@@ -338,7 +343,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
338
  $plugin_name = ucwords( $plugin_name );
339
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
340
  $this->plugin->alerts->Trigger(
341
- 5003, array(
 
342
  'PluginFile' => $plugin_file,
343
  'PluginData' => (object) array(
344
  'Name' => $plugin_name,
@@ -357,7 +363,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
357
  $plugin_name = str_replace( array( '_', '-', ' ' ), ' ', $plugin_name );
358
  $plugin_name = ucwords( $plugin_name );
359
  $this->plugin->alerts->Trigger(
360
- 5003, array(
 
361
  'PluginFile' => $plugin_file,
362
  'PluginData' => (object) array(
363
  'Name' => $plugin_name,
@@ -404,7 +411,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
404
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
405
  $plugin_data = get_plugin_data( $plugin_file, false, true );
406
  $this->plugin->alerts->Trigger(
407
- 5004, array(
 
408
  'PluginFile' => $plugin_file,
409
  'PluginData' => (object) array(
410
  'Name' => $plugin_data['Name'],
@@ -441,7 +449,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
441
  foreach ( $themes as $theme_name ) {
442
  $theme = wp_get_theme( $theme_name );
443
  $this->plugin->alerts->Trigger(
444
- 5031, array(
 
445
  'Theme' => (object) array(
446
  'Name' => $theme->Name,
447
  'ThemeURI' => $theme->ThemeURI,
@@ -464,7 +473,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
464
  $themes = array_diff( wp_get_themes(), $this->old_themes );
465
  foreach ( $themes as $name => $theme ) {
466
  $this->plugin->alerts->Trigger(
467
- 5005, array(
 
468
  'Theme' => (object) array(
469
  'Name' => $theme->Name,
470
  'ThemeURI' => $theme->ThemeURI,
@@ -484,7 +494,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
484
  if ( in_array( $action, array( 'delete-theme' ) ) && current_user_can( 'install_themes' ) ) {
485
  foreach ( $this->GetRemovedThemes() as $index => $theme ) {
486
  $this->plugin->alerts->Trigger(
487
- 5007, array(
 
488
  'Theme' => (object) array(
489
  'Name' => $theme->Name,
490
  'ThemeURI' => $theme->ThemeURI,
@@ -529,7 +540,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
529
  return;
530
  }
531
  $this->plugin->alerts->Trigger(
532
- 5006, array(
 
533
  'Theme' => (object) array(
534
  'Name' => $theme->Name,
535
  'ThemeURI' => $theme->ThemeURI,
@@ -553,6 +565,16 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
553
  return;
554
  }
555
 
 
 
 
 
 
 
 
 
 
 
556
  // Filter $_REQUEST array for security.
557
  $get_array = filter_input_array( INPUT_GET );
558
  $post_array = filter_input_array( INPUT_POST );
@@ -575,7 +597,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
575
  // If the plugin modify the post.
576
  if ( false !== strpos( $get_array['action'], 'edit' ) ) {
577
  $this->plugin->alerts->Trigger(
578
- 2106, array(
 
579
  'PostID' => $post->ID,
580
  'PostType' => $post->post_type,
581
  'PostTitle' => $post->post_title,
@@ -586,7 +609,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
586
  );
587
  } else {
588
  $this->plugin->alerts->Trigger(
589
- 5019, array(
 
590
  'PostID' => $post->ID,
591
  'PostType' => $post->post_type,
592
  'PostTitle' => $post->post_title,
@@ -607,7 +631,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
607
  if ( false !== strpos( $post_array['action'], 'edit' ) ) {
608
  $editor_link = $this->GetEditorLink( $post );
609
  $this->plugin->alerts->Trigger(
610
- 2106, array(
 
611
  'PostID' => $post->ID,
612
  'PostType' => $post->post_type,
613
  'PostTitle' => $post->post_title,
@@ -626,7 +651,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
626
  } else {
627
  $editor_link = $this->GetEditorLink( $post );
628
  $this->plugin->alerts->Trigger(
629
- 5019, array(
 
630
  'PostID' => $post->ID,
631
  'PostType' => $post->post_type,
632
  'PostTitle' => $post->post_title,
@@ -654,7 +680,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
654
  if ( ! in_array( $post->post_type, $this->plugin->alerts->ignored_cpts, true )
655
  || ! empty( $post->post_title ) ) {
656
  $this->plugin->alerts->Trigger(
657
- 5025, array(
 
658
  'PostID' => $post->ID,
659
  'PostType' => $post->post_type,
660
  'PostTitle' => $post->post_title,
@@ -669,7 +696,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
669
  if ( ! in_array( $post->post_type, $this->plugin->alerts->ignored_cpts, true )
670
  || ! empty( $post->post_title ) ) {
671
  $this->plugin->alerts->Trigger(
672
- 5025, array(
 
673
  'PostID' => $post->ID,
674
  'PostType' => $post->post_type,
675
  'PostTitle' => $post->post_title,
@@ -720,7 +748,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
720
  // Check if theme exists.
721
  if ( $theme_obj->exists() ) {
722
  $this->plugin->alerts->Trigger(
723
- 5005, array(
 
724
  'Theme' => (object) array(
725
  'Name' => $theme_obj->Name,
726
  'ThemeURI' => $theme_obj->ThemeURI,
@@ -749,7 +778,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
749
 
750
  $plugin_path = plugin_dir_path( WP_PLUGIN_DIR . '/' . $plugin_slug );
751
  $this->plugin->alerts->Trigger(
752
- 5000, array(
 
753
  'Plugin' => (object) array(
754
  'Name' => $plugin['Name'],
755
  'PluginURI' => $plugin['PluginURI'],
@@ -798,7 +828,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
798
 
799
  if ( ! empty( $plugin_filename ) && in_array( $plugin_filename, $wp_plugins, true ) ) {
800
  $this->plugin->alerts->Trigger(
801
- 5003, array(
 
802
  'PluginFile' => $plugin_filename,
803
  'PluginData' => (object) array(
804
  'Name' => $plugin_name,
@@ -851,7 +882,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
851
  foreach ( $themes as $index => $theme ) {
852
  if ( ! empty( $theme ) && $theme instanceof WP_Theme && in_array( $theme->Name, $wp_themes, true ) ) {
853
  $this->plugin->alerts->Trigger(
854
- 5007, array(
 
855
  'Theme' => (object) array(
856
  'Name' => $theme->Name,
857
  'ThemeURI' => $theme->ThemeURI,
@@ -911,7 +943,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
911
  $plugin = WP_PLUGIN_DIR . '/' . $plugin;
912
  $plugin_data = get_plugin_data( $plugin, false, true );
913
  $this->plugin->alerts->Trigger(
914
- $event, array(
 
915
  'PluginFile' => $plugin,
916
  'PluginData' => (object) array(
917
  'Name' => $plugin_data['Name'],
@@ -932,7 +965,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
932
  $plugin = WP_PLUGIN_DIR . '/' . $plugin;
933
  $plugin_data = get_plugin_data( $plugin, false, true );
934
  $this->plugin->alerts->Trigger(
935
- 5001, array(
 
936
  'PluginFile' => $plugin,
937
  'PluginData' => (object) array(
938
  'Name' => $plugin_data['Name'],
@@ -981,7 +1015,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
981
 
982
  if ( ! empty( $theme ) && $theme instanceof WP_Theme && in_array( $theme->stylesheet, $site_themes, true ) ) {
983
  $this->plugin->alerts->Trigger(
984
- 5031, array(
 
985
  'Theme' => (object) array(
986
  'Name' => $theme->Name,
987
  'ThemeURI' => $theme->ThemeURI,
@@ -1018,7 +1053,8 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
1018
  $plugin_data = get_plugin_data( $plugin_file, false, true );
1019
 
1020
  $this->plugin->alerts->Trigger(
1021
- 5004, array(
 
1022
  'PluginFile' => $plugin_file,
1023
  'PluginData' => (object) array(
1024
  'Name' => $plugin_data['Name'],
201
 
202
  $plugin_path = plugin_dir_path( WP_PLUGIN_DIR . '/' . $plugin_path[0] );
203
  $this->plugin->alerts->Trigger(
204
+ 5000,
205
+ array(
206
  'Plugin' => (object) array(
207
  'Name' => $plugin['Name'],
208
  'PluginURI' => $plugin['PluginURI'],
238
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
239
  $plugin_data = get_plugin_data( $plugin_file, false, true );
240
  $this->plugin->alerts->Trigger(
241
+ 5001,
242
+ array(
243
  'PluginFile' => $plugin_file,
244
  'PluginData' => (object) array(
245
  'Name' => $plugin_data['Name'],
256
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
257
  $plugin_data = get_plugin_data( $plugin_file, false, true );
258
  $this->plugin->alerts->Trigger(
259
+ 5001,
260
+ array(
261
  'PluginFile' => $plugin_file,
262
  'PluginData' => (object) array(
263
  'Name' => $plugin_data['Name'],
295
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
296
  $plugin_data = get_plugin_data( $plugin_file, false, true );
297
  $this->plugin->alerts->Trigger(
298
+ 5002,
299
+ array(
300
  'PluginFile' => $plugin_file,
301
  'PluginData' => (object) array(
302
  'Name' => $plugin_data['Name'],
313
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
314
  $plugin_data = get_plugin_data( $plugin_file, false, true );
315
  $this->plugin->alerts->Trigger(
316
+ 5002,
317
+ array(
318
  'PluginFile' => $plugin_file,
319
  'PluginData' => (object) array(
320
  'Name' => $plugin_data['Name'],
343
  $plugin_name = ucwords( $plugin_name );
344
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
345
  $this->plugin->alerts->Trigger(
346
+ 5003,
347
+ array(
348
  'PluginFile' => $plugin_file,
349
  'PluginData' => (object) array(
350
  'Name' => $plugin_name,
363
  $plugin_name = str_replace( array( '_', '-', ' ' ), ' ', $plugin_name );
364
  $plugin_name = ucwords( $plugin_name );
365
  $this->plugin->alerts->Trigger(
366
+ 5003,
367
+ array(
368
  'PluginFile' => $plugin_file,
369
  'PluginData' => (object) array(
370
  'Name' => $plugin_name,
411
  $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
412
  $plugin_data = get_plugin_data( $plugin_file, false, true );
413
  $this->plugin->alerts->Trigger(
414
+ 5004,
415
+ array(
416
  'PluginFile' => $plugin_file,
417
  'PluginData' => (object) array(
418
  'Name' => $plugin_data['Name'],
449
  foreach ( $themes as $theme_name ) {
450
  $theme = wp_get_theme( $theme_name );
451
  $this->plugin->alerts->Trigger(
452
+ 5031,
453
+ array(
454
  'Theme' => (object) array(
455
  'Name' => $theme->Name,
456
  'ThemeURI' => $theme->ThemeURI,
473
  $themes = array_diff( wp_get_themes(), $this->old_themes );
474
  foreach ( $themes as $name => $theme ) {
475
  $this->plugin->alerts->Trigger(
476
+ 5005,
477
+ array(
478
  'Theme' => (object) array(
479
  'Name' => $theme->Name,
480
  'ThemeURI' => $theme->ThemeURI,
494
  if ( in_array( $action, array( 'delete-theme' ) ) && current_user_can( 'install_themes' ) ) {
495
  foreach ( $this->GetRemovedThemes() as $index => $theme ) {
496
  $this->plugin->alerts->Trigger(
497
+ 5007,
498
+ array(
499
  'Theme' => (object) array(
500
  'Name' => $theme->Name,
501
  'ThemeURI' => $theme->ThemeURI,
540
  return;
541
  }
542
  $this->plugin->alerts->Trigger(
543
+ 5006,
544
+ array(
545
  'Theme' => (object) array(
546
  'Name' => $theme->Name,
547
  'ThemeURI' => $theme->ThemeURI,
565
  return;
566
  }
567
 
568
+ // Ignore if the request is coming from post editor.
569
+ if ( isset( $_REQUEST['_wp_http_referer'] ) ) { // phpcs:ignore
570
+ $referrer = esc_url_raw( wp_unslash( $_REQUEST['_wp_http_referer'] ) ); // phpcs:ignore
571
+ $parsed_url = wp_parse_url( $referrer );
572
+
573
+ if ( isset( $parsed_url['path'] ) && 'post' === basename( $parsed_url['path'], '.php' ) ) {
574
+ return;
575
+ }
576
+ }
577
+
578
  // Filter $_REQUEST array for security.
579
  $get_array = filter_input_array( INPUT_GET );
580
  $post_array = filter_input_array( INPUT_POST );
597
  // If the plugin modify the post.
598
  if ( false !== strpos( $get_array['action'], 'edit' ) ) {
599
  $this->plugin->alerts->Trigger(
600
+ 2106,
601
+ array(
602
  'PostID' => $post->ID,
603
  'PostType' => $post->post_type,
604
  'PostTitle' => $post->post_title,
609
  );
610
  } else {
611
  $this->plugin->alerts->Trigger(
612
+ 5019,
613
+ array(
614
  'PostID' => $post->ID,
615
  'PostType' => $post->post_type,
616
  'PostTitle' => $post->post_title,
631
  if ( false !== strpos( $post_array['action'], 'edit' ) ) {
632
  $editor_link = $this->GetEditorLink( $post );
633
  $this->plugin->alerts->Trigger(
634
+ 2106,
635
+ array(
636
  'PostID' => $post->ID,
637
  'PostType' => $post->post_type,
638
  'PostTitle' => $post->post_title,
651
  } else {
652
  $editor_link = $this->GetEditorLink( $post );
653
  $this->plugin->alerts->Trigger(
654
+ 5019,
655
+ array(
656
  'PostID' => $post->ID,
657
  'PostType' => $post->post_type,
658
  'PostTitle' => $post->post_title,
680
  if ( ! in_array( $post->post_type, $this->plugin->alerts->ignored_cpts, true )
681
  || ! empty( $post->post_title ) ) {
682
  $this->plugin->alerts->Trigger(
683
+ 5025,
684
+ array(
685
  'PostID' => $post->ID,
686
  'PostType' => $post->post_type,
687
  'PostTitle' => $post->post_title,
696
  if ( ! in_array( $post->post_type, $this->plugin->alerts->ignored_cpts, true )
697
  || ! empty( $post->post_title ) ) {
698
  $this->plugin->alerts->Trigger(
699
+ 5025,
700
+ array(
701
  'PostID' => $post->ID,
702
  'PostType' => $post->post_type,
703
  'PostTitle' => $post->post_title,
748
  // Check if theme exists.
749
  if ( $theme_obj->exists() ) {
750
  $this->plugin->alerts->Trigger(
751
+ 5005,
752
+ array(
753
  'Theme' => (object) array(
754
  'Name' => $theme_obj->Name,
755
  'ThemeURI' => $theme_obj->ThemeURI,
778
 
779
  $plugin_path = plugin_dir_path( WP_PLUGIN_DIR . '/' . $plugin_slug );
780
  $this->plugin->alerts->Trigger(
781
+ 5000,
782
+ array(
783
  'Plugin' => (object) array(
784
  'Name' => $plugin['Name'],
785
  'PluginURI' => $plugin['PluginURI'],
828
 
829
  if ( ! empty( $plugin_filename ) && in_array( $plugin_filename, $wp_plugins, true ) ) {
830
  $this->plugin->alerts->Trigger(
831
+ 5003,
832
+ array(
833
  'PluginFile' => $plugin_filename,
834
  'PluginData' => (object) array(
835
  'Name' => $plugin_name,
882
  foreach ( $themes as $index => $theme ) {
883
  if ( ! empty( $theme ) && $theme instanceof WP_Theme && in_array( $theme->Name, $wp_themes, true ) ) {
884
  $this->plugin->alerts->Trigger(
885
+ 5007,
886
+ array(
887
  'Theme' => (object) array(
888
  'Name' => $theme->Name,
889
  'ThemeURI' => $theme->ThemeURI,
943
  $plugin = WP_PLUGIN_DIR . '/' . $plugin;
944
  $plugin_data = get_plugin_data( $plugin, false, true );
945
  $this->plugin->alerts->Trigger(
946
+ $event,
947
+ array(
948
  'PluginFile' => $plugin,
949
  'PluginData' => (object) array(
950
  'Name' => $plugin_data['Name'],
965
  $plugin = WP_PLUGIN_DIR . '/' . $plugin;
966
  $plugin_data = get_plugin_data( $plugin, false, true );
967
  $this->plugin->alerts->Trigger(
968
+ 5001,
969
+ array(
970
  'PluginFile' => $plugin,
971
  'PluginData' => (object) array(
972
  'Name' => $plugin_data['Name'],
1015
 
1016
  if ( ! empty( $theme ) && $theme instanceof WP_Theme && in_array( $theme->stylesheet, $site_themes, true ) ) {
1017
  $this->plugin->alerts->Trigger(
1018
+ 5031,
1019
+ array(
1020
  'Theme' => (object) array(
1021
  'Name' => $theme->Name,
1022
  'ThemeURI' => $theme->ThemeURI,
1053
  $plugin_data = get_plugin_data( $plugin_file, false, true );
1054
 
1055
  $this->plugin->alerts->Trigger(
1056
+ 5004,
1057
+ array(
1058
  'PluginFile' => $plugin_file,
1059
  'PluginData' => (object) array(
1060
  'Name' => $plugin_data['Name'],
classes/Sensors/UserProfile.php CHANGED
@@ -85,7 +85,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
85
  if ( $old_bbpress_roles !== $new_bbpress_roles ) {
86
  $current_user = wp_get_current_user();
87
  $this->plugin->alerts->Trigger(
88
- 4013, array(
 
89
  'TargetUsername' => $new_userdata->user_login,
90
  'OldRole' => $old_bbpress_roles,
91
  'NewRole' => $new_bbpress_roles,
@@ -100,7 +101,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
100
  $event = get_current_user_id() === $user_id ? 4003 : 4004;
101
  $user_roles = implode( ', ', array_map( array( $this, 'filter_role_names' ), $new_userdata->roles ) );
102
  $this->plugin->alerts->Trigger(
103
- $event, array(
 
104
  'TargetUserID' => $user_id,
105
  'TargetUserData' => (object) array(
106
  'Username' => $new_userdata->user_login,
@@ -114,7 +116,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
114
  if ( $old_userdata->user_email !== $new_userdata->user_email ) {
115
  $event = get_current_user_id() === $user_id ? 4005 : 4006;
116
  $this->plugin->alerts->Trigger(
117
- $event, array(
 
118
  'TargetUserID' => $user_id,
119
  'TargetUsername' => $new_userdata->user_login,
120
  'OldEmail' => $old_userdata->user_email,
@@ -126,7 +129,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
126
  // Alert if display name is changed.
127
  if ( $old_userdata->display_name !== $new_userdata->display_name ) {
128
  $this->plugin->alerts->Trigger(
129
- 4020, array(
 
130
  'TargetUsername' => $new_userdata->user_login,
131
  'old_displayname' => $old_userdata->display_name,
132
  'new_displayname' => $new_userdata->display_name,
@@ -143,8 +147,14 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
143
  * @param array $old_roles - Array of old roles.
144
  */
145
  public function event_user_role_changed( $user_id, $new_role, $old_roles ) {
 
146
  $user = get_userdata( $user_id );
147
 
 
 
 
 
 
148
  // If BBPress plugin is active then check for user roles change.
149
  if ( is_plugin_active( 'bbpress/bbpress.php' ) ) {
150
  // BBPress user roles.
@@ -189,7 +199,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
189
  $user = get_userdata( $user_id );
190
  $role = is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles;
191
  $this->plugin->alerts->TriggerIf(
192
- 4007, array(
 
193
  'TargetUserID' => $user_id,
194
  'TargetUserData' => (object) array(
195
  'Username' => $user->user_login,
@@ -198,7 +209,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
198
  'Email' => $user->user_email,
199
  'Roles' => $role ? $role : 'none',
200
  ),
201
- ), array( $this, 'MustNotContainCreateUser' )
 
202
  );
203
  }
204
 
@@ -215,7 +227,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
215
  $updated = isset( $_GET['updated'] ); // @codingStandardsIgnoreLine
216
  if ( $current_user && ( $user->ID !== $current_user->ID ) && ! $updated ) {
217
  $this->plugin->alerts->Trigger(
218
- 4014, array(
 
219
  'UserChanger' => $current_user->user_login,
220
  'TargetUsername' => $user->user_login,
221
  )
@@ -243,7 +256,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
243
  $user = get_userdata( $user_id );
244
  if ( $user && ! in_array( $user->user_login, $this->old_superadmins, true ) ) {
245
  $this->plugin->alerts->Trigger(
246
- 4008, array(
 
247
  'TargetUserID' => $user_id,
248
  'TargetUsername' => $user->user_login,
249
  )
@@ -264,7 +278,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
264
  $user = get_userdata( $user_id );
265
  if ( $user && in_array( $user->user_login, $this->old_superadmins, true ) ) {
266
  $this->plugin->alerts->Trigger(
267
- 4009, array(
 
268
  'TargetUserID' => $user_id,
269
  'TargetUsername' => $user->user_login,
270
  )
85
  if ( $old_bbpress_roles !== $new_bbpress_roles ) {
86
  $current_user = wp_get_current_user();
87
  $this->plugin->alerts->Trigger(
88
+ 4013,
89
+ array(
90
  'TargetUsername' => $new_userdata->user_login,
91
  'OldRole' => $old_bbpress_roles,
92
  'NewRole' => $new_bbpress_roles,
101
  $event = get_current_user_id() === $user_id ? 4003 : 4004;
102
  $user_roles = implode( ', ', array_map( array( $this, 'filter_role_names' ), $new_userdata->roles ) );
103
  $this->plugin->alerts->Trigger(
104
+ $event,
105
+ array(
106
  'TargetUserID' => $user_id,
107
  'TargetUserData' => (object) array(
108
  'Username' => $new_userdata->user_login,
116
  if ( $old_userdata->user_email !== $new_userdata->user_email ) {
117
  $event = get_current_user_id() === $user_id ? 4005 : 4006;
118
  $this->plugin->alerts->Trigger(
119
+ $event,
120
+ array(
121
  'TargetUserID' => $user_id,
122
  'TargetUsername' => $new_userdata->user_login,
123
  'OldEmail' => $old_userdata->user_email,
129
  // Alert if display name is changed.
130
  if ( $old_userdata->display_name !== $new_userdata->display_name ) {
131
  $this->plugin->alerts->Trigger(
132
+ 4020,
133
+ array(
134
  'TargetUsername' => $new_userdata->user_login,
135
  'old_displayname' => $old_userdata->display_name,
136
  'new_displayname' => $new_userdata->display_name,
147
  * @param array $old_roles - Array of old roles.
148
  */
149
  public function event_user_role_changed( $user_id, $new_role, $old_roles ) {
150
+ // Get WP_User object.
151
  $user = get_userdata( $user_id );
152
 
153
+ // Check if $user is false then return.
154
+ if ( ! $user ) {
155
+ return;
156
+ }
157
+
158
  // If BBPress plugin is active then check for user roles change.
159
  if ( is_plugin_active( 'bbpress/bbpress.php' ) ) {
160
  // BBPress user roles.
199
  $user = get_userdata( $user_id );
200
  $role = is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles;
201
  $this->plugin->alerts->TriggerIf(
202
+ 4007,
203
+ array(
204
  'TargetUserID' => $user_id,
205
  'TargetUserData' => (object) array(
206
  'Username' => $user->user_login,
209
  'Email' => $user->user_email,
210
  'Roles' => $role ? $role : 'none',
211
  ),
212
+ ),
213
+ array( $this, 'MustNotContainCreateUser' )
214
  );
215
  }
216
 
227
  $updated = isset( $_GET['updated'] ); // @codingStandardsIgnoreLine
228
  if ( $current_user && ( $user->ID !== $current_user->ID ) && ! $updated ) {
229
  $this->plugin->alerts->Trigger(
230
+ 4014,
231
+ array(
232
  'UserChanger' => $current_user->user_login,
233
  'TargetUsername' => $user->user_login,
234
  )
256
  $user = get_userdata( $user_id );
257
  if ( $user && ! in_array( $user->user_login, $this->old_superadmins, true ) ) {
258
  $this->plugin->alerts->Trigger(
259
+ 4008,
260
+ array(
261
  'TargetUserID' => $user_id,
262
  'TargetUsername' => $user->user_login,
263
  )
278
  $user = get_userdata( $user_id );
279
  if ( $user && in_array( $user->user_login, $this->old_superadmins, true ) ) {
280
  $this->plugin->alerts->Trigger(
281
+ 4009,
282
+ array(
283
  'TargetUserID' => $user_id,
284
  'TargetUsername' => $user->user_login,
285
  )
classes/Settings.php CHANGED
@@ -1422,6 +1422,15 @@ class WSAL_Settings {
1422
  return 'urls';
1423
  }
1424
 
 
 
 
 
 
 
 
 
 
1425
  // Check if the token matches an IP address.
1426
  if (
1427
  filter_var( $token, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) // Validate IPv4.
@@ -1959,4 +1968,213 @@ class WSAL_Settings {
1959
  public function set_events_type_nav( $nav_type ) {
1960
  $this->_plugin->SetGlobalOption( 'events-nav-type', $nav_type );
1961
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1962
  }
1422
  return 'urls';
1423
  }
1424
 
1425
+ // Check for IP range.
1426
+ if ( strpos( $token, '-' ) !== false ) {
1427
+ $ip_range = $this->get_ipv4_by_range( $token );
1428
+
1429
+ if ( $ip_range && filter_var( $ip_range->lower, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) && filter_var( $ip_range->upper, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { // Validate IPv4.
1430
+ return 'ip';
1431
+ }
1432
+ }
1433
+
1434
  // Check if the token matches an IP address.
1435
  if (
1436
  filter_var( $token, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) // Validate IPv4.
1968
  public function set_events_type_nav( $nav_type ) {
1969
  $this->_plugin->SetGlobalOption( 'events-nav-type', $nav_type );
1970
  }
1971
+
1972
+ /**
1973
+ * Query WSAL Options from DB.
1974
+ *
1975
+ * @return array - WSAL Options array.
1976
+ */
1977
+ public function get_wsal_options() {
1978
+ // Get options transient.
1979
+ $wsal_options = get_transient( 'wsal_options' );
1980
+
1981
+ // If options transient is not set then query and set options.
1982
+ if ( false === $wsal_options ) {
1983
+ // Get raw options from DB.
1984
+ $raw_options = $this->query_wsal_options();
1985
+
1986
+ if ( ! empty( $raw_options ) && is_array( $raw_options ) ) {
1987
+ foreach ( $raw_options as $option ) {
1988
+ if ( ! empty( $option->option_value ) ) {
1989
+ $wsal_options[] = $option;
1990
+ }
1991
+ }
1992
+ }
1993
+
1994
+ // Store the results in a transient.
1995
+ set_transient( 'wsal_options', $wsal_options, DAY_IN_SECONDS );
1996
+ }
1997
+
1998
+ return $wsal_options;
1999
+ }
2000
+
2001
+ /**
2002
+ * Query WSAL Options from DB.
2003
+ *
2004
+ * @return array - Array of options.
2005
+ */
2006
+ public function query_wsal_options() {
2007
+ // Query WSAL options.
2008
+ global $wpdb;
2009
+
2010
+ // Set table name.
2011
+ $options_table = $wpdb->prefix . 'wsal_options';
2012
+
2013
+ // Query the options.
2014
+ return $wpdb->get_results( "SELECT * FROM $options_table" ); // phpcs:ignore
2015
+ }
2016
+
2017
+ /**
2018
+ * Check if IP is in range for IPv4.
2019
+ *
2020
+ * This function takes 2 arguments, an IP address and a "range" in several different formats.
2021
+ *
2022
+ * Network ranges can be specified as:
2023
+ * 1. Wildcard format: 1.2.3.*
2024
+ * 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0
2025
+ * 3. Start-End IP format: 1.2.3.0-1.2.3.255
2026
+ *
2027
+ * The function will return true if the supplied IP is within the range.
2028
+ * Note little validation is done on the range inputs - it expects you to
2029
+ * use one of the above 3 formats.
2030
+ *
2031
+ * @link https://github.com/cloudflarearchive/Cloudflare-Tools/blob/master/cloudflare/ip_in_range.php#L55
2032
+ *
2033
+ * @param string $ip - IP address.
2034
+ * @param string $range - Range of IP address.
2035
+ * @return boolean
2036
+ */
2037
+ public function check_ipv4_in_range( $ip, $range ) {
2038
+ if ( strpos( $range, '/' ) !== false ) {
2039
+ // $range is in IP/NETMASK format.
2040
+ list($range, $netmask) = explode( '/', $range, 2 );
2041
+
2042
+ if ( strpos( $netmask, '.' ) !== false ) {
2043
+ // $netmask is a 255.255.0.0 format.
2044
+ $netmask = str_replace( '*', '0', $netmask );
2045
+ $netmask_dec = ip2long( $netmask );
2046
+ return ( ( ip2long( $ip ) & $netmask_dec ) === ( ip2long( $range ) & $netmask_dec ) );
2047
+ } else {
2048
+ // $netmask is a CIDR size block
2049
+ // fix the range argument.
2050
+ $x = explode( '.', $range );
2051
+ $x_count = count( $x );
2052
+
2053
+ while ( $x_count < 4 ) {
2054
+ $x[] = '0';
2055
+ $x_count = count( $x );
2056
+ }
2057
+
2058
+ list($a,$b,$c,$d) = $x;
2059
+ $range = sprintf( '%u.%u.%u.%u', empty( $a ) ? '0' : $a, empty( $b ) ? '0' : $b, empty( $c ) ? '0' : $c, empty( $d ) ? '0' : $d );
2060
+ $range_dec = ip2long( $range );
2061
+ $ip_dec = ip2long( $ip );
2062
+
2063
+ // Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
2064
+ // $netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
2065
+ // Strategy 2 - Use math to create it.
2066
+ $wildcard_dec = pow( 2, ( 32 - $netmask ) ) - 1;
2067
+ $netmask_dec = ~ $wildcard_dec;
2068
+
2069
+ return ( ( $ip_dec & $netmask_dec ) === ( $range_dec & $netmask_dec ) );
2070
+ }
2071
+ } else {
2072
+ // Range might be 255.255.*.* or 1.2.3.0-1.2.3.255.
2073
+ if ( strpos( $range, '*' ) !== false ) { // a.b.*.* format
2074
+ // Just convert to A-B format by setting * to 0 for A and 255 for B.
2075
+ $lower = str_replace( '*', '0', $range );
2076
+ $upper = str_replace( '*', '255', $range );
2077
+ $range = "$lower-$upper";
2078
+ }
2079
+
2080
+ // A-B format.
2081
+ if ( strpos( $range, '-' ) !== false ) {
2082
+ list($lower, $upper) = explode( '-', $range, 2 );
2083
+ $lower_dec = (float) sprintf( '%u', ip2long( $lower ) );
2084
+ $upper_dec = (float) sprintf( '%u', ip2long( $upper ) );
2085
+ $ip_dec = (float) sprintf( '%u', ip2long( $ip ) );
2086
+ return ( ( $ip_dec >= $lower_dec ) && ( $ip_dec <= $upper_dec ) );
2087
+ }
2088
+
2089
+ return false;
2090
+ }
2091
+ }
2092
+
2093
+ /**
2094
+ * Return the range of IP address from 127.0.0.0-24 to 127.0.0.0-127.0.0.24 format.
2095
+ *
2096
+ * @param string $range - Range of IP address.
2097
+ * @return object
2098
+ */
2099
+ public function get_ipv4_by_range( $range ) {
2100
+ list($lower_ip, $upper_ip) = explode( '-', $range, 2 );
2101
+
2102
+ $lower_arr = explode( '.', $lower_ip );
2103
+ $count = count( $lower_arr );
2104
+ unset( $lower_arr[ $count - 1 ] );
2105
+ $upper_ip = implode( '.', $lower_arr ) . '.' . $upper_ip;
2106
+
2107
+ return (object) array(
2108
+ 'lower' => $lower_ip,
2109
+ 'upper' => $upper_ip,
2110
+ );
2111
+ }
2112
+
2113
+ /**
2114
+ * Returns site server directories.
2115
+ *
2116
+ * @param string $context - Context of the directories.
2117
+ * @return array
2118
+ */
2119
+ public function get_server_directories( $context = '' ) {
2120
+ $wp_directories = array();
2121
+
2122
+ // Get WP uploads directory.
2123
+ $wp_uploads = wp_upload_dir();
2124
+ $uploads_dir = $wp_uploads['basedir'];
2125
+
2126
+ if ( 'display' === $context ) {
2127
+ $wp_directories = array(
2128
+ 'root' => __( 'Root directory of WordPress (excluding sub directories)', 'wp-security-audit-log' ),
2129
+ 'wp-admin' => __( 'WP Admin directory (/wp-admin/)', 'wp-security-audit-log' ),
2130
+ WPINC => __( 'WP Includes directory (/wp-includes/)', 'wp-security-audit-log' ),
2131
+ WP_CONTENT_DIR => __( '/wp-content/ directory (excluding plugins, themes & uploads directories)', 'wp-security-audit-log' ),
2132
+ get_theme_root() => __( 'Themes directory (/wp-content/themes/)', 'wp-security-audit-log' ),
2133
+ WP_PLUGIN_DIR => __( 'Plugins directory (/wp-content/plugins/)', 'wp-security-audit-log' ),
2134
+ $uploads_dir => __( 'Uploads directory (/wp-content/uploads/)', 'wp-security-audit-log' ),
2135
+ );
2136
+
2137
+ if ( is_multisite() ) {
2138
+ // Upload directories of subsites.
2139
+ $wp_directories[ $uploads_dir . '/sites' ] = __( 'Uploads directory of all sub sites on this network (/wp-content/sites/*)', 'wp-security-audit-log' );
2140
+ }
2141
+ } else {
2142
+ // Server directories.
2143
+ $wp_directories = array(
2144
+ '', // Root directory.
2145
+ 'wp-admin', // WordPress Admin.
2146
+ WPINC, // wp-includes.
2147
+ WP_CONTENT_DIR, // wp-content.
2148
+ get_theme_root(), // Themes.
2149
+ WP_PLUGIN_DIR, // Plugins.
2150
+ $uploads_dir, // Uploads.
2151
+ );
2152
+ }
2153
+
2154
+ // Prepare directories path.
2155
+ foreach ( $wp_directories as $index => $server_dir ) {
2156
+ if ( 'display' === $context && false !== strpos( $index, ABSPATH ) ) {
2157
+ unset( $wp_directories[ $index ] );
2158
+ $index = untrailingslashit( $index );
2159
+ $index = $this->get_server_directory( $index );
2160
+ } else {
2161
+ $server_dir = untrailingslashit( $server_dir );
2162
+ $server_dir = $this->get_server_directory( $server_dir );
2163
+ }
2164
+
2165
+ $wp_directories[ $index ] = $server_dir;
2166
+ }
2167
+
2168
+ return $wp_directories;
2169
+ }
2170
+
2171
+ /**
2172
+ * Returns a WP directory without ABSPATH.
2173
+ *
2174
+ * @param string $directory - Directory.
2175
+ * @return string
2176
+ */
2177
+ public function get_server_directory( $directory ) {
2178
+ return preg_replace( '/^' . preg_quote( ABSPATH, '/' ) . '/', '', $directory );
2179
+ }
2180
  }
classes/Views/AuditLog.php CHANGED
@@ -88,16 +88,16 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
88
  // Set adverts array.
89
  $this->adverts = array(
90
  0 => array(
91
- 'head' => __( 'Get instantly alerted of important changes via email, search the activity log and use filters, generate reports, see who is logged in and more!', 'wp-security-audit-log' ),
92
- 'desc' => __( 'Upgrade to premium to unlock these powerful activity log features.', 'wp-security-audit-log' ),
93
  ),
94
  1 => array(
95
- 'head' => __( 'Instant email notifications, search & filters, reports, users sessions management, integration tools and more!', 'wp-security-audit-log' ),
96
- 'desc' => __( 'Upgrade to benefit more from the activity logs.', 'wp-security-audit-log' ),
97
  ),
98
  2 => array(
99
- 'head' => __( 'See who is logged in real-time, generate reports, get alerted via email of important changes and more!', 'wp-security-audit-log' ),
100
- 'desc' => __( 'Unlock these and other features with the premium edition of WP Security Audit Log.', 'wp-security-audit-log' ),
101
  ),
102
  );
103
  }
@@ -112,6 +112,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
112
  */
113
  public function AdminNoticesPremium() {
114
  $is_current_view = $this->_plugin->views->GetActiveView() == $this;
 
115
  // Check if any of the extensions is activated.
116
  if (
117
  ! class_exists( 'WSAL_NP_Plugin' )
@@ -427,9 +428,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
427
  ?>
428
  <div data-remodal-id="wsal-setup-modal">
429
  <button data-remodal-action="close" class="remodal-close"></button>
430
- <p>
431
- <?php esc_html_e( 'Thank you very much for using the WP Security Audit Log plugin. We have created a wizard to ease the process of configuring the plugin so you can get the best out of it. Would you like to run the wizard?', 'wp-security-audit-log' ); ?>
432
- </p>
433
  <br>
434
  <button data-remodal-action="confirm" class="remodal-confirm"><?php esc_html_e( 'Yes', 'wp-security-audit-log' ); ?></button>
435
  <button data-remodal-action="cancel" class="remodal-cancel"><?php esc_html_e( 'No', 'wp-security-audit-log' ); ?></button>
88
  // Set adverts array.
89
  $this->adverts = array(
90
  0 => array(
91
+ 'head' => __( 'Get instantly alerted of critical changes via SMS & email, search the activity log, generate user reports, see who is logged in and more!', 'wp-security-audit-log' ),
92
+ 'desc' => __( 'Upgrade to premium to unlock these powerful activity log features & more!', 'wp-security-audit-log' ),
93
  ),
94
  1 => array(
95
+ 'head' => __( 'Instant SMS & email alerts, search & filters, reports, users sessions management and much more!', 'wp-security-audit-log' ),
96
+ 'desc' => __( 'Upgrade to premium to get more out of your activity logs!', 'wp-security-audit-log' ),
97
  ),
98
  2 => array(
99
+ 'head' => __( 'See who logged in on your site in real-time, generate reports, get SMS & email alerts of critical changes and more!', 'wp-security-audit-log' ),
100
+ 'desc' => __( 'Unlock these and other powerful features with WP Security Audit Log Premium.', 'wp-security-audit-log' ),
101
  ),
102
  );
103
  }
112
  */
113
  public function AdminNoticesPremium() {
114
  $is_current_view = $this->_plugin->views->GetActiveView() == $this;
115
+
116
  // Check if any of the extensions is activated.
117
  if (
118
  ! class_exists( 'WSAL_NP_Plugin' )
428
  ?>
429
  <div data-remodal-id="wsal-setup-modal">
430
  <button data-remodal-action="close" class="remodal-close"></button>
431
+ <p><?php esc_html_e( 'Thank you for installing WP Security Audit Log. Do you want to run the wizard to configure the basic plugin settings?', 'wp-security-audit-log' ); ?></p>
 
 
432
  <br>
433
  <button data-remodal-action="confirm" class="remodal-confirm"><?php esc_html_e( 'Yes', 'wp-security-audit-log' ); ?></button>
434
  <button data-remodal-action="cancel" class="remodal-cancel"><?php esc_html_e( 'No', 'wp-security-audit-log' ); ?></button>
classes/Views/Help.php CHANGED
@@ -432,10 +432,10 @@ class WSAL_Views_Help extends WSAL_AbstractView {
432
 
433
  // WSAL options.
434
  $sysinfo .= "\n" . '-- WSAL Options --' . "\n\n";
435
- $options = $this->get_wsal_options();
436
 
437
  if ( ! empty( $options ) && is_array( $options ) ) {
438
- foreach ( $options as $index => $option ) {
439
  $sysinfo .= 'Option: ' . $option->option_name . "\n";
440
  $sysinfo .= 'Value: ' . $option->option_value . "\n\n";
441
  }
@@ -446,51 +446,6 @@ class WSAL_Views_Help extends WSAL_AbstractView {
446
  return $sysinfo;
447
  }
448
 
449
- /**
450
- * Method: Query WSAL Options from DB.
451
- *
452
- * @return array - WSAL Options array.
453
- */
454
- public function get_wsal_options() {
455
- // Get options transient.
456
- $wsal_options = get_transient( 'wsal_options' );
457
-
458
- // If options transient is not set then query and set options.
459
- if ( false === $wsal_options ) {
460
- // Get raw options from DB.
461
- $raw_options = $this->query_wsal_options();
462
-
463
- if ( ! empty( $raw_options ) && is_array( $raw_options ) ) {
464
- foreach ( $raw_options as $option_id => $option ) {
465
- if ( ! empty( $option->option_value ) ) {
466
- $wsal_options[] = $option;
467
- }
468
- }
469
- }
470
-
471
- // Store the results in a transient.
472
- set_transient( 'wsal_options', $wsal_options, DAY_IN_SECONDS );
473
- }
474
-
475
- return $wsal_options;
476
- }
477
-
478
- /**
479
- * Method: Query WSAL Options from DB.
480
- *
481
- * @return array - Array of options.
482
- */
483
- public function query_wsal_options() {
484
- // Query WSAL options.
485
- global $wpdb;
486
-
487
- // Set table name.
488
- $options_table = $wpdb->prefix . 'wsal_options';
489
-
490
- // Query the options.
491
- return $wpdb->get_results( "SELECT * FROM $options_table" );
492
- }
493
-
494
  /**
495
  * Method: Render footer content.
496
  */
432
 
433
  // WSAL options.
434
  $sysinfo .= "\n" . '-- WSAL Options --' . "\n\n";
435
+ $options = $this->_plugin->settings->get_wsal_options();
436
 
437
  if ( ! empty( $options ) && is_array( $options ) ) {
438
+ foreach ( $options as $option ) {
439
  $sysinfo .= 'Option: ' . $option->option_name . "\n";
440
  $sysinfo .= 'Value: ' . $option->option_value . "\n\n";
441
  }
446
  return $sysinfo;
447
  }
448
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  /**
450
  * Method: Render footer content.
451
  */
classes/Views/Settings.php CHANGED
@@ -93,34 +93,46 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
93
  // Tab links.
94
  $wsal_setting_tabs = array(
95
  'general' => array(
96
- 'name' => __( 'General', 'wp-security-audit-log' ),
97
- 'link' => add_query_arg( 'tab', 'general', $this->GetUrl() ),
98
- 'render' => array( $this, 'tab_general' ),
99
- 'save' => array( $this, 'tab_general_save' ),
 
100
  ),
101
  'audit-log' => array(
102
- 'name' => __( 'Activity Log', 'wp-security-audit-log' ),
103
- 'link' => add_query_arg( 'tab', 'audit-log', $this->GetUrl() ),
104
- 'render' => array( $this, 'tab_audit_log' ),
105
- 'save' => array( $this, 'tab_audit_log_save' ),
 
106
  ),
107
  'file-changes' => array(
108
- 'name' => __( 'File Integrity Scan', 'wp-security-audit-log' ),
109
- 'link' => add_query_arg( 'tab', 'file-changes', $this->GetUrl() ),
110
- 'render' => array( $this, 'tab_file_changes' ),
111
- 'save' => array( $this, 'tab_file_changes_save' ),
 
112
  ),
113
  'exclude-objects' => array(
114
- 'name' => __( 'Exclude Objects', 'wp-security-audit-log' ),
115
- 'link' => add_query_arg( 'tab', 'exclude-objects', $this->GetUrl() ),
116
- 'render' => array( $this, 'tab_exclude_objects' ),
117
- 'save' => array( $this, 'tab_exclude_objects_save' ),
 
 
 
 
 
 
 
 
118
  ),
119
  'advanced-settings' => array(
120
- 'name' => __( 'Advanced Settings', 'wp-security-audit-log' ),
121
- 'link' => add_query_arg( 'tab', 'advanced-settings', $this->GetUrl() ),
122
- 'render' => array( $this, 'tab_advanced_settings' ),
123
- 'save' => array( $this, 'tab_advanced_settings_save' ),
 
124
  ),
125
  );
126
 
@@ -131,16 +143,22 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
131
  *
132
  * Setting tabs structure:
133
  * $wsal_setting_tabs['unique-tab-id'] = array(
134
- * 'name' => Name of the tab,
135
- * 'link' => Link of the tab,
136
- * 'render' => This function is used to render HTML elements in the tab,
137
- * 'name' => This function is used to save the related setting of the tab,
 
138
  * );
139
  *
140
  * @param array $wsal_setting_tabs – Array of WSAL Setting Tabs.
141
  * @since 3.2.3
142
  */
143
- $this->wsal_setting_tabs = apply_filters( 'wsal_setting_tabs', $wsal_setting_tabs );
 
 
 
 
 
144
 
145
  // Get the current tab.
146
  $current_tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING );
@@ -195,11 +213,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
195
  * Method: Load saved settings of this view.
196
  */
197
  public function load_file_changes_settings() {
198
- if ( ! is_multisite() ) {
199
- $default_scan_dirs = array( 'root', 'wp-admin', 'wp-includes', 'wp-content', 'wp-content/themes', 'wp-content/plugins', 'wp-content/uploads' );
200
- } else {
201
- $default_scan_dirs = array( 'root', 'wp-admin', 'wp-includes', 'wp-content', 'wp-content/themes', 'wp-content/plugins', 'wp-content/uploads', 'wp-content/uploads/sites' );
202
- }
203
 
204
  // Load saved settings of this view.
205
  $this->scan_settings = array(
@@ -371,6 +385,10 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
371
  }
372
  }
373
 
 
 
 
 
374
  if ( isset( $_GET['pruning'] ) && '1' === $_GET['pruning'] ) {
375
  ?>
376
  <div class="updated">
@@ -387,19 +405,13 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
387
  ?>
388
  <nav id="wsal-tabs" class="nav-tab-wrapper">
389
  <?php foreach ( $this->wsal_setting_tabs as $tab_id => $tab ) : ?>
390
- <?php if ( empty( $this->current_tab ) ) : ?>
391
- <a href="<?php echo esc_url( $tab['link'] ); ?>" class="nav-tab <?php echo ( 'general' === $tab_id ) ? 'nav-tab-active' : false; ?>">
392
- <?php echo esc_html( $tab['name'] ); ?>
393
- </a>
394
- <?php else : ?>
395
- <a href="<?php echo esc_url( $tab['link'] ); ?>" class="nav-tab <?php echo ( $tab_id === $this->current_tab ) ? 'nav-tab-active' : false; ?>">
396
- <?php echo esc_html( $tab['name'] ); ?>
397
- </a>
398
- <?php endif; ?>
399
  <?php endforeach; ?>
400
  </nav>
401
 
402
- <form id="audit-log-settings" method="post">
403
  <input type="hidden" name="page" value="<?php echo isset( $_GET['page'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) : false; ?>" />
404
  <input type="hidden" id="ajaxurl" value="<?php echo esc_attr( admin_url( 'admin-ajax.php' ) ); ?>" />
405
  <?php wp_nonce_field( 'wsal-settings' ); ?>
@@ -418,7 +430,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
418
  <?php
419
  if ( 'sms-provider' === $this->current_tab && $section && 'test' === $section ) {
420
  submit_button( __( 'Send Message', 'wp-security-audit-log' ) );
421
- } else {
422
  submit_button();
423
  }
424
  ?>
@@ -1448,24 +1460,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1448
  <label for="wsal-scan-directories"><?php esc_html_e( 'Directories to scan', 'wp-security-audit-log' ); ?></label>
1449
  </th>
1450
  <td>
1451
- <?php
1452
- // WP Directories.
1453
- $wp_directories = array(
1454
- 'root' => __( 'Root directory of WordPress (excluding sub directories)', 'wp-security-audit-log' ),
1455
- 'wp-admin' => __( 'WP Admin directory (/wp-admin/)', 'wp-security-audit-log' ),
1456
- 'wp-includes' => __( 'WP Includes directory (/wp-includes/)', 'wp-security-audit-log' ),
1457
- 'wp-content' => __( '/wp-content/ directory (excluding plugins, themes & uploads directories)', 'wp-security-audit-log' ),
1458
- 'wp-content/themes' => __( 'Themes directory (/wp-content/themes/)', 'wp-security-audit-log' ),
1459
- 'wp-content/plugins' => __( 'Plugins directory (/wp-content/plugins/)', 'wp-security-audit-log' ),
1460
- 'wp-content/uploads' => __( 'Uploads directory (/wp-content/uploads/)', 'wp-security-audit-log' ),
1461
- );
1462
-
1463
- // Check if multisite.
1464
- if ( is_multisite() ) {
1465
- // Upload directories of subsites.
1466
- $wp_directories['wp-content/uploads/sites'] = __( 'Uploads directory of all sub sites on this network (/wp-content/sites/*)', 'wp-security-audit-log' );
1467
- }
1468
- ?>
1469
  <fieldset id="wsal-scan-directories">
1470
  <?php foreach ( $wp_directories as $value => $html ) : ?>
1471
  <label>
@@ -1748,7 +1743,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1748
  <!-- Exclude Roles -->
1749
 
1750
  <tr>
1751
- <th><label for="IpAddrQueryBox"><?php esc_html_e( 'Exclude IP Addresses:', 'wp-security-audit-log' ); ?></label></th>
1752
  <td>
1753
  <fieldset>
1754
  <input type="text" id="IpAddrQueryBox" style="width: 250px;">
@@ -1764,6 +1759,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1764
  <?php endforeach; ?>
1765
  </div>
1766
  </fieldset>
 
1767
  </td>
1768
  </tr>
1769
  <!-- Exclude IP Addresses -->
@@ -1784,8 +1780,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1784
  </span>
1785
  <?php endforeach; ?>
1786
  </div>
1787
- <p class="description"><?php esc_html_e( 'WordPress has the post and page post types by default though your website might use more post types (custom post types). You can exclude all post types, including the default WordPress ones.', 'wp-security-audit-log' ); ?></p>
1788
  </fieldset>
 
1789
  </td>
1790
  </tr>
1791
  <!-- Exclude Custom Post Types -->
@@ -1806,8 +1802,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1806
  </span>
1807
  <?php endforeach; ?>
1808
  </div>
1809
- <p class="description"><?php esc_html_e( 'You can use the * wildcard to exclude multiple matching custom fields. For example to exclude all custom fields starting with wp123 enter wp123*', 'wp-security-audit-log' ); ?></p>
1810
  </fieldset>
 
1811
  </td>
1812
  </tr>
1813
  <!-- Exclude Custom Fields -->
@@ -1828,14 +1824,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1828
  </span>
1829
  <?php endforeach; ?>
1830
  </div>
1831
- <p class="description">
1832
- <?php esc_html_e( 'Add the non existing URLs for which you do not want to be alerted of HTTP 404 errors in the activity log by specifying the complete URL. Examples below:', 'wp-security-audit-log' ); ?>
1833
- <br>
1834
- <?php echo esc_html__( 'File: ', 'wp-security-audit-log' ) . esc_url( home_url() ) . '/subdirectory/file.php'; ?>
1835
- <br>
1836
- <?php echo esc_html__( 'Directory: ', 'wp-security-audit-log' ) . esc_url( home_url() ) . '/subdirectory/subdirectory2'; ?>
1837
- </p>
1838
  </fieldset>
 
1839
  </td>
1840
  </tr>
1841
  <!-- Exclude 404 URLs -->
@@ -1860,6 +1850,136 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1860
  $this->_plugin->settings->set_excluded_urls( isset( $post_array['ExURLss'] ) ? $post_array['ExURLss'] : array() );
1861
  }
1862
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1863
  /**
1864
  * Tab: `Advanced Settings`
1865
  */
@@ -2500,7 +2620,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
2500
  if ( 0 === $dir ) {
2501
  // Scan started alert.
2502
  $this->_plugin->alerts->Trigger(
2503
- 6033, array(
 
2504
  'CurrentUserID' => '0',
2505
  'ScanStatus' => 'started',
2506
  )
@@ -2508,7 +2629,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
2508
  } elseif ( 6 === $dir ) {
2509
  // Scan stopped.
2510
  $this->_plugin->alerts->Trigger(
2511
- 6033, array(
 
2512
  'CurrentUserID' => '0',
2513
  'ScanStatus' => 'stopped',
2514
  )
93
  // Tab links.
94
  $wsal_setting_tabs = array(
95
  'general' => array(
96
+ 'name' => __( 'General', 'wp-security-audit-log' ),
97
+ 'link' => add_query_arg( 'tab', 'general', $this->GetUrl() ),
98
+ 'render' => array( $this, 'tab_general' ),
99
+ 'save' => array( $this, 'tab_general_save' ),
100
+ 'priority' => 10,
101
  ),
102
  'audit-log' => array(
103
+ 'name' => __( 'Activity Log', 'wp-security-audit-log' ),
104
+ 'link' => add_query_arg( 'tab', 'audit-log', $this->GetUrl() ),
105
+ 'render' => array( $this, 'tab_audit_log' ),
106
+ 'save' => array( $this, 'tab_audit_log_save' ),
107
+ 'priority' => 20,
108
  ),
109
  'file-changes' => array(
110
+ 'name' => __( 'File Integrity Scan', 'wp-security-audit-log' ),
111
+ 'link' => add_query_arg( 'tab', 'file-changes', $this->GetUrl() ),
112
+ 'render' => array( $this, 'tab_file_changes' ),
113
+ 'save' => array( $this, 'tab_file_changes_save' ),
114
+ 'priority' => 30,
115
  ),
116
  'exclude-objects' => array(
117
+ 'name' => __( 'Exclude Objects', 'wp-security-audit-log' ),
118
+ 'link' => add_query_arg( 'tab', 'exclude-objects', $this->GetUrl() ),
119
+ 'render' => array( $this, 'tab_exclude_objects' ),
120
+ 'save' => array( $this, 'tab_exclude_objects_save' ),
121
+ 'priority' => 40,
122
+ ),
123
+ 'import-settings' => array(
124
+ 'name' => __( 'Import/Export', 'wp-security-audit-log' ),
125
+ 'link' => add_query_arg( 'tab', 'import-settings', $this->GetUrl() ),
126
+ 'render' => array( $this, 'tab_import_settings' ),
127
+ 'save' => array( $this, 'tab_import_settings_save' ),
128
+ 'priority' => 40,
129
  ),
130
  'advanced-settings' => array(
131
+ 'name' => __( 'Advanced Settings', 'wp-security-audit-log' ),
132
+ 'link' => add_query_arg( 'tab', 'advanced-settings', $this->GetUrl() ),
133
+ 'render' => array( $this, 'tab_advanced_settings' ),
134
+ 'save' => array( $this, 'tab_advanced_settings_save' ),
135
+ 'priority' => 100,
136
  ),
137
  );
138
 
143
  *
144
  * Setting tabs structure:
145
  * $wsal_setting_tabs['unique-tab-id'] = array(
146
+ * 'name' => Name of the tab,
147
+ * 'link' => Link of the tab,
148
+ * 'render' => This function is used to render HTML elements in the tab,
149
+ * 'name' => This function is used to save the related setting of the tab,
150
+ * 'priority' => Priority of the tab,
151
  * );
152
  *
153
  * @param array $wsal_setting_tabs – Array of WSAL Setting Tabs.
154
  * @since 3.2.3
155
  */
156
+ $wsal_setting_tabs = apply_filters( 'wsal_setting_tabs', $wsal_setting_tabs );
157
+
158
+ // Sort by priority.
159
+ array_multisort( array_column( $wsal_setting_tabs, 'priority' ), SORT_ASC, $wsal_setting_tabs );
160
+
161
+ $this->wsal_setting_tabs = $wsal_setting_tabs;
162
 
163
  // Get the current tab.
164
  $current_tab = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_STRING );
213
  * Method: Load saved settings of this view.
214
  */
215
  public function load_file_changes_settings() {
216
+ $default_scan_dirs = array_keys( $this->_plugin->settings->get_server_directories( 'display' ) );
 
 
 
 
217
 
218
  // Load saved settings of this view.
219
  $this->scan_settings = array(
385
  }
386
  }
387
 
388
+ if ( isset( $_POST['import'] ) ) {
389
+ call_user_func( $this->wsal_setting_tabs[ $this->current_tab ]['save'] );
390
+ }
391
+
392
  if ( isset( $_GET['pruning'] ) && '1' === $_GET['pruning'] ) {
393
  ?>
394
  <div class="updated">
405
  ?>
406
  <nav id="wsal-tabs" class="nav-tab-wrapper">
407
  <?php foreach ( $this->wsal_setting_tabs as $tab_id => $tab ) : ?>
408
+ <a href="<?php echo esc_url( $tab['link'] ); ?>" class="nav-tab <?php echo ( $tab_id === $this->current_tab ) ? 'nav-tab-active' : false; ?>">
409
+ <?php echo esc_html( $tab['name'] ); ?>
410
+ </a>
 
 
 
 
 
 
411
  <?php endforeach; ?>
412
  </nav>
413
 
414
+ <form id="audit-log-settings" method="post"<?php echo 'import-settings' === $this->current_tab ? ' enctype="multipart/form-data"' : false; ?>>
415
  <input type="hidden" name="page" value="<?php echo isset( $_GET['page'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) : false; ?>" />
416
  <input type="hidden" id="ajaxurl" value="<?php echo esc_attr( admin_url( 'admin-ajax.php' ) ); ?>" />
417
  <?php wp_nonce_field( 'wsal-settings' ); ?>
430
  <?php
431
  if ( 'sms-provider' === $this->current_tab && $section && 'test' === $section ) {
432
  submit_button( __( 'Send Message', 'wp-security-audit-log' ) );
433
+ } elseif ( 'import-settings' !== $this->current_tab ) {
434
  submit_button();
435
  }
436
  ?>
1460
  <label for="wsal-scan-directories"><?php esc_html_e( 'Directories to scan', 'wp-security-audit-log' ); ?></label>
1461
  </th>
1462
  <td>
1463
+ <?php $wp_directories = $this->_plugin->settings->get_server_directories( 'display' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1464
  <fieldset id="wsal-scan-directories">
1465
  <?php foreach ( $wp_directories as $value => $html ) : ?>
1466
  <label>
1743
  <!-- Exclude Roles -->
1744
 
1745
  <tr>
1746
+ <th><label for="IpAddrQueryBox"><?php esc_html_e( 'Exclude IP Address(es):', 'wp-security-audit-log' ); ?></label></th>
1747
  <td>
1748
  <fieldset>
1749
  <input type="text" id="IpAddrQueryBox" style="width: 250px;">
1759
  <?php endforeach; ?>
1760
  </div>
1761
  </fieldset>
1762
+ <p class="description"><?php esc_html_e( 'You can exclude an individual IP address or a range of IP addresses. To exclude a range use the following format: [first IP]-[last octet of the last IP]. Example: 172.16.180.6-127.', 'wp-security-audit-log' ); ?></p>
1763
  </td>
1764
  </tr>
1765
  <!-- Exclude IP Addresses -->
1780
  </span>
1781
  <?php endforeach; ?>
1782
  </div>
 
1783
  </fieldset>
1784
+ <p class="description"><?php esc_html_e( 'WordPress has the post and page post types by default though your website might use more post types (custom post types). You can exclude all post types, including the default WordPress ones.', 'wp-security-audit-log' ); ?></p>
1785
  </td>
1786
  </tr>
1787
  <!-- Exclude Custom Post Types -->
1802
  </span>
1803
  <?php endforeach; ?>
1804
  </div>
 
1805
  </fieldset>
1806
+ <p class="description"><?php esc_html_e( 'You can use the * wildcard to exclude multiple matching custom fields. For example to exclude all custom fields starting with wp123 enter wp123*', 'wp-security-audit-log' ); ?></p>
1807
  </td>
1808
  </tr>
1809
  <!-- Exclude Custom Fields -->
1824
  </span>
1825
  <?php endforeach; ?>
1826
  </div>
 
 
 
 
 
 
 
1827
  </fieldset>
1828
+ <p class="description"><?php esc_html_e( 'Add the non existing URLs for which you do not want to be alerted of HTTP 404 errors in the activity log by specifying the complete URL. Examples below:', 'wp-security-audit-log' ); ?><br><?php echo esc_html__( 'File: ', 'wp-security-audit-log' ) . esc_url( home_url() ) . '/subdirectory/file.php'; ?><br><?php echo esc_html__( 'Directory: ', 'wp-security-audit-log' ) . esc_url( home_url() ) . '/subdirectory/subdirectory2'; ?></p>
1829
  </td>
1830
  </tr>
1831
  <!-- Exclude 404 URLs -->
1850
  $this->_plugin->settings->set_excluded_urls( isset( $post_array['ExURLss'] ) ? $post_array['ExURLss'] : array() );
1851
  }
1852
 
1853
+ /**
1854
+ * Tab: `Import/Export`
1855
+ */
1856
+ private function tab_import_settings() {
1857
+ $blogname = str_replace( ' ', '', get_option( 'blogname' ) );
1858
+ $date = date( 'm-d-Y' );
1859
+ $json_name = $blogname . '-' . $date; // Export file name.
1860
+ ?>
1861
+ <p class="description"><?php esc_html_e( 'You can export and import the plugin settings from here, which can also be used as a plugin configuration backup. The plugin settings are exported to a JSON file.', 'wp-security-audit-log' ); ?></p>
1862
+ <h3><?php esc_html_e( 'Export Settings', 'wp-security-audit-log' ); ?></h3>
1863
+ <table class="form-table wsal-tab">
1864
+ <tbody>
1865
+ <tr>
1866
+ <th><label><?php esc_html_e( 'Export Settings', 'wp-security-audit-log' ); ?></label></th>
1867
+ <td>
1868
+ <fieldset>
1869
+ <?php
1870
+ $export_options = array();
1871
+
1872
+ $options = $this->_plugin->settings->get_wsal_options();
1873
+
1874
+ $ignored_options = array( 'site_content', 'local_files_0', 'local_files_1', 'local_files_2', 'local_files_3', 'local_files_4', 'local_files_5', 'local_files_6', 'is_initial_scan_0', 'is_initial_scan_1', 'is_initial_scan_2', 'is_initial_scan_3', 'is_initial_scan_4', 'is_initial_scan_5', 'is_initial_scan_6' );
1875
+
1876
+ foreach ( $options as $option ) {
1877
+ $option_name = str_replace( 'wsal-', '', $option->option_name );
1878
+
1879
+ if ( in_array( $option_name, $ignored_options, true ) ) {
1880
+ continue;
1881
+ }
1882
+
1883
+ $export_options[ $option_name ] = $option->option_value;
1884
+ }
1885
+
1886
+ $json_file = wp_json_encode( $export_options );
1887
+ ?>
1888
+ <input type="hidden" value="<?php echo esc_attr( $json_file ); ?>">
1889
+ <button type="button" name="export" id="wsal-export-options" class="button-primary"><?php esc_html_e( 'Export Settings', 'wp-security-audit-log' ); ?></button>
1890
+ </fieldset>
1891
+ </td>
1892
+ </tr>
1893
+ </tbody>
1894
+ </table>
1895
+ <!-- Export Settings -->
1896
+ <h3><?php esc_html_e( 'Import Settings', 'wp-security-audit-log' ); ?></h3>
1897
+ <table class="form-table wsal-tab">
1898
+ <tbody>
1899
+ <tr>
1900
+ <th><label><?php esc_html_e( 'Import Settings', 'wp-security-audit-log' ); ?></label></th>
1901
+ <td>
1902
+ <fieldset>
1903
+ <input type="file" name="import-settings" id="wsal-import-settings" accept="application/json">
1904
+ <p><button type="submit" name="import" id="wsal-import-settings-btn" class="button-primary button-disabled" disabled><?php esc_html_e( 'Import Settings', 'wp-security-audit-log' ); ?></button></p>
1905
+ </fieldset>
1906
+ </td>
1907
+ </tr>
1908
+ </tbody>
1909
+ </table>
1910
+ <!-- Import Settings -->
1911
+ <script>
1912
+ /**
1913
+ * Create and download a temporary file.
1914
+ *
1915
+ * @param {string} filename - File name.
1916
+ * @param {string} text - File content.
1917
+ */
1918
+ function download(filename, text) {
1919
+ // Create temporary element.
1920
+ var element = document.createElement('a');
1921
+ element.setAttribute('href', 'data:application/json;charset=<?php echo esc_html( get_option( 'blog_charset' ) ); ?>,' + encodeURIComponent(text));
1922
+ element.setAttribute('download', filename);
1923
+
1924
+ // Set the element to not display.
1925
+ element.style.display = 'none';
1926
+ document.body.appendChild(element);
1927
+
1928
+ // Simlate click on the element.
1929
+ element.click();
1930
+
1931
+ // Remove temporary element.
1932
+ document.body.removeChild(element);
1933
+ }
1934
+
1935
+ jQuery( document ).ready( function() {
1936
+ jQuery( '#wsal-export-options' ).click( function( event ) {
1937
+ event.preventDefault();
1938
+ download( '<?php echo esc_html( $json_name ); ?>', jQuery( this ).parent().find( 'input' ).val() );
1939
+ } );
1940
+
1941
+ jQuery( '#wsal-import-settings' ).change( function() {
1942
+ jQuery( '#wsal-import-settings-btn' ).removeAttr( 'disabled' );
1943
+ jQuery( '#wsal-import-settings-btn' ).removeClass( 'button-disabled' );
1944
+ } );
1945
+ } );
1946
+ </script>
1947
+ <?php
1948
+ }
1949
+
1950
+ /**
1951
+ * Save: `Import/Export`
1952
+ */
1953
+ private function tab_import_settings_save() {
1954
+ if ( isset( $_FILES['import-settings'] ) ) {
1955
+ if ( 0 === $_FILES['import-settings']['error'] ) {
1956
+ $filename = isset( $_FILES['import-settings']['name'] ) ? sanitize_text_field( wp_unslash( $_FILES['import-settings']['name'] ) ) : false;
1957
+ $file_type = isset( $_FILES['import-settings']['type'] ) ? sanitize_text_field( wp_unslash( $_FILES['import-settings']['type'] ) ) : false;
1958
+ $file_size = isset( $_FILES['import-settings']['size'] ) ? (int) sanitize_text_field( wp_unslash( $_FILES['import-settings']['size'] ) ) : false;
1959
+
1960
+ if ( 'application/json' === $file_type && $file_size < 500000 ) {
1961
+ $encoded_options = isset( $_FILES['import-settings']['tmp_name'] ) ? file_get_contents( sanitize_text_field( wp_unslash( $_FILES['import-settings']['tmp_name'] ) ) ) : false; // phpcs:ignore
1962
+ $options = json_decode( $encoded_options );
1963
+
1964
+ if ( $options ) {
1965
+ foreach ( $options as $key => $value ) {
1966
+ $value = maybe_unserialize( sanitize_text_field( $value ) );
1967
+ $this->_plugin->SetGlobalOption( $key, $value );
1968
+ }
1969
+
1970
+ echo '<div class="notice notice-success"><p>' . esc_html__( 'The plugin settings have been imported successfully.', 'wp-security-audit-log' ) . '</p></div>';
1971
+ } else {
1972
+ echo '<div class="notice notice-error"><p>' . esc_html__( 'No settings found to import.', 'wp-security-audit-log' ) . '</p></div>';
1973
+ }
1974
+ } else {
1975
+ echo '<div class="notice notice-error"><p>' . esc_html__( 'Invalid file or file size is too large.', 'wp-security-audit-log' ) . '</p></div>';
1976
+ }
1977
+ } else {
1978
+ echo '<div class="notice notice-error"><p>' . esc_html__( 'Error occurred while uploading the file.', 'wp-security-audit-log' ) . '</p></div>';
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
  /**
1984
  * Tab: `Advanced Settings`
1985
  */
2620
  if ( 0 === $dir ) {
2621
  // Scan started alert.
2622
  $this->_plugin->alerts->Trigger(
2623
+ 6033,
2624
+ array(
2625
  'CurrentUserID' => '0',
2626
  'ScanStatus' => 'started',
2627
  )
2629
  } elseif ( 6 === $dir ) {
2630
  // Scan stopped.
2631
  $this->_plugin->alerts->Trigger(
2632
+ 6033,
2633
+ array(
2634
  'CurrentUserID' => '0',
2635
  'ScanStatus' => 'stopped',
2636
  )
classes/Views/SetupWizard.php CHANGED
@@ -323,9 +323,7 @@ final class WSAL_Views_SetupWizard {
323
  $this->wsal->SetGlobalOption( 'wsal-setup-modal-dismissed', 'yes' );
324
  }
325
  ?>
326
- <p><?php esc_html_e( 'Thank you for installing the WP Security Audit Log plugin.', 'wp-security-audit-log' ); ?></p>
327
- <p><?php esc_html_e( 'This wizard will help you configure your WordPress activity log plugin and get you started quickly.', 'wp-security-audit-log' ); ?></p>
328
- <p><?php esc_html_e( 'Anything that can be configured in this wizard can be changed at a later stage from the plugin settings. If you are an experienced user of this plugin you can exit this wizard and configure all the settings manually.', 'wp-security-audit-log' ); ?></p>
329
 
330
  <div class="wsal-setup-actions">
331
  <a class="button button-primary"
@@ -446,6 +444,11 @@ final class WSAL_Views_SetupWizard {
446
  <?php
447
  // Step help text.
448
  $step_help = __( 'The plugin stores the data in the WordPress database in a very efficient way, though the more data you keep the more hard disk space it will consume. If you need need to retain a lot of data we would recommend you to <a href="https://www.wpsecurityauditlog.com/premium-features/" target="_blank">upgrade to Premium</a> and use the Database tools to store the WordPress activity log in an external database.', 'wp-security-audit-log' );
 
 
 
 
 
449
  echo wp_kses( $step_help, $this->wsal->allowed_html_tags );
450
  ?>
451
  </em>
323
  $this->wsal->SetGlobalOption( 'wsal-setup-modal-dismissed', 'yes' );
324
  }
325
  ?>
326
+ <p><?php esc_html_e( 'This wizard helps you configure the basic plugin settings. All these settings can be changed at a later stage from the plugin settings.', 'wp-security-audit-log' ); ?></p>
 
 
327
 
328
  <div class="wsal-setup-actions">
329
  <a class="button button-primary"
444
  <?php
445
  // Step help text.
446
  $step_help = __( 'The plugin stores the data in the WordPress database in a very efficient way, though the more data you keep the more hard disk space it will consume. If you need need to retain a lot of data we would recommend you to <a href="https://www.wpsecurityauditlog.com/premium-features/" target="_blank">upgrade to Premium</a> and use the Database tools to store the WordPress activity log in an external database.', 'wp-security-audit-log' );
447
+
448
+ if ( wsal_freemius()->is__premium_only() ) {
449
+ // Change the help text if premium version of the plugin is active.
450
+ $step_help = __( 'The plugin stores the data in the WordPress database in a very efficient way, though the more data you keep the more hard disk space it will consume. If you need need to retain a lot of data we would recommend you to store the WordPress activity log in an external database or enable archiving.', 'wp-security-audit-log' );
451
+ }
452
  echo wp_kses( $step_help, $this->wsal->allowed_html_tags );
453
  ?>
454
  </em>
css/auditlog.css CHANGED
@@ -2,8 +2,7 @@
2
  * Audit Log admin CSS
3
  *
4
  */
5
-
6
- .top .wsal-ssa select {
7
  margin: 0 3px 0 0;
8
  vertical-align: top;
9
  height: 30px;
@@ -351,14 +350,19 @@ div.wsal_notice__wrapper p {
351
  align-items: center;
352
  }
353
 
354
- #wsal-event-loader,
355
- #wsal-auditlog-end {
356
- text-align: center;
357
  }
 
358
  #wsal-auditlog-end {
359
  display: none;
360
  }
361
 
 
 
 
 
 
362
  .wsal-lds-ellipsis {
363
  display: inline-block;
364
  position: relative;
2
  * Audit Log admin CSS
3
  *
4
  */
5
+ .top .wsal-ssa select {
 
6
  margin: 0 3px 0 0;
7
  vertical-align: top;
8
  height: 30px;
350
  align-items: center;
351
  }
352
 
353
+ #wsal-event-loader {
354
+ display: none;
 
355
  }
356
+
357
  #wsal-auditlog-end {
358
  display: none;
359
  }
360
 
361
+ #wsal-event-loader,
362
+ #wsal-auditlog-end {
363
+ text-align: center;
364
+ }
365
+
366
  .wsal-lds-ellipsis {
367
  display: inline-block;
368
  position: relative;
defaults.php CHANGED
@@ -222,11 +222,11 @@ function wsaldefaults_wsal_init() {
222
  array( 2112, E_NOTICE, __( 'User enabled Comments/Trackbacks and Pingbacks in a post', 'wp-security-audit-log' ), __( 'Enabled %Type% on the %PostStatus% %PostType% titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
223
  array( 2119, E_NOTICE, __( 'User added post tag', 'wp-security-audit-log' ), __( 'Added the tag %tag% to the %PostStatus% post titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
224
  array( 2120, E_NOTICE, __( 'User removed post tag', 'wp-security-audit-log' ), __( 'Removed the tag %tag% from the %PostStatus% post titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
225
- array( 2121, E_NOTICE, __( 'User created new tag', 'wp-security-audit-log' ), __( 'Added a new tag called %TagName%. View the tag: %TagLink%.', 'wp-security-audit-log' ) ),
226
  array( 2122, E_NOTICE, __( 'User deleted tag', 'wp-security-audit-log' ), __( 'Deleted the tag %TagName%.', 'wp-security-audit-log' ) ),
227
- array( 2123, E_NOTICE, __( 'User renamed tag', 'wp-security-audit-log' ), __( 'Renamed a tag from %old_name% to %new_name%. View the tag: %TagLink%.', 'wp-security-audit-log' ) ),
228
- array( 2124, E_NOTICE, __( 'User changed tag slug', 'wp-security-audit-log' ), __( 'Changed the slug of tag %tag% from %old_slug% to %new_slug%. View the tag: %TagLink%.', 'wp-security-audit-log' ) ),
229
- array( 2125, E_NOTICE, __( 'User changed tag description', 'wp-security-audit-log' ), __( 'Changed the description of the tag %tag%%ReportText%.%ChangeText% View the tag: %TagLink%.', 'wp-security-audit-log' ) ),
230
  array( 2016, E_NOTICE, __( 'User changed post category', 'wp-security-audit-log' ), __( 'Changed the category of the %PostStatus% %PostType% titled %PostTitle% from %OldCategories% to %NewCategories%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
231
  array( 2023, E_NOTICE, __( 'User created new category', 'wp-security-audit-log' ), __( 'Created a new category called %CategoryName%. Category slug is %Slug%. %CategoryLink%.', 'wp-security-audit-log' ) ),
232
  array( 2024, E_WARNING, __( 'User deleted category', 'wp-security-audit-log' ), __( 'Deleted the %CategoryName% category. Category slug was %Slug%. %CategoryLink%.', 'wp-security-audit-log' ) ),
222
  array( 2112, E_NOTICE, __( 'User enabled Comments/Trackbacks and Pingbacks in a post', 'wp-security-audit-log' ), __( 'Enabled %Type% on the %PostStatus% %PostType% titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
223
  array( 2119, E_NOTICE, __( 'User added post tag', 'wp-security-audit-log' ), __( 'Added the tag %tag% to the %PostStatus% post titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
224
  array( 2120, E_NOTICE, __( 'User removed post tag', 'wp-security-audit-log' ), __( 'Removed the tag %tag% from the %PostStatus% post titled %PostTitle%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
225
+ array( 2121, E_NOTICE, __( 'User created new tag', 'wp-security-audit-log' ), __( 'Created a new tag called %TagName%. %TagLink%.', 'wp-security-audit-log' ) ),
226
  array( 2122, E_NOTICE, __( 'User deleted tag', 'wp-security-audit-log' ), __( 'Deleted the tag %TagName%.', 'wp-security-audit-log' ) ),
227
+ array( 2123, E_NOTICE, __( 'User renamed tag', 'wp-security-audit-log' ), __( 'Renamed a tag from %old_name% to %new_name%. %TagLink%.', 'wp-security-audit-log' ) ),
228
+ array( 2124, E_NOTICE, __( 'User changed tag slug', 'wp-security-audit-log' ), __( 'Changed the slug of tag %tag% from %old_slug% to %new_slug%. %TagLink%.', 'wp-security-audit-log' ) ),
229
+ array( 2125, E_NOTICE, __( 'User changed tag description', 'wp-security-audit-log' ), __( 'Changed the description of the tag %tag%%ReportText%.%ChangeText% %TagLink%.', 'wp-security-audit-log' ) ),
230
  array( 2016, E_NOTICE, __( 'User changed post category', 'wp-security-audit-log' ), __( 'Changed the category of the %PostStatus% %PostType% titled %PostTitle% from %OldCategories% to %NewCategories%. URL is: %PostUrl%. %EditorLinkPost%.', 'wp-security-audit-log' ) ),
231
  array( 2023, E_NOTICE, __( 'User created new category', 'wp-security-audit-log' ), __( 'Created a new category called %CategoryName%. Category slug is %Slug%. %CategoryLink%.', 'wp-security-audit-log' ) ),
232
  array( 2024, E_WARNING, __( 'User deleted category', 'wp-security-audit-log' ), __( 'Deleted the %CategoryName% category. Category slug was %Slug%. %CategoryLink%.', 'wp-security-audit-log' ) ),
img/users-sessions-management/logged_in_users.png DELETED
Binary file
img/users-sessions-management/users_session_management_config.png DELETED
Binary file
js/auditlog.js CHANGED
@@ -423,6 +423,7 @@ function wsal_dismiss_advert(element) {
423
  */
424
  function wsalLoadEvents( pageNumber ) {
425
  jQuery( '#wsal-event-loader' ).show( 'fast' );
 
426
  jQuery.ajax( {
427
  type:'POST',
428
  url: ajaxurl,
@@ -439,6 +440,7 @@ function wsalLoadEvents( pageNumber ) {
439
  },
440
  success: function( html ) {
441
  jQuery( '#wsal-event-loader' ).hide( '1000' );
 
442
  if ( html ) {
443
  wsalLoadEventsResponse = true;
444
  jQuery( '#audit-log-viewer #the-list' ).append( html ); // This will be the div where our content will be loaded.
@@ -453,9 +455,11 @@ function wsalLoadEvents( pageNumber ) {
453
  console.log( error );
454
  }
455
  });
 
456
  if ( wsalLoadEventsResponse ) {
457
  return pageNumber + 1;
458
  }
 
459
  return 0;
460
  }
461
  var wsalLoadEventsResponse = true; // Global variable to check events loading response.
@@ -493,7 +497,8 @@ jQuery( document ).ready( function() {
493
  if ( wsalAuditLogArgs.infiniteScroll ) {
494
  var count = 2;
495
  jQuery( window ).scroll( function() {
496
- if ( jQuery( window ).scrollTop() === jQuery( document ).height() - jQuery( window ).height() ) {
 
497
  if ( 0 !== count ) {
498
  count = wsalLoadEvents( count );
499
  }
423
  */
424
  function wsalLoadEvents( pageNumber ) {
425
  jQuery( '#wsal-event-loader' ).show( 'fast' );
426
+
427
  jQuery.ajax( {
428
  type:'POST',
429
  url: ajaxurl,
440
  },
441
  success: function( html ) {
442
  jQuery( '#wsal-event-loader' ).hide( '1000' );
443
+
444
  if ( html ) {
445
  wsalLoadEventsResponse = true;
446
  jQuery( '#audit-log-viewer #the-list' ).append( html ); // This will be the div where our content will be loaded.
455
  console.log( error );
456
  }
457
  });
458
+
459
  if ( wsalLoadEventsResponse ) {
460
  return pageNumber + 1;
461
  }
462
+
463
  return 0;
464
  }
465
  var wsalLoadEventsResponse = true; // Global variable to check events loading response.
497
  if ( wsalAuditLogArgs.infiniteScroll ) {
498
  var count = 2;
499
  jQuery( window ).scroll( function() {
500
+ var scrollToTop = Math.round( jQuery( window ).scrollTop() );
501
+ if ( scrollToTop === ( jQuery( document ).height() - jQuery( window ).height() ) ) {
502
  if ( 0 !== count ) {
503
  count = wsalLoadEvents( count );
504
  }
readme.txt CHANGED
@@ -5,9 +5,9 @@ License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: wordpress security plugin, wordpress security audit log, audit log, activity logs, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, dashboard, notification, wordpress monitoring, email notification, wordpress email alerts, SMS messages, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 3.6
8
- Tested up to: 5.1.1
9
- Stable tag: 3.4.0.1
10
- Requires PHP: 5.4.43
11
 
12
  An easy to use & comprehensive WordPress activity log plugin to log all changes on WordPress sites & multisite networks.
13
 
@@ -142,6 +142,7 @@ Refer to the <strong>[WordPress activity log plugin datasheet](https://www.wpsec
142
  We need help translating the plugin and the WordPress Security Alerts. Please visit the [WordPress Translate Project](https://translate.wordpress.org/projects/wp-plugins/wp-security-audit-log) to translate the plugin and drop us an email on support@wpwhitesecurity.com to get mentioned in the list of translators below.
143
 
144
  * Italian translation by [Leonardo Musumeci](http://leonardomusumeci.net/)
 
145
  * German translation by [Mourad Louha](http://excel-translator.de)
146
  * Brazilian Portuguese translation by [Hudson Santos](https://www.smallbee.com.br/)
147
  * Spanish translation by the [WP Body team](https://wpbody.com/)
@@ -197,8 +198,32 @@ Please refer to our [Support & Documentation pages](https://www.wpsecurityauditl
197
 
198
  == Changelog ==
199
 
200
- = 3.4.0.1 (2019-04-10) =
201
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  * **Bug Fixes**
203
- * Backward compatibility issue for PHP 5.4.
204
- * Fix for audit log page search extension check function.
 
 
 
 
 
 
 
 
 
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: wordpress security plugin, wordpress security audit log, audit log, activity logs, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, dashboard, notification, wordpress monitoring, email notification, wordpress email alerts, SMS messages, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 3.6
8
+ Tested up to: 5.2
9
+ Stable tag: 3.4.1
10
+ Requires PHP: 5.5
11
 
12
  An easy to use & comprehensive WordPress activity log plugin to log all changes on WordPress sites & multisite networks.
13
 
142
  We need help translating the plugin and the WordPress Security Alerts. Please visit the [WordPress Translate Project](https://translate.wordpress.org/projects/wp-plugins/wp-security-audit-log) to translate the plugin and drop us an email on support@wpwhitesecurity.com to get mentioned in the list of translators below.
143
 
144
  * Italian translation by [Leonardo Musumeci](http://leonardomusumeci.net/)
145
+ * Brazilian Portuguese by [Hudson Santos](https://www.smallbee.com.br/)
146
  * German translation by [Mourad Louha](http://excel-translator.de)
147
  * Brazilian Portuguese translation by [Hudson Santos](https://www.smallbee.com.br/)
148
  * Spanish translation by the [WP Body team](https://wpbody.com/)
198
 
199
  == Changelog ==
200
 
201
+ = 3.4.1 (2019-05-16) =
202
+
203
+ Release notes: [New plugin settings import/export tool & hooks for theme developers](https://www.wpsecurityauditlog.com/releases/update-3-4-1-import-export-plugin-settings/)
204
+
205
+ * **New Features**
206
+ * Tool to export and import plugin configuration & settings.
207
+ * Exclude range of IP addresses from the activity log.
208
+ * New [hooks for theme developers](https://www.wpsecurityauditlog.com/support-documentation/list-hooks/) to display the custom login messages the plugin shows when multiple user sessions are blocked.
209
+ * New hook to add a list of hidden meta keys the plugin should keep a log of.
210
+
211
+ * **Plugin Improvements**
212
+ * First time plugin use texts is now easier to read and much shorter.
213
+ * Added support for more time & date formats in the [activity log reports for WordPress](https://www.wpsecurityauditlog.com/premium-features/reports-wordpress-activity-log/).
214
+ * Improved content sensor - previously reporting events not neccessarily needed (background processes)
215
+ * Removed hardcoded paths from the [WordPress file integrity scanner](https://www.wpsecurityauditlog.com/support-documentation/wordpress-files-changes-warning-activity-logs/).
216
+ * Removed Sites filter from audit log viewer - made redundant by the site selector drop down menu.
217
+
218
  * **Bug Fixes**
219
+ * Multiple events reporting the same thing generated when user changes WooCommerce shipping / billing address.
220
+ * Updated incorrect tags used in test SMS message.
221
+ * Event 2002 (modified post) reported even when there is a specific change event ID.
222
+ * Event 2016 (plugin modified post) reported whenever a post is updated by user.
223
+ * External database connection cannot be deleted because it is marked in use even when not.
224
+ * Plugin generating error when set_user_role is set to NUL in request.
225
+ * Infinite scroll stops working on Firefox (intermittent issue).
226
+
227
+ = Earlier versions =
228
+
229
+ Please refer to the [complete plugin changelog](https://www.wpsecurityauditlog.com/support-documentation/plugin-changelog/) for more detailed information about what was new, improved and fixed in previous versions of the WP Security Audit Log plugin.
wp-security-audit-log.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin URI: http://www.wpsecurityauditlog.com/
5
  * Description: Identify WordPress security issues before they become a problem. Keep track of everything happening on your WordPress including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log generates a security alert for everything that happens on your WordPress blogs and websites. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  * Author: WP White Security
7
- * Version: 3.4.0.1
8
  * Text Domain: wp-security-audit-log
9
  * Author URI: http://www.wpwhitesecurity.com/
10
  * License: GPL2
@@ -54,7 +54,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
54
  *
55
  * @var string
56
  */
57
- public $version = '3.4.0.1';
58
 
59
  // Plugin constants.
60
  const PLG_CLS_PRFX = 'WSAL_';
4
  * Plugin URI: http://www.wpsecurityauditlog.com/
5
  * Description: Identify WordPress security issues before they become a problem. Keep track of everything happening on your WordPress including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log generates a security alert for everything that happens on your WordPress blogs and websites. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  * Author: WP White Security
7
+ * Version: 3.4.1
8
  * Text Domain: wp-security-audit-log
9
  * Author URI: http://www.wpwhitesecurity.com/
10
  * License: GPL2
54
  *
55
  * @var string
56
  */
57
+ public $version = '3.4.1';
58
 
59
  // Plugin constants.
60
  const PLG_CLS_PRFX = 'WSAL_';